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 import java.awt.event.*;
30 import java.beans.*;
31 
32 import javax.swing.*;
33 import javax.swing.border.Border;
34 import javax.swing.event.MouseInputListener;
35 import javax.swing.plaf.ComponentUI;
36 import javax.swing.plaf.basic.BasicListUI;
37 
38 import 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  */
45 public class AquaListUI extends BasicListUI {
createUI(final JComponent c)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      */
createFocusListener()53     protected FocusListener createFocusListener() {
54         return new AquaListUI.FocusHandler();
55     }
56 
57     /**
58      * Creates a delegate that implements MouseInputListener.
59      */
createMouseInputListener()60     protected MouseInputListener createMouseInputListener() {
61         return new AquaListUI.MouseInputHandler();
62     }
63 
installKeyboardActions()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 
AquaHomeEndAction(final boolean isHomeAction)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          */
actionPerformed(final ActionEvent e)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 {
focusGained(final FocusEvent e)98         public void focusGained(final FocusEvent e) {
99             super.focusGained(e);
100             AquaBorder.repaintBorder(getComponent());
101         }
102 
focusLost(final FocusEvent e)103         public void focusLost(final FocusEvent e) {
104             super.focusLost(e);
105             AquaBorder.repaintBorder(getComponent());
106         }
107     }
108 
createPropertyChangeListener()109     protected PropertyChangeListener createPropertyChangeListener() {
110         return new AquaPropertyChangeHandler();
111     }
112 
113     class AquaPropertyChangeHandler extends PropertyChangeHandler {
propertyChange(final PropertyChangeEvent e)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 
getComponent()138     JList<Object> getComponent() {
139         return list;
140     }
141 
142     // this is used for blinking combobox popup selections when they are selected
repaintCell(final Object value, final int selectedIndex, final boolean 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     */
getSourceListBackgroundPainter()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 
getSourceListSelectionBackgroundPainter()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 
getSourceListFocusedSelectionBackgroundPainter()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 
getListEvenBackgroundPainter()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 
getListOddBackgroundPainter()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 {
paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h)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