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
26package com.apple.laf;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.beans.*;
31
32import javax.swing.*;
33import javax.swing.plaf.UIResource;
34
35/**
36 * This class is used by the text components, AquaEditorPaneUI, AquaTextAreaUI, AquaTextFieldUI and AquaTextPaneUI to control painting of the
37 * component's border.  NOTE: It is assumed that this handler is added to components that extend JComponent.
38 */
39public class AquaFocusHandler implements FocusListener, PropertyChangeListener {
40    // Flag to help focusGained() determine whether the origin focus loss was due to a temporary focus loss or not.
41    private boolean wasTemporary = false;
42
43    // Flag to track when a border needs a repaint due to a window becoming activate/inactive.
44    private boolean repaintBorder = false;
45
46    protected static final String FRAME_ACTIVE_PROPERTY = "Frame.active";
47
48    public void focusGained(final FocusEvent ev) {
49        // If we gained focus and it wasn't due to a previous temporary focus loss
50        // or the frame became active again, then repaint the border on the component.
51        if (!wasTemporary || repaintBorder) {
52            AquaBorder.repaintBorder((JComponent)ev.getSource());
53            repaintBorder = false;
54        }
55        wasTemporary = false;
56    }
57
58    public void focusLost(final FocusEvent ev) {
59        wasTemporary = ev.isTemporary();
60
61        // If we lost focus due to a permanent focus loss then repaint the border on the component.
62        if (!wasTemporary) {
63            AquaBorder.repaintBorder((JComponent)ev.getSource());
64        }
65    }
66
67    public void propertyChange(final PropertyChangeEvent ev) {
68        if (!FRAME_ACTIVE_PROPERTY.equals(ev.getPropertyName())) return;
69
70        if (Boolean.TRUE.equals(ev.getNewValue())) {
71            // The FRAME_ACTIVE_PROPERTY change event is sent before a component gains focus.
72            // We set a flag to help the focusGained() determine when they should be repainting
73            // the components focus.
74            repaintBorder = true;
75        } else if (wasTemporary) {
76            // The FRAME_ACTIVE_PROPERTY change event is sent after a component loses focus.
77            // We use the wasTemporary flag to determine if we need to repaint the border.
78            AquaBorder.repaintBorder((JComponent)ev.getSource());
79        }
80    }
81
82    protected static boolean isActive(final JComponent c) {
83        if (c == null) return true;
84        final Object activeObj = c.getClientProperty(AquaFocusHandler.FRAME_ACTIVE_PROPERTY);
85        if (Boolean.FALSE.equals(activeObj)) return false;
86        return true;
87    }
88
89    static final PropertyChangeListener REPAINT_LISTENER = new PropertyChangeListener() {
90        public void propertyChange(final PropertyChangeEvent evt) {
91            final Object source = evt.getSource();
92            if (source instanceof JComponent) {
93                ((JComponent)source).repaint();
94            }
95        }
96    };
97
98    protected static void install(final JComponent c) {
99        c.addPropertyChangeListener(FRAME_ACTIVE_PROPERTY, REPAINT_LISTENER);
100    }
101
102    protected static void uninstall(final JComponent c) {
103        c.removePropertyChangeListener(FRAME_ACTIVE_PROPERTY, REPAINT_LISTENER);
104    }
105
106    static void swapSelectionColors(final String prefix, final JTree c, final Object value) {
107        // <rdar://problem/8166173> JTree: selection color does not dim when window becomes inactive
108        // TODO inject our colors into the DefaultTreeCellRenderer
109    }
110
111    static void swapSelectionColors(final String prefix, final JTable c, final Object value) {
112        if (!isComponentValid(c)) return;
113
114        final Color bg = c.getSelectionBackground();
115        final Color fg = c.getSelectionForeground();
116        if (!(bg instanceof UIResource) || !(fg instanceof UIResource)) return;
117
118        if (Boolean.FALSE.equals(value)) {
119            setSelectionColors(c, "Table.selectionInactiveForeground", "Table.selectionInactiveBackground");
120            return;
121        }
122
123        if (Boolean.TRUE.equals(value)) {
124            setSelectionColors(c, "Table.selectionForeground", "Table.selectionBackground");
125            return;
126        }
127    }
128
129    static void setSelectionColors(final JTable c, final String fgName, final String bgName) {
130        c.setSelectionForeground(UIManager.getColor(fgName));
131        c.setSelectionBackground(UIManager.getColor(bgName));
132    }
133
134    static void swapSelectionColors(final String prefix, final JList<?> c, final Object value) {
135        if (!isComponentValid(c)) return;
136
137        final Color bg = c.getSelectionBackground();
138        final Color fg = c.getSelectionForeground();
139        if (!(bg instanceof UIResource) || !(fg instanceof UIResource)) return;
140
141        if (Boolean.FALSE.equals(value)) {
142            setSelectionColors(c, "List.selectionInactiveForeground", "List.selectionInactiveBackground");
143            return;
144        }
145
146        if (Boolean.TRUE.equals(value)) {
147            setSelectionColors(c, "List.selectionForeground", "List.selectionBackground");
148            return;
149        }
150    }
151
152    static void setSelectionColors(final JList<?> c, final String fgName, final String bgName) {
153        c.setSelectionForeground(UIManager.getColor(fgName));
154        c.setSelectionBackground(UIManager.getColor(bgName));
155    }
156
157    static boolean isComponentValid(final JComponent c) {
158        if (c == null) return false;
159        final Window window = SwingUtilities.getWindowAncestor(c);
160        if (window == null) return false;
161        return true;
162    }
163}
164