KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > swing > JXEditorPane


1 /*
2  * $Id: JXEditorPane.java,v 1.3 2004/09/22 15:49:17 davidson1 Exp $
3  *
4  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.swing;
9
10 import java.awt.Component JavaDoc;
11 import java.awt.Dimension JavaDoc;
12
13 import java.awt.datatransfer.DataFlavor JavaDoc;
14 import java.awt.datatransfer.Clipboard JavaDoc;
15 import java.awt.datatransfer.Transferable JavaDoc;
16
17 import java.awt.event.ActionEvent JavaDoc;
18 import java.awt.event.ItemEvent JavaDoc;
19 import java.awt.event.ItemListener JavaDoc;
20
21 import java.beans.PropertyChangeEvent JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23
24 import java.io.IOException JavaDoc;
25 import java.io.Reader JavaDoc;
26
27 import java.net.URL JavaDoc;
28
29 import java.util.Vector JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import java.util.regex.Matcher JavaDoc;
35 import java.util.regex.Pattern JavaDoc;
36
37 import javax.swing.*;
38
39 import javax.swing.undo.CannotRedoException JavaDoc;
40 import javax.swing.undo.CannotUndoException JavaDoc;
41 import javax.swing.undo.UndoManager JavaDoc;
42
43 import javax.swing.event.CaretEvent JavaDoc;
44 import javax.swing.event.CaretListener JavaDoc;
45 import javax.swing.event.UndoableEditEvent JavaDoc;
46 import javax.swing.event.UndoableEditListener JavaDoc;
47
48 import javax.swing.text.AttributeSet JavaDoc;
49 import javax.swing.text.Document JavaDoc;
50 import javax.swing.text.EditorKit JavaDoc;
51 import javax.swing.text.Element JavaDoc;
52 import javax.swing.text.MutableAttributeSet JavaDoc;
53 import javax.swing.text.Segment JavaDoc;
54 import javax.swing.text.SimpleAttributeSet JavaDoc;
55 import javax.swing.text.StyleConstants JavaDoc;
56 import javax.swing.text.StyledDocument JavaDoc;
57 import javax.swing.text.StyledEditorKit JavaDoc;
58
59 import javax.swing.text.html.HTML JavaDoc;
60 import javax.swing.text.html.HTMLDocument JavaDoc;
61
62 import org.jdesktop.swing.actions.ActionManager;
63
64 /**
65  * An extended editor pane which has the following features built in:
66  * <ul>
67  * <li>Text search
68  * <li>undo/redo
69  * <li>simple html/plain text editing
70  * </ul>
71  *
72  * @author Mark Davidson
73  */

74 public class JXEditorPane extends JEditorPane implements Searchable {
75
76     private Matcher JavaDoc matcher;
77
78     private UndoableEditListener JavaDoc undoHandler;
79     private UndoManager JavaDoc undoManager;
80     private CaretListener JavaDoc caretHandler;
81     private JComboBox selector;
82
83     // The ids of supported actions. Perhaps this should be public.
84
private final static String JavaDoc ACTION_FIND = "find";
85     private final static String JavaDoc ACTION_UNDO = "undo";
86     private final static String JavaDoc ACTION_REDO = "redo";
87
88     public JXEditorPane() {
89         init();
90     }
91
92     public JXEditorPane(String JavaDoc url) throws IOException JavaDoc {
93         super(url);
94         init();
95     }
96
97     public JXEditorPane(String JavaDoc type, String JavaDoc text) {
98         super(type, text);
99         init();
100     }
101
102     public JXEditorPane(URL JavaDoc initialPage) throws IOException JavaDoc {
103         super(initialPage);
104         init();
105     }
106
107     private void init() {
108         addPropertyChangeListener(new PropertyHandler());
109         getDocument().addUndoableEditListener(getUndoableEditListener());
110         initActions();
111     }
112
113     private class PropertyHandler implements PropertyChangeListener JavaDoc {
114         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
115             String JavaDoc name = evt.getPropertyName();
116             if (name.equals("document")) {
117                 Document JavaDoc doc = (Document JavaDoc)evt.getOldValue();
118                 if (doc != null) {
119                     doc.removeUndoableEditListener(getUndoableEditListener());
120                 }
121
122                 doc = (Document JavaDoc)evt.getNewValue();
123                 if (doc != null) {
124                     doc.addUndoableEditListener(getUndoableEditListener());
125                 }
126             }
127         }
128
129     }
130
131     // pp for testing
132
CaretListener JavaDoc getCaretListener() {
133         return caretHandler;
134     }
135
136     // pp for testing
137
UndoableEditListener JavaDoc getUndoableEditListener() {
138         if (undoHandler == null) {
139             undoHandler = new UndoHandler();
140             undoManager = new UndoManager JavaDoc();
141         }
142         return undoHandler;
143     }
144
145     /**
146      * Overidden to perform document initialization based on type.
147      */

