001/* 002 * $Id: RolloverProducer.java 3982 2011-03-30 12:27:31Z kleopatra $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021package org.jdesktop.swingx.rollover; 022 023import java.awt.Point; 024import java.awt.event.ComponentEvent; 025import java.awt.event.ComponentListener; 026import java.awt.event.MouseEvent; 027import java.awt.event.MouseListener; 028import java.awt.event.MouseMotionListener; 029import java.util.logging.Logger; 030 031import javax.swing.JComponent; 032 033/** 034 * Mouse/Motion/Listener which maps mouse coordinates to client coordinates 035 * and stores these as client properties in the target JComponent. The exact 036 * mapping process is left to subclasses. Typically, they will map to "cell" 037 * coordinates. <p> 038 * 039 * Note: this class assumes that the target component is of type JComponent.<p> 040 * Note: this implementation is stateful, it can't be shared across different 041 * instances of a target component.<p> 042 * 043 * 044 * @author Jeanette Winzenburg 045 */ 046public abstract class RolloverProducer implements MouseListener, MouseMotionListener, 047 ComponentListener { 048 049 @SuppressWarnings("unused") 050 private static final Logger LOG = Logger.getLogger(RolloverProducer.class 051 .getName()); 052 053 /** 054 * Key for client property mapped from mouse-triggered action. 055 * Note that the actual mouse-event which results in setting the property 056 * depends on the implementation of the concrete RolloverProducer. 057 */ 058 public static final String CLICKED_KEY = "swingx.clicked"; 059 060 /** Key for client property mapped from rollover events */ 061 public static final String ROLLOVER_KEY = "swingx.rollover"; 062 063 // public static final String PRESSED_KEY = "swingx.pressed"; 064 065 private boolean isDragging; 066 067 /** 068 * Installs all listeners, as required. 069 * 070 * @param component target to install required listeners on, must 071 * not be null. 072 */ 073 public void install(JComponent component) { 074 component.addMouseListener(this); 075 component.addMouseMotionListener(this); 076 component.addComponentListener(this); 077 } 078 079 /** 080 * Removes all listeners. 081 * 082 * @param component target component to uninstall required listeners from, 083 * must not be null 084 */ 085 public void release(JComponent component) { 086 component.removeMouseListener(this); 087 component.removeMouseMotionListener(this); 088 component.removeComponentListener(this); 089 } 090 091 //----------------- mouseListener 092 093 /** 094 * Implemented to map to Rollover properties as needed. This implemenation calls 095 * updateRollover with both ROLLOVER_KEY and CLICKED_KEY properties. 096 */ 097 @Override 098 public void mouseReleased(MouseEvent e) { 099 Point oldCell = new Point(rollover); 100 // JW: fix for #456-swingx - rollover not updated after end of dragging 101 updateRollover(e, ROLLOVER_KEY, false); 102 // Fix Issue 1387-swingx - no click on release-after-drag 103 if (isClick(e, oldCell, isDragging)) { 104 updateRollover(e, CLICKED_KEY, true); 105 } 106 isDragging = false; 107 } 108 109 /** 110 * Returns a boolean indicating whether or not the given mouse event should 111 * be interpreted as a click. This method is called from mouseReleased 112 * after the cell coordiates were updated. While the ID of mouse event 113 * is not formally enforced, it is assumed to be a MOUSE_RELEASED. Calling 114 * for other types might or might not work as expected. <p> 115 * 116 * This implementation returns true if the current rollover point is the same 117 * cell as the given oldRollover, that is ending a drag inside the same cell 118 * triggers the action while ending a drag somewhere does not. <p> 119 * 120 * PENDING JW: open to more complex logic in case it clashes with existing code, 121 * see Issue #1387. 122 * 123 * @param e the mouseEvent which triggered calling this, assumed to be 124 * a mouseReleased, must not be null 125 * @param oldRollover the cell before the mouseEvent was mapped, must not be null 126 * @param wasDragging true if the release happened 127 * @return a boolean indicating whether or not the given mouseEvent should 128 * be interpreted as a click. 129 */ 130 protected boolean isClick(MouseEvent e, Point oldRollover, boolean wasDragging) { 131 return oldRollover.equals(rollover); 132 } 133 134 /** 135 * Implemented to map to client property rollover and fire only if client 136 * coordinate changed. 137 */ 138 @Override 139 public void mouseEntered(MouseEvent e) { 140// LOG.info("" + e); 141 isDragging = false; 142 updateRollover(e, ROLLOVER_KEY, false); 143 } 144 145 /** 146 * Implemented to remove client properties rollover and clicked. if the 147 * source is a JComponent. Does nothing otherwise. 148 */ 149 @Override 150 public void mouseExited(MouseEvent e) { 151 isDragging = false; 152// screenLocation = null; 153// LOG.info("" + e); 154// if (((JComponent) e.getComponent()).getMousePosition(true) != null) { 155// updateRollover(e, ROLLOVER_KEY, false); 156// } else { 157// } 158 ((JComponent) e.getSource()).putClientProperty(ROLLOVER_KEY, null); 159 ((JComponent) e.getSource()).putClientProperty(CLICKED_KEY, null); 160 161 } 162 163 /** 164 * Implemented to do nothing. 165 */ 166 @Override 167 public void mouseClicked(MouseEvent e) { 168 } 169 170 /** 171 * Implemented to do nothing. 172 */ 173 @Override 174 public void mousePressed(MouseEvent e) { 175 } 176 177 // ---------------- MouseMotionListener 178 /** 179 * Implemented to set a dragging flag to true. 180 */ 181 @Override 182 public void mouseDragged(MouseEvent e) { 183 isDragging = true; 184 } 185 186 /** 187 * Implemented to map to client property rollover and fire only if client 188 * coordinate changed. 189 */ 190 @Override 191 public void mouseMoved(MouseEvent e) { 192 updateRollover(e, ROLLOVER_KEY, false); 193 } 194 195 //---------------- ComponentListener 196 197 198 @Override 199 public void componentShown(ComponentEvent e) { 200 } 201 202 @Override 203 public void componentResized(ComponentEvent e) { 204 updateRollover(e); 205 } 206 207 @Override 208 public void componentMoved(ComponentEvent e) { 209 updateRollover(e); 210 } 211 212 /** 213 * @param e 214 */ 215 private void updateRollover(ComponentEvent e) { 216 Point componentLocation = e.getComponent().getMousePosition(); 217 if (componentLocation == null) { 218 componentLocation = new Point(-1, -1); 219 } 220// LOG.info("" + componentLocation + " / " + e); 221 updateRolloverPoint((JComponent) e.getComponent(), componentLocation); 222 updateClientProperty((JComponent) e.getComponent(), ROLLOVER_KEY, true); 223 } 224 225 @Override 226 public void componentHidden(ComponentEvent e) { 227 } 228 229 //---------------- mapping methods 230 231 /** 232 * Controls the mapping of the given mouse event to a client property. This 233 * implementation first calls updateRolloverPoint to convert the mouse coordinates. 234 * Then calls updateClientProperty to actually set the client property in the 235 * 236 * @param e the MouseEvent to map to client coordinates 237 * @param property the client property to map to 238 * @param fireAlways a flag indicating whether a client event should be fired if unchanged. 239 * 240 * @see #updateRolloverPoint(JComponent, Point) 241 * @see #updateClientProperty(JComponent, String, boolean) 242 */ 243 protected void updateRollover(MouseEvent e, String property, 244 boolean fireAlways) { 245 updateRolloverPoint((JComponent) e.getComponent(), e.getPoint()); 246 updateClientProperty((JComponent) e.getComponent(), property, fireAlways); 247 } 248 249 /** Current mouse location in client coordinates. */ 250 protected Point rollover = new Point(-1, -1); 251 252 /** 253 * Sets the given client property to the value of current mouse location in 254 * client coordinates. If fireAlways, the property is force to fire a change. 255 * 256 * @param component the target component 257 * @param property the client property to set 258 * @param fireAlways a flag indicating whether a client property 259 * should be forced to fire an event. 260 */ 261 protected void updateClientProperty(JComponent component, String property, 262 boolean fireAlways) { 263 if (fireAlways) { 264 // fix Issue #864-swingx: force propertyChangeEvent 265 component.putClientProperty(property, null); 266 component.putClientProperty(property, new Point(rollover)); 267 } else { 268 Point p = (Point) component.getClientProperty(property); 269 if (p == null || (rollover.x != p.x) || (rollover.y != p.y)) { 270 component.putClientProperty(property, new Point(rollover)); 271 } 272 } 273 } 274 275 /** 276 * Subclasses must implement to map the given mouse coordinates into 277 * appropriate client coordinates. The result must be stored in the 278 * rollover field. 279 * 280 * @param component the target component which received a mouse event 281 * @param mousePoint the mouse position of the event, coordinates are 282 * component pixels 283 */ 284 protected abstract void updateRolloverPoint(JComponent component, Point mousePoint); 285 286}