Package javax.swing.plaf.basic

Source Code of javax.swing.plaf.basic.BasicListUI$PropertyChangeHandler

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
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.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.CellRendererPane;
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.Timer;
import javax.swing.TransferHandler;
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.ComponentUI;
import javax.swing.plaf.ListUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.Position;

import org.apache.harmony.x.swing.ExtendedListElement;
import org.apache.harmony.x.swing.ExtendedListFactory;
import org.apache.harmony.x.swing.StringConstants;
import org.apache.harmony.x.swing.Utilities;

import org.apache.harmony.x.swing.internal.nls.Messages;

public class BasicListUI extends ListUI {
    protected int cellHeight = -1;
    protected int cellWidth = -1;
    protected int[] cellHeights;
    protected JList list;
    protected CellRendererPane rendererPane;
    protected int updateLayoutStateNeeded = modelChanged;

    protected static final int modelChanged = 1;
    protected static final int selectionModelChanged = 2;
    protected static final int fontChanged = 4;
    protected static final int fixedCellWidthChanged = 8;
    protected static final int fixedCellHeightChanged = 16;
    protected static final int prototypeCellValueChanged = 32;
    protected static final int cellRendererChanged = 64;
    private static final int otherChanged = 128;

    protected FocusListener focusListener;
    protected ListDataListener listDataListener;
    protected ListSelectionListener listSelectionListener;
    protected MouseInputListener mouseInputListener;
    protected PropertyChangeListener propertyChangeListener;
    private ComponentListener componentListener;
    private KeyListener keyListener;

    boolean extendedSupportEnabled;


    final ListLayouter layouter = new ListLayouter();

    public class FocusHandler implements FocusListener {
        public void focusGained(final FocusEvent e) {
            repaintCellFocus();
        }

        public void focusLost(final FocusEvent e) {
            repaintCellFocus();
        }

        protected void repaintCellFocus() {
            if (list.getLeadSelectionIndex() != -1) {
                Rectangle bounds = getCellBounds(list, list.getLeadSelectionIndex(), list.getLeadSelectionIndex());
                if (bounds != null) {
                    list.repaint(bounds);
                } else {
                    list.repaint();
                }
            }
        }
    }

    public class ListDataHandler implements ListDataListener {
        public void contentsChanged(final ListDataEvent e) {
            layouter.reset();
            list.revalidate();
            list.repaint();
        }

        public void intervalAdded(final ListDataEvent e) {
            list.getSelectionModel().insertIndexInterval(e.getIndex0(), Math.abs(e.getIndex1() - e.getIndex0()) + 1, false);
            layouter.reset();
            list.revalidate();
            list.repaint();
        }

        public void intervalRemoved(final ListDataEvent e) {
            list.getSelectionModel().removeIndexInterval(e.getIndex0(), e.getIndex1());
            layouter.reset();
            list.revalidate();
            list.repaint();
        }
    }

    public class ListSelectionHandler implements ListSelectionListener {
        public void valueChanged(final ListSelectionEvent e) {
            repaintCells(e.getFirstIndex(), e.getLastIndex());
        }
    }

    public class MouseInputHandler implements MouseInputListener {
        private DnDMouseHelper dndhelper = new DnDMouseHelper(list);

        public void mousePressed(final MouseEvent e) {
            if (!SwingUtilities.isLeftMouseButton(e)) {
                return;
            }
            list.requestFocus();

            int cellIndex = locationToIndex(list, e.getPoint());
            dndhelper.mousePressed(e, list.getDragEnabled(), cellIndex != -1,
                                   list.isSelectedIndex(cellIndex));
            if (cellIndex == -1) {
                return;
            }
            if (!list.getDragEnabled() || !list.isSelectedIndex(cellIndex)) {
                list.getSelectionModel().setValueIsAdjusting(true);
                processSelection(e, cellIndex);
            }
        }

        public void mouseReleased(final MouseEvent e) {
            dndhelper.mouseReleased(e);
            if (dndhelper.shouldProcessOnRelease()) {
                int cellIndex = locationToIndex(list, e.getPoint());
                if (cellIndex != -1) {
                    processSelection(e, cellIndex);
                }
            }
            list.getSelectionModel().setValueIsAdjusting(false);
        }

