J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package com.sun.java.swing.plaf.windows; |
| 27 | |
| 28 | import javax.swing.plaf.basic.*; |
| 29 | import javax.swing.plaf.*; |
| 30 | import javax.swing.*; |
| 31 | import java.awt.*; |
| 32 | |
| 33 | import static com.sun.java.swing.plaf.windows.TMSchema.*; |
| 34 | import static com.sun.java.swing.plaf.windows.XPStyle.Skin; |
| 35 | |
| 36 | |
| 37 | /** |
| 38 | * Windows rendition of the component. |
| 39 | * <p> |
| 40 | * <strong>Warning:</strong> |
| 41 | * Serialized objects of this class will not be compatible with |
| 42 | * future Swing releases. The current serialization support is appropriate |
| 43 | * for short term storage or RMI between applications running the same |
| 44 | * version of Swing. A future release of Swing will provide support for |
| 45 | * long term persistence. |
| 46 | * |
| 47 | * @author Michael C. Albers |
| 48 | */ |
| 49 | public class WindowsProgressBarUI extends BasicProgressBarUI |
| 50 | { |
| 51 | |
| 52 | private Rectangle previousFullBox; |
| 53 | private Insets indeterminateInsets; |
| 54 | |
| 55 | public static ComponentUI createUI(JComponent x) { |
| 56 | return new WindowsProgressBarUI(); |
| 57 | } |
| 58 | |
| 59 | |
| 60 | protected void installDefaults() { |
| 61 | super.installDefaults(); |
| 62 | |
| 63 | if (XPStyle.getXP() != null) { |
| 64 | LookAndFeel.installProperty(progressBar, "opaque", Boolean.FALSE); |
| 65 | progressBar.setBorder(null); |
| 66 | indeterminateInsets = UIManager.getInsets("ProgressBar.indeterminateInsets"); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Returns the baseline. |
| 72 | * |
| 73 | * @throws NullPointerException {@inheritDoc} |
| 74 | * @throws IllegalArgumentException {@inheritDoc} |
| 75 | * @see javax.swing.JComponent#getBaseline(int, int) |
| 76 | * @since 1.6 |
| 77 | */ |
| 78 | public int getBaseline(JComponent c, int width, int height) { |
| 79 | int baseline = super.getBaseline(c, width, height); |
| 80 | if (XPStyle.getXP() != null && progressBar.isStringPainted() && |
| 81 | progressBar.getOrientation() == JProgressBar.HORIZONTAL) { |
| 82 | FontMetrics metrics = progressBar. |
| 83 | getFontMetrics(progressBar.getFont()); |
| 84 | int y = progressBar.getInsets().top; |
| 85 | if (progressBar.isIndeterminate()) { |
| 86 | y = -1; |
| 87 | height--; |
| 88 | } |
| 89 | else { |
| 90 | y = 0; |
| 91 | height -= 3; |
| 92 | } |
| 93 | baseline = y + (height + metrics.getAscent() - |
| 94 | metrics.getLeading() - |
| 95 | metrics.getDescent()) / 2; |
| 96 | } |
| 97 | return baseline; |
| 98 | } |
| 99 | |
| 100 | protected Dimension getPreferredInnerHorizontal() { |
| 101 | XPStyle xp = XPStyle.getXP(); |
| 102 | if (xp != null) { |
| 103 | Skin skin = xp.getSkin(progressBar, Part.PP_BAR); |
| 104 | return new Dimension( |
| 105 | (int)super.getPreferredInnerHorizontal().getWidth(), |
| 106 | skin.getHeight()); |
| 107 | } |
| 108 | return super.getPreferredInnerHorizontal(); |
| 109 | } |
| 110 | |
| 111 | protected Dimension getPreferredInnerVertical() { |
| 112 | XPStyle xp = XPStyle.getXP(); |
| 113 | if (xp != null) { |
| 114 | Skin skin = xp.getSkin(progressBar, Part.PP_BARVERT); |
| 115 | return new Dimension( |
| 116 | skin.getWidth(), |
| 117 | (int)super.getPreferredInnerVertical().getHeight()); |
| 118 | } |
| 119 | return super.getPreferredInnerVertical(); |
| 120 | } |
| 121 | |
| 122 | protected void paintDeterminate(Graphics g, JComponent c) { |
| 123 | XPStyle xp = XPStyle.getXP(); |
| 124 | if (xp != null) { |
| 125 | boolean vertical = (progressBar.getOrientation() == JProgressBar.VERTICAL); |
| 126 | boolean isLeftToRight = WindowsGraphicsUtils.isLeftToRight(c); |
| 127 | int barRectWidth = progressBar.getWidth(); |
| 128 | int barRectHeight = progressBar.getHeight()-1; |
| 129 | // amount of progress to draw |
| 130 | int amountFull = getAmountFull(null, barRectWidth, barRectHeight); |
| 131 | |
| 132 | paintXPBackground(g, vertical, barRectWidth, barRectHeight); |
| 133 | // Paint progress |
| 134 | if (progressBar.isStringPainted()) { |
| 135 | // Do not paint the standard stripes from the skin, because they obscure |
| 136 | // the text |
| 137 | g.setColor(progressBar.getForeground()); |
| 138 | barRectHeight -= 2; |
| 139 | barRectWidth -= 2; |
| 140 | Graphics2D g2 = (Graphics2D)g; |
| 141 | g2.setStroke(new BasicStroke((float)(vertical ? barRectWidth : barRectHeight), |
| 142 | BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); |
| 143 | if (!vertical) { |
| 144 | if (isLeftToRight) { |
| 145 | g2.drawLine(2, barRectHeight / 2 + 1, |
| 146 | amountFull - 2, barRectHeight / 2 + 1); |
| 147 | } else { |
| 148 | g2.drawLine(2 + barRectWidth, |
| 149 | barRectHeight / 2 + 1, |
| 150 | 2 + barRectWidth - (amountFull - 2), |
| 151 | barRectHeight / 2 + 1); |
| 152 | } |
| 153 | paintString(g, 0, 0, barRectWidth, barRectHeight, amountFull, null); |
| 154 | } else { |
| 155 | g2.drawLine(barRectWidth/2 + 1, barRectHeight + 1, |
| 156 | barRectWidth/2 + 1, barRectHeight + 1 - amountFull + 2); |
| 157 | paintString(g, 2, 2, barRectWidth, barRectHeight, amountFull, null); |
| 158 | } |
| 159 | |
| 160 | } else { |
| 161 | Skin skin = xp.getSkin(progressBar, vertical ? Part.PP_CHUNKVERT : Part.PP_CHUNK); |
| 162 | int thickness; |
| 163 | if (vertical) { |
| 164 | thickness = barRectWidth - 5; |
| 165 | } else { |
| 166 | thickness = barRectHeight - 5; |
| 167 | } |
| 168 | |
| 169 | int chunkSize = xp.getInt(progressBar, Part.PP_PROGRESS, null, Prop.PROGRESSCHUNKSIZE, 2); |
| 170 | int spaceSize = xp.getInt(progressBar, Part.PP_PROGRESS, null, Prop.PROGRESSSPACESIZE, 0); |
| 171 | int nChunks = (amountFull-4) / (chunkSize + spaceSize); |
| 172 | |
| 173 | // See if we can squeeze in an extra chunk without spacing after |
| 174 | if (spaceSize > 0 && (nChunks * (chunkSize + spaceSize) + chunkSize) < (amountFull-4)) { |
| 175 | nChunks++; |
| 176 | } |
| 177 | |
| 178 | for (int i = 0; i < nChunks; i++) { |
| 179 | if (vertical) { |
| 180 | skin.paintSkin(g, |
| 181 | 3, barRectHeight - i * (chunkSize + spaceSize) - chunkSize - 2, |
| 182 | thickness, chunkSize, null); |
| 183 | } else { |
| 184 | if (isLeftToRight) { |
| 185 | skin.paintSkin(g, |
| 186 | 4 + i * (chunkSize + spaceSize), 2, |
| 187 | chunkSize, thickness, null); |
| 188 | } else { |
| 189 | skin.paintSkin(g, |
| 190 | barRectWidth - (2 + (i+1) * (chunkSize + spaceSize)), 2, |
| 191 | chunkSize, thickness, null); |
| 192 | } |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } else { |
| 197 | super.paintDeterminate(g, c); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | |
| 202 | /** |
| 203 | * {@inheritDoc} |
| 204 | * @since 1.6 |
| 205 | */ |
| 206 | protected void setAnimationIndex(int newValue) { |
| 207 | super.setAnimationIndex(newValue); |
| 208 | XPStyle xp = XPStyle.getXP(); |
| 209 | if (xp != null) { |
| 210 | if (boxRect != null) { |
| 211 | // get the full repaint area and add it the |
| 212 | // previous one so we can erase it |
| 213 | Rectangle chunk = getFullChunkBounds(boxRect); |
| 214 | if (previousFullBox != null) { |
| 215 | chunk.add(previousFullBox); |
| 216 | } |
| 217 | progressBar.repaint(chunk); |
| 218 | } else { |
| 219 | progressBar.repaint(); |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | |
| 225 | /** |
| 226 | * {@inheritDoc} |
| 227 | * @since 1.6 |
| 228 | */ |
| 229 | protected int getBoxLength(int availableLength, int otherDimension) { |
| 230 | XPStyle xp = XPStyle.getXP(); |
| 231 | if (xp != null) { |
| 232 | return 6; // an apparently hard coded value in Windows |
| 233 | } |
| 234 | return super.getBoxLength(availableLength, otherDimension); |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * {@inheritDoc} |
| 239 | * @since 1.6 |
| 240 | */ |
| 241 | protected Rectangle getBox(Rectangle r) { |
| 242 | Rectangle rect = super.getBox(r); |
| 243 | |
| 244 | XPStyle xp = XPStyle.getXP(); |
| 245 | if (xp != null) { |
| 246 | boolean vertical = (progressBar.getOrientation() |
| 247 | == JProgressBar.VERTICAL); |
| 248 | Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR; |
| 249 | Insets ins = indeterminateInsets; |
| 250 | |
| 251 | int currentFrame = getAnimationIndex(); |
| 252 | int framecount = getFrameCount()/2; |
| 253 | |
| 254 | int gap = xp.getInt(progressBar, Part.PP_PROGRESS, null, |
| 255 | Prop.PROGRESSSPACESIZE, 0); |
| 256 | currentFrame = currentFrame % framecount; |
| 257 | |
| 258 | // this code adjusts the chunk size to properly account for the |
| 259 | // size and gap specified in the XP style. It also does it's own |
| 260 | // box placement for the chunk animation. This is required because |
| 261 | // the inherited algorithm from BasicProgressBarUI goes back and |
| 262 | // forth whereas XP only goes in one direction. XP also has ghosted |
| 263 | // trailing chunks to create the illusion of speed. This code |
| 264 | // adjusts the pixel length of the animation to account for the |
| 265 | // trails. |
| 266 | if (!vertical) { |
| 267 | rect.y = rect.y + ins.top; |
| 268 | rect.height = progressBar.getHeight() - ins.top - ins.bottom; |
| 269 | int len = progressBar.getWidth() - ins.left - ins.right; |
| 270 | len += (rect.width+gap)*2; // add 2x for the trails |
| 271 | double delta = (double)(len) / (double)framecount; |
| 272 | rect.x = (int)(delta * currentFrame) + ins.left; |
| 273 | } else { |
| 274 | rect.x = rect.x + ins.left; |
| 275 | rect.width = progressBar.getWidth() - ins.left - ins.right; |
| 276 | int len = progressBar.getHeight() - ins.top - ins.bottom; |
| 277 | len += (rect.height+gap)*2; // add 2x for the trails |
| 278 | double delta = (double)(len) / (double)framecount; |
| 279 | rect.y = (int)(delta * currentFrame) + ins.top; |
| 280 | } |
| 281 | } |
| 282 | return rect; |
| 283 | } |
| 284 | |
| 285 | |
| 286 | protected void paintIndeterminate(Graphics g, JComponent c) { |
| 287 | XPStyle xp = XPStyle.getXP(); |
| 288 | if (xp != null) { |
| 289 | boolean vertical = (progressBar.getOrientation() |
| 290 | == JProgressBar.VERTICAL); |
| 291 | int barRectWidth = progressBar.getWidth(); |
| 292 | int barRectHeight = progressBar.getHeight(); |
| 293 | paintXPBackground(g, vertical, barRectWidth, barRectHeight); |
| 294 | |
| 295 | // Paint the bouncing box. |
| 296 | boxRect = getBox(boxRect); |
| 297 | if (boxRect != null) { |
| 298 | g.setColor(progressBar.getForeground()); |
| 299 | if (!(g instanceof Graphics2D)) { |
| 300 | return; |
| 301 | } |
| 302 | paintIndeterminateFrame(boxRect, (Graphics2D)g, vertical, |
| 303 | barRectWidth, barRectHeight); |
| 304 | if (progressBar.isStringPainted()) { |
| 305 | if (!vertical) { |
| 306 | paintString(g, -1, -1, barRectWidth, barRectHeight, 0, null); |
| 307 | } else { |
| 308 | paintString(g, 1, 1, barRectWidth, barRectHeight, 0, null); |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | } else { |
| 313 | super.paintIndeterminate(g, c); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | private Rectangle getFullChunkBounds(Rectangle box) { |
| 318 | boolean vertical = (progressBar.getOrientation() == JProgressBar.VERTICAL); |
| 319 | XPStyle xp = XPStyle.getXP(); |
| 320 | int gap = xp.getInt(progressBar, Part.PP_PROGRESS, null, |
| 321 | Prop.PROGRESSSPACESIZE, 0); |
| 322 | |
| 323 | if (!vertical) { |
| 324 | int chunksize = box.width+gap; |
| 325 | return new Rectangle(box.x-chunksize*2, box.y, chunksize*3, box.height); |
| 326 | } else { |
| 327 | int chunksize = box.height+gap; |
| 328 | return new Rectangle(box.x, box.y-chunksize*2, box.width, chunksize*3); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | private void paintIndeterminateFrame(Rectangle box, Graphics2D g, |
| 333 | boolean vertical, |
| 334 | int bgwidth, int bgheight) { |
| 335 | XPStyle xp = XPStyle.getXP(); |
| 336 | |
| 337 | // create a new graphics to keep drawing surface state |
| 338 | Graphics2D gfx = (Graphics2D)g.create(); |
| 339 | |
| 340 | Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR; |
| 341 | Part chunk = vertical ? Part.PP_CHUNKVERT : Part.PP_CHUNK; |
| 342 | |
| 343 | // calculate the chunk offsets |
| 344 | int gap = xp.getInt(progressBar, Part.PP_PROGRESS, null, |
| 345 | Prop.PROGRESSSPACESIZE, 0); |
| 346 | int deltax = 0; |
| 347 | int deltay = 0; |
| 348 | if (!vertical) { |
| 349 | deltax = -box.width - gap; |
| 350 | deltay = 0; |
| 351 | } else { |
| 352 | deltax = 0; |
| 353 | deltay = -box.height - gap; |
| 354 | } |
| 355 | |
| 356 | // Calculate the area of the chunks combined |
| 357 | Rectangle fullBox = getFullChunkBounds(box); |
| 358 | |
| 359 | // save this box for the next time |
| 360 | previousFullBox = fullBox; |
| 361 | |
| 362 | // this is the entire progress bar minus the track and borders |
| 363 | Insets ins = indeterminateInsets; |
| 364 | Rectangle progbarExtents = new Rectangle(ins.left, ins.top, |
| 365 | bgwidth - ins.left - ins.right, |
| 366 | bgheight - ins.top - ins.bottom); |
| 367 | |
| 368 | // only paint where the chunks overlap with the progress bar drawing area |
| 369 | Rectangle repaintArea = progbarExtents.intersection(fullBox); |
| 370 | |
| 371 | // adjust the cliprect to chop the chunks when they go off the end |
| 372 | gfx.clip(repaintArea); |
| 373 | |
| 374 | // get the skin |
| 375 | XPStyle.Skin skin = xp.getSkin(progressBar, chunk); |
| 376 | |
| 377 | // do the drawing |
| 378 | gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); |
| 379 | skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null); |
| 380 | box.translate(deltax, deltay); |
| 381 | gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); |
| 382 | skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null); |
| 383 | box.translate(deltax, deltay); |
| 384 | gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f)); |
| 385 | skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null); |
| 386 | |
| 387 | // get rid of our clip and composite changes |
| 388 | gfx.dispose(); |
| 389 | } |
| 390 | |
| 391 | private void paintXPBackground(Graphics g, boolean vertical, |
| 392 | int barRectWidth, int barRectHeight) { |
| 393 | XPStyle xp = XPStyle.getXP(); |
| 394 | Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR; |
| 395 | Skin skin = xp.getSkin(progressBar, part); |
| 396 | |
| 397 | // Paint background |
| 398 | skin.paintSkin(g, 0, 0, barRectWidth, barRectHeight, null); |
| 399 | } |
| 400 | } |