]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/richtext/textpanel/ATextPanelImpl.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / richtext / textpanel / ATextPanelImpl.java
1 /*\r
2  * (C) Copyright IBM Corp. 1998-2004.  All Rights Reserved.\r
3  *\r
4  * The program is provided "as is" without any warranty express or\r
5  * implied, including the warranty of non-infringement and the implied\r
6  * warranties of merchantibility and fitness for a particular purpose.\r
7  * IBM will not be liable for any damages suffered by you as a result\r
8  * of using the Program. In no event will IBM be liable for any\r
9  * special, indirect or consequential damages or lost profits even if\r
10  * IBM has been advised of the possibility of their occurrence. IBM\r
11  * will not be liable for any third party claims against you.\r
12  */\r
13 package com.ibm.richtext.textpanel;\r
14 \r
15 import java.awt.Adjustable;\r
16 import java.awt.Component;\r
17 import java.awt.datatransfer.Clipboard;\r
18 \r
19 import com.ibm.richtext.textlayout.attributes.AttributeMap;\r
20 \r
21 import com.ibm.richtext.styledtext.StyleModifier;\r
22 import com.ibm.richtext.styledtext.MConstText;\r
23 import com.ibm.richtext.styledtext.MText;\r
24 import com.ibm.richtext.styledtext.StyledText;\r
25 import com.ibm.richtext.textformat.TextOffset;\r
26 \r
27 /**\r
28  * Implementation class for TextPanel and JTextPanel.\r
29  */\r
30 final class ATextPanelImpl {\r
31     \r
32     static final String COPYRIGHT =\r
33                 "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";\r
34                 \r
35     private RunStrategy fRunStrategy = null;\r
36     private TextComponent fTextComponent = null;\r
37     private TextSelection fSelection = null;\r
38     private TextEditBehavior fEditBehavior = null;\r
39     private MText fText = null;\r
40 \r
41     private PanelEventBroadcaster fBroadcaster;\r
42     private KeyRemap fRemap = KeyRemap.getIdentityRemap();\r
43 \r
44     // This is a little ugly.  TextPanel supports its modified\r
45     // flag whether or not it is editable, or even selectable.\r
46     // So if there's no command log to keep track of the flag\r
47     // state then its done right here in TextPanel.  If the\r
48     // panel is editable this flag is ignored.\r
49     private boolean fModified = false;\r
50 \r
51     static final TextPanelSettings fgDefaultSettings = new TextPanelSettings();\r
52 \r
53     static TextPanelSettings getDefaultSettings() {\r
54 \r
55         return (TextPanelSettings) fgDefaultSettings.clone();\r
56     }\r
57     \r
58     ATextPanelImpl(RunStrategy runStrategy,\r
59                    TextPanelSettings settings,\r
60                    MConstText initialText,\r
61                    Clipboard clipboard,\r
62                    MTextPanel client,\r
63                    Adjustable horzSb,\r
64                    Adjustable vertSb) {\r
65                     \r
66         fRunStrategy = runStrategy;\r
67         fBroadcaster = new PanelEventBroadcaster(client);\r
68 \r
69         Scroller scroller = null;\r
70         if (settings.getScrollable()) {\r
71             scroller = new Scroller(horzSb, vertSb);\r
72         }\r
73 \r
74         StyledTextClipboard textClipboard =\r
75                             StyledTextClipboard.getClipboardFor(clipboard);\r
76 \r
77         fText = new StyledText();\r
78         if (initialText != null) {\r
79             fText.append(initialText);\r
80         }\r
81 \r
82         fTextComponent = new TextComponent(fText,\r
83                                            settings.getDefaultValues(),\r
84                                            settings.getWraps(),\r
85                                            TextComponent.WINDOW_WIDTH,\r
86                                            TextComponent.DEFAULT_INSET,\r
87                                            textClipboard,\r
88                                            settings.getScrollable(),\r
89                                            scroller,\r
90                                            fBroadcaster);\r
91 \r
92         if (scroller != null) {\r
93             scroller.setClient(fTextComponent);\r
94         }\r
95         \r
96         // May have to wait until component has host to do this:\r
97         if (settings.getSelectable()) {\r
98             fSelection = new TextSelection(fTextComponent, \r
99                                            fBroadcaster,\r
100                                            fRunStrategy);\r
101             fSelection.addToOwner(fTextComponent);\r
102             if (settings.getEditable()) {\r
103                 fEditBehavior = new TextEditBehavior(\r
104                             fTextComponent, fSelection, fBroadcaster, fRemap);\r
105                 fEditBehavior.addToOwner(fTextComponent);\r
106             }\r
107         }\r
108     }\r
109     \r
110     FakeComponent getTextComponent() {\r
111         \r
112         return fTextComponent;\r
113     }\r
114     \r
115     /**\r
116      * Add the given TextPanelListener to the listeners which will\r
117      * receive update notifications from this TextPanel.\r
118      * @param listener the listener to add\r
119      */\r
120     public void addListener(TextPanelListener listener) {\r
121 \r
122         fBroadcaster.addListener(listener);\r
123     }\r
124 \r
125     /**\r
126      * Remove the given TextPanelListener from the listeners which will\r
127      * receive update notifications from this TextPanel.\r
128      * @param listener the listener to remove\r
129      */\r
130     public void removeListener(TextPanelListener listener) {\r
131 \r
132         fBroadcaster.removeListener(listener);\r
133     }\r
134 \r
135     /**\r
136      * You know what this does...\r
137      */\r
138     private static int pin(int value, int min, int max) {\r
139 \r
140         if (min > max) {\r
141             throw new IllegalArgumentException("Invalid range");\r
142         }\r
143 \r
144         if (value < min) {\r
145             value = min;\r
146         }\r
147         else if (value > max) {\r
148             value = max;\r
149         }\r
150         return value;\r
151     }\r
152 \r
153 //============\r
154 // Text Access\r
155 //============\r
156 \r
157     /**\r
158      * Set the document to <tt>newText</tt>.  This operation\r
159      * modifies the text in the TextPanel.  It does not modify or adopt\r
160      * <tt>newText</tt>.  This method sets the selection an insertion point at\r
161      * the end of the text.\r
162      * @param newText the text which will replace the current text.\r
163      */\r
164     public void setText(MConstText newText) {\r
165 \r
166         replaceRange(newText, 0, getTextLength());\r
167     }\r
168 \r
169     /**\r
170      * Append the given text to the end of the document.  Equivalent to\r
171      * <tt>insert(newText, getTextLength())</tt>.\r
172      * @param newText the text to append to the document\r
173      */\r
174     public void append(MConstText newText) {\r
175 \r
176         int length = getTextLength();\r
177         replaceRange(newText, length, length);\r
178     }\r
179 \r
180     /**\r
181      * Insert the given text into the document at the given position.\r
182      * Equivalent to\r
183      * <tt>replaceRange(newText, position, position)</tt>.\r
184      * @param newText the text to insert into the document.\r
185      * @param position the position in the document where the\r
186      *     text will be inserted\r
187      */\r
188     public void insert(MConstText newText, int position) {\r
189 \r
190         replaceRange(newText, position, position);\r
191     }\r
192 \r
193     /**\r
194      * Replace the given range with <tt>newText</tt>.  After this\r
195      * operation the selection range is an insertion point at the\r
196      * end of the new text.\r
197      * @param newText the text with which to replace the range\r
198      * @param start the beginning of the range to replace\r
199      * @param end the end of the range to replace\r
200      */\r
201     public void replaceRange(MConstText newText, int start, int end) {\r
202 \r
203         int length = getTextLength();\r
204 \r
205         start = pin(start, 0, length);\r
206         end = pin(end, start, length);\r
207 \r
208         if (fSelection != null) {\r
209 \r
210             // If we're selectable, but not editable, we'll temporarily\r
211             // make ourselves editable to change the text.  A little funny\r
212             // but there's a lot of code for getting caret stuff right,\r
213             // and this is not a common operation anyway.\r
214 \r
215             TextEditBehavior behavior;\r
216 \r
217             if (fEditBehavior == null) {\r
218                 behavior = new TextEditBehavior(fTextComponent, fSelection, fBroadcaster, fRemap);\r
219                 behavior.addToOwner(fTextComponent);\r
220             }\r
221             else {\r
222                 behavior = fEditBehavior;\r
223             }\r
224 \r
225             TextOffset newSelection = new TextOffset(start + newText.length(),\r
226                                                      TextOffset.AFTER_OFFSET);\r
227 \r
228             TextReplacement replacement = new TextReplacement(start, end,\r
229                                                               newText,\r
230                                                               newSelection,\r
231                                                               newSelection);\r
232 \r
233             fTextComponent.textControlEventOccurred(Behavior.REPLACE,\r
234                                                     replacement);\r
235             if (fEditBehavior == null) {\r
236                 behavior.removeFromOwner();\r
237             }\r
238         }\r
239         else {\r
240 \r
241             MText oldText = fTextComponent.getModifiableText();\r
242             fTextComponent.stopBackgroundFormatting();\r
243             oldText.replaceAll(newText);\r
244             fTextComponent.reformatAndDrawText(0, newText.length(), null, null, null, null);\r
245         }\r
246     }\r
247 \r
248     /**\r
249      * Return the length of the text document in the TextPanel.\r
250      * @return the length of the text document in the TextPanel\r
251      */\r
252     public int getTextLength() {\r
253 \r
254         return fTextComponent.getText().length();\r
255     }\r
256 \r
257     /**\r
258      * Return the text document in the TextPanel.\r
259      * @return the text document in the TextPanel.\r
260      */\r
261     public MConstText getText() {\r
262 \r
263         return fTextComponent.getText();\r
264     }\r
265 \r
266 //============\r
267 // Selection Access\r
268 //============\r
269 \r
270     /**\r
271      * Return the offset of the start of the selection.\r
272      */\r
273     public int getSelectionStart() {\r
274 \r
275         if (fSelection != null) {\r
276             return fSelection.getStart().fOffset;\r
277         }\r
278         else {\r
279             return 0;\r
280         }\r
281     }\r
282 \r
283     /**\r
284      * Return the offset of the end of the selection.\r
285      */\r
286     public int getSelectionEnd() {\r
287 \r
288         if (fSelection != null) {\r
289             return fSelection.getEnd().fOffset;\r
290         }\r
291         else {\r
292             return 0;\r
293         }\r
294     }\r
295 \r
296     /**\r
297      * Set the beginning of the selection range.  This is\r
298      * equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.\r
299      * @param selectionStart the start of the new selection range\r
300      */\r
301     public void setSelectionStart(int selectionStart) {\r
302 \r
303         select(selectionStart, getSelectionEnd());\r
304     }\r
305 \r
306     /**\r
307      * Set the end of the selection range.  This is\r
308      * equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.\r
309      * @param selectionStart the start of the new selection range\r
310      */\r
311     public void setSelectionEnd(int selectionEnd) {\r
312 \r
313         select(getSelectionStart(), selectionEnd);\r
314     }\r
315 \r
316     /**\r
317      * Set the selection range to an insertion point at the given\r
318      * offset.  This is equivalent to\r
319      * <tt>select(position, position)</tt>.\r
320      * @param position the offset of the new insertion point\r
321      */\r
322     public void setCaretPosition(int position) {\r
323 \r
324         select(position, position);\r
325     }\r
326 \r
327     /**\r
328      * Set the selection range to the given range.  The range start\r
329      * is pinned between 0 and the text length;  the range end is pinned\r
330      * between the range start and the end of the text.  These semantics\r
331      * are identical to those of <tt>java.awt.TextComponent</tt>.\r
332      * This method has no effect if the text is not selectable.\r
333      * @param selectionStart the beginning of the selection range\r
334      * @param selectionEnd the end of the selection range\r
335      */\r
336     public void select(int selectionStart, int selectionEnd) {\r
337 \r
338         int length = getTextLength();\r
339 \r
340         selectionStart = pin(selectionStart, 0, length);\r
341         selectionEnd = pin(selectionEnd, selectionStart, length);\r
342 \r
343         TextRange range = new TextRange(selectionStart, selectionEnd);\r
344         fTextComponent.textControlEventOccurred(Behavior.SELECT, range);\r
345     }\r
346 \r
347     /**\r
348      * Select all of the text in the document.  This method has no effect if\r
349      * the text is not selectable.\r
350      */\r
351     public void selectAll() {\r
352 \r
353         select(0, getTextLength());\r
354     }\r
355 \r
356 \r
357 //============\r
358 // Format Width\r
359 //============\r
360 \r
361     /**\r
362      * Return the total format width, in pixels.  The format width is the\r
363      * width to which text is wrapped.\r
364      * @return the format width\r
365      */\r
366     public int getFormatWidth() {\r
367 \r
368         return fTextComponent.getFormatWidth();\r
369     }\r
370 \r
371     /**\r
372      * Return true if the paragraph at the given offset is left-to-right.\r
373      * @param offset an offset in the text\r
374      * @return true if the paragraph at the given offset is left-to-right\r
375      */\r
376     public boolean paragraphIsLeftToRight(int offset) {\r
377         \r
378         return fTextComponent.paragraphIsLeftToRight(offset);\r
379     }\r
380 \r
381     /**\r
382      * Return true if there is a change which can be undone.\r
383      * @return true if there is a change which can be undone.\r
384      */\r
385     public boolean canUndo() {\r
386 \r
387         if (fEditBehavior != null) {\r
388             return fEditBehavior.canUndo();\r
389         }\r
390         else {\r
391             return false;\r
392         }\r
393     }\r
394 \r
395     /**\r
396      * Return true if there is a change which can be redone.\r
397      * @return true if there is a change which can be redone.\r
398      */\r
399     public boolean canRedo() {\r
400 \r
401         if (fEditBehavior != null) {\r
402             return fEditBehavior.canRedo();\r
403         }\r
404         else {\r
405             return false;\r
406         }\r
407     }\r
408 \r
409     /**\r
410      * Return true if the clipboard contains contents which could be\r
411      * transfered into the text.\r
412      * @return true if the clipboard has text content.\r
413      */\r
414     public boolean clipboardNotEmpty() {\r
415 \r
416         return fTextComponent.getClipboard().hasContents();\r
417     }\r
418 \r
419     /**\r
420      * Return an AttributeMap of keys with default values.  The default\r
421      * values are used when displaying text for values which are not\r
422      * specified in the text.\r
423      * @return an AttributeMap of default key-value pairs\r
424      */\r
425     public AttributeMap getDefaultValues() {\r
426 \r
427         return fTextComponent.getDefaultValues();\r
428     }\r
429     \r
430     private static boolean objectsAreEqual(Object lhs, Object rhs) {\r
431     \r
432         if (lhs == null) {\r
433             return rhs == null;\r
434         }\r
435         else {\r
436             return lhs.equals(rhs);\r
437         }\r
438     }\r
439 \r
440     private static Object consistentCharStyle(MConstText text,\r
441                                               int start,\r
442                                               int limit,\r
443                                               Object key,\r
444                                               Object defaultValue) {\r
445 \r
446         if (start >= limit) {\r
447             throw new IllegalArgumentException("Invalid range.");\r
448         }\r
449 \r
450         int runStart = start;\r
451         Object initialValue = text.characterStyleAt(runStart).get(key);\r
452 \r
453         if (initialValue == null) {\r
454             initialValue = defaultValue;\r
455         }\r
456 \r
457         for (runStart = text.characterStyleLimit(runStart);\r
458              runStart < limit;\r
459              runStart = text.characterStyleLimit(runStart)) {\r
460 \r
461             Object nextValue = text.characterStyleAt(runStart).get(key);\r
462 \r
463             if (nextValue == null) {\r
464                 nextValue = defaultValue;\r
465             }\r
466 \r
467             if (!objectsAreEqual(initialValue, nextValue)) {\r
468                 return MTextPanel.MULTIPLE_VALUES;\r
469             }\r
470         }\r
471 \r
472         return initialValue;\r
473     }\r
474 \r
475     /**\r
476      * This method inspects the character style runs in the selection\r
477      * range (or the typing style at the insertion point) and returns:\r
478      * <ul>\r
479      * <li>The value of <tt>key</tt>, if the value of <tt>key</tt>\r
480      * is the same in all of the style runs in the selection, or</li>\r
481      * <li>null, if two or more style runs have different values for <tt>key</tt>.</li>\r
482      * </ul>\r
483      * If a style run does not contain <tt>key</tt>,\r
484      * its value is considered to be <tt>defaultStyle</tt>.\r
485      * This method is useful for configuring style menus.\r
486      * @param key the key used to retrieve values for comparison\r
487      * @param defaultValue the implicit value of <tt>key</tt> in\r
488      *     style runs where <tt>key</tt> is not defined\r
489      */\r
490     public Object getCharacterStyleOverSelection(Object key) {\r
491 \r
492         TextRange selRange;\r
493         if (fSelection != null)\r
494             selRange = fSelection.getSelectionRange();\r
495         else\r
496             selRange = new TextRange(0, 0);\r
497 \r
498         if (selRange.start == selRange.limit) {\r
499 \r
500             AttributeMap compStyle;\r
501 \r
502             if (fEditBehavior != null) {\r
503                 compStyle = fEditBehavior.getInsertionPointStyle();\r
504             }\r
505             else {\r
506                 compStyle = TextEditBehavior.typingStyleAt(fText, selRange.start, selRange.limit);\r
507             }\r
508 \r
509             Object value = compStyle.get(key);\r
510             return value==null? getDefaultValues().get(key) : value;\r
511         }\r
512         else {\r
513             return consistentCharStyle(fText, \r
514                                        selRange.start,\r
515                                        selRange.limit,\r
516                                        key,\r
517                                        getDefaultValues().get(key));\r
518         }\r
519     }\r
520 \r
521     /**\r
522      * This method inspects the paragraph style runs in the selection\r
523      * range (or the typing style at the insertion point) and returns:\r
524      * <ul>\r
525      * <li>The value of <tt>key</tt>, if the value of <tt>key</tt>\r
526      * is the same in all of the style runs in the selection, or</li>\r
527      * <li>null, if two or more style runs have different values for <tt>key</tt>.</li>\r
528      * </ul>\r
529      * If a style run does not contain <tt>key</tt>,\r
530      * its value is considered to be <tt>defaultStyle</tt>.\r
531      * This method is useful for configuring style menus.\r
532      * @param key the key used to retrieve values for comparison\r
533      * @param defaultValue the implicit value of <tt>key</tt> in\r
534      *     style runs where <tt>key</tt> is not defined\r
535      */\r
536     public Object getParagraphStyleOverSelection(Object key) {\r
537 \r
538         TextRange selRange;\r
539         if (fSelection != null) {\r
540             selRange = fSelection.getSelectionRange();\r
541         }\r
542         else {\r
543             selRange = new TextRange(0, 0);\r
544         }\r
545 \r
546         if (selRange.start == selRange.limit) {\r
547             AttributeMap pStyle = fText.paragraphStyleAt(selRange.start);\r
548             Object value = pStyle.get(key);\r
549             return value==null? getDefaultValues().get(key) : value;\r
550         }\r
551         else {\r
552             int paragraphStart = selRange.start;\r
553             Object defaultValue = getDefaultValues().get(key);\r
554             Object initialValue = fText.paragraphStyleAt(paragraphStart).get(key);\r
555             if (initialValue == null) {\r
556                 initialValue = defaultValue;\r
557             }\r
558 \r
559             for (paragraphStart = fText.paragraphLimit(paragraphStart);\r
560                  paragraphStart < selRange.limit;\r
561                  paragraphStart = fText.paragraphLimit(paragraphStart)) {\r
562 \r
563                 Object nextValue = fText.paragraphStyleAt(paragraphStart).get(key);\r
564                 if (nextValue == null) {\r
565                     nextValue = defaultValue;\r
566                 }\r
567 \r
568                 if (!objectsAreEqual(initialValue, nextValue)) {\r
569                     return MTextPanel.MULTIPLE_VALUES;\r
570                 }\r
571             }\r
572 \r
573             return initialValue;\r
574         }\r
575     }\r
576 \r
577     /**\r
578      * Remove the selected text from the document and place it\r
579      * on the clipboard.  This method has no effect if the text\r
580      * is not editable, or if no text is selected.\r
581      */\r
582     public void cut() {\r
583         fTextComponent.textControlEventOccurred(Behavior.CUT, null);\r
584     }\r
585 \r
586     /**\r
587      * Place the selected text on the clipboard.  This method has\r
588      * no effect if no text is selected.\r
589      */\r
590     public void copy() {\r
591         fTextComponent.textControlEventOccurred(Behavior.COPY, null);\r
592     }\r
593 \r
594     /**\r
595      * Replace the currently selected text with the text on the clipboard.\r
596      * This method has no effect if the text is not editable, or if no\r
597      * text is on the clipboard.\r
598      */\r
599     public void paste() {\r
600         fTextComponent.textControlEventOccurred(Behavior.PASTE, null);\r
601     }\r
602 \r
603     /**\r
604      * Remove selected text from the document, without altering the clipboard.\r
605      * This method has no effect if the\r
606      * text is not editable.\r
607      */\r
608     public void clear() {\r
609         fTextComponent.textControlEventOccurred(Behavior.CLEAR, null);\r
610     }\r
611 \r
612     /**\r
613      * Undo the most recent text change.  This method has no effect if\r
614      * there is no change to undo.\r
615      */\r
616     public void undo() {\r
617         fTextComponent.textControlEventOccurred(Behavior.UNDO, null);\r
618     }\r
619 \r
620     /**\r
621      * Redo the most recent text change.  This method has no effect if\r
622      * there is no change to redo.\r
623      */\r
624     public void redo() {\r
625         fTextComponent.textControlEventOccurred(Behavior.REDO, null);\r
626     }\r
627 \r
628     /**\r
629      * Return the number of commands the command log can hold.\r
630      * @return the number of commands the command log can hold\r
631      */\r
632     public int getCommandLogSize() {\r
633 \r
634         if (fEditBehavior != null) {\r
635             return fEditBehavior.getCommandLogSize();\r
636         }\r
637         else {\r
638             return 0;\r
639         }\r
640     }\r
641 \r
642     /**\r
643      * Set the number of commands the command log can hold.  All\r
644      * redoable commands are removed when this method is called.\r
645      * @param size the number of commands kept in the command log\r
646      */\r
647     public void setCommandLogSize(int size) {\r
648         fTextComponent.textControlEventOccurred(Behavior.SET_COMMAND_LOG_SIZE,\r
649                                                 new Integer(size));\r
650     }\r
651 \r
652     /**\r
653      * Remove all commands from the command log.\r
654      */\r
655     public void clearCommandLog() {\r
656         fTextComponent.textControlEventOccurred(Behavior.CLEAR_COMMAND_LOG, null);\r
657     }\r
658 \r
659     /**\r
660      * Modify the character styles on the selected characters.  If no characters\r
661      * are selected, modify the typing style.\r
662      * @param modifier the StyleModifier with which to modify the styles\r
663      */\r
664     public void modifyCharacterStyleOnSelection(StyleModifier modifier) {\r
665         fTextComponent.textControlEventOccurred(Behavior.CHARACTER_STYLE_MOD, modifier);\r
666     }\r
667 \r
668     /**\r
669      * Modify the paragraph styles in paragraphs containing selected characters, or\r
670      * the paragraph containing the insertion point.\r
671      * @param modifier the StyleModifier with which to modify the styles\r
672      */\r
673     public void modifyParagraphStyleOnSelection(StyleModifier modifier) {\r
674         fTextComponent.textControlEventOccurred(Behavior.PARAGRAPH_STYLE_MOD, modifier);\r
675     }\r
676 \r
677     /**\r
678      * Return the KeyRemap used to process key events.\r
679      * @return the key remap used to process key events\r
680      * @see #setKeyRemap\r
681      */\r
682     public KeyRemap getKeyRemap() {\r
683 \r
684         return fRemap;\r
685     }\r
686 \r
687     /**\r
688      * Use the given KeyRemap to map key events to characters.\r
689      * Only key\r
690      * events are affected by the remap;  other text entering the\r
691      * control (via the clipboard, for example) is not affected\r
692      * by the KeyRemap.\r
693      * <p>\r
694      * Do not pass <tt>null</tt> to this method to leave key\r
695      * events unmapped.  Instead, use <tt>KeyRemap.getIdentityRemap()</tt>\r
696      * @param remap the KeyRemap to use for mapping key events to characters\r
697      * @exception java.lang.NullPointerException if parameter is null\r
698      * @see KeyRemap\r
699      */\r
700     public void setKeyRemap(KeyRemap remap) {\r
701 \r
702         if (remap == null) {\r
703             throw new NullPointerException("remap can't be null");\r
704         }\r
705 \r
706         fRemap = remap;\r
707         if (fEditBehavior != null) {\r
708             fEditBehavior.setKeyRemap(remap);\r
709         }\r
710 \r
711         fBroadcaster.textStateChanged(TextPanelEvent.KEYREMAP_CHANGED);\r
712     }\r
713 \r
714     /**\r
715      * Return the modification flag of the current text change.\r
716      * @see #setModified\r
717      */\r
718     public boolean isModified() {\r
719 \r
720         if (fEditBehavior != null) {\r
721             return fEditBehavior.isModified();\r
722         }\r
723         else {\r
724             return fModified;\r
725         }\r
726     }\r
727 \r
728     /**\r
729      * Set the modification flag of the current text change.\r
730      */\r
731     public void setModified(boolean modified) {\r
732 \r
733         boolean handled = fTextComponent.textControlEventOccurred(\r
734                                     Behavior.SET_MODIFIED,\r
735                                     modified? Boolean.TRUE : Boolean.FALSE);\r
736         if (!handled) {\r
737             fModified = modified;\r
738         }\r
739     }\r
740 \r
741     /**\r
742      * This method is for perf-testing only!\r
743      */\r
744     void handleKeyEvent(java.awt.event.KeyEvent keyEvent) {\r
745     \r
746         Component host = fTextComponent.getHost();\r
747         if (host != null) {\r
748             host.dispatchEvent(keyEvent);\r
749         }\r
750     }\r
751 }\r