        public void mouseDragged(final MouseEvent e) {
            dndhelper.mouseDragged(e);

            if (!dndhelper.isDndStarted()
                && SwingUtilities.isLeftMouseButton(e)
                && !e.isControlDown()
                && !e.isShiftDown()) {

                int cellIndex = locationToIndex(list, e.getPoint());
                if (cellIndex != -1 && list.getLeadSelectionIndex() != cellIndex) {
                    list.getSelectionModel().setValueIsAdjusting(true);
                    list.setSelectedIndex(cellIndex);
                    list.ensureIndexIsVisible(cellIndex);
                }
            }
        }

        public void mouseClicked(final MouseEvent e) {
        }

        public void mouseEntered(final MouseEvent e) {
        }

        public void mouseExited(final MouseEvent e) {
        }

        public void mouseMoved(final MouseEvent e) {
        }


        private void processSelection(final MouseEvent e, final int cellIndex) {
            if (e.isControlDown()) {
                if (list.isSelectedIndex(cellIndex)) {
                    list.removeSelectionInterval(cellIndex, cellIndex);
                } else {
                    list.addSelectionInterval(cellIndex, cellIndex);
                }
            } else if (e.isShiftDown() && list.getAnchorSelectionIndex() != -1) {
                list.setSelectionInterval(list.getAnchorSelectionIndex(), cellIndex);
            } else {
                list.setSelectionInterval(cellIndex, cellIndex);
            }
        }
    }

    public class PropertyChangeHandler implements PropertyChangeListener {
        public void propertyChange(final PropertyChangeEvent event) {
            String changedProperty = event.getPropertyName();
            if ("cellRenderer".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | cellRendererChanged;
            } else if ("fixedCellHeight".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | fixedCellHeightChanged;
            } else if ("fixedCellWidth".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | fixedCellWidthChanged;
            } else if ("model".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | modelChanged;
            } else if ("selectionModel".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | selectionModelChanged;
            } else if ("prototypeCellValue".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | prototypeCellValueChanged;
            } else if ("font".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | fontChanged;
            } else if ("visibleRowCount".equals(changedProperty)
                       || "layoutOrientation".equals(changedProperty)
                       || "componentOrientation".equals(changedProperty)
                       || "width".equals(changedProperty)
                       || "height".equals(changedProperty)) {
                updateLayoutStateNeeded = updateLayoutStateNeeded | otherChanged;
            } else if (StringConstants.COMPONENT_ORIENTATION.equals(changedProperty)) {
                uninstallKeyboardActions();
                installKeyboardActions();
            }

            if ("model".equals(changedProperty)) {
                ((ListModel)event.getOldValue()).removeListDataListener(listDataListener);
                ((ListModel)event.getNewValue()).addListDataListener(listDataListener);
            }
            if ("selectionModel".equals(changedProperty)) {
                ((ListSelectionModel)event.getOldValue()).removeListSelectionListener(listSelectionListener);
                ((ListSelectionModel)event.getNewValue()).addListSelectionListener(listSelectionListener);
            }

            if ("enabled".equals(changedProperty)) {
                if (((Boolean)event.getNewValue()).booleanValue()) {
                    list.addMouseListener(mouseInputListener);
                    list.addMouseMotionListener(mouseInputListener);
                } else {
                    list.removeMouseListener(mouseInputListener);
                    list.removeMouseMotionListener(mouseInputListener);
                }
            }

            if (StringConstants.EXTENDED_SUPPORT_ENABLED_PROPERTY.equals(changedProperty)) {
                extendedSupportEnabled = ((Boolean)event.getNewValue()).booleanValue();
                if (extendedSupportEnabled) {
                    list.setCellRenderer(ExtendedListFactory.getFactory().createExtendedRenderer());
                } else {
                    list.setCellRenderer((ListCellRenderer)UIManager.get("List.cellRenderer"));
                }
                updateLayoutStateNeeded = updateLayoutStateNeeded | otherChanged;
            }

            list.revalidate();
            list.repaint();
        }
    }

