001 /* 002 * $Id: JXGlassBox.java 3235 2009-02-01 15:01:07Z rah003 $ 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 */ 021 022 package org.jdesktop.swingx; 023 024 import java.applet.Applet; 025 import java.awt.Color; 026 import java.awt.Component; 027 import java.awt.Container; 028 import java.awt.Dimension; 029 import java.awt.Graphics; 030 import java.awt.Rectangle; 031 import java.awt.Window; 032 import java.awt.event.ActionEvent; 033 import java.awt.event.ActionListener; 034 import java.awt.event.MouseAdapter; 035 import java.awt.event.MouseEvent; 036 037 import javax.swing.JComponent; 038 import javax.swing.SwingConstants; 039 import javax.swing.SwingUtilities; 040 import javax.swing.Timer; 041 042 /** 043 * Component used to display transluscent user-interface content. 044 * This component and all of its content will be displayed with the specified 045 * "alpha" transluscency property value. When this component is made visible, 046 * it's content will fade in until the alpha transluscency level is reached. 047 * <p> 048 * If the glassbox's "dismissOnClick" property is <code>true</code> 049 * (the default) then the glassbox will be made invisible when the user 050 * clicks on it.</p> 051 * <p> 052 * This component is particularly useful for displaying transient messages 053 * on the glasspane.</p> 054 * 055 * @author Amy Fowler 056 * @author Karl George Schaefer 057 * @version 1.0 058 */ 059 public class JXGlassBox extends JXPanel { 060 private static final int SHOW_DELAY = 30; // ms 061 private static final int TIMER_INCREMENT = 10; // ms 062 063 private float alphaStart = 0.01f; 064 private float alphaEnd = 0.8f; 065 066 private Timer animateTimer; 067 private float alphaIncrement = 0.02f; 068 069 private boolean dismissOnClick = false; 070 private MouseAdapter dismissListener = null; 071 072 public JXGlassBox() { 073 setOpaque(false); 074 setAlpha(alphaStart); 075 setBackground(Color.white); 076 setDismissOnClick(true); 077 078 animateTimer = new Timer(TIMER_INCREMENT, new ActionListener() { 079 public void actionPerformed(ActionEvent e) { 080 setAlpha(Math.min(alphaEnd, getAlpha() + alphaIncrement)); 081 } 082 }); 083 } 084 085 public JXGlassBox(float alpha) { 086 this(); 087 setAlpha(alpha); 088 } 089 090 public void setAlpha(float alpha) { 091 super.setAlpha(alpha); 092 this.alphaIncrement = (alphaEnd - alphaStart)/(SHOW_DELAY/TIMER_INCREMENT); 093 } 094 095 /** 096 * Dismisses this glass box. This causes the glass box to be removed from 097 * it's parent and ensure that the display is correctly updated. 098 */ 099 public void dismiss() { 100 JComponent parent = (JComponent) getParent(); 101 102 if (parent != null) { 103 Container toplevel = parent.getTopLevelAncestor(); 104 parent.remove(this); 105 toplevel.validate(); 106 toplevel.repaint(); 107 } 108 } 109 110 /** 111 * Determines if the glass box if dismissed when a user clicks on it. 112 * 113 * @return {@code true} if the glass box can be dismissed with a click; 114 * {@code false} otherwise 115 * @see #setDismissOnClick(boolean) 116 * @see #dismiss() 117 */ 118 public boolean isDismissOnClick() { 119 return dismissOnClick; 120 } 121 122 /** 123 * Configures the glass box to dismiss (or not) when clicked. 124 * 125 * @param dismissOnClick 126 * {@code true} if the glass box should dismiss when clicked; 127 * {@code false} otherwise 128 * @see #isDismissOnClick() 129 * @see #dismiss() 130 */ 131 public void setDismissOnClick(boolean dismissOnClick) { 132 boolean oldDismissOnClick = this.dismissOnClick; 133 this.dismissOnClick = dismissOnClick; 134 135 firePropertyChange("dismissOnClick", oldDismissOnClick, isDismissOnClick()); 136 137 //TODO do this as a reaction to the property change? 138 if (dismissOnClick && !oldDismissOnClick) { 139 if (dismissListener == null) { 140 dismissListener = new MouseAdapter() { 141 public void mouseClicked(MouseEvent e) { 142 dismiss(); 143 } 144 }; 145 } 146 addMouseListener(dismissListener); 147 } 148 else if (!dismissOnClick && oldDismissOnClick) { 149 removeMouseListener(dismissListener); 150 } 151 } 152 153 public void paint(Graphics g) { 154 super.paint(g); 155 if (!animateTimer.isRunning() && getAlpha() < alphaEnd ) { 156 animateTimer.start(); 157 } 158 if (animateTimer.isRunning() && getAlpha() >= alphaEnd) { 159 animateTimer.stop(); 160 } 161 } 162 163 public void setVisible(boolean visible) { 164 boolean old = isVisible(); 165 setAlpha(alphaStart); 166 super.setVisible(visible); 167 firePropertyChange("visible", old, isVisible()); 168 } 169 170 private Container getTopLevel() { 171 Container p = getParent(); 172 while (p != null && !(p instanceof Window || p instanceof Applet)) { 173 p = p.getParent(); 174 } 175 return p; 176 } 177 178 /** 179 * Shows this glass box on the glass pane. The position of the box is 180 * relative to the supplied component and offsets. 181 * 182 * @param glassPane 183 * the glass pane 184 * @param origin 185 * the component representing the origin location 186 * @param offsetX 187 * the offset on the X-axis from the origin 188 * @param offsetY 189 * the offset on the Y-axis from the origin 190 * @param positionHint 191 * a {@code SwingConstants} box position hint ({@code CENTER}, 192 * {@code TOP}, {@code BOTTOM}, {@code LEFT}, or {@code RIGHT}) 193 * @throws NullPointerException 194 * if {@code glassPane} or {@code origin} is {@code null} 195 * @throws IllegalArgumentException 196 * if {@code positionHint} is not a valid hint 197 */ 198 //TODO replace SwingConstant with enum other non-int 199 //TODO this method places the box outside of the origin component 200 // that continues the implementation approach that Amy used. I 201 // think it would be a useful poll to determine whether the box 202 // should be place inside or outside of the origin (by default). 203 public void showOnGlassPane(Container glassPane, Component origin, 204 int offsetX, int offsetY, int positionHint) { 205 Rectangle r = SwingUtilities.convertRectangle(origin, 206 origin.getBounds(), glassPane); 207 Dimension d = getPreferredSize(); 208 209 int originX = offsetX + r.x; 210 int originY = offsetY + r.y; 211 212 switch (positionHint) { 213 case SwingConstants.TOP: 214 originX += (r.width - d.width) / 2; 215 originY -= d.height; 216 break; 217 case SwingConstants.BOTTOM: 218 originX += (r.width - d.width) / 2; 219 originY += r.height; 220 break; 221 case SwingConstants.LEFT: 222 originX -= d.width; 223 originY += (r.height - d.height) / 2; 224 break; 225 case SwingConstants.RIGHT: 226 originX += r.width; 227 originY += (r.height - d.height) / 2; 228 break; 229 case SwingConstants.CENTER: 230 originX += (r.width - d.width) / 2; 231 originY += (r.height - d.height) / 2; 232 break; 233 default: 234 throw new IllegalArgumentException("inavlid position hint"); 235 } 236 237 showOnGlassPane(glassPane, originX, originY); 238 } 239 240 /** 241 * Shows this glass box on the glass pane. 242 * 243 * @param glassPane 244 * the glass pane 245 * @param originX 246 * the location on the X-axis to position the glass box 247 * @param originY 248 * the location on the Y-axis to position the glass box 249 */ 250 public void showOnGlassPane(Container glassPane, int originX, int originY) { 251 Dimension gd = glassPane.getSize(); 252 Dimension bd = getPreferredSize(); 253 254 int x = Math.min(originX, gd.width - bd.width); 255 int y = Math.min(originY, gd.height - bd.height); 256 257 if (x < 0) { 258 x = 0; 259 } 260 261 if (y < 0) { 262 y = 0; 263 } 264 265 int width = x + bd.width < gd.width ? bd.width : gd.width; 266 int height = y + bd.height < gd.height ? bd.height : gd.height; 267 268 glassPane.setLayout(null); 269 setBounds(x, y, width, height); 270 glassPane.add(this); 271 glassPane.setVisible(true); 272 273 Container topLevel = getTopLevel(); 274 topLevel.validate(); 275 topLevel.repaint(); 276 } 277 278 }