1 /* 2 * Copyright (c) 1997, 2010, 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.basic; 27 28 import javax.swing.*; 29 import javax.swing.border.*; 30 import javax.swing.plaf.*; 31 import javax.swing.text.JTextComponent; 32 33 import java.awt.Component; 34 import java.awt.Insets; 35 import java.awt.Dimension; 36 import java.awt.Rectangle; 37 import java.awt.Color; 38 import java.awt.Graphics; 39 40 /** 41 * Factory object that can vend Borders appropriate for the basic L & F. 42 * @author Georges Saab 43 * @author Amy Fowler 44 */ 45 46 public class BasicBorders { 47 48 public static Border getButtonBorder() { 49 UIDefaults table = UIManager.getLookAndFeelDefaults(); 50 Border buttonBorder = new BorderUIResource.CompoundBorderUIResource( 51 new BasicBorders.ButtonBorder( 52 table.getColor("Button.shadow"), 53 table.getColor("Button.darkShadow"), 54 table.getColor("Button.light"), 55 table.getColor("Button.highlight")), 56 new MarginBorder()); 57 return buttonBorder; 58 } 59 60 public static Border getRadioButtonBorder() { 61 UIDefaults table = UIManager.getLookAndFeelDefaults(); 62 Border radioButtonBorder = new BorderUIResource.CompoundBorderUIResource( 63 new BasicBorders.RadioButtonBorder( 64 table.getColor("RadioButton.shadow"), 65 table.getColor("RadioButton.darkShadow"), 66 table.getColor("RadioButton.light"), 67 table.getColor("RadioButton.highlight")), 68 new MarginBorder()); 69 return radioButtonBorder; 70 } 71 72 public static Border getToggleButtonBorder() { 73 UIDefaults table = UIManager.getLookAndFeelDefaults(); 74 Border toggleButtonBorder = new BorderUIResource.CompoundBorderUIResource( 75 new BasicBorders.ToggleButtonBorder( 76 table.getColor("ToggleButton.shadow"), 77 table.getColor("ToggleButton.darkShadow"), 78 table.getColor("ToggleButton.light"), 79 table.getColor("ToggleButton.highlight")), 80 new MarginBorder()); 81 return toggleButtonBorder; 82 } 83 84 public static Border getMenuBarBorder() { 85 UIDefaults table = UIManager.getLookAndFeelDefaults(); 86 Border menuBarBorder = new BasicBorders.MenuBarBorder( 87 table.getColor("MenuBar.shadow"), 88 table.getColor("MenuBar.highlight") 89 ); 90 return menuBarBorder; 91 } 92 93 public static Border getSplitPaneBorder() { 94 UIDefaults table = UIManager.getLookAndFeelDefaults(); 95 Border splitPaneBorder = new BasicBorders.SplitPaneBorder( 96 table.getColor("SplitPane.highlight"), 97 table.getColor("SplitPane.darkShadow")); 98 return splitPaneBorder; 99 } 100 101 /** 102 * Returns a border instance for a JSplitPane divider 103 * @since 1.3 104 */ 105 public static Border getSplitPaneDividerBorder() { 106 UIDefaults table = UIManager.getLookAndFeelDefaults(); 107 Border splitPaneBorder = new BasicBorders.SplitPaneDividerBorder( 108 table.getColor("SplitPane.highlight"), 109 table.getColor("SplitPane.darkShadow")); 110 return splitPaneBorder; 111 } 112 113 public static Border getTextFieldBorder() { 114 UIDefaults table = UIManager.getLookAndFeelDefaults(); 115 Border textFieldBorder = new BasicBorders.FieldBorder( 116 table.getColor("TextField.shadow"), 117 table.getColor("TextField.darkShadow"), 118 table.getColor("TextField.light"), 119 table.getColor("TextField.highlight")); 120 return textFieldBorder; 121 } 122 123 public static Border getProgressBarBorder() { 124 UIDefaults table = UIManager.getLookAndFeelDefaults(); 125 Border progressBarBorder = new BorderUIResource.LineBorderUIResource(Color.green, 2); 126 return progressBarBorder; 127 } 128 129 public static Border getInternalFrameBorder() { 130 UIDefaults table = UIManager.getLookAndFeelDefaults(); 131 Border internalFrameBorder = new BorderUIResource.CompoundBorderUIResource( 132 new BevelBorder(BevelBorder.RAISED, 133 table.getColor("InternalFrame.borderLight"), 134 table.getColor("InternalFrame.borderHighlight"), 135 table.getColor("InternalFrame.borderDarkShadow"), 136 table.getColor("InternalFrame.borderShadow")), 137 BorderFactory.createLineBorder( 138 table.getColor("InternalFrame.borderColor"), 1)); 139 140 return internalFrameBorder; 141 } 142 143 /** 144 * Special thin border for rollover toolbar buttons. 145 * @since 1.4 146 */ 147 public static class RolloverButtonBorder extends ButtonBorder { 148 149 public RolloverButtonBorder(Color shadow, Color darkShadow, 150 Color highlight, Color lightHighlight) { 151 super(shadow, darkShadow, highlight, lightHighlight); 152 } 153 154 public void paintBorder( Component c, Graphics g, int x, int y, int w, int h ) { 155 AbstractButton b = (AbstractButton) c; 156 ButtonModel model = b.getModel(); 157 158 Color shade = shadow; 159 Component p = b.getParent(); 160 if (p != null && p.getBackground().equals(shadow)) { 161 shade = darkShadow; 162 } 163 164 if ((model.isRollover() && !(model.isPressed() && !model.isArmed())) || 165 model.isSelected()) { 166 167 Color oldColor = g.getColor(); 168 g.translate(x, y); 169 170 if (model.isPressed() && model.isArmed() || model.isSelected()) { 171 // Draw the pressd button 172 g.setColor(shade); 173 g.drawRect(0, 0, w-1, h-1); 174 g.setColor(lightHighlight); 175 g.drawLine(w-1, 0, w-1, h-1); 176 g.drawLine(0, h-1, w-1, h-1); 177 } else { 178 // Draw a rollover button 179 g.setColor(lightHighlight); 180 g.drawRect(0, 0, w-1, h-1); 181 g.setColor(shade); 182 g.drawLine(w-1, 0, w-1, h-1); 183 g.drawLine(0, h-1, w-1, h-1); 184 } 185 g.translate(-x, -y); 186 g.setColor(oldColor); 187 } 188 } 189 } 190 191 192 /** 193 * A border which is like a Margin border but it will only honor the margin 194 * if the margin has been explicitly set by the developer. 195 * 196 * Note: This is identical to the package private class 197 * MetalBorders.RolloverMarginBorder and should probably be consolidated. 198 */ 199 static class RolloverMarginBorder extends EmptyBorder { 200 201 public RolloverMarginBorder() { 202 super(3,3,3,3); // hardcoded margin for JLF requirements. 203 } 204 205 public Insets getBorderInsets(Component c, Insets insets) { 206 Insets margin = null; 207 208 if (c instanceof AbstractButton) { 209 margin = ((AbstractButton)c).getMargin(); 210 } 211 if (margin == null || margin instanceof UIResource) { 212 // default margin so replace 213 insets.left = left; 214 insets.top = top; 215 insets.right = right; 216 insets.bottom = bottom; 217 } else { 218 // Margin which has been explicitly set by the user. 219 insets.left = margin.left; 220 insets.top = margin.top; 221 insets.right = margin.right; 222 insets.bottom = margin.bottom; 223 } 224 return insets; 225 } 226 } 227 228 public static class ButtonBorder extends AbstractBorder implements UIResource { 229 protected Color shadow; 230 protected Color darkShadow; 231 protected Color highlight; 232 protected Color lightHighlight; 233 234 public ButtonBorder(Color shadow, Color darkShadow, 235 Color highlight, Color lightHighlight) { 236 this.shadow = shadow; 237 this.darkShadow = darkShadow; 238 this.highlight = highlight; 239 this.lightHighlight = lightHighlight; 240 } 241 242 public void paintBorder(Component c, Graphics g, int x, int y, 243 int width, int height) { 244 boolean isPressed = false; 245 boolean isDefault = false; 246 247 if (c instanceof AbstractButton) { 248 AbstractButton b = (AbstractButton)c; 249 ButtonModel model = b.getModel(); 250 251 isPressed = model.isPressed() && model.isArmed(); 252 253 if (c instanceof JButton) { 254 isDefault = ((JButton)c).isDefaultButton(); 255 } 256 } 257 BasicGraphicsUtils.drawBezel(g, x, y, width, height, 258 isPressed, isDefault, shadow, 259 darkShadow, highlight, lightHighlight); 260 } 261 262 public Insets getBorderInsets(Component c, Insets insets) { 263 // leave room for default visual 264 insets.set(2, 3, 3, 3); 265 return insets; 266 } 267 268 } 269 270 public static class ToggleButtonBorder extends ButtonBorder { 271 272 public ToggleButtonBorder(Color shadow, Color darkShadow, 273 Color highlight, Color lightHighlight) { 274 super(shadow, darkShadow, highlight, lightHighlight); 275 } 276 277 public void paintBorder(Component c, Graphics g, int x, int y, 278 int width, int height) { 279 BasicGraphicsUtils.drawBezel(g, x, y, width, height, 280 false, false, 281 shadow, darkShadow, 282 highlight, lightHighlight); 283 } 284 285 public Insets getBorderInsets(Component c, Insets insets) { 286 insets.set(2, 2, 2, 2); 287 return insets; 288 } 289 } 290 291 public static class RadioButtonBorder extends ButtonBorder { 292 293 public RadioButtonBorder(Color shadow, Color darkShadow, 294 Color highlight, Color lightHighlight) { 295 super(shadow, darkShadow, highlight, lightHighlight); 296 } 297 298 299 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 300 301 if (c instanceof AbstractButton) { 302 AbstractButton b = (AbstractButton)c; 303 ButtonModel model = b.getModel(); 304 305 if (model.isArmed() && model.isPressed() || model.isSelected()) { 306 BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height, 307 shadow, darkShadow, 308 highlight, lightHighlight); 309 } else { 310 BasicGraphicsUtils.drawBezel(g, x, y, width, height, 311 false, b.isFocusPainted() && b.hasFocus(), 312 shadow, darkShadow, 313 highlight, lightHighlight); 314 } 315 } else { 316 BasicGraphicsUtils.drawBezel(g, x, y, width, height, false, false, 317 shadow, darkShadow, highlight, lightHighlight); 318 } 319 } 320 321 public Insets getBorderInsets(Component c, Insets insets) { 322 insets.set(2, 2, 2, 2); 323 return insets; 324 } 325 } 326 327 public static class MenuBarBorder extends AbstractBorder implements UIResource { 328 private Color shadow; 329 private Color highlight; 330 331 public MenuBarBorder(Color shadow, Color highlight) { 332 this.shadow = shadow; 333 this.highlight = highlight; 334 } 335 336 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 337 Color oldColor = g.getColor(); 338 g.translate(x, y); 339 g.setColor(shadow); 340 g.drawLine(0, height-2, width, height-2); 341 g.setColor(highlight); 342 g.drawLine(0, height-1, width, height-1); 343 g.translate(-x,-y); 344 g.setColor(oldColor); 345 } 346 347 public Insets getBorderInsets(Component c, Insets insets) { 348 insets.set(0, 0, 2, 0); 349 return insets; 350 } 351 } 352 353 public static class MarginBorder extends AbstractBorder implements UIResource { 354 public Insets getBorderInsets(Component c, Insets insets) { 355 Insets margin = null; 356 // 357 // Ideally we'd have an interface defined for classes which 358 // support margins (to avoid this hackery), but we've 359 // decided against it for simplicity 360 // 361 if (c instanceof AbstractButton) { 362 AbstractButton b = (AbstractButton)c; 363 margin = b.getMargin(); 364 } else if (c instanceof JToolBar) { 365 JToolBar t = (JToolBar)c; 366 margin = t.getMargin(); 367 } else if (c instanceof JTextComponent) { 368 JTextComponent t = (JTextComponent)c; 369 margin = t.getMargin(); 370 } 371 insets.top = margin != null? margin.top : 0; 372 insets.left = margin != null? margin.left : 0; 373 insets.bottom = margin != null? margin.bottom : 0; 374 insets.right = margin != null? margin.right : 0; 375 376 return insets; 377 } 378 } 379 380 public static class FieldBorder extends AbstractBorder implements UIResource { 381 protected Color shadow; 382 protected Color darkShadow; 383 protected Color highlight; 384 protected Color lightHighlight; 385 386 public FieldBorder(Color shadow, Color darkShadow, 387 Color highlight, Color lightHighlight) { 388 this.shadow = shadow; 389 this.highlight = highlight; 390 this.darkShadow = darkShadow; 391 this.lightHighlight = lightHighlight; 392 } 393 394 public void paintBorder(Component c, Graphics g, int x, int y, 395 int width, int height) { 396 BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height, 397 shadow, darkShadow, 398 highlight, lightHighlight); 399 } 400 401 public Insets getBorderInsets(Component c, Insets insets) { 402 Insets margin = null; 403 if (c instanceof JTextComponent) { 404 margin = ((JTextComponent)c).getMargin(); 405 } 406 insets.top = margin != null? 2+margin.top : 2; 407 insets.left = margin != null? 2+margin.left : 2; 408 insets.bottom = margin != null? 2+margin.bottom : 2; 409 insets.right = margin != null? 2+margin.right : 2; 410 411 return insets; 412 } 413 } 414 415 416 /** 417 * Draws the border around the divider in a splitpane 418 * (when BasicSplitPaneUI is used). To get the appropriate effect, this 419 * needs to be used with a SplitPaneBorder. 420 */ 421 static class SplitPaneDividerBorder implements Border, UIResource { 422 Color highlight; 423 Color shadow; 424 425 SplitPaneDividerBorder(Color highlight, Color shadow) { 426 this.highlight = highlight; 427 this.shadow = shadow; 428 } 429 430 public void paintBorder(Component c, Graphics g, int x, int y, 431 int width, int height) { 432 if (!(c instanceof BasicSplitPaneDivider)) { 433 return; 434 } 435 Component child; 436 Rectangle cBounds; 437 JSplitPane splitPane = ((BasicSplitPaneDivider)c). 438 getBasicSplitPaneUI().getSplitPane(); 439 Dimension size = c.getSize(); 440 441 child = splitPane.getLeftComponent(); 442 // This is needed for the space between the divider and end of 443 // splitpane. 444 g.setColor(c.getBackground()); 445 g.drawRect(x, y, width - 1, height - 1); 446 if(splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { 447 if(child != null) { 448 g.setColor(highlight); 449 g.drawLine(0, 0, 0, size.height); 450 } 451 child = splitPane.getRightComponent(); 452 if(child != null) { 453 g.setColor(shadow); 454 g.drawLine(size.width - 1, 0, size.width - 1, size.height); 455 } 456 } else { 457 if(child != null) { 458 g.setColor(highlight); 459 g.drawLine(0, 0, size.width, 0); 460 } 461 child = splitPane.getRightComponent(); 462 if(child != null) { 463 g.setColor(shadow); 464 g.drawLine(0, size.height - 1, size.width, 465 size.height - 1); 466 } 467 } 468 } 469 public Insets getBorderInsets(Component c) { 470 Insets insets = new Insets(0,0,0,0); 471 if (c instanceof BasicSplitPaneDivider) { 472 BasicSplitPaneUI bspui = ((BasicSplitPaneDivider)c). 473 getBasicSplitPaneUI(); 474 475 if (bspui != null) { 476 JSplitPane splitPane = bspui.getSplitPane(); 477 478 if (splitPane != null) { 479 if (splitPane.getOrientation() == 480 JSplitPane.HORIZONTAL_SPLIT) { 481 insets.top = insets.bottom = 0; 482 insets.left = insets.right = 1; 483 return insets; 484 } 485 // VERTICAL_SPLIT 486 insets.top = insets.bottom = 1; 487 insets.left = insets.right = 0; 488 return insets; 489 } 490 } 491 } 492 insets.top = insets.bottom = insets.left = insets.right = 1; 493 return insets; 494 } 495 public boolean isBorderOpaque() { return true; } 496 } 497 498 499 /** 500 * Draws the border around the splitpane. To work correctly you should 501 * also install a border on the divider (property SplitPaneDivider.border). 502 */ 503 public static class SplitPaneBorder implements Border, UIResource { 504 protected Color highlight; 505 protected Color shadow; 506 507 public SplitPaneBorder(Color highlight, Color shadow) { 508 this.highlight = highlight; 509 this.shadow = shadow; 510 } 511 512 public void paintBorder(Component c, Graphics g, int x, int y, 513 int width, int height) { 514 if (!(c instanceof JSplitPane)) { 515 return; 516 } 517 // The only tricky part with this border is that the divider is 518 // not positioned at the top (for horizontal) or left (for vert), 519 // so this border draws to where the divider is: 520 // ----------------- 521 // |xxxxxxx xxxxxxx| 522 // |x --- x| 523 // |x | | x| 524 // |x |D| x| 525 // |x | | x| 526 // |x --- x| 527 // |xxxxxxx xxxxxxx| 528 // ----------------- 529 // The above shows (rather excessively) what this looks like for 530 // a horizontal orientation. This border then draws the x's, with 531 // the SplitPaneDividerBorder drawing its own border. 532 533 Component child; 534 Rectangle cBounds; 535 536 JSplitPane splitPane = (JSplitPane)c; 537 538 child = splitPane.getLeftComponent(); 539 // This is needed for the space between the divider and end of 540 // splitpane. 541 g.setColor(c.getBackground()); 542 g.drawRect(x, y, width - 1, height - 1); 543 if(splitPane.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { 544 if(child != null) { 545 cBounds = child.getBounds(); 546 g.setColor(shadow); 547 g.drawLine(0, 0, cBounds.width + 1, 0); 548 g.drawLine(0, 1, 0, cBounds.height + 1); 549 550 g.setColor(highlight); 551 g.drawLine(0, cBounds.height + 1, cBounds.width + 1, 552 cBounds.height + 1); 553 } 554 child = splitPane.getRightComponent(); 555 if(child != null) { 556 cBounds = child.getBounds(); 557 558 int maxX = cBounds.x + cBounds.width; 559 int maxY = cBounds.y + cBounds.height; 560 561 g.setColor(shadow); 562 g.drawLine(cBounds.x - 1, 0, maxX, 0); 563 g.setColor(highlight); 564 g.drawLine(cBounds.x - 1, maxY, maxX, maxY); 565 g.drawLine(maxX, 0, maxX, maxY + 1); 566 } 567 } else { 568 if(child != null) { 569 cBounds = child.getBounds(); 570 g.setColor(shadow); 571 g.drawLine(0, 0, cBounds.width + 1, 0); 572 g.drawLine(0, 1, 0, cBounds.height); 573 g.setColor(highlight); 574 g.drawLine(1 + cBounds.width, 0, 1 + cBounds.width, 575 cBounds.height + 1); 576 g.drawLine(0, cBounds.height + 1, 0, cBounds.height + 1); 577 } 578 child = splitPane.getRightComponent(); 579 if(child != null) { 580 cBounds = child.getBounds(); 581 582 int maxX = cBounds.x + cBounds.width; 583 int maxY = cBounds.y + cBounds.height; 584 585 g.setColor(shadow); 586 g.drawLine(0, cBounds.y - 1, 0, maxY); 587 g.drawLine(maxX, cBounds.y - 1, maxX, cBounds.y - 1); 588 g.setColor(highlight); 589 g.drawLine(0, maxY, cBounds.width + 1, maxY); 590 g.drawLine(maxX, cBounds.y, maxX, maxY); 591 } 592 } 593 } 594 public Insets getBorderInsets(Component c) { 595 return new Insets(1, 1, 1, 1); 596 } 597 public boolean isBorderOpaque() { return true; } 598 } 599 600 }