Statistics
| Revision:

root / trunk / libraries / libUIComponent / src / org / gvsig / gui / beans / editabletextcomponent / EditableTextDecorator.java @ 19356

History | View | Annotate | Download (17.6 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.gui.beans.editabletextcomponent;
42

    
43
import java.awt.event.KeyAdapter;
44
import java.awt.event.KeyEvent;
45
import java.awt.event.KeyListener;
46
import java.awt.event.MouseAdapter;
47
import java.awt.event.MouseEvent;
48
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeListener;
50

    
51
import javax.swing.ComboBoxEditor;
52
import javax.swing.event.EventListenerList;
53
import javax.swing.event.UndoableEditEvent;
54
import javax.swing.event.UndoableEditListener;
55
import javax.swing.text.BadLocationException;
56
import javax.swing.text.JTextComponent;
57
import javax.swing.text.PlainDocument;
58
import javax.swing.undo.CannotRedoException;
59
import javax.swing.undo.CannotUndoException;
60
import javax.swing.undo.UndoManager;
61

    
62
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditEvent;
63
import org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener;
64
import org.gvsig.gui.beans.editionpopupmenu.JOptionsEditionByMousePopupMenu;
65

    
66
/**
67
 * <p>Extra functionality that allows a {@link JTextComponent JTextComponent} having graphical edition options,
68
 * which allows user to select any: <i>COPY, CUT, PASTE, SELECT ALL, REMOVE, UNDO, REDO</i> in a popup menu.</p>
69
 * 
70
 * <p>All options will be about the edition of the text in the component.</p>
71
 *  
72
 * <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>
73
 * <b><i>Default fast-access keys:</i></b> 
74
 * <ul>
75
 *  <li><b>COPY:</b> </li> CTRL + C. Copies from the text field to the clipboard.
76
 *  <li><b>CUT:</b> </li> CTRL + X. Cuts from the text field to the clipboad.
77
 *  <li><b>PASTE:</b> </li> CTRL + V. Copies from the clipboard to the text field.
78
 *  <li><b>REMOVE:</b> </li> SUPR (in spanish keyboard, and the equivalent in others). Removes the selected text.
79
 *  <li><b>SELECT ALL:</b> </li> CTRL + A. Selects all the text.
80
 *  <li><b>UNDO:</b> </li> CTRL + Z. Undoes.
81
 *  <li><b>REDO:</b> </li> SHIFT + CTRL + Z. Redoes.
82
 * </ul></p>
83
 * 
84
 * <p>This component by default stores 100 undo/redo actions. This value can be modified.</p>
85
 * 
86
 * <p>A developer would have to re-implement {@linkplain #defineEditorKeyListener() #defineEditorKeyListener()}, creating
87
 *  a new <code>editorKeyListener</code> for changing any fast-access key.</p>
88
 * 
89
 * @version 27/02/2008
90
 * @author Pablo Piqueras Bartolom? (pablo.piqueras@iver.es)
91
 */
92
public class EditableTextDecorator implements IEditableText {
93
        // CONSTANTS
94
        public static final int DEFAULT_UNDO_REDO_LIMIT_ACTIONS = 100;
95
        // END CONSTANTS
96

    
97
        // A POPUPMENU FOR EDITION OPTIONS
98
        private JOptionsEditionByMousePopupMenu optionsEditionByMouse;
99
    // END A POPUPMENU FOR EDITION OPTIONS
100
        
101
        // UNDO-REDO
102
        private UndoManager undoManager;
103
        private int undoRedoLimitActions;
104
        // END UNDO-REDO
105
        
106
        // LISTENERS
107
        protected KeyListener editorKeyListener;
108
        private MouseAdapter editorMouseListener;
109
        private PropertyChangeListener editionMenuListener;
110
        private EventListenerList undoRedoEditListeners;
111
        // END LISTENERS
112
        
113
        // REFERENCE TO THE JTEXTCOMPONENT
114
        private JTextComponent jTextComponent;
115
        // END REFERENCE TO THE JTEXTCOMPONENT
116
        
117
        /**
118
         * Default constructor
119
         */
120
        public EditableTextDecorator(JTextComponent jTextComponent) {
121
                super();
122

    
123
                // Reference to the JTextComponent
124
                this.jTextComponent = jTextComponent;
125
                
126
                initialize();
127
                
128
                // Other configuration tasks
129
                this.createDefaultListeners();
130
                this.configure();
131
        }
132
        
133
        /**
134
         * This method sets the start values of inner attributes and creates the necessary inner objects
135
         */
136
        private void initialize() {
137
                undoRedoEditListeners = new EventListenerList();
138
                
139
        // Allows user to edit
140
                jTextComponent.setEditable(true);
141
                
142
                // Text options edition popupmenu initialization
143
                optionsEditionByMouse = new JOptionsEditionByMousePopupMenu();
144
                
145
                // Undo-Redo initialization
146
                undoManager = new UndoManager();
147
                undoRedoLimitActions = DEFAULT_UNDO_REDO_LIMIT_ACTIONS; // By default is 1
148
                undoManager.setLimit(undoRedoLimitActions);
149
        }
150
        
151
        /**
152
         * Creation of default listeners that will use the component
153
         */
154
        private void createDefaultListeners() {
155
                // Defines a listener for the PopupMenu of text edition options: when a change is produced in that component, it fires an event
156
                //   and this listener captures it
157
                defineEditionMenuPropertyChangeListener();
158

    
159
        // Defines a key listener for the editor of this component
160
        defineEditorKeyListener();
161

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

216
                                        }
217
                                }
218
*/
219
                        }                        
