--- a/components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/swing/ListCellOverlay.java Tue Dec 16 05:53:51 2014 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,651 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
- */
-
-package com.oracle.solaris.vp.util.swing;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.beans.*;
-import java.util.*;
-import java.util.List;
-import javax.swing.*;
-import javax.swing.Timer;
-import javax.swing.event.*;
-import com.oracle.solaris.vp.util.misc.CollectionUtil;
-
-/**
- * The {@code ListCellOverlay} class provides an overlay over each cell in a
- * {@code JList}. This overlay extends the cell to its preferred size so that
- * the user can see the entire contents of a cell that is partially hidden
- * within a scroll pane or is sized smaller than its preferred size.
- * <br/>
- * This class assumes that the {@code JList} is contained within a {@code
- * JScrollPane}; the overlay is styled to complement that scroll pane.
- * <br/>
- * The {@link #getOverlayComponent overlay component} or the {@link
- * #getOverlayScrollPane scroll pane} that contains it may be changed visually
- * (custom borders, background color, etc.) to affect the look of the overlay.
- */
-public class ListCellOverlay {
- //
- // Enums
- //
-
- public enum Style {
- SINGLE_POPUP, MULTI_POPUP
- }
-
- //
- // Inner classes
- //
-
- private static abstract class CellPanel extends JPanel {
- //
- // Instance data
- //
-
- private CellRendererPane rPane = new CellRendererPane();
-
- //
- // Constructors
- //
-
- public CellPanel() {
- setLayout(null);
- add(rPane);
- }
-
- //
- // Component methods
- //
-
- @Override
- public Dimension getPreferredSize() {
- Dimension d = getRendererComponentPreferredSize();
- Insets insets = getInsets();
- d.width += insets.left + insets.right;
- d.height += insets.top + insets.bottom;
-
- return d;
- }
-
- //
- // JComponent methods
- //
-
- @Override
- public void paintComponent(Graphics g) {
- super.paintComponent(g);
-
- Component comp = getRendererComponent();
-
- Insets insets = getInsets();
- int x = insets.left;
- int y = insets.top;
- int width = getWidth() - insets.left - insets.right;
- int height = getHeight() - insets.top - insets.bottom;
-
- rPane.paintComponent(g, comp, this, x, y, width, height, true);
- }
-
- //
- // CellPanel methods
- //
-
- public abstract Component getRendererComponent();
-
- public Dimension getRendererComponentPreferredSize() {
- return getRendererComponent().getPreferredSize();
- }
- }
-
- private class SimpleCellPanel extends CellPanel {
- //
- // Instance data
- //
-
- private Component comp;
-
- //
- // Constructors
- //
-
- public SimpleCellPanel(Component comp) {
- this.comp = comp;
-
- MouseAdapter listener =
- new MouseAdapter() {
- //
- // MouseListener methods
- //
-
- @Override
- public void mouseClicked(MouseEvent e) {
- GUIUtil.propagate(e, list);
- }
-
- @Override
- public void mouseExited(MouseEvent e) {
- configurePopups(e);
- }
-
- @Override
- public void mousePressed(MouseEvent e) {
- GUIUtil.propagate(e, list);
- }
-
- @Override
- public void mouseReleased(MouseEvent e) {
- GUIUtil.propagate(e, list);
- }
-
- //
- // MouseMotionListener methods
- //
-
- @Override
- public void mouseDragged(MouseEvent e) {
- GUIUtil.propagate(e, list);
- }
-
- @Override
- public void mouseMoved(MouseEvent e) {
- GUIUtil.propagate(e, list);
- }
-
- //
- // MouseWheelListener methods
- //
-
- @Override
- public void mouseWheelMoved(MouseWheelEvent e) {
- if (listScroll != null) {
- GUIUtil.propagate(e, listScroll);
- }
- }
- };
-
- addMouseListener(listener);
- addMouseMotionListener(listener);
- addMouseWheelListener(listener);
- }
-
- //
- // CellPanel methods
- //
-
- @Override
- public Component getRendererComponent() {
- return comp;
- }
- }
-
- //
- // Static data
- //
-
- /**
- * The initial delay after hovering over the list before an overlay appears.
- */
- public static final int DELAY = 0;
-
- //
- // Instance data
- //
-
- // Set to true to paint each overlay rectangle with a solid color
- private boolean debug = false;
-
- private JList list;
- private JScrollPane listScroll;
-
- private CellPanel panel;
- private JScrollPane panelScroll;
- private Map<Popup, CellPanel> popups = new HashMap<Popup, CellPanel>();
- private Timer timer;
-
- private int index = -1;
- private boolean enabled = true;
- private Style style = Style.MULTI_POPUP;
-
- private ListDataListener listModelListener =
- new ListDataListener() {
- @Override
- public void contentsChanged(ListDataEvent e) {
- repaintPopups();
- }
-
- @Override
- public void intervalAdded(ListDataEvent e) {
- repaintPopups();
- }
-
- @Override
- public void intervalRemoved(ListDataEvent e) {
- hidePopups();
- }
- };
-
- //
- // Constructors
- //
-
- public ListCellOverlay(final JList list) {
- this.list = list;
-
- panel = new CellPanel() {
- @Override
- public Component getRendererComponent() {
- Object value = list.getModel().getElementAt(index);
- boolean isSelected = list.isSelectedIndex(index);
- boolean hasFocus = list.hasFocus() &&
- (index == list.getLeadSelectionIndex());
-
- return list.getCellRenderer().getListCellRendererComponent(
- list, value, index, isSelected, hasFocus);
- }
-
- @Override
- public Dimension getRendererComponentPreferredSize() {
- Dimension d = super.getRendererComponentPreferredSize();
-
- Rectangle r = list.getCellBounds(index, index);
- d.width = Math.max(d.width, r.width);
- d.height = Math.max(d.height, r.height);
-
- return d;
- }
- };
-
- panel.setOpaque(true);
- panel.setBackground(list.getBackground());
-
- panelScroll = new JScrollPane();
-
- timer = new Timer(DELAY,
- new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- for (Popup popup : popups.keySet()) {
- popup.show();
- }
- }
- });
- timer.setRepeats(false);
-
- HierarchyListener listener =
- new HierarchyListener() {
- @Override
- public void hierarchyChanged(HierarchyEvent e) {
- listScroll = (JScrollPane)SwingUtilities.getAncestorOfClass(
- JScrollPane.class, list);
- }
- };
- list.addHierarchyListener(listener);
-
- // Initialize
- listener.hierarchyChanged(null);
-
- list.addHierarchyBoundsListener(
- new HierarchyBoundsListener() {
- @Override
- public void ancestorMoved(HierarchyEvent e) {
- hidePopups();
- }
-
- @Override
- public void ancestorResized(HierarchyEvent e) {
- hidePopups();
- }
- });
-
- list.addComponentListener(
- new ComponentAdapter() {
- @Override
- public void componentMoved(ComponentEvent e) {
- hidePopups();
- }
-
- @Override
- public void componentResized(ComponentEvent e) {
- hidePopups();
- }
- });
-
- list.addMouseMotionListener(
- new MouseMotionAdapter() {
- @Override
- public void mouseMoved(MouseEvent e) {
- configurePopups(e);
- }
- });
-
- list.addMouseListener(
- new MouseAdapter() {
- @Override
- public void mouseExited(MouseEvent e) {
- configurePopups(e);
- }
- });
-
- list.addListSelectionListener(
- new ListSelectionListener() {
- @Override
- public void valueChanged(ListSelectionEvent e) {
- repaintPopups();
- }
- });
-
- list.addPropertyChangeListener("model",
- new PropertyChangeListener() {
- @Override
- public void propertyChange(PropertyChangeEvent e) {
- modelChanged((ListModel)e.getOldValue(),
- (ListModel)e.getNewValue());
- }
- });
-
- // Initialize
- modelChanged(null, list.getModel());
- }
-
- //
- // ListCellOverlay methods
- //
-
- /**
- * Gets the initial delay after hovering over the list before an overlay of
- * a cell appears.
- */
- public int getDelay() {
- return timer.getInitialDelay();
- }
-
- /**
- * Gets whether this {@code ListCellOverlay} is enabled.
- */
- public boolean getEnabled() {
- return enabled;
- }
-
- public JList getList() {
- return list;
- }
-
- /**
- * Gets the {@code JComponent} that is displays the cell renderer in the
- * overlay. This {@code JComponent} can be customized with a border, a
- * change in background color, etc.
- */
- public JComponent getOverlayComponent() {
- return panel;
- }
-
- /**
- * Gets the {@code JScrollPane} containing the {@link #getOverlayComponent
- * overlay component}. This provides a border that (presumably) matches the
- * one surrounding the list.
- */
- public JScrollPane getOverlayScrollPane() {
- return panelScroll;
- }
-
- /**
- * Gets the style to use when displaying the overlay.
- */
- public Style getStyle() {
- return style;
- }
-
- /**
- * Sets the style to use when displaying the overlay. {@link
- * Style#SINGLE_POPUP} uses a single, tooltip-like popup, complete with
- * borders, as the overlay. {@link Style#MULTI_POPUP} uses multiple popups
- * to make it appear as though the scroll pane that encloses the list
- * changes shape to accommodate the overlay. The latter style may appear
- * more polished, but the former has a lesser performance hit. The default
- * value is {@link Style#MULTI_POPUP}.
- */
- public void setStyle(Style style) {
- this.style = style;
- }
-
- /**
- * Sets the initial delay after hovering over the list before an overlay of
- * a cell appears.
- */
- public void setDelay(int delay) {
- timer.setInitialDelay(delay);
- }
-
- /**
- * Gets whether this {@code ListCellOverlay} is enabled.
- */
- public void setEnabled(boolean enabled) {
- if (this.enabled != enabled) {
- if (!enabled) {
- hidePopups();
- }
- this.enabled = enabled;
- }
- }
-
- //
- // Private methods
- //
-
- private void configurePopups(MouseEvent e) {
- if (!enabled || listScroll == null) {
- return;
- }
-
- Rectangle cellRect = null;
- int index;
-
- Point listScrollPoint = SwingUtilities.convertPoint(
- e.getComponent(), e.getPoint(), listScroll);
-
- // Is the point visible?
- if (!listScroll.contains(listScrollPoint)) {
- index = -1;
- } else {
- Point listPoint = SwingUtilities.convertPoint(
- listScroll, listScrollPoint, list);
-
- index = list.locationToIndex(listPoint);
- if (index != -1) {
- cellRect = list.getCellBounds(index, index);
- if (!cellRect.contains(listPoint)) {
- // The point is not actually in a list cell
- index = -1;
- }
- }
- }
-
- // Has the index of the overlaid cell changed?
- if (this.index != index) {
- hidePopups();
-
- if (index != -1) {
- this.index = index;
-
- if (isCellObscured()) {
- // We now need to figure out where to place popup windows
- // (containing scroll, which contains panel, which paints
- // the cell renderer) so that the cell renderer within the
- // popups aligns with the cell renderer in the list.
-
- // The size of the panel scroll pane border
- // (panelScroll.getInsets() doesn't work under some L&Fs
- // (like synth), so we are forced to do a layout and compare
- // points)
- panelScroll.setViewportView(panel);
- panelScroll.doLayout();
- Point panelScrollInsets = SwingUtilities.convertPoint(
- panel, 0, 0, panelScroll);
-
- // The size of the panel border
- Insets panelInsets = panel.getInsets();
-
- if (cellRect == null) {
- cellRect = list.getCellBounds(index, index);
- }
-
- Dimension panelScrollPrefSize =
- panelScroll.getPreferredSize();
-
- // The bounds of the panel scroll pane in list coordinates
- Rectangle panelScrollBounds = new Rectangle(
- cellRect.x - panelInsets.left - panelScrollInsets.x,
- cellRect.y - panelInsets.top - panelScrollInsets.y,
- panelScrollPrefSize.width, panelScrollPrefSize.height);
-
- // Convert to the list scroll pane's coordinates
- panelScrollBounds = SwingUtilities.convertRectangle(
- list, panelScrollBounds, listScroll);
-
- List<Rectangle> rectList = new ArrayList<Rectangle>();
-
- switch (style) {
- case SINGLE_POPUP:
- rectList.add(panelScrollBounds);
- break;
-
- default:
- case MULTI_POPUP:
- // The bounds of the list viewport in the list
- // scroll pane's coordinates
- Rectangle listViewBounds =
- listScroll.getViewport().getBounds();
-
- Rectangle[] rects =
- SwingUtilities.computeDifference(
- panelScrollBounds, listViewBounds);
-
- CollectionUtil.addAll(rectList, rects);
-
- // Convert to the list scroll pane's coordinates
- cellRect = SwingUtilities.convertRectangle(
- list, cellRect, listScroll);
-
- // Now add one more rect to cover the visible area
- // of the cell entirely. This is necessary because
- // the renderer may be drawing its content truncated
- // (ie. "Foo bar..."), and we need it to be drawn at
- // full size.
- rectList.add(cellRect);
- }
-
- Point listScrollScreenPoint =
- listScroll.getLocationOnScreen();
-
- for (Rectangle rect : rectList) {
- Point point = rect.getLocation();
- point.translate(-panelScrollBounds.x,
- -panelScrollBounds.y);
-
- JViewport viewport = new JViewport();
- viewport.setPreferredSize(new Dimension(
- rect.width, rect.height));
-
- CellPanel cell = new SimpleCellPanel(panelScroll);
-
- if (debug) {
- JLabel label = new JLabel();
- label.setOpaque(true);
- label.setBackground(ColorUtil.getRandomColor());
- cell = new SimpleCellPanel(label);
- }
-
- viewport.setView(cell);
- viewport.setViewPosition(point);
-
- int x = rect.x + listScrollScreenPoint.x;
- int y = rect.y + listScrollScreenPoint.y;
-
- Popup popup = PopupFactory.getSharedInstance().getPopup(
- list, viewport, x, y);
-
- popups.put(popup, cell);
- }
-
- timer.restart();
- }
- }
- }
- }
-
- private boolean getPopupsShowing() {
- return !popups.isEmpty();
- }
-
- private void hidePopups() {
- timer.stop();
- index = -1;
- for (Iterator<Popup> i = popups.keySet().iterator(); i.hasNext();) {
- Popup popup = i.next();
- popup.hide();
- i.remove();
- }
- list.repaint();
- }
-
- private boolean isCellObscured() {
- Rectangle cellRect = list.getCellBounds(index, index);
- Dimension panelSize = panel.getPreferredSize();
-
- // Is the cell rect smaller than the preferred size?
- if (cellRect.width < panelSize.width ||
- cellRect.height < panelSize.height) {
- return true;
- }
-
- // The bounds of panel at its preferred size, relative to listScroll
- Rectangle panelBounds = SwingUtilities.convertRectangle(list,
- new Rectangle(cellRect), listScroll);
-
- Rectangle scrollBounds = new Rectangle(listScroll.getSize());
-
- return !scrollBounds.contains(panelBounds);
- }
-
- private void modelChanged(ListModel oldModel, ListModel newModel) {
- if (oldModel != null) {
- oldModel.removeListDataListener(listModelListener);
- }
-
- if (newModel != null) {
- newModel.addListDataListener(listModelListener);
- }
- }
-
- private void repaintPopups() {
- if (getPopupsShowing()) {
- for (CellPanel cell : popups.values()) {
- cell.repaint();
- }
- }
- }
-}