OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [javax/] [swing/] [plaf/] [basic/] [BasicListUI.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* BasicListUI.java --
2
   Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package javax.swing.plaf.basic;
40
 
41
import java.awt.Component;
42
import java.awt.Dimension;
43
import java.awt.Graphics;
44
import java.awt.Point;
45
import java.awt.Rectangle;
46
import java.awt.event.ActionEvent;
47
import java.awt.event.ActionListener;
48
import java.awt.event.ComponentAdapter;
49
import java.awt.event.ComponentEvent;
50
import java.awt.event.ComponentListener;
51
import java.awt.event.FocusEvent;
52
import java.awt.event.FocusListener;
53
import java.awt.event.MouseEvent;
54
import java.beans.PropertyChangeEvent;
55
import java.beans.PropertyChangeListener;
56
 
57
import javax.swing.AbstractAction;
58
import javax.swing.ActionMap;
59
import javax.swing.CellRendererPane;
60
import javax.swing.DefaultListSelectionModel;
61
import javax.swing.InputMap;
62
import javax.swing.JComponent;
63
import javax.swing.JList;
64
import javax.swing.JViewport;
65
import javax.swing.KeyStroke;
66
import javax.swing.ListCellRenderer;
67
import javax.swing.ListModel;
68
import javax.swing.ListSelectionModel;
69
import javax.swing.LookAndFeel;
70
import javax.swing.UIDefaults;
71
import javax.swing.UIManager;
72
import javax.swing.event.ListDataEvent;
73
import javax.swing.event.ListDataListener;
74
import javax.swing.event.ListSelectionEvent;
75
import javax.swing.event.ListSelectionListener;
76
import javax.swing.event.MouseInputListener;
77
import javax.swing.plaf.ActionMapUIResource;
78
import javax.swing.plaf.ComponentUI;
79
import javax.swing.plaf.InputMapUIResource;
80
import javax.swing.plaf.ListUI;
81
 
82
/**
83
 * The Basic Look and Feel UI delegate for the
84
 * JList.
85
 */
86
public class BasicListUI extends ListUI
87
{
88
 
89
  /**
90
   * A helper class which listens for {@link ComponentEvent}s from
91
   * the JList.
92
   */
93
  private class ComponentHandler extends ComponentAdapter {
94
 
95
    /**
96
     * Called when the component is hidden. Invalidates the internal
97
     * layout.
98
     */
99
    public void componentResized(ComponentEvent ev) {
100
      BasicListUI.this.damageLayout();
101
    }
102
  }
103
 
104
  /**
105
   * A helper class which listens for {@link FocusEvent}s
106
   * from the JList.
107
   */
108
  public class FocusHandler implements FocusListener
109
  {
110
    /**
111
     * Called when the JList acquires focus.
112
     *
113
     * @param e The FocusEvent representing focus acquisition
114
     */
115
    public void focusGained(FocusEvent e)
116
    {
117
      repaintCellFocus();
118
    }
119
 
120
    /**
121
     * Called when the JList loses focus.
122
     *
123
     * @param e The FocusEvent representing focus loss
124
     */
125
    public void focusLost(FocusEvent e)
126
    {
127
      repaintCellFocus();
128
    }
129
 
130
    /**
131
     * Helper method to repaint the focused cell's
132
     * lost or acquired focus state.
133
     */
134
    protected void repaintCellFocus()
135
    {
136
      // TODO: Implement this properly.
137
    }
138
  }
139
 
140
  /**
141
   * A helper class which listens for {@link ListDataEvent}s generated by
142
   * the {@link JList}'s {@link ListModel}.
143
   *
144
   * @see javax.swing.JList#getModel()
145
   */
146
  public class ListDataHandler implements ListDataListener
147
  {
148
    /**
149
     * Called when a general change has happened in the model which cannot
150
     * be represented in terms of a simple addition or deletion.
151
     *
152
     * @param e The event representing the change
153
     */
154
    public void contentsChanged(ListDataEvent e)
155
    {
156
      BasicListUI.this.damageLayout();
157
    }
158
 
159
    /**
160
     * Called when an interval of objects has been added to the model.
161
     *
162
     * @param e The event representing the addition
163
     */
164
    public void intervalAdded(ListDataEvent e)
165
    {
166
      BasicListUI.this.damageLayout();
167
    }
168
 
169
    /**
170
     * Called when an inteval of objects has been removed from the model.
171
     *
172
     * @param e The event representing the removal
173
     */
174
    public void intervalRemoved(ListDataEvent e)
175
    {
176
      BasicListUI.this.damageLayout();
177
    }
178
  }
179
 
180
  /**
181
   * A helper class which listens for {@link ListSelectionEvent}s
182
   * from the {@link JList}'s {@link ListSelectionModel}.
183
   */
184
  public class ListSelectionHandler implements ListSelectionListener
185
  {
186
    /**
187
     * Called when the list selection changes.
188
     *
189
     * @param e The event representing the change
190
     */
191
    public void valueChanged(ListSelectionEvent e)
192
    {
193
      int index1 = e.getFirstIndex();
194
      int index2 = e.getLastIndex();
195
      Rectangle damaged = getCellBounds(list, index1, index2);
196
      list.repaint(damaged);
197
    }
198
  }
199
 
200
  /**
201
   * This class is used to mimmic the behaviour of the JDK when registering
202
   * keyboard actions.  It is the same as the private class used in JComponent
203
   * for the same reason.  This class receives an action event and dispatches
204
   * it to the true receiver after altering the actionCommand property of the
205
   * event.
206
   */
207
  private static class ActionListenerProxy
208
    extends AbstractAction
209
  {
210
    ActionListener target;
211
    String bindingCommandName;
212
 
213
    public ActionListenerProxy(ActionListener li,
214
                               String cmd)
215
    {
216
      target = li;
217
      bindingCommandName = cmd;
218
    }
219
 
220
    public void actionPerformed(ActionEvent e)
221
    {
222
      ActionEvent derivedEvent = new ActionEvent(e.getSource(),
223
                                                 e.getID(),
224
                                                 bindingCommandName,
225
                                                 e.getModifiers());
226
      target.actionPerformed(derivedEvent);
227
    }
228
  }
229
 
230
  class ListAction extends AbstractAction
231
  {
232
    public void actionPerformed (ActionEvent e)
233
    {
234
      int lead = list.getLeadSelectionIndex();
235
      int max = list.getModel().getSize() - 1;
236
      DefaultListSelectionModel selModel = (DefaultListSelectionModel)list.getSelectionModel();
237
      String command = e.getActionCommand();
238
      // Do nothing if list is empty
239
      if (max == -1)
240
        return;
241
 
242
      if (command.equals("selectNextRow"))
243
        {
244
          selectNextIndex();
245
        }
246
      else if (command.equals("selectPreviousRow"))
247
        {
248
          selectPreviousIndex();
249
        }
250
      else if (command.equals("clearSelection"))
251
        {
252
          list.clearSelection();
253
        }
254
      else if (command.equals("selectAll"))
255
        {
256
          list.setSelectionInterval(0, max);
257
          // this next line is to restore the lead selection index to the old
258
          // position, because select-all should not change the lead index
259
          list.addSelectionInterval(lead, lead);
260
        }
261
      else if (command.equals("selectLastRow"))
262
        {
263
          list.setSelectedIndex(list.getModel().getSize() - 1);
264
        }
265
      else if (command.equals("selectLastRowChangeLead"))
266
        {
267
          selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1);
268
        }
269
      else if (command.equals("scrollDownExtendSelection"))
270
        {
271
          int target;
272
          if (lead == list.getLastVisibleIndex())
273
            {
274
              target = Math.min
275
                (max, lead + (list.getLastVisibleIndex() -
276
                    list.getFirstVisibleIndex() + 1));
277
            }
278
          else
279
            target = list.getLastVisibleIndex();
280
          selModel.setLeadSelectionIndex(target);
281
        }
282
      else if (command.equals("scrollDownChangeLead"))
283
        {
284
          int target;
285
          if (lead == list.getLastVisibleIndex())
286
            {
287
              target = Math.min
288
                (max, lead + (list.getLastVisibleIndex() -
289
                    list.getFirstVisibleIndex() + 1));
290
            }
291
          else
292
            target = list.getLastVisibleIndex();
293
          selModel.moveLeadSelectionIndex(target);
294
        }
295
      else if (command.equals("scrollUpExtendSelection"))
296
        {
297
          int target;
298
          if (lead == list.getFirstVisibleIndex())
299
            {
300
              target = Math.max
301
                (0, lead - (list.getLastVisibleIndex() -
302
                    list.getFirstVisibleIndex() + 1));
303
            }
304
          else
305
            target = list.getFirstVisibleIndex();
306
          selModel.setLeadSelectionIndex(target);
307
        }
308
      else if (command.equals("scrollUpChangeLead"))
309
        {
310
          int target;
311
          if (lead == list.getFirstVisibleIndex())
312
            {
313
              target = Math.max
314
                (0, lead - (list.getLastVisibleIndex() -
315
                    list.getFirstVisibleIndex() + 1));
316
            }
317
          else
318
            target = list.getFirstVisibleIndex();
319
          selModel.moveLeadSelectionIndex(target);
320
        }
321
      else if (command.equals("selectNextRowExtendSelection"))
322
        {
323
          selModel.setLeadSelectionIndex(Math.min(lead + 1,max));
324
        }
325
      else if (command.equals("selectFirstRow"))
326
        {
327
          list.setSelectedIndex(0);
328
        }
329
      else if (command.equals("selectFirstRowChangeLead"))
330
          {
331
            selModel.moveLeadSelectionIndex(0);
332
          }
333
      else if (command.equals("selectFirstRowExtendSelection"))
334
        {
335
          selModel.setLeadSelectionIndex(0);
336
        }
337
      else if (command.equals("selectPreviousRowExtendSelection"))
338
        {
339
          selModel.setLeadSelectionIndex(Math.max(0,lead - 1));
340
        }
341
      else if (command.equals("scrollUp"))
342
        {
343
          int target;
344
          if (lead == list.getFirstVisibleIndex())
345
            {
346
              target = Math.max
347
                (0, lead - (list.getLastVisibleIndex() -
348
                    list.getFirstVisibleIndex() + 1));
349
            }
350
          else
351
            target = list.getFirstVisibleIndex();
352
          list.setSelectedIndex(target);
353
        }
354
      else if (command.equals("selectLastRowExtendSelection"))
355
        {
356
          selModel.setLeadSelectionIndex(list.getModel().getSize() - 1);
357
        }
358
      else if (command.equals("scrollDown"))
359
        {
360
          int target;
361
          if (lead == list.getLastVisibleIndex())
362
            {
363
              target = Math.min
364
                (max, lead + (list.getLastVisibleIndex() -
365
                    list.getFirstVisibleIndex() + 1));
366
            }
367
          else
368
            target = list.getLastVisibleIndex();
369
          list.setSelectedIndex(target);
370
        }
371
      else if (command.equals("selectNextRowChangeLead"))
372
          {
373
            if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
374
              selectNextIndex();
375
            else
376
              {
377
                selModel.moveLeadSelectionIndex(Math.min(max, lead + 1));
378
              }
379
          }
380
      else if (command.equals("selectPreviousRowChangeLead"))
381
        {
382
          if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
383
            selectPreviousIndex();
384
          else
385
            {
386
              selModel.moveLeadSelectionIndex(Math.max(0, lead - 1));
387
            }
388
        }
389
      else if (command.equals("addToSelection"))
390
        {
391
          list.addSelectionInterval(lead, lead);
392
        }
393
      else if (command.equals("extendTo"))
394
        {
395
          selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(),
396
                                        lead);
397
        }
398
      else if (command.equals("toggleAndAnchor"))
399
        {
400
          if (!list.isSelectedIndex(lead))
401
            list.addSelectionInterval(lead, lead);
402
          else
403
            list.removeSelectionInterval(lead, lead);
404
          selModel.setAnchorSelectionIndex(lead);
405
        }
406
      else
407
        {
408
          // DEBUG: uncomment the following line to print out 
409
          // key bindings that aren't implemented yet
410
 
411
          // System.out.println ("not implemented: "+e.getActionCommand());
412
        }
413
 
414
      list.ensureIndexIsVisible(list.getLeadSelectionIndex());
415
    }
416
  }
