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