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