417
 
418
  /**
419
   * A helper class which listens for {@link MouseEvent}s
420
   * from the {@link JList}.
421
   */
422
  public class MouseInputHandler implements MouseInputListener
423
  {
424
    /**
425
     * Called when a mouse button press/release cycle completes
426
     * on the {@link JList}
427
     *
428
     * @param event The event representing the mouse click
429
     */
430
    public void mouseClicked(MouseEvent event)
431
    {
432
      Point click = event.getPoint();
433
      int index = locationToIndex(list, click);
434
      if (index == -1)
435
        return;
436
      if (event.isShiftDown())
437
        {
438
          if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
439
            list.setSelectedIndex(index);
440
          else if (list.getSelectionMode() ==
441
                   ListSelectionModel.SINGLE_INTERVAL_SELECTION)
442
            // COMPAT: the IBM VM is compatible with the following line of code.
443
            // However, compliance with Sun's VM would correspond to replacing 
444
            // getAnchorSelectionIndex() with getLeadSelectionIndex().This is 
445
            // both unnatural and contradictory to the way they handle other 
446
            // similar UI interactions.
447
            list.setSelectionInterval(list.getAnchorSelectionIndex(), index);
448
          else
449
            // COMPAT: both Sun and IBM are compatible instead with:
450
            // list.setSelectionInterval
451
            //     (list.getLeadSelectionIndex(),index);
452
            // Note that for IBM this is contradictory to what they did in 
453
            // the above situation for SINGLE_INTERVAL_SELECTION.  
454
            // The most natural thing to do is the following:
455
            if (list.isSelectedIndex(list.getAnchorSelectionIndex()))
456
              list.getSelectionModel().setLeadSelectionIndex(index);
457
            else
458
              list.addSelectionInterval(list.getAnchorSelectionIndex(), index);
459
        }
460
      else if (event.isControlDown())
461
        {
462
          if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
463
            list.setSelectedIndex(index);
464
          else if (list.isSelectedIndex(index))
465
            list.removeSelectionInterval(index,index);
466
          else
467
            list.addSelectionInterval(index,index);
468
        }
469
      else
470
        list.setSelectedIndex(index);
471
 
472
      list.ensureIndexIsVisible(list.getLeadSelectionIndex());
473
    }
474
 
475
    /**
476
     * Called when a mouse button is pressed down on the
477
     * {@link JList}.
478
     *
479
     * @param event The event representing the mouse press
480
     */
481
    public void mousePressed(MouseEvent event)
482
    {
483
      // TODO: What should be done here, if anything?
484
    }
485
 
486
    /**
487
     * Called when a mouse button is released on
488
     * the {@link JList}
489
     *
490
     * @param event The event representing the mouse press
491
     */
492
    public void mouseReleased(MouseEvent event)
493
    {
494
      // TODO: What should be done here, if anything?
495
    }
496
 
497
    /**
498
     * Called when the mouse pointer enters the area bounded
499
     * by the {@link JList}
500
     *
501
     * @param event The event representing the mouse entry
502
     */
503
    public void mouseEntered(MouseEvent event)
504
    {
505
      // TODO: What should be done here, if anything?
506
    }
507
 
508
    /**
509
     * Called when the mouse pointer leaves the area bounded
510
     * by the {@link JList}
511
     *
512
     * @param event The event representing the mouse exit
513
     */
514
    public void mouseExited(MouseEvent event)
515
    {
516
      // TODO: What should be done here, if anything?
517
    }
518
 
519
    /**
520
     * Called when the mouse pointer moves over the area bounded
521
     * by the {@link JList} while a button is held down.
522
     *
523
     * @param event The event representing the mouse drag
524
     */
525
    public void mouseDragged(MouseEvent event)
526
    {
527
      // TODO: What should be done here, if anything?
528
    }
529
 
530
    /**
531
     * Called when the mouse pointer moves over the area bounded
532
     * by the {@link JList}.
533
     *
534
     * @param event The event representing the mouse move
535
     */
536
    public void mouseMoved(MouseEvent event)
537
    {
538
      // TODO: What should be done here, if anything?
539
    }
540
  }
