2 * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
\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
13 package com.ibm.richtext.textpanel;
\r
15 import java.awt.Rectangle;
\r
17 import com.ibm.richtext.textlayout.attributes.AttributeMap;
\r
19 import java.awt.event.KeyEvent;
\r
20 import java.awt.event.MouseEvent;
\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
27 // All changes to the text should happen in this class, or in
\r
28 // its TypingInteractor.
\r
30 class TextEditBehavior extends Behavior {
\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
42 private AttributeMap fSavedTypingStyle = null;
\r
43 private int fSavedInsPt = 0;
\r
45 public TextEditBehavior(TextComponent textComponent,
\r
46 TextSelection selection,
\r
47 PanelEventBroadcaster listener,
\r
50 fTextComponent = textComponent;
\r
51 fSelection = selection;
\r
52 fText = textComponent.getModifiableText();
\r
53 fCommandLog = new SimpleCommandLog(listener);
\r
54 fListener = listener;
\r
58 public KeyRemap getKeyRemap() {
\r
63 public void setKeyRemap(KeyRemap remap) {
\r
68 public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
\r
70 boolean handled = true;
\r
72 if (event == Behavior.CHARACTER_STYLE_MOD ||
\r
73 event == Behavior.PARAGRAPH_STYLE_MOD) {
\r
74 doStyleChange(event, what);
\r
76 else if (event == Behavior.CUT) {
\r
79 else if (event == Behavior.PASTE) {
\r
82 else if (event == Behavior.CLEAR) {
\r
85 else if (event == Behavior.REPLACE) {
\r
86 doUndoableReplace((TextReplacement) what);
\r
88 else if (event == Behavior.UNDO) {
\r
91 else if (event == Behavior.REDO) {
\r
94 else if (event == Behavior.SET_MODIFIED) {
\r
95 fCommandLog.setModified(what == Boolean.TRUE);
\r
97 else if (event == Behavior.CLEAR_COMMAND_LOG) {
\r
98 fCommandLog.clearLog();
\r
100 else if (event == Behavior.SET_COMMAND_LOG_SIZE) {
\r
101 fCommandLog.setLogSize(((Integer)what).intValue());
\r
104 handled = super.textControlEventOccurred(event, what);
\r
107 checkSavedTypingStyle();
\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
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
122 private Rectangle prepareForTextEdit() {
\r
124 fSelection.stopCaretBlinking();
\r
125 fTextComponent.stopBackgroundFormatting();
\r
126 return fTextComponent.getBoundingRect(fSelection.getStart(), fSelection.getEnd());
\r
129 private void doClear() {
\r
130 TextRange selRange = fSelection.getSelectionRange();
\r
132 if (selRange.start == selRange.limit)
\r
135 doUndoableTextChange(selRange.start, selRange.limit, null, new TextOffset(selRange.
\r
136 start), new TextOffset(selRange.start));
\r
139 private void doCut() {
\r
140 TextRange selRange = fSelection.getSelectionRange();
\r
142 if (selRange.start == selRange.limit)
\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
148 fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
\r
151 private void doPaste() {
\r
152 TextRange selRange = fSelection.getSelectionRange();
\r
153 MConstText clipText = fTextComponent.getClipboard().getContents(AttributeMap.EMPTY_ATTRIBUTE_MAP);
\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
161 fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
\r
165 private void doUndoableReplace(TextReplacement replacement) {
\r
167 doUndoableTextChange(replacement.getStart(),
\r
168 replacement.getLimit(),
\r
169 replacement.getText(),
\r
170 replacement.getSelectionStart(),
\r
171 replacement.getSelectionLimit());
\r
175 * Only TypingInteractor and TextCommand should call this!
\r
177 void doReplaceText(int start,
\r
179 MConstText newText,
\r
180 TextOffset newSelStart,
\r
181 TextOffset newSelEnd) {
\r
185 fText.resetDamagedRange();
\r
187 Rectangle oldSelRect = prepareForTextEdit();
\r
189 if (newText == null) {
\r
191 fText.remove(start, limit);
\r
194 textLength = newText.length();
\r
195 fText.replace(start, limit, newText, 0, textLength);
\r
197 fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
\r
198 reformatAndDrawText(fSelection.getStart(),
\r
199 fSelection.getEnd(),
\r
204 * Only the typing interactor should call this!
\r
206 void doReplaceSelectedText(char ch, AttributeMap charStyle) {
\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
214 private void doReplaceText(int start,
\r
217 AttributeMap charStyle,
\r
218 TextOffset newSelStart,
\r
219 TextOffset newSelEnd) {
\r
221 fText.resetDamagedRange();
\r
223 Rectangle oldSelRect = prepareForTextEdit();
\r
225 fText.replace(start, limit, ch, charStyle);
\r
227 fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
\r
228 reformatAndDrawText(fSelection.getStart(),
\r
229 fSelection.getEnd(),
\r
233 private void doStyleChange(Behavior.EventType event, Object what) {
\r
235 TextRange selRange = fSelection.getSelectionRange();
\r
236 boolean character = (event == Behavior.CHARACTER_STYLE_MOD);
\r
238 if (selRange.start != selRange.limit || !character) {
\r
239 doUndoableStyleChange(what, character);
\r
242 TypingInteractor interactor =
\r
243 new TypingInteractor(fTextComponent,
\r
250 interactor.addToOwner(fTextComponent);
\r
251 interactor.textControlEventOccurred(event, what);
\r
256 * Only text commands should call this method!
\r
258 void doModifyStyles(int start,
\r
260 StyleModifier modifier,
\r
262 TextOffset newSelStart,
\r
263 TextOffset newSelEnd) {
\r
265 fText.resetDamagedRange();
\r
267 Rectangle oldSelRect = prepareForTextEdit();
\r
270 fText.modifyCharacterStyles(start, limit, modifier);
\r
273 fText.modifyParagraphStyles(start, limit, modifier);
\r
276 fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
\r
277 reformatAndDrawText(newSelStart,
\r
282 private void doUndoableStyleChange(Object what,
\r
283 boolean character) {
\r
285 TextOffset selStart = fSelection.getStart();
\r
286 TextOffset selEnd = fSelection.getEnd();
\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
292 fCommandLog.addAndDo(command);
\r
294 fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
\r
297 private void doUndoableTextChange(int start,
\r
299 MConstText newText,
\r
300 TextOffset newSelStart,
\r
301 TextOffset newSelEnd) {
\r
303 TextChangeCommand command = new TextChangeCommand(this, fText.extractWritable(start, limit),
\r
304 newText, start, fSelection.getStart(), fSelection.getEnd(),
\r
305 newSelStart, newSelEnd);
\r
307 fCommandLog.addAndDo(command);
\r
310 public boolean canUndo() {
\r
312 boolean canUndo = false;
\r
314 if (fTypingInteractor != null) {
\r
315 canUndo = fTypingInteractor.hasPendingCommand();
\r
319 canUndo = fCommandLog.canUndo();
\r
325 public boolean canRedo() {
\r
327 return fCommandLog.canRedo();
\r
330 public boolean isModified() {
\r
332 if (fTypingInteractor != null) {
\r
333 if (fTypingInteractor.hasPendingCommand()) {
\r
337 return fCommandLog.isModified();
\r
340 public int getCommandLogSize() {
\r
342 return fCommandLog.getLogSize();
\r
345 public AttributeMap getInsertionPointStyle() {
\r
347 if (fTypingInteractor != null) {
\r
348 return fTypingInteractor.getTypingStyle();
\r
351 if (fSavedTypingStyle != null) {
\r
352 return fSavedTypingStyle;
\r
355 TextRange range = fSelection.getSelectionRange();
\r
356 return typingStyleAt(fText, range.start, range.limit);
\r
359 public boolean keyPressed(KeyEvent e) {
\r
361 boolean handled = true;
\r
362 if (TypingInteractor.handledByTypingInteractor(e)) {
\r
363 TypingInteractor interactor = new TypingInteractor(fTextComponent,
\r
370 interactor.addToOwner(fTextComponent);
\r
371 interactor.keyPressed(e);
\r
374 handled = super.keyPressed(e);
\r
375 checkSavedTypingStyle();
\r
381 public boolean keyTyped(KeyEvent e) {
\r
383 boolean handled = true;
\r
384 if (TypingInteractor.handledByTypingInteractor(e)) {
\r
385 TypingInteractor interactor = new TypingInteractor(fTextComponent,
\r
392 interactor.addToOwner(fTextComponent);
\r
393 interactor.keyTyped(e);
\r
396 handled = super.keyTyped(e);
\r
397 checkSavedTypingStyle();
\r
403 public boolean mouseReleased(MouseEvent e) {
\r
405 boolean result = super.mouseReleased(e);
\r
406 checkSavedTypingStyle();
\r
410 private void reformatAndDrawText(TextOffset selStart,
\r
411 TextOffset selLimit,
\r
412 Rectangle oldSelRect)
\r
414 if (!fSelection.enabled()) {
\r
415 selStart = selLimit = null;
\r
418 int reformatStart = fText.damagedRangeStart();
\r
419 int reformatLength = fText.damagedRangeLimit() - reformatStart;
\r
421 if (reformatStart != Integer.MAX_VALUE) {
\r
422 fTextComponent.reformatAndDrawText(reformatStart,
\r
427 fSelection.getHighlightColor());
\r
430 fSelection.scrollToShowSelection();
\r
432 // sometimes this should send SELECTION_STYLES_CHANGED
\r
433 fListener.textStateChanged(TextPanelEvent.TEXT_CHANGED);
\r
435 fSelection.restartCaretBlinking(true);
\r
439 * Only TypingInteractor should call this.
\r
441 void setTypingInteractor(TypingInteractor interactor) {
\r
442 fTypingInteractor = interactor;
\r
446 * Only TypingInteractor should call this.
\r
448 void setSavedTypingStyle(AttributeMap style, int insPt) {
\r
450 fSavedTypingStyle = style;
\r
451 fSavedInsPt = insPt;
\r
454 private void checkSavedTypingStyle() {
\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
466 * Return the style appropriate for typing on the given selection
\r
469 public static AttributeMap typingStyleAt(MConstText text, int start, int limit) {
\r
471 if (start < limit) {
\r
472 return text.characterStyleAt(start);
\r
474 else if (start > 0) {
\r
475 return text.characterStyleAt(start - 1);
\r
478 return text.characterStyleAt(0);
\r