Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libUIComponent / src / org / gvsig / gui / beans / comboboxconfigurablelookup / JComboBoxConfigurableLookUp.java @ 22758

History | View | Annotate | Download (54.5 KB)

1
package org.gvsig.gui.beans.comboboxconfigurablelookup;
2

    
3
import java.awt.Color;
4
import java.awt.event.FocusAdapter;
5
import java.awt.event.FocusEvent;
6
import java.awt.event.FocusListener;
7
import java.awt.event.KeyAdapter;
8
import java.awt.event.KeyEvent;
9
import java.awt.event.KeyListener;
10
import java.awt.event.MouseAdapter;
11
import java.awt.event.MouseEvent;
12
import java.awt.event.MouseListener;
13
import java.util.Vector;
14

    
15
import javax.accessibility.Accessible;
16
import javax.swing.ComboBoxEditor;
17
import javax.swing.ComboBoxModel;
18
import javax.swing.JButton;
19
import javax.swing.JComboBox;
20
import javax.swing.JList;
21
import javax.swing.MutableComboBoxModel;
22
import javax.swing.event.DocumentListener;
23
import javax.swing.event.PopupMenuEvent;
24
import javax.swing.event.PopupMenuListener;
25
import javax.swing.event.UndoableEditListener;
26
import javax.swing.plaf.ComponentUI;
27
import javax.swing.plaf.basic.BasicComboBoxUI;
28
import javax.swing.plaf.basic.BasicComboPopup;
29
import javax.swing.plaf.basic.ComboPopup;
30
import javax.swing.text.AttributeSet;
31
import javax.swing.text.BadLocationException;
32
import javax.swing.text.JTextComponent;
33
import javax.swing.text.PlainDocument;
34

    
35
import org.gvsig.gui.beans.editabletextcomponent.IEditableText;
36
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditEvent;
37
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener;
38

    
39

    
40
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
41
 *
42
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
43
 *
44
 * This program is free software; you can redistribute it and/or
45
 * modify it under the terms of the GNU General Public License
46
 * as published by the Free Software Foundation; either version 2
47
 * of the License, or (at your option) any later version.
48
 *
49
 * This program is distributed in the hope that it will be useful,
50
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
52
 * GNU General Public License for more details.
53
 *
54
 * You should have received a copy of the GNU General Public License
55
 * along with this program; if not, write to the Free Software
56
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
57
 *
58
 * For more information, contact:
59
 *
60
 *  Generalitat Valenciana
61
 *   Conselleria d'Infraestructures i Transport
62
 *   Av. Blasco Ib??ez, 50
63
 *   46010 VALENCIA
64
 *   SPAIN
65
 *
66
 *      +34 963862235
67
 *   gvsig@gva.es
68
 *      www.gvsig.gva.es
69
 *
70
 *    or
71
 *
72
 *   IVER T.I. S.A
73
 *   Salamanca 50
74
 *   46005 Valencia
75
 *   Spain
76
 *
77
 *   +34 963163400
78
 *   dac@iver.es
79
 */
80

    
81

    
82
/** VERSION: BETA
83
 * <p>A configurable combo box component that lists items that carry out with the criterions of a <code>ILookUp</code> agent,
84
 *  considering the text in its inner editor.</p>
85
 * 
86
 * <p>Key features:
87
 *  <ul>
88
 *   <li>Allows configure it's behaviour.</li>
89
 *          <li>Inherits from JComboBox.</li>
90
 *          <li>Has 6 flags to configure its behaviour.</li>
91
 *          <li>It's made according the MVC (Model-View-Controller) pattern.</li>
92
 *          <li>Uses a {@link DefaultComboBoxConfigurableLookUpModel DefaultComboBoxConfigurableLookUpModel} as a dataModel, which it's also configurable.</li>
93
 *  </ul>
94
 * </p>
95
 * 
96
 * <p>Configuration flags:
97
 *  <ul>
98
 *   <li><b>OnlyOneColorOnText:</b> uses red if there is no item or only items that matches with the text written according the <code>ILookUp</code> agent, or uses always black color.</li>
99
 *   <li><b>BeepEnabled:</b> rings a beep sound if there is no item that matches with the text written according the <code>ILookUp</code> agent, or not.</li>
100
 *   <li><b>HidePopupIfThereAreNoItems:</b> hides popup (list box) if there is no item that matches with the text written according the <code>ILookUp</code> agent, or uses the default (JComboBox and more) behaviour.</li>
101
 *   <li><b>ToForceSelectAnItem:</b> forces to select an item (if the component looses the focus or user presses the Enter keyboard key), or uses the default (JComboBox and more) behaviour.</li>
102
 *   <li><b>CompleteArrowKeySelection:</b> writes in the editor the value of the element selected by the arrow keys (up or down) if its enabled, otherwise holds the previous text.</li>
103
 *   <li><b>DisplayAllItemsWithArrowButton:</b> <i>true</i>: displays all this component's dataModel items if its <i>popup</i> it's hided, and this component's dataModel listed only the matches
104
 *    <i>(flag: <code>DefaultComboBoxConfigurableLookUpModel.showAllItemsInListBox == false</code>)</i>; otherwise, if this flag isn't enabled,
105
 *    won't display all items in that situation.</li>
106
 *  </ul>
107
 * </p>
108
 * 
109
 * <p>Default flag values are:
110
 *  <ul>
111
 *   <li><b>OnlyOneColorOnText:</b> <i>false</i>.</li>
112
 *   <li><b>BeepEnabled:</b> <i>false</i>.</li>
113
 *   <li><b>HidePopupIfThereAreNoItems:</b> <i>true</i>.</li>
114
 *   <li><b>ToForceSelectAnItem:</b> <i>true</i>.</li>
115
 *   <li><b>CompleteArrowKeySelection:</b> <i>false</i>.</li>
116
 *   <li><b>DisplayAllItemsWithArrowButton:</b> <i>true</i>.</li>
117
 *  </ul>
118
 * </p>
119
 * 
120
 * <p><i>More information about the behaviour of it's dataModel in {@link DefaultComboBoxConfigurableLookUpModel DefaultComboBoxConfigurableLookUpModel} .</i></p>
121
 * 
122
 * @see JComboBox
123
 * @see DefaultComboBoxConfigurableLookUpModel
124
 * 
125
 * @author Pablo Piqueras Bartolom? (pablo.piqueras@iver.es)
126
 * @version 07/02/2008
127
 */