541
 
542
  /**
543
   * Helper class which listens to {@link PropertyChangeEvent}s
544
   * from the {@link JList}.
545
   */
546
  public class PropertyChangeHandler implements PropertyChangeListener
547
  {
548
    /**
549
     * Called when the {@link JList} changes one of its bound properties.
550
     *
551
     * @param e The event representing the property change
552
     */
553
    public void propertyChange(PropertyChangeEvent e)
554
    {
555
      if (e.getSource() == BasicListUI.this.list)
556
        {
557
          if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
558
            ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener);
559
 
560
          if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
561
            ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener);
562
        }
563
      // Update the updateLayoutStateNeeded flag.
564
      if (e.getPropertyName().equals("model"))
565
        updateLayoutStateNeeded += modelChanged;
566
      else if (e.getPropertyName().equals("selectionModel"))
567
        updateLayoutStateNeeded += selectionModelChanged;
568
      else if (e.getPropertyName().equals("font"))
569
        updateLayoutStateNeeded += fontChanged;
570
      else if (e.getPropertyName().equals("fixedCellWidth"))
571
        updateLayoutStateNeeded += fixedCellWidthChanged;
572
      else if (e.getPropertyName().equals("fixedCellHeight"))
573
        updateLayoutStateNeeded += fixedCellHeightChanged;
