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 java.awt.*;
29 import java.awt.event.*;
30 import java.awt.image.*;
31 import java.lang.ref.*;
32 import java.util.*;
33 import javax.swing.plaf.basic.*;
34 import javax.swing.*;
35 import javax.swing.plaf.ComponentUI;
36
37 import static com.sun.java.swing.plaf.windows.TMSchema.*;
38 import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
39
40
41 /**
42 * Windows rendition of the component.
43 * <p>
44 * <strong>Warning:</strong>
45 * Serialized objects of this class will not be compatible with
46 * future Swing releases. The current serialization support is appropriate
47 * for short term storage or RMI between applications running the same
48 * version of Swing. A future release of Swing will provide support for
49 * long term persistence.
50 */
51 public class WindowsScrollBarUI extends BasicScrollBarUI {
52 private Grid thumbGrid;
53 private Grid highlightGrid;
54 private Dimension horizontalThumbSize;
55 private Dimension verticalThumbSize;
56
57 /**
58 * Creates a UI for a JScrollBar.
59 *
60 * @param c the text field
61 * @return the UI
62 */
63 public static ComponentUI createUI(JComponent c) {
64 return new WindowsScrollBarUI();
65 }
66
67 protected void installDefaults() {
68 super.installDefaults();
69
70 XPStyle xp = XPStyle.getXP();
71 if (xp != null) {
72 scrollbar.setBorder(null);
73 horizontalThumbSize = getSize(scrollbar, xp, Part.SBP_THUMBBTNHORZ);
74 verticalThumbSize = getSize(scrollbar, xp, Part.SBP_THUMBBTNVERT);
75 } else {
76 horizontalThumbSize = null;
77 verticalThumbSize = null;
78 }
79 }
80
81 private static Dimension getSize(Component component, XPStyle xp, Part part) {
82 Skin skin = xp.getSkin(component, part);
83 return new Dimension(skin.getWidth(), skin.getHeight());
84 }
85
86 @Override
87 protected Dimension getMinimumThumbSize() {
88 if ((horizontalThumbSize == null) || (verticalThumbSize == null)) {
89 return super.getMinimumThumbSize();
90 }
91 return JScrollBar.HORIZONTAL == scrollbar.getOrientation()
92 ? horizontalThumbSize
93 : verticalThumbSize;
94 }
95
96 public void uninstallUI(JComponent c) {
97 super.uninstallUI(c);
98 thumbGrid = highlightGrid = null;
99 }
100
101 protected void configureScrollBarColors() {
102 super.configureScrollBarColors();
103 Color color = UIManager.getColor("ScrollBar.trackForeground");
104 if (color != null && trackColor != null) {
105 thumbGrid = Grid.getGrid(color, trackColor);
106 }
107
108 color = UIManager.getColor("ScrollBar.trackHighlightForeground");
109 if (color != null && trackHighlightColor != null) {
110 highlightGrid = Grid.getGrid(color, trackHighlightColor);
111 }
112 }
113
114 protected JButton createDecreaseButton(int orientation) {
115 return new WindowsArrowButton(orientation,
116 UIManager.getColor("ScrollBar.thumb"),
117 UIManager.getColor("ScrollBar.thumbShadow"),
118 UIManager.getColor("ScrollBar.thumbDarkShadow"),
119 UIManager.getColor("ScrollBar.thumbHighlight"));
120 }
121
122 protected JButton createIncreaseButton(int orientation) {
123 return new WindowsArrowButton(orientation,
124 UIManager.getColor("ScrollBar.thumb"),
125 UIManager.getColor("ScrollBar.thumbShadow"),
126 UIManager.getColor("ScrollBar.thumbDarkShadow"),
127 UIManager.getColor("ScrollBar.thumbHighlight"));
128 }
129
130 /**
131 * {@inheritDoc}
132 * @since 1.6
133 */
134 @Override
135 protected ArrowButtonListener createArrowButtonListener(){
136 // we need to repaint the entire scrollbar because state change for each
137 // button causes a state change for the thumb and other button on Vista
138 if(XPStyle.isVista()) {
139 return new ArrowButtonListener() {
140 public void mouseEntered(MouseEvent evt) {
141 repaint();
142 super.mouseEntered(evt);
143 }
144 public void mouseExited(MouseEvent evt) {
145 repaint();
146 super.mouseExited(evt);
147 }
148 private void repaint() {
149 scrollbar.repaint();
150 }
151 };
152 } else {
153 return super.createArrowButtonListener();
154 }
155 }
156
157 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds){
158 boolean v = (scrollbar.getOrientation() == JScrollBar.VERTICAL);
159
160 XPStyle xp = XPStyle.getXP();
161 if (xp != null) {
162 JScrollBar sb = (JScrollBar)c;
163 State state = State.NORMAL;
164 // Pending: Implement rollover (hot) and pressed
165 if (!sb.isEnabled()) {
166 state = State.DISABLED;
167 }
168 Part part = v ? Part.SBP_LOWERTRACKVERT : Part.SBP_LOWERTRACKHORZ;
169 xp.getSkin(sb, part).paintSkin(g, trackBounds, state);
170 } else if (thumbGrid == null) {
171 super.paintTrack(g, c, trackBounds);
172 }
173 else {
174 thumbGrid.paint(g, trackBounds.x, trackBounds.y, trackBounds.width,
175 trackBounds.height);
176 if (trackHighlight == DECREASE_HIGHLIGHT) {
177 paintDecreaseHighlight(g);
178 }
179 else if (trackHighlight == INCREASE_HIGHLIGHT) {
180 paintIncreaseHighlight(g);
181 }
182 }
183 }
184
185 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
186 boolean v = (scrollbar.getOrientation() == JScrollBar.VERTICAL);
187
188 XPStyle xp = XPStyle.getXP();
189 if (xp != null) {
190 JScrollBar sb = (JScrollBar)c;
191 State state = State.NORMAL;
192 if (!sb.isEnabled()) {
193 state = State.DISABLED;
194 } else if (isDragging) {
195 state = State.PRESSED;
196 } else if (isThumbRollover()) {
197 state = State.HOT;
198 } else if (XPStyle.isVista()) {
199 if ((incrButton != null && incrButton.getModel().isRollover()) ||
200 (decrButton != null && decrButton.getModel().isRollover())) {
201 state = State.HOVER;
202 }
203 }
204 // Paint thumb
205 Part thumbPart = v ? Part.SBP_THUMBBTNVERT : Part.SBP_THUMBBTNHORZ;
206 xp.getSkin(sb, thumbPart).paintSkin(g, thumbBounds, state);
207 // Paint gripper
208 Part gripperPart = v ? Part.SBP_GRIPPERVERT : Part.SBP_GRIPPERHORZ;
209 Skin skin = xp.getSkin(sb, gripperPart);
210 Insets gripperInsets = xp.getMargin(c, thumbPart, null, Prop.CONTENTMARGINS);
211 if (gripperInsets == null ||
212 (v && (thumbBounds.height - gripperInsets.top -
213 gripperInsets.bottom >= skin.getHeight())) ||
214 (!v && (thumbBounds.width - gripperInsets.left -
215 gripperInsets.right >= skin.getWidth()))) {
216 skin.paintSkin(g,
217 thumbBounds.x + (thumbBounds.width - skin.getWidth()) / 2,
218 thumbBounds.y + (thumbBounds.height - skin.getHeight()) / 2,
219 skin.getWidth(), skin.getHeight(), state);
220 }
221 } else {
222 super.paintThumb(g, c, thumbBounds);
223 }
224 }
225
226
227 protected void paintDecreaseHighlight(Graphics g) {
228 if (highlightGrid == null) {
229 super.paintDecreaseHighlight(g);
230 }
231 else {
232 Insets insets = scrollbar.getInsets();
233 Rectangle thumbR = getThumbBounds();
234 int x, y, w, h;
235
236 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
237 x = insets.left;
238 y = decrButton.getY() + decrButton.getHeight();
239 w = scrollbar.getWidth() - (insets.left + insets.right);
240 h = thumbR.y - y;
241 }
242 else {
243 x = decrButton.getX() + decrButton.getHeight();
244 y = insets.top;
245 w = thumbR.x - x;
246 h = scrollbar.getHeight() - (insets.top + insets.bottom);
247 }
248 highlightGrid.paint(g, x, y, w, h);
249 }
250 }
251
252
253 protected void paintIncreaseHighlight(Graphics g) {
254 if (highlightGrid == null) {
255 super.paintDecreaseHighlight(g);
256 }
257 else {
258 Insets insets = scrollbar.getInsets();
259 Rectangle thumbR = getThumbBounds();
260 int x, y, w, h;
261
262 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
263 x = insets.left;
264 y = thumbR.y + thumbR.height;
265 w = scrollbar.getWidth() - (insets.left + insets.right);
266 h = incrButton.getY() - y;
267 }
268 else {
269 x = thumbR.x + thumbR.width;
270 y = insets.top;
271 w = incrButton.getX() - x;
272 h = scrollbar.getHeight() - (insets.top + insets.bottom);
273 }
274 highlightGrid.paint(g, x, y, w, h);
275 }
276 }
277
278
279 /**
280 * {@inheritDoc}
281 * @since 1.6
282 */
283 @Override
284 protected void setThumbRollover(boolean active) {
285 boolean old = isThumbRollover();
286 super.setThumbRollover(active);
287 // we need to repaint the entire scrollbar because state change for thumb
288 // causes state change for incr and decr buttons on Vista
289 if(XPStyle.isVista() && active != old) {
290 scrollbar.repaint();
291 }
292 }
293
294 /**
295 * WindowsArrowButton is used for the buttons to position the
296 * document up/down. It differs from BasicArrowButton in that the
297 * preferred size is always a square.
298 */
299 @SuppressWarnings("serial") // Superclass is not serializable across versions
300 private class WindowsArrowButton extends BasicArrowButton {
301
302 public WindowsArrowButton(int direction, Color background, Color shadow,
303 Color darkShadow, Color highlight) {
304 super(direction, background, shadow, darkShadow, highlight);
305 }
306
307 public WindowsArrowButton(int direction) {
308 super(direction);
309 }
310
311 public void paint(Graphics g) {
312 XPStyle xp = XPStyle.getXP();
313 if (xp != null) {
314 ButtonModel model = getModel();
315 Skin skin = xp.getSkin(this, Part.SBP_ARROWBTN);
316 State state = null;
317
318 boolean jointRollover = XPStyle.isVista() && (isThumbRollover() ||
319 (this == incrButton && decrButton.getModel().isRollover()) ||
320 (this == decrButton && incrButton.getModel().isRollover()));
321
322 // normal, rollover, pressed, disabled
323 if (model.isArmed() && model.isPressed()) {
324 switch (direction) {
325 case NORTH: state = State.UPPRESSED; break;
326 case SOUTH: state = State.DOWNPRESSED; break;
327 case WEST: state = State.LEFTPRESSED; break;
328 case EAST: state = State.RIGHTPRESSED; break;
329 }
330 } else if (!model.isEnabled()) {
331 switch (direction) {
332 case NORTH: state = State.UPDISABLED; break;
333 case SOUTH: state = State.DOWNDISABLED; break;
334 case WEST: state = State.LEFTDISABLED; break;
335 case EAST: state = State.RIGHTDISABLED; break;
336 }
337 } else if (model.isRollover() || model.isPressed()) {
338 switch (direction) {
339 case NORTH: state = State.UPHOT; break;
340 case SOUTH: state = State.DOWNHOT; break;
341 case WEST: state = State.LEFTHOT; break;
342 case EAST: state = State.RIGHTHOT; break;
343 }
344 } else if (jointRollover) {
345 switch (direction) {
346 case NORTH: state = State.UPHOVER; break;
347 case SOUTH: state = State.DOWNHOVER; break;
348 case WEST: state = State.LEFTHOVER; break;
349 case EAST: state = State.RIGHTHOVER; break;
350 }
351 } else {
352 switch (direction) {
353 case NORTH: state = State.UPNORMAL; break;
354 case SOUTH: state = State.DOWNNORMAL; break;
355 case WEST: state = State.LEFTNORMAL; break;
356 case EAST: state = State.RIGHTNORMAL; break;
357 }
358 }
359
360 skin.paintSkin(g, 0, 0, getWidth(), getHeight(), state);
361 } else {
362 super.paint(g);
363 }
364 }
365
366 public Dimension getPreferredSize() {
367 int size = 16;
368 if (scrollbar != null) {
369 switch (scrollbar.getOrientation()) {
370 case JScrollBar.VERTICAL:
371 size = scrollbar.getWidth();
372 break;
373 case JScrollBar.HORIZONTAL:
374 size = scrollbar.getHeight();
375 break;
376 }
377 size = Math.max(size, 5);
378 }
379 return new Dimension(size, size);
380 }
381 }
382
383
384 /**
385 * This should be pulled out into its own class if more classes need to
386 * use it.
387 * <p>
388 * Grid is used to draw the track for windows scrollbars. Grids
389 * are cached in a HashMap, with the key being the rgb components
390 * of the foreground/background colors. Further the Grid is held through
391 * a WeakRef so that it can be freed when no longer needed. As the
392 * Grid is rather expensive to draw, it is drawn in a BufferedImage.
393 */
394 private static class Grid {
395 private static final int BUFFER_SIZE = 64;
396 private static HashMap<String, WeakReference<Grid>> map;
397
398 private BufferedImage image;
399
400 static {
401 map = new HashMap<String, WeakReference<Grid>>();
402 }
403
404 public static Grid getGrid(Color fg, Color bg) {
405 String key = fg.getRGB() + " " + bg.getRGB();
406 WeakReference<Grid> ref = map.get(key);
407 Grid grid = (ref == null) ? null : ref.get();
408 if (grid == null) {
409 grid = new Grid(fg, bg);
410 map.put(key, new WeakReference<Grid>(grid));
411 }
412 return grid;
413 }
414
415 public Grid(Color fg, Color bg) {
416 int cmap[] = { fg.getRGB(), bg.getRGB() };
417 IndexColorModel icm = new IndexColorModel(8, 2, cmap, 0, false, -1,
418 DataBuffer.TYPE_BYTE);
419 image = new BufferedImage(BUFFER_SIZE, BUFFER_SIZE,
420 BufferedImage.TYPE_BYTE_INDEXED, icm);
421 Graphics g = image.getGraphics();
422 try {
423 g.setClip(0, 0, BUFFER_SIZE, BUFFER_SIZE);
424 paintGrid(g, fg, bg);
425 }
426 finally {
427 g.dispose();
428 }
429 }
430
431 /**
432 * Paints the grid into the specified Graphics at the specified
433 * location.
434 */
435 public void paint(Graphics g, int x, int y, int w, int h) {
436 Rectangle clipRect = g.getClipBounds();
437 int minX = Math.max(x, clipRect.x);
438 int minY = Math.max(y, clipRect.y);
439 int maxX = Math.min(clipRect.x + clipRect.width, x + w);
440 int maxY = Math.min(clipRect.y + clipRect.height, y + h);
441
442 if (maxX <= minX || maxY <= minY) {
443 return;
444 }
445 int xOffset = (minX - x) % 2;
446 for (int xCounter = minX; xCounter < maxX;
447 xCounter += BUFFER_SIZE) {
448 int yOffset = (minY - y) % 2;
449 int width = Math.min(BUFFER_SIZE - xOffset,
450 maxX - xCounter);
451
452 for (int yCounter = minY; yCounter < maxY;
453 yCounter += BUFFER_SIZE) {
454 int height = Math.min(BUFFER_SIZE - yOffset,
455 maxY - yCounter);
456
457 g.drawImage(image, xCounter, yCounter,
458 xCounter + width, yCounter + height,
459 xOffset, yOffset,
460 xOffset + width, yOffset + height, null);
461 if (yOffset != 0) {
462 yCounter -= yOffset;
463 yOffset = 0;
464 }
465 }
466 if (xOffset != 0) {
467 xCounter -= xOffset;
468 xOffset = 0;
469 }
470 }
471 }
472
473 /**
474 * Actually renders the grid into the Graphics {@code g}.
475 */
476 private void paintGrid(Graphics g, Color fg, Color bg) {
477 Rectangle clipRect = g.getClipBounds();
478 g.setColor(bg);
479 g.fillRect(clipRect.x, clipRect.y, clipRect.width,
480 clipRect.height);
481 g.setColor(fg);
482 g.translate(clipRect.x, clipRect.y);
483 int width = clipRect.width;
484 int height = clipRect.height;
485 int xCounter = clipRect.x % 2;
486 for (int end = width - height; xCounter < end; xCounter += 2) {
487 g.drawLine(xCounter, 0, xCounter + height, height);
488 }
489 for (int end = width; xCounter < end; xCounter += 2) {
490 g.drawLine(xCounter, 0, width, width - xCounter);
491 }
492
493 int yCounter = ((clipRect.x % 2) == 0) ? 2 : 1;
494 for (int end = height - width; yCounter < end; yCounter += 2) {
495 g.drawLine(0, yCounter, width, yCounter + width);
496 }
497 for (int end = height; yCounter < end; yCounter += 2) {
498 g.drawLine(0, yCounter, height - yCounter, height);
499 }
500 g.translate(-clipRect.x, -clipRect.y);
501 }
502 }
503 }
--- EOF ---