001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: * Contributor(s): Soot Phengsy
042: */
043:
044: package org.netbeans.swing.dirchooser;
045:
046: import java.awt.Component;
047: import java.awt.Dimension;
048: import java.awt.Point;
049: import java.awt.Rectangle;
050: import java.awt.event.ActionEvent;
051: import java.awt.event.FocusAdapter;
052: import java.awt.event.FocusEvent;
053: import java.awt.event.KeyEvent;
054: import java.awt.event.KeyListener;
055: import java.awt.event.MouseAdapter;
056: import java.awt.event.MouseEvent;
057: import java.awt.event.MouseMotionListener;
058: import java.io.File;
059: import java.util.Vector;
060: import java.util.logging.Level;
061: import java.util.logging.Logger;
062: import javax.swing.AbstractAction;
063: import javax.swing.JFileChooser;
064: import javax.swing.JList;
065: import javax.swing.JPopupMenu;
066: import javax.swing.JScrollPane;
067: import javax.swing.JTextField;
068: import javax.swing.KeyStroke;
069: import javax.swing.ListSelectionModel;
070: import javax.swing.SwingUtilities;
071: import javax.swing.text.BadLocationException;
072: import javax.swing.text.Document;
073: import javax.swing.text.JTextComponent;
074:
075: /**
076: * A class that handles the "File Name:" text field auto-completion drop-down selection list.
077: *
078: * @author Soot Phengsy
079: */
080: public class FileCompletionPopup extends JPopupMenu implements
081: KeyListener {
082:
083: private JList list;
084: private JTextField textField;
085: private JFileChooser chooser;
086:
087: public FileCompletionPopup(JFileChooser chooser,
088: JTextField textField, Vector<File> files) {
089: this .list = new JList(files);
090: this .textField = textField;
091: this .chooser = chooser;
092: list.setVisibleRowCount(4);
093: list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
094:
095: JScrollPane jsp = new JScrollPane(list);
096: add(jsp);
097:
098: list.setFocusable(false);
099: jsp.setFocusable(false);
100: setFocusable(false);
101:
102: list.addFocusListener(new FocusHandler());
103: list.addMouseListener(new MouseHandler());
104: list.addMouseMotionListener(new MouseHandler());
105:
106: textField.addKeyListener(this );
107: }
108:
109: public void setDataList(Vector files) {
110: list.setListData(files);
111: ensureSelection();
112: }
113:
114: private void setSelectNext() {
115: if (list.getModel().getSize() > 0) {
116: int cur = (list.getSelectedIndex() + 1)
117: % list.getModel().getSize();
118: list.setSelectedIndex(cur);
119: list.ensureIndexIsVisible(cur);
120: }
121: }
122:
123: private void setSelectPrevious() {
124: if (list.getModel().getSize() > 0) {
125: int cur = (list.getSelectedIndex() == -1) ? 0 : list
126: .getSelectedIndex();
127: cur = (cur == 0) ? list.getModel().getSize() - 1 : cur - 1;
128: list.setSelectedIndex(cur);
129: list.ensureIndexIsVisible(cur);
130: }
131: }
132:
133: public void showPopup(JTextComponent source, int x, int y) {
134: if (list.getModel().getSize() == 0) {
135: return;
136: }
137: setPreferredSize(new Dimension(source.getWidth(), source
138: .getHeight() * 4));
139: show(source, x, y);
140: ensureSelection();
141: }
142:
143: // #106268: always have some item selected for better usability
144: private void ensureSelection() {
145: if (list.getSelectedIndex() == -1
146: && (list.getModel().getSize() > 0)) {
147: list.setSelectedIndex(0);
148: }
149: }
150:
151: private class FocusHandler extends FocusAdapter {
152: public void focusLost(FocusEvent e) {
153: if (!e.isTemporary()) {
154: setVisible(false);
155: textField.requestFocus();
156: }
157: }
158: }
159:
160: private class MouseHandler extends MouseAdapter implements
161: MouseMotionListener {
162: public void mouseMoved(MouseEvent e) {
163: if (e.getSource() == list) {
164: Point location = e.getPoint();
165: int index = list.locationToIndex(location);
166: Rectangle r = new Rectangle();
167: list.computeVisibleRect(r);
168: if (r.contains(location)) {
169: list.setSelectedIndex(index);
170: }
171: }
172: }
173:
174: public void mouseDragged(MouseEvent e) {
175: if (e.getSource() == list) {
176: return;
177: }
178: if (isVisible()) {
179: MouseEvent newEvent = convertMouseEvent(e);
180: Rectangle r = new Rectangle();
181: list.computeVisibleRect(r);
182: Point location = newEvent.getPoint();
183: int index = list.locationToIndex(location);
184: if (r.contains(location)) {
185: list.setSelectedIndex(index);
186: }
187: }
188: }
189:
190: public void mouseClicked(MouseEvent e) {
191: Point p = e.getPoint();
192: int index = list.locationToIndex(p);
193: list.setSelectedIndex(index);
194: setVisible(false);
195: File file = (File) list.getSelectedValue();
196: if (file.equals(chooser.getCurrentDirectory())) {
197: chooser.firePropertyChange(
198: JFileChooser.DIRECTORY_CHANGED_PROPERTY, false,
199: true);
200: } else {
201: chooser.setCurrentDirectory(file);
202: }
203: textField.requestFocus();
204: }
205:
206: private MouseEvent convertMouseEvent(MouseEvent e) {
207: Point convertedPoint = SwingUtilities.convertPoint(
208: (Component) e.getSource(), e.getPoint(), list);
209: MouseEvent newEvent = new MouseEvent((Component) e
210: .getSource(), e.getID(), e.getWhen(), e
211: .getModifiers(), convertedPoint.x,
212: convertedPoint.y, e.getClickCount(), e
213: .isPopupTrigger());
214: return newEvent;
215: }
216: }
217:
218: /****** implementation of KeyListener of fileNameTextField ******/
219:
220: public void keyPressed(KeyEvent e) {
221: if (!isVisible()) {
222: return;
223: }
224:
225: int code = e.getKeyCode();
226: switch (code) {
227: case KeyEvent.VK_DOWN:
228: setSelectNext();
229: e.consume();
230: break;
231: case KeyEvent.VK_UP:
232: setSelectPrevious();
233: e.consume();
234: break;
235: case KeyEvent.VK_ESCAPE:
236: setVisible(false);
237: textField.requestFocus();
238: e.consume();
239: break;
240: }
241:
242: if (isCompletionKey(code, textField)) {
243: File file = (File) list.getSelectedValue();
244: if (file != null) {
245: if (file.equals(chooser.getCurrentDirectory())) {
246: chooser.firePropertyChange(
247: JFileChooser.DIRECTORY_CHANGED_PROPERTY,
248: false, true);
249: } else {
250: chooser.setSelectedFiles(new File[] { file });
251: chooser.setCurrentDirectory(file);
252: }
253: if (file.isDirectory()) {
254: try {
255: Document doc = textField.getDocument();
256: doc.insertString(doc.getLength(),
257: File.separator, null);
258: } catch (BadLocationException ex) {
259: Logger.getLogger(getClass().getName()).log(
260: Level.FINE,
261: "Cannot append directory separator.",
262: ex);
263: }
264: }
265: }
266: setVisible(false);
267: textField.requestFocus();
268: e.consume();
269: }
270: }
271:
272: public void keyReleased(KeyEvent e) {
273: // no operation
274: }
275:
276: public void keyTyped(KeyEvent e) {
277: // no operation
278: }
279:
280: private boolean isCompletionKey(int keyCode, JTextField textField) {
281: if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_TAB) {
282: return true;
283: }
284: if (keyCode == KeyEvent.VK_RIGHT
285: && (textField.getCaretPosition() >= (textField
286: .getDocument().getLength() - 1))) {
287: return true;
288: }
289:
290: return false;
291: }
292:
293: }
|