1 /* 2 * Copyright (c) 2011, 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 sun.lwawt; 27 28 import javax.swing.*; 29 import javax.swing.event.*; 30 import java.awt.*; 31 import java.awt.event.*; 32 import java.awt.peer.ListPeer; 33 import java.util.Arrays; 34 35 final class LWListPeer 36 extends LWComponentPeer<List, LWListPeer.ScrollableJList> 37 implements ListPeer { 38 39 LWListPeer(final List target, final PlatformComponent platformComponent) { 40 super(target, platformComponent); 41 if (!getTarget().isBackgroundSet()) { 42 getTarget().setBackground(SystemColor.text); 43 } 44 } 45 46 @Override 47 protected ScrollableJList createDelegate() { 48 return new ScrollableJList(); 49 } 50 51 @Override 52 void initializeImpl() { 53 super.initializeImpl(); 54 setMultipleMode(getTarget().isMultipleMode()); 55 final int[] selectedIndices = getTarget().getSelectedIndexes(); 56 synchronized (getDelegateLock()) { 57 getDelegate().setSkipStateChangedEvent(true); 58 getDelegate().getView().setSelectedIndices(selectedIndices); 59 getDelegate().setSkipStateChangedEvent(false); 60 } 61 } 62 63 @Override 64 public boolean isFocusable() { 65 return true; 66 } 67 68 @Override 69 protected Component getDelegateFocusOwner() { 70 return getDelegate().getView(); 71 } 72 73 @Override 74 public int[] getSelectedIndexes() { 75 synchronized (getDelegateLock()) { 76 return getDelegate().getView().getSelectedIndices(); 77 } 78 } 79 80 @Override 81 public void add(final String item, final int index) { 82 synchronized (getDelegateLock()) { 83 getDelegate().getModel().add(index, item); 84 revalidate(); 85 } 86 } 87 88 @Override 89 public void delItems(final int start, final int end) { 90 synchronized (getDelegateLock()) { 91 getDelegate().getModel().removeRange(start, end); 92 revalidate(); 93 } 94 } 95 96 @Override 97 public void removeAll() { 98 synchronized (getDelegateLock()) { 99 getDelegate().getModel().removeAllElements(); 100 revalidate(); 101 } 102 } 103 104 @Override 105 public void select(final int index) { 106 synchronized (getDelegateLock()) { 107 getDelegate().setSkipStateChangedEvent(true); 108 getDelegate().getView().setSelectedIndex(index); 109 getDelegate().setSkipStateChangedEvent(false); 110 } 111 } 112 113 @Override 114 public void deselect(final int index) { 115 synchronized (getDelegateLock()) { 116 getDelegate().getView().getSelectionModel(). 117 removeSelectionInterval(index, index); 118 } 119 } 120 121 @Override 122 public void makeVisible(final int index) { 123 synchronized (getDelegateLock()) { 124 getDelegate().getView().ensureIndexIsVisible(index); 125 } 126 } 127 128 @Override 129 public void setMultipleMode(final boolean m) { 130 synchronized (getDelegateLock()) { 131 getDelegate().getView().setSelectionMode(m ? 132 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION 133 : ListSelectionModel.SINGLE_SELECTION); 134 } 135 } 136 137 @Override 138 public Dimension getPreferredSize(final int rows) { 139 return getMinimumSize(rows); 140 } 141 142 @Override 143 public Dimension getMinimumSize(final int rows) { 144 synchronized (getDelegateLock()) { 145 final int margin = 2; 146 final int space = 1; 147 148 // TODO: count ScrollPane's scrolling elements if any. 149 final FontMetrics fm = getFontMetrics(getFont()); 150 final int itemHeight = (fm.getHeight() - fm.getLeading()) + (2 * space); 151 152 return new Dimension(20 + (fm == null ? 10 * 15 : fm.stringWidth("0123456789abcde")), 153 (fm == null ? 10 : itemHeight) * rows + (2 * margin)); 154 } 155 } 156 157 private void revalidate() { 158 synchronized (getDelegateLock()) { 159 getDelegate().getView().invalidate(); 160 getDelegate().validate(); 161 } 162 } 163 164 final class ScrollableJList extends JScrollPane implements ListSelectionListener { 165 166 private boolean skipStateChangedEvent; 167 168 private DefaultListModel<Object> model = 169 new DefaultListModel<Object>() { 170 @Override 171 public void add(final int index, final Object element) { 172 if (index == -1) { 173 addElement(element); 174 } else { 175 super.add(index, element); 176 } 177 } 178 }; 179 180 private int[] oldSelectedIndices = new int[0]; 181 182 ScrollableJList() { 183 getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); 184 final JList<Object> list = new JListDelegate(); 185 list.addListSelectionListener(this); 186 187 getViewport().setView(list); 188 189 // Pull the items from the target. 190 final String[] items = getTarget().getItems(); 191 for (int i = 0; i < items.length; i++) { 192 model.add(i, items[i]); 193 } 194 } 195 196 public boolean isSkipStateChangedEvent() { 197 return skipStateChangedEvent; 198 } 199 200 public void setSkipStateChangedEvent(boolean skipStateChangedEvent) { 201 this.skipStateChangedEvent = skipStateChangedEvent; 202 } 203 204 @Override 205 public void valueChanged(final ListSelectionEvent e) { 206 if (!e.getValueIsAdjusting() && !isSkipStateChangedEvent()) { 207 final JList source = (JList) e.getSource(); 208 for(int i = 0 ; i < source.getModel().getSize(); i++) { 209 210 final boolean wasSelected = Arrays.binarySearch(oldSelectedIndices, i) >= 0; 211 final boolean isSelected = source.isSelectedIndex(i); 212 213 if (wasSelected == isSelected) { 214 continue; 215 } 216 217 final int state = !wasSelected && isSelected ? ItemEvent.SELECTED: ItemEvent.DESELECTED; 218 219 LWListPeer.this.postEvent(new ItemEvent(getTarget(), ItemEvent.ITEM_STATE_CHANGED, 220 i, state)); 221 } 222 oldSelectedIndices = source.getSelectedIndices(); 223 } 224 } 225 226 public JList getView() { 227 return (JList) getViewport().getView(); 228 } 229 230 public DefaultListModel<Object> getModel() { 231 return model; 232 } 233 234 @Override 235 public void setEnabled(final boolean enabled) { 236 getView().setEnabled(enabled); 237 super.setEnabled(enabled); 238 } 239 240 @Override 241 public void setOpaque(final boolean isOpaque) { 242 super.setOpaque(isOpaque); 243 if (getView() != null) { 244 getView().setOpaque(isOpaque); 245 } 246 } 247 248 private final class JListDelegate extends JList<Object> { 249 250 JListDelegate() { 251 super(ScrollableJList.this.model); 252 } 253 254 @Override 255 public boolean hasFocus() { 256 return getTarget().hasFocus(); 257 } 258 259 @Override 260 protected void processMouseEvent(final MouseEvent e) { 261 super.processMouseEvent(e); 262 if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 2) { 263 final int index = locationToIndex(e.getPoint()); 264 if (0 <= index && index < getModel().getSize()) { 265 LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, 266 getModel().getElementAt(index).toString(), e.getWhen(), e.getModifiers())); 267 } 268 } 269 } 270 271 @Override 272 protected void processKeyEvent(final KeyEvent e) { 273 super.processKeyEvent(e); 274 if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ENTER) { 275 final Object selectedValue = getSelectedValue(); 276 if(selectedValue != null){ 277 LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, 278 selectedValue.toString(), e.getWhen(), e.getModifiers())); 279 } 280 } 281 } 282 283 //Needed for Autoscroller. 284 @Override 285 public Point getLocationOnScreen() { 286 return LWListPeer.this.getLocationOnScreen(); 287 } 288 } 289 } 290 }