001: /*
002: * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
003: *
004: * The program is provided "as is" without any warranty express or
005: * implied, including the warranty of non-infringement and the implied
006: * warranties of merchantibility and fitness for a particular purpose.
007: * IBM will not be liable for any damages suffered by you as a result
008: * of using the Program. In no event will IBM be liable for any
009: * special, indirect or consequential damages or lost profits even if
010: * IBM has been advised of the possibility of their occurrence. IBM
011: * will not be liable for any third party claims against you.
012: */
013: /*
014: 2/25/99 - Now processing characters from keyTyped method (not keyPressed).
015: This new way is input-method friendly on 1.2, and is generally
016: more correct.
017:
018: 7/7/97 - the mouseDidSomething methods used to remove the typing interactor.
019: This is definitely wrong, but maybe that made sense at one time. Anyway,
020: now the mousePressed / mouseReleased methods remove the interactor; the
021: others do nothing.
022: */
023:
024: package com.ibm.richtext.textpanel;
025:
026: import com.ibm.richtext.textlayout.attributes.AttributeMap;
027:
028: import java.awt.event.KeyEvent;
029: import java.awt.event.MouseEvent;
030: import java.awt.event.FocusEvent;
031:
032: import java.text.BreakIterator;
033:
034: import com.ibm.richtext.styledtext.StyleModifier;
035: import com.ibm.richtext.styledtext.MConstText;
036: import com.ibm.richtext.styledtext.MText;
037: import com.ibm.richtext.styledtext.StyledText;
038: import com.ibm.richtext.textformat.TextOffset;
039:
040: final class TypingInteractor extends Behavior {
041:
042: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
043:
044: private static final char BACKSPACE = 8;
045: private static final char TAB = '\t';
046: private static final char RETURN = '\r';
047: private static final char LINE_FEED = '\n';
048: private static final char PARAGRAPH_SEP = '\u2029';
049:
050: private TextComponent fTextComponent;
051: private TextSelection fSelection;
052: private AttributeMap fTypingStyle;
053: private MConstText fText;
054: private TextEditBehavior fParent;
055: private TextChangeCommand fCommand = null;
056: private SimpleCommandLog fCommandLog;
057: private PanelEventBroadcaster fListener;
058: private BreakIterator fCharBreak = null;
059:
060: /**
061: * Not all characters that come from the keyboard are handled
062: * as input. For example, ctrl-c is not a typable character.
063: * This method determines whether a particular character from
064: * the keyboard will affect the text.
065: */
066: private static boolean isTypingInteractorChar(char ch) {
067:
068: return ch >= ' ' || ch == LINE_FEED || ch == RETURN
069: || ch == TAB || ch == BACKSPACE;
070: }
071:
072: /**
073: * This method determines whether a TypingInteractor should
074: * handle the given KeyEvent.
075: */
076: static boolean handledByTypingInteractor(KeyEvent event) {
077:
078: final int id = event.getID();
079:
080: if (id == KeyEvent.KEY_TYPED) {
081: return isTypingInteractorChar(event.getKeyChar());
082: } else {
083: return (id == KeyEvent.KEY_PRESSED && event.getKeyCode() == KeyEvent.VK_DELETE);
084: }
085: }
086:
087: public TypingInteractor(TextComponent textComponent,
088: TextSelection selection, AttributeMap typingStyle,
089: TextEditBehavior parent, SimpleCommandLog commandLog,
090: PanelEventBroadcaster listener) {
091:
092: fTextComponent = textComponent;
093: fText = textComponent.getText();
094: fSelection = selection;
095: fTypingStyle = typingStyle;
096: fParent = parent;
097: fCommandLog = commandLog;
098: fListener = listener;
099:
100: fParent.setTypingInteractor(this );
101: }
102:
103: private void endInteraction() {
104:
105: removeFromOwner();
106: postTextChangeCommand();
107:
108: int selStart = fSelection.getStart().fOffset;
109: int selLimit = fSelection.getEnd().fOffset;
110: fParent.setSavedTypingStyle(selStart == selLimit ? fTypingStyle
111: : null, selStart);
112:
113: fParent.setTypingInteractor(null);
114: }
115:
116: public boolean textControlEventOccurred(Behavior.EventType event,
117: Object what) {
118:
119: if (fCommand == null && event == Behavior.CHARACTER_STYLE_MOD) {
120:
121: pickUpTypingStyle();
122: fTypingStyle = ((StyleModifier) what)
123: .modifyStyle(fTypingStyle);
124:
125: fListener
126: .textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
127:
128: return true;
129: } else {
130: Behavior next = nextBehavior(); // save because removeFromOwner() will trash this
131:
132: endInteraction();
133:
134: if (next != null)
135: return next.textControlEventOccurred(event, what);
136: else
137: return false;
138: }
139: }
140:
141: private void doBackspace() {
142:
143: int selStart = fSelection.getStart().fOffset;
144: int selLimit = fSelection.getEnd().fOffset;
145:
146: if (selStart == selLimit) {
147: if (selStart != 0) {
148: fTypingStyle = null;
149: pickUpTypingStyle();
150: makeTextChangeCommand();
151: if (selStart <= fCommand.affectedRangeStart()) {
152: fCommand.prependToOldText(fText.extract(
153: selStart - 1, selStart));
154: }
155: TextOffset insPt = new TextOffset(selStart - 1);
156: fParent.doReplaceText(selStart - 1, selStart, null,
157: insPt, insPt);
158: }
159: } else {
160: fTypingStyle = null;
161: makeTextChangeCommand();
162: TextOffset insPt = new TextOffset(selStart);
163: fParent.doReplaceText(selStart, selLimit, null, insPt,
164: insPt);
165: }
166: }
167:
168: private void doFwdDelete(boolean ignoreCharBreak) {
169:
170: int selStart = fSelection.getStart().fOffset;
171: int selLimit = fSelection.getEnd().fOffset;
172:
173: TextOffset insPt = new TextOffset(selStart);
174:
175: if (selStart == selLimit) {
176: if (selStart != fText.length()) {
177: fTypingStyle = null;
178: makeTextChangeCommand();
179: int numChars;
180: if (ignoreCharBreak) {
181: numChars = 1;
182: } else {
183: if (fCharBreak == null) {
184: fCharBreak = BreakIterator
185: .getCharacterInstance();
186: }
187: fCharBreak.setText(fText.createCharacterIterator());
188: numChars = fCharBreak.following(selStart)
189: - selStart;
190: }
191: fCommand.appendToOldText(fText.extract(selStart,
192: selStart + numChars));
193: fParent.doReplaceText(selStart, selStart + numChars,
194: null, insPt, insPt);
195: }
196: } else {
197: fTypingStyle = null;
198: makeTextChangeCommand();
199: fParent.doReplaceText(selStart, selLimit, null, insPt,
200: insPt);
201: }
202: }
203:
204: private void doNormalKey(char ch) {
205:
206: // Sigh - 1.1 reports enter key events as return chars, but
207: // 1.2 reports them as linefeeds.
208: if (ch == RETURN) {
209: ch = LINE_FEED;
210: }
211: pickUpTypingStyle();
212: makeTextChangeCommand();
213: fParent.doReplaceSelectedText(ch, fTypingStyle);
214: }
215:
216: public boolean focusGained(FocusEvent e) {
217:
218: // pass through, but stick around...
219: return super .focusGained(e);
220: }
221:
222: public boolean focusLost(FocusEvent e) {
223:
224: // pass through, but stick around...
225: return super .focusLost(e);
226: }
227:
228: public boolean keyTyped(KeyEvent e) {
229:
230: if (e.getKeyChar() == BACKSPACE) {
231: doBackspace();
232: } else {
233: if (isTypingInteractorChar(e.getKeyChar())) {
234: KeyRemap remap = fParent.getKeyRemap();
235: doNormalKey(remap.remap(e));
236: }
237: }
238:
239: return true;
240: }
241:
242: public boolean keyPressed(KeyEvent e) {
243:
244: int key = e.getKeyCode();
245: if (key == KeyEvent.VK_DELETE) {
246: doFwdDelete(e.isShiftDown());
247: return true;
248: }
249:
250: Behavior next = nextBehavior();
251:
252: if (TextSelection.keyAffectsSelection(e)) {
253:
254: endInteraction();
255: }
256:
257: return next.keyPressed(e);
258: }
259:
260: public boolean keyReleased(KeyEvent e) {
261: return true;
262: }
263:
264: private void makeTextChangeCommand() {
265: if (fCommand == null) {
266: TextOffset selStart = fSelection.getStart();
267: TextOffset selEnd = fSelection.getEnd();
268:
269: MText writableText = new StyledText();
270: writableText.replace(0, 0, fText, selStart.fOffset,
271: selEnd.fOffset);
272: fCommand = new TextChangeCommand(fParent, writableText,
273: null, selStart.fOffset, selStart, selEnd,
274: new TextOffset(), new TextOffset());
275:
276: fListener
277: .textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
278: }
279: }
280:
281: public boolean mouseDragged(MouseEvent e) {
282:
283: return true;
284: }
285:
286: public boolean mouseEntered(MouseEvent e) {
287:
288: return true;
289: }
290:
291: public boolean mouseExited(MouseEvent e) {
292:
293: return true;
294: }
295:
296: public boolean mouseMoved(MouseEvent e) {
297:
298: return true;
299: }
300:
301: public boolean mousePressed(MouseEvent e) {
302:
303: Behavior next = nextBehavior(); // save because removeFromOwner() will trash this
304:
305: endInteraction();
306:
307: if (next != null)
308: return next.mousePressed(e);
309: else
310: return false;
311: }
312:
313: public boolean mouseReleased(MouseEvent e) {
314:
315: Behavior next = nextBehavior(); // save because removeFromOwner() will trash this
316:
317: endInteraction();
318:
319: if (next != null)
320: return next.mouseReleased(e);
321: else
322: return false;
323: }
324:
325: private void pickUpTypingStyle() {
326: if (fTypingStyle == null) {
327: int selStart = fSelection.getStart().fOffset;
328: int selLimit = fSelection.getEnd().fOffset;
329: fTypingStyle = TextEditBehavior.typingStyleAt(fText,
330: selStart, selLimit);
331: }
332: }
333:
334: private void postTextChangeCommand() {
335: if (fCommand != null) {
336: TextOffset selStart = fSelection.getStart();
337: TextOffset selEnd = fSelection.getEnd();
338:
339: fCommand.setNewText(fText.extract(fCommand
340: .affectedRangeStart(), selStart.fOffset));
341: fCommand.setSelRangeAfter(selStart, selEnd);
342: fCommandLog.add(fCommand);
343: }
344: }
345:
346: boolean hasPendingCommand() {
347:
348: return fCommand != null;
349: }
350:
351: AttributeMap getTypingStyle() {
352:
353: pickUpTypingStyle();
354: return fTypingStyle;
355: }
356: }
|