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 */ 61 public static ComponentUI createUI(JComponent x) { 62 return new SynthProgressBarUI(); 63 } 64 65 /** 66 * {@inheritDoc} 67 */ 68 @Override 69 protected void installListeners() { 70 super.installListeners(); 71 progressBar.addPropertyChangeListener(this); 72 } 73 74 /** 75 * {@inheritDoc} 76 */ 77 @Override 78 protected void uninstallListeners() { 79 super.uninstallListeners(); 80 progressBar.removePropertyChangeListener(this); 81 } 82 83 /** 84 * {@inheritDoc} 85 */ 86 @Override 87 protected void installDefaults() { 88 updateStyle(progressBar); 89 } 90 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 context.dispose(); 122 } 123 124 /** 125 * {@inheritDoc} 126 */ 127 @Override 128 protected void uninstallDefaults() { 129 SynthContext context = getContext(progressBar, ENABLED); 130 131 style.uninstallDefaults(context); 132 context.dispose(); 133 style = null; 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public SynthContext getContext(JComponent c) { 141 return getContext(c, getComponentState(c)); 142 } 143 144 private SynthContext getContext(JComponent c, int state) { 145 return SynthContext.getContext(c, style, state); 146 } 147 148 private int getComponentState(JComponent c) { 149 return SynthLookAndFeel.getComponentState(c); 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public int getBaseline(JComponent c, int width, int height) { 157 super.getBaseline(c, width, height); 158 if (progressBar.isStringPainted() && 159 progressBar.getOrientation() == JProgressBar.HORIZONTAL) { 160 SynthContext context = getContext(c); 161 Font font = context.getStyle().getFont(context); 162 FontMetrics metrics = progressBar.getFontMetrics(font); 163 context.dispose(); 164 return (height - metrics.getAscent() - metrics.getDescent()) / 2 + 165 metrics.getAscent(); 166 } 167 return -1; 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 @Override 174 protected Rectangle getBox(Rectangle r) { 175 if (tileWhenIndeterminate) { 176 return SwingUtilities.calculateInnerArea(progressBar, r); 177 } else { 178 return super.getBox(r); 179 } 180 } 181 182 /** 183 * {@inheritDoc} 184 */ 185 @Override 186 protected void setAnimationIndex(int newValue) { 187 if (paintOutsideClip) { 188 if (getAnimationIndex() == newValue) { 189 return; 190 } 191 super.setAnimationIndex(newValue); 192 progressBar.repaint(); 193 } else { 194 super.setAnimationIndex(newValue); 195 } 196 } 197 198 /** 199 * Notifies this UI delegate to repaint the specified component. 200 * This method paints the component background, then calls 201 * the {@link #paint(SynthContext,Graphics)} method. 202 * 203 * <p>In general, this method does not need to be overridden by subclasses. 204 * All Look and Feel rendering code should reside in the {@code paint} method. 205 * 206 * @param g the {@code Graphics} object used for painting 207 * @param c the component being painted 208 * @see #paint(SynthContext,Graphics) 209 */ 210 @Override 211 public void update(Graphics g, JComponent c) { 212 SynthContext context = getContext(c); 213 214 SynthLookAndFeel.update(context, g); 215 context.getPainter().paintProgressBarBackground(context, 216 g, 0, 0, c.getWidth(), c.getHeight(), 217 progressBar.getOrientation()); 218 paint(context, g); 219 context.dispose(); 220 } 221 222 /** 223 * Paints the specified component according to the Look and Feel. 224 * <p>This method is not used by Synth Look and Feel. 225 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 226 * 227 * @param g the {@code Graphics} object used for painting 228 * @param c the component being painted 229 * @see #paint(SynthContext,Graphics) 230 */ 231 @Override 232 public void paint(Graphics g, JComponent c) { 233 SynthContext context = getContext(c); 234 235 paint(context, g); 236 context.dispose(); 237 } 238 239 /** 240 * Paints the specified component. 241 * 242 * @param context context for the component being painted 243 * @param g the {@code Graphics} object used for painting 244 * @see #update(Graphics,JComponent) 245 */ 246 protected void paint(SynthContext context, Graphics g) { 247 JProgressBar pBar = (JProgressBar)context.getComponent(); 248 int x = 0, y = 0, width = 0, height = 0; 249 if (!pBar.isIndeterminate()) { 250 Insets pBarInsets = pBar.getInsets(); 251 double percentComplete = pBar.getPercentComplete(); 252 if (percentComplete != 0.0) { 253 if (pBar.getOrientation() == JProgressBar.HORIZONTAL) { 254 x = pBarInsets.left + progressPadding; 255 y = pBarInsets.top + progressPadding; 256 width = (int)(percentComplete * (pBar.getWidth() 257 - (pBarInsets.left + progressPadding 258 + pBarInsets.right + progressPadding))); 259 height = pBar.getHeight() 260 - (pBarInsets.top + progressPadding 261 + pBarInsets.bottom + progressPadding); 262 263 if (!SynthLookAndFeel.isLeftToRight(pBar)) { 264 x = pBar.getWidth() - pBarInsets.right - width 265 - progressPadding - glowWidth; 266 } 267 } else { // JProgressBar.VERTICAL 268 x = pBarInsets.left + progressPadding; 269 width = pBar.getWidth() 270 - (pBarInsets.left + progressPadding 271 + pBarInsets.right + progressPadding); 272 height = (int)(percentComplete * (pBar.getHeight() 273 - (pBarInsets.top + progressPadding 274 + pBarInsets.bottom + progressPadding))); 275 y = pBar.getHeight() - pBarInsets.bottom - height 276 - progressPadding; 277 278 if (SynthLookAndFeel.isLeftToRight(pBar)) { 279 y -= glowWidth; 280 } 281 } 282 } 283 } else { 284 boxRect = getBox(boxRect); 285 x = boxRect.x + progressPadding; 286 y = boxRect.y + progressPadding; 287 width = boxRect.width - progressPadding - progressPadding; 288 height = boxRect.height - progressPadding - progressPadding; 289 } 290 291 //if tiling and indeterminate, then paint the progress bar foreground a 292 //bit wider than it should be. Shift as needed to ensure that there is 293 //an animated effect 294 if (tileWhenIndeterminate && pBar.isIndeterminate()) { 295 double percentComplete = (double)getAnimationIndex() / (double)getFrameCount(); 296 int offset = (int)(percentComplete * tileWidth); 297 Shape clip = g.getClip(); 298 g.clipRect(x, y, width, height); 299 if (pBar.getOrientation() == JProgressBar.HORIZONTAL) { 300 //paint each tile horizontally 301 for (int i=x-tileWidth+offset; i<=width; i+=tileWidth) { 302 context.getPainter().paintProgressBarForeground( 303 context, g, i, y, tileWidth, height, pBar.getOrientation()); 304 } 305 } else { //JProgressBar.VERTICAL 306 //paint each tile vertically 307 for (int i=y-offset; i<height+tileWidth; i+=tileWidth) { 308 context.getPainter().paintProgressBarForeground( 309 context, g, x, i, width, tileWidth, pBar.getOrientation()); 310 } 311 } 312 g.setClip(clip); 313 } else { 314 if (minBarSize == null || (width >= minBarSize.width 315 && height >= minBarSize.height)) { 316 context.getPainter().paintProgressBarForeground(context, g, 317 x, y, width, height, pBar.getOrientation()); 318 } 319 } 320 321 if (pBar.isStringPainted()) { 322 paintText(context, g, pBar.getString()); 323 } 324 } 325 326 /** 327 * Paints the component's text. 328 * 329 * @param context context for the component being painted 330 * @param g {@code Graphics} object used for painting 331 * @param title the text to paint 332 */ 333 protected void paintText(SynthContext context, Graphics g, String title) { 334 if (progressBar.isStringPainted()) { 335 SynthStyle style = context.getStyle(); 336 Font font = style.getFont(context); 337 FontMetrics fm = SwingUtilities2.getFontMetrics( 338 progressBar, g, font); 339 int strLength = style.getGraphicsUtils(context). 340 computeStringWidth(context, font, fm, title); 341 Rectangle bounds = progressBar.getBounds(); 342 343 if (rotateText && 344 progressBar.getOrientation() == JProgressBar.VERTICAL){ 345 Graphics2D g2 = (Graphics2D)g; 346 // Calculate the position for the text. 347 Point textPos; 348 AffineTransform rotation; 349 if (progressBar.getComponentOrientation().isLeftToRight()){ 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 } else { 355 rotation = AffineTransform.getRotateInstance(Math.PI/2); 356 textPos = new Point( 357 (bounds.width-fm.getAscent()+fm.getDescent())/2, 358 (bounds.height-strLength)/2); 359 } 360 361 // Progress bar isn't wide enough for the font. Don't paint it. 362 if (textPos.x < 0) { 363 return; 364 } 365 366 // Paint the text. 367 font = font.deriveFont(rotation); 368 g2.setFont(font); 369 g2.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 370 style.getGraphicsUtils(context).paintText(context, g, title, 371 textPos.x, textPos.y, -1); 372 } else { 373 // Calculate the bounds for the text. 374 Rectangle textRect = new Rectangle( 375 (bounds.width / 2) - (strLength / 2), 376 (bounds.height - 377 (fm.getAscent() + fm.getDescent())) / 2, 378 0, 0); 379 380 // Progress bar isn't tall enough for the font. Don't paint it. 381 if (textRect.y < 0) { 382 return; 383 } 384 385 // Paint the text. 386 g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); 387 g.setFont(font); 388 style.getGraphicsUtils(context).paintText(context, g, title, 389 textRect.x, textRect.y, -1); 390 } 391 } 392 } 393 394 /** 395 * {@inheritDoc} 396 */ 397 @Override 398 public void paintBorder(SynthContext context, Graphics g, int x, 399 int y, int w, int h) { 400 context.getPainter().paintProgressBarBorder(context, g, x, y, w, h, 401 progressBar.getOrientation()); 402 } 403 404 /** 405 * {@inheritDoc} 406 */ 407 @Override 408 public void propertyChange(PropertyChangeEvent e) { 409 if (SynthLookAndFeel.shouldUpdateStyle(e) || 410 "indeterminate".equals(e.getPropertyName())) { 411 updateStyle((JProgressBar)e.getSource()); 412 } 413 } 414 415 /** 416 * {@inheritDoc} 417 */ 418 @Override 419 public Dimension getPreferredSize(JComponent c) { 420 Dimension size = null; 421 Insets border = progressBar.getInsets(); 422 FontMetrics fontSizer = progressBar.getFontMetrics(progressBar.getFont()); 423 String progString = progressBar.getString(); 424 int stringHeight = fontSizer.getHeight() + fontSizer.getDescent(); 425 426 if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { 427 size = new Dimension(getPreferredInnerHorizontal()); 428 if (progressBar.isStringPainted()) { 429 // adjust the height if necessary to make room for the string 430 if (stringHeight > size.height) { 431 size.height = stringHeight; 432 } 433 434 // adjust the width if necessary to make room for the string 435 int stringWidth = SwingUtilities2.stringWidth( 436 progressBar, fontSizer, progString); 437 if (stringWidth > size.width) { 438 size.width = stringWidth; 439 } 440 } 441 } else { 442 size = new Dimension(getPreferredInnerVertical()); 443 if (progressBar.isStringPainted()) { 444 // make sure the width is big enough for the string 445 if (stringHeight > size.width) { 446 size.width = stringHeight; 447 } 448 449 // make sure the height is big enough for the string 450 int stringWidth = SwingUtilities2.stringWidth( 451 progressBar, fontSizer, progString); 452 if (stringWidth > size.height) { 453 size.height = stringWidth; 454 } 455 } 456 } 457 458 // handle scaling for sizeVarients for special case components. The 459 // key "JComponent.sizeVariant" scales for large/small/mini 460 // components are based on Apples LAF 461 String scaleKey = (String)progressBar.getClientProperty( 462 "JComponent.sizeVariant"); 463 if (scaleKey != null){ 464 if ("large".equals(scaleKey)){ 465 size.width *= 1.15f; 466 size.height *= 1.15f; 467 } else if ("small".equals(scaleKey)){ 468 size.width *= 0.90f; 469 size.height *= 0.90f; 470 } else if ("mini".equals(scaleKey)){ 471 size.width *= 0.784f; 472 size.height *= 0.784f; 473 } 474 } 475 476 size.width += border.left + border.right; 477 size.height += border.top + border.bottom; 478 479 return size; 480 } 481 }