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 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 @SuppressWarnings("serial") // Superclass is not serializable across versions 277 private class WindowsArrowButton extends BasicArrowButton { 278 279 public WindowsArrowButton(int direction, Color background, Color shadow, 280 Color darkShadow, Color highlight) { 281 super(direction, background, shadow, darkShadow, highlight); 282 } 283 284 public WindowsArrowButton(int direction) { 285 super(direction); 286 } 287 288 public void paint(Graphics g) { 289 XPStyle xp = XPStyle.getXP(); 290 if (xp != null) { 291 ButtonModel model = getModel(); 292 Skin skin = xp.getSkin(this, Part.SBP_ARROWBTN); 293 State state = null; 294 295 boolean jointRollover = XPStyle.isVista() && (isThumbRollover() || 296 (this == incrButton && decrButton.getModel().isRollover()) || 297 (this == decrButton && incrButton.getModel().isRollover())); 298 299 // normal, rollover, pressed, disabled 300 if (model.isArmed() && model.isPressed()) { 301 switch (direction) { 302 case NORTH: state = State.UPPRESSED; break; 303 case SOUTH: state = State.DOWNPRESSED; break; 304 case WEST: state = State.LEFTPRESSED; break; 305 case EAST: state = State.RIGHTPRESSED; break; 306 } 307 } else if (!model.isEnabled()) { 308 switch (direction) { 309 case NORTH: state = State.UPDISABLED; break; 310 case SOUTH: state = State.DOWNDISABLED; break; 311 case WEST: state = State.LEFTDISABLED; break; 312 case EAST: state = State.RIGHTDISABLED; break; 313 } 314 } else if (model.isRollover() || model.isPressed()) { 315 switch (direction) { 316 case NORTH: state = State.UPHOT; break; 317 case SOUTH: state = State.DOWNHOT; break; 318 case WEST: state = State.LEFTHOT; break; 319 case EAST: state = State.RIGHTHOT; break; 320 } 321 } else if (jointRollover) { 322 switch (direction) { 323 case NORTH: state = State.UPHOVER; break; 324 case SOUTH: state = State.DOWNHOVER; break; 325 case WEST: state = State.LEFTHOVER; break; 326 case EAST: state = State.RIGHTHOVER; break; 327 } 328 } else { 329 switch (direction) { 330 case NORTH: state = State.UPNORMAL; break; 331 case SOUTH: state = State.DOWNNORMAL; break; 332 case WEST: state = State.LEFTNORMAL; break; 333 case EAST: state = State.RIGHTNORMAL; break; 334 } 335 } 336 337 skin.paintSkin(g, 0, 0, getWidth(), getHeight(), state); 338 } else { 339 super.paint(g); 340 } 341 } 342 343 public Dimension getPreferredSize() { 344 int size = 16; 345 if (scrollbar != null) { 346 switch (scrollbar.getOrientation()) { 347 case JScrollBar.VERTICAL: 348 size = scrollbar.getWidth(); 349 break; 350 case JScrollBar.HORIZONTAL: 351 size = scrollbar.getHeight(); 352 break; 353 } 354 size = Math.max(size, 5); 355 } 356 return new Dimension(size, size); 357 } 358 } 359 360 361 /** 362 * This should be pulled out into its own class if more classes need to 363 * use it. 364 * <p> 365 * Grid is used to draw the track for windows scrollbars. Grids 366 * are cached in a HashMap, with the key being the rgb components 367 * of the foreground/background colors. Further the Grid is held through 368 * a WeakRef so that it can be freed when no longer needed. As the 369 * Grid is rather expensive to draw, it is drawn in a BufferedImage. 370 */ 371 private static class Grid { 372 private static final int BUFFER_SIZE = 64; 373 private static HashMap<String, WeakReference<Grid>> map; 374 375 private BufferedImage image; 376 377 static { 378 map = new HashMap<String, WeakReference<Grid>>(); 379 } 380 381 public static Grid getGrid(Color fg, Color bg) { 382 String key = fg.getRGB() + " " + bg.getRGB(); 383 WeakReference<Grid> ref = map.get(key); 384 Grid grid = (ref == null) ? null : ref.get(); 385 if (grid == null) { 386 grid = new Grid(fg, bg); 387 map.put(key, new WeakReference<Grid>(grid)); 388 } 389 return grid; 390 } 391 392 public Grid(Color fg, Color bg) { 393 int cmap[] = { fg.getRGB(), bg.getRGB() }; 394 IndexColorModel icm = new IndexColorModel(8, 2, cmap, 0, false, -1, 395 DataBuffer.TYPE_BYTE); 396 image = new BufferedImage(BUFFER_SIZE, BUFFER_SIZE, 397 BufferedImage.TYPE_BYTE_INDEXED, icm); 398 Graphics g = image.getGraphics(); 399 try { 400 g.setClip(0, 0, BUFFER_SIZE, BUFFER_SIZE); 401 paintGrid(g, fg, bg); 402 } 403 finally { 404 g.dispose(); 405 } 406 } 407 408 /** 409 * Paints the grid into the specified Graphics at the specified 410 * location. 411 */ 412 public void paint(Graphics g, int x, int y, int w, int h) { 413 Rectangle clipRect = g.getClipBounds(); 414 int minX = Math.max(x, clipRect.x); 415 int minY = Math.max(y, clipRect.y); 416 int maxX = Math.min(clipRect.x + clipRect.width, x + w); 417 int maxY = Math.min(clipRect.y + clipRect.height, y + h); 418 419 if (maxX <= minX || maxY <= minY) { 420 return; 421 } 422 int xOffset = (minX - x) % 2; 423 for (int xCounter = minX; xCounter < maxX; 424 xCounter += BUFFER_SIZE) { 425 int yOffset = (minY - y) % 2; 426 int width = Math.min(BUFFER_SIZE - xOffset, 427 maxX - xCounter); 428 429 for (int yCounter = minY; yCounter < maxY; 430 yCounter += BUFFER_SIZE) { 431 int height = Math.min(BUFFER_SIZE - yOffset, 432 maxY - yCounter); 433 434 g.drawImage(image, xCounter, yCounter, 435 xCounter + width, yCounter + height, 436 xOffset, yOffset, 437 xOffset + width, yOffset + height, null); 438 if (yOffset != 0) { 439 yCounter -= yOffset; 440 yOffset = 0; 441 } 442 } 443 if (xOffset != 0) { 444 xCounter -= xOffset; 445 xOffset = 0; 446 } 447 } 448 } 449 450 /** 451 * Actually renders the grid into the Graphics <code>g</code>. 452 */ 453 private void paintGrid(Graphics g, Color fg, Color bg) { 454 Rectangle clipRect = g.getClipBounds(); 455 g.setColor(bg); 456 g.fillRect(clipRect.x, clipRect.y, clipRect.width, 457 clipRect.height); 458 g.setColor(fg); 459 g.translate(clipRect.x, clipRect.y); 460 int width = clipRect.width; 461 int height = clipRect.height; 462 int xCounter = clipRect.x % 2; 463 for (int end = width - height; xCounter < end; xCounter += 2) { 464 g.drawLine(xCounter, 0, xCounter + height, height); 465 } 466 for (int end = width; xCounter < end; xCounter += 2) { 467 g.drawLine(xCounter, 0, width, width - xCounter); 468 } 469 470 int yCounter = ((clipRect.x % 2) == 0) ? 2 : 1; 471 for (int end = height - width; yCounter < end; yCounter += 2) { 472 g.drawLine(0, yCounter, width, yCounter + width); 473 } 474 for (int end = height; yCounter < end; yCounter += 2) { 475 g.drawLine(0, yCounter, height - yCounter, height); 476 } 477 g.translate(-clipRect.x, -clipRect.y); 478 } 479 } 480 }