1/*
2 * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package test.java.awt.event.helpers.lwcomponents;
25
26import java.awt.*;
27import java.awt.event.*;
28import java.util.Vector;
29import java.util.Enumeration;
30
31/**
32 * Remarks : Source for LightWeight component - List.
33 *
34 * Scroll bar support is not available for this component, so if the
35 * items exceeds visibility those items will be truncated. Also, here
36 * double buffering is not used so there will be little bit flickering
37 * while it repaints. Item listener support is not enabled in this
38 * component. Listeners handled were Mouse, Key and Focus.
39 *
40 * @author R.Govindarajan (govind@siptech.co.in), G.N.V.Sekhar (sekharv@siptech.co.in)
41 */
42
43public class LWList extends LWComponent implements ItemSelectable {
44
45  // Constants used for component size
46  private final int MIN_WIDTH   = 100;
47  private final int MIN_HEIGHT  = 100;
48  private final int PREF_WIDTH  = 100;
49  private final int PREF_HEIGHT = 100;
50
51  // Constants used for setting color for component
52  private final Color BACK_COLOR          = Color.white;
53  private final Color FRONT_COLOR         = Color.black;
54  private final Color BORDER_COLOR        = Color.darkGray;
55  private final Color FOCUS_COLOR         = Color.blue;
56  private final Color FOCUS_FORECOLOR     = Color.white;
57  private final Color FOCUS_ENABLED_COLOR = Color.red;
58  private final int BORDER_WIDTH = 2;
59
60  private Vector stringList;  // List of items
61  private Vector selList;     // List of selected items
62  private int rows;           // Visible rows
63  private int focusIndex, prevfocusIndex;
64  private Dimension minSize;
65  private Dimension prefSize;
66  private boolean pressed, eventOccurred, focusEnabled;
67  private boolean multipleMode;
68
69  // Listeners handled for this component
70  private ActionListener actionListener;
71  private KeyListener    keyListener;
72  private FocusListener  focusListener;
73  private ItemListener   itemListener;
74
75  private static int nameCounter = 0;
76
77  /**
78   * Creates a new list.
79   */
80  public LWList() {
81    this(0);
82  }
83
84  /**
85   * Creates a new list with the specified number of rows;
86   * multiple selection mode is disabled.
87   *
88   * @param i  the number of rows
89   */
90  public LWList(int i) {
91    this(i, false);
92  }
93
94  /**
95   * Creates a new list with the specified number of rows and multiple selection mode.
96   *
97   * @param rows  the number of rows
98   * @param flag  determines whether the list allows multiple selections
99   */
100  public LWList(int rows, boolean flag) {
101    multipleMode        = flag;
102    this.rows           = rows;
103    minSize             = new Dimension(MIN_WIDTH, MIN_HEIGHT);
104    prefSize            = new Dimension(PREF_WIDTH, PREF_HEIGHT);
105    stringList          = new Vector();
106    selList             = new Vector();
107    selList.addElement(0);
108    focusIndex          = -1;
109    prevfocusIndex      = focusIndex;
110    enableEvents(AWTEvent.MOUSE_EVENT_MASK);
111    enableEvents(AWTEvent.KEY_EVENT_MASK);
112    enableEvents(AWTEvent.FOCUS_EVENT_MASK);
113    enableEvents(AWTEvent.ITEM_EVENT_MASK);
114    setName(makeComponentName()); // set the name to the component
115  }
116
117  String makeComponentName() {
118    String s = "LWList" + nameCounter++;
119    return s;
120  }
121
122  /**
123   * Set whether the component is enabled or not.
124   * @param enabled  if {@code true}, the component is to be enabled
125   */
126  @Override
127  public void setEnabled(boolean enabled) {
128    super.setEnabled(enabled);
129
130    if (enabled) {
131      enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
132    } else {
133      disableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
134    }
135    repaint(1);
136  }
137
138  /**
139   * Set the selection mode.
140   *
141   * @param flag  determines whether the list allows multiple selections
142   */
143  public void setSelectionMode(boolean flag) {
144    multipleMode = flag;
145  }
146
147  /**
148   * Check if the list allows multiple selections.
149   *
150   * @return  {@code true} if the list allows multiple selections
151   */
152  public boolean isMultipleMode() {
153    return multipleMode;
154  }
155
156  /**
157   * Add the specified item.
158   *
159   * @param listItem  the item
160   */
161  public void add(String listItem) {
162    stringList.addElement(listItem);
163    invalidate();
164    repaint();
165  }
166
167  /**
168   * Get minimum dimension for the list.
169   *
170   * @return  the minimum dimensions for displaying
171   */
172  @Override
173  public Dimension getMinimumSize() {
174    return minSize;
175  }
176
177  /**
178   * Get the preferred size of the list.
179   *
180   * @return  the preferred dimensions for displaying
181   */
182  @Override
183  public Dimension getPreferredSize() {
184    return prefSize;
185  }
186
187  /**
188   * Get the background color for the component.
189   *
190   * @return  the background color for the component
191   */
192  @Override
193  public Color getBackground() {
194    return BACK_COLOR;
195  }
196
197  /**
198   * Get the foreground color for the component.
199   *
200   * @return  the foreground color for the component
201   */
202  @Override
203  public Color getForeground() {
204    return FRONT_COLOR;
205  }
206
207  /**
208   * Get the border color for the component.
209   *
210   * @return  the border color for the component
211   */
212  public Color getBorder() {
213    return BORDER_COLOR;
214  }
215
216  /**
217   * Get background color for the selected item.
218   *
219   * @return  the color for the selected item
220   */
221  public Color getFocusColor() {
222    return FOCUS_COLOR;
223  }
224
225  /**
226   * Get foreground color for the selected item.
227   *
228   * @return  the foreground color for the selected item
229   */
230  public Color getFocusForeColor() {
231    return FOCUS_FORECOLOR;
232  }
233
234  /**
235   * Get a "focus enabled" color - a small rectangle around the item
236   * should be drawn when the component got the focus.
237   *
238   * @return  the "focus enabled" color
239   */
240  public Color getFocusEnabledColor() {
241    return FOCUS_ENABLED_COLOR;
242  }
243
244  /**
245   * Get border width.
246   *
247   * @return  the border width
248   */
249  public int getBorderWidth() {
250    return BORDER_WIDTH;
251  }
252
253  /**
254   * Get the list item count.
255   *
256   * @return  the count of items
257   */
258  public int getItemCount() {
259    return stringList.size();
260  }
261
262  /**
263   * Get the specified item from the list.
264   *
265   * @param index  the index
266   * @return  the item string
267   */
268  public String getItem(int index) {
269    return (String)stringList.elementAt(index);
270  }
271
272  /**
273   * Get array of items from the list.
274   *
275   * @return  the array of item strings
276   */
277  public String[] getItems() {
278    String str[] = new String[getItemCount()];
279    int count = 0;
280    for (Enumeration e = stringList.elements(); e.hasMoreElements(); ) {
281      str[count++] = (String)e.nextElement();
282    }
283    return str;
284  }
285
286  /**
287   * Check whether the component can be a focus owner (explicitly enabled here).
288   *
289   * @return {@code true} if the component is focusable
290   */
291  @Override
292  public boolean isFocusTraversable() {
293    return true;
294  }
295
296  /**
297   * Check whether mouse click point lies within the list of items.
298   *
299   * @param pt  the click point
300   * @return  {@code true} if the click point lies within the list of items
301   */
302  @Override
303  public boolean contains(Point pt) {
304    Rectangle rect = new Rectangle();
305    Dimension d = getSize();
306    rect.x = getBorderWidth();
307    rect.y = getBorderWidth();
308    rect.width  = d.width  - (getBorderWidth() * 2);
309    rect.height = d.height - (getBorderWidth() * 2);
310    return rect.contains(pt);
311  }
312
313  /**
314   * Given a click point the item that has to be selected is found from the list
315   * and focusIndex variable is set accordingly.
316   *
317   * @param pt  the click point
318   */
319  private void findSelectedIndex(Point pt) {
320    Font f = getFont();
321    FontMetrics fm = getFontMetrics(f);
322    focusIndex = pt.y / fm.getHeight() - 1;
323    if (multipleMode) {
324      Integer fi = focusIndex;
325      if (selList.contains(fi)) {
326        int i = selList.indexOf(fi);
327        selList.removeElementAt(i);
328      } else {
329        selList.addElement(fi);
330      }
331    }
332  }
333
334  /**
335   * Set index of the selected item.
336   *
337   * @param index  the index
338   */
339  public void setSelectedIndex(int index) {
340    prevfocusIndex = focusIndex;
341    focusIndex = index;
342  }
343
344  /**
345   * Get the selected item index.
346   *
347   * @return  the selected item index.
348   */
349  public int getSelectedIndex() {
350    return focusIndex;
351  }
352
353  /**
354   * Get an array of the selected Objects.
355   *
356   * @return  array of the Objects
357   */
358  @Override
359  public Object[] getSelectedObjects() {
360    int ai[] = getSelectedIndexes();
361    Object aobj[] = new Object[selList.size()];
362    for (int i = 0; i < selList.size(); i++) {
363      aobj[i] = stringList.elementAt(ai[i]);
364    }
365    return aobj;
366  }
367
368  /**
369   * Get an array of the selected item indices.
370   *
371   * @return  the array of the indices
372   */
373  public int[] getSelectedIndexes() {
374    int ai[] = new int[selList.size()];
375    for (int i = 0; i < selList.size(); i++) {
376      ai[i] = ((Integer)selList.elementAt(i));
377    }
378    return ai;
379  }
380
381  /**
382   * Add the specified item listener to receive item events from the list.
383   *
384   * @param itemlistener  the item listener
385   */
386  @Override
387  public synchronized void addItemListener(ItemListener itemlistener) {
388    itemListener = AWTEventMulticaster.add(itemListener, itemlistener);
389    enableEvents(AWTEvent.ITEM_EVENT_MASK);
390  }
391
392  /**
393   * Remove the specified item listener so
394   * that it no longer receives item events from this list.
395   *
396   * @param itemlistener  the item listener
397   */
398  @Override
399  public synchronized void removeItemListener(ItemListener itemlistener) {
400    itemListener = AWTEventMulticaster.remove(itemListener, itemlistener);
401  }
402
403  /**
404   * Add the specified action listener to receive action events from this list.
405   *
406   * @param listener  the action listener
407   */
408  public synchronized void addActionListener(ActionListener listener) {
409    actionListener = AWTEventMulticaster.add(actionListener, listener);
410    enableEvents(AWTEvent.MOUSE_EVENT_MASK);
411  }
412
413  /**
414   * Remove the specified action listener so
415   * that it no longer receives action events from this list.
416   *
417   * @param listener  the action listener
418   */
419  public synchronized void removeActionListener(ActionListener listener) {
420    actionListener = AWTEventMulticaster.remove(actionListener, listener);
421  }
422
423  /**
424   * Add the specified key listener to receive key events from this component.
425   *
426   * @param listener  the key listener
427   */
428  @Override
429  public synchronized void addKeyListener(KeyListener listener) {
430    keyListener = AWTEventMulticaster.add(keyListener, listener);
431    enableEvents(AWTEvent.KEY_EVENT_MASK);
432  }
433
434  /**
435   * Remove the specified key listener so
436   * that it no longer receives key events from this component.
437   *
438   * @param listener  the key listener
439   */
440  @Override
441  public synchronized void removeKeyListener(KeyListener listener) {
442    keyListener = AWTEventMulticaster.remove(keyListener, listener);
443  }
444
445  /**
446   * Add the specified focus listener to receive focus events
447   * from this component when it gains input focus.
448   *
449   * @param listener  the focus listener
450   */
451  @Override
452  public synchronized void addFocusListener(FocusListener listener) {
453    focusListener = AWTEventMulticaster.add(focusListener, listener);
454    enableEvents(AWTEvent.FOCUS_EVENT_MASK);
455  }
456
457  /**
458   * Remove the specified focus listener so
459   * that it no longer receives focus events from this component.
460   *
461   * @param listener  the focus listener
462   */
463  @Override
464  public synchronized void removeFocusListener(FocusListener listener) {
465    focusListener = AWTEventMulticaster.remove(focusListener, listener);
466  }
467
468  @Override
469  protected void processEvent(AWTEvent awtevent) {
470
471    if (awtevent instanceof FocusEvent) {
472      processFocusEvent((FocusEvent)awtevent);
473    } else if (awtevent instanceof ItemEvent) {
474      processItemEvent((ItemEvent)awtevent);
475    } else if (awtevent instanceof KeyEvent) {
476      processKeyEvent((KeyEvent)awtevent);
477    } else if (awtevent instanceof MouseEvent) {
478      switch (awtevent.getID()) {
479      case MouseEvent.MOUSE_CLICKED:
480      case MouseEvent.MOUSE_PRESSED:
481      case MouseEvent.MOUSE_RELEASED:
482      case MouseEvent.MOUSE_ENTERED:
483      case MouseEvent.MOUSE_EXITED:
484    processMouseEvent((MouseEvent)awtevent);
485    break;
486
487      case MouseEvent.MOUSE_MOVED:
488      case MouseEvent.MOUSE_DRAGGED:
489    super.processEvent((MouseEvent)awtevent);
490    break;
491      }
492    } else {
493      if (awtevent instanceof ComponentEvent)
494    super.processComponentEvent((ComponentEvent)awtevent);
495      else
496    super.processEvent(awtevent);
497    }
498  }
499
500  protected void processItemEvent(ItemEvent itemevent) {
501    if (itemListener != null) {
502      itemListener.itemStateChanged(itemevent);
503    }
504  }
505
506  @Override
507  protected void processFocusEvent(FocusEvent e) {
508    switch (e.getID()) {
509    case FocusEvent.FOCUS_GAINED:
510      if (focusListener != null) { focusListener.focusGained(e); }
511      if (getSelectedIndex() == -1) { setSelectedIndex(0); }
512      focusEnabled = true;
513      repaint();
514      break;
515    case FocusEvent.FOCUS_LOST:
516      if (focusListener != null) {
517        focusListener.focusLost(e);
518      }
519      focusEnabled = false;
520      repaint();
521      break;
522    }
523    super.processFocusEvent(e);
524  }
525
526  @Override
527  protected void processKeyEvent(KeyEvent e) {
528    rows = getItemCount();
529
530    switch (e.getID()) {
531
532    case KeyEvent.KEY_TYPED:
533      if (keyListener != null) {
534        keyListener.keyTyped(e);
535      }
536      break;
537
538    case KeyEvent.KEY_PRESSED:
539      if (keyListener != null) {
540        keyListener.keyPressed(e);
541      }
542      if (e.getKeyCode() == KeyEvent.VK_DOWN) {
543        prevfocusIndex = focusIndex;
544        int index = getSelectedIndex() + 1;
545        if (index > rows) { break; }
546        setSelectedIndex(index);
547        processItemEvent(new ItemEvent(this, 0, index, 0));
548        eventOccurred = true;
549        repaint();
550      } else if (e.getKeyCode() == KeyEvent.VK_UP) {
551        int index = getSelectedIndex()-1;
552        if (index >= 0) {
553          setSelectedIndex(index);
554          if (e.getID() != 400) {
555            processItemEvent(new ItemEvent(this, 0, index, 0));
556          }
557          eventOccurred = true;
558          repaint();
559        }
560      }
561      break;
562
563    case KeyEvent.KEY_RELEASED:
564      if (keyListener != null) {
565        keyListener.keyReleased(e);
566      }
567      if (e.getKeyCode() == KeyEvent.VK_ENTER) {
568        eventOccurred = true;
569
570        // ActionEvent is fired here
571        if (actionListener != null) {
572          actionListener.actionPerformed( new ActionEvent(
573              this, ActionEvent.ACTION_PERFORMED, null));
574        }
575        repaint();
576      }
577      break;
578    } // switch
579    super.processKeyEvent(e);
580  }
581
582  @Override
583  protected void processMouseEvent(MouseEvent e) {
584    switch (e.getID()) {
585    case MouseEvent.MOUSE_PRESSED:
586      pressed = true;
587      if (contains(e.getPoint())) {
588        findSelectedIndex(e.getPoint());
589        processItemEvent(new ItemEvent(this, 0, focusIndex, 0));
590        eventOccurred = true;
591      }
592      repaint();
593      break;
594
595    case MouseEvent.MOUSE_RELEASED:
596      if (pressed) { requestFocus(); }
597
598      if (contains(e.getPoint())) {
599        findSelectedIndex(e.getPoint());
600        eventOccurred = true;
601      }
602      // ActionEvent is fired here
603      if (actionListener != null) {
604        actionListener.actionPerformed(new ActionEvent(
605            this, ActionEvent.ACTION_PERFORMED, null));
606      }
607
608      if (pressed) {
609        pressed = false;
610        repaint();
611      }
612      break;
613    }
614    super.processMouseEvent(e);
615  }
616
617  @Override
618  /**
619   * Paint the list.
620   *
621   * @param g  the graphics context to be used for testing
622   */
623  public void paint(Graphics g) {
624    super.paint(g);
625    restrictGraphicsToClientArea(g);
626
627    Point     loc = getClientLocation();
628    Dimension dim = getClientSize();
629    Color prevColor = g.getColor();
630
631    // List border is drawn here
632    g.setColor(getBackground());
633    g.fillRect(0, 0, dim.width - 2, dim.height - 2);
634    g.setColor(getBorder());
635    g.drawRect(0, 0, dim.width - 2, dim.height - 2);
636
637    if (getItemCount() > 0) {
638      Font f = getFont();
639      if (f != null) {
640        String str[] = getItems();
641        FontMetrics fm = getFontMetrics(f);
642        int drawRow = loc.x + getBorderWidth() + fm.getAscent();
643        int drawCol = loc.y + getBorderWidth();
644        int rectRow = loc.y + getBorderWidth();
645        int i = 0;
646
647        // Draw items (if the items exceeds visibility those items will be truncated
648        // as scrollbar support is not enabled
649
650        for (;
651             i < str.length && drawRow < (dim.height - getBorderWidth());
652             i++) {
653               if (fm.stringWidth(str[i]) < (dim.width - (getBorderWidth() * 2))) {
654                 drawItem(g, i, drawCol, drawRow, rectRow, fm);
655                 drawRow += fm.getHeight();
656                 rectRow += fm.getHeight();
657               } else {
658                 LWComponent.errorMsg("string width exceeds list width");
659                 LWComponent.errorMsg("Horizontal scrollbar support is not available");
660               }
661            } // for
662
663        if ( (drawRow > (dim.height - getBorderWidth())) && (str.length > i) ) {
664          //LWComponent.errorMsg("no of strings exceeds list height");
665          //LWComponent.errorMsg("Vertical scrollbar support is not available");
666        }
667      } else { LWComponent.errorMsg("Font not available.."); }
668    }
669
670    eventOccurred = false;
671    g.setColor(prevColor);
672    unrestrictGraphicsFromClientArea(g);
673  }
674
675  // Draw String items
676  private void drawItem(Graphics g, int listIndex, int drawCol,
677      int drawRow, int rectRow, FontMetrics fm) {
678    Point     loc = getClientLocation();
679    Dimension dim = getClientSize();
680    String    str = getItem(listIndex);
681    if (multipleMode) {
682      for (int i1 = 0; i1 < selList.size(); i1++) {
683        if (listIndex == ((Integer)selList.elementAt(i1))) {
684          g.setColor(getFocusColor());
685          g.fillRect(loc.x + getBorderWidth(),
686                     rectRow,
687                     dim.width - getBorderWidth() * 2,
688                     fm.getHeight());
689          g.setColor(getFocusEnabledColor());
690          g.drawRect(loc.x + getBorderWidth(),
691                     rectRow,
692                     dim.width - getBorderWidth() * 2,
693                     fm.getHeight());
694        }
695      } // for
696    } else {
697      if (listIndex == getSelectedIndex() && !multipleMode) {
698        g.setColor(getFocusColor());
699        g.fillRect(loc.x + getBorderWidth(),
700                   rectRow,
701                   dim.width - getBorderWidth() * 2,
702                   fm.getHeight());
703        g.setColor(getFocusForeColor());
704      }
705      if ((listIndex == prevfocusIndex) && (prevfocusIndex != getSelectedIndex()) && !multipleMode) {
706        g.setColor(getBackground());
707        g.fillRect(loc.x + getBorderWidth(),
708                   rectRow,
709                   dim.width - getBorderWidth() * 2,
710                   fm.getHeight());
711        prevfocusIndex = getSelectedIndex();
712      }
713      if (focusEnabled && listIndex == getSelectedIndex() && !multipleMode) {
714        g.setColor(getFocusEnabledColor());
715        g.drawRect(loc.x + getBorderWidth(),
716                   rectRow,
717                   dim.width - getBorderWidth() * 2,
718                   fm.getHeight());
719      }
720    }
721    g.setColor(getForeground());
722    g.drawString(str,drawCol,drawRow);
723  }
724
725}
726
727