1 /*
   2  * Copyright (c) 1997, 2014, 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 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 
 141                 if (barRectWidth <= 0 || barRectHeight <= 0) {
 142                     return;
 143                 }
 144 
 145                 Graphics2D g2 = (Graphics2D)g;
 146                 g2.setStroke(new BasicStroke((float)(vertical ? barRectWidth : barRectHeight),
 147                                              BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
 148                 if (!vertical) {
 149                     if (isLeftToRight) {
 150                         g2.drawLine(2,              barRectHeight / 2 + 1,
 151                                     amountFull - 2, barRectHeight / 2 + 1);
 152                     } else {
 153                         g2.drawLine(2 + barRectWidth,
 154                                     barRectHeight / 2 + 1,
 155                                     2 + barRectWidth - (amountFull - 2),
 156                                     barRectHeight / 2 + 1);
 157                     }
 158                     paintString(g, 0, 0, barRectWidth, barRectHeight, amountFull, null);
 159                 } else {
 160                     g2.drawLine(barRectWidth/2 + 1, barRectHeight + 1,
 161                                 barRectWidth/2 + 1, barRectHeight + 1 - amountFull + 2);
 162                     paintString(g, 2, 2, barRectWidth, barRectHeight, amountFull, null);
 163                 }
 164 
 165             } else {
 166                 Skin skin = xp.getSkin(progressBar, vertical ? Part.PP_CHUNKVERT : Part.PP_CHUNK);
 167                 int thickness;
 168                 if (vertical) {
 169                     thickness = barRectWidth - 5;
 170                 } else {
 171                     thickness = barRectHeight - 5;
 172                 }
 173 
 174                 int chunkSize = xp.getInt(progressBar, Part.PP_PROGRESS, null, Prop.PROGRESSCHUNKSIZE, 2);
 175                 int spaceSize = xp.getInt(progressBar, Part.PP_PROGRESS, null, Prop.PROGRESSSPACESIZE, 0);
 176                 int nChunks = (amountFull-4) / (chunkSize + spaceSize);
 177 
 178                 // See if we can squeeze in an extra chunk without spacing after
 179                 if (spaceSize > 0 && (nChunks * (chunkSize + spaceSize) + chunkSize) < (amountFull-4)) {
 180                     nChunks++;
 181                 }
 182 
 183                 for (int i = 0; i < nChunks; i++) {
 184                     if (vertical) {
 185                         skin.paintSkin(g,
 186                                        3, barRectHeight - i * (chunkSize + spaceSize) - chunkSize - 2,
 187                                        thickness, chunkSize, null);
 188                     } else {
 189                         if (isLeftToRight) {
 190                             skin.paintSkin(g,
 191                                            4 + i * (chunkSize + spaceSize), 2,
 192                                            chunkSize, thickness, null);
 193                         } else {
 194                             skin.paintSkin(g,
 195                                            barRectWidth - (2 + (i+1) * (chunkSize + spaceSize)), 2,
 196                                            chunkSize, thickness, null);
 197                         }
 198                     }
 199                 }
 200             }
 201         } else {
 202             super.paintDeterminate(g, c);
 203         }
 204     }
 205 
 206 
 207     /**
 208      * {@inheritDoc}
 209      * @since 1.6
 210      */
 211     protected void setAnimationIndex(int newValue) {
 212         super.setAnimationIndex(newValue);
 213         XPStyle xp = XPStyle.getXP();
 214         if (xp != null) {
 215             if (boxRect != null) {
 216                 // get the full repaint area and add it the
 217                 // previous one so we can erase it
 218                 Rectangle chunk = getFullChunkBounds(boxRect);
 219                 if (previousFullBox != null) {
 220                     chunk.add(previousFullBox);
 221                 }
 222                 progressBar.repaint(chunk);
 223             } else {
 224                 progressBar.repaint();
 225             }
 226         }
 227     }
 228 
 229 
 230     /**
 231      * {@inheritDoc}
 232      * @since 1.6
 233      */
 234     protected int getBoxLength(int availableLength, int otherDimension) {
 235         XPStyle xp = XPStyle.getXP();
 236         if (xp != null) {
 237             return 6; // an apparently hard coded value in Windows
 238         }
 239         return super.getBoxLength(availableLength, otherDimension);
 240     }
 241 
 242     /**
 243      * {@inheritDoc}
 244      * @since 1.6
 245      */
 246     protected Rectangle getBox(Rectangle r) {
 247         Rectangle rect = super.getBox(r);
 248 
 249         XPStyle xp = XPStyle.getXP();
 250         if (xp != null) {
 251             boolean vertical = (progressBar.getOrientation()
 252                                  == JProgressBar.VERTICAL);
 253             Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR;
 254             Insets ins = indeterminateInsets;
 255 
 256             int currentFrame = getAnimationIndex();
 257             int framecount = getFrameCount()/2;
 258 
 259             int gap = xp.getInt(progressBar, Part.PP_PROGRESS, null,
 260                     Prop.PROGRESSSPACESIZE, 0);
 261             currentFrame = currentFrame % framecount;
 262 
 263             // this code adjusts the chunk size to properly account for the
 264             // size and gap specified in the XP style. It also does it's own
 265             // box placement for the chunk animation. This is required because
 266             // the inherited algorithm from BasicProgressBarUI goes back and
 267             // forth whereas XP only goes in one direction. XP also has ghosted
 268             // trailing chunks to create the illusion of speed. This code
 269             // adjusts the pixel length of the animation to account for the
 270             // trails.
 271             if (!vertical) {
 272                 rect.y = rect.y + ins.top;
 273                 rect.height = progressBar.getHeight() - ins.top - ins.bottom;
 274                 int len = progressBar.getWidth() - ins.left - ins.right;
 275                 len += (rect.width+gap)*2; // add 2x for the trails
 276                 double delta = (double)(len) / (double)framecount;
 277                 rect.x = (int)(delta * currentFrame) + ins.left;
 278             } else {
 279                 rect.x = rect.x + ins.left;
 280                 rect.width = progressBar.getWidth() - ins.left - ins.right;
 281                 int len = progressBar.getHeight() - ins.top - ins.bottom;
 282                 len += (rect.height+gap)*2; // add 2x for the trails
 283                 double delta = (double)(len) / (double)framecount;
 284                 rect.y = (int)(delta * currentFrame) + ins.top;
 285             }
 286         }
 287         return rect;
 288     }
 289 
 290 
 291     protected void paintIndeterminate(Graphics g, JComponent c) {
 292         XPStyle xp = XPStyle.getXP();
 293         if (xp != null) {
 294             boolean vertical = (progressBar.getOrientation()
 295                                  == JProgressBar.VERTICAL);
 296             int barRectWidth = progressBar.getWidth();
 297             int barRectHeight = progressBar.getHeight();
 298             paintXPBackground(g, vertical, barRectWidth, barRectHeight);
 299 
 300             // Paint the bouncing box.
 301             boxRect = getBox(boxRect);
 302             if (boxRect != null) {
 303                 g.setColor(progressBar.getForeground());
 304                 if (!(g instanceof Graphics2D)) {
 305                     return;
 306                 }
 307                 paintIndeterminateFrame(boxRect, (Graphics2D)g, vertical,
 308                                         barRectWidth, barRectHeight);
 309                 if (progressBar.isStringPainted()) {
 310                     if (!vertical) {
 311                         paintString(g, -1, -1, barRectWidth, barRectHeight, 0, null);
 312                     } else {
 313                         paintString(g, 1, 1, barRectWidth, barRectHeight, 0, null);
 314                     }
 315                 }
 316             }
 317         } else {
 318             super.paintIndeterminate(g, c);
 319         }
 320     }
 321 
 322     private Rectangle getFullChunkBounds(Rectangle box) {
 323         boolean vertical = (progressBar.getOrientation() == JProgressBar.VERTICAL);
 324         XPStyle xp = XPStyle.getXP();
 325         int gap = (xp != null) ? xp.getInt(progressBar, Part.PP_PROGRESS,
 326                                            null, Prop.PROGRESSSPACESIZE, 0)
 327                                : 0;
 328 
 329         if (!vertical) {
 330             int chunksize = box.width+gap;
 331             return new Rectangle(box.x-chunksize*2, box.y, chunksize*3, box.height);
 332         } else {
 333             int chunksize = box.height+gap;
 334             return new Rectangle(box.x, box.y-chunksize*2, box.width, chunksize*3);
 335         }
 336     }
 337 
 338     private void paintIndeterminateFrame(Rectangle box, Graphics2D g,
 339                                           boolean vertical,
 340                                           int bgwidth, int bgheight) {
 341         XPStyle xp = XPStyle.getXP();
 342         if (xp == null) {
 343             return;
 344         }
 345 
 346         // create a new graphics to keep drawing surface state
 347         Graphics2D gfx = (Graphics2D)g.create();
 348 
 349         Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR;
 350         Part chunk = vertical ? Part.PP_CHUNKVERT : Part.PP_CHUNK;
 351 
 352         // calculate the chunk offsets
 353         int gap = xp.getInt(progressBar, Part.PP_PROGRESS, null,
 354                             Prop.PROGRESSSPACESIZE, 0);
 355         int deltax = 0;
 356         int deltay = 0;
 357         if (!vertical) {
 358             deltax = -box.width - gap;
 359             deltay = 0;
 360         } else {
 361             deltax = 0;
 362             deltay = -box.height - gap;
 363         }
 364 
 365         // Calculate the area of the chunks combined
 366         Rectangle fullBox = getFullChunkBounds(box);
 367 
 368         // save this box for the next time
 369         previousFullBox = fullBox;
 370 
 371         // this is the entire progress bar minus the track and borders
 372         Insets ins = indeterminateInsets;
 373         Rectangle progbarExtents = new Rectangle(ins.left, ins.top,
 374                                                  bgwidth  - ins.left - ins.right,
 375                                                  bgheight - ins.top  - ins.bottom);
 376 
 377         // only paint where the chunks overlap with the progress bar drawing area
 378         Rectangle repaintArea = progbarExtents.intersection(fullBox);
 379 
 380         // adjust the cliprect to chop the chunks when they go off the end
 381         gfx.clip(repaintArea);
 382 
 383         // get the skin
 384         XPStyle.Skin skin = xp.getSkin(progressBar, chunk);
 385 
 386         // do the drawing
 387         gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
 388         skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null);
 389         box.translate(deltax, deltay);
 390         gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
 391         skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null);
 392         box.translate(deltax, deltay);
 393         gfx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
 394         skin.paintSkin(gfx, box.x, box.y, box.width, box.height, null);
 395 
 396         // get rid of our clip and composite changes
 397         gfx.dispose();
 398     }
 399 
 400     private void paintXPBackground(Graphics g, boolean vertical,
 401                                    int barRectWidth, int barRectHeight) {
 402         XPStyle xp = XPStyle.getXP();
 403         if (xp == null) {
 404             return;
 405         }
 406         Part part = vertical ? Part.PP_BARVERT : Part.PP_BAR;
 407         Skin skin = xp.getSkin(progressBar, part);
 408 
 409         // Paint background
 410         skin.paintSkin(g, 0, 0, barRectWidth, barRectHeight, null);
 411     }
 412 }