001/* 002 * $Id: JXBusyLabel.java 4147 2012-02-01 17:13:24Z kschaefe $ 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 022package org.jdesktop.swingx; 023 024import java.awt.Dimension; 025import java.awt.Rectangle; 026import java.awt.event.ActionEvent; 027import java.awt.event.ActionListener; 028 029import javax.swing.JLabel; 030import javax.swing.Timer; 031import javax.swing.plaf.LabelUI; 032 033import org.jdesktop.beans.JavaBean; 034import org.jdesktop.swingx.icon.PainterIcon; 035import org.jdesktop.swingx.painter.BusyPainter; 036import org.jdesktop.swingx.plaf.BusyLabelAddon; 037import org.jdesktop.swingx.plaf.BusyLabelUI; 038import org.jdesktop.swingx.plaf.LookAndFeelAddons; 039 040/** 041 * <p>A simple circular animation, useful for denoting an action is taking 042 * place that may take an unknown length of time to complete. Similar to an 043 * indeterminant JProgressBar, but with a different look.</p> 044 * 045 * <p>For example: 046 * <pre><code> 047 * JXFrame frame = new JXFrame("test", true); 048 * JXBusyLabel label = new JXBusyLabel(); 049 * frame.add(label); 050 * //... 051 * label.setBusy(true); 052 * </code></pre></p> 053 * Another more complicated example: 054 * <pre><code> 055 * JXBusyLabel label = new JXBusyLabel(new Dimension(100,84)); 056 * BusyPainter painter = new BusyPainter( 057 * new Rectangle2D.Float(0, 0,13.500001f,1), 058 * new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10)); 059 * painter.setTrailLength(5); 060 * painter.setPoints(31); 061 * painter.setFrame(1); 062 * label.setPreferredSize(new Dimension(100,84)); 063 * label.setIcon(new EmptyIcon(100,84)); 064 * label.setBusyPainter(painter); 065 *</code></pre> 066 * 067 * Another example: 068 * <pre><code> 069 * JXBusyLabel label = new MyBusyLabel(new Dimension(100, 84)); 070 * </code></pre> 071 * 072 * where MyBusyLabel is:<br> 073 * <pre><code> 074 * public class MyBusyLabel extends JXBusyLabel { 075 * public MyBusyLabel(Dimension prefSize) { 076 * super(prefSize); 077 * } 078 * 079 * protected BusyLabel createBusyLabel(Dimension dim) { 080 * BusyPainter painter = new BusyPainter( 081 * new Rectangle2D.Float(0, 0,13.500001f,1), 082 * new RoundRectangle2D.Float(12.5f,12.5f,59.0f,59.0f,10,10)); 083 * painter.setTrailLength(5); 084 * painter.setPoints(31); 085 * painter.setFrame(1); 086 * 087 * return painter; 088 * } 089 * } 090 * </code></pre> 091 * 092 * @author rbair 093 * @author joshy 094 * @author rah003 095 * @author headw01 096 */ 097@JavaBean 098public class JXBusyLabel extends JLabel { 099 100 private static final long serialVersionUID = 5979268460848257147L; 101 private BusyPainter busyPainter; 102 private Timer busy; 103 private int delay; 104 /** Status flag to save/restore status of timer when moving component between containers. */ 105 private boolean wasBusyOnNotify = false; 106 107 /** 108 * UI Class ID 109 */ 110 public final static String uiClassID = "BusyLabelUI"; 111 112 /** 113 * Sets direction of rotation. <code>Direction.RIGHT</code> is the default 114 * value. Direction is taken from the very top point so <code>Direction.RIGHT</code> enables rotation clockwise. 115 * @param dir Direction of rotation. 116 */ 117 public void setDirection(BusyPainter.Direction dir) { 118 direction = dir; 119 getBusyPainter().setDirection(dir); 120 } 121 122 private BusyPainter.Direction direction; 123 124 /** 125 * Creates a default JXLoginPane instance 126 */ 127 static { 128 LookAndFeelAddons.contribute(new BusyLabelAddon()); 129 } 130 131 { 132 // Initialize the delay from the UI class. 133 BusyLabelUI ui = (BusyLabelUI)getUI(); 134 if (ui != null) { 135 delay = ui.getDelay(); 136 } 137 } 138 139 /** Creates a new instance of <code>JXBusyLabel</code> initialized to circular shape in bounds of 26 by 26 points.*/ 140 public JXBusyLabel() { 141 this(null); 142 } 143 144 /** 145 * Creates a new instance of <code>JXBusyLabel</code> initialized to the arbitrary size and using default circular progress indicator. 146 * @param dim Preferred size of the label. 147 */ 148 public JXBusyLabel(Dimension dim) { 149 super(); 150 this.setPreferredSize(dim); 151 152 // Initialize the BusyPainter. 153 getBusyPainter(); 154 } 155 156 /** 157 * Initialize the BusyPainter and (this) JXBusyLabel with the given 158 * preferred size. This method is called automatically when the 159 * BusyPainter is set/changed. 160 * 161 * @param dim The new Preferred Size for the BusyLabel. 162 * 163 * @see #getBusyPainter() 164 * @see #setBusyPainter(BusyPainter) 165 */ 166 protected void initPainter(Dimension dim) { 167 BusyPainter busyPainter = getBusyPainter(); 168 169 // headw01 170 // TODO: Should we force the busyPainter to NOT be cached? 171 // I think we probably should, otherwise the UI will never 172 // be updated after the first paint. 173 if (null != busyPainter) { 174 busyPainter.setCacheable(false); 175 } 176 177 PainterIcon icon = new PainterIcon(dim); 178 icon.setPainter(busyPainter); 179 this.setIcon(icon); 180 } 181 /** 182 * Create and return a BusyPpainter to use for the Label. This may 183 * be overridden to return any painter you like. By default, this 184 * method uses the UI (BusyLabelUI)to create a BusyPainter. 185 * @param dim Painter size. 186 * 187 * @see #getUI() 188 */ 189 protected BusyPainter createBusyPainter(Dimension dim) { 190 BusyPainter busyPainter = null; 191 192 BusyLabelUI ui = (BusyLabelUI)getUI(); 193 if (ui != null) { 194 busyPainter = ui.getBusyPainter(dim); 195 196 } 197 198 return busyPainter; 199 } 200 201 /** 202 * <p>Gets whether this <code>JXBusyLabel</code> is busy. If busy, then 203 * the <code>JXBusyLabel</code> instance will indicate that it is busy, 204 * generally by animating some state.</p> 205 * 206 * @return true if this instance is busy 207 */ 208 public boolean isBusy() { 209 return busy != null; 210 } 211 212 /** 213 * <p>Sets whether this <code>JXBusyLabel</code> instance should consider 214 * itself busy. A busy component may indicate that it is busy via animation, 215 * or some other means.</p> 216 * 217 * @param busy whether this <code>JXBusyLabel</code> instance should 218 * consider itself busy 219 */ 220 public void setBusy(boolean busy) { 221 boolean old = isBusy(); 222 if (!old && busy) { 223 startAnimation(); 224 firePropertyChange("busy", old, isBusy()); 225 } else if (old && !busy) { 226 stopAnimation(); 227 firePropertyChange("busy", old, isBusy()); 228 } 229 } 230 231 private void startAnimation() { 232 if(busy != null) { 233 stopAnimation(); 234 } 235 236 busy = new Timer(delay, new ActionListener() { 237 BusyPainter busyPainter = getBusyPainter(); 238 int frame = busyPainter.getPoints(); 239 @Override 240 public void actionPerformed(ActionEvent e) { 241 frame = (frame+1)%busyPainter.getPoints(); 242 busyPainter.setFrame(direction == BusyPainter.Direction.LEFT ? busyPainter.getPoints() - frame : frame); 243 frameChanged(); 244 } 245 }); 246 busy.start(); 247 } 248 249 250 251 252 private void stopAnimation() { 253 if (busy != null) { 254 busy.stop(); 255 getBusyPainter().setFrame(-1); 256 repaint(); 257 busy = null; 258 } 259 } 260 261 @Override 262 public void removeNotify() { 263 // fix for #698 264 wasBusyOnNotify = isBusy(); 265 // fix for #626 266 stopAnimation(); 267 super.removeNotify(); 268 } 269 270 @Override 271 public void addNotify() { 272 super.addNotify(); 273 // fix for #698 274 if (wasBusyOnNotify) { 275 // fix for #626 276 startAnimation(); 277 } 278 } 279 280 protected void frameChanged() { 281 repaint(); 282 } 283 284 /** 285 * Returns the current BusyPainter. If no BusyPainter is currently 286 * set on this BusyLabel, the {@link #createBusyPainter(Dimension)} 287 * method is called to create one. Afterwards, 288 * {@link #initPainter(Dimension)} is called to update the BusyLabel 289 * with the created BusyPainter. 290 * 291 * @return the busyPainter 292 * 293 * @see #createBusyPainter(Dimension) 294 * @see #initPainter(Dimension) 295 */ 296 public final BusyPainter getBusyPainter() { 297 if (null == busyPainter) { 298 Dimension prefSize = getPreferredSize(); 299 300 busyPainter = createBusyPainter((prefSize.width == 0 && prefSize.height == 0 && !isPreferredSizeSet()) ? null : prefSize); 301 302 if (null != busyPainter) { 303 if (!isPreferredSizeSet() && (null == prefSize || prefSize.width == 0 || prefSize.height == 0)) { 304 Rectangle rt = busyPainter.getTrajectory().getBounds(); 305 Rectangle rp = busyPainter.getPointShape().getBounds(); 306 int max = Math.max(rp.width, rp.height); 307 prefSize = new Dimension(rt.width + max, rt.height + max); 308 } 309 310 initPainter(prefSize); 311 } 312 } 313 return busyPainter; 314 } 315 316 /** 317 * @param busyPainter the busyPainter to set 318 */ 319 public final void setBusyPainter(BusyPainter busyPainter) { 320 this.busyPainter = busyPainter; 321 initPainter(new Dimension(getIcon().getIconWidth(), getIcon().getIconHeight())); 322 } 323 324 /** 325 * @return the delay 326 */ 327 public int getDelay() { 328 return delay; 329 } 330 331 /** 332 * @param delay the delay to set 333 */ 334 public void setDelay(int delay) { 335 int old = getDelay(); 336 this.delay = delay; 337 if (old != getDelay()) { 338 if (busy != null && busy.isRunning()) { 339 busy.setDelay(getDelay()); 340 } 341 firePropertyChange("delay", old, getDelay()); 342 } 343 } 344 //------------------------------------------------------------- UI Logic 345 346 /** 347 * Notification from the <code>UIManager</code> that the L&F has changed. 348 * Replaces the current UI object with the latest version from the 349 * <code>UIManager</code>. 350 * 351 * @see javax.swing.JComponent#updateUI 352 */ 353 @Override 354 public void updateUI() { 355 setUI((LabelUI) LookAndFeelAddons.getUI(this, BusyLabelUI.class)); 356 } 357 358 /** 359 * Returns the name of the L&F class that renders this component. 360 * 361 * @return the string {@link #uiClassID} 362 * @see javax.swing.JComponent#getUIClassID 363 * @see javax.swing.UIDefaults#getUI 364 */ 365 @Override 366 public String getUIClassID() { 367 return uiClassID; 368 } 369 370 371} 372