574
      else if (e.getPropertyName().equals("prototypeCellValue"))
575
        updateLayoutStateNeeded += prototypeCellValueChanged;
576
      else if (e.getPropertyName().equals("cellRenderer"))
577
        updateLayoutStateNeeded += cellRendererChanged;
578
      BasicListUI.this.damageLayout();
579
    }
580
  }
581
 
582
  /**
583
   * A constant to indicate that the model has changed.
584
   */
585
  protected static final int modelChanged = 1;
586
 
587
  /**
588
   * A constant to indicate that the selection model has changed.
589
   */
590
  protected static final int selectionModelChanged = 2;
591
 
592
  /**
593
   * A constant to indicate that the font has changed.
594
   */
595
  protected static final int fontChanged = 4;
596
 
597
  /**
598
   * A constant to indicate that the fixedCellWidth has changed.
599
   */
600
  protected static final int fixedCellWidthChanged = 8;
601
 
602
  /**
603
   * A constant to indicate that the fixedCellHeight has changed.
604
   */
605
  protected static final int fixedCellHeightChanged = 16;
606
 
607
  /**
608
   * A constant to indicate that the prototypeCellValue has changed.
609
   */
610
  protected static final int prototypeCellValueChanged = 32;
611
 
612
  /**
613
   * A constant to indicate that the cellRenderer has changed.
614
   */
615
  protected static final int cellRendererChanged = 64;
616
 
617
  /**
618
   * Creates a new BasicListUI for the component.
619
   *
620
   * @param c The component to create a UI for
621
   *
622
   * @return A new UI
623
   */
624
  public static ComponentUI createUI(final JComponent c)
625
  {
626
    return new BasicListUI();
627
  }
628
 
629
  /** The current focus listener. */
630
  protected FocusListener focusListener;
631
 
632
  /** The data listener listening to the model. */
633
  protected ListDataListener listDataListener;
634
 
635
  /** The selection listener listening to the selection model. */
636
  protected ListSelectionListener listSelectionListener;
637
 
638
  /** The mouse listener listening to the list. */
639
  protected MouseInputListener mouseInputListener;
640
 
641
  /** The property change listener listening to the list. */
642
  protected PropertyChangeListener propertyChangeListener;
643
 
644
 
645
  /** The component listener that receives notification for resizing the
646
   * JList component.*/
647
  private ComponentListener componentListener;
648
 
649
  /** Saved reference to the list this UI was created for. */
650
  protected JList list;
651
 
652
  /**
653
   * The height of a single cell in the list. This field is used when the
654
   * fixedCellHeight property of the list is set. Otherwise this field is
655
   * set to <code>-1</code> and {@link #cellHeights} is used instead.
656
   */
657
  protected int cellHeight;
658
 
659
  /** The width of a single cell in the list. */
660
  protected int cellWidth;
661
 
662
  /**
663
   * An array of varying heights of cells in the list, in cases where each
664
   * cell might have a different height. This field is used when the
665
   * <code>fixedCellHeight</code> property of the list is not set. Otherwise
666
   * this field is <code>null</code> and {@link #cellHeight} is used.
667
   */
668
  protected int[] cellHeights;
669
 
670
  /**
671
   * A bitmask that indicates which properties of the JList have changed.
672
   * When nonzero, indicates that the UI class is out of
673
   * date with respect to the underlying list, and must recalculate the
674
   * list layout before painting or performing size calculations.
675
   *
676
   * @see #modelChanged
677
   * @see #selectionModelChanged
678
   * @see #fontChanged
679
   * @see #fixedCellWidthChanged
680
   * @see #fixedCellHeightChanged
681
   * @see #prototypeCellValueChanged
682
   * @see #cellRendererChanged
683
   */
684
  protected int updateLayoutStateNeeded;
685
 
686
  /**
687
   * The {@link CellRendererPane} that is used for painting.
688
   */
689
  protected CellRendererPane rendererPane;
690
 
691
  /** The action bound to KeyStrokes. */
692
  ListAction action;
693
 
694
  /**
695
   * Calculate the height of a particular row. If there is a fixed {@link
696
   * #cellHeight}, return it; otherwise return the specific row height
697
   * requested from the {@link #cellHeights} array. If the requested row
698
   * is invalid, return <code>-1</code>.
699
   *
700
   * @param row The row to get the height of
701
   *
702
   * @return The height, in pixels, of the specified row
703
   */
704
  protected int getRowHeight(int row)
705
  {
706
    int height;
707
    if (cellHeights == null)
708
      height = cellHeight;
709
    else
710
      {
711
        if (row < 0 || row >= cellHeights.length)
712
          height = -1;
713
        else
714
          height = cellHeights[row];
715
      }
716
    return height;
717
  }
718
 
719
  /**
720
   * Calculate the bounds of a particular cell, considering the upper left
721
   * corner of the list as the origin position <code>(0,0)</code>.
722
   *
723
   * @param l Ignored; calculates over <code>this.list</code>
724
   * @param index1 The first row to include in the bounds
725
   * @param index2 The last row to incude in the bounds
726
   *
727
   * @return A rectangle encompassing the range of rows between
728
   * <code>index1</code> and <code>index2</code> inclusive
729
   */