128
public class JComboBoxConfigurableLookUp extends JComboBox implements java.io.Serializable {
129

    
130

    
131
        // CONSTANTS FOR CONFIGURE THE BEHAVIOR
132
        public static final boolean DEFAULT_ONLY_ONE_COLOR_ON_TEXT_CONFIGURATION = false;
133
        public static final boolean DEFAULT_BEEP_ENABLED_CONFIGURATION = false;
134
        public static final boolean DEFAULT_HIDE_POPUP_IF_THERE_ARE_NO_ITEMS_CONFIGURATION = true;
135
        public static final boolean DEFAULT_TO_FORCE_SELECT_AN_ITEM_CONFIGURATION = true;
136
        public static final boolean DEFAULT_COMPLETE_ARROW_KEY_SELECTION_CONFIGURATION = false;
137
        public static final boolean DEFAULT_DISPLAY_ALL_ITEMS_WITH_ARROW_BUTTON_CONFIGURATION = true;
138
        // END CONSTANTS FOR CONFIGURE THE BEHAVIOR
139
        
140
        // EDITOR DOCUMENT REFERENCE
141
        /**
142
         * <p>A reference to the document of this component.</p> 
143
         */
144
        private PlainDocumentTextFormatter document;
145
        // END EDITOR DOCUMENT REFERENCE
146
        
147
        // NEW ATTRIBUTES 
148
        /**
149
         * <p>A reference to the dataModel of this component (according to the MVC <i>(Model-View-Controller)</i> pattern).</p>
150
         */
151
        private DefaultComboBoxConfigurableLookUpModel model;
152

    
153
        /**
154
         * <p>Has or not to hide the popup on focus loss.</p>
155
         */
156
        private boolean hidePopupOnFocusLoss;
157

    
158
        /**
159
         * <p>Has an item of the popup been selected or not by the user.</p>
160
         */
161
        private boolean popupItemSelected;
162
        
163
        /**
164
         * <p>Determines if an arrow key (meanwhile down or up) has been pressed.</p>
165
         */
166
        private boolean arrowKeyPressed;
167

    
168
        /**
169
         * <p>Determines if the arrow button of the GUI has been clicked.</p>
170
         */
171
        private boolean arrowButtonClicked;
172

    
173
        /**
174
         * <p>Determines if is showing all items temporally in the GUI .</p>
175
         */
176
        private boolean showingAllItemsTemporally;
177

    
178
        /**
179
         * <p>Flag to ensure the we don't get multiple ActionEvents on item selection.</p>
180
         */
181
        private boolean selectingItem = false;
182
        
183
        /**
184
         * <p>Last item selected.</p>
185
         */
186
        private Object previousSelected;
187

    
188
        /**
189
         * <p>Determines if the UI of this component has been updated.</p>
190
         * <p>This parameter is used by the listener that shows all items when user presses the <i>arrow button</i>
191
         *  and its enabled the flag <code>displayAllItemsWithArrowButton</code>.</p>
192
         */
193
        private boolean updatedUI = true;
194
        
195
        private boolean blockPopupHided = false;
196
        // END NEW ATTRIBUTES
197
        
198
        // LISTENERS
199
        /**
200
         * <p>Listener for the editor key.</p> 
201
         */
202
        private KeyListener editorKeyListener;
203
        
204
        /**
205
         * <p>Listener for the editor focus.</p>
206
         */
207
        private FocusListener editorFocusListener;
208
        
209
        /**
210
         * <p>Listener for the popup menu.</p> 
211
         */
212
        private PopupMenuListener popupMenuListener;
213
        
214
        /**
215
         * <p>Listener for the arrow button.</p>
216
         */
217
        private MouseListener arrowMouseListener;
218
        // END LISTENERS
219
        
220
        // CONFIGURATION FLAGS
221
        /**
222
         * <p>Flag: use red if there is no item that matches with the text written according the <code>ILookUp</code> agent, or uses always black colour.</p>
223
         */
224
        private boolean onlyOneColorOnText;
225
        
226
        /**
227
         * <p>Flag: rings a beep sound if there is no item that matches with the text written according the <code>ILookUp</code> agent, otherwise no.</p>
228
         */
229
        private boolean beepEnabled;
230
        
231
        /**
232
         * <p>Flag: hide popup (list box) if there is no item that matches with the text written according the <code>ILookUp</code> agent, or uses the default (JComboBox and more) behaviour.</p>
233
         */
234
        private boolean hidePopupIfThereAreNoItems;
235
        
236
        /**
237
         * <p>Flag: forces to select an item (if the component looses the focus or user presses the Enter keyboard key), or uses the default (JComboBox and more) behaviour.</p>
238
         */
239
        private boolean toForceSelectAnItem;
240

    
241
        /**
242
         * <p>Flag: determines if has to write in the editor the value of the element selected by the 
243
         * arrow keys (up or down) if its enabled, or hold the previous text if isn't.</p>
244
         */
245
        private boolean completeArrowKeySelection;
246
        
247
        /**
248
         * <p>Flag: displays all this component's dataModel items if its <i>popup</i> it's hided, this component's dataModel displayed only the matches, and this flag enabled; if this flag isn't enabled,
249
         *  won't display all items in that situation.</p>
250
         */
251
        private boolean displayAllItemsWithArrowButton;
252
        // END FLAGS
253
        
254
        /**
255
         * <p>Default constructor without parameters.</p>
256
         */
257
        public JComboBoxConfigurableLookUp() {
258
                super();
259
                initialize();
260
        }
261

    
262
        /**
263
         * <p>Default constructor with a {@link DefaultComboBoxConfigurableLookUpModel} as parameter.</p>
264
         * 
265
         * @param aModel javax.swing.ComboBoxModel
266
         */
267
        public JComboBoxConfigurableLookUp(DefaultComboBoxConfigurableLookUpModel aModel) {
268
                super(aModel);
269

    
270
                initialize();
271
        }        
272

    
273
        /**
274
         * <p>Default constructor with an array of objects as parameter.</p>
275
         * 
276
         * @param items An array of objects. All them must implement a <i>'String toStrin()'</i> method
277
         */
278
        public JComboBoxConfigurableLookUp(Object[] items) {
279
                super(items);
280

    
281
                initialize();
282
        }
283

    
284
        /**
285
         * <p>Default constructor with a Vector of objects as parameter.</p>
286
         * 
287
         * @param items A {@link Vector} of objects. All them must implement a <i>'String toStrin()'</i> method
288
         */
289
        public JComboBoxConfigurableLookUp(Vector<Object> items) {
290
                super(items);
291

    
292
                initialize();
293
        }
294
        
295
        ///// NEW METHODS /////
296
        
297
        /**
298
         * <p>This method sets the start values of inner attributes and creates the necessary inner objects.</p>
299
         * 
300
         * @param dataModel dataModel used by this combo box instance
301
         */
302
        protected void initialize() {
303
                // By default user hasn't selected an item of the popup
304
                popupItemSelected = false;
305
                
306
                // By default no arrow key has been pressed
307
                arrowKeyPressed = false;
308
                
309
                // By default the arrow button of the GUI hasn't been clicked
310
                arrowButtonClicked = false;
311
                
312
                // By default isn't showing all items temporally in the GUI
313
                showingAllItemsTemporally = false;
314
                
315
                // By default no item has been selected
316
                previousSelected = null;
317

    
318
                // Set default flags configuration
319
                this.setDefaultBehaviorFlagsConfiguration();
320

    
321
                // Allows user to edit on the combobox
322
                super.setEditable(true);
323
                                
324
                // Other configuration tasks
325
                configure();
326

    
327
                // If there are items -> select the first
328
                if ((toForceSelectAnItem) && (model.getData().size() > 0)) {
329
                        model.setSelectedItem(model.getData().elementAt(0));
330
                }
331

    
332
                // Resets the UI property to a value from the current look and feel
333
                updateUI();
334
        }
335
        
336
        /**
337
         * <p>Configures the component and some of its elements.</p>
338
         */
339
        protected void configure() {
340
                // Defines a key listener for the editor of this component
341
                defineEditorKeyListener(this);
342

    
343
                // Defines a focus listener for the editor of this component
344
                defineEditorFocusListener(this);
345
                                
346
                // Configures the document of the editor of this component
347
                configureDocument();
348
                
349
                // Configures the editor (ComboBoxEditor) of this component
350
                configureEditor(this.getEditor());
351
                
352
                // Configures the popup of this component
353
                configurePopUp(this);
354
        }
355
        
356
        /**
357
         * <p>Configures the editor ( {@link ComboBoxEditor} ) of this component.</p>
358
         * 
359
         * @param newEditor The new editor to configure
360
         */
361
        protected void configureEditor(ComboBoxEditor newEditor) {
362
                if (newEditor != null) {
363
                        JTextComponent jTextComponentOfEditor = (JTextComponent) newEditor.getEditorComponent();
364

    
365
                        // Adds the new document (tries to remove it if it existed before)
366
                        jTextComponentOfEditor.setDocument(this.document);
367

    
368
                        // Adds the new Key Listener (tries to remove it if it existed before)
369
                        jTextComponentOfEditor.removeKeyListener(this.editorKeyListener);
370
                        jTextComponentOfEditor.addKeyListener(this.editorKeyListener);
371

    
372
                        // Adds the new Focus Listener (tries to remove it if it existed before)
373
                        jTextComponentOfEditor.removeFocusListener(this.editorFocusListener);
374
                        jTextComponentOfEditor.addFocusListener(this.editorFocusListener);
375
                }
376
        }
377

    
378
        /**
379
         * <p>Configures the document of the editor of this component.</p>
380
         */
381
        protected void configureDocument() {
382
                // Creates the document of the editor of this component
383
                document = new PlainDocumentTextFormatter();
384

    
385
                // Set reference to the container component
386
                document.setJComboBoxReference(this);
387
        }
388

    
389
        /**
390
         * <p>Configures the popup of this component.</p>
391
         *
392
         * @param comboBox A reference of this component
393
         */
394
        protected void configurePopUp(JComboBoxConfigurableLookUp comboBox) {
395
                final JComboBoxConfigurableLookUp comboBoxReference = comboBox;
396
                this.addPopupMenuListener(this.popupMenuListener);
397
                                
398
                BasicComboBoxUI comboBoxUi = (BasicComboBoxUI)comboBoxReference.getUI();
399
                Accessible aC = comboBoxUi.getAccessibleChild(comboBoxReference, 0);
400

    
401
                if (aC instanceof ComboPopup) {
402
                        JList jlist = ((ComboPopup)aC).getList();
403

    
404
                        jlist.addMouseListener(new MouseAdapter() {
405
                                /*
406
                                 *  (non-Javadoc)
407
                                 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
408
                                 */
409
                                public void mousePressed(MouseEvent e) {
410
                                        // User selects an item of the popup using the mouse
411
                                        popupItemSelected = true;
412
                                        System.out.println("Pulsado");
413
                                }
414
                        });
415
                                
416
                        if (aC instanceof BasicComboPopup) {
417
                                ((BasicComboPopup)aC).addPopupMenuListener(new PopupMenuListener() {
418
                                        /*
419
                                         * (non-Javadoc)
420
                                         * @see javax.swing.event.PopupMenuListener#popupMenuCanceled(javax.swing.event.PopupMenuEvent)
421
                                         */
422
                                        public void popupMenuCanceled(PopupMenuEvent e) {
423
                                                if (arrowButtonClicked) {
424
                                                        arrowButtonClicked = false;
425
                                                }
426
                                        }
427

    
428
                                        /*
429
                                         * (non-Javadoc)
430
                                         * @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent)
431
                                         */
432
                                        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
433
                                                if (arrowButtonClicked) {
434
                                                        arrowButtonClicked = false;
435
                                                }
436
                                        }
437

    
438
                                        /*
439
                                         * (non-Javadoc)
440
                                         * @see javax.swing.event.PopupMenuListener#popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent)
441
                                         */
442
                                        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
443
                                                if (arrowButtonClicked) {
444
                                                        arrowButtonClicked = false;
445
                                                }
446
                                        }
447
                                });
448
                        }
449
                }
