mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1422 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1422 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Java
		
	
	
	
/* BasicListUI.java --
 | 
						|
   Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package javax.swing.plaf.basic;
 | 
						|
 | 
						|
import java.awt.Component;
 | 
						|
import java.awt.Dimension;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Insets;
 | 
						|
import java.awt.Point;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.event.ActionEvent;
 | 
						|
import java.awt.event.ActionListener;
 | 
						|
import java.awt.event.FocusEvent;
 | 
						|
import java.awt.event.FocusListener;
 | 
						|
import java.awt.event.MouseEvent;
 | 
						|
import java.beans.PropertyChangeEvent;
 | 
						|
import java.beans.PropertyChangeListener;
 | 
						|
 | 
						|
import javax.swing.AbstractAction;
 | 
						|
import javax.swing.ActionMap;
 | 
						|
import javax.swing.CellRendererPane;
 | 
						|
import javax.swing.DefaultListSelectionModel;
 | 
						|
import javax.swing.InputMap;
 | 
						|
import javax.swing.JComponent;
 | 
						|
import javax.swing.JList;
 | 
						|
import javax.swing.ListCellRenderer;
 | 
						|
import javax.swing.ListModel;
 | 
						|
import javax.swing.ListSelectionModel;
 | 
						|
import javax.swing.LookAndFeel;
 | 
						|
import javax.swing.SwingUtilities;
 | 
						|
import javax.swing.TransferHandler;
 | 
						|
import javax.swing.UIDefaults;
 | 
						|
import javax.swing.UIManager;
 | 
						|
import javax.swing.event.ListDataEvent;
 | 
						|
import javax.swing.event.ListDataListener;
 | 
						|
import javax.swing.event.ListSelectionEvent;
 | 
						|
import javax.swing.event.ListSelectionListener;
 | 
						|
import javax.swing.event.MouseInputListener;
 | 
						|
import javax.swing.plaf.ActionMapUIResource;
 | 
						|
import javax.swing.plaf.ComponentUI;
 | 
						|
import javax.swing.plaf.ListUI;
 | 
						|
import javax.swing.plaf.UIResource;
 | 
						|
 | 
						|
/**
 | 
						|
 * The Basic Look and Feel UI delegate for the
 | 
						|
 * JList.
 | 
						|
 */
 | 
						|
