001: /*--------------------------------------------------------------------------*
002: | Copyright (C) 2006 Christopher Kohlhaas |
003: | |
004: | This program is free software; you can redistribute it and/or modify |
005: | it under the terms of the GNU General Public License as published by the |
006: | Free Software Foundation. A copy of the license has been included with |
007: | these distribution in the COPYING file, if not go to www.fsf.org |
008: | |
009: | As a special exception, you are granted the permissions to link this |
010: | program with every library, which license fulfills the Open Source |
011: | Definition as published by the Open Source Initiative (OSI). |
012: *--------------------------------------------------------------------------*/
013:
014: package org.rapla.components.calendar;
015:
016: import java.awt.BorderLayout;
017: import java.awt.CardLayout;
018: import java.awt.Dimension;
019: import java.awt.Font;
020: import java.awt.Point;
021: import java.awt.Toolkit;
022: import java.awt.event.ActionEvent;
023: import java.awt.event.ActionListener;
024: import java.awt.event.MouseEvent;
025: import java.awt.event.MouseListener;
026:
027: import javax.swing.JButton;
028: import javax.swing.JComponent;
029: import javax.swing.JLabel;
030: import javax.swing.JMenuItem;
031: import javax.swing.JPanel;
032: import javax.swing.JPopupMenu;
033: import javax.swing.event.PopupMenuEvent;
034: import javax.swing.event.PopupMenuListener;
035:
036: /** This is an alternative ComboBox implementation. The main differences are:
037: <ul>
038: <li>No pluggable look and feel</li>
039: <li>drop-down button and editor in a separate component (each can get focus)</li>
040: <li>the popup-component can be an arbitrary JComponent.</li>
041: </ul>
042: */
043: public abstract class RaplaComboBox extends JPanel {
044:
045: private JPopupMenu m_popup;
046: private boolean m_popupVisible = false;
047: private boolean m_isDropDown = true;
048: protected JButton m_popupButton;
049: protected JLabel jLabel;
050: protected JComponent m_editorComponent;
051:
052: // If you click on the popupButton while the PopupWindow is shown,
053: // the Window will be closed, because you clicked outside the Popup
054: // (not because you clicked that special button).
055: // The flag popupVisible is unset.
056: // After that the same click will trigger the actionPerfomed method
057: // on our button. And the window would popup again.
058: // Solution: We track down that one mouseclick that closes the popup
059: // with the following flags:
060: private boolean m_closing = false;
061: private boolean m_pressed = false;
062: private Listener m_listener = new Listener();
063:
064: public RaplaComboBox(JComponent editorComponent) {
065: this (true, editorComponent);
066: }
067:
068: RaplaComboBox(boolean isDropDown, JComponent editorComponent) {
069: m_editorComponent = editorComponent;
070: m_isDropDown = isDropDown;
071: jLabel = new JLabel();
072: setLayout(new BorderLayout());
073: add(jLabel, BorderLayout.WEST);
074: add(m_editorComponent, BorderLayout.CENTER);
075:
076: if (m_isDropDown) {
077: installPopupButton();
078: add(m_popupButton, BorderLayout.EAST);
079: m_popupButton.addActionListener(m_listener);
080: m_popupButton.addMouseListener(m_listener);
081: m_popupVisible = false;
082: }
083: }
084:
085: public JLabel getLabel() {
086: return jLabel;
087: }
088:
089: class Listener implements ActionListener, MouseListener,
090: PopupMenuListener {
091: // Implementation of ActionListener
092: public void actionPerformed(ActionEvent e) {
093: // Ignore action, when Popup is closing
094: if (m_pressed && m_closing) {
095: m_closing = false;
096: return;
097: }
098: if (!m_popupVisible) {
099: showPopup();
100: } else {
101: m_popupVisible = false;
102: closePopup();
103: } // end of else
104: }
105:
106: // Implementation of MouseListener
107: public void mousePressed(MouseEvent evt) {
108: m_pressed = true;
109: m_closing = false;
110: }
111:
112: public void mouseClicked(MouseEvent evt) {
113: }
114:
115: public void mouseReleased(MouseEvent evt) {
116: m_pressed = false;
117: }
118:
119: public void mouseEntered(MouseEvent me) {
120: }
121:
122: public void mouseExited(MouseEvent me) {
123: }
124:
125: // Implementation of PopupListener
126: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
127: m_popupVisible = true;
128: }
129:
130: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
131: m_popupVisible = false;
132: m_closing = true;
133: m_popupButton.requestFocus();
134: }
135:
136: public void popupMenuCanceled(PopupMenuEvent e) {
137: m_popupVisible = false;
138: m_popupButton.requestFocus();
139: }
140:
141: };
142:
143: private void installPopupButton() {
144: // Maybe we could use the combobox drop-down button here.
145: m_popupButton = new RaplaArrowButton('v');
146: }
147:
148: public void setFont(Font font) {
149: super .setFont(font);
150: // Method called during constructor?
151: if (font == null)
152: return;
153: if (m_editorComponent != null)
154: m_editorComponent.setFont(font);
155: if (m_popupButton != null && m_isDropDown) {
156: int size = (int) getPreferredSize().getHeight();
157: m_popupButton.setSize(size, size);
158: }
159: }
160:
161: public void setEnabled(boolean enabled) {
162: super .setEnabled(enabled);
163: if (m_editorComponent != null) {
164: m_editorComponent.setEnabled(enabled);
165: }
166: if (m_popupButton != null) {
167: m_popupButton.setEnabled(enabled);
168: }
169: }
170:
171: protected void showPopup() {
172: if (m_popup == null)
173: createPopup();
174: Dimension screenSize = Toolkit.getDefaultToolkit()
175: .getScreenSize();
176: Dimension menuSize = m_popup.getPreferredSize();
177: Dimension buttonSize = m_popupButton.getSize();
178: Point location = m_popupButton.getLocationOnScreen();
179: int diffx = buttonSize.width - menuSize.width;
180: if (location.x + diffx < 0)
181: diffx = -location.x;
182: int diffy = buttonSize.height;
183: if (location.y + diffy + menuSize.height > screenSize.height)
184: diffy = screenSize.height - menuSize.height - location.y;
185: m_popup.show(m_popupButton, diffx, diffy);
186: m_popup.requestFocus();
187: }
188:
189: protected void closePopup() {
190: if (m_popup != null && m_popup.isVisible()) {
191: // #Workaround for JMenuPopup-Bug in JDK 1.4
192: // intended behaviour: m_popup.setVisible(false);
193:
194: javax.swing.SwingUtilities.invokeLater(new Runnable() {
195: public void run() {
196: // Show JMenuItem and fire a mouse-click
197: cardLayout.last(m_popup);
198: menuItem.menuSelectionChanged(true);
199: menuItem.dispatchEvent(new MouseEvent(m_popup,
200: MouseEvent.MOUSE_RELEASED, System
201: .currentTimeMillis(), 0, 0, 0, 1,
202: false));
203: // show original popup again
204: cardLayout.first(m_popup);
205: m_popupButton.requestFocus();
206: }
207: });
208: }
209: m_popupVisible = false;
210: }
211:
212: private JMenuItem menuItem;
213: private CardLayout cardLayout;
214:
215: class MyPopup extends JPopupMenu {
216: private static final long serialVersionUID = 1L;
217:
218: MyPopup() {
219: super ();
220: }
221:
222: public void menuSelectionChanged(boolean isIncluded) {
223: closePopup();
224: }
225: }
226:
227: private void createPopup() {
228: m_popup = new JPopupMenu();
229: /* try {
230: PopupMenu.class.getMethod("isPopupTrigger",new Object[]{});
231: } catch (Exception ex) {
232: m_popup.setLightWeightPopupEnabled(true);
233: }*/
234: m_popup.setBorder(null);
235: cardLayout = new CardLayout();
236: m_popup.setLayout(cardLayout);
237: m_popup.setInvoker(this );
238: m_popup.add(getPopupComponent(), "0");
239: menuItem = new JMenuItem("");
240: m_popup.add(menuItem, "1");
241: m_popup.setBorderPainted(true);
242: m_popup.addPopupMenuListener(m_listener);
243: }
244:
245: /** the component that should apear in the popup menu */
246: protected abstract JComponent getPopupComponent();
247: }
|