450
        }
451

    
452
        /**
453
         * <p>Returns the listener invoked when user presses the arrow button of this <code>JComboBox</code>.</p>
454
         * 
455
         * @return java.awt.event.MouseListener
456
         */
457
        public MouseListener getArrowMouseListener() {
458
                if (arrowMouseListener == null) {
459
                        // New mouse listener
460
                        arrowMouseListener = new MouseAdapter() {
461
                                /*
462
                                 * (non-Javadoc)
463
                                 * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
464
                                 */
465
                                public void mousePressed(MouseEvent e) {
466
                                        arrowButtonClicked = true;
467
                                        
468
                                        // Avoids a bug: when the component is created an shown, first time the arrow button is pressed, the method
469
                                        // #isPopupVisible() returns 'true' instead of returning 'false'
470
                                        boolean popupVisible;
471
                                        
472
                                        if (updatedUI) {
473
                                                popupVisible = false;
474
                                        }
475
                                        else {
476
                                                popupVisible = isPopupVisible();
477
                                        }
478
                
479
                                        // Shows all items temporally in the GUI
480
                                        if ((! model.isShowAllItemsInListBox()) && (displayAllItemsWithArrowButton) && (! popupVisible)) {
481
                                                showingAllItemsTemporally = true;
482
                                                
483
                                                // Stores the configuration of the flags                                
484
                                                int itemsOrderFlag = model.getItemsOrder();
485
                                                String languageRules = model.getLocaleRules();
486
                                                boolean caseSensitive = model.isCaseSensitive();
487
                                                ILookUp lookUpAgent = model.getLookUpAgent();
488
                                                
489
                                                // Must replace the dataModel for update the data in the popup correctly
490
                                                setModel(new DefaultComboBoxConfigurableLookUpModel(model.getData()));
491
                                                
492
                                                // Restores the configuration of the flags
493
                                                model.setItemsOrder(itemsOrderFlag);
494
                                                model.setShowAllItemsInListBox(true);
495
                                                model.setLocaleRules(languageRules);
496
                                                model.setCaseSensitive(caseSensitive);
497
                                                model.setLookUpAgent(lookUpAgent);
498
                
499
                                                model.setTextWritten(document.textWritten);
500
                                                
501
                                                if (updatedUI) {
502
                                                        updatedUI = false;
503
                                                        showPopup();
504
                                                }
505
                                        }
506
                                }
507
                        };
508
                }
509
                
510
                return arrowMouseListener;
511
        }
512
        
513
        /**
514
         * <p>Sets the default values of the flags.</p>
515
         */
516
        protected void setDefaultBehaviorFlagsConfiguration() {
517
                onlyOneColorOnText = DEFAULT_ONLY_ONE_COLOR_ON_TEXT_CONFIGURATION;
518
                beepEnabled = DEFAULT_BEEP_ENABLED_CONFIGURATION;
519
                hidePopupIfThereAreNoItems = DEFAULT_HIDE_POPUP_IF_THERE_ARE_NO_ITEMS_CONFIGURATION;
520
                toForceSelectAnItem = DEFAULT_TO_FORCE_SELECT_AN_ITEM_CONFIGURATION;
521
                completeArrowKeySelection = DEFAULT_COMPLETE_ARROW_KEY_SELECTION_CONFIGURATION;
522
                displayAllItemsWithArrowButton = DEFAULT_DISPLAY_ALL_ITEMS_WITH_ARROW_BUTTON_CONFIGURATION;
523
        }
524
        
525
        /**
526
         * <p>Defines a key listener for the editor of this component.</p>
527
         * 
528
         * <p><b>Warning:</b><i>This method is another important difference from the JComboBox; and could work badly with some key-maps.</i></p>
529
         * 
530
         * @param comboBox A reference of this component
531
         */
532
        private void defineEditorKeyListener(JComboBoxConfigurableLookUp comboBox) {
533
                final JComboBoxConfigurableLookUp comboBoxReference = comboBox;
534
                
535
                editorKeyListener = new KeyAdapter() {
536
                        /*
537
                         * (non-Javadoc)
538
                         * @see java.awt.event.KeyAdapter#keyPressed(java.awt.event.KeyEvent)
539
                         */
540
                        public void keyPressed(KeyEvent ke)  // Executed on the Start view state or Search view state
541
                        {
542
                                // Restore the status if was showing all items temporally
543
                                if (showingAllItemsTemporally) {
544
                                        showingAllItemsTemporally = false;
545
                                        
546
                                        // Stores the configuration of the flags                                
547
                                        int itemsOrderFlag = model.getItemsOrder();
548
                                        String languageRules = model.getLocaleRules();
549
                                        boolean caseSensitive = model.isCaseSensitive();
550
                                        ILookUp lookUpAgent = model.getLookUpAgent();
551
                                        
552
                                        // Must replace the dataModel for update the data in the popup correctly
553
                                        comboBoxReference.setModel(new DefaultComboBoxConfigurableLookUpModel(model.getData()));
554
                                        
555
                                        // Restores the configuration of the flags
556
                                        model.setItemsOrder(itemsOrderFlag);
557
                                        model.setShowAllItemsInListBox(false);
558
                                        model.setLocaleRules(languageRules);
559
                                        model.setCaseSensitive(caseSensitive);
560
                                        model.setLookUpAgent(lookUpAgent);
561
                                        model.setTextWritten(document.textWritten);
562
                                }
563
                                
564
                                // According the key pressed, do some actions or others
565
                                switch (ke.getKeyCode())
566
                                {
567
                                        case KeyEvent.VK_ENTER :
568
                                                // Don't allow execute the default instructions because they have a bug (first time we remove some characters, no item will be displayed in the popup)
569
                                                ke.consume();
570

    
571
                                                // Sets the caret position of the text in the document to the end:
572
                                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(document.getLength());
573

    
574
                                                final DefaultComboBoxConfigurableLookUpModel c_model = (DefaultComboBoxConfigurableLookUpModel)comboBoxReference.getModel();
575
                                                
576
                                                if (toForceSelectAnItem) {
577
                                                        if (c_model.isShowAllItemsInListBox()) {
578
                                                                // Select now the first item or the previous selected
579
                                                                if (c_model.getSelectedItem() == null) {
580
                                                                        if (c_model.getData().size() > 0) {
581
                                                                                if (previousSelected == null)
582
                                                                                        previousSelected = c_model.getDataAccordingItemsOrder().elementAt(0);
583
                                                                                else {
584
                                                                                        ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
585
                                                                                }
586
                                                                        }
587
                                                                        else {
588
                                                                                previousSelected = null;
589
                                                                        }
590
                                                                }
591
                                                                else {                                                                
592
                                                                        previousSelected = c_model.getSelectedItem();                                                                        
593
                                                                        comboBoxReference.setSelectedItem(previousSelected);
594
                                                                        
595
                                                                        ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
596
                                                                }
597
                                                        }
598
                                                        else {
599
                                                                // Select now the first item or the previous selected
600
                                                                switch (comboBoxReference.getModel().getSize()) {
601
                                                                        case 0:
602
                                                                                if (previousSelected == null) {
603
                                                                                        if (c_model.getData().size() > 0) {
604
                                                                                                previousSelected = c_model.getDataAccordingItemsOrder().elementAt(0);
605
                                                                                        }
606
                                                                                        else {
607
                                                                                                previousSelected = null;
608
                                                                                        }
609
                                                                                }
610
                                                                                
611
                                                                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
612
                                                                                break;
613
                                                                        case 1:
614
                                                                                // If there is only one item -> select it
615
                                                                                previousSelected = comboBoxReference.getItemAt(0); // Select the first
616
                                                                                comboBoxReference.setSelectedIndex(0);
617
                                                                                break;
618
                                                                        default:
619
                                                                                if (previousSelected == null) {
620
                                                                                        previousSelected = comboBoxReference.getItemAt(0); // Select the first
621
                                                                                        comboBoxReference.setSelectedIndex(0);
622
                                                                                }
623
                                                                                else
624
                                                                                        ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
625
                                                                }
626
                                                        }
627
                                                }                                                
628
                                                
629
                                                // Hide the popup
630
                                                comboBoxReference.hidePopup();
631
                                                
632
                                                // Force select one item
633
                                                if (completeArrowKeySelection) {
634
                                                        model.setTextWritten(document.textWritten);
635
                                                }
636
                                                break;
637
                                                
638
                                        case KeyEvent.VK_UP: case KeyEvent.VK_DOWN:
639
                                                // User selects an item of the popup using the mouse
640
                                                popupItemSelected = true;
641
                                                arrowKeyPressed = true;
642
                                                break;
643
                                }
644
                        }
645
                };
646
        }