730
  public Rectangle getCellBounds(JList l, int index1, int index2)
731
  {
732
    maybeUpdateLayoutState();
733
 
734
    if (l != list || cellWidth == -1)
735
      return null;
736
 
737
    int minIndex = Math.min(index1, index2);
738
    int maxIndex = Math.max(index1, index2);
739
    Point loc = indexToLocation(list, minIndex);
740
    Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth,
741
                                     getRowHeight(minIndex));
742
 
743
    for (int i = minIndex + 1; i <= maxIndex; i++)
744
      {
745
        Point hiLoc = indexToLocation(list, i);
746
        Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth,
747
                                       getRowHeight(i));
748
        bounds = bounds.union(hibounds);
749
      }
750
 
751
    return bounds;
752
  }
753
 
754
  /**
755
   * Calculate the Y coordinate of the upper edge of a particular row,
756
   * considering the Y coordinate <code>0</code> to occur at the top of the
757
   * list.
758
   *
759
   * @param row The row to calculate the Y coordinate of
760
   *
761
   * @return The Y coordinate of the specified row, or <code>-1</code> if
762
   * the specified row number is invalid
763
   */
764
  protected int convertRowToY(int row)
765
  {
766
    int y = 0;
767
    for (int i = 0; i < row; ++i)
768
      {
769
        int h = getRowHeight(i);
770
        if (h == -1)
771
          return -1;
772
        y += h;
773
      }
774
    return y;
775
  }
776
 
777
  /**
778
   * Calculate the row number containing a particular Y coordinate,
779
   * considering the Y coodrinate <code>0</code> to occur at the top of the
780
   * list.
781
   *
782
   * @param y0 The Y coordinate to calculate the row number for
783
   *
784
   * @return The row number containing the specified Y value, or <code>-1</code>
785
   *         if the list model is empty
786
   *
787
   * @specnote This method is specified to return -1 for an invalid Y
788
   *           coordinate. However, some simple tests show that the behaviour
789
   *           is to return the index of the last list element for an Y
790
   *           coordinate that lies outside of the list bounds (even for
791
   *           negative indices). <code>-1</code>
792
   *           is only returned if the list model is empty.
793
   */
794
  protected int convertYToRow(int y0)
795
  {
796
    if (list.getModel().getSize() == 0)
797
      return -1;
798
 
799
    // When y0 < 0, then the JDK returns the maximum row index of the list. So
800
    // do we.
801
    if (y0 < 0)
802
      return list.getModel().getSize() - 1;
803
 
804
    // Update the layout if necessary.
805
    maybeUpdateLayoutState();
806
 
807
    int index = list.getModel().getSize() - 1;;
808
 
809
    // If a fixed cell height is set, then we can work more efficient.
810
    if (cellHeight > 0)
811
      index = Math.min(y0 / cellHeight, index);
812
    // If we have no fixed cell height, we must add up each cell height up
813
    // to y0.
814
    else
815
      {
816
        int h = 0;
817
        for (int row = 0; row < cellHeights.length; ++row)
818
          {
819
            h += cellHeights[row];
820
            if (y0 < h)
821
              {
822
                index = row;
823
                break;
824
              }
825
          }
826
      }
827
    return index;
828
  }
829
 
830
  /**
831
   * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
832
   * #cellWidth} properties by examining the variouis properties of the
833
   * {@link JList}.
834
   */
835
  protected void updateLayoutState()
836
  {
837
    int nrows = list.getModel().getSize();
838
    cellHeight = -1;
839
    cellWidth = -1;
840
    if (cellHeights == null || cellHeights.length != nrows)
841
      cellHeights = new int[nrows];
842
    ListCellRenderer rend = list.getCellRenderer();
843
    // Update the cellHeight(s) fields.
844
    int fixedCellHeight = list.getFixedCellHeight();
845
    if (fixedCellHeight > 0)
846
      {
847
        cellHeight = fixedCellHeight;
848
        cellHeights = null;
849
      }
850
    else
851
      {
852
        cellHeight = -1;
853
        for (int i = 0; i < nrows; ++i)
854
          {
855
            Component flyweight =
856
              rend.getListCellRendererComponent(list,
857
                      list.getModel().getElementAt(i),
858
                      i, list.isSelectedIndex(i),
859
                      list.getSelectionModel().getAnchorSelectionIndex() == i);
860
            Dimension dim = flyweight.getPreferredSize();
861
            cellHeights[i] = dim.height;
862
          }
863
      }
864
 
865
    // Update the cellWidth field.
866
    int fixedCellWidth = list.getFixedCellWidth();
867
    if (fixedCellWidth > 0)
868
      cellWidth = fixedCellWidth;
869
    else
870
      {
871
        for (int i = 0; i < nrows; ++i)
872
          {
873
            Component flyweight =
874
              rend.getListCellRendererComponent(list,
875
                                                list.getModel().getElementAt(i),
876
                                                i, list.isSelectedIndex(i),
877
                                                list.getSelectionModel().getAnchorSelectionIndex() == i);
878
            Dimension dim = flyweight.getPreferredSize();
879
            cellWidth = Math.max(cellWidth, dim.width);
880
          }
881
        if (list.getLayoutOrientation() == JList.VERTICAL)
882
          cellWidth = Math.max(cellWidth, list.getSize().width);
883
      }
884
  }
885
 
886
  /**
887
   * Marks the current layout as damaged and requests revalidation from the
888
   * JList.
889
   * This is package-private to avoid an accessor method.
890
   *
891
   * @see #updateLayoutStateNeeded
892
   */
893
  void damageLayout()
