svn-gvsig-desktop / trunk / libraries / libUI / src / org / gvsig / gui / beans / swing / jComboBoxItemsSeeker / services / JComboBoxSearchURLOfServices.java @ 11620
History | View | Annotate | Download (17 KB)
1 | 11620 | ppiqueras | package org.gvsig.gui.beans.swing.jComboBoxItemsSeeker.services; |
---|---|---|---|
2 | |||
3 | import java.awt.event.FocusAdapter; |
||
4 | import java.awt.event.FocusEvent; |
||
5 | import java.awt.event.FocusListener; |
||
6 | import java.awt.event.KeyAdapter; |
||
7 | import java.awt.event.KeyEvent; |
||
8 | import java.awt.event.KeyListener; |
||
9 | import java.awt.event.MouseAdapter; |
||
10 | import java.awt.event.MouseEvent; |
||
11 | import java.util.Vector; |
||
12 | |||
13 | import javax.accessibility.Accessible; |
||
14 | import javax.swing.ComboBoxEditor; |
||
15 | import javax.swing.ComboBoxModel; |
||
16 | import javax.swing.JComboBox; |
||
17 | import javax.swing.JList; |
||
18 | import javax.swing.event.PopupMenuListener; |
||
19 | import javax.swing.plaf.basic.BasicComboBoxUI; |
||
20 | import javax.swing.plaf.basic.ComboPopup; |
||
21 | import javax.swing.text.AttributeSet; |
||
22 | import javax.swing.text.BadLocationException; |
||
23 | import javax.swing.text.JTextComponent; |
||
24 | import javax.swing.text.PlainDocument; |
||
25 | |||
26 | import org.gvsig.gui.beans.swing.jComboBoxItemsSeeker.models.DefaultComboBoxModelOrderedSearch; |
||
27 | |||
28 | /* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
|
||
29 | *
|
||
30 | * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
|
||
31 | *
|
||
32 | * This program is free software; you can redistribute it and/or
|
||
33 | * modify it under the terms of the GNU General Public License
|
||
34 | * as published by the Free Software Foundation; either version 2
|
||
35 | * of the License, or (at your option) any later version.
|
||
36 | *
|
||
37 | * This program is distributed in the hope that it will be useful,
|
||
38 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
39 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
40 | * GNU General Public License for more details.
|
||
41 | *
|
||
42 | * You should have received a copy of the GNU General Public License
|
||
43 | * along with this program; if not, write to the Free Software
|
||
44 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
45 | *
|
||
46 | * For more information, contact:
|
||
47 | *
|
||
48 | * Generalitat Valenciana
|
||
49 | * Conselleria d'Infraestructures i Transport
|
||
50 | * Av. Blasco Ib??ez, 50
|
||
51 | * 46010 VALENCIA
|
||
52 | * SPAIN
|
||
53 | *
|
||
54 | * +34 963862235
|
||
55 | * gvsig@gva.es
|
||
56 | * www.gvsig.gva.es
|
||
57 | *
|
||
58 | * or
|
||
59 | *
|
||
60 | * IVER T.I. S.A
|
||
61 | * Salamanca 50
|
||
62 | * 46005 Valencia
|
||
63 | * Spain
|
||
64 | *
|
||
65 | * +34 963163400
|
||
66 | * dac@iver.es
|
||
67 | */
|
||
68 | |||
69 | /**
|
||
70 | * This class will have the necessary behavior for the combo box with URL of services:<br>
|
||
71 | *
|
||
72 | * <ul>
|
||
73 | * <li>All items will be sorted</li>
|
||
74 | * <li>Allows repeated items</li>
|
||
75 | * <li>Finds all items that their first characters are the written by the user</li>
|
||
76 | * <li>If no item has its first characters as the written by the user -> don't show the popup</li>
|
||
77 | * <li>Similar behaviour as the Mozilla Firefox URL combo box</li>
|
||
78 | * </ul>
|
||
79 | *
|
||
80 | * @author Pablo Piqueras Bartolom? (p_queras@hotmail.com)
|
||
81 | */
|
||
82 | public class JComboBoxSearchURLOfServices extends JComboBox implements java.io.Serializable { |
||
83 | private static final long serialVersionUID = -6928118339485676552L; |
||
84 | |||
85 | // EDITOR DOCUMENT REFERENCE
|
||
86 | /**
|
||
87 | * A reference to the document of this component
|
||
88 | */
|
||
89 | private PlainDocumentTextFormatter document;
|
||
90 | // END EDITOR DOCUMENT REFERENCE
|
||
91 | |||
92 | // NEW ATTRIBUTES
|
||
93 | /**
|
||
94 | * A reference to the model of this component (according to the MVC <i>(Model-View-Controller)</i> pattern)
|
||
95 | */
|
||
96 | private DefaultComboBoxModelOrderedSearch model;
|
||
97 | |||
98 | /**
|
||
99 | * Has or not to hide the popup on focus loss
|
||
100 | */
|
||
101 | private boolean hidePopupOnFocusLoss; |
||
102 | |||
103 | /**
|
||
104 | * Has an item of the popup been selected or not by the user
|
||
105 | */
|
||
106 | private boolean popupItemSelected; |
||
107 | // END NEW ATTRIBUTES
|
||
108 | |||
109 | // LISTENERS
|
||
110 | /**
|
||
111 | * Listener for the editor key
|
||
112 | */
|
||
113 | private KeyListener editorKeyListener; |
||
114 | |||
115 | /**
|
||
116 | * Listener for the editor focus
|
||
117 | */
|
||
118 | private FocusListener editorFocusListener; |
||
119 | |||
120 | /**
|
||
121 | * Listener for the popup menu
|
||
122 | */
|
||
123 | private PopupMenuListener popupMenuListener; |
||
124 | // END LISTENERS
|
||
125 | |||
126 | /**
|
||
127 | * Default constructor without parameters
|
||
128 | */
|
||
129 | public JComboBoxSearchURLOfServices() {
|
||
130 | super();
|
||
131 | initialize(); |
||
132 | } |
||
133 | |||
134 | /**
|
||
135 | * Default constructor with a {@link ComboBoxModel} as parameter
|
||
136 | *
|
||
137 | * @param aModel javax.swing.ComboBoxModel
|
||
138 | */
|
||
139 | public JComboBoxSearchURLOfServices(ComboBoxModel aModel) { |
||
140 | super(aModel);
|
||
141 | initialize(); |
||
142 | } |
||
143 | |||
144 | /**
|
||
145 | * Default constructor with an array of objects as parameter
|
||
146 | *
|
||
147 | * @param items An array of objects. All them must implement a <i>'String toStrin()'</i> method
|
||
148 | */
|
||
149 | public JComboBoxSearchURLOfServices(Object[] items) { |
||
150 | super(items);
|
||
151 | initialize(); |
||
152 | } |
||
153 | |||
154 | /**
|
||
155 | * Default constructor with a Vector of objects as parameter
|
||
156 | *
|
||
157 | * @param items A {@link Vector} of objects. All them must implement a <i>'String toStrin()'</i> method
|
||
158 | */
|
||
159 | public JComboBoxSearchURLOfServices(Vector items) { |
||
160 | super(items);
|
||
161 | initialize(); |
||
162 | } |
||
163 | |||
164 | /**
|
||
165 | * This method sets the start values of inner attributes and creates the necessary inner objects
|
||
166 | */
|
||
167 | private void initialize() { |
||
168 | // By default user hasn't selected an item of the popup
|
||
169 | popupItemSelected = false;
|
||
170 | |||
171 | // Creates the model for this component and gets it reference
|
||
172 | model = new DefaultComboBoxModelOrderedSearch();
|
||
173 | super.setModel(model);
|
||
174 | |||
175 | // Allows user to edit on the combobox
|
||
176 | super.setEditable(true); |
||
177 | |||
178 | // Other configuration tasks
|
||
179 | this.configure();
|
||
180 | |||
181 | // If there are items -> select the first
|
||
182 | if (model.getSize() > 0) |
||
183 | model.setSelectedItem(model.getElementAt(0));
|
||
184 | } |
||
185 | |||
186 | /**
|
||
187 | * Configures the component and some of its elements
|
||
188 | */
|
||
189 | private void configure() { |
||
190 | // Defines a key listener for the editor of this component
|
||
191 | this.defineEditorKeyListener(this); |
||
192 | |||
193 | // Defines a focus listener for the editor of this component
|
||
194 | this.defineEditorFocusListener(this); |
||
195 | |||
196 | // Configures the document of the editor of this component
|
||
197 | this.configureDocument();
|
||
198 | |||
199 | // Configures the editor (ComboBoxEditor) of this component
|
||
200 | this.configureEditor(this.getEditor()); |
||
201 | |||
202 | // Configures the popup of this component
|
||
203 | this.configurePopUp(this); |
||
204 | } |
||
205 | |||
206 | /**
|
||
207 | * Configures the editor ( {@link ComboBoxEditor} ) of this component
|
||
208 | *
|
||
209 | * @param newEditor The new editor to configure
|
||
210 | */
|
||
211 | private void configureEditor(ComboBoxEditor newEditor) { |
||
212 | if (newEditor != null) { |
||
213 | JTextComponent jTextComponentOfEditor = (JTextComponent) newEditor.getEditorComponent(); |
||
214 | |||
215 | // Adds the new document (tries to remove it if it existed before)
|
||
216 | jTextComponentOfEditor.setDocument(this.document);
|
||
217 | |||
218 | // Adds the new Key Listener (tries to remove it if it existed before)
|
||
219 | jTextComponentOfEditor.removeKeyListener(this.editorKeyListener);
|
||
220 | jTextComponentOfEditor.addKeyListener(this.editorKeyListener);
|
||
221 | |||
222 | // Adds the new Focus Listener (tries to remove it if it existed before)
|
||
223 | jTextComponentOfEditor.removeFocusListener(this.editorFocusListener);
|
||
224 | jTextComponentOfEditor.addFocusListener(this.editorFocusListener);
|
||
225 | } |
||
226 | } |
||
227 | |||
228 | /**
|
||
229 | * Configures the document of the editor of this component
|
||
230 | */
|
||
231 | private void configureDocument() { |
||
232 | // Creates the document of the editor of this component
|
||
233 | document = new PlainDocumentTextFormatter();
|
||
234 | |||
235 | // Set reference to the container component
|
||
236 | document.setJComboBoxReference(this);
|
||
237 | } |
||
238 | |||
239 | /**
|
||
240 | * Configures the popup of this component
|
||
241 | *
|
||
242 | * @param combo_Box A reference of this component
|
||
243 | */
|
||
244 | private void configurePopUp(JComboBoxSearchURLOfServices combo_Box) { |
||
245 | final JComboBoxSearchURLOfServices comboBoxReference = combo_Box;
|
||
246 | this.addPopupMenuListener(this.popupMenuListener); |
||
247 | |||
248 | BasicComboBoxUI comboBoxUi = (BasicComboBoxUI)comboBoxReference.getUI(); |
||
249 | Accessible a = comboBoxUi.getAccessibleChild(comboBoxReference, 0); |
||
250 | if (a instanceof ComboPopup) { |
||
251 | JList jlist = ((ComboPopup)a).getList(); |
||
252 | |||
253 | jlist.addMouseListener(new MouseAdapter() { |
||
254 | /*
|
||
255 | * (non-Javadoc)
|
||
256 | * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
|
||
257 | */
|
||
258 | public void mousePressed(MouseEvent e) { |
||
259 | // User selects an item of the popup using the mouse
|
||
260 | popupItemSelected = true;
|
||
261 | } |
||
262 | }); |
||
263 | } |
||
264 | } |
||
265 | |||
266 | /**
|
||
267 | * Defines a key listener for the editor of this component
|
||
268 | * This method is another important difference from the JComboBox; and could work badly with some keymaps
|
||
269 | *
|
||
270 | * @param combo_Box A reference of this component
|
||
271 | */
|
||
272 | private void defineEditorKeyListener(JComboBoxSearchURLOfServices combo_Box) { |
||
273 | final JComboBoxSearchURLOfServices comboBoxReference = combo_Box;
|
||
274 | |||
275 | editorKeyListener = new KeyAdapter() { |
||
276 | /*
|
||
277 | * (non-Javadoc)
|
||
278 | * @see java.awt.event.KeyAdapter#keyPressed(java.awt.event.KeyEvent)
|
||
279 | */
|
||
280 | public void keyPressed(KeyEvent ke) // Executed on the Start view state or Search view state |
||
281 | { |
||
282 | // According the key pressed, do some actions or others
|
||
283 | switch (ke.getKeyCode())
|
||
284 | { |
||
285 | case KeyEvent.VK_ENTER : |
||
286 | // Don't allow execute the default instructions because they have a bug (first time we remove some characteres, no item will be displayed in the popup)
|
||
287 | ke.consume(); |
||
288 | |||
289 | // Hide the popup
|
||
290 | hidePopup(); |
||
291 | |||
292 | // Sets the caret position of the text in the document to the end:
|
||
293 | ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(document.getLength());
|
||
294 | break;
|
||
295 | |||
296 | case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: |
||
297 | // User selects an item of the popup using the mouse
|
||
298 | popupItemSelected = true;
|
||
299 | break;
|
||
300 | } |
||
301 | } |
||
302 | }; |
||
303 | } |
||
304 | |||
305 | /**
|
||
306 | * Defines a focus listener for the editor of this component
|
||
307 | *
|
||
308 | * @param combo_Box A reference of this component
|
||
309 | */
|
||
310 | private void defineEditorFocusListener(JComboBoxSearchURLOfServices combo_Box) { |
||
311 | final JComboBoxSearchURLOfServices comboBoxReference = combo_Box;
|
||
312 | |||
313 | // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out
|
||
314 | hidePopupOnFocusLoss=System.getProperty("java.version").startsWith("1.5"); |
||
315 | |||
316 | // Highlight whole text when gaining focus
|
||
317 | editorFocusListener = new FocusAdapter() { |
||
318 | |||
319 | /*
|
||
320 | * (non-Javadoc)
|
||
321 | * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
|
||
322 | */
|
||
323 | public void focusLost(FocusEvent e) { |
||
324 | // Workaround for Bug 5100422 - Hide Popup on focus loss
|
||
325 | if (hidePopupOnFocusLoss)
|
||
326 | comboBoxReference.setPopupVisible(false);
|
||
327 | } |
||
328 | }; |
||
329 | } |
||
330 | |||
331 | /**
|
||
332 | * Inner class that inherits of the class PlainDocument, and is used for manipulate the textWritten that has the document of the editor of this component
|
||
333 | * This class is also optimized for items seek
|
||
334 | *
|
||
335 | * @author Pablo Piqueras Bartolom? (p_queras@hotmail.com)
|
||
336 | */
|
||
337 | private class PlainDocumentTextFormatter extends PlainDocument { |
||
338 | private static final long serialVersionUID = 4158213349939840209L; |
||
339 | private JComboBoxSearchURLOfServices comboBoxReference;
|
||
340 | private String textWritten; |
||
341 | private boolean updatedModel; |
||
342 | private String textOfReplacement; |
||
343 | private int old_caretPosition; |
||
344 | |||
345 | /**
|
||
346 | * Default Constructor
|
||
347 | */
|
||
348 | public PlainDocumentTextFormatter() {
|
||
349 | super();
|
||
350 | this.initialize();
|
||
351 | } |
||
352 | |||
353 | /**
|
||
354 | * This method makes some initialize operations
|
||
355 | */
|
||
356 | private void initialize() { |
||
357 | textWritten = "";
|
||
358 | textOfReplacement = "";
|
||
359 | } |
||
360 | |||
361 | /**
|
||
362 | * Sets a reference of this component
|
||
363 | *
|
||
364 | * @param combo_Box A reference to the class that contains this.
|
||
365 | */
|
||
366 | private void setJComboBoxReference(JComboBoxSearchURLOfServices combo_Box) { |
||
367 | comboBoxReference = combo_Box; |
||
368 | } |
||
369 | |||
370 | /*
|
||
371 | * (non-Javadoc)
|
||
372 | * @see javax.swing.text.Document#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
|
||
373 | */
|
||
374 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { |
||
375 | if (updatedModel) {
|
||
376 | model.setTextWritten(textWritten); |
||
377 | super.insertString(offs, textWritten, a);
|
||
378 | |||
379 | return;
|
||
380 | } |
||
381 | |||
382 | hidePopup(); |
||
383 | |||
384 | if (textWritten.length() > 0) { |
||
385 | if (offs < textWritten.length()) {
|
||
386 | String old_textWritten = textWritten;
|
||
387 | |||
388 | // End of the text
|
||
389 | textWritten = textWritten.substring(offs, textWritten.length()); |
||
390 | |||
391 | // Beginning of the text
|
||
392 | if (offs > 0) { |
||
393 | // Reset (without text written)
|
||
394 | textWritten = old_textWritten.substring(0, offs) + str + textWritten;
|
||
395 | } |
||
396 | else {
|
||
397 | textWritten = str + textWritten; |
||
398 | } |
||
399 | } |
||
400 | else {
|
||
401 | textWritten = textWritten.substring(0, offs) + str;
|
||
402 | } |
||
403 | } |
||
404 | else {
|
||
405 | textWritten = str; |
||
406 | } |
||
407 | |||
408 | model.setTextWritten(textWritten); |
||
409 | |||
410 | super.insertString(offs, str, a);
|
||
411 | |||
412 | // Only show the popup if there are items to show
|
||
413 | if (model.getSize() > 0) { |
||
414 | showPopup(); |
||
415 | } |
||
416 | |||
417 | // Update the caret position -> at the same place it was or at the end
|
||
418 | updateCaretPosition(textWritten.length()); |
||
419 | } |
||
420 | |||
421 | /*
|
||
422 | * (non-Javadoc)
|
||
423 | * @see javax.swing.text.AbstractDocument#replace(int, int, java.lang.String, javax.swing.text.AttributeSet)
|
||
424 | */
|
||
425 | public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { |
||
426 | // Avoid two replaces for the same operation
|
||
427 | if ((text.compareTo("") == 0) && (length == textWritten.length())) |
||
428 | return;
|
||
429 | |||
430 | // Only remove if there is text to be replaced (text selected)
|
||
431 | if (length > 0) { |
||
432 | textOfReplacement = text; |
||
433 | remove(offset, length); |
||
434 | } |
||
435 | else {
|
||
436 | if ((offset > 0) && (offset != textWritten.length())) { |
||
437 | // Must replace the model for update the data in the popup correctly
|
||
438 | String old_textWritten;
|
||
439 | |||
440 | // There is a bug that when text has been removed, the elements in the popup aren seen.
|
||
441 | // The solution to this bug is add them as a new model
|
||
442 | hidePopup(); |
||
443 | |||
444 | updatedModel = true;
|
||
445 | |||
446 | old_textWritten = textWritten; |
||
447 | old_caretPosition = ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getCaretPosition() + text.length();
|
||
448 | |||
449 | textWritten = "";
|
||
450 | model.setTextWritten(textWritten); |
||
451 | |||
452 | super.remove(0, old_textWritten.length()); |
||
453 | |||
454 | // Must replace the model for update the data in the popup correctly
|
||
455 | model = new DefaultComboBoxModelOrderedSearch(model.getData());
|
||
456 | comboBoxReference.setModel(model); |
||
457 | |||
458 | // Reset (without text written)
|
||
459 | insertString(0, textWritten, null); |
||
460 | |||
461 | // Insert the new text
|
||
462 | textWritten = old_textWritten.substring(0, offset) + text + old_textWritten.substring(offset, old_textWritten.length());
|
||
463 | |||
464 | insertString(0, textWritten, null); |
||
465 | |||
466 | updatedModel = false;
|
||
467 | |||
468 | // Update the caret position -> at the same place it was or at the end
|
||
469 | updateCaretPosition(old_caretPosition); |
||
470 | |||
471 | // Only show the popup if there are items to show
|
||
472 | if (model.getSize() > 0) { |
||
473 | showPopup(); |
||
474 | } |
||
475 | } |
||
476 | else {
|
||
477 | // Default replacement
|
||
478 | super.replace(offset, length, text, attrs);
|
||
479 | } |
||
480 | } |
||
481 | } |
||
482 | |||
483 | /*
|
||
484 | * (non-Javadoc)
|
||
485 | * @see javax.swing.text.Document#remove(int, int)
|
||
486 | */
|
||
487 | public void remove(int offs, int len) throws BadLocationException { |
||
488 | String old_textWritten;
|
||
489 | |||
490 | // There is a bug that when text has been removed, the elements in the popup aren seen.
|
||
491 | // The solution to this bug is add them as a new model
|
||
492 | hidePopup(); |
||
493 | |||
494 | updatedModel = true;
|
||
495 | |||
496 | old_textWritten = textWritten; |
||
497 | old_caretPosition = Math.min(((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getCaretPosition(), offs); |
||
498 | |||
499 | textWritten = "";
|
||
500 | model.setTextWritten(textWritten); |
||
501 | |||
502 | super.remove(0, old_textWritten.length()); |
||
503 | |||
504 | // Must replace the model for update the data in the popup correctly
|
||
505 | model = new DefaultComboBoxModelOrderedSearch(model.getData());
|
||
506 | comboBoxReference.setModel(model); |
||
507 | |||
508 | // Reset (without text written)
|
||
509 | insertString(0, textWritten, null); |
||
510 | |||
511 | // Insert the new text
|
||
512 | textWritten = old_textWritten.substring(0, offs) + textOfReplacement + old_textWritten.substring(offs + len, old_textWritten.length());
|
||
513 | textOfReplacement = "";
|
||
514 | |||
515 | insertString(0, textWritten, null); |
||
516 | |||
517 | updatedModel = false;
|
||
518 | |||
519 | // Only show the popup if there are items to show
|
||
520 | if (model.getSize() > 0) { |
||
521 | showPopup(); |
||
522 | } |
||
523 | |||
524 | // Update the caret position -> at the same place it was or at the end
|
||
525 | updateCaretPosition(old_caretPosition); |
||
526 | } |
||
527 | |||
528 | /**
|
||
529 | * Updates the position of the caret in the {@link ComboBoxEditor} of this component.
|
||
530 | *
|
||
531 | * @param position The new position of the caret.
|
||
532 | */
|
||
533 | private void updateCaretPosition(int position) { |
||
534 | // If user has selected an item of the popup (using the mouse) -> set the caret position to the end of the text
|
||
535 | if (popupItemSelected) {
|
||
536 | ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(textWritten.length());
|
||
537 | popupItemSelected = false;
|
||
538 | return;
|
||
539 | } |
||
540 | |||
541 | if (position > textWritten.length())
|
||
542 | ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(textWritten.length());
|
||
543 | else
|
||
544 | ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(position);
|
||
545 | } |
||
546 | } |
||
547 | } |