647
        
648
        /**
649
         * <p>Defines a focus listener for the editor of this component.</p>
650
         * 
651
         * @param comboBox A reference of this component
652
         */
653
        private void defineEditorFocusListener(JComboBoxConfigurableLookUp comboBox) {
654
                final JComboBoxConfigurableLookUp comboBoxReference = comboBox;
655
                
656
                // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out                
657
                hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5");
658
                
659
                // Highlight whole text when gaining focus
660
                editorFocusListener = new FocusAdapter() {
661
                        /*
662
                         *  (non-Javadoc)
663
                         * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
664
                         */
665
                        public void focusLost(FocusEvent e) {
666
                                // Restore the status if was showing all items temporally
667
                                if (showingAllItemsTemporally) {
668
                                        showingAllItemsTemporally = false;
669
                                        
670
                                        // Stores the configuration of the flags                                
671
                                        int itemsOrderFlag = model.getItemsOrder();
672
                                        String languageRules = model.getLocaleRules();
673
                                        boolean caseSensitive = model.isCaseSensitive();
674
                                        ILookUp lookUpAgent = model.getLookUpAgent();
675
                                        
676
                                        // Must replace the dataModel for update the data in the popup correctly
677
                                        comboBoxReference.setModel(new DefaultComboBoxConfigurableLookUpModel(model.getData()));
678
                                        
679
                                        // Restores the configuration of the flags
680
                                        model.setItemsOrder(itemsOrderFlag);
681
                                        model.setShowAllItemsInListBox(false);
682
                                        model.setLocaleRules(languageRules);
683
                                        model.setCaseSensitive(caseSensitive);
684
                                        model.setLookUpAgent(lookUpAgent);
685
                                        model.setTextWritten(document.textWritten);
686
                                }
687
                                
688
                                
689
                                // Sets the caret position of the text in the document to the end:
690
                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(document.getLength());
691

    
692
                                final DefaultComboBoxConfigurableLookUpModel c_model = (DefaultComboBoxConfigurableLookUpModel)comboBoxReference.getModel();
693

    
694
                                if (toForceSelectAnItem) {
695
                                        if (c_model.isShowAllItemsInListBox()) {
696
                                                // Select now the first item or the previous selected
697
                                                if (c_model.getSelectedItem() == null) {
698
                                                        if (c_model.getData().size() > 0) {
699
                                                                if (previousSelected == null)
700
                                                                        previousSelected = c_model.getDataAccordingItemsOrder().elementAt(0);
701
                                                                else {
702
                                                                        ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
703
                                                                }
704
                                                        }
705
                                                        else {
706
                                                                previousSelected = null;
707
                                                        }
708
                                                }
709
                                                else {                                                                
710
                                                        previousSelected = c_model.getSelectedItem();                                                                        
711
                                                        comboBoxReference.setSelectedItem(previousSelected);
712
                                                }
713

    
714
                                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
715
                                        }
716
                                        else {
717
                                                // Select now the first item or the previous selected
718
                                                switch (model.getData().size()) {
719
                                                        case 0:
720
                                                                if (previousSelected == null) {
721
                                                                        if (c_model.getData().size() > 0) {
722
                                                                                previousSelected = c_model.getDataAccordingItemsOrder().elementAt(0);
723

    
724
                                                                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
725
                                                                        }
726
                                                                        else {
727
                                                                                previousSelected = null;
728
                                                                        }
729
                                                                }
730
                                                                break;
731
                                                        default:
732
                                                                if (previousSelected == null) {
733
                                                                        previousSelected = comboBoxReference.getItemAt(0); // Select the first
734
                                                                        comboBoxReference.setSelectedIndex(0);
735
                                                                }
736
                                                                else
737
                                                                        ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setText(previousSelected.toString());
738
                                                }
739
                                        }
740
                                }
741

    
742
                                comboBoxReference.hidePopup();
743

    
744
                                // Workaround for Bug 5100422 - Hide Popup on focus loss
745
                                if (hidePopupOnFocusLoss)
746
                                        comboBoxReference.setPopupVisible(false);
747
                        }                        
748
                };
749
        }
750
        
751
        ///// END NEW METHODS /////
752
        
753
        ///// REIMPLEMENTATION OF METHODS OF 'JComboBox' /////
754
        
755
        /**
756
         * <p>Sets the editor used to paint and edit the selected item in the 
757
     * <code>JComboBox</code> field.  The editor is used only if the
758
     * receiving <code>JComboBox</code> is editable. If not editable,
759
     * the combo box uses the renderer to paint the selected item.</p>
760
     * 
761
     * <p>The editor must be a <code>JTextComponent</code>, and its document a <code>PlainDocument</code> object.</p>
762
     *  
763
     * @param anEditor  the <code>ComboBoxEditor</code> that
764
     *                        displays the selected item
765
     * @see #setRenderer
766
     * @beaninfo
767
     *     bound: true
768
     *    expert: true
769
     *  description: The editor that combo box uses to edit the current value
770
         */
771
        public void setEditor(ComboBoxEditor anEditor) {
772
                if (anEditor == null) {
773
                        super.setEditor(anEditor);
774
                        return;
775
                }
776
                
777
                JTextComponent jTextComponentOfEditor = (JTextComponent) anEditor.getEditorComponent();
778

    
779
                // Adds the new ''PlainDocumentTextFormatter'', adding the listeners of the new editor's document
780
                PlainDocument old_document = ((PlainDocument)jTextComponentOfEditor.getDocument());
781
                
782
                DocumentListener[] documentListeners = old_document.getDocumentListeners();
783
                UndoableEditListener[] undoableEditListeners = old_document.getUndoableEditListeners();
784
                
785
                // Defines a key listener for the editor of this component
786
                defineEditorKeyListener(this);
787
                jTextComponentOfEditor.addKeyListener(editorKeyListener);
788
                
789
                // Defines a focus listener for the editor of this component
790
                defineEditorFocusListener(this);
791
                jTextComponentOfEditor.addFocusListener(editorFocusListener);
792
                
793
                document = new PlainDocumentTextFormatter();
794
                                
795
                // Set reference to the container component
796
                document.setJComboBoxReference(this);
797
                        
798
                // Removes all previous 'document' and 'undoableEdit' listeners
799
                DocumentListener[] removableDocumentListeners = document.getDocumentListeners();
800
                for (DocumentListener dL : removableDocumentListeners)
801
                        document.removeDocumentListener(dL);
802

    
803
                UndoableEditListener[] removableUndoableEditListeners = document.getUndoableEditListeners();
804
                for (UndoableEditListener dUEL : removableUndoableEditListeners)
805
                        document.removeUndoableEditListener(dUEL);
806

    
807
                for (DocumentListener rdL : documentListeners)
808
                        document.addDocumentListener(rdL);
809
                
810
                // The undo-redo listeners fail
811
                for (UndoableEditListener uEL : undoableEditListeners) 
812
                        document.addUndoableEditListener(uEL);
813
                
814
                if (undoableEditListeners.length > 0) {
815
                        if (jTextComponentOfEditor instanceof IEditableText) {
816
                                ((IEditableText)jTextComponentOfEditor).addUndoRedoEditListener(new UndoRedoEditListener() {
817
                                        /*
818
                                         * (non-Javadoc)
819
                                         * @see org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener#operationExecuted(org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditEvent)
820
                                         */
821
                                        public void operationExecuted(UndoRedoEditEvent e) {
822
                                                model.setTextWritten(e.getNewText());
823
                                                document.updateOnlyTextColor();
824
                                        }
825
                                });
826
                        }
827
                }
828

    
829
                jTextComponentOfEditor.setDocument(document);
830
                
831
                super.setEditor(anEditor);
832
        }