220
                };
221
        }
222

    
223
        /**
224
         * Defines a key listener for the editor of the text component.
225
         */
226
        protected void defineEditorKeyListener() {
227
                editorKeyListener = new KeyAdapter() {
228
                        /*
229
                         * (non-Javadoc)
230
                         * @see java.awt.event.KeyAdapter#keyPressed(java.awt.event.KeyEvent)
231
                         */
232
                        public void keyPressed(KeyEvent ke)  // Executed on the Start view state or Search view state
233
                        {
234
                                // COPY, CUT, PASTE, SELECT ALL, UNDO AND REDO WITH THE KEYBOARD (Combination of keys; REMOVE is implemented after: KV_DELETE (supr spanish key))
235
                                if (ke.isControlDown())
236
                                {
237
                                        // COPY
238
                                    if (ke.getKeyCode() == KeyEvent.VK_C) {
239
                                            jTextComponent.copy();
240
                                            ke.consume();
241
                                            return;
242
                                    }
243
                                    
244
                                    // CUT
245
                                    if (ke.getKeyCode() == KeyEvent.VK_X) {
246
                                            jTextComponent.cut();
247
                                            ke.consume();
248
                                            return;
249
                                    }
250
                                    
251
                                    // PASTE
252
                                    if (ke.getKeyCode() == KeyEvent.VK_V) {
253
                                            jTextComponent.paste();
254
                                            ke.consume();
255
                                            return;
256
                                    }
257
                                    
258
                                    // SELECT ALL
259
                                    if (ke.getKeyCode() == KeyEvent.VK_A) {
260
                                            jTextComponent.selectAll();
261
                                            ke.consume();
262
                                            return;
263
                                    }
264

    
265
                                    // UNDO OR REDO
266
                                    if (ke.getKeyCode() == KeyEvent.VK_Z) {
267
                                            if (ke.isShiftDown()) // REDO
268
                                                    redoOperationLogic();                                                            
269
                                            else //UNDO
270
                                                    undoOperationLogic();
271
                                            
272
                                            ke.consume();
273
                                            return;
274
                                     }
275
                                }
276
                                
277
                                // According the key pressed, do some actions or others
278
                                switch (ke.getKeyCode())
279
                                {
280
                                        case KeyEvent.VK_DELETE : // 'supr' key in spanish keyboard
281
                                                deleteTextLogic();
282
                                                ke.consume();
283
                                        break;
284
                                }
285
                        }
286
                };
287
        }
288

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

    
342
                // Configures the editor (ComboBoxEditor) of this component
343
                configureEditor(document);
344
                
345
                // Configures the text-edition-options popupmenu
346
                configureOptionsEditionByMouse();
347
        }
348

    
349
        /**
350
         * Configures the document of the editor of the text component
351
         */
352
        private PlainDocument configureDocument() {
353
                // Creates the document of the editor of this component
354
                PlainDocument document = new PlainDocument();
355
        
356
                // Configures the document
357
                configureUndoManager(document);
358
                
359
                return document;
360
        }
361
        
362
        /**
363
         * Configures the editor {@link ComboBoxEditor ComboBoxEditor} of the text component.
364
         * 
365
         * @param document the document to display/edit
366
         */
367
        private void configureEditor(PlainDocument document) {
368
        if (jTextComponent != null) {
369
                   // Removes some previous listeners and adds some new others:     
370
                
371
                // Adds the new Key Listener (tries to remove it if it existed before)
372
                jTextComponent.removeKeyListener(this.editorKeyListener);
373
                jTextComponent.addKeyListener(this.editorKeyListener);
374
                
375
                   // Adds the new Mouse Listener (tries to remove it if it existed before)
376
                jTextComponent.removeMouseListener(this.editorMouseListener);
377
                jTextComponent.addMouseListener(this.editorMouseListener);
378
                
379
                // Adds the new document (tries to remove it if it existed before)
380
                jTextComponent.setDocument(document);
381
        }
382
        }
383

    
384
        /** 
385
         * Configures the text-edition-options popup menu.
386
         */
387
        private void configureOptionsEditionByMouse() {
388
                this.optionsEditionByMouse.addPropertyChangeListener(editionMenuListener);
389
        }
390
        
391
        /**
392
         * Configures the UndoManager for Undo-Redo operations.
393
         */
394
        private void configureUndoManager(PlainDocument document) {
395
        // Listen for undo and redo events
396
        document.addUndoableEditListener(new UndoableEditListener() {
397
                /*
398
                 * (non-Javadoc)
399
                 * @see javax.swing.event.UndoableEditListener#undoableEditHappened(javax.swing.event.UndoableEditEvent)
400
                 */
401
            public void undoableEditHappened(UndoableEditEvent evt) {
402
                    undoManager.addEdit(evt.getEdit());
403
            }
404
        });
405
        }
406
        
407
        /**
408
         * This method has the logic for a "undo" operation.
409
         */
410
        private void undoOperationLogic() {
411
                try {
412
            if (undoManager.canUndo()) {
413
                    String previousText = jTextComponent.getText();
414
                    undoManager.undo();
415
                            String newText = jTextComponent.getText();
416
                            fireUndoRedoEditUpdate(new UndoRedoEditEvent(jTextComponent, UndoRedoEditEvent.UNDO, previousText, newText));
417
            }
418
                } catch (CannotUndoException e) {
419
                e.printStackTrace();
420
        }
421
        }
422
        
423
        /**
424
         * This method has the logic for a "redo" operation.
425
         */
426
        private void redoOperationLogic() {
427
        try {
428
            if (undoManager.canRedo()) {
429
                    String previousText = jTextComponent.getText();
430
                            undoManager.redo();
431
                            String newText = jTextComponent.getText();
432
                            fireUndoRedoEditUpdate(new UndoRedoEditEvent(jTextComponent, UndoRedoEditEvent.REDO, previousText, newText));
433
            }
434
            } catch (CannotRedoException e) {
435
                    e.printStackTrace();
436
            }
437
        }
438

    
439
        ////// OTHER METHODS //////
440

    
441
        /*
442
         * (non-Javadoc)
443
         * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#setUndoRedoLimitActions(int)
444
         */
445
        public void setUndoRedoLimitActions(int limit) {
446
            this.undoRedoLimitActions = limit;
447
            undoManager.setLimit(undoRedoLimitActions);
448
    }
449
    
450
        /*
451
         * (non-Javadoc)
452
         * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#getUndoRedoLimitActions()
453
         */
454
        public int getUndoRedoLimitActions() {
455
            return this.undoRedoLimitActions;
456
    }
457
     
458
        /**
459
         * 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'
460
         *    in spanish keyboard) key.
461
         */
462
        private void deleteTextLogic() {
463
                //                 Get the new text:                
464
                  try {
465
                          PlainDocument document = (PlainDocument)jTextComponent.getDocument();
466
                          int caretPosition = jTextComponent.getCaretPosition();
467
                          int markPosition = jTextComponent.getCaret().getMark();
468
                            
469
                          int min_index = Math.min(caretPosition, markPosition);
470
                          int length = Math.abs(caretPosition - markPosition);
471
                            
472
                          document.remove(min_index, length);
473
                  } catch (BadLocationException e) {
474
                          e.printStackTrace();
475
                  }
476
        }
477
        
478
   /*
479
    * (non-Javadoc)
480
    * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#addUndoRedoEditListener(org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener)
481
    */
482
    public void addUndoRedoEditListener(UndoRedoEditListener listener) {
483
            undoRedoEditListeners.add(UndoRedoEditListener.class, listener);
484
    }
485

    
486
    /*
487
     * (non-Javadoc)
488
     * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#removeCaretListener(org.gvsig.gui.beans.editabletextcomponent.event.UndoRedoEditListener)
489
     */
490
    public void removeUndoRedoEditListener(UndoRedoEditListener listener) {
491
            undoRedoEditListeners.remove(UndoRedoEditListener.class, listener);
492
    }
493

    
494
    /*
495
     * (non-Javadoc)
496
     * @see org.gvsig.gui.beans.editabletextcomponent.IEditableText#getUndoRedoEditListeners()
497
     */
498
    public UndoRedoEditListener[] getUndoRedoEditListeners() {
499
        return (UndoRedoEditListener[])undoRedoEditListeners.getListeners(UndoRedoEditListener.class);
500
    }
501
    
502
    /**
503
     * Notifies all listeners that have registered interest for
504
     * notification on this event type.  The event instance 
505
     * is lazily created using the parameters passed into 
506
     * the fire method.  The listener list is processed in a
507
     * last-to-first manner.
508
     *
509
     * @param e the event
510
     * @see EventListenerList
511
     */
512
    protected void fireUndoRedoEditUpdate(UndoRedoEditEvent e) {
513
        // Guaranteed to return a non-null array
514
        Object[] listeners = undoRedoEditListeners.getListenerList();
515
        
516
            if (listeners != null) {
517
                for (int i = 1; i < listeners.length; i+=2) {
518
                        if (listeners[i] instanceof UndoRedoEditListener) {
519
                  ((UndoRedoEditListener)listeners[i]).operationExecuted(e);
520
              }
521
                }
522
            }
523
    }
524
    ////// END OTHER METHODS //////
525
}