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 
createUI(JComponent x)55     public static ComponentUI createUI(JComponent x) {
56         return new WindowsProgressBarUI();
57     }
58 
59 
installDefaults()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      */
getBaseline(JComponent c, int width, int height)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 
getPreferredInnerHorizontal()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 
getPreferredInnerVertical()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 
paintDeterminate(Graphics g, JComponent c)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      */
setAnimationIndex(int newValue)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      */
getBoxLength(int availableLength, int otherDimension)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      */
getBox(Rectangle r)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 
paintIndeterminate(Graphics g, JComponent c)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 
getFullChunkBounds(Rectangle box)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 
paintIndeterminateFrame(Rectangle box, Graphics2D g, boolean vertical, int bgwidth, int bgheight)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 
paintXPBackground(Graphics g, boolean vertical, int barRectWidth, int barRectHeight)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 }
413