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 "public" due to a compiler bug. This class should be treated as a 95 * "protected" 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