833

    
834
        /*
835
         *  (non-Javadoc)
836
         * @see javax.swing.JComboBox#setModel(javax.swing.ComboBoxModel)
837
         */
838
        public void setModel(ComboBoxModel aModel) {
839
                // If model isn't DefaultComboBoxConfigurableLookUpModel, sets as model a new DefaultComboBoxConfigurableLookUpModel, with the data
840
                //  of ''aModel'':
841
                if (! (aModel instanceof DefaultComboBoxConfigurableLookUpModel) ) {
842
                        Vector<Object> data = new Vector<Object>(aModel.getSize());
843
                        
844
                        for (int i = 0; i < aModel.getSize(); i++) {
845
                                data.add(aModel.getElementAt(i));
846
                        }
847
                        
848
                        aModel = new DefaultComboBoxConfigurableLookUpModel(data);
849
                }
850
                
851
                super.setModel(aModel);
852
                
853
                model = (DefaultComboBoxConfigurableLookUpModel)super.getModel();
854
        }
855
        
856
        /*
857
         *  (non-Javadoc)
858
         * @see javax.swing.JComboBox#setSelectedIndex(int)
859
         */
860
        public void setSelectedIndex(int anIndex) {
861
                if (anIndex < -1 || anIndex >= model.getSize())
862
                        // Fails because index is out of bounds.
863
                        throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
864
                else
865
                        // Selects the item at the given index or clears the selection if the
866
                        // index value is -1.
867
                        setSelectedItem((anIndex == -1) ? null : model.getElementAt(anIndex));
868
        }
869
        
870
        /*
871
         *  (non-Javadoc)
872
         * @see javax.swing.JComboBox#setSelectedItem(java.lang.Object)
873
         */
874
        public void setSelectedItem(Object anObject) {                
875
                if ((selectedItemReminder == null) || (!selectedItemReminder.equals(anObject))) {
876
                        if (arrowKeyPressed) {
877
                                
878
                                selectingItem = true;
879
                                model.setSelectedItem(anObject);
880
                                selectingItem = false;
881
                                
882
                                if (model.getSize() > 0)
883
                                        previousSelected = anObject;
884
                        }
885
                        else {
886
                                selectingItem = true;
887
                                model.setSelectedItem(anObject);
888
                                selectingItem = false;
889
                                
890
                                editor.setItem(anObject);
891
                                
892
                                if (model.getSize() > 0)
893
                                        previousSelected = anObject;
894
                        }
895
                        
896
                        if (selectedItemReminder != model.getSelectedItem()) {
897
                                // In case a users implementation of ComboBoxModel
898
                                // doesn't fire a ListDataEvent when the selection
899
                                // changes.
900
                                selectedItemChanged();
901
                        }
902
                }                        
903
                fireActionEvent();
904
                document.updateOnlyTextColor();
905
        }
906

    
907
        /*
908
         * <p>Unsupported operation. A <code>JComboBoxConfigurableLookUp</code> is always editable.</p>
909
         *
910
         * (non-Javadoc)
911
         * @see javax.swing.JComboBox#setEditable(boolean)
912
         */
913
        public void setEditable(boolean b) {
914
                throw new UnsupportedOperationException();
915
        }
916
        
917
        /*
918
         * (non-Javadoc)
919
         * @see javax.swing.JComboBox#removeAllItems()
920
         */
921
        public void removeAllItems() {
922
                if ( !(model instanceof MutableComboBoxModel) )
923
                        throw new RuntimeException("Cannot use this method with a non-Mutable data dataModel.");
924

    
925
                //if ( model instanceof DefaultComboBoxConfigurableLookUpModel ) {
926
                model.setTextWritten("");
927
                ((DefaultComboBoxConfigurableLookUpModel)model).removeAllElements();
928

    
929
                selectedItemReminder = null;
930

    
931
                if (isEditable()) {
932
                        editor.setItem(null);
933
                }
934
                
935
                document.updateTextColorAndRingBeep();
936
        }
937
        
938
        public void addItem(Object anObject) {
939
                super.addItem(anObject);
940

    
941
                if ((toForceSelectAnItem) && (getSelectedIndex() == -1))
942
                        setSelectedIndex(0);
943
                
944
                document.updateTextColorAndRingBeep();
945
        }
946

    
947
        /*
948
         * (non-Javadoc)
949
         * @see javax.swing.JComboBox#removeItem(java.lang.Object)
950
         */
951
        public void removeItem(Object anObject) {
952
                super.removeItem(anObject);
953
                
954
                document.updateTextColorAndRingBeep();
955
        }
956

    
957
        /*
958
         * (non-Javadoc)
959
         * @see javax.swing.JComboBox#removeItemAt(int)
960
         */
961
        public void removeItemAt(int anIndex) {
962
                super.removeItemAt(anIndex);
963
                
964
                document.updateTextColorAndRingBeep();
965
        }
966

    
967
        /*
968
         * (non-Javadoc)
969
         * @see javax.swing.JComponent#setUI(javax.swing.plaf.ComponentUI)
970
         */
971
        protected void setUI(ComponentUI newUI) {
972
                super.setUI(newUI);
973
                ComponentUI oldUI = newUI;
974
                
975
                // Remove the default key selection manager
976
                super.setKeySelectionManager(null);
977
                
978
        firePropertyChange("UI", oldUI, newUI);
979
        revalidate();
980
        repaint();
981
        }
982

    
983
        /*
984
         * <p>Unsupported operation.</p>
985
         * 
986
         * (non-Javadoc)
987
         * @see javax.swing.JComboBox#selectWithKeyChar(char)
988
         */
989
        public boolean selectWithKeyChar(char keyChar) {
990
                throw new UnsupportedOperationException();
991
        }
992

    
993
        /*
994
         * <p>Unsupported operation.</p>
995
         * 
996
         * (non-Javadoc)
997
         * @see javax.swing.JComboBox#processKeyEvent(java.awt.event.KeyEvent)
998
         */
999
        public void processKeyEvent(KeyEvent e) {
1000
                throw new UnsupportedOperationException();
1001
        }
1002

    
1003
        /*
1004
         * <p>Unsupported operation.</p>
1005
         * 
1006
         * (non-Javadoc)
1007
         * @see javax.swing.JComboBox#createDefaultKeySelectionManager()
1008
         */
1009
        protected KeySelectionManager createDefaultKeySelectionManager() {
1010
                throw new UnsupportedOperationException();
1011
        }
1012

    
1013
        /*
1014
         * (non-Javadoc)
1015
         * @see javax.swing.JComboBox#paramString()
1016
         */
1017
        protected String paramString() {
1018
                return super.paramString() +
1019
                  "onlyOneColorOnText" + onlyOneColorOnText +
1020
                  "beepEnabled" + beepEnabled +
1021
                  "hidePopupIfThereAreNoItems" + hidePopupIfThereAreNoItems +
1022
                  "toForceSelectAnItem" + toForceSelectAnItem +
1023
                  "completeArrowKeySelection" + completeArrowKeySelection +
1024
                  "displayAllItemsWithArrowButton" + displayAllItemsWithArrowButton +
1025
                  "itemsOrder" + model.getItemsOrder() +
1026
                  "showAllItemsInListBox" + model.isShowAllItemsInListBox() +
1027
                  "localeRules" + model.getLocaleRules() +
1028
                  "caseSensitive" + model.isCaseSensitive();
1029
        }
1030

    
1031
        /*
1032
         * (non-Javadoc)
1033
         * @see javax.swing.JComboBox#updateUI()
1034
         */
1035
        public void updateUI() {
1036
                super.updateUI();
1037
                
1038
                // Force to hide the popup
1039
                updatedUI = true;
1040

    
1041
                for (int i = 0; i < getComponentCount(); i++) {
1042
                        if (getComponent(i) instanceof JButton) {
1043
                                ((JButton)getComponent(i)).addMouseListener(getArrowMouseListener());
1044
                                return;
1045
                        }
1046
                }
1047
        }