894
  {
895
    updateLayoutStateNeeded = 1;
896
  }
897
 
898
  /**
899
   * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
900
   * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
901
   */
902
  protected void maybeUpdateLayoutState()
903
  {
904
    if (updateLayoutStateNeeded != 0)
905
      {
906
        updateLayoutState();
907
        updateLayoutStateNeeded = 0;
908
      }
909
  }
910
 
911
  /**
912
   * Creates a new BasicListUI object.
913
   */
914
  public BasicListUI()
915
  {
916
    updateLayoutStateNeeded = 1;
917
    rendererPane = new CellRendererPane();
918
  }
919
 
920
  /**
921
   * Installs various default settings (mostly colors) from the {@link
922
   * UIDefaults} into the {@link JList}
923
   *
924
   * @see #uninstallDefaults
925
   */
926
  protected void installDefaults()
927
  {
928
    LookAndFeel.installColorsAndFont(list, "List.background",
929
                                     "List.foreground", "List.font");
930
    list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
931
    list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
932
    list.setOpaque(true);
933
  }
934
 
935
  /**
936
   * Resets to <code>null</code> those defaults which were installed in
937
   * {@link #installDefaults}
938
   */
939
  protected void uninstallDefaults()
940
  {
941
    list.setForeground(null);
942
    list.setBackground(null);
943
    list.setSelectionForeground(null);
944
    list.setSelectionBackground(null);
945
  }
946
 
947
  /**
948
   * Attaches all the listeners we have in the UI class to the {@link
949
   * JList}, its model and its selection model.
950
   *
951
   * @see #uninstallListeners
952
   */
953
  protected void installListeners()
954
  {
955
    if (focusListener == null)
956
      focusListener = createFocusListener();
957
    list.addFocusListener(focusListener);
958
    if (listDataListener == null)
959
      listDataListener = createListDataListener();
960
    list.getModel().addListDataListener(listDataListener);
961
    if (listSelectionListener == null)
962
      listSelectionListener = createListSelectionListener();
963
    list.addListSelectionListener(listSelectionListener);
964
    if (mouseInputListener == null)
965
      mouseInputListener = createMouseInputListener();
966
    list.addMouseListener(mouseInputListener);
967
    list.addMouseMotionListener(mouseInputListener);
968
    if (propertyChangeListener == null)
969
      propertyChangeListener = createPropertyChangeListener();
970
    list.addPropertyChangeListener(propertyChangeListener);
971
 
972
    // FIXME: Are these two really needed? At least they are not documented.
973
    //keyListener = new KeyHandler();
974
    componentListener = new ComponentHandler();
975
    list.addComponentListener(componentListener);
976
    //list.addKeyListener(keyListener);
977
  }
978
 
979
  /**
980
   * Detaches all the listeners we attached in {@link #installListeners}.
981
   */
982
  protected void uninstallListeners()
983
  {
984
    list.removeFocusListener(focusListener);
985
    list.getModel().removeListDataListener(listDataListener);
986
    list.removeListSelectionListener(listSelectionListener);
987
    list.removeMouseListener(mouseInputListener);
988
    //list.removeKeyListener(keyListener);
989
    list.removeMouseMotionListener(mouseInputListener);
990
    list.removePropertyChangeListener(propertyChangeListener);
991
  }
992
 
993
  /**
994
   * Installs keyboard actions for this UI in the {@link JList}.
995
   */
996
  protected void installKeyboardActions()
997
  {
998
    InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap");
999
    InputMapUIResource parentInputMap = new InputMapUIResource();
1000
    // FIXME: The JDK uses a LazyActionMap for parentActionMap
1001
    ActionMap parentActionMap = new ActionMapUIResource();
1002
    action = new ListAction();
1003
    Object keys[] = focusInputMap.allKeys();
1004
    // Register key bindings in the UI InputMap-ActionMap pair
1005
    for (int i = 0; i < keys.length; i++)
1006
      {
1007
        KeyStroke stroke = (KeyStroke)keys[i];
1008
        String actionString = (String) focusInputMap.get(stroke);
1009
        parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(),
1010
                                                  stroke.getModifiers()),
1011
                           actionString);
1012
 
1013
        parentActionMap.put (actionString,
1014
                             new ActionListenerProxy(action, actionString));
1015
      }
1016
    // Register the new InputMap-ActionMap as the parents of the list's
1017
    // InputMap and ActionMap
1018
    parentInputMap.setParent(list.getInputMap().getParent());
1019
    parentActionMap.setParent(list.getActionMap().getParent());
1020
    list.getInputMap().setParent(parentInputMap);
1021
    list.getActionMap().setParent(parentActionMap);
1022
  }
1023
 
1024
  /**
1025
   * Uninstalls keyboard actions for this UI in the {@link JList}.
1026
   */
1027
  protected void uninstallKeyboardActions()
1028
  {
1029
    // TODO: Implement this properly.
1030
  }
1031
 
1032
  /**
1033
   * Installs the various aspects of the UI in the {@link JList}. In
1034
   * particular, calls {@link #installDefaults}, {@link #installListeners}
1035
   * and {@link #installKeyboardActions}. Also saves a reference to the
1036
   * provided component, cast to a {@link JList}.
1037
   *
1038
   * @param c The {@link JList} to install the UI into
1039
   */
1040
  public void installUI(final JComponent c)
1041
  {
1042
    super.installUI(c);
1043
    list = (JList) c;
1044
    installDefaults();
1045
    installListeners();
1046
    installKeyboardActions();
1047
    maybeUpdateLayoutState();
1048
  }
1049
 
1050
  /**
1051
   * Uninstalls all the aspects of the UI which were installed in {@link
1052
   * #installUI}. When finished uninstalling, drops the saved reference to
1053
   * the {@link JList}.
1054
   *
1055
   * @param c Ignored; the UI is uninstalled from the {@link JList}
1056
   * reference saved during the call to {@link #installUI}
1057
   */
