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