1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.commons.gui.completion;
21
22 import static org.xnap.commons.gui.completion.CompletionModeFactory.I18N;
23 import java.awt.event.InputEvent;
24 import java.awt.event.KeyAdapter;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.KeyListener;
27 import javax.swing.KeyStroke;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.xnap.commons.gui.shortcut.DefaultShortcut;
31 import org.xnap.commons.gui.shortcut.ShortcutManager;
32
33
34 /***
35 * This completion mode mimics Emacs' <code>(dabbrev-expand)</code>.
36 *
37 * The keys triggering the forward/backward completion can be configured
38 * in the {@link ShortcutManager} framework.
39 *
40 * @author Felix Berger
41 */
42 public class EmacsCompletionMode extends AbstractCompletionMode
43 {
44 public static final DefaultShortcut SHORTCUT_FORWARD = new DefaultShortcut
45 (KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK),
46 I18N.tr("Completion"), I18N.tr("Emacs Cycle Forward"));
47
48
49 public static final DefaultShortcut SHORTCUT_BACKWARD = new DefaultShortcut
50 (KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK),
51 I18N.tr("Completion"), I18N.tr("Emacs Cycle Backward"));
52
53 private static Log logger = LogFactory.getLog(EmacsCompletionMode.class);
54
55 /***
56 * Holds the key listener used for receiving the mode's trigger key events.
57 */
58 private KeyListener listener = new KeyHandler();
59 /***
60 * The current set of possible completions for the input which can be
61 * traversed.
62 */
63 private Object[] matches = null;
64 /***
65 * The index of the currently shown completion.
66 */
67 private int index = 0;
68 /***
69 * The original string which triggered the completion.
70 */
71 private String orig = null;
72
73 public void disable()
74 {
75 getTextComponent().removeKeyListener(listener);
76 }
77
78 protected void enable()
79 {
80 getTextComponent().addKeyListener(listener);
81 }
82
83 private Object[] getMatches()
84 {
85 int size = getModel().getSize();
86 Object[] matches = new Object[size];
87
88 for (int i = 0; i < size; i++) {
89 matches[i] = getModel().getElementAt(i);
90 }
91
92 return matches;
93 }
94
95 private class KeyHandler extends KeyAdapter
96 {
97 public void keyPressed(KeyEvent e)
98 {
99 if (SHORTCUT_FORWARD.getKeyStroke() != null
100 && SHORTCUT_FORWARD.getKeyStroke().equals
101 (KeyStroke.getKeyStrokeForEvent(e))) {
102
103 if (matches != null) {
104 if ((index + 1) == matches.length) {
105 setText(orig);
106 getTextComponent().getToolkit().beep();
107 matches = null;
108 }
109 else {
110 setText(matches[++index].toString());
111 }
112 }
113 else if (getModel().complete(getText())) {
114 orig = getText();
115 matches = getMatches();
116 index = 0;
117 setText(matches[0].toString());
118 }
119 e.consume();
120 }
121 else if (SHORTCUT_BACKWARD.getKeyStroke() != null
122 && SHORTCUT_BACKWARD.getKeyStroke().equals
123 (KeyStroke.getKeyStrokeForEvent(e))) {
124
125 if (matches != null) {
126 if (index > 0) {
127 setText(matches[--index].toString());
128 }
129 else {
130 setText(orig);
131 matches = null;
132 }
133 }
134 e.consume();
135 }
136 else if (e.getKeyCode() != KeyEvent.VK_ALT
137 && e.getKeyCode() != KeyEvent.VK_CONTROL
138 && e.getKeyCode() != KeyEvent.VK_SHIFT
139 && e.getKeyCode() != KeyEvent.VK_META) {
140 matches = null;
141 }
142 }
143 }
144 }