1 /* 2 * Copyright (c) 1997, 2006, 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 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<String, WeakReference<Grid>> map; 373 374 private BufferedImage image; 375 376 static { 377 map = new HashMap<String, WeakReference<Grid>>(); 378 } 379 380 public static Grid getGrid(Color fg, Color bg) { 381 String key = fg.getRGB() + " " + bg.getRGB(); 382 WeakReference<Grid> ref = map.get(key); 383 Grid grid = (ref == null) ? null : ref.get(); 384 if (grid == null) { 385 grid = new Grid(fg, bg); 386 map.put(key, new WeakReference<Grid>(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 }