1 /*
   2  * Copyright (c) 1997, 2013, 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 }