📄 jxlist.java
字号:
/* * $Id: JXList.java,v 1.22 2005/10/14 14:29:50 kleopatra Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */package org.jdesktop.swingx;import java.awt.Component;import java.awt.Cursor;import java.awt.Point;import java.awt.Rectangle;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.beans.PropertyChangeEvent;import java.beans.PropertyChangeListener;import java.util.Vector;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.swing.AbstractButton;import javax.swing.AbstractListModel;import javax.swing.Action;import javax.swing.DefaultListCellRenderer;import javax.swing.JComponent;import javax.swing.JList;import javax.swing.KeyStroke;import javax.swing.ListCellRenderer;import javax.swing.ListModel;import javax.swing.event.ChangeEvent;import javax.swing.event.ChangeListener;import javax.swing.event.ListDataEvent;import javax.swing.event.ListDataListener;import org.jdesktop.swingx.decorator.ComponentAdapter;import org.jdesktop.swingx.decorator.FilterPipeline;import org.jdesktop.swingx.decorator.HighlighterPipeline;import org.jdesktop.swingx.decorator.PipelineEvent;import org.jdesktop.swingx.decorator.PipelineListener;import org.jdesktop.swingx.decorator.Selection;import org.jdesktop.swingx.decorator.Sorter;/** * JXList * * Enabled Rollover/LinkModel handling. Enabled Highlighter support. * * Added experimental support for filtering/sorting. This feature is disabled by * default because it has side-effects which might break "normal" expectations * when using a JList: if enabled all row coordinates (including those returned * by the selection) are in view coordinates. Furthermore, the model returned * from getModel() is a wrapper around the actual data. * * * * @author Ramesh Gupta * @author Jeanette Winzenburg */public class JXList extends JList { /** The pipeline holding the filters. */ protected FilterPipeline filters; /** * The pipeline holding the highlighters. */ protected HighlighterPipeline highlighters; /** listening to changeEvents from highlighterPipeline. */ private ChangeListener highlighterChangeListener; /** The ComponentAdapter for model data access. */ protected ComponentAdapter dataAdapter; /** * Mouse/Motion/Listener keeping track of mouse moved in cell coordinates. */ private RolloverProducer rolloverProducer; /** * RolloverController: listens to cell over events and repaints * entered/exited rows. */ private LinkController linkController; /** A wrapper around the default renderer enabling decoration. */ private DelegatingRenderer delegatingRenderer; private WrappingListModel wrappingModel; private PipelineListener pipelineListener; private boolean filterEnabled; private Selection selection; private Searchable searchable; public JXList() { init(); } public JXList(ListModel dataModel) { super(dataModel); init(); } public JXList(Object[] listData) { super(listData); init(); } public JXList(Vector listData) { super(listData); init(); } private void init() { Action findAction = createFindAction(); getActionMap().put("find", findAction); // JW: this should be handled by the LF! KeyStroke findStroke = KeyStroke.getKeyStroke("control F"); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find"); } private Action createFindAction() { Action findAction = new UIAction("find") { public void actionPerformed(ActionEvent e) { doFind(); } }; return findAction; } protected void doFind() { SearchFactory.getInstance().showFindInput(this, getSearchable()); } /** * * @returns a not-null Searchable for this editor. */ public Searchable getSearchable() { if (searchable == null) { searchable = new ListSearchable(); } return searchable; } /** * sets the Searchable for this editor. If null, a default * searchable will be used. * * @param searchable */ public void setSearchable(Searchable searchable) { this.searchable = searchable; } public class ListSearchable extends AbstractSearchable { @Override protected void findMatchAndUpdateState(Pattern pattern, int startRow, boolean backwards) { SearchResult searchResult = null; if (backwards) { for (int index = startRow; index >= 0 && searchResult == null; index--) { searchResult = findMatchAt(pattern, index); } } else { for (int index = startRow; index < getSize() && searchResult == null; index++) { searchResult = findMatchAt(pattern, index); } } updateState(searchResult); } @Override protected SearchResult findExtendedMatch(Pattern pattern, int row) { return findMatchAt(pattern, row); } /** * Matches the cell content at row/col against the given Pattern. * Returns an appropriate SearchResult if matching or null if no * matching * * @param pattern * @param row a valid row index in view coordinates * @param column a valid column index in view coordinates * @return */ protected SearchResult findMatchAt(Pattern pattern, int row) { Object value = getElementAt(row); if (value != null) { Matcher matcher = pattern.matcher(value.toString()); if (matcher.find()) { return createSearchResult(matcher, row, -1); } } return null; } @Override protected int getSize() { return getElementCount(); } @Override protected void moveMatchMarker() { setSelectedIndex(lastSearchResult.foundRow); if (lastSearchResult.foundRow >= 0) { ensureIndexIsVisible(lastSearchResult.foundRow); } } } /** * Property to enable/disable rollover support. This can be enabled to show * "live" rollover behaviour, f.i. the cursor over LinkModel cells. Default * is disabled. * * @param rolloverEnabled */ public void setRolloverEnabled(boolean rolloverEnabled) { boolean old = isRolloverEnabled(); if (rolloverEnabled == old) return; if (rolloverEnabled) { rolloverProducer = createRolloverProducer(); addMouseListener(rolloverProducer); addMouseMotionListener(rolloverProducer); linkController = new LinkController(); addPropertyChangeListener(linkController); } else { removeMouseListener(rolloverProducer); removeMouseMotionListener(rolloverProducer); rolloverProducer = null; removePropertyChangeListener(linkController); linkController = null; } firePropertyChange("rolloverEnabled", old, isRolloverEnabled()); } /** * creates and returns the RolloverProducer to use with this tree. * * @return */ protected RolloverProducer createRolloverProducer() { RolloverProducer r = new RolloverProducer() { protected void updateRolloverPoint(JComponent component, Point mousePoint) { JXList list = (JXList) component; int row = list.locationToIndex(mousePoint); if (row >= 0) { Rectangle cellBounds = list.getCellBounds(row, row); if (!cellBounds.contains(mousePoint)) { row = -1; } } int col = row < 0 ? -1 : 0; rollover.x = col; rollover.y = row; } }; return r; } /** * returns the rolloverEnabled property. * * @return */ public boolean isRolloverEnabled() { return rolloverProducer != null; } public void setLinkVisitor(ActionListener linkVisitor) { if (linkVisitor != null) { setRolloverEnabled(true); getDelegatingRenderer().setLinkVisitor(linkVisitor); } else { // JW: think - need to revert? } } /** * listens to rollover properties. * Repaints effected component regions. * Updates link cursor. * * @author Jeanette Winzenburg */ public class LinkController implements PropertyChangeListener { private Cursor oldCursor; public void propertyChange(PropertyChangeEvent evt) { if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())) { rollover((JXList) evt.getSource(), (Point) evt.getOldValue(), (Point) evt.getOldValue()); } else if (RolloverProducer.CLICKED_KEY.equals(evt.getPropertyName())) { click((JXList) evt.getSource(), (Point) evt.getOldValue(), (Point) evt.getNewValue()); } } // --------------------------------- JList rollover private void rollover(JXList list, Point oldLocation, Point newLocation) { setLinkCursor(list, newLocation); // JW: partial repaints incomplete list.repaint(); } private void click(JXList list, Point oldLocation, Point newLocation) { if (!isLinkElement(list, newLocation)) return; ListCellRenderer renderer = list.getCellRenderer(); // PENDING: JW - don't ask the model, ask the list! Component comp = renderer.getListCellRendererComponent(list, list.getModel().getElementAt(newLocation.y), newLocation.y, false, true); if (comp instanceof AbstractButton) { // this is fishy - needs to be removed as soon as JList is editable ((AbstractButton) comp).doClick(); list.repaint(); } } /** * something weird: cursor in JList behaves different from JTable? * @param list * @param location */ private void setLinkCursor(JXList list, Point location) { if (isLinkElement(list, location)) { oldCursor = list.getCursor(); list.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } else { list.setCursor(oldCursor); oldCursor = null; } } private boolean isLinkElement(JXList list, Point location) { if (location == null || location.y < 0) return false; // PENDING: JW - don't ask the model, ask the list! return (list.getModel().getElementAt(location.y) instanceof LinkModel); } } // ---------------------------- filters /** * returns the element at the given index. The index is * in view coordinates which might differ from model * coordinates if filtering is enabled and filters/sorters * are active. * * @param viewIndex the index in view coordinates * @return the element at the index * @throws IndexOutOfBoundsException * if viewIndex < 0 or viewIndex >= getElementCount() */ public Object getElementAt(int viewIndex) { return getModel().getElementAt(viewIndex); } /** * Returns the number of elements in this list in view * coordinates. If filters are active this number might be * less than the number of elements in the underlying model. * * @return */ public int getElementCount() { return getModel().getSize(); } /** * Convert row index from view coordinates to model coordinates accounting * for the presence of sorters and filters. * * @param viewIndex index in view coordinates * @return index in model coordinates * @throws IndexOutOfBoundsException if viewIndex < 0 or viewIndex >= getElementCount() */ public int convertIndexToModel(int viewIndex) { return isFilterEnabled() ? getFilters().convertRowIndexToModel( viewIndex) : viewIndex; } /** * Convert index from model coordinates to view coordinates accounting * for the presence of sorters and filters. * * PENDING Filter guards against out of range - should not? * * @param modelIndex index in model coordinates * @return index in view coordinates if the model index maps to a view coordinate * or -1 if not contained in the view. * */ public int convertIndexToView(int modelIndex) { return isFilterEnabled() ? getFilters().convertRowIndexToView( modelIndex) : modelIndex; } /** * returns the underlying model. If !isFilterEnabled this will be the same * as getModel(). * * @return */ public ListModel getWrappedModel() { return isFilterEnabled() ? wrappingModel.getModel() : getModel(); } /** * Enables/disables filtering support. If enabled all row indices - * including the selection - are in view coordinates and getModel returns a * wrapper around the underlying model. * * Note: as an implementation side-effect calling this method clears the * selection (done in super.setModel). * * PENDING: cleanup state transitions!! - currently this can be safely * applied once only to enable. Internal state is inconsistent if trying to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -