Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libUI / src / org / gvsig / gui / beans / swing / jComboBoxItemsSeeker / services / JComboBoxSearchURLOfServices.java @ 11620

History | View | Annotate | Download (17 KB)

1
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
}