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 
getListHeaderBorder()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 
AquaTableHeaderBorder()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;
paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height)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 
getState(final JComponent jc)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     private static final RecyclableSingleton<Border> alternateBorder = new RecyclableSingleton<Border>() {
130         @Override
131         protected Border getInstance() {
132             return BorderFactory.createRaisedBevelBorder();
133         }
134     };
getAlternateBorder()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      */
getBorderInsets(final Component c)143     public Insets getBorderInsets(final Component c) {
144         // bad to create new one each time. For debugging only.
145         return editorBorderInsets;
146     }
147 
getBorderInsets(final Component c, final Insets insets)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      */
isBorderOpaque()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;
setSelected(final boolean inSelected)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;
setHorizontalShift(final int inShift)177     protected void setHorizontalShift(final int inShift) {
178         fHorizontalShift = inShift;
179     }
180 
181     private int sortOrder = SORT_NONE;
setSortOrder(final int inSortOrder)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 }
190