148     public void setEditorKit(EditorKit JavaDoc kit) {
149         super.setEditorKit(kit);
150
151         if (kit instanceof StyledEditorKit JavaDoc) {
152             if (caretHandler == null) {
153                 caretHandler = new CaretHandler();
154             }
155             addCaretListener(caretHandler);
156         }
157     }
158
159     /**
160      * Register the actions that this class can handle.
161      */

162     protected void initActions() {
163         ActionMap map = getActionMap();
164         map.put(ACTION_FIND, new Actions(ACTION_FIND));
165         map.put(ACTION_UNDO, new Actions(ACTION_UNDO));
166         map.put(ACTION_REDO, new Actions(ACTION_REDO));
167     }
168
169     // undo/redo implementation
170

171     private class UndoHandler implements UndoableEditListener JavaDoc {
172         public void undoableEditHappened(UndoableEditEvent JavaDoc evt) {
173             undoManager.addEdit(evt.getEdit());
174             updateActionState();
175         }
176     }
177
178     /**
179      * Updates the state of the actions in response to an undo/redo operation.
180      */

181     private void updateActionState() {
182         // Update the state of the undo and redo actions
183
Runnable JavaDoc doEnabled = new Runnable JavaDoc() {
184                 public void run() {
185                     ActionManager manager = Application.getInstance().getActionManager();
186                     manager.setEnabled(ACTION_UNDO, undoManager.canUndo());
187                     manager.setEnabled(ACTION_REDO, undoManager.canRedo());
188                 }
189             };
190         SwingUtilities.invokeLater(doEnabled);
191     }
192
193     /**
194      * A small class which dispatches actions.
195      * TODO: Is there a way that we can make this static?
196      */

197     private class Actions extends UIAction {
198         Actions(String JavaDoc name) {
199             super(name);
200         }
201
202         public void actionPerformed(ActionEvent JavaDoc evt) {
203             String JavaDoc name = getName();
204             if (ACTION_FIND.equals(name)) {
205                 find();
206             }
207             else if (ACTION_UNDO.equals(name)) {
208                 try {
209                     undoManager.undo();
210                 } catch (CannotUndoException JavaDoc ex) {
211                     ex.printStackTrace();
212                 }
213                 updateActionState();
214             }
215             else if (ACTION_REDO.equals(name)) {
216                 try {
217                     undoManager.redo();
218                 } catch (CannotRedoException JavaDoc ex) {
219                     ex.printStackTrace();
220                 }
221                 updateActionState();
222             }
223             else {
224                 System.out.println("ActionHandled: " + name);
225             }
226
227         }
228     }
229
230     /**
231      * Retrieves a component which will be used as the paragraph selector.
232      * This can be placed in the toolbar.
233      * <p>
234      * Note: This is only valid for the HTMLEditorKit
235      */

236     public JComboBox getParagraphSelector() {
237         if (selector == null) {
238             selector = new ParagraphSelector();
239         }
240         return selector;
241     }
242
243     /**
244      * A control which should be placed in the toolbar to enable
245      * paragraph selection.
246      */

247     private class ParagraphSelector extends JComboBox implements ItemListener JavaDoc {
248
249         private Map JavaDoc itemMap;
250
251         public ParagraphSelector() {
252
253             // The item map is for rendering
254
itemMap = new HashMap JavaDoc();
255             itemMap.put(HTML.Tag.P, "Paragraph");
256             itemMap.put(HTML.Tag.H1, "Heading 1");
257             itemMap.put(HTML.Tag.H2, "Heading 2");
258             itemMap.put(HTML.Tag.H3, "Heading 3");
259             itemMap.put(HTML.Tag.H4, "Heading 4");
260             itemMap.put(HTML.Tag.H5, "Heading 5");
261             itemMap.put(HTML.Tag.H6, "Heading 6");
262             itemMap.put(HTML.Tag.PRE, "Preformatted");
263
264             // The list of items
265
Vector JavaDoc items = new Vector JavaDoc();
266             items.addElement(HTML.Tag.P);
267             items.addElement(HTML.Tag.H1);
268             items.addElement(HTML.Tag.H2);
269             items.addElement(HTML.Tag.H3);
270             items.addElement(HTML.Tag.H4);
271             items.addElement(HTML.Tag.H5);
272             items.addElement(HTML.Tag.H6);
273             items.addElement(HTML.Tag.PRE);
274
275             setModel(new DefaultComboBoxModel(items));
276             setRenderer(new ParagraphRenderer());
277             addItemListener(this);
278             setFocusable(false);
279         }
280
281         public void itemStateChanged(ItemEvent JavaDoc evt) {
282             if (evt.getStateChange() == ItemEvent.SELECTED) {
283                 applyTag((HTML.Tag JavaDoc)evt.getItem());
284             }
285         }
286
287         private class ParagraphRenderer extends DefaultListCellRenderer {
288
289             public ParagraphRenderer() {
290                 setOpaque(true);
291             }
292
293             public Component JavaDoc getListCellRendererComponent(JList list,
294                                                           Object JavaDoc value,
295                                                           int index,
296                                                           boolean isSelected,
297                                                           boolean cellHasFocus) {
298                 super.getListCellRendererComponent(list, value, index, isSelected,
299                                                    cellHasFocus);
300
301                 setText((String JavaDoc)itemMap.get(value));
302
303                 return this;
304             }
305         }
306
307
308         // TODO: Should have a rendererer which does stuff like:
309
// Paragraph, Heading 1, etc...
310
}
311
312     /**
313      * Applys the tag to the current selection
314      */

315     protected void applyTag(HTML.Tag JavaDoc tag) {
316         Document JavaDoc doc = getDocument();
317         if (!(doc instanceof HTMLDocument JavaDoc)) {
318             return;
319         }
320         HTMLDocument JavaDoc hdoc = (HTMLDocument JavaDoc)doc;
321         int start = getSelectionStart();
322         int end = getSelectionEnd();
323
324         Element JavaDoc element = hdoc.getParagraphElement(start);
325         MutableAttributeSet JavaDoc newAttrs = new SimpleAttributeSet JavaDoc(element.getAttributes());
326         newAttrs.addAttribute(StyleConstants.NameAttribute, tag);
327
328         hdoc.setParagraphAttributes(start, end - start, newAttrs, true);
329     }
330
331     private JXFindDialog dialog = null;
332
333     /**
334      * The paste method has been overloaded to strip off the <html><body> tags
335      * This doesn't really work.
336      */

337     public void paste() {
338         Clipboard JavaDoc clipboard = getToolkit().getSystemClipboard();
339         Transferable JavaDoc content = clipboard.getContents(this);
340         if (content != null) {
341             DataFlavor JavaDoc[] flavors = content.getTransferDataFlavors();
342             try {
343                 for (int i = 0; i < flavors.length; i++) {
344                     if (String JavaDoc.class.equals(flavors[i].getRepresentationClass())) {
345                         Object JavaDoc data = content.getTransferData(flavors[i]);
346
347                         if (flavors[i].isMimeTypeEqual("text/plain")) {
348                             // This works but we lose all the formatting.
349
replaceSelection(data.toString());
350                             break;
351                         } /*
352                             else if (flavors[i].isMimeTypeEqual("text/html")) {
353                             // This doesn't really work since we would
354                             // have to strip off the <html><body> tags
355                             Reader reader = flavors[i].getReaderForText(content);
356                             int start = getSelectionStart();
357                             int end = getSelectionEnd();
358                             int length = end - start;
359                             EditorKit kit = getUI().getEditorKit(this);
360                             Document doc = getDocument();
361                             if (length > 0) {
362                             doc.remove(start, length);
363                             }
364                             kit.read(reader, doc, start);
365                             break;
366                             } */

367                     }
368                 }
369             } catch (Exception JavaDoc ex) {
370                 ex.printStackTrace();
371             }
372         }
373     }
374
375     private void find() {
376         if (dialog == null) {
377             dialog = new JXFindDialog(this);
378         }
379         dialog.setVisible(true);
380     }
381
382     public int search(String JavaDoc searchString) {
383         return search(searchString, -1);
384     }
385
386     public int search(String JavaDoc searchString, int columnIndex) {
387         Pattern JavaDoc pattern = null;
388         if (searchString != null) {
389             return search(Pattern.compile(searchString, 0), columnIndex);
390         }
391         return -1;
392     }
393
394     public int search(Pattern JavaDoc pattern) {
395         return search(pattern, -1);
396     }
397
398     public int search(Pattern JavaDoc pattern, int startIndex) {
399         return search(pattern, startIndex, false);
400     }
401
402     /**
403      * @return end position of matching string or -1
404      */

405     public int search(Pattern JavaDoc pattern, int startIndex, boolean backwards) {
406         if (pattern == null) {
407             return -1;
408         }
409
410         int start = startIndex + 1;
411         int end = -1;
412
413         Segment JavaDoc segment = new Segment JavaDoc();
414         try {
415             Document JavaDoc doc = getDocument();
416             doc.getText(start, doc.getLength() - start, segment);
417         } catch (Exception JavaDoc ex) {
418             ex.printStackTrace();
419         }
420
421         matcher = pattern.matcher(segment.toString());
422         if (matcher.find()) {
423             start = matcher.start() + startIndex;
424             end = matcher.end() + startIndex;
425             select(start + 1, end + 1);
426         } else {
427             return -1;
428         }
429         return end;
430     }
431
432     /**
433      * Listens to the caret placement and adjusts the editing
434      * properties as appropriate.
435      *
436      * Should add more attributes as required.
437      */

438     private class CaretHandler implements CaretListener JavaDoc {
439         public void caretUpdate(CaretEvent JavaDoc evt) {
440             StyledDocument JavaDoc document = (StyledDocument JavaDoc)getDocument();
441             int dot = evt.getDot();
442             Element JavaDoc elem = document.getCharacterElement(dot);
443             AttributeSet JavaDoc set = elem.getAttributes();
444
445             ActionManager manager = Application.getInstance().getActionManager();
446             manager.setSelected("font-bold", StyleConstants.isBold(set));
447             manager.setSelected("font-italic", StyleConstants.isItalic(set));
448             manager.setSelected("font-underline", StyleConstants.isUnderline(set));
449
450             elem = document.getParagraphElement(dot);
451             set = elem.getAttributes();
452
453             // Update the paragraph selector if applicable.
454
if (selector != null) {
455                 selector.setSelectedItem(set.getAttribute(StyleConstants.NameAttribute));
456             }
457
458             switch (StyleConstants.getAlignment(set)) {
459                 // XXX There is a bug here. the setSelected method
460
// should only affect the UI actions rather than propagate
461
// down into the action map actions.
462
case StyleConstants.ALIGN_LEFT:
463                 manager.setSelected("left-justify", true);
464                 break;
465
466             case StyleConstants.ALIGN_CENTER:
467                 manager.setSelected("center-justify", true);
468                 break;
469
470             case StyleConstants.ALIGN_RIGHT:
471                 manager.setSelected("right-justify", true);
472                 break;
473             }
474         }
475     }
476 }
477
Popular Tags