1058
  public void uninstallUI(final JComponent c)
1059
  {
1060
    uninstallKeyboardActions();
1061
    uninstallListeners();
1062
    uninstallDefaults();
1063
    list = null;
1064
  }
1065
 
1066
  /**
1067
   * Gets the size this list would prefer to assume. This is calculated by
1068
   * calling {@link #getCellBounds} over the entire list.
1069
   *
1070
   * @param c Ignored; uses the saved {@link JList} reference
1071
   *
1072
   * @return DOCUMENT ME!
1073
   */
1074
  public Dimension getPreferredSize(JComponent c)
1075
  {
1076
    int size = list.getModel().getSize();
1077
    if (size == 0)
1078
      return new Dimension(0, 0);
1079
    int visibleRows = list.getVisibleRowCount();
1080
    int layoutOrientation = list.getLayoutOrientation();
1081
    Rectangle bounds = getCellBounds(list, 0, list.getModel().getSize() - 1);
1082
    Dimension retVal = bounds.getSize();
1083
    Component parent = list.getParent();
1084
    if ((visibleRows == -1) && (parent instanceof JViewport))
1085
      {
1086
        JViewport viewport = (JViewport) parent;
1087
 
1088
        if (layoutOrientation == JList.HORIZONTAL_WRAP)
1089
          {
1090
            int h = viewport.getSize().height;
1091
            int cellsPerCol = h / cellHeight;
1092
            int w = size / cellsPerCol * cellWidth;
1093
            retVal = new Dimension(w, h);
1094
          }
1095
        else if (layoutOrientation == JList.VERTICAL_WRAP)
1096
          {
1097
            int w = viewport.getSize().width;
1098
            int cellsPerRow = Math.max(w / cellWidth, 1);
1099
            int h = size / cellsPerRow * cellHeight;
1100
            retVal = new Dimension(w, h);
1101
          }
1102
      }
1103
    return retVal;
1104
  }
1105
 
1106
  /**
1107
   * Paints a single cell in the list.
1108
   *
1109
   * @param g The graphics context to paint in
1110
   * @param row The row number to paint
1111
   * @param bounds The bounds of the cell to paint, assuming a coordinate
1112
   * system beginning at <code>(0,0)</code> in the upper left corner of the
1113
   * list
1114
   * @param rend A cell renderer to paint with
1115
   * @param data The data to provide to the cell renderer
1116
   * @param sel A selection model to provide to the cell renderer
1117
   * @param lead The lead selection index of the list
1118
   */
1119
  protected void paintCell(Graphics g, int row, Rectangle bounds,
1120
                 ListCellRenderer rend, ListModel data,
1121
                 ListSelectionModel sel, int lead)
1122
  {
1123
    boolean isSel = list.isSelectedIndex(row);
1124
    boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus();
1125
    Component comp = rend.getListCellRendererComponent(list,
1126
                                                       data.getElementAt(row),
1127
                                                       0, isSel, hasFocus);
1128
    rendererPane.paintComponent(g, comp, list, bounds);
1129
  }
1130
 
1131
  /**
1132
   * Paints the list by repeatedly calling {@link #paintCell} for each visible
1133
   * cell in the list.
1134
   *
1135
   * @param g The graphics context to paint with
1136
   * @param c Ignored; uses the saved {@link JList} reference
1137
   */
1138
  public void paint(Graphics g, JComponent c)
1139
  {
1140
    int nrows = list.getModel().getSize();
1141
    if (nrows == 0)
1142
      return;
1143
 
1144
    maybeUpdateLayoutState();
1145
    ListCellRenderer render = list.getCellRenderer();
1146
    ListModel model = list.getModel();
1147
    ListSelectionModel sel = list.getSelectionModel();
1148
    int lead = sel.getLeadSelectionIndex();
1149
    Rectangle clip = g.getClipBounds();
1150
 
1151
    int startIndex = list.locationToIndex(new Point(clip.x, clip.y));
1152
    int endIndex = list.locationToIndex(new Point(clip.x + clip.width,
1153
                                                  clip.y + clip.height));
1154
 
1155
    for (int row = startIndex; row <= endIndex; ++row)
1156
      {
1157
        Rectangle bounds = getCellBounds(list, row, row);
1158
        if (bounds.intersects(clip))
1159
          paintCell(g, row, bounds, render, model, sel, lead);
1160
      }
1161
  }
1162
 
1163
  /**
1164
   * Computes the index of a list cell given a point within the list. If the
1165
   * location lies outside the bounds of the list, the greatest index in the
1166
   * list model is returned.
1167
   *
1168
   * @param list the list which on which the computation is based on
1169
   * @param location the coordinates
1170
   *
1171
   * @return the index of the list item that is located at the given
1172
   *         coordinates or <code>-1</code> if the list model is empty
1173
   */
1174
  public int locationToIndex(JList list, Point location)
