1 /* 2 * Copyright (c) 2011, 2013, 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 /** 36 * Lightweight implementation of {@link ListPeer}. Delegates most of the work to 37 * the {@link JList}, which is placed inside {@link JScrollPane}. 38 */ 39 final class LWListPeer extends LWComponentPeer<List, LWListPeer.ScrollableJList> 40 implements ListPeer { 41 42 /** 43 * The default number of visible rows. 44 */ 45 private static final int DEFAULT_VISIBLE_ROWS = 4; // From java.awt.List, 46 47 /** 48 * This text is used for cell bounds calculation. 49 */ 50 private static final String TEXT = "0123456789abcde"; 51 52 LWListPeer(final List target, final PlatformComponent platformComponent) { 53 super(target, platformComponent); 54 if (!getTarget().isBackgroundSet()) { 55 getTarget().setBackground(SystemColor.text); 56 } 57 } 58 59 @Override 60 ScrollableJList createDelegate() { 61 return new ScrollableJList(); 62 } 63 64 @Override 65 void initializeImpl() { 66 super.initializeImpl(); 67 setMultipleMode(getTarget().isMultipleMode()); 68 final int[] selectedIndices = getTarget().getSelectedIndexes(); 69 synchronized (getDelegateLock()) { 70 getDelegate().setSkipStateChangedEvent(true); 71 getDelegate().getView().setSelectedIndices(selectedIndices); 72 getDelegate().setSkipStateChangedEvent(false); 73 } 74 } 75 76 @Override 77 public boolean isFocusable() { 78 return true; 79 } 80 81 @Override 82 Component getDelegateFocusOwner() { 83 return getDelegate().getView(); 84 } 85 86 @Override 87 public int[] getSelectedIndexes() { 88 synchronized (getDelegateLock()) { 89 return getDelegate().getView().getSelectedIndices(); 90 } 91 } 92 93 @Override 94 public void add(final String item, final int index) { 95 synchronized (getDelegateLock()) { 96 getDelegate().getModel().add(index, item); 97 revalidate(); 98 } 99 } 100 101 @Override 102 public void delItems(final int start, final int end) { 103 synchronized (getDelegateLock()) { 104 getDelegate().getModel().removeRange(start, end); 105 revalidate(); 106 } 107 } 108 109 @Override 110 public void removeAll() { 111 synchronized (getDelegateLock()) { 112 getDelegate().getModel().removeAllElements(); 113 revalidate(); 114 } 115 } 116 117 @Override 118 public void select(final int index) { 119 synchronized (getDelegateLock()) { 120 getDelegate().setSkipStateChangedEvent(true); 121 getDelegate().getView().setSelectedIndex(index); 122 getDelegate().setSkipStateChangedEvent(false); 123 } 124 } 125 126 @Override 127 public void deselect(final int index) { 128 synchronized (getDelegateLock()) { 129 getDelegate().getView().getSelectionModel(). 130 removeSelectionInterval(index, index); 131 } 132 } 133 134 @Override 135 public void makeVisible(final int index) { 136 synchronized (getDelegateLock()) { 137 getDelegate().getView().ensureIndexIsVisible(index); 138 } 139 } 140 141 @Override 142 public void setMultipleMode(final boolean m) { 143 synchronized (getDelegateLock()) { 144 getDelegate().getView().setSelectionMode(m ? 145 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION 146 : ListSelectionModel.SINGLE_SELECTION); 147 } 148 } 149 150 @Override 151 public Dimension getPreferredSize() { 152 return getMinimumSize(); 153 } 154 155 @Override 156 public Dimension getMinimumSize() { 157 return getMinimumSize(DEFAULT_VISIBLE_ROWS); 158 } 159 160 @Override 161 public Dimension getPreferredSize(final int rows) { 162 return getMinimumSize(rows); 163 } 164 165 @Override 166 public Dimension getMinimumSize(final int rows) { 167 synchronized (getDelegateLock()) { 168 final Dimension size = getCellSize(); 169 size.height *= rows; 170 // Always take vertical scrollbar into account. 171 final JScrollBar vbar = getDelegate().getVerticalScrollBar(); 172 size.width += vbar != null ? vbar.getMinimumSize().width : 0; 173 // JScrollPane and JList insets 174 final Insets pi = getDelegate().getInsets(); 175 final Insets vi = getDelegate().getView().getInsets(); 176 size.width += pi.left + pi.right + vi.left + vi.right; 177 size.height += pi.top + pi.bottom + vi.top + vi.bottom; 178 return size; 179 } 180 } 181 182 private Dimension getCellSize() { 183 final JList<String> jList = getDelegate().getView(); 184 final ListCellRenderer<? super String> cr = jList.getCellRenderer(); 185 final Component cell = cr.getListCellRendererComponent(jList, TEXT, 0, 186 false, false); 187 return cell.getPreferredSize(); 188 } 189 190 private void revalidate() { 191 synchronized (getDelegateLock()) { 192 getDelegate().getView().invalidate(); 193 getDelegate().validate(); 194 } 195 } 196 197 @SuppressWarnings("serial")// Safe: outer class is non-serializable. 198 final class ScrollableJList extends JScrollPane implements ListSelectionListener { 199 200 private boolean skipStateChangedEvent; 201 202 private final DefaultListModel<String> model = 203 new DefaultListModel<String>() { 204 @Override 205 public void add(final int index, final String element) { 206 if (index == -1) { 207 addElement(element); 208 } else { 209 super.add(index, element); 210 } 211 } 212 }; 213 214 private int[] oldSelectedIndices = new int[0]; 215 216 ScrollableJList() { 217 getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); 218 final JList<String> list = new JListDelegate(); 219 list.addListSelectionListener(this); 220 221 getViewport().setView(list); 222 223 // Pull the items from the target. 224 final String[] items = getTarget().getItems(); 225 for (int i = 0; i < items.length; i++) { 226 model.add(i, items[i]); 227 } 228 } 229 230 public boolean isSkipStateChangedEvent() { 231 return skipStateChangedEvent; 232 } 233 234 public void setSkipStateChangedEvent(boolean skipStateChangedEvent) { 235 this.skipStateChangedEvent = skipStateChangedEvent; 236 } 237 238 @Override 239 @SuppressWarnings("unchecked") 240 public void valueChanged(final ListSelectionEvent e) { 241 if (!e.getValueIsAdjusting() && !isSkipStateChangedEvent()) { 242 final JList<?> source = (JList<?>) e.getSource(); 243 for(int i = 0 ; i < source.getModel().getSize(); i++) { 244 245 final boolean wasSelected = Arrays.binarySearch(oldSelectedIndices, i) >= 0; 246 final boolean isSelected = source.isSelectedIndex(i); 247 248 if (wasSelected == isSelected) { 249 continue; 250 } 251 252 final int state = !wasSelected && isSelected ? ItemEvent.SELECTED: ItemEvent.DESELECTED; 253 254 LWListPeer.this.postEvent(new ItemEvent(getTarget(), ItemEvent.ITEM_STATE_CHANGED, 255 i, state)); 256 } 257 oldSelectedIndices = source.getSelectedIndices(); 258 } 259 } 260 261 @SuppressWarnings("unchecked") 262 public JList<String> getView() { 263 return (JList<String>) getViewport().getView(); 264 } 265 266 public DefaultListModel<String> getModel() { 267 return model; 268 } 269 270 @Override 271 public void setEnabled(final boolean enabled) { 272 getView().setEnabled(enabled); 273 super.setEnabled(enabled); 274 } 275 276 @Override 277 public void setOpaque(final boolean isOpaque) { 278 super.setOpaque(isOpaque); 279 if (getView() != null) { 280 getView().setOpaque(isOpaque); 281 } 282 } 283 284 @Override 285 public void setFont(Font font) { 286 super.setFont(font); 287 if (getView() != null) { 288 getView().setFont(font); 289 LWListPeer.this.revalidate(); 290 } 291 } 292 293 private final class JListDelegate extends JList<String> { 294 295 JListDelegate() { 296 super(model); 297 } 298 299 @Override 300 public boolean hasFocus() { 301 return getTarget().hasFocus(); 302 } 303 304 @Override 305 protected void processMouseEvent(final MouseEvent e) { 306 super.processMouseEvent(e); 307 if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 2) { 308 final int index = locationToIndex(e.getPoint()); 309 if (0 <= index && index < getModel().getSize()) { 310 LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, 311 getModel().getElementAt(index), e.getWhen(), e.getModifiers())); 312 } 313 } 314 } 315 316 @Override 317 protected void processKeyEvent(final KeyEvent e) { 318 super.processKeyEvent(e); 319 if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ENTER) { 320 final String selectedValue = getSelectedValue(); 321 if(selectedValue != null){ 322 LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, 323 selectedValue, e.getWhen(), e.getModifiers())); 324 } 325 } 326 } 327 328 //Needed for Autoscroller. 329 @Override 330 public Point getLocationOnScreen() { 331 return LWListPeer.this.getLocationOnScreen(); 332 } 333 } 334 } 335 }