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