001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Anton Avtamonov
020: * @version $Revision$
021: */package javax.swing.plaf.basic;
022:
023: import java.awt.Color;
024: import java.awt.Component;
025: import java.awt.ComponentOrientation;
026: import java.awt.Dimension;
027: import java.awt.Font;
028: import java.awt.Insets;
029: import java.awt.Point;
030: import java.awt.Rectangle;
031: import java.awt.event.ActionEvent;
032: import java.awt.event.ActionListener;
033: import java.awt.event.ItemEvent;
034: import java.awt.event.ItemListener;
035: import java.awt.event.KeyAdapter;
036: import java.awt.event.KeyEvent;
037: import java.awt.event.KeyListener;
038: import java.awt.event.MouseAdapter;
039: import java.awt.event.MouseEvent;
040: import java.awt.event.MouseListener;
041: import java.awt.event.MouseMotionAdapter;
042: import java.awt.event.MouseMotionListener;
043: import java.beans.PropertyChangeEvent;
044: import java.beans.PropertyChangeListener;
045:
046: import javax.accessibility.AccessibleContext;
047: import javax.swing.BorderFactory;
048: import javax.swing.ComboBoxModel;
049: import javax.swing.JComboBox;
050: import javax.swing.JList;
051: import javax.swing.JPopupMenu;
052: import javax.swing.JScrollPane;
053: import javax.swing.ListCellRenderer;
054: import javax.swing.ListSelectionModel;
055: import javax.swing.SwingUtilities;
056: import javax.swing.Timer;
057: import javax.swing.UIManager;
058: import javax.swing.event.ListDataEvent;
059: import javax.swing.event.ListDataListener;
060: import javax.swing.event.ListSelectionEvent;
061: import javax.swing.event.ListSelectionListener;
062:
063: import org.apache.harmony.x.swing.StringConstants;
064: import org.apache.harmony.x.swing.Utilities;
065:
066: import org.apache.harmony.x.swing.internal.nls.Messages;
067:
068: public class BasicComboPopup extends JPopupMenu implements ComboPopup {
069: /**
070: * This class is replaced with ActionMap/InputMap functionality and does nothing
071: */
072: public class InvocationKeyHandler extends KeyAdapter {
073: public void keyReleased(final KeyEvent e) {
074: }
075: }
076:
077: /**
078: * Is obsolete and is not used. Replaced with item listener
079: *
080: */
081: public class ListDataHandler implements ListDataListener {
082: public void intervalAdded(final ListDataEvent e) {
083: }
084:
085: public void intervalRemoved(final ListDataEvent e) {
086: }
087:
088: public void contentsChanged(final ListDataEvent e) {
089: }
090: }
091:
092: /**
093: * Is obsolete and is not used.
094: *
095: */
096: protected class ListSelectionHandler implements
097: ListSelectionListener {
098: public void valueChanged(final ListSelectionEvent e) {
099: }
100: }
101:
102: protected class InvocationMouseHandler extends MouseAdapter {
103: private boolean mouseInside(final MouseEvent event) {
104: return event.getComponent().contains(event.getPoint());
105: }
106:
107: public void mousePressed(final MouseEvent e) {
108: if ((e.getButton() != MouseEvent.BUTTON1)
109: || !mouseInside(e)) {
110: return;
111: }
112: if (!comboBox.isEnabled()) {
113: return;
114: }
115: delegateFocus(e);
116: togglePopup();
117: }
118:
119: public void mouseReleased(final MouseEvent e) {
120: if (e.getButton() != MouseEvent.BUTTON1) {
121: return;
122: }
123:
124: stopAutoScrolling();
125: if (!isShowing()) {
126: return;
127: }
128:
129: if (hasEntered) {
130: comboBox.setSelectedIndex(list.getSelectedIndex());
131: }
132:
133: if (hasEntered || leftComboBox) {
134: comboBox.setPopupVisible(false);
135: hasEntered = false;
136: leftComboBox = false;
137: }
138: }
139: }
140:
141: protected class InvocationMouseMotionHandler extends
142: MouseMotionAdapter {
143: public void mouseDragged(final MouseEvent e) {
144: if (!comboBox.isEnabled()) {
145: return;
146: }
147: Rectangle listVisibleRect = list.getVisibleRect();
148: MouseEvent convertedEvent = convertMouseEvent(e);
149: hasEntered = listVisibleRect.contains(convertedEvent
150: .getPoint());
151: if (!leftComboBox) {
152: leftComboBox = e.getPoint().x < 0
153: || e.getPoint().x > comboBox.getWidth()
154: || e.getPoint().y < 0
155: || e.getPoint().y > comboBox.getHeight();
156: }
157:
158: if (hasEntered) {
159: updateListBoxSelectionForEvent(convertedEvent, false);
160: stopAutoScrolling();
161: } else if (leftComboBox) {
162: if (convertedEvent.getPoint().y < listVisibleRect.y) {
163: startAutoScrolling(SCROLL_UP);
164: } else if (convertedEvent.getPoint().y > listVisibleRect.y
165: + list.getVisibleRect().height) {
166: startAutoScrolling(SCROLL_DOWN);
167: } else {
168: stopAutoScrolling();
169: }
170: }
171: }
172: }
173:
174: protected class ItemHandler implements ItemListener {
175: public void itemStateChanged(final ItemEvent e) {
176: int selectedIndex = comboBox.getSelectedIndex();
177: if (selectedIndex != -1) {
178: list.setSelectedIndex(selectedIndex);
179: list.ensureIndexIsVisible(selectedIndex);
180: }
181: }
182: }
183:
184: protected class ListMouseHandler extends MouseAdapter {
185: public void mousePressed(final MouseEvent e) {
186:
187: }
188:
189: public void mouseReleased(final MouseEvent e) {
190: BasicListUI ui = (BasicListUI) list.getUI();
191: if (!ui.extendedSupportEnabled
192: || ui.isChoosable(list.getSelectedIndex())) {
193: comboBox.setPopupVisible(false);
194: comboBox.setSelectedIndex(list.getSelectedIndex());
195: }
196: }
197: }
198:
199: protected class ListMouseMotionHandler extends MouseMotionAdapter {
200: public void mouseMoved(final MouseEvent event) {
201: updateListBoxSelectionForEvent(event, false);
202: }
203: }
204:
205: protected class PropertyChangeHandler implements
206: PropertyChangeListener {
207: public void propertyChange(final PropertyChangeEvent event) {
208: if (StringConstants.FONT_PROPERTY_CHANGED.equals(event
209: .getPropertyName())) {
210: list.setFont((Font) event.getNewValue());
211: } else if (StringConstants.BACKGROUND_PROPERTY_CHANGED
212: .equals(event.getPropertyName())) {
213: list.setBackground((Color) event.getNewValue());
214: } else if (StringConstants.FOREGROUND_PROPERTY_CHANGED
215: .equals(event.getPropertyName())) {
216: list.setForeground((Color) event.getNewValue());
217: } else if (StringConstants.RENDERER_PROPERTY_CHANGED
218: .equals(event.getPropertyName())) {
219: list.setCellRenderer((ListCellRenderer) event
220: .getNewValue());
221: } else if (StringConstants.COMPONENT_ORIENTATION
222: .equals(event.getPropertyName())) {
223: applyComponentOrientation((ComponentOrientation) event
224: .getNewValue());
225: } else if (StringConstants.LIGHTWEIGHT_POPUP_ENABLED_PROPERTY_CHANGED
226: .equals(event.getPropertyName())) {
227: setLightWeightPopupEnabled(((Boolean) event
228: .getNewValue()).booleanValue());
229: }
230: }
231: }
232:
233: protected JComboBox comboBox;
234: protected JList list;
235: protected boolean hasEntered;
236: protected boolean isAutoScrolling;
237: protected int scrollDirection;
238: protected JScrollPane scroller;
239: protected boolean valueIsAdjusting;
240: protected Timer autoscrollTimer;
241:
242: protected ItemListener itemListener;
243: protected KeyListener keyListener;
244: protected ListDataListener listDataListener;
245: protected MouseListener listMouseListener;
246: protected MouseMotionListener listMouseMotionListener;
247: protected ListSelectionListener listSelectionListener;
248: protected MouseListener mouseListener;
249: protected MouseMotionListener mouseMotionListener;
250: protected PropertyChangeListener propertyChangeListener;
251:
252: protected static final int SCROLL_UP = 0;
253: protected static final int SCROLL_DOWN = 1;
254:
255: private boolean leftComboBox;
256: private Insets cachedInsets = new Insets(0, 0, 0, 0);
257:
258: public BasicComboPopup(final JComboBox combo) {
259: comboBox = combo;
260: list = createList();
261: configureList();
262:
263: scroller = createScroller();
264: configureScroller();
265:
266: add(scroller);
267:
268: configurePopup();
269:
270: installComboBoxListeners();
271: installComboBoxModelListeners(comboBox.getModel());
272: installKeyboardActions();
273:
274: setBorder(BorderFactory.createLineBorder(Color.black, 1));
275: }
276:
277: public void hide() {
278: setVisible(false);
279: }
280:
281: public void show() {
282: if (isShowing()) {
283: return;
284: }
285: leftComboBox = false;
286: hasEntered = false;
287:
288: Point screenPosition = comboBox.getLocationOnScreen();
289: Insets comboInsets = comboBox.getInsets(cachedInsets);
290: Rectangle popupBounds = computePopupBounds(
291: screenPosition.x + comboInsets.left,
292: screenPosition.y,
293: comboBox.getWidth() - comboInsets.left
294: - comboInsets.right,
295: getPopupHeightForRowCount(comboBox.getMaximumRowCount()));
296: Point popupPosition = new Point(popupBounds.x, popupBounds.y);
297: Insets popupInsets = getInsets();
298: scroller.setPreferredSize(new Dimension(popupBounds.width
299: - popupInsets.left - popupInsets.right,
300: popupBounds.height));
301:
302: list.setSelectedIndex(comboBox.getSelectedIndex());
303:
304: setLocation(popupPosition.x, popupPosition.y);
305: super .setVisible(true);
306:
307: int selectedIndex = list.getSelectedIndex();
308: if (selectedIndex != -1) {
309: list.ensureIndexIsVisible(selectedIndex);
310: }
311: }
312:
313: public JList getList() {
314: return list;
315: }
316:
317: public MouseListener getMouseListener() {
318: if (mouseListener == null) {
319: mouseListener = createMouseListener();
320: }
321: return mouseListener;
322: }
323:
324: public MouseMotionListener getMouseMotionListener() {
325: if (mouseMotionListener == null) {
326: mouseMotionListener = createMouseMotionListener();
327: }
328: return mouseMotionListener;
329: }
330:
331: public KeyListener getKeyListener() {
332: return keyListener;
333: }
334:
335: public void uninstallingUI() {
336: uninstallKeyboardActions();
337: uninstallComboBoxModelListeners(comboBox.getModel());
338: uninstallComboBoxListeners();
339: }
340:
341: public boolean isFocusTraversable() {
342: return false;
343: }
344:
345: public AccessibleContext getAccessibleContext() {
346: if (accessibleContext == null) {
347: accessibleContext = super .getAccessibleContext();
348: accessibleContext.setAccessibleParent(comboBox);
349: }
350:
351: return accessibleContext;
352: }
353:
354: protected void installComboBoxListeners() {
355: itemListener = createItemListener();
356: if (itemListener != null) {
357: comboBox.addItemListener(itemListener);
358: }
359:
360: propertyChangeListener = createPropertyChangeListener();
361: if (propertyChangeListener != null) {
362: comboBox.addPropertyChangeListener(propertyChangeListener);
363: }
364: }
365:
366: protected void installComboBoxModelListeners(
367: final ComboBoxModel model) {
368: }
369:
370: protected void uninstallComboBoxModelListeners(
371: final ComboBoxModel model) {
372:
373: }
374:
375: protected void installKeyboardActions() {
376:
377: }
378:
379: protected void uninstallKeyboardActions() {
380:
381: }
382:
383: protected JList createList() {
384: return new JList(comboBox.getModel());
385: }
386:
387: protected void configureList() {
388: installListListeners();
389: list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
390: list.setFocusable(false);
391: list.setBorder(null);
392: list.setSelectionBackground(UIManager
393: .getColor("ComboBox.selectionBackground"));
394: list.setSelectionForeground(UIManager
395: .getColor("ComboBox.selectionForeground"));
396: list.setCellRenderer(comboBox.getRenderer());
397: list.setBackground(comboBox.getBackground());
398: list.setForeground(comboBox.getForeground());
399: list.setFont(comboBox.getFont());
400: }
401:
402: protected JScrollPane createScroller() {
403: return new JScrollPane(list);
404: }
405:
406: protected void configureScroller() {
407: scroller.setFocusable(true);
408: scroller.setBorder(null);
409: }
410:
411: protected void configurePopup() {
412: setInvoker(comboBox);
413: putClientProperty(
414: StringConstants.HIDE_ON_INVOKER_PRESSED_PROPERTY,
415: Boolean.TRUE);
416: }
417:
418: protected void installListListeners() {
419: listDataListener = createListDataListener();
420: if (listDataListener != null) {
421: list.getModel().addListDataListener(listDataListener);
422: }
423:
424: listMouseListener = createListMouseListener();
425: if (listMouseListener != null) {
426: list.addMouseListener(listMouseListener);
427: }
428:
429: listMouseMotionListener = createListMouseMotionListener();
430: if (listMouseMotionListener != null) {
431: list.addMouseMotionListener(listMouseMotionListener);
432: }
433:
434: listSelectionListener = createListSelectionListener();
435: if (listSelectionListener != null) {
436: list.addListSelectionListener(listSelectionListener);
437: }
438: }
439:
440: protected void firePopupMenuWillBecomeVisible() {
441: super .firePopupMenuWillBecomeVisible();
442: comboBox.firePopupMenuWillBecomeVisible();
443: }
444:
445: protected void firePopupMenuWillBecomeInvisible() {
446: super .firePopupMenuWillBecomeInvisible();
447: comboBox.firePopupMenuWillBecomeInvisible();
448: }
449:
450: protected void firePopupMenuCanceled() {
451: super .firePopupMenuCanceled();
452: comboBox.firePopupMenuCanceled();
453: }
454:
455: protected MouseListener createMouseListener() {
456: return new InvocationMouseHandler();
457: }
458:
459: protected MouseMotionListener createMouseMotionListener() {
460: return new InvocationMouseMotionHandler();
461: }
462:
463: protected KeyListener createKeyListener() {
464: return null;
465: }
466:
467: protected ListSelectionListener createListSelectionListener() {
468: return null;
469: }
470:
471: protected ListDataListener createListDataListener() {
472: return null;
473: }
474:
475: protected MouseListener createListMouseListener() {
476: return new ListMouseHandler();
477: }
478:
479: protected MouseMotionListener createListMouseMotionListener() {
480: return new ListMouseMotionHandler();
481: }
482:
483: protected PropertyChangeListener createPropertyChangeListener() {
484: return new PropertyChangeHandler();
485: }
486:
487: protected ItemListener createItemListener() {
488: return new ItemHandler();
489: }
490:
491: protected void startAutoScrolling(final int direction) {
492: if (list.getModel().getSize() == 0) {
493: return;
494: }
495: scrollDirection = direction;
496: if (!isAutoScrolling) {
497: isAutoScrolling = true;
498: getTimer().start();
499: }
500: }
501:
502: protected void stopAutoScrolling() {
503: if (!isAutoScrolling) {
504: return;
505: }
506: getTimer().stop();
507: isAutoScrolling = false;
508: }
509:
510: protected void autoScrollUp() {
511: int selection = list.getSelectedIndex();
512: int firstVisible = list.getFirstVisibleIndex();
513: if (selection == firstVisible && selection > 0) {
514: list.setSelectedIndex(selection - 1);
515: list.ensureIndexIsVisible(selection - 1);
516: } else if (selection != firstVisible) {
517: list.setSelectedIndex(firstVisible);
518: list.ensureIndexIsVisible(firstVisible);
519: }
520: }
521:
522: protected void autoScrollDown() {
523: int selection = list.getSelectedIndex();
524: int lastVisible = list.getLastVisibleIndex();
525: if (selection == lastVisible
526: && selection < list.getModel().getSize() - 1) {
527: list.setSelectedIndex(selection + 1);
528: list.ensureIndexIsVisible(selection + 1);
529: } else if (selection != lastVisible) {
530: list.setSelectedIndex(lastVisible);
531: list.ensureIndexIsVisible(lastVisible);
532: }
533: }
534:
535: protected void delegateFocus(final MouseEvent e) {
536: if (comboBox.isEditable()) {
537: comboBox.getEditor().getEditorComponent().requestFocus();
538: } else {
539: comboBox.requestFocus();
540: }
541: }
542:
543: protected void togglePopup() {
544: comboBox.setPopupVisible(!comboBox.isPopupVisible());
545: }
546:
547: protected MouseEvent convertMouseEvent(final MouseEvent e) {
548: return SwingUtilities.convertMouseEvent((Component) e
549: .getSource(), e, list);
550: }
551:
552: protected int getPopupHeightForRowCount(final int maxRowCount) {
553: int maxRow = Math.min(list.getModel().getSize(), maxRowCount);
554: if (maxRow == 0) {
555: return 100;
556: } else {
557: return list.getCellBounds(0, maxRow - 1).height;
558: }
559: }
560:
561: protected Rectangle computePopupBounds(final int px, final int py,
562: final int pw, final int ph) {
563: Insets insets = comboBox.getInsets(cachedInsets);
564: Rectangle anchor = new Rectangle(px, py, comboBox.getWidth()
565: - insets.left - insets.right, comboBox.getHeight()
566: - insets.top - insets.bottom);
567: Point location = Utilities.getPopupLocation(anchor,
568: new Dimension(pw, ph), comboBox
569: .getComponentOrientation().isLeftToRight(),
570: false, comboBox.getGraphicsConfiguration());
571: return new Rectangle(location.x, location.y, pw, ph);
572: }
573:
574: protected void updateListBoxSelectionForEvent(final MouseEvent e,
575: final boolean shouldScroll) {
576: int index = list.locationToIndex(e.getPoint());
577: if (index != -1) {
578: list.setSelectedIndex(index);
579: if (shouldScroll) {
580: list.ensureIndexIsVisible(index);
581: }
582: }
583: }
584:
585: private void uninstallComboBoxListeners() {
586: if (itemListener != null) {
587: comboBox.removeItemListener(itemListener);
588: }
589: itemListener = null;
590:
591: if (propertyChangeListener != null) {
592: comboBox
593: .removePropertyChangeListener(propertyChangeListener);
594: }
595: propertyChangeListener = null;
596: }
597:
598: private Timer getTimer() {
599: if (autoscrollTimer == null) {
600: autoscrollTimer = new Timer(100, new ActionListener() {
601: public void actionPerformed(final ActionEvent e) {
602: if (scrollDirection == SCROLL_UP) {
603: autoScrollUp();
604: } else if (scrollDirection == SCROLL_DOWN) {
605: autoScrollDown();
606: } else {
607: throw new IllegalArgumentException(Messages
608: .getString("swing.6E")); //$NON-NLS-1$
609: }
610: }
611: });
612: }
613:
614: return autoscrollTimer;
615: }
616: }
|