Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.ui / src / main / java / org / gvsig / gui / beans / editabletextcomponent / EditableTextDecorator.java @ 40561

History | View | Annotate | Download (17.4 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.gui.beans.editabletextcomponent;
25

    
26
import java.awt.event.KeyAdapter;
27
import java.awt.event.KeyEvent;
28
import java.awt.event.KeyListener;
29
import java.awt.event.MouseAdapter;
30
import java.awt.event.MouseEvent;
31
import java.beans.PropertyChangeEvent;
32
import java.beans.PropertyChangeListener;
33

    
34
import javax.swing.ComboBoxEditor;
35
import javax.swing.event.EventListenerList;
36
import javax.swing.event.UndoableEditEvent;
37
import javax.swing.event.UndoableEditListener;
38
import javax.swing.text.BadLocationException;
39
import javax.swing.text.JTextComponent;
40
import javax.swing.text.PlainDocument;
41
import javax.swing.undo.CannotRedoException;
42
import javax.swing.undo.CannotUndoException;
43
import javax.swing.undo.UndoManager;
44

    
45
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditEvent;
46
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener;
47
import org.gvsig.gui.beans.editionpopupmenu.JOptionsEditionByMousePopupMenu;
48

    
49
/**
50
 * <p>Extra functionality that allows a {@link JTextComponent JTextComponent} having graphical edition options,
51
 * which allows user to select any: <i>COPY, CUT, PASTE, SELECT ALL, REMOVE, UNDO, REDO</i> in a popup menu.</p>
52
 * 
53
 * <p>All options will be about the edition of the text in the component.</p>
54
 *  
55
 * <p><b><i>How select an edition option using the mouse:</i></b> press the right button of the mouse on the text area, and a popup with the options will be displayed. Select and option.<br>
56
 * <b><i>Default fast-access keys:</i></b> 
57
 * <ul>
58
 *  <li><b>COPY:</b> </li> CTRL + C. Copies from the text field to the clipboard.
59
 *  <li><b>CUT:</b> </li> CTRL + X. Cuts from the text field to the clipboad.
60
 *  <li><b>PASTE:</b> </li> CTRL + V. Copies from the clipboard to the text field.
61
 *  <li><b>REMOVE:</b> </li> SUPR (in spanish keyboard, and the equivalent in others). Removes the selected text.
62
 *  <li><b>SELECT ALL:</b> </li> CTRL + A. Selects all the text.
63
 *  <li><b>UNDO:</b> </li> CTRL + Z. Undoes.
64
 *  <li><b>REDO:</b> </li> SHIFT + CTRL + Z. Redoes.
65
 * </ul></p>
66
 * 
67
 * <p>This component by default stores 100 undo/redo actions. This value can be modified.</p>
68
 * 
69
 * <p>A developer would have to re-implement {@linkplain #defineEditorKeyListener() #defineEditorKeyListener()}, creating
70
 *  a new <code>editorKeyListener</code> for changing any fast-access key.</p>
71
 * 
72
 * @version 27/02/2008
73
 * @author Pablo Piqueras Bartolom? (pablo.piqueras@iver.es)
74
 */
75
public class EditableTextDecorator implements IEditableText {
76
        // CONSTANTS
77
        public static final int DEFAULT_UNDO_REDO_LIMIT_ACTIONS = 100;
78
        // END CONSTANTS
79

    
80
        // A POPUPMENU FOR EDITION OPTIONS
81
        private JOptionsEditionByMousePopupMenu optionsEditionByMouse;
82
    // END A POPUPMENU FOR EDITION OPTIONS
83
        
84
        // UNDO-REDO
85
        private UndoManager undoManager;
86
        private int undoRedoLimitActions;
87
        // END UNDO-REDO
88
        
89
        // LISTENERS
90
        protected KeyListener editorKeyListener;
91
        private MouseAdapter editorMouseListener;
92
        private PropertyChangeListener editionMenuListener;
93
        private EventListenerList undoRedoEditListeners;
94
        // END LISTENERS
95
        
96
        // REFERENCE TO THE JTEXTCOMPONENT
97
        private JTextComponent jTextComponent;
98
        // END REFERENCE TO THE JTEXTCOMPONENT
99
        
100
        /**
101
         * Default constructor
102
         */
103
        public EditableTextDecorator(JTextComponent jTextComponent) {
104
                super();
105

    
106
                // Reference to the JTextComponent
107
                this.jTextComponent = jTextComponent;
108
                
109
                initialize();
110
                
111
                // Other configuration tasks
112
                this.createDefaultListeners();
113
                this.configure();
114
        }
115
        
116
        /**
117
         * This method sets the start values of inner attributes and creates the necessary inner objects
118
         */
119
        private void initialize() {
120
                undoRedoEditListeners = new EventListenerList();
121
                
122
        // Allows user to edit
123
                jTextComponent.setEditable(true);
124
                
125
                // Text options edition popupmenu initialization
126
                optionsEditionByMouse = new JOptionsEditionByMousePopupMenu();
127
                
128
                // Undo-Redo initialization
129
                undoManager = new UndoManager();
130
                undoRedoLimitActions = DEFAULT_UNDO_REDO_LIMIT_ACTIONS; // By default is 1
131
                undoManager.setLimit(undoRedoLimitActions);
132
        }
133
        
134
        /**
135
         * Creation of default listeners that will use the component
136
         */
137
        private void createDefaultListeners() {
138
                // Defines a listener for the PopupMenu of text edition options: when a change is produced in that component, it fires an event
139
                //   and this listener captures it
140
                defineEditionMenuPropertyChangeListener();
141

    
142
        // Defines a key listener for the editor of this component
143
        defineEditorKeyListener();
144

    
145
        // Defines a mouse listener for the editor of this component
146
        defineEditorMouseListener();
147
        }
148
        
149
        /**
150
         * Defines a listener for the PopupMenu of text edition options: when a change is produced in that component, it fires an event
151
         *    and this listener captures and treats it
152
         */
153
        private void defineEditionMenuPropertyChangeListener() {
154
                editionMenuListener = new PropertyChangeListener() {
155
                        /*
156
                         * (non-Javadoc)
157
                         * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
158
                         */
159
                        public void propertyChange(PropertyChangeEvent arg0) {
160
                                if (arg0.getPropertyName().equals(JOptionsEditionByMousePopupMenu.SELECTEDOPTION)) {
161
                                                                    
162
                                  switch(Integer.parseInt(arg0.getNewValue().toString()))
163
                                  {
164
                                          case JOptionsEditionByMousePopupMenu.UNDO:
165
                                                  undoOperationLogic();
166
                                                  break;
167
                                          case JOptionsEditionByMousePopupMenu.REDO:
168
                                                  redoOperationLogic();
169
                                                  break;
170
                                          case JOptionsEditionByMousePopupMenu.CUT:
171
                                                  jTextComponent.cut();
172
                                                  break;
173
                                          case JOptionsEditionByMousePopupMenu.COPY:
174
                                                  jTextComponent.copy();
175
                                                  break;
176
                                          case JOptionsEditionByMousePopupMenu.PASTE:
177
                                                  jTextComponent.paste();
178
                                                  break;
179
                                          case JOptionsEditionByMousePopupMenu.DELETE:
180
                                                  deleteTextLogic();
181
                                                  break;
182
                                          case JOptionsEditionByMousePopupMenu.SELECT_ALL:
183
                                                  jTextComponent.selectAll();
184
                                                  break;
185
                                          default: // do anything
186
                                  }
187
                                }
188
/*
189
                                else
190
                                {
191
                                        if (arg0.getPropertyName().equals(JOptionsEditionByMousePopupMenu.VISIBILITYCHANGED))
192
                                        {
193
                                                // First True, after False (when false -> optionsEditorWasVisible = true)
194
                                                if (!optionsEditionByMouse.isVisible())
195
                                                        optionsEditorWasVisible = true;
196
                                                else
197
                                                        optionsEditorWasVisible = false;
198

199
                                        }
200
                                }
201
*/
202
                        }                        
203
                };
204
        }
205

    
206
        /**
207
         * Defines a key listener for the editor of the text component.
208
         */
209
        protected void defineEditorKeyListener() {
210
                editorKeyListener = new KeyAdapter() {
211
                        /*
212
                         * (non-Javadoc)
213
                         * @see java.awt.event.KeyAdapter#keyPressed(java.awt.event.KeyEvent)
214
                         */
215
                        public void keyPressed(KeyEvent ke)  // Executed on the Start view state or Search view state
216
                        {
217
                                // COPY, CUT, PASTE, SELECT ALL, UNDO AND REDO WITH THE KEYBOARD (Combination of keys; REMOVE is implemented after: KV_DELETE (supr spanish key))
218
                                if (ke.isControlDown())
219
                                {
220
                                        // COPY
221
                                    if (ke.getKeyCode() == KeyEvent.VK_C) {
222
                                            jTextComponent.copy();
223
                                            ke.consume();
224
                                            return;
225
                                    }
226
                                    
227
                                    // CUT
228
                                    if (ke.getKeyCode() == KeyEvent.VK_X) {
229
                                            jTextComponent.cut();
230
                                            ke.consume();
231
                                            return;
232
                                    }
233
                                    
234
                                    // PASTE
235
                                    if (ke.getKeyCode() == KeyEvent.VK_V) {
236
                                            jTextComponent.paste();
237
                                            ke.consume();
238
                                            return;
239
                                    }
240
                                    
241
                                    // SELECT ALL
242
                                    if (ke.getKeyCode() == KeyEvent.VK_A) {
243
                                            jTextComponent.selectAll();
244
                                            ke.consume();
245
                                            return;
246
                                    }
247

    
248
                                    // UNDO OR REDO
249
                                    if (ke.getKeyCode() == KeyEvent.VK_Z) {
250
                                            if (ke.isShiftDown()) // REDO
251
                                                    redoOperationLogic();                                                            
252
                                            else //UNDO
253
                                                    undoOperationLogic();
254
                                            
255
                                            ke.consume();
256
                                            return;
257
                                     }
258
                                }
259
                                
260
                                // According the key pressed, do some actions or others
261
                                switch (ke.getKeyCode())
262
                                {
263
                                        case KeyEvent.VK_DELETE : // 'supr' key in spanish keyboard
264
                                                deleteTextLogic();
265
                                                ke.consume();
266
                                        break;
267
                                }
268
                        }
269
                };
270
        }
271

    
272
        /**
273
         * Defines a mouse listener for the editor of the text component.
274
         */
275
        private void defineEditorMouseListener() {
276
                editorMouseListener = new MouseAdapter() {
277
                        /*
278
                         * (non-Javadoc)
279
                         * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
280
                         */
281
                        public void mouseClicked(MouseEvent e) {
282
        
283
                                if (e.getButton() == MouseEvent.BUTTON3) {                            
284
                            // By default disable all options
285
                            optionsEditionByMouse.setEnabledAllOptions(false);
286
                            
287
                            // Enable the "Undo" option if there is any previous state to restore
288
                            if (undoManager.canUndo())
289
                                    optionsEditionByMouse.setEnabledUndoOption(true);
290
                            
291
                            // Enable the "Redo" option if there is any later state to restore
292
                            if (undoManager.canRedo())
293
                                    optionsEditionByMouse.setEnabledRedoOption(true);
294
                            
295
                            // Enable the "Copy", "Cut" and "Delete" options if there is text selected
296
                            if (jTextComponent.getCaretPosition() != jTextComponent.getCaret().getMark())
297
                            {
298
                                    optionsEditionByMouse.setEnabledCopyOption(true);
299
                                    optionsEditionByMouse.setEnabledCutOption(true);
300
                                    optionsEditionByMouse.setEnabledDeleteOption(true);
301
                            }
302
                            
303
                            // Enable the "Paste" option if there is text on the system's clipboard
304
                            if ( jTextComponent.getToolkit().getSystemClipboard().getContents(this).toString().length() > 0 )
305
                                    optionsEditionByMouse.setEnabledPasteOption(true);//
306
                            
307
                            // Enable the "Select-All" option (by default it's always enabled)
308
                            optionsEditionByMouse.setEnabledSelectAllOption(true);
309
                            
310
                                        optionsEditionByMouse.setLocation((int)jTextComponent.getLocationOnScreen().getX() + e.getX(), (int)jTextComponent.getLocationOnScreen().getY() + e.getY());
311
                            optionsEditionByMouse.setInvoker(jTextComponent);
312
                            optionsEditionByMouse.setVisible(true);
313
                    }
314
                }
315
                };
316
        }
317
        
318
        /**
319
         * Configures the component and some of its parts
320
         */
321
        private void configure() {
322
                // Configures the document of the editor of this component
323
                PlainDocument document = this.configureDocument();
324

    
325
                // Configures the editor (ComboBoxEditor) of this component
326
                configureEditor(document);
327
                
328
                // Configures the text-edition-options popupmenu
329
                configureOptionsEditionByMouse();
330
        }
331

    
332
        /**
333
         * Configures the document of the editor of the text component
334
         */
335
        private PlainDocument configureDocument() {
336
                // Creates the document of the editor of this component
337
                PlainDocument document = new PlainDocument();
338
        
339
                // Configures the document
340
                configureUndoManager(document);
341
                
342
                return document;
343
        }
344
        
345
        /**
346
         * Configures the editor {@link ComboBoxEditor ComboBoxEditor} of the text component.
347
         * 
348
         * @param document the document to display/edit
349
         */
350
        private void configureEditor(PlainDocument document) {
351
        if (jTextComponent != null) {
352
                   // Removes some previous listeners and adds some new others:     
353
                
354
                // Adds the new Key Listener (tries to remove it if it existed before)
355
                jTextComponent.removeKeyListener(this.editorKeyListener);
356
                jTextComponent.addKeyListener(this.editorKeyListener);
357
                
358
                   // Adds the new Mouse Listener (tries to remove it if it existed before)
359
                jTextComponent.removeMouseListener(this.editorMouseListener);
360
                jTextComponent.addMouseListener(this.editorMouseListener);
361
                
362
                // Adds the new document (tries to remove it if it existed before)
363
                jTextComponent.setDocument(document);
364
        }
365
        }
366

    
367
        /** 
368
         * Configures the text-edition-options popup menu.
369
         */
370
        private void configureOptionsEditionByMouse() {
371
                this.optionsEditionByMouse.addPropertyChangeListener(editionMenuListener);
372
        }
373
        
374
        /**
375
         * Configures the UndoManager for Undo-Redo operations.
376
         */
377
        private void configureUndoManager(PlainDocument document) {
378
        // Listen for undo and redo events
379
        document.addUndoableEditListener(new UndoableEditListener() {
380
                /*
381
                 * (non-Javadoc)
382
                 * @see javax.swing.event.UndoableEditListener#undoableEditHappened(javax.swing.event.UndoableEditEvent)
383
                 */
384
            public void undoableEditHappened(UndoableEditEvent evt) {
385
                    undoManager.addEdit(evt.getEdit());
386
            }
387
        });
388
        }
389
        
390
        /**
391
         * This method has the logic for a "undo" operation.
392
         */
393
        private void undoOperationLogic() {
394
                try {
395
            if (undoManager.canUndo()) {
396
                    String previousText = jTextComponent.getText();
397
                    undoManager.undo();
398
                            String newText = jTextComponent.getText();
399
                            fireUndoRedoEditUpdate(new UndoRedoEditEvent(jTextComponent, UndoRedoEditEvent.UNDO, previousText, newText));
400
            }
401
                } catch (CannotUndoException e) {
402
                e.printStackTrace();
403
        }
404
        }
405
        
406
        /**
407
         * This method has the logic for a "redo" operation.
408
         */
409
        private void redoOperationLogic() {
410
        try {
411
            if (undoManager.canRedo()) {
412
                    String previousText = jTextComponent.getText();
413
                            undoManager.redo();
414
                            String newText = jTextComponent.getText();
415
                            fireUndoRedoEditUpdate(new UndoRedoEditEvent(jTextComponent, UndoRedoEditEvent.REDO, previousText, newText));
416
            }
417
            } catch (CannotRedoException e) {
418
                    e.printStackTrace();
419
            }
420
        }
421

    
422
        ////// OTHER METHODS //////
423

    
424
        /*
425
         * (non-Javadoc)
426
         * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#setUndoRedoLimitActions(int)
427
         */
428
        public void setUndoRedoLimitActions(int limit) {
429
            this.undoRedoLimitActions = limit;
430
            undoManager.setLimit(undoRedoLimitActions);
431
    }
432
    
433
        /*
434
         * (non-Javadoc)
435
         * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#getUndoRedoLimitActions()
436
         */
437
        public int getUndoRedoLimitActions() {
438
            return this.undoRedoLimitActions;
439
    }
440
     
441
        /**
442
         * This method is invoked when some text has to be removed: With the 'Delete' option of the text-edition-popupmenu or with the 'delete' ('supr'
443
         *    in spanish keyboard) key.
444
         */
445
        private void deleteTextLogic() {
446
                //                 Get the new text:                
447
                  try {
448
                          PlainDocument document = (PlainDocument)jTextComponent.getDocument();
449
                          int caretPosition = jTextComponent.getCaretPosition();
450
                          int markPosition = jTextComponent.getCaret().getMark();
451
                            
452
                          int min_index = Math.min(caretPosition, markPosition);
453
                          int length = Math.abs(caretPosition - markPosition);
454
                            
455
                          document.remove(min_index, length);
456
                  } catch (BadLocationException e) {
457
                          e.printStackTrace();
458
                  }
459
        }
460
        
461
   /*
462
    * (non-Javadoc)
463
    * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#addUndoRedoEditListener(org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener)
464
    */
465
    public void addUndoRedoEditListener(UndoRedoEditListener listener) {
466
            undoRedoEditListeners.add(UndoRedoEditListener.class, listener);
467
    }
468

    
469
    /*
470
     * (non-Javadoc)
471
     * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#removeCaretListener(org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener)
472
     */
473
    public void removeUndoRedoEditListener(UndoRedoEditListener listener) {
474
            undoRedoEditListeners.remove(UndoRedoEditListener.class, listener);
475
    }
476

    
477
    /*
478
     * (non-Javadoc)
479
     * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#getUndoRedoEditListeners()
480
     */
481
    public UndoRedoEditListener[] getUndoRedoEditListeners() {
482
        return (UndoRedoEditListener[])undoRedoEditListeners.getListeners(UndoRedoEditListener.class);
483
    }
484
    
485
    /**
486
     * Notifies all listeners that have registered interest for
487
     * notification on this event type.  The event instance 
488
     * is lazily created using the parameters passed into 
489
     * the fire method.  The listener list is processed in a
490
     * last-to-first manner.
491
     *
492
     * @param e the event
493
     * @see EventListenerList
494
     */
495
    protected void fireUndoRedoEditUpdate(UndoRedoEditEvent e) {
496
        // Guaranteed to return a non-null array
497
        Object[] listeners = undoRedoEditListeners.getListenerList();
498
        
499
            if (listeners != null) {
500
                for (int i = 1; i < listeners.length; i+=2) {
501
                        if (listeners[i] instanceof UndoRedoEditListener) {
502
                  ((UndoRedoEditListener)listeners[i]).operationExecuted(e);
503
              }
504
                }
505
            }
506
    }
507
    ////// END OTHER METHODS //////
508
}