0001: /*
0002: * OpenFileTextFieldHandler.java
0003: *
0004: * Copyright (C) 1998-2004 Peter Graves
0005: * $Id: OpenFileTextFieldHandler.java,v 1.54 2004/09/20 00:44:11 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.j;
0023:
0024: import gnu.regexp.RE;
0025: import gnu.regexp.REMatch;
0026: import gnu.regexp.UncheckedRE;
0027: import java.awt.Component;
0028: import java.awt.event.InputEvent;
0029: import java.awt.event.KeyEvent;
0030: import java.awt.event.MouseEvent;
0031: import java.awt.event.MouseListener;
0032: import java.lang.reflect.Method;
0033: import java.util.ArrayList;
0034: import java.util.List;
0035: import javax.swing.JList;
0036: import javax.swing.JPopupMenu;
0037: import javax.swing.JScrollPane;
0038: import javax.swing.MenuElement;
0039: import javax.swing.MenuSelectionManager;
0040: import javax.swing.SwingUtilities;
0041: import org.armedbear.j.mail.MailCommands;
0042:
0043: public final class OpenFileTextFieldHandler extends
0044: DefaultTextFieldHandler implements Constants, MouseListener {
0045: private static final boolean filenamesIgnoreCase = Platform
0046: .isPlatformWindows();
0047:
0048: private String title = "Open File";
0049:
0050: // Options.
0051: private boolean allowRemote = true;
0052: private boolean fileMustExist = false;
0053: private boolean checkBuffers = true;
0054: private boolean checkSourcePath = true;
0055:
0056: private Object returned;
0057: private String encoding;
0058:
0059: private JPopupMenu popup;
0060: private JList listbox;
0061:
0062: private String originalText;
0063: private String originalPrefix;
0064:
0065: public OpenFileTextFieldHandler(Editor editor,
0066: HistoryTextField textField) {
0067: super (editor, textField);
0068: textField.addMouseListener(this );
0069: }
0070:
0071: public final void setTitle(String s) {
0072: title = s;
0073: }
0074:
0075: public final void setAllowRemote(boolean b) {
0076: allowRemote = b;
0077: }
0078:
0079: public final void setFileMustExist(boolean b) {
0080: fileMustExist = b;
0081: }
0082:
0083: public final void setCheckBuffers(boolean b) {
0084: checkBuffers = b;
0085: }
0086:
0087: public final void setCheckSourcePath(boolean b) {
0088: checkSourcePath = b;
0089: }
0090:
0091: public void enter() {
0092: final Buffer buffer = editor.getBuffer();
0093: String entry = textField.getText();
0094: if (!entry.equals(buffer.getFileNameForDisplay()))
0095: saveHistory();
0096: entry = preprocess(entry);
0097: if (encoding != null
0098: && !Utilities.isSupportedEncoding(encoding)) {
0099: FastStringBuffer sb = new FastStringBuffer(
0100: "Unsupported encoding \"");
0101: sb.append(encoding);
0102: sb.append('"');
0103: error(sb.toString());
0104: return;
0105: }
0106: File currentDir = buffer.getCompletionDirectory();
0107: if (entry.length() == 0) {
0108: returned = currentDir;
0109: done();
0110: return;
0111: }
0112: // Aliases.
0113: String value = editor.getAlias(entry);
0114: if (value != null)
0115: entry = value;
0116: if (entry.startsWith("pop://") || entry.startsWith("{")
0117: || entry.startsWith("mailbox:")) {
0118: MailCommands.openMailbox(editor, entry);
0119: editor.ensureActive();
0120: editor.setFocusToDisplay();
0121: editor.updateLocation();
0122: editor.updateDisplay();
0123: return;
0124: }
0125: File candidate = null;
0126: if (Utilities.isFilenameAbsolute(entry)) {
0127: candidate = File.getInstance(currentDir, entry);
0128: if (candidate == null) {
0129: error("Invalid path");
0130: return;
0131: }
0132: } else if (entry.startsWith("./") || entry.startsWith(".\\")) {
0133: // Path specified is relative to current directory (even if remote).
0134: candidate = File.getInstance(currentDir, entry);
0135: if (candidate == null) {
0136: error("Invalid path");
0137: return;
0138: }
0139: }
0140: if (candidate != null) {
0141: if (candidate.isRemote()) {
0142: if (!allowRemote) {
0143: error("File is remote");
0144: return;
0145: }
0146: } else {
0147: // Not remote.
0148: if (!candidate.exists()) {
0149: if (fileMustExist) {
0150: error("File not found");
0151: return;
0152: }
0153: if (!checkParentDirectory(candidate, title)) {
0154: editor.setFocusToDisplay();
0155: editor.updateLocation();
0156: return;
0157: }
0158: }
0159: }
0160: returned = candidate;
0161: done();
0162: return;
0163: }
0164: // Not absolute. Look in current directory.
0165: candidate = File.getInstance(currentDir, entry);
0166: if (candidate != null && candidate.exists()) {
0167: returned = candidate;
0168: done();
0169: return;
0170: }
0171: // Not in current directory. Look for a match in one of the current
0172: // buffers.
0173: if (checkBuffers) {
0174: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
0175: Buffer buf = it.nextBuffer();
0176: if (buf.getFile() == null)
0177: continue;
0178: boolean found;
0179: if (filenamesIgnoreCase)
0180: found = buf.getFile().getName().equalsIgnoreCase(
0181: entry);
0182: else
0183: found = buf.getFile().getName().equals(entry);
0184: if (found) {
0185: returned = buf;
0186: done();
0187: return;
0188: }
0189: }
0190: }
0191: // Not currently in a buffer. Look in source and include paths as
0192: // appropriate.
0193: if (checkSourcePath) {
0194: candidate = Utilities.findFile(editor, entry);
0195: if (candidate != null) {
0196: returned = candidate;
0197: done();
0198: return;
0199: }
0200: }
0201: // Not found in source or include path.
0202: if (allowRemote) {
0203: if (entry.startsWith("www.")) {
0204: returned = File.getInstance("http://".concat(entry));
0205: done();
0206: return;
0207: }
0208: if (entry.startsWith("ftp.")) {
0209: returned = File.getInstance("ftp://".concat(entry));
0210: done();
0211: return;
0212: }
0213: }
0214: // We failed. Use current directory.
0215: if (currentDir.isRemote())
0216: currentDir = Directories.getUserHomeDirectory();
0217: candidate = File.getInstance(currentDir, entry);
0218: if (candidate == null) {
0219: error("Invalid path");
0220: return;
0221: }
0222: if (fileMustExist && !candidate.exists()) {
0223: error("File not found");
0224: return;
0225: }
0226: if (!checkParentDirectory(candidate, title)) {
0227: editor.setFocusToDisplay();
0228: editor.updateLocation();
0229: return;
0230: }
0231: returned = candidate;
0232: done();
0233: }
0234:
0235: private String preprocess(String s) {
0236: encoding = null;
0237: s = s.trim();
0238: if (s.startsWith("-e ")) {
0239: s = s.substring(3).trim();
0240: int index = s.indexOf(' ');
0241: encoding = s.substring(0, index);
0242: return s.substring(index + 1).trim();
0243: }
0244: int index = s.indexOf(" -e ");
0245: if (index < 0)
0246: return s; // No encoding specified.
0247: encoding = s.substring(index + 4).trim();
0248: return s.substring(0, index).trim();
0249: }
0250:
0251: private boolean checkParentDirectory(File file, String context) {
0252: File parentDir = file.getParentFile();
0253: if (parentDir != null && parentDir.isDirectory())
0254: return true;
0255: FastStringBuffer sb = new FastStringBuffer("Invalid path \"");
0256: sb.append(file.canonicalPath());
0257: sb.append('"');
0258: MessageDialog.showMessageDialog(sb.toString(), context);
0259: return false;
0260: }
0261:
0262: private void done() {
0263: Object owner = textField.getOwner();
0264: if (owner instanceof OpenFileDialog) {
0265: OpenFileDialog dialog = (OpenFileDialog) owner;
0266: dialog.setResult(returned);
0267: dialog.ok();
0268: return;
0269: }
0270: Debug.assertTrue(editor != null);
0271: Buffer buf = null;
0272: if (returned instanceof Buffer) {
0273: buf = (Buffer) returned;
0274: } else if (returned instanceof File) {
0275: File file = (File) returned;
0276: file.setEncoding(encoding);
0277: if (file instanceof HttpFile) {
0278: if (Editor.getModeList().modeAccepts(IMAGE_MODE,
0279: file.getName())) {
0280: buf = Editor.getBufferList().findBuffer(file);
0281: if (buf == null)
0282: buf = new RemoteBuffer(file);
0283: } else if (Editor.preferences().getBooleanProperty(
0284: Property.ENABLE_WEB)) {
0285: int modeId = Editor.getModeList()
0286: .getModeIdForFileName(file.getName());
0287: if (modeId < 0 || modeId == HTML_MODE) {
0288: if (editor.getMode() instanceof WebMode) {
0289: // Current buffer is already a web buffer.
0290: buf = editor.getBuffer();
0291: Debug.assertTrue(buf instanceof WebBuffer);
0292: ((WebBuffer) buf).saveHistory(
0293: buf.getFile(), buf
0294: .getAbsoluteOffset(editor
0295: .getDot()),
0296: ((WebBuffer) buf).getContentType());
0297: // If we don't call setCache(null), go() will use the
0298: // existing cache.
0299: buf.setCache(null);
0300: ((WebBuffer) buf).go(file, 0, null);
0301: } else {
0302: // Look for existing buffer.
0303: for (BufferIterator it = new BufferIterator(); it
0304: .hasNext();) {
0305: Buffer b = it.nextBuffer();
0306: if (b instanceof WebBuffer
0307: && b.getFile().equals(file)) {
0308: buf = b;
0309: break;
0310: }
0311: }
0312: if (buf == null) {
0313: // Existing buffer not found.
0314: buf = WebBuffer.createWebBuffer(file,
0315: null, null);
0316: }
0317: }
0318: }
0319: }
0320: }
0321: if (buf == null)
0322: buf = editor.openFile(file);
0323: }
0324: Editor.setCurrentEditor(editor);
0325: if (buf != null && buf != editor.getBuffer()) {
0326: editor.makeNext(buf);
0327: editor.switchToBuffer(buf);
0328: }
0329: if (Editor.getEditorList().contains(editor)) {
0330: editor.ensureActive();
0331: editor.setFocusToDisplay();
0332: editor.updateLocation();
0333: editor.updateDisplay();
0334: }
0335: }
0336:
0337: private void saveHistory() {
0338: final History history = textField.getHistory();
0339: if (history != null) {
0340: String entry = textField.getText().trim();
0341: if (entry.length() > 0) {
0342: history.append(entry);
0343: history.save();
0344: }
0345: }
0346: }
0347:
0348: public void escape() {
0349: if (popup != null) {
0350: Debug.bug();
0351: popup.setVisible(false);
0352: popup = null;
0353: }
0354: Object owner = textField.getOwner();
0355: if (owner instanceof OpenFileDialog) {
0356: OpenFileDialog dialog = (OpenFileDialog) owner;
0357: dialog.cancel();
0358: } else {
0359: // Using location bar.
0360: editor.setFocusToDisplay();
0361: editor.updateLocation();
0362: editor.ensureActive();
0363: }
0364: }
0365:
0366: public boolean wantTab() {
0367: return true;
0368: }
0369:
0370: public void tab() {
0371: final String entry = textField.getText();
0372: if (entry.startsWith("http:") || entry.startsWith("https:")
0373: || entry.startsWith("ftp:"))
0374: return;
0375: final File dir = editor.getCompletionDirectory();
0376: if (dir == null)
0377: return;
0378: String prefix = null;
0379: if (Utilities.isFilenameAbsolute(entry)
0380: || entry.startsWith("..")) {
0381: File file = File.getInstance(dir, entry);
0382: if (file != null) {
0383: if (file.isRemote())
0384: prefix = file.netPath();
0385: else if (dir.isRemote())
0386: prefix = file.netPath();
0387: else
0388: prefix = file.canonicalPath();
0389: if (entry.endsWith(LocalFile.getSeparator()))
0390: prefix = prefix.concat(LocalFile.getSeparator());
0391: }
0392: } else
0393: prefix = entry;
0394: if (prefix == null)
0395: return;
0396: editor.setWaitCursor();
0397: final boolean showCompletionList;
0398: if (textField.getOwner() instanceof OpenFileDialog) {
0399: showCompletionList = false;
0400: } else {
0401: showCompletionList = Editor.preferences()
0402: .getBooleanProperty(Property.SHOW_COMPLETION_LIST);
0403: }
0404: if (showCompletionList) {
0405: if (popup == null) {
0406: long start = System.currentTimeMillis();
0407: completions = getCompletions(prefix);
0408: long elapsed = System.currentTimeMillis() - start;
0409: Log.debug("getCompletions " + elapsed + " ms "
0410: + completions.size() + " completions");
0411: index = 0;
0412: originalText = textField.getText();
0413: originalPrefix = prefix;
0414: if (completions.size() == 1) {
0415: String s = (String) completions.get(0);
0416: textField.setText(s);
0417: Runnable r = new Runnable() {
0418: public void run() {
0419: textField.setCaretPosition(textField
0420: .getText().length());
0421: }
0422: };
0423: SwingUtilities.invokeLater(r);
0424: } else if (completions.size() > 1)
0425: showCompletionsPopup();
0426: } else
0427: tabPopup(+1, true);
0428: } else {
0429: // No completion list.
0430: while (true) {
0431: String s = getCompletion(prefix);
0432: if (s == null)
0433: break;
0434: if (s.equals(entry)) {
0435: // Only one possible completion. Accept it and continue.
0436: prefix = entry;
0437: reset();
0438: File file = File.getInstance(dir, entry);
0439: if (file != null && file.isDirectory())
0440: continue;
0441: else
0442: break;
0443: }
0444: // More than one possible completion. Present the current one
0445: // and let the user decide what to do next.
0446: textField.setText(s);
0447: textField.setCaretPosition(s.length());
0448: break;
0449: }
0450: }
0451: editor.setDefaultCursor();
0452: }
0453:
0454: public List getCompletions(String prefix) {
0455: final File dir = editor.getCompletionDirectory();
0456: ArrayList completions = new ArrayList();
0457: final String sourcePath = checkSourcePath ? getSourcePath()
0458: : null;
0459: prefix = File.normalize(prefix);
0460: boolean ignoreCase = Platform.isPlatformWindows()
0461: || Editor.preferences().getBooleanProperty(
0462: Property.FILENAME_COMPLETIONS_IGNORE_CASE);
0463: FilenameCompletion completion = new FilenameCompletion(dir,
0464: prefix, sourcePath, ignoreCase);
0465: final File currentDirectory = getCurrentDirectory();
0466: List files = completion.listFiles();
0467: if (files != null) {
0468: for (int i = 0, limit = files.size(); i < limit; i++) {
0469: final File file = (File) files.get(i);
0470: final String name = getNameForFile(file,
0471: currentDirectory);
0472: if (file.isDirectory()) {
0473: addCompletion(completions, name.concat(file
0474: .getSeparator()), ignoreCase);
0475: continue;
0476: }
0477: if (isExcluded(name))
0478: continue;
0479: addCompletion(completions, name, ignoreCase);
0480: }
0481: }
0482: if (checkBuffers && !Utilities.isFilenameAbsolute(prefix)
0483: && prefix.indexOf(LocalFile.getSeparatorChar()) < 0) {
0484: // Short name.
0485: addCompletionsFromBufferList(completions, prefix,
0486: currentDirectory, ignoreCase);
0487: }
0488: return completions;
0489: }
0490:
0491: private void addCompletionsFromBufferList(List list, String prefix,
0492: File currentDirectory, boolean ignoreCase) {
0493: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
0494: Buffer buf = it.nextBuffer();
0495: if (buf.getType() != Buffer.TYPE_NORMAL)
0496: continue;
0497: if (buf == editor.getBuffer())
0498: continue;
0499: File file = buf.getFile();
0500: if (file != null) {
0501: boolean isMatch = false;
0502: if (ignoreCase)
0503: isMatch = file.getName().regionMatches(true, 0,
0504: prefix, 0, prefix.length());
0505: else
0506: isMatch = file.getName().startsWith(prefix);
0507: if (isMatch)
0508: addCompletion(list, getNameForFile(file,
0509: currentDirectory), ignoreCase);
0510: }
0511: }
0512: }
0513:
0514: // Returns file.netPath(), file.canonicalPath(), or file.getName(),
0515: // depending on the situation.
0516: private String getNameForFile(File file, File currentDirectory) {
0517: String name;
0518: if (currentDirectory != null) {
0519: if (currentDirectory.isLocal()) {
0520: if (file.isRemote())
0521: name = file.netPath();
0522: else if (currentDirectory.equals(file.getParentFile()))
0523: name = file.getName();
0524: else
0525: name = file.canonicalPath();
0526: } else {
0527: // Current directory is remote. There might be local as well as
0528: // remote completions, so we need to use the net path.
0529: name = file.netPath();
0530: }
0531: } else {
0532: if (file.isRemote())
0533: name = file.netPath();
0534: else
0535: name = file.canonicalPath();
0536: }
0537: return name;
0538: }
0539:
0540: // Add string to list if it's not already there.
0541: private void addCompletion(List list, String s, boolean ignoreCase) {
0542: if (s != null) {
0543: for (int i = list.size(); i-- > 0;) {
0544: if (ignoreCase) {
0545: if (s.equalsIgnoreCase((String) list.get(i)))
0546: return;
0547: } else if (s.equals((String) list.get(i)))
0548: return;
0549: }
0550: // Didn't find it.
0551: list.add(s);
0552: }
0553: }
0554:
0555: private boolean isExcluded(String pathname) {
0556: final int length = pathname.length();
0557: if (length > 0) {
0558: if (pathname.charAt(length - 1) == '~')
0559: return true;
0560: }
0561: String extension = Utilities.getExtension(pathname);
0562: if (Platform.isPlatformWindows())
0563: extension = extension.toLowerCase();
0564: if (extension.equals(".class") || extension.equals(".cls")
0565: || extension.equals(".abcl")) {
0566: return true;
0567: }
0568: if (Platform.isPlatformWindows()) {
0569: if (extension.equals(".obj") || extension.equals(".exe")) {
0570: return true;
0571: }
0572: }
0573: return false;
0574: }
0575:
0576: // Returns null if there is no file associated with the current buffer.
0577: private File getCurrentDirectory() {
0578: File file = editor.getBuffer().getFile();
0579: if (file == null)
0580: return null;
0581: if (file.isDirectory())
0582: return file;
0583: return file.getParentFile();
0584: }
0585:
0586: private String getSourcePath() {
0587: ArrayList dirs = new ArrayList();
0588: // We want to search the mode-specific source path first.
0589: String sourcePathForMode = editor.getBuffer()
0590: .getStringProperty(Property.SOURCE_PATH);
0591: if (sourcePathForMode != null)
0592: dirs.addAll(Utilities
0593: .getDirectoriesInPath(sourcePathForMode));
0594: // Append any additional directories from the global source path.
0595: String globalSourcePath = Editor.preferences()
0596: .getStringProperty(Property.SOURCE_PATH);
0597: if (globalSourcePath != null) {
0598: List list = Utilities
0599: .getDirectoriesInPath(globalSourcePath);
0600: for (int i = 0; i < list.size(); i++) {
0601: String s = (String) list.get(i);
0602: if (!dirs.contains(s))
0603: dirs.add(s);
0604: }
0605: }
0606: // Reconstruct source path string.
0607: FastStringBuffer sb = new FastStringBuffer();
0608: for (int i = 0; i < dirs.size(); i++) {
0609: sb.append((String) dirs.get(i));
0610: sb.append(LocalFile.getPathSeparatorChar());
0611: }
0612: // Remove extra path separator at end of string.
0613: if (sb.length() > 0)
0614: sb.setLength(sb.length() - 1);
0615: return sb.toString();
0616: }
0617:
0618: private final void error(String message) {
0619: MessageDialog.showMessageDialog(editor, message, title);
0620: editor.setFocusToDisplay();
0621: editor.updateLocation();
0622: }
0623:
0624: private void showCompletionsPopup() {
0625: String[] array = new String[completions.size()];
0626: completions.toArray(array);
0627: popup = new JPopupMenu();
0628: popup.add(new CompletionsList(array));
0629: popup.show(textField, 0, textField.getHeight());
0630: final String completion = (String) completions.get(0);
0631: Runnable r = new Runnable() {
0632: public void run() {
0633: updateTextField(completion);
0634: }
0635: };
0636: SwingUtilities.invokeLater(r);
0637: }
0638:
0639: private void tabPopup(int n, boolean wrap) {
0640: int count = listbox.getModel().getSize();
0641: if (count == 0) {
0642: Debug.bug();
0643: return;
0644: }
0645: int index = listbox.getSelectedIndex();
0646: int i = index + n;
0647: if (wrap) {
0648: if (i >= count)
0649: i = 0;
0650: else if (i < 0)
0651: i = count - 1;
0652: } else {
0653: if (i >= count || i < 0)
0654: i = index;
0655: }
0656: if (i != index) {
0657: listbox.setSelectedIndex(i);
0658: listbox.ensureIndexIsVisible(i);
0659: String completion = (String) listbox.getSelectedValue();
0660: updateTextField(completion);
0661: }
0662: }
0663:
0664: private void updateTextField(String completion) {
0665: if (completion == null)
0666: return;
0667: textField.setText(completion);
0668: if (Editor.preferences().getBooleanProperty(
0669: Property.SELECT_COMPLETION)) {
0670: if (originalText != null && originalText.length() > 0) {
0671: boolean ignoreCase = Editor
0672: .preferences()
0673: .getBooleanProperty(
0674: Property.FILENAME_COMPLETIONS_IGNORE_CASE);
0675: boolean select = completion.regionMatches(ignoreCase,
0676: 0, originalPrefix, 0, originalPrefix.length());
0677: if (select) {
0678: textField.setCaretPosition(originalPrefix.length());
0679: textField.moveCaretPosition(completion.length());
0680: textField.getCaret().setVisible(false);
0681: } else {
0682: char c = originalText.charAt(0);
0683: if (c == '/' || c == '\\') {
0684: int index;
0685: if (ignoreCase) {
0686: index = completion.toLowerCase()
0687: .lastIndexOf(
0688: originalText.toLowerCase());
0689: } else {
0690: index = completion
0691: .lastIndexOf(originalText);
0692: }
0693: if (index >= 0) {
0694: textField.setCaretPosition(index
0695: + originalText.length());
0696: textField.moveCaretPosition(completion
0697: .length());
0698: textField.getCaret().setVisible(false);
0699: }
0700: } else {
0701: RE re = new UncheckedRE("[\\/]"
0702: .concat(originalText),
0703: ignoreCase ? RE.REG_ICASE : 0);
0704: REMatch lastMatch = null;
0705: int index = 0;
0706: while (true) {
0707: REMatch match = re.getMatch(completion,
0708: index);
0709: if (match != null) {
0710: lastMatch = match;
0711: index = match.getEndIndex();
0712: } else
0713: break;
0714: }
0715: if (lastMatch != null) {
0716: textField.setCaretPosition(index);
0717: textField.moveCaretPosition(completion
0718: .length());
0719: textField.getCaret().setVisible(false);
0720: }
0721: }
0722: }
0723: }
0724: }
0725: }
0726:
0727: private void enterPopup() {
0728: popup.setVisible(false);
0729: popup = null;
0730: File file = File.getInstance(editor.getCompletionDirectory(),
0731: textField.getText());
0732: if (file == null || file.isDirectory()) {
0733: textField.requestFocus();
0734: end();
0735: } else {
0736: editor.repaintNow();
0737: enter();
0738: }
0739: }
0740:
0741: private void end() {
0742: Runnable r = new Runnable() {
0743: public void run() {
0744: textField
0745: .setCaretPosition(textField.getText().length());
0746: textField.getCaret().setVisible(true);
0747: }
0748: };
0749: SwingUtilities.invokeLater(r);
0750: }
0751:
0752: private void left() {
0753: reset();
0754: final int pos;
0755: final int start = textField.getSelectionStart();
0756: if (start != textField.getSelectionEnd())
0757: pos = start;
0758: else
0759: pos = Math.max(0, textField.getCaretPosition() - 1);
0760: textField.requestFocus();
0761: Runnable r = new Runnable() {
0762: public void run() {
0763: textField.setCaretPosition(pos);
0764: textField.getCaret().setVisible(true);
0765: }
0766: };
0767: SwingUtilities.invokeLater(r);
0768: }
0769:
0770: protected void reset() {
0771: if (popup != null) {
0772: popup.setVisible(false);
0773: popup = null;
0774: }
0775: super .reset();
0776: }
0777:
0778: private class CompletionsList extends JScrollPane implements
0779: MenuElement, MouseListener {
0780: public CompletionsList(String[] completions) {
0781: super (listbox = new JList(completions));
0782: listbox.setFont(textField.getFont());
0783: if (completions.length < 8)
0784: listbox.setVisibleRowCount(completions.length);
0785: listbox.setFocusTraversalKeysEnabled(false);
0786: listbox.setSelectedIndex(0);
0787: listbox.addMouseListener(this );
0788: }
0789:
0790: public void processMouseEvent(MouseEvent e, MenuElement[] path,
0791: MenuSelectionManager manager) {
0792: }
0793:
0794: public void processKeyEvent(KeyEvent e, MenuElement[] path,
0795: MenuSelectionManager manager) {
0796: final int keyCode = e.getKeyCode();
0797: final int modifiers = e.getModifiers();
0798: final int id = e.getID();
0799: if (id == KeyEvent.KEY_PRESSED) {
0800: switch (keyCode) {
0801: case KeyEvent.VK_TAB:
0802: if (modifiers == 0)
0803: tabPopup(+1, true);
0804: else if (modifiers == SHIFT_MASK)
0805: tabPopup(-1, true);
0806: e.consume();
0807: return;
0808: case KeyEvent.VK_ENTER: {
0809: enterPopup();
0810: e.consume();
0811: return;
0812: }
0813: case KeyEvent.VK_DELETE:
0814: case KeyEvent.VK_ESCAPE: {
0815: popup.setVisible(false);
0816: popup = null;
0817: textField.setText(originalText);
0818: originalText = null;
0819: originalPrefix = null;
0820: textField.requestFocus();
0821: end();
0822: e.consume();
0823: return;
0824: }
0825: case KeyEvent.VK_UP:
0826: case KeyEvent.VK_KP_UP:
0827: tabPopup(-1, false);
0828: e.consume();
0829: break;
0830: case KeyEvent.VK_DOWN:
0831: case KeyEvent.VK_KP_DOWN:
0832: tabPopup(+1, false);
0833: e.consume();
0834: break;
0835: case KeyEvent.VK_LEFT:
0836: case KeyEvent.VK_KP_LEFT:
0837: left();
0838: e.consume();
0839: return;
0840: case KeyEvent.VK_END:
0841: case KeyEvent.VK_RIGHT:
0842: case KeyEvent.VK_KP_RIGHT:
0843: reset();
0844: originalText = null;
0845: originalPrefix = null;
0846: textField.requestFocus();
0847: end();
0848: e.consume();
0849: return;
0850: case KeyEvent.VK_SHIFT:
0851: break;
0852: default:
0853: break;
0854: }
0855: } else if (id == KeyEvent.KEY_TYPED) {
0856: // Forward event to textfield.
0857: keyTyped(e);
0858: }
0859: super .processKeyEvent(e);
0860: }
0861:
0862: public void menuSelectionChanged(boolean isIncluded) {
0863: }
0864:
0865: public MenuElement[] getSubElements() {
0866: return new MenuElement[0];
0867: }
0868:
0869: public Component getComponent() {
0870: return this ;
0871: }
0872:
0873: public void mouseClicked(MouseEvent e) {
0874: enterPopup();
0875: }
0876:
0877: public void mousePressed(MouseEvent e) {
0878: // Mask off the bits we don't care about (Java 1.4).
0879: int modifiers = e.getModifiers() & 0x1f;
0880: if (modifiers == InputEvent.BUTTON1_MASK
0881: || modifiers == InputEvent.BUTTON2_MASK) {
0882: listbox.setSelectedIndex(listbox.locationToIndex(e
0883: .getPoint()));
0884: String s = (String) listbox.getSelectedValue();
0885: textField.setText(s);
0886: }
0887: }
0888:
0889: public void mouseReleased(MouseEvent e) {
0890: }
0891:
0892: public void mouseEntered(MouseEvent e) {
0893: }
0894:
0895: public void mouseExited(MouseEvent e) {
0896: }
0897: }
0898:
0899: public void keyPressed(KeyEvent e) {
0900: if (popup != null) {
0901: int modifiers = e.getModifiers();
0902: switch (e.getKeyCode()) {
0903: case KeyEvent.VK_ENTER:
0904: enterPopup();
0905: e.consume();
0906: return;
0907: case KeyEvent.VK_ESCAPE:
0908: popup.setVisible(false);
0909: popup = null;
0910: textField.setText(originalText);
0911: originalText = null;
0912: originalPrefix = null;
0913: textField.requestFocus();
0914: e.consume();
0915: return;
0916: case KeyEvent.VK_TAB:
0917: if (modifiers == 0)
0918: tabPopup(+1, true);
0919: else if (modifiers == SHIFT_MASK)
0920: tabPopup(-1, true);
0921: e.consume();
0922: return;
0923: case KeyEvent.VK_UP:
0924: case KeyEvent.VK_KP_UP:
0925: if (modifiers == 0) {
0926: tabPopup(-1, false);
0927: e.consume();
0928: return;
0929: }
0930: break;
0931: case KeyEvent.VK_DOWN:
0932: case KeyEvent.VK_KP_DOWN:
0933: if (modifiers == 0) {
0934: tabPopup(+1, false);
0935: e.consume();
0936: return;
0937: }
0938: break;
0939: case KeyEvent.VK_RIGHT:
0940: case KeyEvent.VK_KP_RIGHT:
0941: case KeyEvent.VK_END:
0942: textField.getCaret().setVisible(true);
0943: break;
0944: default:
0945: break;
0946: }
0947: } else {
0948: switch (e.getKeyCode()) {
0949: case KeyEvent.VK_LEFT:
0950: case KeyEvent.VK_KP_LEFT:
0951: case KeyEvent.VK_RIGHT:
0952: case KeyEvent.VK_KP_RIGHT:
0953: textField.getCaret().setVisible(true);
0954: originalText = null;
0955: originalPrefix = null;
0956: break;
0957: }
0958: }
0959: super .keyPressed(e);
0960: }
0961:
0962: public void keyTyped(KeyEvent e) {
0963: char c = e.getKeyChar();
0964: if (c == 8) {
0965: // backspace
0966: textField.getCaret().setVisible(true);
0967: return;
0968: }
0969: if ((e.getModifiers() & (ALT_MASK | CTRL_MASK | META_MASK)) != 0) {
0970: e.consume();
0971: return;
0972: }
0973: if (c >= ' ' && c != 127) {
0974: if (popup != null) {
0975: popup.setVisible(false);
0976: popup = null;
0977: }
0978: String text = textField.getText();
0979: if (textField.getSelectionStart() != textField
0980: .getSelectionEnd()) {
0981: if (originalText != null) {
0982: text = originalText;
0983: originalText = null;
0984: originalPrefix = null;
0985: } else {
0986: FastStringBuffer sb = new FastStringBuffer(
0987: text.substring(0, textField
0988: .getSelectionStart()));
0989: sb.append(text.substring(textField
0990: .getSelectionEnd()));
0991: text = sb.toString();
0992: textField.setCaretPosition(textField
0993: .getSelectionStart());
0994: }
0995: }
0996: // Insert (or append) typed char.
0997: final int pos = Math.min(textField.getCaretPosition(), text
0998: .length());
0999: FastStringBuffer sb = new FastStringBuffer(text.substring(
1000: 0, pos));
1001: sb.append(c);
1002: if (pos < text.length())
1003: sb.append(text.substring(pos));
1004: textField.setText(sb.toString());
1005: textField.requestFocus();
1006: Runnable r = new Runnable() {
1007: public void run() {
1008: int caretPos;
1009: final String s = textField.getText();
1010: if (s != null)
1011: caretPos = Math.min(pos + 1, s.length());
1012: else
1013: caretPos = 0;
1014: textField.setCaretPosition(caretPos);
1015: textField.getCaret().setVisible(true);
1016: }
1017: };
1018: SwingUtilities.invokeLater(r);
1019: }
1020: e.consume();
1021: }
1022:
1023: public void mousePressed(MouseEvent e) {
1024: Editor.setCurrentEditor(editor);
1025: originalText = null;
1026: originalPrefix = null;
1027: }
1028:
1029: public void mouseReleased(MouseEvent e) {
1030: }
1031:
1032: public void mouseClicked(MouseEvent e) {
1033: }
1034:
1035: public void mouseEntered(MouseEvent e) {
1036: }
1037:
1038: public void mouseExited(MouseEvent e) {
1039: }
1040: }
|