1 /* 2 * Copyright (c) 2002, 2015, 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 javax.swing.plaf.synth; 27 28 import java.awt.*; 29 import java.beans.*; 30 import javax.swing.*; 31 import javax.swing.plaf.*; 32 import javax.swing.plaf.basic.*; 33 34 35 /** 36 * Provides the Synth L&F UI delegate for 37 * {@link javax.swing.JScrollBar}. 38 * 39 * @author Scott Violet 40 * @since 1.7 41 */ 42 public class SynthScrollBarUI extends BasicScrollBarUI 43 implements PropertyChangeListener, SynthUI { 44 45 private SynthStyle style; 46 private SynthStyle thumbStyle; 47 private SynthStyle trackStyle; 48 49 private boolean validMinimumThumbSize; 50 51 /** 52 * Returns a UI. 53 * @return a UI 54 * @param c a component 55 */ 56 public static ComponentUI createUI(JComponent c) { 57 return new SynthScrollBarUI(); 58 } 59 60 /** 61 * {@inheritDoc} 62 */ 63 @Override 64 protected void installDefaults() { 65 super.installDefaults(); 66 trackHighlight = NO_HIGHLIGHT; 67 if (scrollbar.getLayout() == null || 68 (scrollbar.getLayout() instanceof UIResource)) { 69 scrollbar.setLayout(this); 70 } 71 configureScrollBarColors(); 72 updateStyle(scrollbar); 73 } 74 75 /** 76 * {@inheritDoc} 77 */ 78 @Override 79 protected void configureScrollBarColors() { 80 } 81 82 private void updateStyle(JScrollBar c) { 83 SynthStyle oldStyle = style; 84 SynthContext context = getContext(c, ENABLED); 85 style = SynthLookAndFeel.updateStyle(context, this); 86 if (style != oldStyle) { 87 scrollBarWidth = style.getInt(context,"ScrollBar.thumbHeight", 14); 88 minimumThumbSize = (Dimension)style.get(context, 89 "ScrollBar.minimumThumbSize"); 90 if (minimumThumbSize == null) { 91 minimumThumbSize = new Dimension(); 92 validMinimumThumbSize = false; 93 } 94 else { 95 validMinimumThumbSize = true; 96 } 97 maximumThumbSize = (Dimension)style.get(context, 98 "ScrollBar.maximumThumbSize"); 99 if (maximumThumbSize == null) { 100 maximumThumbSize = new Dimension(4096, 4097); 101 } 102 103 incrGap = style.getInt(context, "ScrollBar.incrementButtonGap", 0); 104 decrGap = style.getInt(context, "ScrollBar.decrementButtonGap", 0); 105 106 // handle scaling for sizeVarients for special case components. The 107 // key "JComponent.sizeVariant" scales for large/small/mini 108 // components are based on Apples LAF 109 String scaleKey = (String)scrollbar.getClientProperty( 110 "JComponent.sizeVariant"); 111 if (scaleKey != null){ 112 if ("large".equals(scaleKey)){ 113 scrollBarWidth *= 1.15; 114 incrGap *= 1.15; 115 decrGap *= 1.15; 116 } else if ("small".equals(scaleKey)){ 117 scrollBarWidth *= 0.857; 118 incrGap *= 0.857; 119 decrGap *= 0.857; 120 } else if ("mini".equals(scaleKey)){ 121 scrollBarWidth *= 0.714; 122 incrGap *= 0.714; 123 decrGap *= 0.714; 124 } 125 } 126 127 if (oldStyle != null) { 128 uninstallKeyboardActions(); 129 installKeyboardActions(); 130 } 131 } 132 context.dispose(); 133 134 context = getContext(c, Region.SCROLL_BAR_TRACK, ENABLED); 135 trackStyle = SynthLookAndFeel.updateStyle(context, this); 136 context.dispose(); 137 138 context = getContext(c, Region.SCROLL_BAR_THUMB, ENABLED); 139 thumbStyle = SynthLookAndFeel.updateStyle(context, this); 140 context.dispose(); 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override 147 protected void installListeners() { 148 super.installListeners(); 149 scrollbar.addPropertyChangeListener(this); 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 protected void uninstallListeners() { 157 super.uninstallListeners(); 158 scrollbar.removePropertyChangeListener(this); 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 protected void uninstallDefaults(){ 166 SynthContext context = getContext(scrollbar, ENABLED); 167 style.uninstallDefaults(context); 168 context.dispose(); 169 style = null; 170 171 context = getContext(scrollbar, Region.SCROLL_BAR_TRACK, ENABLED); 172 trackStyle.uninstallDefaults(context); 173 context.dispose(); 174 trackStyle = null; 175 176 context = getContext(scrollbar, Region.SCROLL_BAR_THUMB, ENABLED); 177 thumbStyle.uninstallDefaults(context); 178 context.dispose(); 179 thumbStyle = null; 180 181 super.uninstallDefaults(); 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 public SynthContext getContext(JComponent c) { 189 return getContext(c, SynthLookAndFeel.getComponentState(c)); 190 } 191 192 private SynthContext getContext(JComponent c, int state) { 193 return SynthContext.getContext(c, style, state); 194 } 195 196 private SynthContext getContext(JComponent c, Region region) { 197 return getContext(c, region, getComponentState(c, region)); 198 } 199 200 private SynthContext getContext(JComponent c, Region region, int state) { 201 SynthStyle style = trackStyle; 202 203 if (region == Region.SCROLL_BAR_THUMB) { 204 style = thumbStyle; 205 } 206 return SynthContext.getContext(c, region, style, state); 207 } 208 209 private int getComponentState(JComponent c, Region region) { 210 if (region == Region.SCROLL_BAR_THUMB && isThumbRollover() && 211 c.isEnabled()) { 212 return MOUSE_OVER; 213 } 214 return SynthLookAndFeel.getComponentState(c); 215 } 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override 221 public boolean getSupportsAbsolutePositioning() { 222 SynthContext context = getContext(scrollbar); 223 boolean value = style.getBoolean(context, 224 "ScrollBar.allowsAbsolutePositioning", false); 225 context.dispose(); 226 return value; 227 } 228 229 /** 230 * Notifies this UI delegate to repaint the specified component. 231 * This method paints the component background, then calls 232 * the {@link #paint(SynthContext,Graphics)} method. 233 * 234 * <p>In general, this method does not need to be overridden by subclasses. 235 * All Look and Feel rendering code should reside in the {@code paint} method. 236 * 237 * @param g the {@code Graphics} object used for painting 238 * @param c the component being painted 239 * @see #paint(SynthContext,Graphics) 240 */ 241 @Override 242 public void update(Graphics g, JComponent c) { 243 SynthContext context = getContext(c); 244 245 SynthLookAndFeel.update(context, g); 246 context.getPainter().paintScrollBarBackground(context, 247 g, 0, 0, c.getWidth(), c.getHeight(), 248 scrollbar.getOrientation()); 249 paint(context, g); 250 context.dispose(); 251 } 252 253 /** 254 * Paints the specified component according to the Look and Feel. 255 * <p>This method is not used by Synth Look and Feel. 256 * Painting is handled by the {@link #paint(SynthContext,Graphics)} method. 257 * 258 * @param g the {@code Graphics} object used for painting 259 * @param c the component being painted 260 * @see #paint(SynthContext,Graphics) 261 */ 262 @Override 263 public void paint(Graphics g, JComponent c) { 264 SynthContext context = getContext(c); 265 266 paint(context, g); 267 context.dispose(); 268 } 269 270 /** 271 * Paints the specified component. 272 * 273 * @param context context for the component being painted 274 * @param g the {@code Graphics} object used for painting 275 * @see #update(Graphics,JComponent) 276 */ 277 protected void paint(SynthContext context, Graphics g) { 278 SynthContext subcontext = getContext(scrollbar, 279 Region.SCROLL_BAR_TRACK); 280 paintTrack(subcontext, g, getTrackBounds()); 281 subcontext.dispose(); 282 283 subcontext = getContext(scrollbar, Region.SCROLL_BAR_THUMB); 284 paintThumb(subcontext, g, getThumbBounds()); 285 subcontext.dispose(); 286 } 287 288 /** 289 * {@inheritDoc} 290 */ 291 @Override 292 public void paintBorder(SynthContext context, Graphics g, int x, 293 int y, int w, int h) { 294 context.getPainter().paintScrollBarBorder(context, g, x, y, w, h, 295 scrollbar.getOrientation()); 296 } 297 298 /** 299 * Paints the scrollbar track. 300 * 301 * @param context context for the component being painted 302 * @param g {@code Graphics} object used for painting 303 * @param trackBounds bounding box for the track 304 */ 305 protected void paintTrack(SynthContext context, Graphics g, 306 Rectangle trackBounds) { 307 SynthLookAndFeel.updateSubregion(context, g, trackBounds); 308 context.getPainter().paintScrollBarTrackBackground(context, g, trackBounds.x, 309 trackBounds.y, trackBounds.width, trackBounds.height, 310 scrollbar.getOrientation()); 311 context.getPainter().paintScrollBarTrackBorder(context, g, trackBounds.x, 312 trackBounds.y, trackBounds.width, trackBounds.height, 313 scrollbar.getOrientation()); 314 } 315 316 /** 317 * Paints the scrollbar thumb. 318 * 319 * @param context context for the component being painted 320 * @param g {@code Graphics} object used for painting 321 * @param thumbBounds bounding box for the thumb 322 */ 323 protected void paintThumb(SynthContext context, Graphics g, 324 Rectangle thumbBounds) { 325 SynthLookAndFeel.updateSubregion(context, g, thumbBounds); 326 int orientation = scrollbar.getOrientation(); 327 context.getPainter().paintScrollBarThumbBackground(context, g, thumbBounds.x, 328 thumbBounds.y, thumbBounds.width, thumbBounds.height, 329 orientation); 330 context.getPainter().paintScrollBarThumbBorder(context, g, thumbBounds.x, 331 thumbBounds.y, thumbBounds.width, thumbBounds.height, 332 orientation); 333 } 334 335 /** 336 * A vertical scrollbar's preferred width is the maximum of 337 * preferred widths of the (non {@code null}) 338 * increment/decrement buttons, 339 * and the minimum width of the thumb. The preferred height is the 340 * sum of the preferred heights of the same parts. The basis for 341 * the preferred size of a horizontal scrollbar is similar. 342 * <p> 343 * The {@code preferredSize} is only computed once, subsequent 344 * calls to this method just return a cached size. 345 * 346 * @param c the {@code JScrollBar} that's delegating this method to us 347 * @return the preferred size of a Basic JScrollBar 348 * @see #getMaximumSize 349 * @see #getMinimumSize 350 */ 351 @Override 352 public Dimension getPreferredSize(JComponent c) { 353 Insets insets = c.getInsets(); 354 return (scrollbar.getOrientation() == JScrollBar.VERTICAL) 355 ? new Dimension(scrollBarWidth + insets.left + insets.right, 48) 356 : new Dimension(48, scrollBarWidth + insets.top + insets.bottom); 357 } 358 359 /** 360 * {@inheritDoc} 361 */ 362 @Override 363 protected Dimension getMinimumThumbSize() { 364 if (!validMinimumThumbSize) { 365 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) { 366 minimumThumbSize.width = scrollBarWidth; 367 minimumThumbSize.height = 7; 368 } else { 369 minimumThumbSize.width = 7; 370 minimumThumbSize.height = scrollBarWidth; 371 } 372 } 373 return minimumThumbSize; 374 } 375 376 /** 377 * {@inheritDoc} 378 */ 379 @Override 380 protected JButton createDecreaseButton(int orientation) { 381 @SuppressWarnings("serial") // anonymous class 382 SynthArrowButton synthArrowButton = new SynthArrowButton(orientation) { 383 @Override 384 public boolean contains(int x, int y) { 385 if (decrGap < 0) { //there is an overlap between the track and button 386 int width = getWidth(); 387 int height = getHeight(); 388 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) { 389 //adjust the height by decrGap 390 //Note: decrGap is negative! 391 height += decrGap; 392 } else { 393 //adjust the width by decrGap 394 //Note: decrGap is negative! 395 width += decrGap; 396 } 397 return (x >= 0) && (x < width) && (y >= 0) && (y < height); 398 } 399 return super.contains(x, y); 400 } 401 }; 402 synthArrowButton.setName("ScrollBar.button"); 403 return synthArrowButton; 404 } 405 406 /** 407 * {@inheritDoc} 408 */ 409 @Override 410 protected JButton createIncreaseButton(int orientation) { 411 @SuppressWarnings("serial") // anonymous class 412 SynthArrowButton synthArrowButton = new SynthArrowButton(orientation) { 413 @Override 414 public boolean contains(int x, int y) { 415 if (incrGap < 0) { //there is an overlap between the track and button 416 int width = getWidth(); 417 int height = getHeight(); 418 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) { 419 //adjust the height and y by incrGap 420 //Note: incrGap is negative! 421 height += incrGap; 422 y += incrGap; 423 } else { 424 //adjust the width and x by incrGap 425 //Note: incrGap is negative! 426 width += incrGap; 427 x += incrGap; 428 } 429 return (x >= 0) && (x < width) && (y >= 0) && (y < height); 430 } 431 return super.contains(x, y); 432 } 433 }; 434 synthArrowButton.setName("ScrollBar.button"); 435 return synthArrowButton; 436 } 437 438 /** 439 * {@inheritDoc} 440 */ 441 @Override 442 protected void setThumbRollover(boolean active) { 443 if (isThumbRollover() != active) { 444 scrollbar.repaint(getThumbBounds()); 445 super.setThumbRollover(active); 446 } 447 } 448 449 private void updateButtonDirections() { 450 int orient = scrollbar.getOrientation(); 451 if (scrollbar.getComponentOrientation().isLeftToRight()) { 452 ((SynthArrowButton)incrButton).setDirection( 453 orient == HORIZONTAL? EAST : SOUTH); 454 ((SynthArrowButton)decrButton).setDirection( 455 orient == HORIZONTAL? WEST : NORTH); 456 } 457 else { 458 ((SynthArrowButton)incrButton).setDirection( 459 orient == HORIZONTAL? WEST : SOUTH); 460 ((SynthArrowButton)decrButton).setDirection( 461 orient == HORIZONTAL ? EAST : NORTH); 462 } 463 } 464 465 // 466 // PropertyChangeListener 467 // 468 public void propertyChange(PropertyChangeEvent e) { 469 String propertyName = e.getPropertyName(); 470 471 if (SynthLookAndFeel.shouldUpdateStyle(e)) { 472 updateStyle((JScrollBar)e.getSource()); 473 } 474 475 if ("orientation" == propertyName) { 476 updateButtonDirections(); 477 } 478 else if ("componentOrientation" == propertyName) { 479 updateButtonDirections(); 480 } 481 } 482 } --- EOF ---