1048
        
1049
        /*
1050
         * (non-Javadoc)
1051
         * @see javax.swing.JComboBox#showPopup()
1052
         */
1053
        public void showPopup() {
1054
                if (this.isShowing() && (!blockPopupHided))
1055
                        setPopupVisible(true);
1056
        }
1057

    
1058
        /**
1059
         * <p>Sets if the popup of this component will always remain hided, or can be shown when is invoked.</p>
1060
         * 
1061
         * @param b <code>true</code> if will remain blocked; <code>false</code> if can be shown 
1062
         */
1063
        public void setBlockPopupHided(boolean b) {
1064
                blockPopupHided = b;
1065
        }
1066
        
1067

    
1068
        /**
1069
         * <p>Determines if the popup of this component will always remain hided.</p>
1070
         * 
1071
         * @param b <code>true</code> if will remain blocked; <code>false</code> if can be shown 
1072
         */
1073
        public boolean isBlockPopupHided() {
1074
                return blockPopupHided;
1075
        }
1076
        ///// END REIMPLEMENTATION OF METHODS OF 'JComboBox' /////    
1077
        
1078
        ////// METHODS FOR THE BEHAVIOR FLAGS  //////
1079
        /**
1080
         * <p>Returns the 'only_One_Color_On_Text' configuration value of this component. Configuration values are:
1081
         *  <ul>
1082
         *   <li><i>true</i>: always uses black colour on text.</li>
1083
         *   <li><i>false</i>: by default uses black colour on text, but if text written by user doesn't match with any item according the <code>ILookUp</code> agent, text will be on red colour.</li>
1084
         *  </ul>
1085
         * </p>
1086
         * 
1087
         * @return 'only_One_Color_On_Text' configuration
1088
         */
1089
        public boolean isOnlyOneColorOnText() {
1090
                return onlyOneColorOnText;
1091
        }
1092
        
1093
        /**
1094
         * <p>Sets the 'only_One_Color_On_Text' configuration value for this component. Configuration values are:
1095
         *  <ul>
1096
         *   <li><i>true</i>: always uses black colour on text.</li>
1097
         *   <li><i>false</i>: by default uses black colour on text, but if text written by user doesn't match with any item according the <code>ILookUp</code> agent, text will be on red colour.</li>
1098
         *  </ul>
1099
         * </p>
1100
         * 
1101
         * @param b value for 'only_One_Color_On_Text' configuration flag
1102
         */
1103
        public void setOnlyOneColorOnText(boolean b) {
1104
                onlyOneColorOnText = b;
1105
        }
1106

    
1107
        /**
1108
         * <p>Returns the 'beep_Enabled' configuration value of this component. Configuration values are:
1109
         *  <ul>
1110
         *   <li><i>true</i>: a beep-sound is listened when no item matches with the text written according the <code>ILookUp</code> agent.</li>
1111
         *   <li><i>false</i>: no sound is listened.</li>
1112
         *  </ul>
1113
         * </p>
1114
         * 
1115
         * @return 'beep_Enabled' configuration
1116
         */
1117
        public boolean isBeepEnabled() {
1118
                return beepEnabled;
1119
        }
1120
        
1121
        /**
1122
         * <p>Sets the 'beep_Enabled' configuration value for this component. Configuration values are:
1123
         *  <ul>
1124
         *   <li><i>true</i>: a beep-sound is listened when no item matches with the text written according the <code>ILookUp</code> agent.</li>
1125
         *   <li><i>false</i>: no sound is listened.</li>
1126
         *  </ul>
1127
         * </p>
1128
         * 
1129
         * @param b value for 'beep_Enabled' configuration flag
1130
         */
1131
        public void setBeepEnabled(boolean b) {
1132
                beepEnabled = b;
1133
        }        
1134

    
1135
        /**
1136
         * <p>Returns the 'hidePopupIfThereAreNoItems' configuration value of this component. Configuration values are:
1137
         *  <ul>
1138
         *   <li><i>true</i>: hides the popup if there are no items when you write.</li>
1139
         *   <li><i>false</i>: default behaviour (if there are no items when you write, popup remains visible) .</li>
1140
         *  <ul>
1141
         * </p>
1142
         * 
1143
         * @return 'hidePopupIfThereAreNoItems' configuration
1144
         */
1145
        public boolean isHidePopupIfThereAreNoItems() {
1146
                return hidePopupIfThereAreNoItems;
1147
        }
1148
        
1149
        /**
1150
         * <p>Sets the 'hidePopupIfThereAreNoItems' configuration value for this component. Configuration values are:
1151
         *  <ul>
1152
         *   <li><i>true</i>: hides the popup if there are no items when you write.</li>
1153
         *   <li><i>false</i>: default behaviour (if there are no items when you write, popup remains visible) .</li>
1154
         *  <ul>
1155
         * </p>
1156
         * 
1157
         * @param b value for 'hidePopupIfThereAreNoItems' configuration flag
1158
         */
1159
        public void setHidePopupIfThereAreNoItems(boolean b) {
1160
                hidePopupIfThereAreNoItems = b;
1161
        }
1162

    
1163
        /**
1164
         * <p>Returns the 'toForceSelectAnItem' configuration value of this component. Configuration values are:
1165
         *  <ul>
1166
         *   <li><i>true</i>: selects the previous selected item or the first of the list when user presses the Enter key or when the component loses the focus .</li>
1167
         *   <li><i>false</i>: default behaviour .</li>
1168
         *  </ul>
1169
         * </p>
1170
         * 
1171
         * @return 'toForceSelectAnItem' configuration
1172
         */
1173
        public boolean isToForceSelectAnItem() {
1174
                return toForceSelectAnItem;
1175
        }
1176
        
1177
        /**
1178
         * <p>Sets the 'toForceSelectAnItem' configuration value for this component. Configuration values are:
1179
         *  <ul>
1180
         *   <li><i>true</i>: selects the previous selected item or the first of the list when user presses the Enter key or when the component loses the focus .</li>
1181
         *   <li><i>false</i>: default behaviour .</li>
1182
         *  </ul>
1183
         * </p>
1184
         * 
1185
         * @param value for 'toForceSelectAnItem' configuration flag
1186
         */
1187
        public void setToForceSelectAnItem(boolean b) {
1188
                toForceSelectAnItem = b;
1189
        }
1190

    
1191
        /**
1192
         * <p>Returns the 'completeArrowKeySelection' configuration value of this component. Configuration values are:
1193
         *  <ul>
1194
         *   <li><i>true</i>: writes in the editor the value of the element selected by the arrow keys (up or down).</li>
1195
         *   <li><i>false</i>: holds the previous text written .</li>
1196
         *  </ul>
1197
         * </p>
1198
         * 
1199
         * @return 'completeArrowKeySelection' configuration
1200
         */
1201
        public boolean isCompleteArrowKeySelection() {
1202
                return completeArrowKeySelection;
1203
        }
1204

    
1205
        /**
1206
         * <p>Sets the 'completeArrowKeySelection' configuration value for this component. Configuration values are:
1207
         *  <ul>
1208
         *   <li><i>true</i>: writes in the editor the value of the element selected by the arrow keys (up or down).</li>
1209
         *   <li><i>false</i>: holds the previous text written .</li>
1210
         *  </ul>
1211
         * </p>
1212
         * 
1213
         * @param b value for 'completeArrowKeySelection' configuration flag
1214
         */
1215
        public void setCompleteArrowKeySelection(boolean b) {
1216
                completeArrowKeySelection = b;
1217
        }
1218

    
1219
        /**
1220
         * <p>Returns the 'displayAllItemsWithArrowButton' configuration value of this component. Configuration values are:
1221
         *  <ul>
1222
         *   <li><i>true</i>: displays all this component's dataModel items if its <i>popup</i> it's hided, and this component's dataModel
1223
         *    listed only the matches <i>(flag: <code>DefaultComboBoxConfigurableLookUpModel.showAllItemsInListBox == false</code>)</i>.</li>
1224
         *   <li><i>false</i>: normal behaviour according the other <i>flags</i>.</li>
1225
         *  </ul>
1226
         * </p>
1227
         * 
1228
         * @return 'displayAllItemsWithArrowButton' configuration
1229
         */
1230
        public boolean isDisplayAllItemsWithArrowButton() {
1231
                return displayAllItemsWithArrowButton;
1232
        }
1233
        