    private class ComponentReshapeHandler extends ComponentAdapter {
        @Override
        public void componentResized(final ComponentEvent e) {
            if (layouter.getLayoutStrategy().isSizeDependent()) {
                layouter.reset();
                list.repaint();
            }
        }
    }

    private class KeyHandler extends KeyAdapter {
        private StringBuffer searchPrefix = new StringBuffer();
        private Timer searchTimer = new Timer(1000, new AbstractAction() {
            /**
             * This class is not guaranteed to be correctly deserialized.
             */
            private static final long serialVersionUID = 1L;

            public void actionPerformed(final ActionEvent e) {
                resetSearch();
            }
        });

        @Override
        public void keyTyped(final KeyEvent e) {
            if (list.getModel().getSize() == 0) {
                return;
            }

            if (e.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
                return;
            }
            if ((e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0
                || (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {

                return;
            }

            searchPrefix.append(e.getKeyChar());

            int startIndex = list.getSelectedIndex();
            if (startIndex == -1) {
                startIndex = 0;
            }

            int nextIndex = getNextMatch(startIndex);
            if (nextIndex == -1) {
                if (searchPrefix.length() > 1) {
                    resetSearch();
                    searchPrefix.append(e.getKeyChar());
                    startIndex++;
                    if (startIndex >= list.getModel().getSize()) {
                        startIndex = 0;
                    }
                    nextIndex = getNextMatch(startIndex);
                }
            }
            if (nextIndex != -1) {
                searchTimer.stop();
                list.setSelectedIndex(nextIndex);
                list.ensureIndexIsVisible(nextIndex);
                searchTimer.restart();
            } else {
                resetSearch();
            }
        }

        private int getNextMatch(final int startIndex) {
            int candidateIndex = list.getNextMatch(searchPrefix.toString(), startIndex, Position.Bias.Forward);
            if (candidateIndex == -1 || !extendedSupportEnabled) {
                return candidateIndex;
            }

            int firstFoundIndex = candidateIndex;
            do {
                Object value = list.getModel().getElementAt(candidateIndex);
                if (!(value instanceof ExtendedListElement)
                    || ((ExtendedListElement)value).isChoosable()) {

                    return candidateIndex;
                }
                int nextStartIndex = candidateIndex + 1;
                if (nextStartIndex == list.getModel().getSize()) {
                    nextStartIndex = 0;
                }
                candidateIndex = list.getNextMatch(searchPrefix.toString(), nextStartIndex, Position.Bias.Forward);
            } while(firstFoundIndex != candidateIndex);

            return -1;
        }

        private void resetSearch() {
            searchPrefix.delete(0, searchPrefix.length());
            searchTimer.stop();
        }
    };


    private class ListTransferHandler extends TransferHandler {
        /**
         * This class is not guaranteed to be correctly deserialized.
         */
        private static final long serialVersionUID = -3865101058747175640L;

        private final String lineSeparator = System.getProperty("line.separator");

        @Override
        public int getSourceActions(final JComponent c) {
            return COPY;
        }

        @Override
        protected Transferable createTransferable(final JComponent c) {
            Object[] selectedValues = list.getSelectedValues();
            if (selectedValues == null || selectedValues.length == 0) {
                return null;
            }

            StringBuffer content = new StringBuffer();
            for (int i = 0; i < selectedValues.length; i++) {
                content.append(selectedValues[i]);
                if (i < selectedValues.length - 1) {
                    content.append(lineSeparator);
                }
            }
            return new StringSelection(content.toString());
        }
    }



    public static ComponentUI createUI(final JComponent list) {
        return new BasicListUI();
    }

    @Override
    public Rectangle getCellBounds(final JList list, final int index1, final int index2) {
        layouter.setList(list);
        maybeUpdateLayoutState();

        Rectangle result = null;
        if (index1 < 0 || index1 >= list.getModel().getSize()
            || index2 < 0 || index2 >= list.getModel().getSize()) {

            return result;
        }

        if (index1 <= index2) {
            for (int i = index1; i <= index2; i++) {
                if (result == null) {
                    result = layouter.getLayoutStrategy().getBounds(i);
                } else {
                    result.add(layouter.getLayoutStrategy().getBounds(i));
                }
            }
        } else {
            for (int i = index2; i <= index1; i++) {
                if (result == null) {
                    result = layouter.getLayoutStrategy().getBounds(i);
                } else {
                    result.add(layouter.getLayoutStrategy().getBounds(i));
                }
            }
        }

        return result;
    }

    @Override
    public Dimension getMaximumSize(final JComponent c) {
        return getPreferredSize(c);
    }

    @Override
    public Dimension getMinimumSize(final JComponent c) {
        return getPreferredSize(c);
    }

    @Override
    public Dimension getPreferredSize(final JComponent c) {
        maybeUpdateLayoutState();

        JList list = (JList)c;
        layouter.setList(list);
        return layouter.getLayoutStrategy().getSize();
    }

    @Override
    public Point indexToLocation(final JList list, final int index) {
        if (index < 0 || index >= list.getModel().getSize()) {
            return null;
        }

        layouter.setList(list);
        maybeUpdateLayoutState();

        Rectangle bounds = getCellBounds(list, index, index);
        return new Point(bounds.x, bounds.y);
    }

    @Override
    public int locationToIndex(final JList list, final Point location) {
        if (location == null) {
            throw new NullPointerException();
        }

        layouter.setList(list);
        maybeUpdateLayoutState();

        return layouter.getNearestIndex(location);
    }

    @Override
    public void paint(final Graphics g, final JComponent c) {
        maybeUpdateLayoutState();

        Rectangle clipRect = g.getClipBounds();
        if (clipRect != null) {
            for (int i = 0; i < list.getModel().getSize(); i++) {
                Rectangle bounds = layouter.getLayoutStrategy().getBounds(i);
                if (bounds.intersects(clipRect)) {
                    paintCell(g, i, bounds, list.getCellRenderer(), list.getModel(), list.getSelectionModel(), i);
                }
            }
        } else {
            for (int i = 0; i < list.getModel().getSize(); i++) {
                Rectangle bounds = layouter.getLayoutStrategy().getBounds(i);
                paintCell(g, i, bounds, list.getCellRenderer(), list.getModel(), list.getSelectionModel(), i);
            }
        }
    }


    @Override
    public void installUI(final JComponent c) {
        list = (JList)c;
        rendererPane = new CellRendererPane();
        rendererPane.setVisible(false);
        list.add(rendererPane);
        layouter.setList(list);

        installDefaults();
        installListeners();
        installKeyboardActions();
    }

    @Override
    public void uninstallUI(final JComponent c) {
        list = (JList)c;
        list.remove(rendererPane);
        rendererPane = null;
        layouter.setList(null);

        uninstallDefaults();
        uninstallListeners();
        uninstallKeyboardActions();
    }

    protected void installDefaults() {
        LookAndFeel.installColorsAndFont(list, "List.background", "List.foreground", "List.font");
        LookAndFeel.installBorder(list, "List.border");
        LookAndFeel.installProperty(list, "opaque", Boolean.TRUE);

        if ((list.getSelectionBackground() == null) || (list.getSelectionBackground() instanceof UIResource)) {
            list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
        }
        if ((list.getSelectionForeground() == null) || (list.getSelectionForeground() instanceof UIResource)) {
            list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
        }

        if ((list.getCellRenderer() == null) || (list.getCellRenderer() instanceof UIResource)) {
            list.setCellRenderer((ListCellRenderer)UIManager.get("List.cellRenderer"));
        }

        list.setTransferHandler(new ListTransferHandler());
    }

    protected void installKeyboardActions() {
        BasicListKeyboardActions.installKeyboardActions(list);
    }

    protected void installListeners() {
        focusListener = createFocusListener();
        list.addFocusListener(focusListener);

        listSelectionListener = createListSelectionListener();
        list.getSelectionModel().addListSelectionListener(listSelectionListener);

        listDataListener = createListDataListener();
        list.getModel().addListDataListener(listDataListener);

        propertyChangeListener = createPropertyChangeListener();
        list.addPropertyChangeListener(propertyChangeListener);

        mouseInputListener = createMouseInputListener();
        list.addMouseListener(mouseInputListener);
        list.addMouseMotionListener(mouseInputListener);

        componentListener = createComponentListener();
        list.addComponentListener(componentListener);

        keyListener = new KeyHandler();
        list.addKeyListener(keyListener);
    }

    protected void uninstallDefaults() {
        LookAndFeel.uninstallBorder(list);
        if (list.getCellRenderer() instanceof UIResource) {
            list.setCellRenderer(null);
        }
        Utilities.uninstallColorsAndFont(list);

        list.setTransferHandler(null);
    }

    protected void uninstallKeyboardActions() {
        BasicListKeyboardActions.uninstallKeyboardActions(list);
    }

    protected void uninstallListeners() {
        list.removeListSelectionListener(listSelectionListener);
        list.getSelectionModel().removeListSelectionListener(listSelectionListener);
        listSelectionListener = null;

        list.getModel().removeListDataListener(listDataListener);
        listDataListener = null;

        list.removeFocusListener(focusListener);
        focusListener = null;

        list.removePropertyChangeListener(propertyChangeListener);
        propertyChangeListener = null;

        list.removeMouseListener(mouseInputListener);
        list.removeMouseMotionListener(mouseInputListener);
        mouseInputListener = null;

        list.removeComponentListener(componentListener);
        componentListener = null;

        list.removeKeyListener(keyListener);
        keyListener = null;
    }

    protected void updateLayoutState() {
        layouter.reinit();
    }

    protected void maybeUpdateLayoutState() {
        if (updateLayoutStateNeeded > 0) {
            updateLayoutState();
            updateLayoutStateNeeded = 0;
        }
    }

    protected void paintCell(final Graphics g, final int row, final Rectangle rowBounds, final ListCellRenderer cellRenderer, final ListModel dataModel, final ListSelectionModel selModel, final int leadIndex) {
        if (list.getCellRenderer() != null){
             Component renderer = list.getCellRenderer().getListCellRendererComponent(list, dataModel.getElementAt(row), row, selModel.isSelectedIndex(row), list.isFocusOwner() && selModel.getLeadSelectionIndex() == row);
             rendererPane.paintComponent(g, renderer, list, rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height);
      }
    }

    protected void selectNextIndex() {
        int currectSelection = list.getMinSelectionIndex();

        if (currectSelection < list.getModel().getSize() - 1 && list.getModel().getSize() > 0) {
            list.setSelectedIndex(currectSelection + 1);
            list.ensureIndexIsVisible(currectSelection + 1);
        }
    }

    protected void selectPreviousIndex() {
        int currectSelection = list.getMinSelectionIndex();
        if (currectSelection > 0) {
            list.setSelectedIndex(currectSelection - 1);
            list.ensureIndexIsVisible(currectSelection - 1);
        }
    }

    protected int getRowHeight(final int row) {
        if (row < 0 || row >= list.getModel().getSize()) {
            return -1;
        }

        return layouter.getLayoutStrategy().getBounds(row).height;
    }

    protected int convertRowToY(final int row) {
        if (row < 0 || row >= list.getModel().getSize()) {
            return -1;
        }

        return getCellBounds(list, row, row).y;
    }

    protected int convertYToRow(final int y) {
        for (int row = 0; row < list.getModel().getSize(); row++) {
            Rectangle bounds = getCellBounds(list, row, row);
            if (bounds.y <= y && bounds.y + bounds.height >= y) {
                return row;
            }
        }

        return -1;
    }

    protected FocusListener createFocusListener() {
        return new FocusHandler();
    }

    protected ListDataListener createListDataListener() {
        return new ListDataHandler();
    }

    protected ListSelectionListener createListSelectionListener() {
        return new ListSelectionHandler();
    }

    protected MouseInputListener createMouseInputListener() {
        return new MouseInputHandler();
    }

    protected PropertyChangeListener createPropertyChangeListener() {
        return new PropertyChangeHandler();
    }


    boolean isChoosable(final int index) {
        if (!extendedSupportEnabled) {
            return true;
        }
        Object value = list.getModel().getElementAt(index);
        return (value instanceof ExtendedListElement) ? ((ExtendedListElement)value).isChoosable() : true;
    }


    private ComponentListener createComponentListener() {
        return new ComponentReshapeHandler();
    }


    private void repaintCells(final int firstIndex, final int lastIndex) {
        if (firstIndex == -1 || lastIndex == -1) {
            return;
        }
        int modelSize = list.getModel().getSize();
        int adjustedFirstIndex = firstIndex;
        if (adjustedFirstIndex >= modelSize) {
            adjustedFirstIndex = modelSize - 1;
        }
        int adjustedLastIndex = lastIndex;
        if (adjustedLastIndex >= modelSize) {
            adjustedLastIndex = modelSize - 1;
        }

        Rectangle bounds = getCellBounds(list, adjustedFirstIndex, adjustedLastIndex);
        if (bounds != null) {
            list.repaint(bounds);
        }
    }


    interface LayoutStrategy {
        Rectangle getBounds(final int index);
        int getRow(final int index);
        int getColumn(final int index);
        int getIndex(final int row, final int column);
        int getRowCount();
        int getColumnCount();
        Dimension getSize();
        boolean isSizeDependent();
    }

    final class ListLayouter {
        private JList list;
        private Insets insets;
        private LayoutStrategy strategy;
        private Insets cachedInsets = new Insets(0, 0, 0, 0);

        public abstract class AbstractLayoutStrategy implements LayoutStrategy {
            public abstract int getRowCount();
            public abstract int getColumnCount();

            public AbstractLayoutStrategy() {
                cellWidth = getMaximumWidth();
                cellHeight = getMaximumHeight();
                cellHeights = null;
            }

            public Rectangle getBounds(final int index) {
                return new Rectangle(insets.left + getColumn(index) * cellWidth, insets.top + getRow(index) * cellHeight, cellWidth, cellHeight);
            }

            public Dimension getSize() {
                Dimension result = new Dimension(getColumnCount() * cellWidth, getRowCount() * cellHeight);
                return Utilities.addInsets(result, list.getInsets(cachedInsets));
            }

            public boolean isSizeDependent() {
                return list.getVisibleRowCount() <= 0;
            }
        }

        public class VerticalLayoutStategy extends AbstractLayoutStrategy {
            public VerticalLayoutStategy() {
                if (list.getFixedCellHeight() <= 0) {
                    cellHeight = -1;

                    cellHeights = new int[getNumberOfElements()];
                    for (int i = 0; i < getNumberOfElements(); i++) {
                        cellHeights[i] = getPreferredSize(i).height;
                    }
                }
            }

            @Override
            public Rectangle getBounds(final int index) {
                int y = 0;
                int height = cellHeight;
                if (cellHeight == -1) {
                    for (int i = 0; i < index; i++) {
                        y += cellHeights[i];
                    }
                    height = cellHeights[index];
                } else if (index > 0) {
                    y = cellHeight * index;
                }

                return new Rectangle(insets.left, insets.top + y, list.getWidth() - insets.left - insets.right, height);
            }

            public int getRow(final int index) {
                return index;
            }

            public int getColumn(final int index) {
                return 0;
            }

            public int getIndex(final int row, final int column) {
                return row;
            }

            @Override
            public int getRowCount() {
                return list.getModel().getSize();
            }

            @Override
            public int getColumnCount() {
                return getNumberOfElements() > 0 ? 1 : 0;
            }

            @Override
            public Dimension getSize() {
                int height = 0;
                if (cellHeight == -1) {
                    for (int i = 0; i < getRowCount(); i++) {
                        height += cellHeights[i];
                    }
                } else {
                    height = cellHeight * getRowCount();
                }

                Dimension result = new Dimension(getColumnCount() * cellWidth, height);
                return Utilities.addInsets(result, list.getInsets(cachedInsets));
            }

            @Override
            public boolean isSizeDependent() {
                return false;
            }
        }

        public class VerticalWrapVisibleSpecifiedLayoutStrategy extends AbstractLayoutStrategy {
            public int getRow(final int index) {
                return index % getRowCount();
            }

            public int getIndex(final int row, final int column) {
                return column * getRowCount() + row;
            }

            public int getColumn(final int index) {
                return index / getRowCount();
            }

            @Override
            public int getRowCount() {
                return list.getVisibleRowCount();
            }

            @Override
            public int getColumnCount() {
                int modelSize = list.getModel().getSize();
                return (modelSize == 0 ? 0 : modelSize - 1) / getRowCount() + 1;
            }
        }

        public class VerticalWrapVisibleUnspecifiedLayoutStrategy extends AbstractLayoutStrategy {
            private int maximumNumOfRows;

            public VerticalWrapVisibleUnspecifiedLayoutStrategy() {
                maximumNumOfRows = (list.getHeight() - insets.top - insets.bottom) / cellHeight;
                if (maximumNumOfRows == 0) {
                    maximumNumOfRows = 1;
                }
            }

            public int getIndex(final int row, final int column) {
                return column * getRowCount() + row;
            }

            public int getRow(final int index) {
                return index % getRowCount();
            }

            public int getColumn(final int index) {
                return index / getRowCount();
            }

            @Override
            public int getRowCount() {
                return maximumNumOfRows;
            }

            @Override
            public int getColumnCount() {
                int modelSize = list.getModel().getSize();
                return (modelSize == 0 ? 0 : modelSize - 1) / getRowCount() + 1;
            }
        }

        public class HorizontalWrapVisibleSpecifiedLayoutStrategy extends AbstractLayoutStrategy {
            public int getRow(final int index) {
                return index / getColumnCount();
            }

            public int getIndex(final int row, final int column) {
                return row * getColumnCount() + column;
            }

            public int getColumn(final int index) {
                return index % getColumnCount();
            }

            @Override
            public int getRowCount() {
                return list.getVisibleRowCount();
            }

            @Override
            public int getColumnCount() {
                int modelSize = list.getModel().getSize();
                return (modelSize == 0 ? 0 : modelSize - 1) / getRowCount() + 1;
            }
        }

        public class HorizontalWrapVisibleUnspecifiedLayoutStrategy extends AbstractLayoutStrategy {
            private int maximumNumOfColumns;

            public HorizontalWrapVisibleUnspecifiedLayoutStrategy() {
                maximumNumOfColumns = (list.getWidth() - insets.left - insets.right) / cellWidth;
                if (maximumNumOfColumns == 0) {
                    maximumNumOfColumns = 1;
                }
            }

            public int getIndex(final int row, final int column) {
                return row * getColumnCount() + column;
            }

            public int getRow(final int index) {
                return index / getColumnCount();
            }

            public int getColumn(final int index) {
                return index % getColumnCount();
            }

            @Override
            public int getRowCount() {
                int modelSize = list.getModel().getSize();
                return (modelSize == 0 ? 0 : modelSize - 1) / getColumnCount() + 1;
            }

            @Override
            public int getColumnCount() {
                return maximumNumOfColumns;
            }
        }

        public class OrientationStrategy implements LayoutStrategy {
            protected final LayoutStrategy strategy;

            public OrientationStrategy(final LayoutStrategy strategy) {
                this.strategy = strategy;
            }

            public int getIndex(final int row, final int column) {
                return strategy.getIndex(row, column);
            }

            public int getRowCount() {
                return strategy.getRowCount();
            }

            public int getColumnCount() {
                return strategy.getColumnCount();
            }

            public int getRow(final int index) {
                return strategy.getRow(index);
            }

            public int getColumn(final int index) {
                return strategy.getColumn(index);
            }

            public Dimension getSize() {
                return strategy.getSize();
            }

            public Rectangle getBounds(final int index) {
                return strategy.getBounds(index);
            }

            public boolean isSizeDependent() {
                return strategy.isSizeDependent();
            }
        }

        public class RightToLeftStrategy extends OrientationStrategy {
            public RightToLeftStrategy(final LayoutStrategy strategy) {
                super(strategy);
            }

            @Override
            public Rectangle getBounds(final int index) {
                Rectangle bounds = strategy.getBounds(index);
                int x = list.getWidth() - bounds.x - bounds.width;

                return new Rectangle(x, bounds.y, bounds.width, bounds.height);
            }
        }

        public ListLayouter() {
            reset();
        }

        public void setList(final JList list) {
            if (this.list != list) {
                this.list = list;
                reset();
            }
        }

        public void reinit() {
            reset();
            getLayoutStrategy();
        }

        public void reset() {
            strategy = null;
        }

        public LayoutStrategy getLayoutStrategy() {
            reinitFields();
            if (strategy == null) {
                if (list.getComponentOrientation().isLeftToRight()) {
                    strategy = getStrategy();
                } else {
                    strategy = new RightToLeftStrategy(getStrategy());
                }
            }

            return strategy;
        }

        private LayoutStrategy getStrategy() {
            LayoutStrategy result;

            if (list.getLayoutOrientation() == JList.VERTICAL) {
                result = new VerticalLayoutStategy();
            } else if (list.getLayoutOrientation() == JList.VERTICAL_WRAP) {
                if (list.getVisibleRowCount() > 0) {
                    result = new VerticalWrapVisibleSpecifiedLayoutStrategy();
                } else {
                    result = new VerticalWrapVisibleUnspecifiedLayoutStrategy();
                }
            } else if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) {
                if (list.getVisibleRowCount() > 0) {
                    result = new HorizontalWrapVisibleSpecifiedLayoutStrategy();
                } else {
                    result = new HorizontalWrapVisibleUnspecifiedLayoutStrategy();
                }
            } else {
                throw new IllegalArgumentException(Messages.getString("swing.70")); //$NON-NLS-1$
            }

            return result;
        }

        public int getNumberOfElements() {
            return list.getModel().getSize();
        }

        public JList getList() {
            return list;
        }

        public int getNearestIndex(final Point location) {
            int result = -1;
            int distance = Integer.MAX_VALUE;
            LayoutStrategy s = getLayoutStrategy();
            for (int i = 0; i < getNumberOfElements(); i++) {
                Rectangle bounds = s.getBounds(i);
                int d = getDistance(bounds, location);
                if (distance > d) {
                    distance = d;
                    result = i;
                }
            }

            return result;
        }

        private void reinitFields() {
            insets = list.getInsets(cachedInsets);
        }

        private int getMaximumWidth() {
            if (list.getFixedCellWidth() >= 0) {
                return list.getFixedCellWidth();
            }

            int result = -1;
            for (int i = 0; i < list.getModel().getSize(); i++) {
                Dimension size = getPreferredSize(i);
                if (result < size.width) {
                    result = size.width;
                }
            }
            return result;
        }

        private int getMaximumHeight() {
            if (list.getFixedCellHeight() >= 0) {
                return list.getFixedCellHeight();
            }

            int result = -1;
            for (int i = 0; i < list.getModel().getSize(); i++) {
                Dimension size = getPreferredSize(i);
                if (result < size.height) {
                    result = size.height;
                }
            }
            return result;
        }

        private Dimension getPreferredSize(final int i) {
            if (list.getCellRenderer() == null) {
                return new Dimension(0, 0);
            }
            return list.getCellRenderer().getListCellRendererComponent(list, list.getModel().getElementAt(i), i, false, false).getPreferredSize();
        }

        private int getDistance(final Rectangle r, final Point p) {
            int dx = 0;
            if (p.x < r.x) {
                dx = r.x - p.x;
            } else if (p.x > r.x + r.width) {
                dx = p.x - r.x - r.width;
            } else {
                dx = 0;
            }

            int dy = 0;
            if (p.y < r.y) {
                dy = r.y - p.y;
            } else if (p.y > r.y + r.height) {
                dy = p.y - r.y - r.height;
            } else {
                dy = 0;
            }

            return dx + dy;
        }
    }
}
TOP

Related Classes of javax.swing.plaf.basic.BasicListUI$PropertyChangeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.