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 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     static class AquaHomeEndAction extends AbstractAction {
71         private boolean fHomeAction = false;
72 
AquaHomeEndAction(final boolean isHomeAction)73         protected AquaHomeEndAction(final boolean isHomeAction) {
74             fHomeAction = isHomeAction;
75         }
76 
77         /**
78          * For a Home action, scrolls to the top. Otherwise, scroll to the end.
79          */
actionPerformed(final ActionEvent e)80         public void actionPerformed(final ActionEvent e) {
81             final JList list = (JList)e.getSource();
82 
83             if (fHomeAction) {
84                 list.ensureIndexIsVisible(0);
85             } else {
86                 final int size = list.getModel().getSize();
87                 list.ensureIndexIsVisible(size - 1);
88             }
89         }
90     }
91 
92     /**
93      * This inner class is marked "public" due to a compiler bug. This class should be treated as a
94      * "protected" inner class. Instantiate it only within subclasses of BasicListUI.
95      */
96     class FocusHandler extends BasicListUI.FocusHandler {
focusGained(final FocusEvent e)97         public void focusGained(final FocusEvent e) {
98             super.focusGained(e);
99             AquaBorder.repaintBorder(getComponent());
100         }
101 
focusLost(final FocusEvent e)102         public void focusLost(final FocusEvent e) {
103             super.focusLost(e);
104             AquaBorder.repaintBorder(getComponent());
105         }
106     }
107 
createPropertyChangeListener()108     protected PropertyChangeListener createPropertyChangeListener() {
109         return new AquaPropertyChangeHandler();
110     }
111 
112     class AquaPropertyChangeHandler extends PropertyChangeHandler {
propertyChange(final PropertyChangeEvent e)113         public void propertyChange(final PropertyChangeEvent e) {
114             final String prop = e.getPropertyName();
115             if (AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) {
116                 AquaBorder.repaintBorder(getComponent());
117                 AquaFocusHandler.swapSelectionColors("List", getComponent(), e.getNewValue());
118             } else {
119                 super.propertyChange(e);
120             }
121         }
122     }
123 
124     // TODO: Using default handler for now, need to handle cmd-key
125 
126     // Replace the mouse event with one that returns the cmd-key state when asked
127     // for the control-key state, which super assumes is what everyone does to discontiguously extend selections
128     class MouseInputHandler extends BasicListUI.MouseInputHandler {
129         /*public void mousePressed(final MouseEvent e) {
130             super.mousePressed(new SelectionMouseEvent(e));
131         }
132         public void mouseDragged(final MouseEvent e) {
133             super.mouseDragged(new SelectionMouseEvent(e));
134         }*/
135     }
136 
getComponent()137     JList getComponent() {
138         return list;
139     }
140 
141     // this is used for blinking combobox popup selections when they are selected
repaintCell(final Object value, final int selectedIndex, final boolean selected)142     protected void repaintCell(final Object value, final int selectedIndex, final boolean selected) {
143         final Rectangle rowBounds = getCellBounds(list, selectedIndex, selectedIndex);
144         if (rowBounds == null) return;
145 
146         final ListCellRenderer renderer = list.getCellRenderer();
147         if (renderer == null) return;
148 
149         final Component rendererComponent = renderer.getListCellRendererComponent(list, value, selectedIndex, selected, true);
150         if (rendererComponent == null) return;
151 
152         final AquaComboBoxRenderer aquaRenderer = renderer instanceof AquaComboBoxRenderer ? (AquaComboBoxRenderer)renderer : null;
153         if (aquaRenderer != null) aquaRenderer.setDrawCheckedItem(false);
154         rendererPane.paintComponent(list.getGraphics().create(), rendererComponent, list, rowBounds.x, rowBounds.y, rowBounds.width, rowBounds.height, true);
155         if (aquaRenderer != null) aquaRenderer.setDrawCheckedItem(true);
156     }
157 
158     /*
159     Insert note on JIDESoft naughtiness
160     */
getSourceListBackgroundPainter()161     public static Border getSourceListBackgroundPainter() {
162         final AquaBorder border = new ComponentPainter();
163         border.painter.state.set(Widget.GRADIENT);
164         border.painter.state.set(Variant.GRADIENT_SIDE_BAR);
165         return border;
166     }
167 
getSourceListSelectionBackgroundPainter()168     public static Border getSourceListSelectionBackgroundPainter() {
169         final AquaBorder border = new ComponentPainter();
170         border.painter.state.set(Widget.GRADIENT);
171         border.painter.state.set(Variant.GRADIENT_SIDE_BAR_SELECTION);
172         return border;
173     }
174 
getSourceListFocusedSelectionBackgroundPainter()175     public static Border getSourceListFocusedSelectionBackgroundPainter() {
176         final AquaBorder border = new ComponentPainter();
177         border.painter.state.set(Widget.GRADIENT);
178         border.painter.state.set(Variant.GRADIENT_SIDE_BAR_FOCUSED_SELECTION);
179         return border;
180     }
181 
getListEvenBackgroundPainter()182     public static Border getListEvenBackgroundPainter() {
183         final AquaBorder border = new ComponentPainter();
184         border.painter.state.set(Widget.GRADIENT);
185         border.painter.state.set(Variant.GRADIENT_LIST_BACKGROUND_EVEN);
186         return border;
187     }
188 
getListOddBackgroundPainter()189     public static Border getListOddBackgroundPainter() {
190         final AquaBorder border = new ComponentPainter();
191         border.painter.state.set(Widget.GRADIENT);
192         border.painter.state.set(Variant.GRADIENT_LIST_BACKGROUND_ODD);
193         return border;
194     }
195 
196     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)197         public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int w, final int h) {
198             final JComponent jc = c instanceof JComponent ? (JComponent)c : null;
199             if (jc != null && !AquaFocusHandler.isActive(jc)) {
200                 painter.state.set(State.INACTIVE);
201             } else {
202                 painter.state.set(State.ACTIVE);
203             }
204             super.paintBorder(c, g, x, y, w, h);
205         }
206     }
207 }
208