2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.form;
45
import java.awt.event.*;
47
import javax.swing.border.EmptyBorder;
48
import javax.swing.event.PopupMenuEvent;
49
import javax.swing.event.PopupMenuListener;
50
import javax.swing.event.TreeSelectionEvent;
51
import javax.swing.event.TreeSelectionListener;
52
import javax.swing.tree.*;
55
* ComboBox with tree in popup.
59
public class ComboBoxWithTree extends JComboBox {
60
/** Window used as popup. */
64
/** Scroll pane enclosing the tree. */
65
private JScrollPane scrollPane;
66
/** Converter between tree path and its string representation. */
67
private Converter converter;
70
* Creates new <code>ComboBoxWithTree</code>.
72
* @param treeModel tree model.
73
* @param treeCellRenderer tree cell renderer.
74
* @param converter converter between tree path and its string representation.
76
public ComboBoxWithTree(TreeModel treeModel, TreeCellRenderer treeCellRenderer, Converter converter) {
77
this.converter = converter;
79
initTree(treeModel, treeCellRenderer);
83
* Initializes the combo.
85
private void initCombo() {
87
addPopupMenuListener(new PopupMenuListener() {
88
public void popupMenuCanceled(PopupMenuEvent e) {
89
if (issue112997Hack) {
90
setPopupVisible(true);
93
getPopup().setVisible(false);
96
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
97
if (issue112997Hack) {
98
issue112997Hack = false;
99
setPopupVisible(true);
102
getPopup().setVisible(false);
105
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
106
updateTreeSelection();
107
Dimension dim = getSize();
108
Point p = getLocationOnScreen();
109
Window w = getPopup();
110
w.setLocation(p.x, p.y + dim.height);
111
w.setSize(new Dimension(dim.width, scrollPane.getPreferredSize().height));
115
// Get rid of original popup
116
setModel(new DefaultComboBoxModel(new Object[] {""})); // NOI18N
117
setRenderer(new DefaultListCellRenderer() {
119
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
120
if (index == -1) { // Make sure that the combo has a correct preferred size
121
return super.getListCellRendererComponent(list, "null", index, isSelected, cellHasFocus); // NOI18N
123
JLabel comp = new JLabel();
124
comp.setPreferredSize(new Dimension(0,-10000));
132
* Initializes the tree.
134
* @param treeModel tree model.
135
* @param treeCellRenderer tree cell renderer.
137
private void initTree(TreeModel treeModel, TreeCellRenderer treeCellRenderer) {
139
TreeSelectionModel selectionModel = new DefaultTreeSelectionModel();
140
selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
141
tree.setRootVisible(false);
142
tree.setShowsRootHandles(true);
143
tree.setSelectionModel(selectionModel);
144
tree.setVisibleRowCount(10);
145
tree.addKeyListener(new KeyAdapter() {
147
public void keyPressed(KeyEvent e) {
148
if (!isPopupVisible()) {
149
setPopupVisible(true);
151
int code = e.getKeyCode();
152
if ((code == KeyEvent.VK_ENTER) || (code == KeyEvent.VK_ESCAPE)) {
153
setPopupVisible(false);
157
tree.addMouseListener(new MouseAdapter() {
159
public void mouseClicked(MouseEvent e) {
160
if (!isPopupVisible()) {
161
setPopupVisible(true);
163
if (e.getClickCount() > 1) {
164
setPopupVisible(false);
168
tree.addTreeSelectionListener(new TreeSelectionListener() {
169
public void valueChanged(TreeSelectionEvent e) {
170
String value = converter.pathToString(e.getPath());
171
setSelectedItem(value);
174
tree.setModel(treeModel);
175
tree.setCellRenderer(treeCellRenderer);
183
private Window getPopup() {
185
popup = new Window(SwingUtilities.getWindowAncestor(this));
186
scrollPane = new JScrollPane(tree);
187
// The scrollPane must be in JPopupMenu to ensure that
188
// it is not closed when components within it obtain the focus
189
scrollPane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
190
JPopupMenu menu = new JPopupMenu() {
191
// Cannot use setVisible(true) on JDK 6 due to changes in isPopupMenu()
193
public boolean isVisible() {
197
menu.setBorder(new EmptyBorder(0,0,0,0));
198
menu.setLayout(new BorderLayout());
199
menu.add(scrollPane);
206
* Updates tree selection according to string in combo.
208
private void updateTreeSelection() {
209
final TreePath path = getSelectedTreePath();
211
tree.clearSelection();
213
tree.setSelectionPath(path);
214
EventQueue.invokeLater(new Runnable() {
216
tree.scrollPathToVisible(path);
222
public TreePath getSelectedTreePath() {
223
String value = getEditor().getItem().toString();
224
TreePath path = converter.stringToPath(value);
229
public void addNotify() {
231
Container cont = getParent();
232
while (!(cont instanceof Window) && (cont.getParent() != null)) {
233
cont = cont.getParent();
235
if (cont instanceof Window) {
236
((Window)cont).addWindowListener(new WindowAdapter() {
238
public void windowDeactivated(WindowEvent e) {
239
if (isPopupVisible()) {
240
issue112997Hack = true;
246
private boolean issue112997Hack = false;
249
* Converter between tree path and its string representation.
251
public static interface Converter {
253
* Converts tree path to string representation.
255
* @param path path to convert.
256
* @return string representation of tree path.
258
String pathToString(TreePath path);
261
* Converts string representation to tree path.
263
* @param value string to convert.
264
* @return tree path that corresponds to the given string representation.
266
TreePath stringToPath(String value);