1 /*
   2  * Copyright (c) 2011, 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.apple.laf;
  27 
  28 import java.awt.*;
  29 
  30 import javax.swing.*;
  31 import javax.swing.border.*;
  32 import javax.swing.plaf.UIResource;
  33 
  34 import apple.laf.JRSUIState;
  35 import apple.laf.JRSUIConstants.*;
  36 
  37 import com.apple.laf.AquaUtils.RecyclableSingleton;
  38 
  39 @SuppressWarnings("serial") // Superclass is not serializable across versions
  40 public class AquaTableHeaderBorder extends AbstractBorder {
  41     protected static final int SORT_NONE = 0;
  42     protected static final int SORT_ASCENDING = 1;
  43     protected static final int SORT_DECENDING = -1;
  44 
  45     protected final Insets editorBorderInsets = new Insets(1, 3, 1, 3);
  46     protected final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIState.getInstance());
  47 
  48     protected static AquaTableHeaderBorder getListHeaderBorder() {
  49         // we don't want to share this, because the .setSelected() state
  50         // would persist to all other JTable instances
  51         return new AquaTableHeaderBorder();
  52     }
  53 
  54     protected AquaTableHeaderBorder() {
  55         painter.state.set(AlignmentHorizontal.LEFT);
  56         painter.state.set(AlignmentVertical.TOP);
  57     }
  58 
  59     /**
  60      * Paints the border for the specified component with the specified
  61      * position and size.
  62      * @param c the component for which this border is being painted
  63      * @param g the paint graphics
  64      * @param x the x position of the painted border
  65      * @param y the y position of the painted border
  66      * @param width the width of the painted border
  67      * @param height the height of the painted border
  68      */
  69     protected boolean doPaint = true;
  70     public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
  71         if (!doPaint) return;
  72         final JComponent jc = (JComponent)c;
  73 
  74         // if the developer wants to set their own color, we should
  75         // interpret this as "get out of the way", and don't draw aqua.
  76         final Color componentBackground = jc.getBackground();
  77         if (!(componentBackground instanceof UIResource)) {
  78             doPaint = false;
  79             jc.paint(g);
  80             getAlternateBorder().paintBorder(jc, g, x, y, width, height);
  81             doPaint = true;
  82             return;
  83         }
  84 
  85         final State state = getState(jc);
  86         painter.state.set(state);
  87         painter.state.set(jc.hasFocus() ? Focused.YES : Focused.NO);
  88         painter.state.set(height > 16 ? Widget.BUTTON_BEVEL : Widget.BUTTON_LIST_HEADER);
  89         painter.state.set(selected ? BooleanValue.YES : BooleanValue.NO);
  90 
  91         switch (sortOrder) {
  92             case SORT_ASCENDING:
  93                 painter.state.set(Direction.UP);
  94                 break;
  95             case SORT_DECENDING:
  96                 painter.state.set(Direction.DOWN);
  97                 break;
  98             default:
  99                 painter.state.set(Direction.NONE);
 100                 break;
 101         }
 102 
 103         final int newX = x;
 104         final int newY = y;
 105         final int newWidth = width;
 106         final int newHeight = height;
 107 
 108         painter.paint(g, c, newX - 1, newY - 1, newWidth + 1, newHeight);
 109 
 110         // Draw the header
 111         g.clipRect(newX, y, newWidth, height);
 112         g.translate(fHorizontalShift, -1);
 113         doPaint = false;
 114         jc.paint(g);
 115         doPaint = true;
 116     }
 117 
 118     protected State getState(final JComponent jc) {
 119         if (!jc.isEnabled()) return State.DISABLED;
 120 
 121         final JRootPane rootPane = jc.getRootPane();
 122         if (rootPane == null) return State.ACTIVE;
 123 
 124         if (!AquaFocusHandler.isActive(rootPane)) return State.INACTIVE;
 125 
 126         return State.ACTIVE;
 127     }
 128 
 129     static final RecyclableSingleton<Border> alternateBorder = new RecyclableSingleton<Border>() {
 130         @Override
 131         protected Border getInstance() {
 132             return BorderFactory.createRaisedBevelBorder();
 133         }
 134     };
 135     protected static Border getAlternateBorder() {
 136         return alternateBorder.get();
 137     }
 138 
 139     /**
 140      * Returns the insets of the border.
 141      * @param c the component for which this border insets value applies
 142      */
 143     public Insets getBorderInsets(final Component c) {
 144         // bad to create new one each time. For debugging only.
 145         return editorBorderInsets;
 146     }
 147 
 148     public Insets getBorderInsets(final Component c, final Insets insets) {
 149         insets.left = editorBorderInsets.left;
 150         insets.top = editorBorderInsets.top;
 151         insets.right = editorBorderInsets.right;
 152         insets.bottom = editorBorderInsets.bottom;
 153         return insets;
 154     }
 155 
 156     /**
 157      * Returns whether or not the border is opaque.  If the border
 158      * is opaque, it is responsible for filling in it's own
 159      * background when painting.
 160      */
 161     public boolean isBorderOpaque() {
 162         return false;
 163     }
 164 
 165     /**
 166      * Sets whether or not this instance of Border draws selected or not.  Used by AquaFileChooserUI
 167      */
 168     private boolean selected = false;
 169     protected void setSelected(final boolean inSelected) {
 170         selected = inSelected;
 171     }
 172 
 173     /**
 174      * Sets an amount to shift the position of the labels.  Used by AquaFileChooserUI
 175      */
 176     private int fHorizontalShift = 0;
 177     protected void setHorizontalShift(final int inShift) {
 178         fHorizontalShift = inShift;
 179     }
 180 
 181     private int sortOrder = SORT_NONE;
 182     protected void setSortOrder(final int inSortOrder) {
 183         if (inSortOrder < SORT_DECENDING || inSortOrder > SORT_ASCENDING) {
 184             throw new IllegalArgumentException("Invalid sort order constant: " + inSortOrder);
 185         }
 186 
 187         sortOrder = inSortOrder;
 188     }
 189 }