]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/richtext/textpanel/TextEditBehavior.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / richtext / textpanel / TextEditBehavior.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.Rectangle;\r
16 \r
17 import com.ibm.richtext.textlayout.attributes.AttributeMap;\r
18 \r
19 import java.awt.event.KeyEvent;\r
20 import java.awt.event.MouseEvent;\r
21 \r
22 import com.ibm.richtext.styledtext.MConstText;\r
23 import com.ibm.richtext.styledtext.MText;\r
24 import com.ibm.richtext.textformat.TextOffset;\r
25 import com.ibm.richtext.styledtext.StyleModifier;\r
26 \r
27 // All changes to the text should happen in this class, or in\r
28 // its TypingInteractor.\r
29 \r
30 class TextEditBehavior extends Behavior {\r
31 \r
32     static final String COPYRIGHT =\r
33                 "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";\r
34     private TextComponent fTextComponent;\r
35     private TextSelection fSelection;\r
36     private MText fText;\r
37     private SimpleCommandLog fCommandLog;\r
38     private PanelEventBroadcaster fListener;\r
39     private TypingInteractor fTypingInteractor = null;\r
40     private KeyRemap fRemap;\r
41     \r
42     private AttributeMap fSavedTypingStyle = null;\r
43     private int fSavedInsPt = 0;\r
44     \r
45     public TextEditBehavior(TextComponent textComponent,\r
46                             TextSelection selection,\r
47                             PanelEventBroadcaster listener,\r
48                             KeyRemap remap) {\r
49 \r
50         fTextComponent = textComponent;\r
51         fSelection = selection;\r
52         fText = textComponent.getModifiableText();\r
53         fCommandLog = new SimpleCommandLog(listener);\r
54         fListener = listener;\r
55         fRemap = remap;\r
56     }\r
57 \r
58     public KeyRemap getKeyRemap() {\r
59 \r
60         return fRemap;\r
61     }\r
62 \r
63     public void setKeyRemap(KeyRemap remap) {\r
64 \r
65         fRemap = remap;\r
66     }\r
67 \r
68     public boolean textControlEventOccurred(Behavior.EventType event, Object what) {\r
69 \r
70         boolean handled = true;\r
71         \r
72         if (event == Behavior.CHARACTER_STYLE_MOD ||\r
73             event == Behavior.PARAGRAPH_STYLE_MOD) {\r
74             doStyleChange(event, what);\r
75         }\r
76         else if (event == Behavior.CUT) {\r
77             doCut();\r
78         }\r
79         else if (event == Behavior.PASTE) {\r
80             doPaste();\r
81         }\r
82         else if (event == Behavior.CLEAR) {\r
83             doClear();\r
84         }\r
85         else if (event == Behavior.REPLACE) {\r
86             doUndoableReplace((TextReplacement) what);\r
87         }\r
88         else if (event == Behavior.UNDO) {\r
89             fCommandLog.undo();\r
90         }\r
91         else if (event == Behavior.REDO) {\r
92             fCommandLog.redo();\r
93         }\r
94         else if (event == Behavior.SET_MODIFIED) {\r
95             fCommandLog.setModified(what == Boolean.TRUE);\r
96         }\r
97         else if (event == Behavior.CLEAR_COMMAND_LOG) {\r
98             fCommandLog.clearLog();\r
99         }\r
100         else if (event == Behavior.SET_COMMAND_LOG_SIZE) {\r
101             fCommandLog.setLogSize(((Integer)what).intValue());\r
102         }\r
103         else {\r
104             handled = super.textControlEventOccurred(event, what);\r
105         }\r
106 \r
107         checkSavedTypingStyle();\r
108         \r
109         return handled;\r
110     }\r
111 \r
112     /**\r
113      * It's unfortunate that the text is modified and reformatted in\r
114      * three different methods.  This method is the "common prologue"\r
115      * for all text modifications.\r
116      *\r
117      * This method should be called before modifying and reformatting\r
118      * the text.  It does three things:  stops caret blinking, stops\r
119      * background formatting, and returns the Rectangle containing the\r
120      * current (soon-to-be obsolete) selection.\r
121      */\r
122     private Rectangle prepareForTextEdit() {\r
123 \r
124         fSelection.stopCaretBlinking();\r
125         fTextComponent.stopBackgroundFormatting();\r
126         return fTextComponent.getBoundingRect(fSelection.getStart(), fSelection.getEnd());\r
127     }\r
128 \r
129     private void doClear() {\r
130         TextRange selRange = fSelection.getSelectionRange();\r
131 \r
132         if (selRange.start == selRange.limit)\r
133             return;\r
134 \r
135         doUndoableTextChange(selRange.start, selRange.limit, null, new TextOffset(selRange.\r
136                             start), new TextOffset(selRange.start));\r
137     }\r
138 \r
139     private void doCut() {\r
140         TextRange selRange = fSelection.getSelectionRange();\r
141 \r
142         if (selRange.start == selRange.limit)\r
143             return;\r
144 \r
145         fTextComponent.getClipboard().setContents(fText.extract(selRange.start, selRange.limit));\r
146         doUndoableTextChange(selRange.start, selRange.limit, null, new TextOffset(selRange.start), new TextOffset(selRange.start));\r
147 \r
148         fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);\r
149     }\r
150 \r
151     private void doPaste() {\r
152         TextRange selRange = fSelection.getSelectionRange();\r
153         MConstText clipText = fTextComponent.getClipboard().getContents(AttributeMap.EMPTY_ATTRIBUTE_MAP);\r
154 \r
155         if (clipText != null) {\r
156             doUndoableTextChange(selRange.start, selRange.limit, clipText,\r
157                                 new TextOffset(selRange.start + clipText.length()),\r
158                                 new TextOffset(selRange.start + clipText.length()));\r
159         }\r
160         else {\r
161             fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);\r
162         }\r
163     }\r
164 \r
165     private void doUndoableReplace(TextReplacement replacement) {\r
166 \r
167         doUndoableTextChange(replacement.getStart(),\r
168                              replacement.getLimit(),\r
169                              replacement.getText(),\r
170                              replacement.getSelectionStart(),\r
171                              replacement.getSelectionLimit());\r
172     }\r
173 \r
174     /**\r
175      * Only TypingInteractor and TextCommand should call this!\r
176      */\r
177     void doReplaceText(int start,\r
178                        int limit,\r
179                        MConstText newText,\r
180                        TextOffset newSelStart,\r
181                        TextOffset newSelEnd) {\r
182 \r
183         int textLength;\r
184 \r
185         fText.resetDamagedRange();\r
186 \r
187         Rectangle oldSelRect = prepareForTextEdit();\r
188 \r
189         if (newText == null) {\r
190             textLength = 0;\r
191             fText.remove(start, limit);\r
192         }\r
193         else {\r
194             textLength = newText.length();\r
195             fText.replace(start, limit, newText, 0, textLength);\r
196         }\r
197         fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);\r
198         reformatAndDrawText(fSelection.getStart(),\r
199                             fSelection.getEnd(),\r
200                             oldSelRect);\r
201     }\r
202 \r
203     /**\r
204      * Only the typing interactor should call this!\r
205      */\r
206     void doReplaceSelectedText(char ch, AttributeMap charStyle) {\r
207 \r
208         int start = fSelection.getStart().fOffset;\r
209         int limit = fSelection.getEnd().fOffset;\r
210         TextOffset newOffset = new TextOffset(start + 1);\r
211         doReplaceText(start, limit, ch, charStyle, newOffset, newOffset);\r
212     }\r
213 \r
214     private void doReplaceText(int start,\r
215                                int limit,\r
216                                char ch,\r
217                                AttributeMap charStyle,\r
218                                TextOffset newSelStart,\r
219                                TextOffset newSelEnd) {\r
220 \r
221         fText.resetDamagedRange();\r
222 \r
223         Rectangle oldSelRect = prepareForTextEdit();\r
224 \r
225         fText.replace(start, limit, ch, charStyle);\r
226 \r
227         fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);\r
228         reformatAndDrawText(fSelection.getStart(),\r
229                             fSelection.getEnd(),\r
230                             oldSelRect);\r
231     }\r
232 \r
233     private void doStyleChange(Behavior.EventType event, Object what) {\r
234 \r
235         TextRange selRange = fSelection.getSelectionRange();\r
236         boolean character = (event == Behavior.CHARACTER_STYLE_MOD);\r
237 \r
238         if (selRange.start != selRange.limit || !character) {\r
239             doUndoableStyleChange(what, character);\r
240         }\r
241         else {\r
242             TypingInteractor interactor =\r
243                 new TypingInteractor(fTextComponent, \r
244                                      fSelection,\r
245                                      fSavedTypingStyle,\r
246                                      this,\r
247                                      fCommandLog,\r
248                                      fListener);\r
249 \r
250             interactor.addToOwner(fTextComponent);\r
251             interactor.textControlEventOccurred(event, what);\r
252         }\r
253     }\r
254 \r
255     /**\r
256      * Only text commands should call this method!\r
257      */\r
258     void doModifyStyles(int start,\r
259                         int limit,\r
260                         StyleModifier modifier,\r
261                         boolean character,\r
262                         TextOffset newSelStart,\r
263                         TextOffset newSelEnd) {\r
264 \r
265         fText.resetDamagedRange();\r
266 \r
267         Rectangle oldSelRect = prepareForTextEdit();\r
268 \r
269         if (character) {\r
270             fText.modifyCharacterStyles(start, limit, modifier);\r
271         }\r
272         else {\r
273             fText.modifyParagraphStyles(start, limit, modifier);\r
274         }\r
275 \r
276         fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);\r
277         reformatAndDrawText(newSelStart,\r
278                             newSelEnd,\r
279                             oldSelRect);\r
280     }\r
281 \r
282     private void doUndoableStyleChange(Object what,\r
283                                        boolean character) {\r
284 \r
285         TextOffset selStart = fSelection.getStart();\r
286         TextOffset selEnd = fSelection.getEnd();\r
287 \r
288         MText oldText = fText.extractWritable(selStart.fOffset, selEnd.fOffset);\r
289         StyleChangeCommand command = new StyleChangeCommand(\r
290                 this, oldText, selStart, selEnd, (StyleModifier) what, character);\r
291 \r
292         fCommandLog.addAndDo(command);\r
293 \r
294         fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);\r
295     }\r
296 \r
297     private void doUndoableTextChange(int start,\r
298                                       int limit,\r
299                                       MConstText newText,\r
300                                       TextOffset newSelStart,\r
301                                       TextOffset newSelEnd) {\r
302 \r
303         TextChangeCommand command = new TextChangeCommand(this, fText.extractWritable(start, limit),\r
304                                 newText, start, fSelection.getStart(), fSelection.getEnd(),\r
305                                 newSelStart, newSelEnd);\r
306 \r
307         fCommandLog.addAndDo(command);\r
308     }\r
309 \r
310     public boolean canUndo() {\r
311 \r
312         boolean canUndo = false;\r
313 \r
314         if (fTypingInteractor != null) {\r
315             canUndo = fTypingInteractor.hasPendingCommand();\r
316         }\r
317 \r
318         if (!canUndo) {\r
319             canUndo = fCommandLog.canUndo();\r
320         }\r
321 \r
322         return canUndo;\r
323     }\r
324 \r
325     public boolean canRedo() {\r
326 \r
327         return fCommandLog.canRedo();\r
328     }\r
329 \r
330     public boolean isModified() {\r
331 \r
332         if (fTypingInteractor != null) {\r
333             if (fTypingInteractor.hasPendingCommand()) {\r
334                 return true;\r
335             }\r
336         }\r
337         return fCommandLog.isModified();\r
338     }\r
339 \r
340     public int getCommandLogSize() {\r
341 \r
342         return fCommandLog.getLogSize();\r
343     }\r
344 \r
345     public AttributeMap getInsertionPointStyle() {\r
346 \r
347         if (fTypingInteractor != null) {\r
348             return fTypingInteractor.getTypingStyle();\r
349         }\r
350 \r
351         if (fSavedTypingStyle != null) {\r
352             return fSavedTypingStyle;\r
353         }\r
354         \r
355         TextRange range = fSelection.getSelectionRange();\r
356         return typingStyleAt(fText, range.start, range.limit);\r
357     }\r
358     \r
359     public boolean keyPressed(KeyEvent e) {\r
360 \r
361         boolean handled = true;\r
362         if (TypingInteractor.handledByTypingInteractor(e)) {\r
363             TypingInteractor interactor = new TypingInteractor(fTextComponent,\r
364                                                                fSelection,\r
365                                                                fSavedTypingStyle,\r
366                                                                this,\r
367                                                                fCommandLog,\r
368                                                                fListener);\r
369 \r
370             interactor.addToOwner(fTextComponent);\r
371             interactor.keyPressed(e);\r
372         }\r
373         else {\r
374             handled = super.keyPressed(e);\r
375             checkSavedTypingStyle();\r
376         }\r
377         \r
378         return handled;\r
379     }\r
380 \r
381     public boolean keyTyped(KeyEvent e) {\r
382         \r
383         boolean handled = true;\r
384         if (TypingInteractor.handledByTypingInteractor(e)) {\r
385             TypingInteractor interactor = new TypingInteractor(fTextComponent, \r
386                                                                fSelection,\r
387                                                                fSavedTypingStyle,\r
388                                                                this,\r
389                                                                fCommandLog,\r
390                                                                fListener);\r
391 \r
392             interactor.addToOwner(fTextComponent);\r
393             interactor.keyTyped(e);\r
394         }\r
395         else {\r
396             handled = super.keyTyped(e);\r
397             checkSavedTypingStyle();\r
398         }\r
399         \r
400         return handled;\r
401     }\r
402 \r
403     public boolean mouseReleased(MouseEvent e) {\r
404         \r
405         boolean result = super.mouseReleased(e);\r
406         checkSavedTypingStyle();\r
407         return result;\r
408     }\r
409     \r
410     private void reformatAndDrawText(TextOffset selStart,\r
411                                      TextOffset selLimit,\r
412                                      Rectangle oldSelRect)\r
413     {\r
414         if (!fSelection.enabled()) {\r
415             selStart = selLimit = null;\r
416         }\r
417 \r
418         int reformatStart = fText.damagedRangeStart();\r
419         int reformatLength = fText.damagedRangeLimit() - reformatStart;\r
420 \r
421         if (reformatStart != Integer.MAX_VALUE) {\r
422             fTextComponent.reformatAndDrawText(reformatStart,\r
423                                                reformatLength,\r
424                                                selStart,\r
425                                                selLimit,\r
426                                                oldSelRect,\r
427                                                fSelection.getHighlightColor());\r
428         }\r
429 \r
430         fSelection.scrollToShowSelection();\r
431         \r
432         // sometimes this should send SELECTION_STYLES_CHANGED\r
433         fListener.textStateChanged(TextPanelEvent.TEXT_CHANGED);\r
434 \r
435         fSelection.restartCaretBlinking(true);\r
436     }\r
437 \r
438     /**\r
439      * Only TypingInteractor should call this.\r
440      */\r
441     void setTypingInteractor(TypingInteractor interactor) {\r
442         fTypingInteractor = interactor;\r
443     }\r
444 \r
445     /**\r
446      * Only TypingInteractor should call this.\r
447      */\r
448     void setSavedTypingStyle(AttributeMap style, int insPt) {\r
449     \r
450         fSavedTypingStyle = style;\r
451         fSavedInsPt = insPt;\r
452     }\r
453     \r
454     private void checkSavedTypingStyle() {\r
455     \r
456         if (fSavedTypingStyle != null) {\r
457             int selStart = fSelection.getStart().fOffset;\r
458             int selLimit = fSelection.getEnd().fOffset;\r
459             if (selStart != fSavedInsPt || selStart != selLimit) {\r
460                 fSavedTypingStyle = null;\r
461             }\r
462         }            \r
463     }\r
464     \r
465     /**\r
466      * Return the style appropriate for typing on the given selection\r
467      * range.\r
468      */\r
469     public static AttributeMap typingStyleAt(MConstText text, int start, int limit) {\r
470 \r
471         if (start < limit) {\r
472             return text.characterStyleAt(start);\r
473         }\r
474         else if (start > 0) {\r
475             return text.characterStyleAt(start - 1);\r
476         }\r
477         else {\r
478             return text.characterStyleAt(0);\r
479         }\r
480     }\r
481 }\r