View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      //--- Constant(s) ---
41  
42      //--- Data field(s) ---
43  
44      private CompletionModel model;
45      private JTextComponent jtc;
46      private boolean wholeText;
47  	private boolean enabled = false;
48  
49      //--- Constructor(s) ---
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      //--- Method(s) ---
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 			// remove listeners
159 			disable();
160 			this.jtc = jtc;
161 			// add listeners
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                 // TODO Auto-generated catch block
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 	// TODO
234 	// FIX
235 	private String getPreviousWord()
236 	{
237 		int start = jtc.getCaretPosition() + 1;
238 		// System.out.println("current pos " + start);
239 		try {
240 			start = Utilities.getPreviousWord(jtc, start);
241 			// System.out.println("start " + start);
242 			String ret = jtc.getText(start, jtc.getCaretPosition() - start + 1);
243 			// System.out.println(ret);
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 }