1234
        /**
1235
         * <p>Sets the 'toForceSelectAnItem' configuration value for this component. Configuration values are:
1236
         *  <ul>
1237
         *   <li><i>true</i>: displays all this component's dataModel items if its <i>popup</i> it's hided, and this component's dataModel
1238
         *    listed only the matches <i>(flag: <code>DefaultComboBoxConfigurableLookUpModel.showAllItemsInListBox == false</code>)</i>.</li>
1239
         *   <li><i>false</i>: normal behaviour according the other <i>flags</i>.</li>
1240
         *  </ul>
1241
         * </p>
1242
         * 
1243
         * @param value for 'displayAllItemsWithArrowButton' configuration flag
1244
         */
1245
        public void setDisplayAllItemsWithArrowButton(boolean b) {
1246
                displayAllItemsWithArrowButton = b;
1247
        }
1248

    
1249
        ////// END METHODS FOR THE BEHAVIOR FLAGS  //////
1250

    
1251

    
1252
        /**
1253
         * <p>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.</p>
1254
         * 
1255
         * <p>This class is also optimized for items look up process.</p>
1256
         * 
1257
         * @author Pablo Piqueras Bartolom? (p_queras@hotmail.com)
1258
         */
1259
        public class PlainDocumentTextFormatter extends PlainDocument {
1260
                private static final long serialVersionUID = -1858441733547527816L;
1261
                private JComboBoxConfigurableLookUp comboBoxReference;
1262
                private String textWritten;
1263
                private boolean updatedModel;
1264
                private String textOfReplacement;
1265
                private boolean textInRedColor;
1266
                private int old_caretPosition;
1267
                private String completeArrowKeySelectionValue;
1268

    
1269
                /**
1270
                 * <p>Default Constructor.</p>
1271
                 */
1272
                public PlainDocumentTextFormatter() {
1273
                        super();
1274
                        this.initialize();
1275
                }
1276
                
1277
                /**
1278
                 * <p>This method makes some initialize operations.</p> 
1279
                 */
1280
                protected void initialize() {
1281
                        textWritten = "";
1282
                        textOfReplacement = "";
1283
                        textInRedColor = false;
1284
                        completeArrowKeySelectionValue = null;
1285
                }
1286

    
1287
                /**
1288
                 * <p>Sets a reference of this component.</p>
1289
                 * 
1290
                 * @param comboBox A reference to the class that contains this.
1291
                 */
1292
                protected void setJComboBoxReference(JComboBoxConfigurableLookUp comboBox) {
1293
                        comboBoxReference = comboBox;
1294
                }
1295

    
1296
                /*
1297
                 *  (non-Javadoc)
1298
                 * @see javax.swing.text.Document#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
1299
                 */
1300
                public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
1301
                        if (updatedModel) {
1302
                                model.setTextWritten(textWritten);
1303
                                super.insertString(offs, textWritten, a);
1304
                                
1305
                                return;
1306
                        }
1307

    
1308
                        hidePopup();
1309
                        
1310
                        boolean caretToEnd = true;
1311
                        
1312
                        if (textWritten.length() > 0) {
1313
                                if (offs < textWritten.length()) {
1314
                                        String old_textWritten = textWritten;
1315
                                        
1316
                                        // End of the text
1317
                                        textWritten = textWritten.substring(offs, textWritten.length());
1318
                                        
1319
                                        // Beginning of the text
1320
                                        if (offs > 0) {
1321
                                                // Reset (without text written)
1322
                                                textWritten = old_textWritten.substring(0, offs) + str + textWritten;
1323
                                        }
1324
                                        else {
1325
                                                
1326
                                                if (offs == 0) {
1327
                                                        caretToEnd = false;
1328
                                                }
1329
                                                
1330
                                                textWritten = str + textWritten;
1331
                                        }
1332
                                }
1333
                                else {
1334
                                        textWritten = textWritten.substring(0, offs) + str;
1335
                                }
1336
                        }
1337
                        else {
1338
                                textWritten =  str;
1339
                        }
1340
                                
1341
                        if (completeArrowKeySelectionValue == null) {
1342
                                model.setTextWritten(textWritten);
1343
                        }
1344
                        else {
1345
                                model.setTextWritten(completeArrowKeySelectionValue);
1346
                        }
1347
                        
1348
                        super.insertString(offs, str, a);
1349

    
1350
                        // Update the color of the text
1351
                        updateTextColorAndRingBeep();
1352
                
1353
                        if (model.getSize() > 0) {
1354
                                if (isShowing()) {
1355
                                        showPopup();
1356
                                }
1357
                        }
1358
                        else {
1359
                                if ((textWritten.compareTo("") != 0) && (!hidePopupIfThereAreNoItems)) {
1360
                                        showPopup();        
1361
                                }
1362
                        }
1363
                        
1364
                        if (caretToEnd) {
1365
                                // Update the caret position -> at the same place it was or at the end
1366
                                updateCaretPosition(textWritten.length());
1367
                        }
1368
                        else {
1369
                                // Update the caret position -> at the same place it was or at the end
1370
                                updateCaretPosition(str.length());
1371
                        }
1372
                }
1373
                
1374
                /*
1375
                 *  (non-Javadoc)
1376
                 * @see javax.swing.text.AbstractDocument#replace(int, int, java.lang.String, javax.swing.text.AttributeSet)
1377
                 */
1378
                public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
1379
                        String old_textWritten;
1380

    
1381
                        if (arrowKeyPressed) {
1382
                                if (!model.isShowAllItemsInListBox()) {
1383
                                        if (completeArrowKeySelection) {
1384
                                                String currentText = ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getText();
1385
                                                
1386
                                                old_textWritten = textWritten;
1387
                                                old_caretPosition = ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getCaretPosition() + text.length();
1388
                                                                
1389
                                                textWritten = "";
1390

    
1391
                                                super.remove(0, old_textWritten.length());
1392
                
1393
                                                if ((completeArrowKeySelectionValue == null) || (!currentText.equals(old_textWritten))) 
1394
                                                        completeArrowKeySelectionValue = old_textWritten;
1395

    
1396
                                                insertString(0, text, null);
1397
                                        }
1398
                                        else {
1399
                                                insertString(0, "", null);
1400
                                        }
1401
                                }
1402

    
1403
                                previousSelected = text;
1404
                                arrowKeyPressed = false;
1405

    
1406
                                return;
1407
                        }
1408

    
1409
                        completeArrowKeySelectionValue = null;
1410
                        
1411
                        // Avoid two replaces for the same operation
1412
                        if ((text.compareTo("") == 0) && (length == textWritten.length()))
1413
                                return;
1414
                        
1415
                        // Only remove if there is text to be replaced (text selected)
1416
                        if (length > 0) {
1417

    
1418
                                textOfReplacement = text;
1419
                                remove(offset, length);
1420
                        }
1421
                        else {
1422
                                if ((offset > 0) && (offset != textWritten.length())) {
1423
                                        // Must replace the dataModel for update the data in the popup correctly
1424
                                                
1425
                                        // There is a bug that when text has been removed, the elements in the popup aren seen.
1426
                                        // The solution to this bug is add them as a new dataModel
1427
                                        hidePopup();
1428

    
1429
                                        updatedModel = true;
1430

    
1431
                                        old_textWritten = textWritten;
1432
                                        old_caretPosition = ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getCaretPosition() + text.length();
1433
                                        
1434
                                        textWritten = "";
1435
                                        model.setTextWritten(textWritten);
1436

    
1437
                                        super.remove(0, old_textWritten.length());
1438
                                
1439
                                        // Stores the configuration of the flags                                
1440
                                        int itemsOrderFlag = model.getItemsOrder();
1441
                                        boolean showAllItemsInListBox = model.isShowAllItemsInListBox();
1442
                                        String languageRules = model.getLocaleRules();
1443
                                        boolean caseSensitive = model.isCaseSensitive();
1444
                                        ILookUp lookUpAgent = model.getLookUpAgent();
1445
                                        
1446
                                        // Must replace the dataModel for update the data in the popup correctly
1447
                                        comboBoxReference.setModel(new DefaultComboBoxConfigurableLookUpModel(model.getData()));
1448
                                        
1449
                                        // Restores the configuration of the flags
1450
                                        model.setItemsOrder(itemsOrderFlag);
1451
                                        model.setShowAllItemsInListBox(showAllItemsInListBox);
1452
                                        model.setLocaleRules(languageRules);
1453
                                        model.setCaseSensitive(caseSensitive);
1454
                                        model.setLookUpAgent(lookUpAgent);
1455
                                        
1456
                                        // Reset (without text written)
1457
                                        insertString(0, textWritten, null);
1458
                                        
1459
                                        // Insert the new text
1460
                                        textWritten = old_textWritten.substring(0, offset) + text + old_textWritten.substring(offset, old_textWritten.length());
1461
                                        
1462
                                        insertString(0, textWritten, null);
1463

    
1464
                                        updatedModel = false;
1465

    
1466
                                        // Update the color of the text
1467
                                        updateTextColorAndRingBeep();
1468
                                        
1469
                                        // Update the caret position -> at the same place it was or at the end
1470
                                        updateCaretPosition(old_caretPosition);
1471
                                        
1472
                                        // Only show the popup if there are items to show
1473
                                        if (model.getSize() > 0) {
1474
                                                 showPopup();
1475
                                        }
1476
                                        else {
1477
                                                if ((textWritten.compareTo("") != 0) && (!hidePopupIfThereAreNoItems)) {
1478
                                                        showPopup();        
1479
                                                }
1480
                                        }
1481
                                }
1482
                                else {
1483
                                        // Default replacement
1484
                                        super.replace(offset, length, text, attrs);
1485
                                }
1486
                        }
