1 /* 2 * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.plaf.synth; 27 28 import java.awt.*; 29 import java.awt.geom.AffineTransform; 30 import javax.swing.*; 31 import javax.swing.plaf.*; 32 import javax.swing.plaf.basic.BasicProgressBarUI; 33 import java.beans.PropertyChangeListener; 34 import java.beans.PropertyChangeEvent; 35 import sun.swing.SwingUtilities2; 36 37 /** 38 * Provides the Synth L&F UI delegate for 39 * {@link javax.swing.JProgressBar}. 40 * 41 * @author Joshua Outwater 42 * @since 1.7 43 */ 44 public class SynthProgressBarUI extends BasicProgressBarUI 45 implements SynthUI, PropertyChangeListener { 46 private SynthStyle style; 47 private int progressPadding; 48 private boolean rotateText; // added for Nimbus LAF 49 private boolean paintOutsideClip; 50 private boolean tileWhenIndeterminate; //whether to tile indeterminate painting 51 private int tileWidth; //the width of each tile 52 private Dimension minBarSize; // minimal visible bar size 53 private int glowWidth; // Glow around the bar foreground 54 55 /** 56 * Creates a new UI object for the given component. 57 * 58 * @param x component to create UI object for 59 * @return the UI object 60 */ createUI(JComponent x)61 public static ComponentUI createUI(JComponent x) { 62 return new SynthProgressBarUI(); 63 } 64 65 /** 66 * {@inheritDoc} 67 */ 68 @Override installListeners()69 protected void installListeners() { 70 super.installListeners(); 71 progressBar.addPropertyChangeListener(this); 72 } 73 74 /** 75 * {@inheritDoc} 76 */ 77 @Override uninstallListeners()78 protected void uninstallListeners() { 79 super.uninstallListeners(); 80 progressBar.removePropertyChangeListener(this); 81 } 82 83 /** 84 * {@inheritDoc} 85 */ 86 @Override installDefaults()87 protected void installDefaults() { 88 updateStyle(progressBar); 89 } 90 updateStyle(JProgressBar c)91 private void updateStyle(JProgressBar c) { 92 SynthContext context = getContext(c, ENABLED); 93 SynthStyle oldStyle = style; 94 style = SynthLookAndFeel.updateStyle(context, this); 95 setCellLength(style.getInt(context, "ProgressBar.cellLength", 1)); 96 setCellSpacing(style.getInt(context, "ProgressBar.cellSpacing", 0)); 97 progressPadding = style.getInt(context, 98 "ProgressBar.progressPadding", 0); 99 paintOutsideClip = style.getBoolean(context, 100 "ProgressBar.paintOutsideClip", false); 101 rotateText = style.getBoolean(context, 102 "ProgressBar.rotateText", false); 103 tileWhenIndeterminate = style.getBoolean(context, "ProgressBar.tileWhenIndeterminate", false); 104 tileWidth = style.getInt(context, "ProgressBar.tileWidth", 15); 105 // handle scaling for sizeVarients for special case components. The 106 // key "JComponent.sizeVariant" scales for large/small/mini 107 // components are based on Apples LAF 108 String scaleKey = (String)progressBar.getClientProperty( 109 "JComponent.sizeVariant"); 110 if (scaleKey != null){ 111 if ("large".equals(scaleKey)){ 112 tileWidth *= 1.15; 113 } else if ("small".equals(scaleKey)){ 114 tileWidth *= 0.857; 115 } else if ("mini".equals(scaleKey)){ 116 tileWidth *= 0.784; 117 } 118 } 119 minBarSize = (Dimension)style.get(context, "ProgressBar.minBarSize"); 120 glowWidth = style.getInt(context, "ProgressBar.glowWidth", 0); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override uninstallDefaults()127 protected void uninstallDefaults() { 128 SynthContext context = getContext(progressBar, ENABLED); 129 130 style.uninstallDefaults(context); 131 style = null; 132 } 133 134 /** 135 * {@inheritDoc} 136 */ 137 @Override getContext(JComponent c)138 public SynthContext getContext(JComponent c) { 139 return getContext(c, getComponentState(c)); 140 } 141 getContext(JComponent c, int state)142 private SynthContext getContext(JComponent c, int state) { 143 return SynthContext.getContext(c, style, state); 144 } 145 getComponentState(JComponent c)146 private int getComponentState(JComponent c) { 147 return SynthLookAndFeel.getComponentState(c); 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override getBaseline(JComponent c, int width, int height)154 public int getBaseline(JComponent c, int width, int height) { 155 super.getBaseline(c, width, height); 156 if (progressBar.isStringPainted() && 157 progressBar.getOrientation() == JProgressBar.HORIZONTAL) { 158 SynthContext context = getContext(c); 159 Font font = context.getStyle().getFont(context); 160 FontMetrics metrics = progressBar.getFontMetrics(font); 161 return (height - metrics.getAscent() - metrics.getDescent()) / 2 + 162 metrics.getAscent(); 163 } 164 return -1; 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override getBox(Rectangle r)171 protected Rectangle getBox(Rectangle r) { 172 if (tileWhenIndeterminate) { 173 return SwingUtilities.calculateInnerArea(progressBar, r); 174 } else { 175 return super.getBox(r); 176 } 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override setAnimationIndex(int newValue)183 protected void setAnimationIndex(int newValue) { 184 if (paintOutsideClip) { 185 if (getAnimationIndex() == newValue) { 186 return; 187 } 188 super.setAnimationIndex(newValue); 189 progressBar.repaint(); 190 } else { 191 super.setAnimationIndex(newValue); 192 } 193 } 194 195 /** 196 * Notifies this UI delegate to repaint the specified component. 197 * This method paints the component background, then calls 198 * the {@link #paint(SynthContext,Graphics)} method. 199 * 200 * <p>In general, this method does not need to be overridden by subclasses. 201 * All Look and Feel rendering code should reside in the {@code paint} method. 202 * 203 * @param g the {@code Graphics} object used for painting 204 * @param c the component being painted 205 * @see #paint(SynthContext,Graphics) 206 */ 207 @Override update(Graphics g, JComponent c)208 public void update(Graphics g, JComponent c) { 209 SynthContext context = getContext(c); 210 211 SynthLookAndFeel.update(context, g); 212 context.getPainter().paintProgressBarBackground(context, 213 g, 0, 0, c.getWidth(), c.getHeight(), 214 progressBar.getOrientation()); 215 paint(context, g); 216 } 217 218 /** 219 * Paints the specified component according to the Look and Feel. 220 * <p>This method is not used by Synth Look and Feel. 221 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 222 * 223 * @param g the {@code Graphics} object used for painting 224 * @param c the component being painted 225 * @see #paint(SynthContext,Graphics) 226 */ 227 @Override paint(Graphics g, JComponent c)228 public void paint(Graphics g, JComponent c) { 229 SynthContext context = getContext(c); 230 231 paint(context, g); 232 } 233 234 /** 235 * Paints the specified component. 236 * 237 * @param context context for the component being painted 238 * @param g the {@code Graphics} object used for painting 239 * @see #update(Graphics,JComponent) 240 */ paint(SynthContext context, Graphics g)241 protected void paint(SynthContext context, Graphics g) { 242 JProgressBar pBar = (JProgressBar)context.getComponent(); 243 int x = 0, y = 0, width = 0, height = 0; 244 if (!pBar.isIndeterminate()) { 245 Insets pBarInsets = pBar.getInsets(); 246 double percentComplete = pBar.getPercentComplete(); 247 if (percentComplete != 0.0) { 248 if (pBar.getOrientation() == JProgressBar.HORIZONTAL) { 249 x = pBarInsets.left + progressPadding; 250 y = pBarInsets.top + progressPadding; 251 width = (int)(percentComplete * (pBar.getWidth() 252 - (pBarInsets.left + progressPadding 253 + pBarInsets.right + progressPadding))); 254 height = pBar.getHeight() 255 - (pBarInsets.top + progressPadding 256 + pBarInsets.bottom + progressPadding); 257 258 if (!SynthLookAndFeel.isLeftToRight(pBar)) { 259 x = pBar.getWidth() - pBarInsets.right - width 260 - progressPadding - glowWidth; 261 } 262 } else { // JProgressBar.VERTICAL 263 x = pBarInsets.left + progressPadding; 264 width = pBar.getWidth() 265 - (pBarInsets.left + progressPadding 266 + pBarInsets.right + progressPadding); 267 height = (int)(percentComplete * (pBar.getHeight() 268 - (pBarInsets.top + progressPadding 269 + pBarInsets.bottom + progressPadding))); 270 y = pBar.getHeight() - pBarInsets.bottom - height 271 - progressPadding; 272 273 if (SynthLookAndFeel.isLeftToRight(pBar)) { 274 y -= glowWidth; 275 } 276 } 277 } 278 } else { 279 boxRect = getBox(boxRect); 280 x = boxRect.x + progressPadding; 281 y = boxRect.y + progressPadding; 282 width = boxRect.width - progressPadding - progressPadding; 283 height = boxRect.height - progressPadding - progressPadding; 284 } 285 286 //if tiling and indeterminate, then paint the progress bar foreground a 287 //bit wider than it should be. Shift as needed to ensure that there is 288 //an animated effect 289 if (tileWhenIndeterminate && pBar.isIndeterminate()) { 290 double percentComplete = (double)getAnimationIndex() / (double)getFrameCount(); 291 int offset = (int)(percentComplete * tileWidth); 292 Shape clip = g.getClip(); 293 g.clipRect(x, y, width, height); 294 if (pBar.getOrientation() == JProgressBar.HORIZONTAL) { 295 //paint each tile horizontally 296 for (int i=x-tileWidth+offset; i<=width; i+=tileWidth) { 297 context.getPainter().paintProgressBarForeground( 298 context, g, i, y, tileWidth, height, pBar.getOrientation()); 299 } 300 } else { //JProgressBar.VERTICAL 301 //paint each tile vertically 302 for (int i=y-offset; i<height+tileWidth; i+=tileWidth) { 303 context.getPainter().paintProgressBarForeground( 304 context, g, x, i, width, tileWidth, pBar.getOrientation()); 305 } 306 } 307 g.setClip(clip); 308 } else { 309 if (minBarSize == null || (width >= minBarSize.width 310 && height >= minBarSize.height)) { 311 context.getPainter().paintProgressBarForeground(context, g, 312 x, y, width, height, pBar.getOrientation()); 313 } 314 } 315 316 if (pBar.isStringPainted()) { 317 paintText(context, g, pBar.getString()); 318 } 319 } 320 321 /** 322 * Paints the component's text. 323 * 324 * @param context context for the component being painted 325 * @param g {@code Graphics} object used for painting 326 * @param title the text to paint 327 */ paintText(SynthContext context, Graphics g, String title)328 protected void paintText(SynthContext context, Graphics g, String title) { 329 if (progressBar.isStringPainted()) { 330 SynthStyle style = context.getStyle(); 331 Font font = style.getFont(context); 332 FontMetrics fm = SwingUtilities2.getFontMetrics( 333 progressBar, g, font); 334 int strLength = style.getGraphicsUtils(context). 335 computeStringWidth(context, font, fm, title); 336 Rectangle bounds = progressBar.getBounds(); 337 338 if (rotateText && 339 progressBar.getOrientation() == JProgressBar.VERTICAL){ 340 Graphics2D g2 = (Graphics2D)g; 341 // Calculate the position for the text. 342 Point textPos; 343 AffineTransform rotation; 344 if (progressBar.getComponentOrientation().isLeftToRight()){ 345 rotation = AffineTransform.getRotateInstance(-Math.PI/2); 346 textPos = new Point( 347 (bounds.width+fm.getAscent()-fm.getDescent())/2, 348 (bounds.height+strLength)/2); 349 } else { 350 rotation = AffineTransform.getRotateInstance(Math.PI/2); 351 textPos = new Point( 352 (bounds.width-fm.getAscent()+fm.getDescent())/2, 353 (bounds.height-strLength)/2); 354 } 355 356 // Progress bar isn't wide enough for the font. Don't paint it. 357 if (textPos.x < 0) { 358 return; 359 } 360 361 // Paint the text. 362 font = font.deriveFont(rotation); 363 g2.setFont(font); 364 g2.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 365 style.getGraphicsUtils(context).paintText(context, g, title, 366 textPos.x, textPos.y, -1); 367 } else { 368 // Calculate the bounds for the text. 369 Rectangle textRect = new Rectangle( 370 (bounds.width / 2) - (strLength / 2), 371 (bounds.height - 372 (fm.getAscent() + fm.getDescent())) / 2, 373 0, 0); 374 375 // Progress bar isn't tall enough for the font. Don't paint it. 376 if (textRect.y < 0) { 377 return; 378 } 379 380 // Paint the text. 381 g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 382 g.setFont(font); 383 style.getGraphicsUtils(context).paintText(context, g, title, 384 textRect.x, textRect.y, -1); 385 } 386 } 387 } 388 389 /** 390 * {@inheritDoc} 391 */ 392 @Override paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h)393 public void paintBorder(SynthContext context, Graphics g, int x, 394 int y, int w, int h) { 395 context.getPainter().paintProgressBarBorder(context, g, x, y, w, h, 396 progressBar.getOrientation()); 397 } 398 399 /** 400 * {@inheritDoc} 401 */ 402 @Override propertyChange(PropertyChangeEvent e)403 public void propertyChange(PropertyChangeEvent e) { 404 if (SynthLookAndFeel.shouldUpdateStyle(e) || 405 "indeterminate".equals(e.getPropertyName())) { 406 updateStyle((JProgressBar)e.getSource()); 407 } 408 } 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override getPreferredSize(JComponent c)414 public Dimension getPreferredSize(JComponent c) { 415 Dimension size = null; 416 Insets border = progressBar.getInsets(); 417 FontMetrics fontSizer = progressBar.getFontMetrics(progressBar.getFont()); 418 String progString = progressBar.getString(); 419 int stringHeight = fontSizer.getHeight() + fontSizer.getDescent(); 420 421 if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { 422 size = new Dimension(getPreferredInnerHorizontal()); 423 if (progressBar.isStringPainted()) { 424 // adjust the height if necessary to make room for the string 425 if (stringHeight > size.height) { 426 size.height = stringHeight; 427 } 428 429 // adjust the width if necessary to make room for the string 430 int stringWidth = SwingUtilities2.stringWidth( 431 progressBar, fontSizer, progString); 432 if (stringWidth > size.width) { 433 size.width = stringWidth; 434 } 435 } 436 } else { 437 size = new Dimension(getPreferredInnerVertical()); 438 if (progressBar.isStringPainted()) { 439 // make sure the width is big enough for the string 440 if (stringHeight > size.width) { 441 size.width = stringHeight; 442 } 443 444 // make sure the height is big enough for the string 445 int stringWidth = SwingUtilities2.stringWidth( 446 progressBar, fontSizer, progString); 447 if (stringWidth > size.height) { 448 size.height = stringWidth; 449 } 450 } 451 } 452 453 // handle scaling for sizeVarients for special case components. The 454 // key "JComponent.sizeVariant" scales for large/small/mini 455 // components are based on Apples LAF 456 String scaleKey = (String)progressBar.getClientProperty( 457 "JComponent.sizeVariant"); 458 if (scaleKey != null){ 459 if ("large".equals(scaleKey)){ 460 size.width *= 1.15f; 461 size.height *= 1.15f; 462 } else if ("small".equals(scaleKey)){ 463 size.width *= 0.90f; 464 size.height *= 0.90f; 465 } else if ("mini".equals(scaleKey)){ 466 size.width *= 0.784f; 467 size.height *= 0.784f; 468 } 469 } 470 471 size.width += border.left + border.right; 472 size.height += border.top + border.bottom; 473 474 return size; 475 } 476 } 477