1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.xnap.gui.component;
20
21 import org.apache.log4j.Logger;
22
23 import javax.swing.text.BadLocationException;
24 import javax.swing.text.JTextComponent;
25 import javax.swing.text.Utilities;
26
27
28 /***
29 * This class handles the way completion is actually done.
30 *
31 * It decides when and how to offer completion to the user. Completion may be
32 * triggered using a special key, it may be visible all the time, it may use a
33 * {@link org.xnap.gui.component.CompletionPopup} to present all possibilities
34 * or may insert the text directly.
35 */
36 public abstract class CompletionMode
37 {
38 private static Logger logger = Logger.getLogger(CompletionMode.class);
39
40
41
42
43
44 private CompletionModel model;
45 private JTextComponent jtc;
46 private boolean wholeText;
47 private boolean enabled = false;
48
49
50
51 public CompletionMode(JTextComponent textComponent, CompletionModel model,
52 boolean wholeText)
53 {
54 this.model = model;
55 this.jtc = textComponent;
56 this.wholeText = wholeText;
57 }
58
59 public CompletionMode(JTextComponent textComponent, boolean wholeText)
60 {
61 this(textComponent, new DefaultCompletionModel(), wholeText);
62 }
63
64 public CompletionMode(JTextComponent textComponent, CompletionModel model)
65 {
66 this(textComponent, model, true);
67 }
68
69 public CompletionMode(JTextComponent textComponent)
70 {
71 this(textComponent, true);
72 }
73
74
75
76 /***
77 * Returns the name of this completion mode.
78 *
79 * This should be localized since it's shown to the user.
80 */
81 public abstract String getName();
82 /***
83 * Enables the completion mode letting it add its listeners to the text
84 * component.
85 */
86 protected abstract void enable();
87 /***
88 * Disables the completion mode letting it remove its listeners from the
89 * text component.
90 */
91 protected abstract void disable();
92
93 /***
94 * Enables or disables the completion mode.
95 *
96 * @param enabled true to enable the completion mode, false to disable it
97 */
98 public final void setEnabled(boolean enabled)
99 {
100 this.enabled = enabled;
101 if (enabled) {
102 enable();
103 }
104 else {
105 disable();
106 }
107 }
108
109 /***
110 * Returns true if the completion mode is enabled.
111 */
112 public final boolean isEnabled()
113 {
114 return enabled;
115 }
116
117 /***
118 * Sets the completion model.
119 *
120 * If the completion mode is currently enabled, it will be disabled before
121 * the model is set and enabled afterwards. Thus subclasses have a chance
122 * of being notified of the model change in their {@link #disable()}
123 * {@link #enable()} methods.
124 *
125 * @param model the new completion model which will be used henceforth
126 */
127 public void setModel(CompletionModel model)
128 {
129 if (isEnabled()) {
130 disable();
131 this.model = model;
132 enable();
133 }
134 else {
135 this.model = model;
136 }
137 }
138
139 /***
140 * Returns the currently used model.
141 */
142 public CompletionModel getModel()
143 {
144 return model;
145 }
146
147 /***
148 * Sets a new text component for this completion mode.
149 *
150 * If the completion mode is currently enabled {@link disable()} and
151 * {@link enable()} are called to give listeners a chance to remove
152 * themselves from the old text component add themselves to the new text
153 * component.
154 */
155 public void setTextComponent(JTextComponent jtc)
156 {
157 if (isEnabled()) {
158
159 disable();
160 this.jtc = jtc;
161
162 enable();
163 }
164 else {
165 this.jtc = jtc;
166 }
167 }
168
169 /***
170 * Returns the text component this completion mode is responsible for
171 */
172 public JTextComponent getTextComponent()
173 {
174 return jtc;
175 }
176
177 /***
178 * Returns whether this completion mode is supposed to complete the whole
179 * text of its text component made availabe through {@link
180 * JTextComponent#getText()} or just the last word before the cursor.
181 */
182 public final boolean isWholeTextCompletion()
183 {
184 return wholeText;
185 }
186
187 /***
188 * Convenience wrapper for {@link setText(String, int, int)}.
189 *
190 * Sets the text without any selection and setts the cursor to the end
191 * of the set text.
192 *
193 * @param text the text to set
194 */
195 protected void setText(String text)
196 {
197 setText(text, text.length(), text.length());
198 }
199
200 /***
201 * Sets the given text honoring the whole text mode.
202 *
203 * @param text the text to set
204 * @param selectionStart the offset of the selection start relative to the
205 * beginning of the set text. Can be greater than <code>selectionEnd</code>.
206 * @param selectionEnd the offset of the selection end relative
207 * to the beginning of the set text. This is where the cursor is afterwards.
208 */
209 protected void setText(String text, int selectionStart,
210 int selectionEnd)
211 {
212 if (isWholeTextCompletion()) {
213 jtc.setText(text);
214 jtc.setCaretPosition(selectionStart);
215 jtc.moveCaretPosition(selectionEnd);
216 }
217 else {
218 int offs = -1;
219 try {
220 offs = Utilities.getPreviousWord(jtc, jtc.getCaretPosition());
221 }
222 catch (BadLocationException e) {
223
224 e.printStackTrace();
225 }
226 jtc.moveCaretPosition(offs);
227 jtc.replaceSelection(text);
228 jtc.setCaretPosition(offs + selectionStart);
229 jtc.moveCaretPosition(offs + selectionEnd);
230 }
231 }
232
233
234
235 private String getPreviousWord()
236 {
237 int start = jtc.getCaretPosition() + 1;
238
239 try {
240 start = Utilities.getPreviousWord(jtc, start);
241
242 String ret = jtc.getText(start, jtc.getCaretPosition() - start + 1);
243
244 return ret;
245 }
246 catch (BadLocationException ble) {
247 return "";
248 }
249 }
250
251 /***
252 * Returns the text which should be completed.
253 *
254 * @return Returns {@link JTextComponent#getText()} if wholeText is true
255 * otherwise the word before the cursor.
256 */
257 protected String getText()
258 {
259 if (wholeText) {
260 return jtc.getText();
261 }
262 else {
263 return getPreviousWord();
264 }
265 }
266 }