1487
                }
1488
                        
1489
                /*
1490
                 *  (non-Javadoc)
1491
                 * @see javax.swing.text.Document#remove(int, int)
1492
                 */
1493
                public void remove(int offs, int len) throws BadLocationException {
1494
                        String old_textWritten;
1495
                                
1496
                        // There is a bug that when text has been removed, the elements in the popup aren seen.
1497
                        // The solution to this bug is add them as a new dataModel
1498
                        hidePopup();
1499

    
1500
                        updatedModel = true;
1501

    
1502
                        old_textWritten = textWritten;
1503
                        
1504
                        old_caretPosition = Math.min(((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).getCaretPosition(), offs);
1505
                        
1506
                        textWritten = "";
1507
                        model.setTextWritten(textWritten);
1508

    
1509
                        super.remove(0, old_textWritten.length());
1510
                        
1511
                        int itemsOrderFlag = model.getItemsOrder();
1512
                        boolean showAllItemsInListBox = model.isShowAllItemsInListBox();
1513
                        String languageRules = model.getLocaleRules();
1514
                        boolean caseSensitive = model.isCaseSensitive();
1515
                        ILookUp lookUpAgent = model.getLookUpAgent();
1516
                
1517
                        // Must replace the dataModel for update the data in the popup correctly
1518
                        comboBoxReference.setModel(new DefaultComboBoxConfigurableLookUpModel(model.getData()));
1519
                        
1520
                        // Restores the configuration of the flags
1521
                        model.setItemsOrder(itemsOrderFlag);
1522
                        model.setShowAllItemsInListBox(showAllItemsInListBox);
1523
                        model.setLocaleRules(languageRules);
1524
                        model.setCaseSensitive(caseSensitive);
1525
                        model.setLookUpAgent(lookUpAgent);
1526

    
1527
                        // Reset (without text written)
1528
                        insertString(0, textWritten, null);
1529
                        
1530
                        // Insert the new text
1531
                        textWritten = old_textWritten.substring(0, offs) + textOfReplacement + old_textWritten.substring(offs + len, old_textWritten.length());
1532

    
1533
                        insertString(0, textWritten, null);
1534
                        
1535
                        updatedModel = false;
1536
                        
1537
                        // Update the color of the text
1538
                        updateTextColorAndRingBeep();
1539
                        
1540
                        // Only show the popup if there are items to show
1541
                        if (model.getSize() > 0) {
1542
                                 showPopup();
1543
                        }
1544
                        else {
1545
                                if ((textWritten.compareTo("") != 0) && (!hidePopupIfThereAreNoItems)) {
1546
                                        showPopup();        
1547
                                }
1548
                        }
1549
        
1550
//                        if (old_caretPosition == 0) {
1551
//                                // Set the caret position to the end of the text
1552
//                                updateCaretPosition(textWritten.length());
1553
//                        }
1554
//                        else {
1555
                                // Update the caret position -> at the same place it was or at the end
1556
                                updateCaretPosition(old_caretPosition + textOfReplacement.length());
1557
//                        }
1558
                        
1559
                        textOfReplacement = "";
1560
                }
1561

    
1562
                /**
1563
                 * <p>Updates the color of the text in the {@link ComboBoxEditor} of this component, and
1564
                 *  rings a beep if there is no item.</p>
1565
                 * 
1566
                 * <ul>
1567
                 *  <li><i>Text in <u>red color</u></i>: if no item matches with the text written according the <code>ILookUp</code> agent.</li>
1568
                 *  <li><i>Text in <u>black color</u></i>: if there is at least one item matches with the text written according the <code>ILookUp</code> agent.</li>
1569
                 * </ul>
1570
                 */
1571
                public void updateTextColorAndRingBeep() {
1572
                        if (! comboBoxReference.isOnlyOneColorOnText()) {
1573
                                if (model.isShowAllItemsInListBox()) {
1574
                                        // Set text into red color if no items matches with the text written, or in black color if it was in red
1575
                                        //   color and now there is almost one element that starts with the characteres written by the user
1576
                                        if (textWritten.compareTo("") != 0) {
1577
                                                if (model.getSelectedItem() == null) {
1578
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.RED);
1579
                                                        textInRedColor = true;
1580
                                                                 
1581
                                                                 // Rings a beep if allowed
1582
                                                if (beepEnabled)
1583
                                                        comboBoxReference.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
1584
                                                }
1585
                                                else {
1586
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.BLACK);
1587
                                                        textInRedColor = false;
1588
                                                }
1589
                                        }
1590
                                        else {
1591
                                                if (textInRedColor) {
1592
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.BLACK);
1593
                                                        textInRedColor = false;
1594
                                                }
1595
                                        }
1596
                                }
1597
                                else {
1598
                                        // Set text into red color if no items matches with the text written, or in black color if it was in red
1599
                                        //   color and now there is almost one element that starts with the characteres written by the user
1600
                                        if (textWritten.compareTo("") != 0) {
1601
                                                if (model.getSize() == 0) {
1602
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.RED);
1603
                                                        textInRedColor = true;
1604
                                                                 
1605
                                                                 // Rings a beep if allowed
1606
                                                if (beepEnabled)
1607
                                                        comboBoxReference.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
1608
                                                }
1609
                                                else {
1610
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.BLACK);
1611
                                                        textInRedColor = false;
1612
                                                }
1613
                                        }
1614
                                        else {
1615
                                                if (textInRedColor) {
1616
                                                        comboBoxReference.getEditor().getEditorComponent().setForeground(Color.BLACK);
1617
                                                        textInRedColor = false;
1618
                                                }
1619
                                        }
1620
                                }
1621
                        }
1622
                        else {
1623
                                // Rings a beep if no there is no item, and if it's allowed
1624
                                if ((textWritten.compareTo("") != 0) && (model.getSize() == 0)) {
1625
                                        if (beepEnabled)
1626
                                                comboBoxReference.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
1627
                                }
1628
                        }
1629
                }
1630
                
1631
                /**
1632
                 * <p>Updates the position of the caret in the {@link ComboBoxEditor} of this component.</p>
1633
                 * 
1634
                 * @param position the new position of the caret.
1635
                 */
1636
                public void updateCaretPosition(int position) {
1637
                        // If user has selected an item of the popup (using the mouse) -> set the caret position to the end of the text
1638
                        if (popupItemSelected) {
1639
                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(textWritten.length());
1640
                                popupItemSelected = false;
1641
                                return;
1642
                        }
1643
                        
1644
                        if (position > textWritten.length())
1645
                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(textWritten.length());
1646
                        else {
1647
                                ((JTextComponent) comboBoxReference.getEditor().getEditorComponent()).setCaretPosition(position);
1648
                        }
1649
                }
1650

    
1651
                /**
1652
                 * <p>Updates the color of the text in this document.</p>
1653
                 * 
1654
                 * <p>Sets text to red color if there is no item in the visible list that the <i>look up</i> agent
1655
                 *  returned with that text and the items of the dataModel, and, being enabled the second color in
1656
                 *  the configuration the the <code>JcomboBoxConfigurableLookUp</code> agent. Otherwise, the foreground color
1657
                 *  of the text will be black.</p>
1658
                 * 
1659
                 * <p>This method disables the <i>beep</i> during the process of updating the color of the text, and
1660
                 *  restores it after.</p>
1661
                 */
1662
                public void updateOnlyTextColor() {
1663
                        boolean isBeepEnabled = isBeepEnabled();
1664
                        setBeepEnabled(false);
1665
                        
1666
                        updateTextColorAndRingBeep();
1667
                                
1668
                        setBeepEnabled(isBeepEnabled);
1669
                }
1670
        }
1671
}