1175
  {
1176
    int layoutOrientation = list.getLayoutOrientation();
1177
    int index = -1;
1178
    switch (layoutOrientation)
1179
      {
1180
      case JList.VERTICAL:
1181
        index = convertYToRow(location.y);
1182
        break;
1183
      case JList.HORIZONTAL_WRAP:
1184
        // determine visible rows and cells per row
1185
        int visibleRows = list.getVisibleRowCount();
1186
        int cellsPerRow = -1;
1187
        int numberOfItems = list.getModel().getSize();
1188
        Dimension listDim = list.getSize();
1189
        if (visibleRows <= 0)
1190
          {
1191
            try
1192
              {
1193
                cellsPerRow = listDim.width / cellWidth;
1194
              }
1195
            catch (ArithmeticException ex)
1196
              {
1197
                cellsPerRow = 1;
1198
              }
1199
          }
1200
        else
1201
          {
1202
            cellsPerRow = numberOfItems / visibleRows + 1;
1203
          }
1204
 
1205
        // determine index for the given location
1206
        int cellsPerColumn = numberOfItems / cellsPerRow + 1;
1207
        int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
1208
        int gridY = Math.min(location.y / cellHeight, cellsPerColumn);
1209
        index = gridX + gridY * cellsPerRow;
1210
        break;
1211
      case JList.VERTICAL_WRAP:
1212
        // determine visible rows and cells per column
1213
        int visibleRows2 = list.getVisibleRowCount();
1214
        if (visibleRows2 <= 0)
1215
          {
1216
            Dimension listDim2 = list.getSize();
1217
            visibleRows2 = listDim2.height / cellHeight;
1218
          }
1219
        int numberOfItems2 = list.getModel().getSize();
1220
        int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
1221
 
1222
        int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
1223
        int gridY2 = Math.min(location.y / cellHeight, visibleRows2);
1224
        index = gridY2 + gridX2 * visibleRows2;
1225
        break;
1226
      }
1227
    return index;
1228
  }
1229
 
1230
  public Point indexToLocation(JList list, int index)
1231
  {
1232
    int layoutOrientation = list.getLayoutOrientation();
1233
    Point loc = null;
1234
    switch (layoutOrientation)
1235
      {
1236
      case JList.VERTICAL:
1237
        loc = new Point(0, convertRowToY(index));
1238
        break;
1239
      case JList.HORIZONTAL_WRAP:
1240
        // determine visible rows and cells per row
1241
        int visibleRows = list.getVisibleRowCount();
1242
        int numberOfCellsPerRow = -1;
1243
        if (visibleRows <= 0)
1244
          {
1245
            Dimension listDim = list.getSize();
1246
            numberOfCellsPerRow = Math.max(listDim.width / cellWidth, 1);
1247
          }
1248
        else
1249
          {
1250
            int numberOfItems = list.getModel().getSize();
1251
            numberOfCellsPerRow = numberOfItems / visibleRows + 1;
1252
          }
1253
        // compute coordinates inside the grid
1254
        int gridX = index % numberOfCellsPerRow;
1255
        int gridY = index / numberOfCellsPerRow;
1256
        int locX = gridX * cellWidth;
1257
        int locY = gridY * cellHeight;
1258
        loc = new Point(locX, locY);
1259
        break;
1260
      case JList.VERTICAL_WRAP:
1261
        // determine visible rows and cells per column
1262
        int visibleRows2 = list.getVisibleRowCount();
1263
        if (visibleRows2 <= 0)
1264
          {
1265
            Dimension listDim2 = list.getSize();
1266
            visibleRows2 = listDim2.height / cellHeight;
1267
          }
1268
        // compute coordinates inside the grid
1269
        if (visibleRows2 > 0)
1270
          {
1271
            int gridY2 = index % visibleRows2;
1272
            int gridX2 = index / visibleRows2;
1273
            int locX2 = gridX2 * cellWidth;
1274
            int locY2 = gridY2 * cellHeight;
1275
            loc = new Point(locX2, locY2);
1276
          }
1277
        else
1278
          loc = new Point(0, convertRowToY(index));
1279
        break;
1280
      }
1281
    return loc;
1282
  }
1283
 
1284
  /**
1285
   * Creates and returns the focus listener for this UI.
1286
   *
1287
   * @return the focus listener for this UI
1288
   */
1289
  protected FocusListener createFocusListener()
1290
  {
1291
    return new FocusHandler();
1292
  }
1293
 
1294
  /**
1295
   * Creates and returns the list data listener for this UI.
1296
   *
1297
   * @return the list data listener for this UI
1298
   */
1299
  protected ListDataListener createListDataListener()
1300
  {
1301
    return new ListDataHandler();
1302
  }
1303
 
1304
  /**
1305
   * Creates and returns the list selection listener for this UI.
1306
   *
1307
   * @return the list selection listener for this UI
1308
   */
1309
  protected ListSelectionListener createListSelectionListener()
1310
  {
1311
    return new ListSelectionHandler();
1312
  }
1313
 
1314
  /**
1315
   * Creates and returns the mouse input listener for this UI.
1316
   *
1317
   * @return the mouse input listener for this UI
1318
   */
1319
  protected MouseInputListener createMouseInputListener()
1320
  {
1321
    return new MouseInputHandler();
1322
  }
1323
 
1324
  /**
1325
   * Creates and returns the property change listener for this UI.
1326
   *
1327
   * @return the property change listener for this UI
1328
   */
1329
  protected PropertyChangeListener createPropertyChangeListener()
1330
  {
1331
    return new PropertyChangeHandler();
1332
  }
1333
 
1334
  /**
1335
   * Selects the next list item and force it to be visible.
1336
   */
1337
  protected void selectNextIndex()
1338
  {
1339
    int index = list.getSelectionModel().getLeadSelectionIndex();
1340
    if (index < list.getModel().getSize() - 1)
1341
      {
1342
        index++;
1343
        list.setSelectedIndex(index);
1344
      }
1345
    list.ensureIndexIsVisible(index);
1346
  }
1347
 
1348
  /**
1349
   * Selects the previous list item and force it to be visible.
1350
   */
1351
  protected void selectPreviousIndex()
1352
  {
1353
    int index = list.getSelectionModel().getLeadSelectionIndex();
1354
    if (index > 0)
1355
      {
1356
        index--;
1357
        list.setSelectedIndex(index);
1358
      }
1359
    list.ensureIndexIsVisible(index);
1360
  }
1361
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.