public class BasicListUI extends ListUI
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * A helper class which listens for {@link FocusEvent}s
 | 
						|
   * from the JList.
 | 
						|
   */
 | 
						|
  public class FocusHandler implements FocusListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Called when the JList acquires focus.
 | 
						|
     *
 | 
						|
     * @param e The FocusEvent representing focus acquisition
 | 
						|
     */
 | 
						|
    public void focusGained(FocusEvent e)
 | 
						|
    {
 | 
						|
      repaintCellFocus();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when the JList loses focus.
 | 
						|
     *
 | 
						|
     * @param e The FocusEvent representing focus loss
 | 
						|
     */
 | 
						|
    public void focusLost(FocusEvent e)
 | 
						|
    {
 | 
						|
      repaintCellFocus();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Helper method to repaint the focused cell's
 | 
						|
     * lost or acquired focus state.
 | 
						|
     */
 | 
						|
    protected void repaintCellFocus()
 | 
						|
    {
 | 
						|
      // TODO: Implement this properly.
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * A helper class which listens for {@link ListDataEvent}s generated by
 | 
						|
   * the {@link JList}'s {@link ListModel}.
 | 
						|
   *
 | 
						|
   * @see javax.swing.JList#getModel()
 | 
						|
   */
 | 
						|
  public class ListDataHandler implements ListDataListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Called when a general change has happened in the model which cannot
 | 
						|
     * be represented in terms of a simple addition or deletion.
 | 
						|
     *
 | 
						|
     * @param e The event representing the change
 | 
						|
     */
 | 
						|
    public void contentsChanged(ListDataEvent e)
 | 
						|
    {
 | 
						|
      updateLayoutStateNeeded |= modelChanged;
 | 
						|
      list.revalidate();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when an interval of objects has been added to the model.
 | 
						|
     *
 | 
						|
     * @param e The event representing the addition
 | 
						|
     */
 | 
						|
    public void intervalAdded(ListDataEvent e)
 | 
						|
    {
 | 
						|
      updateLayoutStateNeeded |= modelChanged;
 | 
						|
      list.revalidate();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when an inteval of objects has been removed from the model.
 | 
						|
     *
 | 
						|
     * @param e The event representing the removal
 | 
						|
     */
 | 
						|
    public void intervalRemoved(ListDataEvent e)
 | 
						|
    {
 | 
						|
      updateLayoutStateNeeded |= modelChanged;
 | 
						|
      list.revalidate();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * A helper class which listens for {@link ListSelectionEvent}s
 | 
						|
   * from the {@link JList}'s {@link ListSelectionModel}.
 | 
						|
   */
 | 
						|
  public class ListSelectionHandler implements ListSelectionListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Called when the list selection changes.
 | 
						|
     *
 | 
						|
     * @param e The event representing the change
 | 
						|
     */
 | 
						|
    public void valueChanged(ListSelectionEvent e)
 | 
						|
    {
 | 
						|
      int index1 = e.getFirstIndex();
 | 
						|
      int index2 = e.getLastIndex();
 | 
						|
      Rectangle damaged = getCellBounds(list, index1, index2);
 | 
						|
      if (damaged != null)
 | 
						|
        list.repaint(damaged);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This class is used to mimmic the behaviour of the JDK when registering
 | 
						|
   * keyboard actions.  It is the same as the private class used in JComponent
 | 
						|
   * for the same reason.  This class receives an action event and dispatches
 | 
						|
   * it to the true receiver after altering the actionCommand property of the
 | 
						|
   * event.
 | 
						|
   */
 | 
						|
  private static class ActionListenerProxy
 | 
						|
    extends AbstractAction
 | 
						|
  {
 | 
						|
    ActionListener target;
 | 
						|
    String bindingCommandName;
 | 
						|
 | 
						|
    public ActionListenerProxy(ActionListener li,
 | 
						|
                               String cmd)
 | 
						|
    {
 | 
						|
      target = li;
 | 
						|
      bindingCommandName = cmd;
 | 
						|
    }
 | 
						|
 | 
						|
    public void actionPerformed(ActionEvent e)
 | 
						|
    {
 | 
						|
      ActionEvent derivedEvent = new ActionEvent(e.getSource(),
 | 
						|
                                                 e.getID(),
 | 
						|
                                                 bindingCommandName,
 | 
						|
                                                 e.getModifiers());
 | 
						|
      target.actionPerformed(derivedEvent);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Implements the action for the JList's keyboard commands.
 | 
						|
   */
 | 
						|
  private class ListAction
 | 
						|
    extends AbstractAction
 | 
						|
  {
 | 
						|
    // TODO: Maybe make a couple of classes out of this bulk Action.
 | 
						|
    // Form logical groups of Actions when doing this.
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a new ListAction for the specified command.
 | 
						|
     *
 | 
						|
     * @param cmd the actionCommand to set
 | 
						|
     */
 | 
						|
    ListAction(String cmd)
 | 
						|
    {
 | 
						|
      putValue(ACTION_COMMAND_KEY, cmd);
 | 
						|
    }
 | 
						|
 | 
						|
    public void actionPerformed(ActionEvent e)
 | 
						|
    {
 | 
						|
      int lead = list.getLeadSelectionIndex();
 | 
						|
      int max = list.getModel().getSize() - 1;
 | 
						|
      DefaultListSelectionModel selModel
 | 
						|
          = (DefaultListSelectionModel) list.getSelectionModel();
 | 
						|
      String command = e.getActionCommand();
 | 
						|
      // Do nothing if list is empty
 | 
						|
      if (max == -1)
 | 
						|
        return;
 | 
						|
 | 
						|
      if (command.equals("selectNextRow"))
 | 
						|
        {
 | 
						|
          selectNextIndex();
 | 
						|
        }
 | 
						|
      else if (command.equals("selectPreviousRow"))
 | 
						|
        {
 | 
						|
          selectPreviousIndex();
 | 
						|
        }
 | 
						|
      else if (command.equals("clearSelection"))
 | 
						|
        {
 | 
						|
          list.clearSelection();
 | 
						|
        }
 | 
						|
      else if (command.equals("selectAll"))
 | 
						|
        {
 | 
						|
          list.setSelectionInterval(0, max);
 | 
						|
          // this next line is to restore the lead selection index to the old
 | 
						|
          // position, because select-all should not change the lead index
 | 
						|
          list.addSelectionInterval(lead, lead);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectLastRow"))
 | 
						|
        {
 | 
						|
          list.setSelectedIndex(list.getModel().getSize() - 1);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectLastRowChangeLead"))
 | 
						|
        {
 | 
						|
          selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1);
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollDownExtendSelection"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getLastVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.min(max, lead + (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getLastVisibleIndex();
 | 
						|
          selModel.setLeadSelectionIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollDownChangeLead"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getLastVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.min(max, lead + (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getLastVisibleIndex();
 | 
						|
          selModel.moveLeadSelectionIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollUpExtendSelection"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getFirstVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.max(0, lead - (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getFirstVisibleIndex();
 | 
						|
          selModel.setLeadSelectionIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollUpChangeLead"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getFirstVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.max(0, lead - (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getFirstVisibleIndex();
 | 
						|
          selModel.moveLeadSelectionIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectNextRowExtendSelection"))
 | 
						|
        {
 | 
						|
          selModel.setLeadSelectionIndex(Math.min(lead + 1, max));
 | 
						|
        }
 | 
						|
      else if (command.equals("selectFirstRow"))
 | 
						|
        {
 | 
						|
          list.setSelectedIndex(0);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectFirstRowChangeLead"))
 | 
						|
        {
 | 
						|
          selModel.moveLeadSelectionIndex(0);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectFirstRowExtendSelection"))
 | 
						|
        {
 | 
						|
          selModel.setLeadSelectionIndex(0);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectPreviousRowExtendSelection"))
 | 
						|
        {
 | 
						|
          selModel.setLeadSelectionIndex(Math.max(0, lead - 1));
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollUp"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getFirstVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.max(0, lead - (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getFirstVisibleIndex();
 | 
						|
          list.setSelectedIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectLastRowExtendSelection"))
 | 
						|
        {
 | 
						|
          selModel.setLeadSelectionIndex(list.getModel().getSize() - 1);
 | 
						|
        }
 | 
						|
      else if (command.equals("scrollDown"))
 | 
						|
        {
 | 
						|
          int target;
 | 
						|
          if (lead == list.getLastVisibleIndex())
 | 
						|
            {
 | 
						|
              target = Math.min(max, lead + (list.getLastVisibleIndex()
 | 
						|
                  - list.getFirstVisibleIndex() + 1));
 | 
						|
            }
 | 
						|
          else
 | 
						|
            target = list.getLastVisibleIndex();
 | 
						|
          list.setSelectedIndex(target);
 | 
						|
        }
 | 
						|
      else if (command.equals("selectNextRowChangeLead"))
 | 
						|
          {
 | 
						|
            if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 | 
						|
              selectNextIndex();
 | 
						|
            else
 | 
						|
              {
 | 
						|
                selModel.moveLeadSelectionIndex(Math.min(max, lead + 1));
 | 
						|
              }
 | 
						|
          }
 | 
						|
      else if (command.equals("selectPreviousRowChangeLead"))
 | 
						|
        {
 | 
						|
          if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 | 
						|
            selectPreviousIndex();
 | 
						|
          else
 | 
						|
            {
 | 
						|
              selModel.moveLeadSelectionIndex(Math.max(0, lead - 1));
 | 
						|
            }
 | 
						|
        }
 | 
						|
      else if (command.equals("addToSelection"))
 | 
						|
        {
 | 
						|
          list.addSelectionInterval(lead, lead);
 | 
						|
        }
 | 
						|
      else if (command.equals("extendTo"))
 | 
						|
        {
 | 
						|
          selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(),
 | 
						|
                                        lead);
 | 
						|
        }
 | 
						|
      else if (command.equals("toggleAndAnchor"))
 | 
						|
        {
 | 
						|
          if (!list.isSelectedIndex(lead))
 | 
						|
            list.addSelectionInterval(lead, lead);
 | 
						|
          else
 | 
						|
            list.removeSelectionInterval(lead, lead);
 | 
						|
          selModel.setAnchorSelectionIndex(lead);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          // DEBUG: uncomment the following line to print out
 | 
						|
          // key bindings that aren't implemented yet
 | 
						|
 | 
						|
          // System.out.println ("not implemented: "+e.getActionCommand());
 | 
						|
        }
 | 
						|
 | 
						|
      list.ensureIndexIsVisible(list.getLeadSelectionIndex());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * A helper class which listens for {@link MouseEvent}s
 | 
						|
   * from the {@link JList}.
 | 
						|
   */
 | 
						|
  public class MouseInputHandler implements MouseInputListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Called when a mouse button press/release cycle completes
 | 
						|
     * on the {@link JList}
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse click
 | 
						|
     */
 | 
						|
    public void mouseClicked(MouseEvent event)
 | 
						|
    {
 | 
						|
      Point click = event.getPoint();
 | 
						|
      int index = locationToIndex(list, click);
 | 
						|
      if (index == -1)
 | 
						|
        return;
 | 
						|
      if (event.isShiftDown())
 | 
						|
        {
 | 
						|
          if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
 | 
						|
            list.setSelectedIndex(index);
 | 
						|
          else if (list.getSelectionMode() ==
 | 
						|
                   ListSelectionModel.SINGLE_INTERVAL_SELECTION)
 | 
						|
            // COMPAT: the IBM VM is compatible with the following line of code.
 | 
						|
            // However, compliance with Sun's VM would correspond to replacing
 | 
						|
            // getAnchorSelectionIndex() with getLeadSelectionIndex().This is
 | 
						|
            // both unnatural and contradictory to the way they handle other
 | 
						|
            // similar UI interactions.
 | 
						|
            list.setSelectionInterval(list.getAnchorSelectionIndex(), index);
 | 
						|
          else
 | 
						|
            // COMPAT: both Sun and IBM are compatible instead with:
 | 
						|
            // list.setSelectionInterval
 | 
						|
            //     (list.getLeadSelectionIndex(),index);
 | 
						|
            // Note that for IBM this is contradictory to what they did in
 | 
						|
            // the above situation for SINGLE_INTERVAL_SELECTION.
 | 
						|
            // The most natural thing to do is the following:
 | 
						|
            if (list.isSelectedIndex(list.getAnchorSelectionIndex()))
 | 
						|
              list.getSelectionModel().setLeadSelectionIndex(index);
 | 
						|
            else
 | 
						|
              list.addSelectionInterval(list.getAnchorSelectionIndex(), index);
 | 
						|
        }
 | 
						|
      else if (event.isControlDown())
 | 
						|
        {
 | 
						|
          if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
 | 
						|
            list.setSelectedIndex(index);
 | 
						|
          else if (list.isSelectedIndex(index))
 | 
						|
            list.removeSelectionInterval(index, index);
 | 
						|
          else
 | 
						|
            list.addSelectionInterval(index, index);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        list.setSelectedIndex(index);
 | 
						|
 | 
						|
      list.ensureIndexIsVisible(list.getLeadSelectionIndex());
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when a mouse button is pressed down on the
 | 
						|
     * {@link JList}.
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse press
 | 
						|
     */
 | 
						|
    public void mousePressed(MouseEvent event)
 | 
						|
    {
 | 
						|
      // We need to explicitly request focus.
 | 
						|
      list.requestFocusInWindow();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when a mouse button is released on
 | 
						|
     * the {@link JList}
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse press
 | 
						|
     */
 | 
						|
    public void mouseReleased(MouseEvent event)
 | 
						|
    {
 | 
						|
      // TODO: What should be done here, if anything?
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when the mouse pointer enters the area bounded
 | 
						|
     * by the {@link JList}
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse entry
 | 
						|
     */
 | 
						|
    public void mouseEntered(MouseEvent event)
 | 
						|
    {
 | 
						|
      // TODO: What should be done here, if anything?
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when the mouse pointer leaves the area bounded
 | 
						|
     * by the {@link JList}
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse exit
 | 
						|
     */
 | 
						|
    public void mouseExited(MouseEvent event)
 | 
						|
    {
 | 
						|
      // TODO: What should be done here, if anything?
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when the mouse pointer moves over the area bounded
 | 
						|
     * by the {@link JList} while a button is held down.
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse drag
 | 
						|
     */
 | 
						|
    public void mouseDragged(MouseEvent event)
 | 
						|
    {
 | 
						|
      Point click = event.getPoint();
 | 
						|
      int index = locationToIndex(list, click);
 | 
						|
      if (index == -1)
 | 
						|
        return;
 | 
						|
      if (!event.isShiftDown() && !event.isControlDown())
 | 
						|
        list.setSelectedIndex(index);
 | 
						|
 | 
						|
      list.ensureIndexIsVisible(list.getLeadSelectionIndex());
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Called when the mouse pointer moves over the area bounded
 | 
						|
     * by the {@link JList}.
 | 
						|
     *
 | 
						|
     * @param event The event representing the mouse move
 | 
						|
     */
 | 
						|
    public void mouseMoved(MouseEvent event)
 | 
						|
    {
 | 
						|
      // TODO: What should be done here, if anything?
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Helper class which listens to {@link PropertyChangeEvent}s
 | 
						|
   * from the {@link JList}.
 | 
						|
   */
 | 
						|
  public class PropertyChangeHandler implements PropertyChangeListener
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Called when the {@link JList} changes one of its bound properties.
 | 
						|
     *
 | 
						|
     * @param e The event representing the property change
 | 
						|
     */
 | 
						|
    public void propertyChange(PropertyChangeEvent e)
 | 
						|
    {
 | 
						|
      if (e.getPropertyName().equals("model"))
 | 
						|
        {
 | 
						|
          if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
 | 
						|
            {
 | 
						|
              ListModel oldModel = (ListModel) e.getOldValue();
 | 
						|
              oldModel.removeListDataListener(listDataListener);
 | 
						|
            }
 | 
						|
          if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
 | 
						|
            {
 | 
						|
              ListModel newModel = (ListModel) e.getNewValue();
 | 
						|
              newModel.addListDataListener(BasicListUI.this.listDataListener);
 | 
						|
            }
 | 
						|
 | 
						|
          updateLayoutStateNeeded |= modelChanged;
 | 
						|
        }
 | 
						|
      else if (e.getPropertyName().equals("selectionModel"))
 | 
						|
        updateLayoutStateNeeded |= selectionModelChanged;
 | 
						|
      else if (e.getPropertyName().equals("font"))
 | 
						|
        updateLayoutStateNeeded |= fontChanged;
 | 
						|
      else if (e.getPropertyName().equals("fixedCellWidth"))
 | 
						|
        updateLayoutStateNeeded |= fixedCellWidthChanged;
 | 
						|
      else if (e.getPropertyName().equals("fixedCellHeight"))
 | 
						|
        updateLayoutStateNeeded |= fixedCellHeightChanged;
 | 
						|
      else if (e.getPropertyName().equals("prototypeCellValue"))
 | 
						|
        updateLayoutStateNeeded |= prototypeCellValueChanged;
 | 
						|
      else if (e.getPropertyName().equals("cellRenderer"))
 | 
						|
        updateLayoutStateNeeded |= cellRendererChanged;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the model has changed.
 | 
						|
   */
 | 
						|
  protected static final int modelChanged = 1;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the selection model has changed.
 | 
						|
   */
 | 
						|
  protected static final int selectionModelChanged = 2;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the font has changed.
 | 
						|
   */
 | 
						|
  protected static final int fontChanged = 4;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the fixedCellWidth has changed.
 | 
						|
   */
 | 
						|
  protected static final int fixedCellWidthChanged = 8;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the fixedCellHeight has changed.
 | 
						|
   */
 | 
						|
  protected static final int fixedCellHeightChanged = 16;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the prototypeCellValue has changed.
 | 
						|
   */
 | 
						|
  protected static final int prototypeCellValueChanged = 32;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A constant to indicate that the cellRenderer has changed.
 | 
						|
   */
 | 
						|
  protected static final int cellRendererChanged = 64;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new BasicListUI for the component.
 | 
						|
   *
 | 
						|
   * @param c The component to create a UI for
 | 
						|
   *
 | 
						|
   * @return A new UI
 | 
						|
   */
 | 
						|
  public static ComponentUI createUI(final JComponent c)
 | 
						|
  {
 | 
						|
    return new BasicListUI();
 | 
						|
  }
 | 
						|
 | 
						|
  /** The current focus listener. */
 | 
						|
  protected FocusListener focusListener;
 | 
						|
 | 
						|
  /** The data listener listening to the model. */
 | 
						|
  protected ListDataListener listDataListener;
 | 
						|
 | 
						|
  /** The selection listener listening to the selection model. */
 | 
						|
  protected ListSelectionListener listSelectionListener;
 | 
						|
 | 
						|
  /** The mouse listener listening to the list. */
 | 
						|
  protected MouseInputListener mouseInputListener;
 | 
						|
 | 
						|
  /** The property change listener listening to the list. */
 | 
						|
  protected PropertyChangeListener propertyChangeListener;
 | 
						|
 | 
						|
  /** Saved reference to the list this UI was created for. */
 | 
						|
  protected JList list;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The height of a single cell in the list. This field is used when the
 | 
						|
   * fixedCellHeight property of the list is set. Otherwise this field is
 | 
						|
   * set to <code>-1</code> and {@link #cellHeights} is used instead.
 | 
						|
   */
 | 
						|
  protected int cellHeight;
 | 
						|
 | 
						|
  /** The width of a single cell in the list. */
 | 
						|
  protected int cellWidth;
 | 
						|
 | 
						|
  /**
 | 
						|
   * An array of varying heights of cells in the list, in cases where each
 | 
						|
   * cell might have a different height. This field is used when the
 | 
						|
   * <code>fixedCellHeight</code> property of the list is not set. Otherwise
 | 
						|
   * this field is <code>null</code> and {@link #cellHeight} is used.
 | 
						|
   */
 | 
						|
  protected int[] cellHeights;
 | 
						|
 | 
						|
  /**
 | 
						|
   * A bitmask that indicates which properties of the JList have changed.
 | 
						|
   * When nonzero, indicates that the UI class is out of
 | 
						|
   * date with respect to the underlying list, and must recalculate the
 | 
						|
   * list layout before painting or performing size calculations.
 | 
						|
   *
 | 
						|
   * @see #modelChanged
 | 
						|
   * @see #selectionModelChanged
 | 
						|
   * @see #fontChanged
 | 
						|
   * @see #fixedCellWidthChanged
 | 
						|
   * @see #fixedCellHeightChanged
 | 
						|
   * @see #prototypeCellValueChanged
 | 
						|
   * @see #cellRendererChanged
 | 
						|
   */
 | 
						|
  protected int updateLayoutStateNeeded;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The {@link CellRendererPane} that is used for painting.
 | 
						|
   */
 | 
						|
  protected CellRendererPane rendererPane;
 | 
						|
 | 
						|
  /** The action bound to KeyStrokes. */
 | 
						|
  ListAction action;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculate the height of a particular row. If there is a fixed {@link
 | 
						|
   * #cellHeight}, return it; otherwise return the specific row height
 | 
						|
   * requested from the {@link #cellHeights} array. If the requested row
 | 
						|
   * is invalid, return <code>-1</code>.
 | 
						|
   *
 | 
						|
   * @param row The row to get the height of
 | 
						|
   *
 | 
						|
   * @return The height, in pixels, of the specified row
 | 
						|
   */
 | 
						|
  protected int getRowHeight(int row)
 | 
						|
  {
 | 
						|
    int height;
 | 
						|
    if (cellHeights == null)
 | 
						|
      height = cellHeight;
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (row < 0 || row >= cellHeights.length)
 | 
						|
          height = -1;
 | 
						|
        else
 | 
						|
          height = cellHeights[row];
 | 
						|
      }
 | 
						|
    return height;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculate the bounds of a particular cell, considering the upper left
 | 
						|
   * corner of the list as the origin position <code>(0,0)</code>.
 | 
						|
   *
 | 
						|
   * @param l Ignored; calculates over <code>this.list</code>
 | 
						|
   * @param index1 The first row to include in the bounds
 | 
						|
   * @param index2 The last row to incude in the bounds
 | 
						|
   *
 | 
						|
   * @return A rectangle encompassing the range of rows between
 | 
						|
   * <code>index1</code> and <code>index2</code> inclusive, or null
 | 
						|
   * such a rectangle couldn't be calculated for the given indexes.
 | 
						|
   */
 | 
						|
  public Rectangle getCellBounds(JList l, int index1, int index2)
 | 
						|
  {
 | 
						|
    maybeUpdateLayoutState();
 | 
						|
 | 
						|
    if (l != list || cellWidth == -1)
 | 
						|
      return null;
 | 
						|
 | 
						|
    int minIndex = Math.min(index1, index2);
 | 
						|
    int maxIndex = Math.max(index1, index2);
 | 
						|
    Point loc = indexToLocation(list, minIndex);
 | 
						|
 | 
						|
    // When the layoutOrientation is VERTICAL, then the width == the list
 | 
						|
    // width. Otherwise the cellWidth field is used.
 | 
						|
    int width = cellWidth;
 | 
						|
    if (l.getLayoutOrientation() == JList.VERTICAL)
 | 
						|
      width = l.getWidth();
 | 
						|
 | 
						|
    Rectangle bounds = new Rectangle(loc.x, loc.y, width,
 | 
						|
                                     getCellHeight(minIndex));
 | 
						|
    for (int i = minIndex + 1; i <= maxIndex; i++)
 | 
						|
      {
 | 
						|
        Point hiLoc = indexToLocation(list, i);
 | 
						|
        bounds = SwingUtilities.computeUnion(hiLoc.x, hiLoc.y, width,
 | 
						|
                                             getCellHeight(i), bounds);
 | 
						|
      }
 | 
						|
 | 
						|
    return bounds;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculates the maximum cell height.
 | 
						|
   *
 | 
						|
   * @param index the index of the cell
 | 
						|
   *
 | 
						|
   * @return the maximum cell height
 | 
						|
   */
 | 
						|
  private int getCellHeight(int index)
 | 
						|
  {
 | 
						|
    int height = cellHeight;
 | 
						|
    if (height <= 0)
 | 
						|
      {
 | 
						|
        if (list.getLayoutOrientation() == JList.VERTICAL)
 | 
						|
          height = getRowHeight(index);
 | 
						|
        else
 | 
						|
          {
 | 
						|
            for (int j = 0; j < cellHeights.length; j++)
 | 
						|
              height = Math.max(height, cellHeights[j]);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return height;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculate the Y coordinate of the upper edge of a particular row,
 | 
						|
   * considering the Y coordinate <code>0</code> to occur at the top of the
 | 
						|
   * list.
 | 
						|
   *
 | 
						|
   * @param row The row to calculate the Y coordinate of
 | 
						|
   *
 | 
						|
   * @return The Y coordinate of the specified row, or <code>-1</code> if
 | 
						|
   * the specified row number is invalid
 | 
						|
   */
 | 
						|
  protected int convertRowToY(int row)
 | 
						|
  {
 | 
						|
    int y = 0;
 | 
						|
    for (int i = 0; i < row; ++i)
 | 
						|
      {
 | 
						|
        int h = getRowHeight(i);
 | 
						|
        if (h == -1)
 | 
						|
          return -1;
 | 
						|
        y += h;
 | 
						|
      }
 | 
						|
    return y;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculate the row number containing a particular Y coordinate,
 | 
						|
   * considering the Y coodrinate <code>0</code> to occur at the top of the
 | 
						|
   * list.
 | 
						|
   *
 | 
						|
   * @param y0 The Y coordinate to calculate the row number for
 | 
						|
   *
 | 
						|
   * @return The row number containing the specified Y value, or <code>-1</code>
 | 
						|
   *         if the list model is empty
 | 
						|
   *
 | 
						|
   * @specnote This method is specified to return -1 for an invalid Y
 | 
						|
   *           coordinate. However, some simple tests show that the behaviour
 | 
						|
   *           is to return the index of the last list element for an Y
 | 
						|
   *           coordinate that lies outside of the list bounds (even for
 | 
						|
   *           negative indices). <code>-1</code>
 | 
						|
   *           is only returned if the list model is empty.
 | 
						|
   */
 | 
						|
  protected int convertYToRow(int y0)
 | 
						|
  {
 | 
						|
    if (list.getModel().getSize() == 0)
 | 
						|
      return -1;
 | 
						|
 | 
						|
    // When y0 < 0, then the JDK returns the maximum row index of the list. So
 | 
						|
    // do we.
 | 
						|
    if (y0 < 0)
 | 
						|
      return list.getModel().getSize() - 1;
 | 
						|
 | 
						|
    // Update the layout if necessary.
 | 
						|
    maybeUpdateLayoutState();
 | 
						|
 | 
						|
    int index = list.getModel().getSize() - 1;
 | 
						|
 | 
						|
    // If a fixed cell height is set, then we can work more efficient.
 | 
						|
    if (cellHeight > 0)
 | 
						|
      index = Math.min(y0 / cellHeight, index);
 | 
						|
    // If we have no fixed cell height, we must add up each cell height up
 | 
						|
    // to y0.
 | 
						|
    else
 | 
						|
      {
 | 
						|
        int h = 0;
 | 
						|
        for (int row = 0; row < cellHeights.length; ++row)
 | 
						|
          {
 | 
						|
            h += cellHeights[row];
 | 
						|
            if (y0 < h)
 | 
						|
              {
 | 
						|
                index = row;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return index;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
 | 
						|
   * #cellWidth} properties by examining the variouis properties of the
 | 
						|
   * {@link JList}.
 | 
						|
   */
 | 
						|
  protected void updateLayoutState()
 | 
						|
  {
 | 
						|
    int nrows = list.getModel().getSize();
 | 
						|
    cellHeight = -1;
 | 
						|
    cellWidth = -1;
 | 
						|
    if (cellHeights == null || cellHeights.length != nrows)
 | 
						|
      cellHeights = new int[nrows];
 | 
						|
    ListCellRenderer rend = list.getCellRenderer();
 | 
						|
    // Update the cellHeight(s) fields.
 | 
						|
    int fixedCellHeight = list.getFixedCellHeight();
 | 
						|
    if (fixedCellHeight > 0)
 | 
						|
      {
 | 
						|
        cellHeight = fixedCellHeight;
 | 
						|
        cellHeights = null;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        cellHeight = -1;
 | 
						|
        for (int i = 0; i < nrows; ++i)
 | 
						|
          {
 | 
						|
            Component flyweight =
 | 
						|
              rend.getListCellRendererComponent(list,
 | 
						|
                      list.getModel().getElementAt(i),
 | 
						|
                      i, list.isSelectedIndex(i),
 | 
						|
                      list.getSelectionModel().getAnchorSelectionIndex() == i);
 | 
						|
            Dimension dim = flyweight.getPreferredSize();
 | 
						|
            cellHeights[i] = dim.height;
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    // Update the cellWidth field.
 | 
						|
    int fixedCellWidth = list.getFixedCellWidth();
 | 
						|
    if (fixedCellWidth > 0)
 | 
						|
      cellWidth = fixedCellWidth;
 | 
						|
    else
 | 
						|
      {
 | 
						|
        for (int i = 0; i < nrows; ++i)
 | 
						|
          {
 | 
						|
            Component flyweight =
 | 
						|
              rend.getListCellRendererComponent(list,
 | 
						|
                                                list.getModel().getElementAt(i),
 | 
						|
                                                i, list.isSelectedIndex(i),
 | 
						|
                                                list.getSelectionModel().getAnchorSelectionIndex() == i);
 | 
						|
            Dimension dim = flyweight.getPreferredSize();
 | 
						|
            cellWidth = Math.max(cellWidth, dim.width);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
 | 
						|
   * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
 | 
						|
   */
 | 
						|
  protected void maybeUpdateLayoutState()
 | 
						|
  {
 | 
						|
    if (updateLayoutStateNeeded != 0 || !list.isValid())
 | 
						|
      {
 | 
						|
        updateLayoutState();
 | 
						|
        updateLayoutStateNeeded = 0;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new BasicListUI object.
 | 
						|
   */
 | 
						|
  public BasicListUI()
 | 
						|
  {
 | 
						|
    updateLayoutStateNeeded = 1;
 | 
						|
    rendererPane = new CellRendererPane();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Installs various default settings (mostly colors) from the {@link
 | 
						|
   * UIDefaults} into the {@link JList}
 | 
						|
   *
 | 
						|
   * @see #uninstallDefaults
 | 
						|
   */
 | 
						|
  protected void installDefaults()
 | 
						|
  {
 | 
						|
    LookAndFeel.installColorsAndFont(list, "List.background",
 | 
						|
                                     "List.foreground", "List.font");
 | 
						|
    list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
 | 
						|
    list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
 | 
						|
    list.setOpaque(true);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Resets to <code>null</code> those defaults which were installed in
 | 
						|
   * {@link #installDefaults}
 | 
						|
   */
 | 
						|
  protected void uninstallDefaults()
 | 
						|
  {
 | 
						|
    list.setForeground(null);
 | 
						|
    list.setBackground(null);
 | 
						|
    list.setSelectionForeground(null);
 | 
						|
    list.setSelectionBackground(null);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Attaches all the listeners we have in the UI class to the {@link
 | 
						|
   * JList}, its model and its selection model.
 | 
						|
   *
 | 
						|
   * @see #uninstallListeners
 | 
						|
   */
 | 
						|
  protected void installListeners()
 | 
						|
  {
 | 
						|
    if (focusListener == null)
 | 
						|
      focusListener = createFocusListener();
 | 
						|
    list.addFocusListener(focusListener);
 | 
						|
    if (listDataListener == null)
 | 
						|
      listDataListener = createListDataListener();
 | 
						|
    list.getModel().addListDataListener(listDataListener);
 | 
						|
    if (listSelectionListener == null)
 | 
						|
      listSelectionListener = createListSelectionListener();
 | 
						|
    list.addListSelectionListener(listSelectionListener);
 | 
						|
    if (mouseInputListener == null)
 | 
						|
      mouseInputListener = createMouseInputListener();
 | 
						|
    list.addMouseListener(mouseInputListener);
 | 
						|
    list.addMouseMotionListener(mouseInputListener);
 | 
						|
    if (propertyChangeListener == null)
 | 
						|
      propertyChangeListener = createPropertyChangeListener();
 | 
						|
    list.addPropertyChangeListener(propertyChangeListener);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Detaches all the listeners we attached in {@link #installListeners}.
 | 
						|
   */
 | 
						|
  protected void uninstallListeners()
 | 
						|
  {
 | 
						|
    list.removeFocusListener(focusListener);
 | 
						|
    list.getModel().removeListDataListener(listDataListener);
 | 
						|
    list.removeListSelectionListener(listSelectionListener);
 | 
						|
    list.removeMouseListener(mouseInputListener);
 | 
						|
    list.removeMouseMotionListener(mouseInputListener);
 | 
						|
    list.removePropertyChangeListener(propertyChangeListener);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Installs keyboard actions for this UI in the {@link JList}.
 | 
						|
   */
 | 
						|
  protected void installKeyboardActions()
 | 
						|
  {
 | 
						|
    // Install UI InputMap.
 | 
						|
    InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap");
 | 
						|
    SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
 | 
						|
                                     focusInputMap);
 | 
						|
 | 
						|
    // Install UI ActionMap.
 | 
						|
    ActionMap am = (ActionMap) UIManager.get("List.actionMap");
 | 
						|
    if (am == null)
 | 
						|
      {
 | 
						|
        // Create the actionMap once and store it in the current UIDefaults
 | 
						|
        // for use in other components.
 | 
						|
        am = new ActionMapUIResource();
 | 
						|
        ListAction action;
 | 
						|
        action = new ListAction("selectPreviousRow");
 | 
						|
        am.put("selectPreviousRow", action);
 | 
						|
        action = new ListAction("selectNextRow");
 | 
						|
        am.put("selectNextRow", action);
 | 
						|
        action = new ListAction("selectPreviousRowExtendSelection");
 | 
						|
        am.put("selectPreviousRowExtendSelection", action);
 | 
						|
        action = new ListAction("selectNextRowExtendSelection");
 | 
						|
        am.put("selectNextRowExtendSelection", action);
 | 
						|
 | 
						|
        action = new ListAction("selectPreviousColumn");
 | 
						|
        am.put("selectPreviousColumn", action);
 | 
						|
        action = new ListAction("selectNextColumn");
 | 
						|
        am.put("selectNextColumn", action);
 | 
						|
        action = new ListAction("selectPreviousColumnExtendSelection");
 | 
						|
        am.put("selectPreviousColumnExtendSelection", action);
 | 
						|
        action = new ListAction("selectNextColumnExtendSelection");
 | 
						|
        am.put("selectNextColumnExtendSelection", action);
 | 
						|
 | 
						|
        action = new ListAction("selectFirstRow");
 | 
						|
        am.put("selectFirstRow", action);
 | 
						|
        action = new ListAction("selectLastRow");
 | 
						|
        am.put("selectLastRow", action);
 | 
						|
        action = new ListAction("selectFirstRowExtendSelection");
 | 
						|
        am.put("selectFirstRowExtendSelection", action);
 | 
						|
        action = new ListAction("selectLastRowExtendSelection");
 | 
						|
        am.put("selectLastRowExtendSelection", action);
 | 
						|
 | 
						|
        action = new ListAction("scrollUp");
 | 
						|
        am.put("scrollUp", action);
 | 
						|
        action = new ListAction("scrollUpExtendSelection");
 | 
						|
        am.put("scrollUpExtendSelection", action);
 | 
						|
        action = new ListAction("scrollDown");
 | 
						|
        am.put("scrollDown", action);
 | 
						|
        action = new ListAction("scrollDownExtendSelection");
 | 
						|
        am.put("scrollDownExtendSelection", action);
 | 
						|
 | 
						|
        action = new ListAction("selectAll");
 | 
						|
        am.put("selectAll", action);
 | 
						|
        action = new ListAction("clearSelection");
 | 
						|
        am.put("clearSelection", action);
 | 
						|
 | 
						|
        am.put("copy", TransferHandler.getCopyAction());
 | 
						|
        am.put("cut", TransferHandler.getCutAction());
 | 
						|
        am.put("paste", TransferHandler.getPasteAction());
 | 
						|
 | 
						|
        UIManager.put("List.actionMap", am);
 | 
						|
      }
 | 
						|
 | 
						|
    SwingUtilities.replaceUIActionMap(list, am);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Uninstalls keyboard actions for this UI in the {@link JList}.
 | 
						|
   */
 | 
						|
  protected void uninstallKeyboardActions()
 | 
						|
  {
 | 
						|
    // Uninstall the InputMap.
 | 
						|
    InputMap im = SwingUtilities.getUIInputMap(list, JComponent.WHEN_FOCUSED);
 | 
						|
    if (im instanceof UIResource)
 | 
						|
      SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
 | 
						|
 | 
						|
    // Uninstall the ActionMap.
 | 
						|
    if (SwingUtilities.getUIActionMap(list) instanceof UIResource)
 | 
						|
      SwingUtilities.replaceUIActionMap(list, null);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Installs the various aspects of the UI in the {@link JList}. In
 | 
						|
   * particular, calls {@link #installDefaults}, {@link #installListeners}
 | 
						|
   * and {@link #installKeyboardActions}. Also saves a reference to the
 | 
						|
   * provided component, cast to a {@link JList}.
 | 
						|
   *
 | 
						|
   * @param c The {@link JList} to install the UI into
 | 
						|
   */
 | 
						|
  public void installUI(final JComponent c)
 | 
						|
  {
 | 
						|
    super.installUI(c);
 | 
						|
    list = (JList) c;
 | 
						|
    installDefaults();
 | 
						|
    installListeners();
 | 
						|
    installKeyboardActions();
 | 
						|
    maybeUpdateLayoutState();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Uninstalls all the aspects of the UI which were installed in {@link
 | 
						|
   * #installUI}. When finished uninstalling, drops the saved reference to
 | 
						|
   * the {@link JList}.
 | 
						|
   *
 | 
						|
   * @param c Ignored; the UI is uninstalled from the {@link JList}
 | 
						|
   * reference saved during the call to {@link #installUI}
 | 
						|
   */
 | 
						|
  public void uninstallUI(final JComponent c)
 | 
						|
  {
 | 
						|
    uninstallKeyboardActions();
 | 
						|
    uninstallListeners();
 | 
						|
    uninstallDefaults();
 | 
						|
    list = null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the size this list would prefer to assume. This is calculated by
 | 
						|
   * calling {@link #getCellBounds} over the entire list.
 | 
						|
   *
 | 
						|
   * @param c Ignored; uses the saved {@link JList} reference
 | 
						|
   *
 | 
						|
   * @return DOCUMENT ME!
 | 
						|
   */
 | 
						|
  public Dimension getPreferredSize(JComponent c)
 | 
						|
  {
 | 
						|
    maybeUpdateLayoutState();
 | 
						|
    int size = list.getModel().getSize();
 | 
						|
    int visibleRows = list.getVisibleRowCount();
 | 
						|
    int layoutOrientation = list.getLayoutOrientation();
 | 
						|
 | 
						|
    int h;
 | 
						|
    int w;
 | 
						|
    int maxCellHeight = cellHeight;
 | 
						|
    if (maxCellHeight <= 0)
 | 
						|
      {
 | 
						|
        for (int i = 0; i < cellHeights.length; i++)
 | 
						|
          maxCellHeight = Math.max(maxCellHeight, cellHeights[i]);
 | 
						|
      }
 | 
						|
    if (layoutOrientation == JList.HORIZONTAL_WRAP)
 | 
						|
      {
 | 
						|
        if (visibleRows > 0)
 | 
						|
          {
 | 
						|
            // We cast to double here to force double divisions.
 | 
						|
            double modelSize = size;
 | 
						|
            int neededColumns = (int) Math.ceil(modelSize / visibleRows);
 | 
						|
            int adjustedRows = (int) Math.ceil(modelSize / neededColumns);
 | 
						|
            h = maxCellHeight * adjustedRows;
 | 
						|
            w = cellWidth * neededColumns;
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            int neededColumns = Math.min(1, list.getWidth() / cellWidth);
 | 
						|
            h = size / neededColumns * maxCellHeight;
 | 
						|
            w = neededColumns * cellWidth;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else if (layoutOrientation == JList.VERTICAL_WRAP)
 | 
						|
      {
 | 
						|
        if (visibleRows > 0)
 | 
						|
          h = visibleRows * maxCellHeight;
 | 
						|
        else
 | 
						|
          h = Math.max(list.getHeight(), maxCellHeight);
 | 
						|
        int neededColumns = h / maxCellHeight;
 | 
						|
        w = cellWidth * neededColumns;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (list.getFixedCellWidth() > 0)
 | 
						|
          w = list.getFixedCellWidth();
 | 
						|
        else
 | 
						|
          w = cellWidth;
 | 
						|
        if (list.getFixedCellHeight() > 0)
 | 
						|
          // FIXME: We need to add some cellVerticalMargins here, according
 | 
						|
          // to the specs.
 | 
						|
          h = list.getFixedCellHeight() * size;
 | 
						|
        else
 | 
						|
          h = maxCellHeight * size;
 | 
						|
      }
 | 
						|
    Insets insets = list.getInsets();
 | 
						|
    Dimension retVal = new Dimension(w + insets.left + insets.right,
 | 
						|
                                     h + insets.top + insets.bottom);
 | 
						|
    return retVal;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Paints a single cell in the list.
 | 
						|
   *
 | 
						|
   * @param g The graphics context to paint in
 | 
						|
   * @param row The row number to paint
 | 
						|
   * @param bounds The bounds of the cell to paint, assuming a coordinate
 | 
						|
   * system beginning at <code>(0,0)</code> in the upper left corner of the
 | 
						|
   * list
 | 
						|
   * @param rend A cell renderer to paint with
 | 
						|
   * @param data The data to provide to the cell renderer
 | 
						|
   * @param sel A selection model to provide to the cell renderer
 | 
						|
   * @param lead The lead selection index of the list
 | 
						|
   */
 | 
						|
  protected void paintCell(Graphics g, int row, Rectangle bounds,
 | 
						|
                 ListCellRenderer rend, ListModel data,
 | 
						|
                 ListSelectionModel sel, int lead)
 | 
						|
  {
 | 
						|
    boolean isSel = list.isSelectedIndex(row);
 | 
						|
    boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus();
 | 
						|
    Component comp = rend.getListCellRendererComponent(list,
 | 
						|
                                                       data.getElementAt(row),
 | 
						|
                                                       row, isSel, hasFocus);
 | 
						|
    rendererPane.paintComponent(g, comp, list, bounds);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Paints the list by repeatedly calling {@link #paintCell} for each visible
 | 
						|
   * cell in the list.
 | 
						|
   *
 | 
						|
   * @param g The graphics context to paint with
 | 
						|
   * @param c Ignored; uses the saved {@link JList} reference
 | 
						|
   */
 | 
						|
  public void paint(Graphics g, JComponent c)
 | 
						|
  {
 | 
						|
    int nrows = list.getModel().getSize();
 | 
						|
    if (nrows == 0)
 | 
						|
      return;
 | 
						|
 | 
						|
    maybeUpdateLayoutState();
 | 
						|
    ListCellRenderer render = list.getCellRenderer();
 | 
						|
    ListModel model = list.getModel();
 | 
						|
    ListSelectionModel sel = list.getSelectionModel();
 | 
						|
    int lead = sel.getLeadSelectionIndex();
 | 
						|
    Rectangle clip = g.getClipBounds();
 | 
						|
 | 
						|
    int startIndex = locationToIndex(list, new Point(clip.x, clip.y));
 | 
						|
    int endIndex = locationToIndex(list, new Point(clip.x + clip.width,
 | 
						|
                                             clip.y + clip.height));
 | 
						|
 | 
						|
    for (int row = startIndex; row <= endIndex; ++row)
 | 
						|
      {
 | 
						|
        Rectangle bounds = getCellBounds(list, row, row);
 | 
						|
        if (bounds != null && bounds.intersects(clip))
 | 
						|
          paintCell(g, row, bounds, render, model, sel, lead);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Computes the index of a list cell given a point within the list. If the
 | 
						|
   * location lies outside the bounds of the list, the greatest index in the
 | 
						|
   * list model is returned.
 | 
						|
   *
 | 
						|
   * @param l the list which on which the computation is based on
 | 
						|
   * @param location the coordinates
 | 
						|
   *
 | 
						|
   * @return the index of the list item that is located at the given
 | 
						|
   *         coordinates or <code>-1</code> if the list model is empty
 | 
						|
   */
 | 
						|
  public int locationToIndex(JList l, Point location)
 | 
						|
  {
 | 
						|
    int layoutOrientation = list.getLayoutOrientation();
 | 
						|
    int index = -1;
 | 
						|
    switch (layoutOrientation)
 | 
						|
      {
 | 
						|
      case JList.VERTICAL:
 | 
						|
        index = convertYToRow(location.y);
 | 
						|
        break;
 | 
						|
      case JList.HORIZONTAL_WRAP:
 | 
						|
        // determine visible rows and cells per row
 | 
						|
        int maxCellHeight = getCellHeight(0);
 | 
						|
        int visibleRows = list.getHeight() / maxCellHeight;
 | 
						|
        int cellsPerRow = -1;
 | 
						|
        int numberOfItems = list.getModel().getSize();
 | 
						|
        cellsPerRow = numberOfItems / visibleRows + 1;
 | 
						|
 | 
						|
        // determine index for the given location
 | 
						|
        int cellsPerColumn = numberOfItems / cellsPerRow + 1;
 | 
						|
        int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
 | 
						|
        int gridY = Math.min(location.y / maxCellHeight, cellsPerColumn);
 | 
						|
        index = gridX + gridY * cellsPerRow;
 | 
						|
        break;
 | 
						|
      case JList.VERTICAL_WRAP:
 | 
						|
        // determine visible rows and cells per column
 | 
						|
        int maxCellHeight2 = getCellHeight(0);
 | 
						|
        int visibleRows2 = list.getHeight() / maxCellHeight2;
 | 
						|
        int numberOfItems2 = list.getModel().getSize();
 | 
						|
        int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
 | 
						|
 | 
						|
        int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
 | 
						|
        int gridY2 = Math.min(location.y / maxCellHeight2, visibleRows2);
 | 
						|
        index = gridY2 + gridX2 * visibleRows2;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    return index;
 | 
						|
  }
 | 
						|
 | 
						|
  public Point indexToLocation(JList l, int index)
 | 
						|
  {
 | 
						|
    int layoutOrientation = list.getLayoutOrientation();
 | 
						|
    Point loc = null;
 | 
						|
    switch (layoutOrientation)
 | 
						|
      {
 | 
						|
      case JList.VERTICAL:
 | 
						|
        loc = new Point(0, convertRowToY(index));
 | 
						|
        break;
 | 
						|
      case JList.HORIZONTAL_WRAP:
 | 
						|
        // determine visible rows and cells per row
 | 
						|
        int maxCellHeight = getCellHeight(0);
 | 
						|
        int visibleRows = list.getHeight() / maxCellHeight;
 | 
						|
        int numberOfCellsPerRow = -1;
 | 
						|
        int numberOfItems = list.getModel().getSize();
 | 
						|
        numberOfCellsPerRow = numberOfItems / visibleRows + 1;
 | 
						|
 | 
						|
        // compute coordinates inside the grid
 | 
						|
        int gridX = index % numberOfCellsPerRow;
 | 
						|
        int gridY = index / numberOfCellsPerRow;
 | 
						|
        int locX = gridX * cellWidth;
 | 
						|
        int locY;
 | 
						|
        locY = gridY * maxCellHeight;
 | 
						|
        loc = new Point(locX, locY);
 | 
						|
        break;
 | 
						|
      case JList.VERTICAL_WRAP:
 | 
						|
        // determine visible rows and cells per column
 | 
						|
        int maxCellHeight2 = getCellHeight(0);
 | 
						|
        int visibleRows2 = list.getHeight() / maxCellHeight2;
 | 
						|
        // compute coordinates inside the grid
 | 
						|
        if (visibleRows2 > 0)
 | 
						|
          {
 | 
						|
            int gridY2 = index % visibleRows2;
 | 
						|
            int gridX2 = index / visibleRows2;
 | 
						|
            int locX2 = gridX2 * cellWidth;
 | 
						|
            int locY2 = gridY2 * maxCellHeight2;
 | 
						|
            loc = new Point(locX2, locY2);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          loc = new Point(0, convertRowToY(index));
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    return loc;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns the focus listener for this UI.
 | 
						|
   *
 | 
						|
   * @return the focus listener for this UI
 | 
						|
   */
 | 
						|
  protected FocusListener createFocusListener()
 | 
						|
  {
 | 
						|
    return new FocusHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns the list data listener for this UI.
 | 
						|
   *
 | 
						|
   * @return the list data listener for this UI
 | 
						|
   */
 | 
						|
  protected ListDataListener createListDataListener()
 | 
						|
  {
 | 
						|
    return new ListDataHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns the list selection listener for this UI.
 | 
						|
   *
 | 
						|
   * @return the list selection listener for this UI
 | 
						|
   */
 | 
						|
  protected ListSelectionListener createListSelectionListener()
 | 
						|
  {
 | 
						|
    return new ListSelectionHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns the mouse input listener for this UI.
 | 
						|
   *
 | 
						|
   * @return the mouse input listener for this UI
 | 
						|
   */
 | 
						|
  protected MouseInputListener createMouseInputListener()
 | 
						|
  {
 | 
						|
    return new MouseInputHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates and returns the property change listener for this UI.
 | 
						|
   *
 | 
						|
   * @return the property change listener for this UI
 | 
						|
   */
 | 
						|
  protected PropertyChangeListener createPropertyChangeListener()
 | 
						|
  {
 | 
						|
    return new PropertyChangeHandler();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Selects the next list item and force it to be visible.
 | 
						|
   */
 | 
						|
  protected void selectNextIndex()
 | 
						|
  {
 | 
						|
    int index = list.getSelectionModel().getLeadSelectionIndex();
 | 
						|
    if (index < list.getModel().getSize() - 1)
 | 
						|
      {
 | 
						|
        index++;
 | 
						|
        list.setSelectedIndex(index);
 | 
						|
      }
 | 
						|
    list.ensureIndexIsVisible(index);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Selects the previous list item and force it to be visible.
 | 
						|
   */
 | 
						|
  protected void selectPreviousIndex()
 | 
						|
  {
 | 
						|
    int index = list.getSelectionModel().getLeadSelectionIndex();
 | 
						|
    if (index > 0)
 | 
						|
      {
 | 
						|
        index--;
 | 
						|
        list.setSelectedIndex(index);
 | 
						|
      }
 | 
						|
    list.ensureIndexIsVisible(index);
 | 
						|
  }
 | 
						|
}
 |