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
26package com.apple.laf;
27
28import java.awt.*;
29import java.awt.event.*;
30import java.beans.*;
31
32import javax.swing.*;
33import javax.swing.border.Border;
34import javax.swing.event.MouseInputListener;
35import javax.swing.plaf.ComponentUI;
36import javax.swing.plaf.basic.BasicListUI;
37
38import apple.laf.JRSUIConstants.*;
39
40/**
41 * A Mac L&F implementation of JList
42 *
43 * All this does is look for a ThemeBorder and invalidate it when the focus changes
44 */
45public class AquaListUI extends BasicListUI {
46    public static ComponentUI createUI(final JComponent c) {
47        return new AquaListUI();
48    }
49
50    /**
51     * Creates the focus listener to repaint the focus ring
52     */
53    protected FocusListener createFocusListener() {
54        return new AquaListUI.FocusHandler();
55    }
56
57    /**
58     * Creates a delegate that implements MouseInputListener.
59     */
60    protected MouseInputListener createMouseInputListener() {
61        return new AquaListUI.MouseInputHandler();
62    }
63
64    protected void installKeyboardActions() {
65        super.installKeyboardActions();
66        list.getActionMap().put("aquaHome", new AquaHomeEndAction(true));
67        list.getActionMap().put("aquaEnd", new AquaHomeEndAction(false));
68    }
69
70    @SuppressWarnings("serial") // Superclass is not serializable across versions
71    static class AquaHomeEndAction extends AbstractAction {
72        private boolean fHomeAction = false;
73
74        protected AquaHomeEndAction(final boolean isHomeAction) {
75            fHomeAction = isHomeAction;
76        }
77
78        /**
79         * For a Home action, scrolls to the top. Otherwise, scroll to the end.
80         */
81        public void actionPerformed(final ActionEvent e) {
82            final JList<?> list = (JList<?>)e.getSource();
83
84            if (fHomeAction) {
85                list.ensureIndexIsVisible(0);
86            } else {
87                final int size = list.getModel().getSize();
88                list.ensureIndexIsVisible(size - 1);
89            }
90        }
91    }
92
93    /**
94     * This inner class is marked &quot;public&quot; due to a compiler bug. This class should be treated as a
95     * &quot;protected&quot; inner class. Instantiate it only within subclasses of BasicListUI.
96     */
97    class FocusHandler extends BasicListUI.FocusHandler {
98        public void focusGained(final FocusEvent e) {
99            super.focusGained(e);
100            AquaBorder.repaintBorder(getComponent());
101        }
102
103        public void focusLost(final FocusEvent e) {
104            super.focusLost(e);
105            AquaBorder.repaintBorder(getComponent());
106        }
107    }
108
109    protected PropertyChangeListener createPropertyChangeListener() {
110        return new AquaPropertyChangeHandler();
111    }
112
113    class AquaPropertyChangeHandler extends PropertyChangeHandler {
114        public void propertyChange(final PropertyChangeEvent e) {
115            final String prop = e.getPropertyName();
116            if (AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) {
117                AquaBorder.repaintBorder(getComponent());
118                AquaFocusHandler.swapSelectionColors("List", getComponent(), e.getNewValue());
119            } else {
120                super.propertyChange(e);
121            }
122        }
123    }
124
125    // TODO: Using default handler for now, need to handle cmd-key
126
127    // Replace the mouse event with one that returns the cmd-key state when asked
128    // for the control-key state, which super assumes is what everyone does to discontiguously extend selections
129    class MouseInputHandler extends BasicListUI.MouseInputHandler {
130        /*public void mousePressed(final MouseEvent e) {
131            super.mousePressed(new SelectionMouseEvent(e));
132        }
133        public void mouseDragged(final MouseEvent e) {
134            super.mouseDragged(new SelectionMouseEvent(e));
135        }*/
136    }
137
138    JList<Object> getComponent() {
139        return list;
140    }
141
142    // this is used for blinking combobox popup selections when they are selected
143    protected void repaintCell(final Object value, final int selectedIndex, final boolean selected) {
144        final Rectangle rowBounds = getCellBounds(list, selectedIndex, selectedIndex);
145        if (rowBounds == null) return;
146
147        final ListCellRenderer<Object> renderer = list.getCellRenderer();
148        if (renderer == null) return;
149
150        final Component rendererComponent = renderer.getListCellRendererComponent(list, value, selectedIndex, selected, true);
151        if (rendererComponent == null) return;
152
153        final AquaComboBoxRenderer aquaRenderer = renderer instanceof AquaComboBoxRenderer ? (AquaComboBoxRenderer)renderer : null;
154        if (aquaRenderer != null) aquaRenderer.setDrawCheckedItem(false);
155        rendererPane.paintComponent(list.getGraphics().create(), rendererComponent, list, rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height, true);
156        if (aquaRenderer != null) aquaRenderer.setDrawCheckedItem(true);
157    }
158
159    /*
160    Insert note on JIDESoft naughtiness
161    */
162    public static Border getSourceListBackgroundPainter() {
163        final AquaBorder border = new ComponentPainter();
164        border.painter.state.set(Widget.GRADIENT);
165        border.painter.state.set(Variant.GRADIENT_SIDE_BAR);
166        return border;
167    }
168
169    public static Border getSourceListSelectionBackgroundPainter() {
170        final AquaBorder border = new ComponentPainter();
171        border.painter.state.set(Widget.GRADIENT);
172        border.painter.state.set(Variant.GRADIENT_SIDE_BAR_SELECTION);
173        return border;
174    }
175
176    public static Border getSourceListFocusedSelectionBackgroundPainter() {
177        final AquaBorder border = new ComponentPainter();
178        border.painter.state.set(Widget.GRADIENT);
179        border.painter.state.set(Variant.GRADIENT_SIDE_BAR_FOCUSED_SELECTION);
180        return border;
181    }
182
183    public static Border getListEvenBackgroundPainter() {
184        final AquaBorder border = new ComponentPainter();
185        border.painter.state.set(Widget.GRADIENT);
186        border.painter.state.set(Variant.GRADIENT_LIST_BACKGROUND_EVEN);
187        return border;
188    }
189
190    public static Border getListOddBackgroundPainter() {
191        final AquaBorder border = new ComponentPainter();
192        border.painter.state.set(Widget.GRADIENT);
193        border.painter.state.set(Variant.GRADIENT_LIST_BACKGROUND_ODD);
194        return border;
195    }
196
197    static class ComponentPainter extends AquaBorder.Default {
198        public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) {
199            final JComponent jc = c instanceof JComponent ? (JComponent)c : null;
200            if (jc != null && !AquaFocusHandler.isActive(jc)) {
201                painter.state.set(State.INACTIVE);
202            } else {
203                painter.state.set(State.ACTIVE);
204            }
205            super.paintBorder(c, g, x, y, w, h);
206        }
207    }
208}
209