| /* |
| * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved. |
| * |
| * The program is provided "as is" without any warranty express or |
| * implied, including the warranty of non-infringement and the implied |
| * warranties of merchantibility and fitness for a particular purpose. |
| * IBM will not be liable for any damages suffered by you as a result |
| * of using the Program. In no event will IBM be liable for any |
| * special, indirect or consequential damages or lost profits even if |
| * IBM has been advised of the possibility of their occurrence. IBM |
| * will not be liable for any third party claims against you. |
| */ |
| /* |
| 2/25/99 - Now processing characters from keyTyped method (not keyPressed). |
| This new way is input-method friendly on 1.2, and is generally |
| more correct. |
| |
| 7/7/97 - the mouseDidSomething methods used to remove the typing interactor. |
| This is definitely wrong, but maybe that made sense at one time. Anyway, |
| now the mousePressed / mouseReleased methods remove the interactor; the |
| others do nothing. |
| */ |
| |
| package com.ibm.richtext.textpanel; |
| |
| import com.ibm.richtext.textlayout.attributes.AttributeMap; |
| |
| import java.awt.event.KeyEvent; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.FocusEvent; |
| |
| import java.text.BreakIterator; |
| |
| import com.ibm.richtext.styledtext.StyleModifier; |
| import com.ibm.richtext.styledtext.MConstText; |
| import com.ibm.richtext.styledtext.MText; |
| import com.ibm.richtext.styledtext.StyledText; |
| import com.ibm.richtext.textformat.TextOffset; |
| |
| final class TypingInteractor extends Behavior { |
| |
| static final String COPYRIGHT = |
| "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved"; |
| |
| private static final char BACKSPACE = 8; |
| private static final char TAB = '\t'; |
| private static final char RETURN = '\r'; |
| private static final char LINE_FEED = '\n'; |
| private static final char PARAGRAPH_SEP = '\u2029'; |
| |
| private TextComponent fTextComponent; |
| private TextSelection fSelection; |
| private AttributeMap fTypingStyle; |
| private MConstText fText; |
| private TextEditBehavior fParent; |
| private TextChangeCommand fCommand = null; |
| private SimpleCommandLog fCommandLog; |
| private PanelEventBroadcaster fListener; |
| private BreakIterator fCharBreak = null; |
| |
| /** |
| * Not all characters that come from the keyboard are handled |
| * as input. For example, ctrl-c is not a typable character. |
| * This method determines whether a particular character from |
| * the keyboard will affect the text. |
| */ |
| private static boolean isTypingInteractorChar(char ch) { |
| |
| return ch >= ' ' || |
| ch == LINE_FEED || |
| ch == RETURN || |
| ch == TAB || |
| ch == BACKSPACE; |
| } |
| |
| /** |
| * This method determines whether a TypingInteractor should |
| * handle the given KeyEvent. |
| */ |
| static boolean handledByTypingInteractor(KeyEvent event) { |
| |
| final int id = event.getID(); |
| |
| if (id == KeyEvent.KEY_TYPED) { |
| return isTypingInteractorChar(event.getKeyChar()); |
| } |
| else { |
| return (id == KeyEvent.KEY_PRESSED && |
| event.getKeyCode() == KeyEvent.VK_DELETE); |
| } |
| } |
| |
| public TypingInteractor(TextComponent textComponent, |
| TextSelection selection, |
| AttributeMap typingStyle, |
| TextEditBehavior parent, |
| SimpleCommandLog commandLog, |
| PanelEventBroadcaster listener) { |
| |
| fTextComponent = textComponent; |
| fText = textComponent.getText(); |
| fSelection = selection; |
| fTypingStyle = typingStyle; |
| fParent = parent; |
| fCommandLog = commandLog; |
| fListener = listener; |
| |
| fParent.setTypingInteractor(this); |
| } |
| |
| private void endInteraction() { |
| |
| removeFromOwner(); |
| postTextChangeCommand(); |
| |
| int selStart = fSelection.getStart().fOffset; |
| int selLimit = fSelection.getEnd().fOffset; |
| fParent.setSavedTypingStyle(selStart==selLimit? fTypingStyle : null, selStart); |
| |
| fParent.setTypingInteractor(null); |
| } |
| |
| public boolean textControlEventOccurred(Behavior.EventType event, Object what) { |
| |
| if (fCommand == null && event == Behavior.CHARACTER_STYLE_MOD) { |
| |
| pickUpTypingStyle(); |
| fTypingStyle = ((StyleModifier)what).modifyStyle(fTypingStyle); |
| |
| fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED); |
| |
| return true; |
| } |
| else { |
| Behavior next = nextBehavior(); // save because removeFromOwner() will trash this |
| |
| endInteraction(); |
| |
| if (next != null) |
| return next.textControlEventOccurred(event, what); |
| else |
| return false; |
| } |
| } |
| |
| private void doBackspace() { |
| |
| int selStart = fSelection.getStart().fOffset; |
| int selLimit = fSelection.getEnd().fOffset; |
| |
| if (selStart == selLimit) { |
| if (selStart != 0) { |
| fTypingStyle = null; |
| pickUpTypingStyle(); |
| makeTextChangeCommand(); |
| if (selStart <= fCommand.affectedRangeStart()) { |
| fCommand.prependToOldText(fText.extract(selStart - 1, selStart)); |
| } |
| TextOffset insPt = new TextOffset(selStart - 1); |
| fParent.doReplaceText(selStart - 1, selStart, null, insPt, insPt); |
| } |
| } |
| else { |
| fTypingStyle = null; |
| makeTextChangeCommand(); |
| TextOffset insPt = new TextOffset(selStart); |
| fParent.doReplaceText(selStart, selLimit, null, insPt, insPt); |
| } |
| } |
| |
| private void doFwdDelete(boolean ignoreCharBreak) { |
| |
| int selStart = fSelection.getStart().fOffset; |
| int selLimit = fSelection.getEnd().fOffset; |
| |
| TextOffset insPt = new TextOffset(selStart); |
| |
| if (selStart == selLimit) { |
| if (selStart != fText.length()) { |
| fTypingStyle = null; |
| makeTextChangeCommand(); |
| int numChars; |
| if (ignoreCharBreak) { |
| numChars = 1; |
| } |
| else { |
| if (fCharBreak == null) { |
| fCharBreak = BreakIterator.getCharacterInstance(); |
| } |
| fCharBreak.setText(fText.createCharacterIterator()); |
| numChars = fCharBreak.following(selStart) - selStart; |
| } |
| fCommand.appendToOldText(fText.extract(selStart, selStart + numChars)); |
| fParent.doReplaceText(selStart, selStart + numChars, null, insPt, insPt); |
| } |
| } |
| else { |
| fTypingStyle = null; |
| makeTextChangeCommand(); |
| fParent.doReplaceText(selStart, selLimit, null, insPt, insPt); |
| } |
| } |
| |
| private void doNormalKey(char ch) { |
| |
| // Sigh - 1.1 reports enter key events as return chars, but |
| // 1.2 reports them as linefeeds. |
| if (ch == RETURN) { |
| ch = LINE_FEED; |
| } |
| pickUpTypingStyle(); |
| makeTextChangeCommand(); |
| fParent.doReplaceSelectedText(ch, fTypingStyle); |
| } |
| |
| public boolean focusGained(FocusEvent e) { |
| |
| // pass through, but stick around... |
| return super.focusGained(e); |
| } |
| |
| public boolean focusLost(FocusEvent e) { |
| |
| // pass through, but stick around... |
| return super.focusLost(e); |
| } |
| |
| public boolean keyTyped(KeyEvent e) { |
| |
| if (e.getKeyChar() == BACKSPACE) { |
| doBackspace(); |
| } |
| else { |
| if (isTypingInteractorChar(e.getKeyChar())) { |
| KeyRemap remap = fParent.getKeyRemap(); |
| doNormalKey(remap.remap(e)); |
| } |
| } |
| |
| return true; |
| } |
| |
| public boolean keyPressed(KeyEvent e) { |
| |
| int key = e.getKeyCode(); |
| if (key == KeyEvent.VK_DELETE) { |
| doFwdDelete(e.isShiftDown()); |
| return true; |
| } |
| |
| Behavior next = nextBehavior(); |
| |
| if (TextSelection.keyAffectsSelection(e)) { |
| |
| endInteraction(); |
| } |
| |
| return next.keyPressed(e); |
| } |
| |
| public boolean keyReleased(KeyEvent e) { |
| return true; |
| } |
| |
| private void makeTextChangeCommand() { |
| if (fCommand == null) { |
| TextOffset selStart = fSelection.getStart(); |
| TextOffset selEnd = fSelection.getEnd(); |
| |
| MText writableText = new StyledText(); |
| writableText.replace(0, 0, fText, selStart.fOffset, selEnd.fOffset); |
| fCommand = new TextChangeCommand(fParent, |
| writableText, |
| null, selStart.fOffset, selStart, selEnd, |
| new TextOffset(), new TextOffset()); |
| |
| fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED); |
| } |
| } |
| |
| public boolean mouseDragged(MouseEvent e) { |
| |
| return true; |
| } |
| |
| public boolean mouseEntered(MouseEvent e) { |
| |
| return true; |
| } |
| |
| public boolean mouseExited(MouseEvent e) { |
| |
| return true; |
| } |
| |
| public boolean mouseMoved(MouseEvent e) { |
| |
| return true; |
| } |
| |
| public boolean mousePressed(MouseEvent e) { |
| |
| Behavior next = nextBehavior(); // save because removeFromOwner() will trash this |
| |
| endInteraction(); |
| |
| if (next != null) |
| return next.mousePressed(e); |
| else |
| return false; |
| } |
| |
| public boolean mouseReleased(MouseEvent e) { |
| |
| Behavior next = nextBehavior(); // save because removeFromOwner() will trash this |
| |
| endInteraction(); |
| |
| if (next != null) |
| return next.mouseReleased(e); |
| else |
| return false; |
| } |
| |
| private void pickUpTypingStyle() { |
| if (fTypingStyle == null) { |
| int selStart = fSelection.getStart().fOffset; |
| int selLimit = fSelection.getEnd().fOffset; |
| fTypingStyle = TextEditBehavior.typingStyleAt(fText, selStart, selLimit); |
| } |
| } |
| |
| private void postTextChangeCommand() { |
| if (fCommand != null) { |
| TextOffset selStart = fSelection.getStart(); |
| TextOffset selEnd = fSelection.getEnd(); |
| |
| fCommand.setNewText(fText.extract(fCommand.affectedRangeStart(), selStart.fOffset)); |
| fCommand.setSelRangeAfter(selStart, selEnd); |
| fCommandLog.add(fCommand); |
| } |
| } |
| |
| boolean hasPendingCommand() { |
| |
| return fCommand != null; |
| } |
| |
| AttributeMap getTypingStyle() { |
| |
| pickUpTypingStyle(); |
| return fTypingStyle; |
| } |
| } |