RichEdit AWT and Swing panels. This is the central package for the control.

X-SVN-Rev: 1191
This commit is contained in:
Alan Liu 2000-04-20 17:51:23 +00:00
parent bf82494a6f
commit e9f426a35d
37 changed files with 8110 additions and 0 deletions

View file

@ -0,0 +1,761 @@
/*
* @(#)$RCSfile: ATextPanelImpl.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Adjustable;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.datatransfer.Clipboard;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.textlayout.attributes.AttributeSet;
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;
/**
* Implementation class for TextPanel and JTextPanel.
*/
final class ATextPanelImpl {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private RunStrategy fRunStrategy = null;
private TextComponent fTextComponent = null;
private TextSelection fSelection = null;
private TextEditBehavior fEditBehavior = null;
private MText fText = null;
private PanelEventBroadcaster fBroadcaster;
private KeyRemap fRemap = KeyRemap.getIdentityRemap();
// This is a little ugly. TextPanel supports its modified
// flag whether or not it is editable, or even selectable.
// So if there's no command log to keep track of the flag
// state then its done right here in TextPanel. If the
// panel is editable this flag is ignored.
private boolean fModified = false;
static final TextPanelSettings fgDefaultSettings = new TextPanelSettings();
static TextPanelSettings getDefaultSettings() {
return (TextPanelSettings) fgDefaultSettings.clone();
}
ATextPanelImpl(RunStrategy runStrategy,
TextPanelSettings settings,
MConstText initialText,
Clipboard clipboard,
MTextPanel client,
Adjustable horzSb,
Adjustable vertSb) {
fRunStrategy = runStrategy;
fBroadcaster = new PanelEventBroadcaster(client);
Scroller scroller = null;
if (settings.getScrollable()) {
scroller = new Scroller(horzSb, vertSb);
}
StyledTextClipboard textClipboard =
StyledTextClipboard.getClipboardFor(clipboard);
fText = new StyledText();
if (initialText != null) {
fText.append(initialText);
}
fTextComponent = new TextComponent(fText,
settings.getDefaultValues(),
settings.getWraps(),
TextComponent.WINDOW_WIDTH,
TextComponent.DEFAULT_INSET,
textClipboard,
settings.getScrollable(),
scroller,
fBroadcaster);
if (scroller != null) {
scroller.setClient(fTextComponent);
}
// May have to wait until component has host to do this:
if (settings.getSelectable()) {
fSelection = new TextSelection(fTextComponent,
fBroadcaster,
fRunStrategy);
fSelection.addToOwner(fTextComponent);
if (settings.getEditable()) {
fEditBehavior = new TextEditBehavior(
fTextComponent, fSelection, fBroadcaster, fRemap);
fEditBehavior.addToOwner(fTextComponent);
}
}
}
FakeComponent getTextComponent() {
return fTextComponent;
}
/**
* Add the given TextPanelListener to the listeners which will
* receive update notifications from this TextPanel.
* @param listener the listener to add
*/
public void addListener(TextPanelListener listener) {
fBroadcaster.addListener(listener);
}
/**
* Remove the given TextPanelListener from the listeners which will
* receive update notifications from this TextPanel.
* @param listener the listener to remove
*/
public void removeListener(TextPanelListener listener) {
fBroadcaster.removeListener(listener);
}
/**
* You know what this does...
*/
private static int pin(int value, int min, int max) {
if (min > max) {
throw new IllegalArgumentException("Invalid range");
}
if (value < min) {
value = min;
}
else if (value > max) {
value = max;
}
return value;
}
//============
// Text Access
//============
/**
* Set the document to <tt>newText</tt>. This operation
* modifies the text in the TextPanel. It does not modify or adopt
* <tt>newText</tt>. This method sets the selection an insertion point at
* the end of the text.
* @param newText the text which will replace the current text.
*/
public void setText(MConstText newText) {
replaceRange(newText, 0, getTextLength());
}
/**
* Append the given text to the end of the document. Equivalent to
* <tt>insert(newText, getTextLength())</tt>.
* @param newText the text to append to the document
*/
public void append(MConstText newText) {
int length = getTextLength();
replaceRange(newText, length, length);
}
/**
* Insert the given text into the document at the given position.
* Equivalent to
* <tt>replaceRange(newText, position, position)</tt>.
* @param newText the text to insert into the document.
* @param position the position in the document where the
* text will be inserted
*/
public void insert(MConstText newText, int position) {
replaceRange(newText, position, position);
}
/**
* Replace the given range with <tt>newText</tt>. After this
* operation the selection range is an insertion point at the
* end of the new text.
* @param newText the text with which to replace the range
* @param start the beginning of the range to replace
* @param end the end of the range to replace
*/
public void replaceRange(MConstText newText, int start, int end) {
int length = getTextLength();
start = pin(start, 0, length);
end = pin(end, start, length);
if (fSelection != null) {
// If we're selectable, but not editable, we'll temporarily
// make ourselves editable to change the text. A little funny
// but there's a lot of code for getting caret stuff right,
// and this is not a common operation anyway.
TextEditBehavior behavior;
if (fEditBehavior == null) {
behavior = new TextEditBehavior(fTextComponent, fSelection, fBroadcaster, fRemap);
behavior.addToOwner(fTextComponent);
}
else {
behavior = fEditBehavior;
}
TextOffset newSelection = new TextOffset(start + newText.length(),
TextOffset.AFTER_OFFSET);
TextReplacement replacement = new TextReplacement(start, end,
newText,
newSelection,
newSelection);
fTextComponent.textControlEventOccurred(Behavior.REPLACE,
replacement);
if (fEditBehavior == null) {
behavior.removeFromOwner();
}
}
else {
MText oldText = fTextComponent.getModifiableText();
fTextComponent.stopBackgroundFormatting();
oldText.replaceAll(newText);
fTextComponent.reformatAndDrawText(0, newText.length(), null, null, null, null);
}
}
/**
* Return the length of the text document in the TextPanel.
* @return the length of the text document in the TextPanel
*/
public int getTextLength() {
return fTextComponent.getText().length();
}
/**
* Return the text document in the TextPanel.
* @return the text document in the TextPanel.
*/
public MConstText getText() {
return fTextComponent.getText();
}
//============
// Selection Access
//============
/**
* Return the offset of the start of the selection.
*/
public int getSelectionStart() {
if (fSelection != null) {
return fSelection.getStart().fOffset;
}
else {
return 0;
}
}
/**
* Return the offset of the end of the selection.
*/
public int getSelectionEnd() {
if (fSelection != null) {
return fSelection.getEnd().fOffset;
}
else {
return 0;
}
}
/**
* Set the beginning of the selection range. This is
* equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionStart(int selectionStart) {
select(selectionStart, getSelectionEnd());
}
/**
* Set the end of the selection range. This is
* equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionEnd(int selectionEnd) {
select(getSelectionStart(), selectionEnd);
}
/**
* Set the selection range to an insertion point at the given
* offset. This is equivalent to
* <tt>select(position, position)</tt>.
* @param position the offset of the new insertion point
*/
public void setCaretPosition(int position) {
select(position, position);
}
/**
* Set the selection range to the given range. The range start
* is pinned between 0 and the text length; the range end is pinned
* between the range start and the end of the text. These semantics
* are identical to those of <tt>java.awt.TextComponent</tt>.
* This method has no effect if the text is not selectable.
* @param selectionStart the beginning of the selection range
* @param selectionEnd the end of the selection range
*/
public void select(int selectionStart, int selectionEnd) {
int length = getTextLength();
selectionStart = pin(selectionStart, 0, length);
selectionEnd = pin(selectionEnd, selectionStart, length);
TextRange range = new TextRange(selectionStart, selectionEnd);
fTextComponent.textControlEventOccurred(Behavior.SELECT, range);
}
/**
* Select all of the text in the document. This method has no effect if
* the text is not selectable.
*/
public void selectAll() {
select(0, getTextLength());
}
//============
// Format Width
//============
/**
* Return the total format width, in pixels. The format width is the
* width to which text is wrapped.
* @return the format width
*/
public int getFormatWidth() {
return fTextComponent.getFormatWidth();
}
/**
* Return true if the paragraph at the given offset is left-to-right.
* @param offset an offset in the text
* @return true if the paragraph at the given offset is left-to-right
*/
public boolean paragraphIsLeftToRight(int offset) {
return fTextComponent.paragraphIsLeftToRight(offset);
}
/**
* Return true if there is a change which can be undone.
* @return true if there is a change which can be undone.
*/
public boolean canUndo() {
if (fEditBehavior != null) {
return fEditBehavior.canUndo();
}
else {
return false;
}
}
/**
* Return true if there is a change which can be redone.
* @return true if there is a change which can be redone.
*/
public boolean canRedo() {
if (fEditBehavior != null) {
return fEditBehavior.canRedo();
}
else {
return false;
}
}
/**
* Return true if the clipboard contains contents which could be
* transfered into the text.
* @return true if the clipboard has text content.
*/
public boolean clipboardNotEmpty() {
return fTextComponent.getClipboard().hasContents();
}
/**
* Return an AttributeMap of keys with default values. The default
* values are used when displaying text for values which are not
* specified in the text.
* @return an AttributeMap of default key-value pairs
*/
public AttributeMap getDefaultValues() {
return fTextComponent.getDefaultValues();
}
private static boolean objectsAreEqual(Object lhs, Object rhs) {
if (lhs == null) {
return rhs == null;
}
else {
return lhs.equals(rhs);
}
}
private static Object consistentCharStyle(MConstText text,
int start,
int limit,
Object key,
Object defaultValue) {
if (start >= limit) {
throw new IllegalArgumentException("Invalid range.");
}
int runStart = start;
Object initialValue = text.characterStyleAt(runStart).get(key);
if (initialValue == null) {
initialValue = defaultValue;
}
for (runStart = text.characterStyleLimit(runStart);
runStart < limit;
runStart = text.characterStyleLimit(runStart)) {
Object nextValue = text.characterStyleAt(runStart).get(key);
if (nextValue == null) {
nextValue = defaultValue;
}
if (!objectsAreEqual(initialValue, nextValue)) {
return MTextPanel.MULTIPLE_VALUES;
}
}
return initialValue;
}
/**
* This method inspects the character style runs in the selection
* range (or the typing style at the insertion point) and returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li>null, if two or more style runs have different values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be <tt>defaultStyle</tt>.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @param defaultValue the implicit value of <tt>key</tt> in
* style runs where <tt>key</tt> is not defined
*/
public Object getCharacterStyleOverSelection(Object key) {
TextRange selRange;
if (fSelection != null)
selRange = fSelection.getSelectionRange();
else
selRange = new TextRange(0, 0);
if (selRange.start == selRange.limit) {
AttributeMap compStyle;
if (fEditBehavior != null) {
compStyle = fEditBehavior.getInsertionPointStyle();
}
else {
compStyle = TextEditBehavior.typingStyleAt(fText, selRange.start, selRange.limit);
}
Object value = compStyle.get(key);
return value==null? getDefaultValues().get(key) : value;
}
else {
return consistentCharStyle(fText,
selRange.start,
selRange.limit,
key,
getDefaultValues().get(key));
}
}
/**
* This method inspects the paragraph style runs in the selection
* range (or the typing style at the insertion point) and returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li>null, if two or more style runs have different values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be <tt>defaultStyle</tt>.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @param defaultValue the implicit value of <tt>key</tt> in
* style runs where <tt>key</tt> is not defined
*/
public Object getParagraphStyleOverSelection(Object key) {
TextRange selRange;
if (fSelection != null) {
selRange = fSelection.getSelectionRange();
}
else {
selRange = new TextRange(0, 0);
}
if (selRange.start == selRange.limit) {
AttributeMap pStyle = fText.paragraphStyleAt(selRange.start);
Object value = pStyle.get(key);
return value==null? getDefaultValues().get(key) : value;
}
else {
int paragraphStart = selRange.start;
Object defaultValue = getDefaultValues().get(key);
Object initialValue = fText.paragraphStyleAt(paragraphStart).get(key);
if (initialValue == null) {
initialValue = defaultValue;
}
for (paragraphStart = fText.paragraphLimit(paragraphStart);
paragraphStart < selRange.limit;
paragraphStart = fText.paragraphLimit(paragraphStart)) {
Object nextValue = fText.paragraphStyleAt(paragraphStart).get(key);
if (nextValue == null) {
nextValue = defaultValue;
}
if (!objectsAreEqual(initialValue, nextValue)) {
return MTextPanel.MULTIPLE_VALUES;
}
}
return initialValue;
}
}
/**
* Remove the selected text from the document and place it
* on the clipboard. This method has no effect if the text
* is not editable, or if no text is selected.
*/
public void cut() {
fTextComponent.textControlEventOccurred(Behavior.CUT, null);
}
/**
* Place the selected text on the clipboard. This method has
* no effect if no text is selected.
*/
public void copy() {
fTextComponent.textControlEventOccurred(Behavior.COPY, null);
}
/**
* Replace the currently selected text with the text on the clipboard.
* This method has no effect if the text is not editable, or if no
* text is on the clipboard.
*/
public void paste() {
fTextComponent.textControlEventOccurred(Behavior.PASTE, null);
}
/**
* Remove selected text from the document, without altering the clipboard.
* This method has no effect if the
* text is not editable.
*/
public void clear() {
fTextComponent.textControlEventOccurred(Behavior.CLEAR, null);
}
/**
* Undo the most recent text change. This method has no effect if
* there is no change to undo.
*/
public void undo() {
fTextComponent.textControlEventOccurred(Behavior.UNDO, null);
}
/**
* Redo the most recent text change. This method has no effect if
* there is no change to redo.
*/
public void redo() {
fTextComponent.textControlEventOccurred(Behavior.REDO, null);
}
/**
* Return the number of commands the command log can hold.
* @return the number of commands the command log can hold
*/
public int getCommandLogSize() {
if (fEditBehavior != null) {
return fEditBehavior.getCommandLogSize();
}
else {
return 0;
}
}
/**
* Set the number of commands the command log can hold. All
* redoable commands are removed when this method is called.
* @param size the number of commands kept in the command log
*/
public void setCommandLogSize(int size) {
fTextComponent.textControlEventOccurred(Behavior.SET_COMMAND_LOG_SIZE,
new Integer(size));
}
/**
* Remove all commands from the command log.
*/
public void clearCommandLog() {
fTextComponent.textControlEventOccurred(Behavior.CLEAR_COMMAND_LOG, null);
}
/**
* Modify the character styles on the selected characters. If no characters
* are selected, modify the typing style.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyCharacterStyleOnSelection(StyleModifier modifier) {
fTextComponent.textControlEventOccurred(Behavior.CHARACTER_STYLE_MOD, modifier);
}
/**
* Modify the paragraph styles in paragraphs containing selected characters, or
* the paragraph containing the insertion point.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyParagraphStyleOnSelection(StyleModifier modifier) {
fTextComponent.textControlEventOccurred(Behavior.PARAGRAPH_STYLE_MOD, modifier);
}
/**
* Return the KeyRemap used to process key events.
* @return the key remap used to process key events
* @see #setKeyRemap
*/
public KeyRemap getKeyRemap() {
return fRemap;
}
/**
* Use the given KeyRemap to map key events to characters.
* Only key
* events are affected by the remap; other text entering the
* control (via the clipboard, for example) is not affected
* by the KeyRemap.
* <p>
* Do not pass <tt>null</tt> to this method to leave key
* events unmapped. Instead, use <tt>KeyRemap.getIdentityRemap()</tt>
* @param remap the KeyRemap to use for mapping key events to characters
* @exception java.lang.NullPointerException if parameter is null
* @see KeyRemap
*/
public void setKeyRemap(KeyRemap remap) {
if (remap == null) {
throw new NullPointerException("remap can't be null");
}
fRemap = remap;
if (fEditBehavior != null) {
fEditBehavior.setKeyRemap(remap);
}
fBroadcaster.textStateChanged(TextPanelEvent.KEYREMAP_CHANGED);
}
/**
* Return the modification flag of the current text change.
* @see #setModified
*/
public boolean isModified() {
if (fEditBehavior != null) {
return fEditBehavior.isModified();
}
else {
return fModified;
}
}
/**
* Set the modification flag of the current text change.
*/
public void setModified(boolean modified) {
boolean handled = fTextComponent.textControlEventOccurred(
Behavior.SET_MODIFIED,
modified? Boolean.TRUE : Boolean.FALSE);
if (!handled) {
fModified = modified;
}
}
/**
* This method is for perf-testing only!
*/
void handleKeyEvent(java.awt.event.KeyEvent keyEvent) {
switch (keyEvent.getID()) {
case java.awt.event.KeyEvent.KEY_PRESSED:
fTextComponent.keyPressed(keyEvent);
break;
case java.awt.event.KeyEvent.KEY_TYPED:
fTextComponent.keyTyped(keyEvent);
break;
case java.awt.event.KeyEvent.KEY_RELEASED:
fTextComponent.keyReleased(keyEvent);
break;
}
}
}

View file

@ -0,0 +1,180 @@
/*
* @(#)$RCSfile: ArabicTransliteration.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
*
* (C) Copyright IBM Corp. 1998, All Rights Reserved
*/
package com.ibm.richtext.textpanel;
/*
For any incoming character C
if you can map C using the following FakeArabicTable, return
FakeArabicTable(C)
else if C is from A through Z, return FakeArabicTable(lowercase(C))
else just return C
FakeArabicTable is defined by the following mapping
0 0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
1 0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
2 0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
3 0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
4 0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
5 0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
6 0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
7 0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
8 0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
9 0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
% 066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
. 066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
, 060C;ARABIC COMMA;Po;0;R;;;;;N;;;;;
- 0640;ARABIC TATWEEL;Lm;0;R;;;;;N;;;;;
' 0652;ARABIC SUKUN;Mn;34;R;;;;;N;;;;;
" 0651;ARABIC SHADDA;Mn;33;R;;;;;N;ARABIC SHADDAH;;;;
; 061B;ARABIC SEMICOLON;Po;0;R;;;;;N;;;;;
? 061F;ARABIC QUESTION MARK;Po;0;R;;;;;N;;;;;
a 0627;ARABIC LETTER ALEF;Lo;0;R;;;;;N;;;;;
A 0639;ARABIC LETTER AIN;Lo;0;R;;;;;N;;;;;
b 0628;ARABIC LETTER BEH;Lo;0;R;;;;;N;ARABIC LETTER BAA;;;;
c 0635;ARABIC LETTER SAD;Lo;0;R;;;;;N;;;;;
d 062F;ARABIC LETTER DAL;Lo;0;R;;;;;N;;;;;
D 0630;ARABIC LETTER THAL;Lo;0;R;;;;;N;;;;;
E 064B;ARABIC FATHATAN;Mn;27;R;;;;;N;;;;;
e 064E;ARABIC FATHA;Mn;30;R;;;;;N;ARABIC FATHAH;;;;
f 0641;ARABIC LETTER FEH;Lo;0;R;;;;;N;ARABIC LETTER FA;;;;
g 063A;ARABIC LETTER GHAIN;Lo;0;R;;;;;N;;;;;
h 062D;ARABIC LETTER HAH;Lo;0;R;;;;;N;ARABIC LETTER HAA;;;;
H 0647;ARABIC LETTER HEH;Lo;0;R;;;;;N;ARABIC LETTER HA;;;;
I 064D;ARABIC KASRATAN;Mn;29;R;;;;;N;;;;;
i 0650;ARABIC KASRA;Mn;32;R;;;;;N;ARABIC KASRAH;;;;
j 062C;ARABIC LETTER JEEM;Lo;0;R;;;;;N;;;;;
K 062E;ARABIC LETTER KHAH;Lo;0;R;;;;;N;ARABIC LETTER KHAA;;;;
k 0643;ARABIC LETTER KAF;Lo;0;R;;;;;N;ARABIC LETTER CAF;;;;
l 0644;ARABIC LETTER LAM;Lo;0;R;;;;;N;;;;;
m 0645;ARABIC LETTER MEEM;Lo;0;R;;;;;N;;;;;
n 0646;ARABIC LETTER NOON;Lo;0;R;;;;;N;;;;;
o 064F;ARABIC DAMMA;Mn;31;R;;;;;N;ARABIC DAMMAH;;;;
p 0628;ARABIC LETTER BEH;Lo;0;R;;;;;N;ARABIC LETTER BAA;;;;
q 0642;ARABIC LETTER QAF;Lo;0;R;;;;;N;;;;;
r 0631;ARABIC LETTER REH;Lo;0;R;;;;;N;ARABIC LETTER RA;;;;
s 0633;ARABIC LETTER SEEN;Lo;0;R;;;;;N;;;;;
S 0634;ARABIC LETTER SHEEN;Lo;0;R;;;;;N;;;;;
t 062A;ARABIC LETTER TEH;Lo;0;R;;;;;N;ARABIC LETTER TAA;;;;
T 062B;ARABIC LETTER THEH;Lo;0;R;;;;;N;ARABIC LETTER THAA;;;;
U 064C;ARABIC DAMMATAN;Mn;28;R;;;;;N;;;;;
u 064F;ARABIC DAMMA;Mn;31;R;;;;;N;ARABIC DAMMAH;;;;
v 0641;ARABIC LETTER FEH;Lo;0;R;;;;;N;ARABIC LETTER FA;;;;
w 0648;ARABIC LETTER WAW;Lo;0;R;;;;;N;;;;;
x 0633;ARABIC LETTER SEEN;Lo;0;R;;;;;N;;;;;
y 064A;ARABIC LETTER YEH;Lo;0;R;;;;;N;ARABIC LETTER YA;;;;
z 0632;ARABIC LETTER ZAIN;Lo;0;R;;;;;N;;;;;
Z 0638;ARABIC LETTER ZAH;Lo;0;R;;;;;N;ARABIC LETTER DHAH;;;;
*/
/**
* This class implements KeyRemap to produce transliterated Arabic
* characters from Latin-1 characters.
*/
final class ArabicTransliteration extends KeyRemap {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public char remap(char c) {
switch (c) {
case '0': return '\u0660'; // ARABIC-INDIC DIGIT ZERO
case '1': return '\u0661'; // ARABIC-INDIC DIGIT ONE
case '2': return '\u0662'; // ARABIC-INDIC DIGIT TWO
case '3': return '\u0663'; // ARABIC-INDIC DIGIT THREE
case '4': return '\u0664'; // ARABIC-INDIC DIGIT FOUR
case '5': return '\u0665'; // ARABIC-INDIC DIGIT FIVE
case '6': return '\u0666'; // ARABIC-INDIC DIGIT SIX
case '7': return '\u0667'; // ARABIC-INDIC DIGIT SEVEN
case '8': return '\u0668'; // ARABIC-INDIC DIGIT EIGHT
case '9': return '\u0669'; // ARABIC-INDIC DIGIT NINE
case '%': return '\u066A'; // ARABIC PERCENT SIGN
// the Traditional Arabic font does not contain this character
// case '.': return '\u066B'; // ARABIC DECIMAL SEPARATOR
case ',': return '\u060C'; // ARABIC COMMA
case '-': return '\u0640'; // ARABIC TATWEEL
case '\'': return '\u0652'; // ARABIC SUKUN
case '"': return '\u0651'; // ARABIC SHADDA
case ';': return '\u061B'; // ARABIC SEMICOLON
case '?': return '\u061F'; // ARABIC QUESTION MARK
case 'a': return '\u0627'; // ARABIC LETTER ALEF
case 'A': return '\u0639'; // ARABIC LETTER AIN
case 'b': return '\u0628'; // ARABIC LETTER BEH
case 'B': return '\u0628'; // ARABIC LETTER BEH
case 'c': return '\u0635'; // ARABIC LETTER SAD
case 'C': return '\u0635'; // ARABIC LETTER SAD
case 'd': return '\u062F'; // ARABIC LETTER DAL
case 'D': return '\u0630'; // ARABIC LETTER THAL
case 'e': return '\u064E'; // ARABIC FATHA
case 'E': return '\u064B'; // ARABIC FATHATAN
case 'f': return '\u0641'; // ARABIC LETTER FEH
case 'F': return '\u0641'; // ARABIC LETTER FEH
case 'g': return '\u063A'; // ARABIC LETTER GHAIN
case 'G': return '\u063A'; // ARABIC LETTER GHAIN
case 'h': return '\u062D'; // ARABIC LETTER HAH
case 'H': return '\u0647'; // ARABIC LETTER HEH
case 'i': return '\u0650'; // ARABIC KASRA
case 'I': return '\u064D'; // ARABIC KASRATAN
case 'j': return '\u062C'; // ARABIC LETTER JEEM
case 'J': return '\u062C'; // ARABIC LETTER JEEM
case 'k': return '\u0643'; // ARABIC LETTER KAF
case 'K': return '\u062E'; // ARABIC LETTER KHAH
case 'l': return '\u0644'; // ARABIC LETTER LAM
case 'L': return '\u0644'; // ARABIC LETTER LAM
case 'm': return '\u0645'; // ARABIC LETTER MEEM
case 'M': return '\u0645'; // ARABIC LETTER MEEM
case 'n': return '\u0646'; // ARABIC LETTER NOON
case 'N': return '\u0646'; // ARABIC LETTER NOON
case 'o': return '\u064F'; // ARABIC DAMMA
case 'O': return '\u064F'; // ARABIC DAMMA
case 'p': return '\u0628'; // ARABIC LETTER BEH
case 'P': return '\u0628'; // ARABIC LETTER BEH
case 'q': return '\u0642'; // ARABIC LETTER QAF
case 'Q': return '\u0642'; // ARABIC LETTER QAF
case 'r': return '\u0631'; // ARABIC LETTER REH
case 'R': return '\u0631'; // ARABIC LETTER REH
case 's': return '\u0633'; // ARABIC LETTER SEEN
case 'S': return '\u0634'; // ARABIC LETTER SHEEN
case 't': return '\u062A'; // ARABIC LETTER TEH
case 'T': return '\u062B'; // ARABIC LETTER THEH
case 'U': return '\u064C'; // ARABIC DAMMATAN
case 'u': return '\u064F'; // ARABIC DAMMA
case 'v': return '\u0641'; // ARABIC LETTER FEH
case 'V': return '\u0641'; // ARABIC LETTER FEH
case 'w': return '\u0648'; // ARABIC LETTER WAW
case 'W': return '\u0648'; // ARABIC LETTER WAW
case 'x': return '\u0633'; // ARABIC LETTER SEEN
case 'X': return '\u0633'; // ARABIC LETTER SEEN
case 'y': return '\u064A'; // ARABIC LETTER YEH
case 'Y': return '\u064A'; // ARABIC LETTER YEH
case 'z': return '\u0632'; // ARABIC LETTER ZAIN
case 'Z': return '\u0638'; // ARABIC LETTER ZAH
}
return c;
}
}

View file

@ -0,0 +1,203 @@
/*
* @(#)$RCSfile: Behavior.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
/** A class that handles events for a BehaviorOwner.
* A behavior enacpsulates some piece of the event-handling logic for a component.
* This allows the client to separate event-handling logic out into separate classes
* according to function, or to dynamically change the way a component handles
* events without adding a lot of special-case code to the panel itself.
* Behaviors are stored in a linked list, and all behaviors get a crack at an event before
* the owner gets a crack at them (right now, we rely on objects that implement
* BehaviorOwner to support these semantics).
* Behavior provides all the same event-handling functions that Component provides, and
* they all have exactly the same syntax and semantics. */
abstract class Behavior {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private Behavior fNextBehavior = null;
private BehaviorOwner fOwner = null;
static class EventType {
EventType() {
}
}
// events - should these be in TextPanel (or elsewhere)?
// This event's WHAT parameter is a TextRange instance
static final EventType SELECT = new EventType();
// No WHAT param for these:
static final EventType CUT = new EventType();
static final EventType COPY = new EventType();
static final EventType PASTE = new EventType();
static final EventType CLEAR = new EventType();
static final EventType UNDO = new EventType();
static final EventType REDO = new EventType();
static final EventType CLEAR_COMMAND_LOG = new EventType();
// WHAT param is a StyleModifier
static final EventType CHARACTER_STYLE_MOD = new EventType();
static final EventType PARAGRAPH_STYLE_MOD = new EventType();
// With this event, values of the WHAT parameter are
// either Boolean.TRUE or Boolean.FALSE
static final EventType SET_MODIFIED = new EventType();
// WHAT param is a TextReplacement
static final EventType REPLACE = new EventType();
// WHAT param is an Integer
static final EventType SET_COMMAND_LOG_SIZE = new EventType();
public Behavior() {
}
public void addToOwner(BehaviorOwner owner) {
removeFromOwner();
fOwner = owner;
setNextBehavior(owner.getBehavior());
owner.setBehavior(this);
}
public boolean focusGained(FocusEvent e) {
if (fNextBehavior != null)
return fNextBehavior.focusGained(e);
else
return false;
}
public boolean focusLost(FocusEvent e) {
if (fNextBehavior != null)
return fNextBehavior.focusLost(e);
else
return false;
}
public boolean keyPressed(KeyEvent e) {
if (fNextBehavior != null)
return fNextBehavior.keyPressed(e);
else
return false;
}
public boolean keyTyped(KeyEvent e) {
if (fNextBehavior != null) {
return fNextBehavior.keyTyped(e);
}
else {
return false;
}
}
public boolean keyReleased(KeyEvent e) {
if (fNextBehavior != null)
return fNextBehavior.keyReleased(e);
else
return false;
}
public boolean mouseDragged(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mouseDragged(e);
else
return false;
}
public boolean mouseEntered(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mouseEntered(e);
else
return false;
}
public boolean mouseExited(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mouseExited(e);
else
return false;
}
public boolean mouseMoved(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mouseMoved(e);
else
return false;
}
public boolean mousePressed(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mousePressed(e);
else
return false;
}
public boolean mouseReleased(MouseEvent e) {
if (fNextBehavior != null)
return fNextBehavior.mouseReleased(e);
else
return false;
}
public final Behavior nextBehavior() {
return fNextBehavior;
}
public boolean paint(Graphics g, Rectangle drawRect) {
if (fNextBehavior != null)
return fNextBehavior.paint(g, drawRect);
else
return false;
}
public void removeFromOwner() {
if (fOwner != null) {
if (fOwner.getBehavior() == this)
fOwner.setBehavior(nextBehavior());
else {
Behavior current = fOwner.getBehavior();
while (current != null && current.nextBehavior() != this)
current = current.nextBehavior();
if (current != null)
current.setNextBehavior(nextBehavior());
}
setNextBehavior(null);
fOwner = null;
}
}
public final void setNextBehavior(Behavior next) {
fNextBehavior = next;
}
public boolean textControlEventOccurred(EventType event, Object data) {
if (fNextBehavior != null)
return fNextBehavior.textControlEventOccurred(event, data);
else
return false;
}
}

View file

@ -0,0 +1,22 @@
/*
* @(#)$RCSfile: BehaviorOwner.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
interface BehaviorOwner {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public Behavior getBehavior();
public void setBehavior(Behavior b);
}

View file

@ -0,0 +1,55 @@
/*
* @(#)$RCSfile: Command.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
abstract class Command {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private Command fPreviousCommand = null;
// fModified is used to keep a textModified flag for
// clients
private boolean fModified;
public Command() {
fModified = true;
}
public Command previousCommand() {
return fPreviousCommand;
}
public void setPreviousCommand(Command previousCommand) {
fPreviousCommand = previousCommand;
}
public abstract void execute();
public abstract void undo();
public void redo() {
execute();
}
public final boolean isModified() {
return fModified;
}
public final void setModified(boolean modified) {
fModified = modified;
}
}

View file

@ -0,0 +1,32 @@
/*
* @(#)$RCSfile: FakeComponent.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Component;
import java.awt.Graphics;
abstract class FakeComponent {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
protected Component fHost;
abstract void addNotify();
abstract void paint(Graphics g);
void setHost(Component host) {
fHost = host;
}
abstract void requestFocus();
}

View file

@ -0,0 +1,183 @@
/*
* @(#)$RCSfile: HackArabicTransliteration.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
*
* (C) Copyright IBM Corp. 1998, All Rights Reserved
*/
package com.ibm.richtext.textpanel;
/*
For any incoming character C
if you can map C using the following FakeArabicTable, return
FakeArabicTable(C)
else if C is from A through Z, return FakeArabicTable(lowercase(C))
else just return C
FakeArabicTable is defined by the following mapping
0 0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
1 0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
2 0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
3 0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
4 0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
5 0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
6 0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
7 0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
8 0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
9 0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
% 066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
. 066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
, 060C;ARABIC COMMA;Po;0;R;;;;;N;;;;;
- 0640;ARABIC TATWEEL;Lm;0;R;;;;;N;;;;;
' 0652;ARABIC SUKUN;Mn;34;R;;;;;N;;;;;
" 0651;ARABIC SHADDA;Mn;33;R;;;;;N;ARABIC SHADDAH;;;;
; 061B;ARABIC SEMICOLON;Po;0;R;;;;;N;;;;;
? 061F;ARABIC QUESTION MARK;Po;0;R;;;;;N;;;;;
a 0627;ARABIC LETTER ALEF;Lo;0;R;;;;;N;;;;;
A 0639;ARABIC LETTER AIN;Lo;0;R;;;;;N;;;;;
b 0628;ARABIC LETTER BEH;Lo;0;R;;;;;N;ARABIC LETTER BAA;;;;
c 0635;ARABIC LETTER SAD;Lo;0;R;;;;;N;;;;;
d 062F;ARABIC LETTER DAL;Lo;0;R;;;;;N;;;;;
D 0630;ARABIC LETTER THAL;Lo;0;R;;;;;N;;;;;
E 064B;ARABIC FATHATAN;Mn;27;R;;;;;N;;;;;
e 064E;ARABIC FATHA;Mn;30;R;;;;;N;ARABIC FATHAH;;;;
f 0641;ARABIC LETTER FEH;Lo;0;R;;;;;N;ARABIC LETTER FA;;;;
g 063A;ARABIC LETTER GHAIN;Lo;0;R;;;;;N;;;;;
h 062D;ARABIC LETTER HAH;Lo;0;R;;;;;N;ARABIC LETTER HAA;;;;
H 0647;ARABIC LETTER HEH;Lo;0;R;;;;;N;ARABIC LETTER HA;;;;
I 064D;ARABIC KASRATAN;Mn;29;R;;;;;N;;;;;
i 0650;ARABIC KASRA;Mn;32;R;;;;;N;ARABIC KASRAH;;;;
j 062C;ARABIC LETTER JEEM;Lo;0;R;;;;;N;;;;;
K 062E;ARABIC LETTER KHAH;Lo;0;R;;;;;N;ARABIC LETTER KHAA;;;;
k 0643;ARABIC LETTER KAF;Lo;0;R;;;;;N;ARABIC LETTER CAF;;;;
l 0644;ARABIC LETTER LAM;Lo;0;R;;;;;N;;;;;
m 0645;ARABIC LETTER MEEM;Lo;0;R;;;;;N;;;;;
n 0646;ARABIC LETTER NOON;Lo;0;R;;;;;N;;;;;
o 064F;ARABIC DAMMA;Mn;31;R;;;;;N;ARABIC DAMMAH;;;;
p 0628;ARABIC LETTER BEH;Lo;0;R;;;;;N;ARABIC LETTER BAA;;;;
q 0642;ARABIC LETTER QAF;Lo;0;R;;;;;N;;;;;
r 0631;ARABIC LETTER REH;Lo;0;R;;;;;N;ARABIC LETTER RA;;;;
s 0633;ARABIC LETTER SEEN;Lo;0;R;;;;;N;;;;;
S 0634;ARABIC LETTER SHEEN;Lo;0;R;;;;;N;;;;;
t 062A;ARABIC LETTER TEH;Lo;0;R;;;;;N;ARABIC LETTER TAA;;;;
T 062B;ARABIC LETTER THEH;Lo;0;R;;;;;N;ARABIC LETTER THAA;;;;
U 064C;ARABIC DAMMATAN;Mn;28;R;;;;;N;;;;;
u 064F;ARABIC DAMMA;Mn;31;R;;;;;N;ARABIC DAMMAH;;;;
v 0641;ARABIC LETTER FEH;Lo;0;R;;;;;N;ARABIC LETTER FA;;;;
w 0648;ARABIC LETTER WAW;Lo;0;R;;;;;N;;;;;
x 0633;ARABIC LETTER SEEN;Lo;0;R;;;;;N;;;;;
y 064A;ARABIC LETTER YEH;Lo;0;R;;;;;N;ARABIC LETTER YA;;;;
z 0632;ARABIC LETTER ZAIN;Lo;0;R;;;;;N;;;;;
Z 0638;ARABIC LETTER ZAH;Lo;0;R;;;;;N;ARABIC LETTER DHAH;;;;
*/
/**
* This class implements KeyRemap to produce transliterated Arabic
* characters from Latin-1 characters.
*/
// NOTE: this class eliminates Arabic vowels which look ugly
// in the font in which we happen to be demo'ing. It's totally
// bogus otherwise.
final class HackArabicTransliteration extends KeyRemap {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public char remap(char c) {
switch (c) {
case '0': return '\u0660'; // ARABIC-INDIC DIGIT ZERO
case '1': return '\u0661'; // ARABIC-INDIC DIGIT ONE
case '2': return '\u0662'; // ARABIC-INDIC DIGIT TWO
case '3': return '\u0663'; // ARABIC-INDIC DIGIT THREE
case '4': return '\u0664'; // ARABIC-INDIC DIGIT FOUR
case '5': return '\u0665'; // ARABIC-INDIC DIGIT FIVE
case '6': return '\u0666'; // ARABIC-INDIC DIGIT SIX
case '7': return '\u0667'; // ARABIC-INDIC DIGIT SEVEN
case '8': return '\u0668'; // ARABIC-INDIC DIGIT EIGHT
case '9': return '\u0669'; // ARABIC-INDIC DIGIT NINE
case '%': return '\u066A'; // ARABIC PERCENT SIGN
// the Traditional Arabic font does not contain this character
// case '.': return '\u066B'; // ARABIC DECIMAL SEPARATOR
case ',': return '\u060C'; // ARABIC COMMA
case '-': return '\u0640'; // ARABIC TATWEEL
case '\'': return '\u0652'; // ARABIC SUKUN
case '"': return '\u0651'; // ARABIC SHADDA
case ';': return '\u061B'; // ARABIC SEMICOLON
case '?': return '\u061F'; // ARABIC QUESTION MARK
case 'a': return '\u0627'; // ARABIC LETTER ALEF
case 'A': return '\u0639'; // ARABIC LETTER AIN
case 'b': return '\u0628'; // ARABIC LETTER BEH
case 'B': return '\u0628'; // ARABIC LETTER BEH
case 'c': return '\u0635'; // ARABIC LETTER SAD
case 'C': return '\u0635'; // ARABIC LETTER SAD
case 'd': return '\u062F'; // ARABIC LETTER DAL
case 'D': return '\u0630'; // ARABIC LETTER THAL
case 'e': //return '\u064E'; // ARABIC FATHA
case 'E': //return '\u064B'; // ARABIC FATHATAN
case 'f': return '\u0641'; // ARABIC LETTER FEH
case 'F': return '\u0641'; // ARABIC LETTER FEH
case 'g': return '\u063A'; // ARABIC LETTER GHAIN
case 'G': return '\u063A'; // ARABIC LETTER GHAIN
case 'h': return '\u062D'; // ARABIC LETTER HAH
case 'H': return '\u0647'; // ARABIC LETTER HEH
case 'i': //return '\u0650'; // ARABIC KASRA
case 'I': //return '\u064D'; // ARABIC KASRATAN
case 'j': return '\u062C'; // ARABIC LETTER JEEM
case 'J': return '\u062C'; // ARABIC LETTER JEEM
case 'k': return '\u0643'; // ARABIC LETTER KAF
case 'K': return '\u062E'; // ARABIC LETTER KHAH
case 'l': return '\u0644'; // ARABIC LETTER LAM
case 'L': return '\u0644'; // ARABIC LETTER LAM
case 'm': return '\u0645'; // ARABIC LETTER MEEM
case 'M': return '\u0645'; // ARABIC LETTER MEEM
case 'n': return '\u0646'; // ARABIC LETTER NOON
case 'N': return '\u0646'; // ARABIC LETTER NOON
case 'o': //return '\u064F'; // ARABIC DAMMA
case 'O': //return '\u064F'; // ARABIC DAMMA
case 'p': return '\u0628'; // ARABIC LETTER BEH
case 'P': return '\u0628'; // ARABIC LETTER BEH
case 'q': return '\u0642'; // ARABIC LETTER QAF
case 'Q': return '\u0642'; // ARABIC LETTER QAF
case 'r': return '\u0631'; // ARABIC LETTER REH
case 'R': return '\u0631'; // ARABIC LETTER REH
case 's': return '\u0633'; // ARABIC LETTER SEEN
case 'S': return '\u0634'; // ARABIC LETTER SHEEN
case 't': return '\u062A'; // ARABIC LETTER TEH
case 'T': return '\u062B'; // ARABIC LETTER THEH
case 'U': //return '\u064C'; // ARABIC DAMMATAN
case 'u': //return '\u064F'; // ARABIC DAMMA
case 'v': return '\u0641'; // ARABIC LETTER FEH
case 'V': return '\u0641'; // ARABIC LETTER FEH
case 'w': return '\u0648'; // ARABIC LETTER WAW
case 'W': return '\u0648'; // ARABIC LETTER WAW
case 'x': return '\u0633'; // ARABIC LETTER SEEN
case 'X': return '\u0633'; // ARABIC LETTER SEEN
case 'y': return '\u064A'; // ARABIC LETTER YEH
case 'Y': return '\u064A'; // ARABIC LETTER YEH
case 'z': return '\u0632'; // ARABIC LETTER ZAIN
case 'Z': return '\u0638'; // ARABIC LETTER ZAH
}
return c;
}
}

View file

@ -0,0 +1,90 @@
/*
* @(#)$RCSfile: HebrewTransliteration.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
*
* (C) Copyright IBM Corp. 1998, All Rights Reserved
*/
package com.ibm.richtext.textpanel;
/**
* This class implements KeyRemap to produce transliterated Hebrew
* characters from Latin-1 characters.
*/
final class HebrewTransliteration extends KeyRemap {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public char remap(char c) {
switch (c) {
case 'a': return '\u05D0'; // HEBREW LETTER ALEF
case 'A': return '\u05E2'; // HEBREW LETTER AYIN
case 'b': return '\u05D1'; // HEBREW LETTER BET
case 'B': return '\u05D1'; // HEBREW LETTER BET
case 'c': return '\u05E6'; // HEBREW LETTER TSADI
case 'C': return '\u05E5'; // HEBREW LETTER FINAL TSADI
case 'd': return '\u05D3'; // HEBREW LETTER DALET
case 'D': return '\u05BC'; // HEBREW POINT DAGESH
case 'e': return '\u05B5'; // HEBREW POINT TSERE
case 'E': return '\u05B6'; // HEBREW POINT SEGOL
case 'f': return '\u05E4'; // HEBREW LETTER PE
case 'F': return '\u05E4'; // HEBREW LETTER PE
case 'g': return '\u05D2'; // HEBREW LETTER GIMEL
case 'G': return '\u05D2'; // HEBREW LETTER GIMEL
case 'h': return '\u05D4'; // HEBREW LETTER HE
case 'H': return '\u05D7'; // HEBREW LETTER HET
case 'i': return '\u05D9'; // HEBREW LETTER YOD
case 'I': return '\u05B4'; // HEBREW POINT HIRIQ
case 'j': return '\u05D9'; // HEBREW LETTER YOD
case 'J': return '\u05C1'; // HEBREW POINT SHIN DOT
case 'k': return '\u05DB'; // HEBREW LETTER KAF
case 'K': return '\u05DA'; // HEBREW LETTER FINAL KAF
case 'l': return '\u05DC'; // HEBREW LETTER LAMED
case 'L': return '\u05DC'; // HEBREW LETTER LAMED
case 'm': return '\u05DE'; // HEBREW LETTER MEM
case 'M': return '\u05DD'; // HEBREW LETTER FINAL MEM
case 'n': return '\u05E0'; // HEBREW LETTER NUN
case 'N': return '\u05DF'; // HEBREW LETTER FINAL NUN
case 'o': return '\u05D5'; // HEBREW LETTER VAV
case 'O': return '\u05B9'; // HEBREW POINT HOLAM
case 'p': return '\u05E4'; // HEBREW LETTER PE
case 'P': return '\u05E3'; // HEBREW LETTER FINAL PE
case 'q': return '\u05E7'; // HEBREW LETTER QOF
case 'Q': return '\u05E7'; // HEBREW LETTER QOF
case 'r': return '\u05E8'; // HEBREW LETTER RESH
case 'R': return '\u05BF'; // HEBREW POINT RAFE
case 's': return '\u05E9'; // HEBREW LETTER SHIN
case 'S': return '\u05E1'; // HEBREW LETTER SAMEKH
case 't': return '\u05EA'; // HEBREW LETTER TAV
case 'T': return '\u05D8'; // HEBREW LETTER TET
case 'u': return '\u05D5'; // HEBREW LETTER VAV
case 'U': return '\u05BB'; // HEBREW POINT QUBUTS
case 'v': return '\u05D5'; // HEBREW LETTER VAV
case 'V': return '\u05B7'; // HEBREW POINT PATAH
case 'w': return '\u05D5'; // HEBREW LETTER VAV
case 'W': return '\u05B8'; // HEBREW POINT QAMATS
case 'x': return '\u05E6'; // HEBREW LETTER TSADI
case 'X': return '\u05E5'; // HEBREW LETTER FINAL TSADI
case 'y': return '\u05D9'; // HEBREW LETTER YOD
case 'Y': return '\u05D9'; // HEBREW LETTER YOD
case 'z': return '\u05D6'; // HEBREW LETTER ZAYIN
case 'Z': return '\u05C2'; // HEBREW POINT SIN DOT
}
return c;
}
}

View file

@ -0,0 +1,84 @@
/*
* @(#)$RCSfile: IsraelNikudKeyboard.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
*
* (C) Copyright IBM Corp. 1998, All Rights Reserved
*/
package com.ibm.richtext.textpanel;
import java.awt.event.KeyEvent;
/**
* This class simulates a Nikud keyboard on a US-English
* keyboard. It is very much a work in progress.
*/
final class IsraelNikudKeyboard extends KeyRemap {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public char remap(char c) {
switch(c) {
case 't': return '\u05D0'; // alef
case 'c': return '\u05D1'; // bet
case 'd': return '\u05D2'; // gimel
case 's': return '\u05D3'; // dalet
case 'v': return '\u05D4'; // he
case 'u': return '\u05D5'; // vav
case 'z': return '\u05D6'; // zayin
case 'j': return '\u05D7'; // het
case 'y': return '\u05D8'; // tet
case 'h': return '\u05D9'; // yod
case 'l': return '\u05DA'; // final kaf
case 'f': return '\u05DB'; // kaf
case 'k': return '\u05DC'; // lamed
case 'o': return '\u05DD'; // final mem
case 'n': return '\u05DE'; // mem
case 'i': return '\u05DF'; // final nun
case 'b': return '\u05E0'; // nun
case 'x': return '\u05E1'; // samech
case 'g': return '\u05E2'; // ayin
case ';': return '\u05E3'; // final pe
case 'p': return '\u05E4'; // pe
case '.': return '\u05E5'; // final tsadi
case 'm': return '\u05E6'; // tsadi
case 'e': return '\u05E7'; // qof
case 'r': return '\u05E8'; // resh
case 'a': return '\u05E9'; // shin
case ',': return '\u05EA'; // tav
case 'w': return ',';
case 'q': return '/';
case '/': return '.';
}
return c;
}
public char remap(KeyEvent keyEvent) {
// Note: only one ctrl case now (ctrl-/ -> dagesh).
// Better implementation will be needed for more cases.
if (keyEvent.isControlDown()) {
if (keyEvent.getKeyCode() == KeyEvent.VK_SLASH) {
return '\u05BC'; // dagesh
}
}
return remap(keyEvent.getKeyChar());
}
}

View file

@ -0,0 +1,571 @@
/*
* @(#)$RCSfile: JTextPanel.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JPanel;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import java.awt.datatransfer.Clipboard;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.richtext.styledtext.StyleModifier;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.StyledText;
/**
* JTextPanel is an implementation of MTextPanel in a Swing JPanel.
* @see MTextPanel
*/
public final class JTextPanel extends JPanel implements MTextPanel {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private ATextPanelImpl fImpl;
/**
* Return a TextPanelSettings instance with all settings set
* to the default values. Clients can modify this object;
* modifications will not affect the default values.
* @return a TextPanelSettings instance set to default values
* @see TextPanelSettings
*/
public static TextPanelSettings getDefaultSettings() {
return ATextPanelImpl.getDefaultSettings();
}
/**
* Create a new JTextPanel with the default settings.
* @param initialText the text document. If null document text is empty.
* @param clipboard the clipboard to use for cut, copy, and paste
* operations. If null this panel will use a private clipboard.
*/
public JTextPanel(MConstText initialText,
java.awt.datatransfer.Clipboard clipboard) {
this(ATextPanelImpl.fgDefaultSettings, initialText, clipboard);
}
/**
* Create a new JTextPanel.
* @param settings the settings for this JTextPanel
* @param initialText the text document. If null document text is empty.
* @param clipboard the clipboard to use for cut, copy, and paste
* operations. If null this panel will use a private clipboard.
* @see TextPanelSettings
*/
public JTextPanel(TextPanelSettings settings,
MConstText initialText,
Clipboard clipboard) {
super(false);
JScrollBar horzSb = null;
JScrollBar vertSb = null;
if (settings.getScrollable()) {
setLayout(new ScrollBarLayout());
boolean scrollBarsVisible = settings.getScrollBarsVisible();
if (scrollBarsVisible) {
horzSb = new JScrollBar(JScrollBar.HORIZONTAL);
vertSb = new JScrollBar(JScrollBar.VERTICAL);
add("South", horzSb);
add("East", vertSb);
}
}
else {
setLayout(new BorderLayout());
}
RunStrategy runStrategy = new RunStrategy() {
void doIt(Runnable r) {
try {
SwingUtilities.invokeAndWait(r);
}
catch(InterruptedException e) {
// If operation was interrupted, then client
// called wait or sleep (or something similar)
// which is inappropriate for a client of this
// class. Rethrow error and let client handle it.
e.printStackTrace();
throw new Error("Interrupted in RunStrategy: " + e);
}
catch(InvocationTargetException e) {
// Who knows how this one happens...
e.printStackTrace();
throw new Error("InvocationTargetException in RunStrategy: " + e);
}
}
};
fImpl = new ATextPanelImpl(runStrategy,
settings,
initialText,
clipboard,
this,
horzSb,
vertSb);
final FakeComponent textComponent = fImpl.getTextComponent();
JComponent textHost = new JComponent() {
{
textComponent.setHost(this);
}
public void addNotify() {
super.addNotify();
textComponent.addNotify();
}
public void paint(Graphics g) {
textComponent.paint(g);
}
};
add("Center", textHost);
textHost.requestFocus();
}
/**
* Add the given TextPanelListener to the listeners which will
* receive update notifications from this JTextPanel.
* @param listener the listener to add
*/
public void addListener(TextPanelListener listener) {
fImpl.addListener(listener);
}
/**
* Remove the given TextPanelListener from the listeners which will
* receive update notifications from this JTextPanel.
* @param listener the listener to remove
*/
public void removeListener(TextPanelListener listener) {
fImpl.removeListener(listener);
}
//============
// Text Access
//============
/**
* Set the document to <tt>newText</tt>. This operation
* modifies the text in the JTextPanel. It does not modify or adopt
* <tt>newText</tt>. This method sets the selection an insertion point at
* the end of the text.
* @param newText the text which will replace the current text.
*/
public void setText(MConstText newText) {
fImpl.setText(newText);
}
/**
* Append the given text to the end of the document. Equivalent to
* <tt>insert(newText, getTextLength())</tt>.
* @param newText the text to append to the document
*/
public void append(MConstText newText) {
fImpl.append(newText);
}
/**
* Insert the given text into the document at the given position.
* Equivalent to
* <tt>replaceRange(newText, position, position)</tt>.
* @param newText the text to insert into the document.
* @param position the position in the document where the
* text will be inserted
*/
public void insert(MConstText newText, int position) {
fImpl.insert(newText, position);
}
/**
* Replace the given range with <tt>newText</tt>. After this
* operation the selection range is an insertion point at the
* end of the new text.
* @param newText the text with which to replace the range
* @param start the beginning of the range to replace
* @param end the end of the range to replace
*/
public void replaceRange(MConstText newText, int start, int end) {
fImpl.replaceRange(newText, start, end);
}
/**
* Return the length of the text document in the JTextPanel.
* @return the length of the text document in the JTextPanel
*/
public int getTextLength() {
return fImpl.getTextLength();
}
/**
* Return the text document in the JTextPanel.
* @return the text document in the JTextPanel.
*/
public MConstText getText() {
return fImpl.getText();
}
//============
// Selection Access
//============
/**
* Return the offset of the start of the selection.
*/
public int getSelectionStart() {
return fImpl.getSelectionStart();
}
/**
* Return the offset of the end of the selection.
*/
public int getSelectionEnd() {
return fImpl.getSelectionEnd();
}
/**
* Set the beginning of the selection range. This is
* equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionStart(int selectionStart) {
fImpl.setSelectionStart(selectionStart);
}
/**
* Set the end of the selection range. This is
* equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionEnd(int selectionEnd) {
fImpl.setSelectionEnd(selectionEnd);
}
/**
* Set the selection range to an insertion point at the given
* offset. This is equivalent to
* <tt>select(position, position)</tt>.
* @param position the offset of the new insertion point
*/
public void setCaretPosition(int position) {
fImpl.setCaretPosition(position);
}
/**
* Set the selection range to the given range. The range start
* is pinned between 0 and the text length; the range end is pinned
* between the range start and the end of the text. These semantics
* are identical to those of <tt>java.awt.TextComponent</tt>.
* This method has no effect if the text is not selectable.
* @param selectionStart the beginning of the selection range
* @param selectionEnd the end of the selection range
*/
public void select(int selectionStart, int selectionEnd) {
fImpl.select(selectionStart, selectionEnd);
}
/**
* Select all of the text in the document. This method has no effect if
* the text is not selectable.
*/
public void selectAll() {
fImpl.selectAll();
}
//============
// Format Width
//============
/**
* Return the total format width, in pixels. The format width is the
* width to which text is wrapped.
* @return the format width
*/
public int getFormatWidth() {
return fImpl.getFormatWidth();
}
/**
* Return true if the paragraph at the given offset is left-to-right.
* @param offset an offset in the text
* @return true if the paragraph at the given offset is left-to-right
*/
public boolean paragraphIsLeftToRight(int offset) {
return fImpl.paragraphIsLeftToRight(offset);
}
/**
* Return true if there is a change which can be undone.
* @return true if there is a change which can be undone.
*/
public boolean canUndo() {
return fImpl.canUndo();
}
/**
* Return true if there is a change which can be redone.
* @return true if there is a change which can be redone.
*/
public boolean canRedo() {
return fImpl.canRedo();
}
/**
* Return true if the clipboard contains contents which could be
* transfered into the text.
* @return true if the clipboard has text content.
*/
public boolean clipboardNotEmpty() {
return fImpl.clipboardNotEmpty();
}
/**
* Return an AttributeMap of keys with default values. The default
* values are used when displaying text for values which are not
* specified in the text.
* @return an AttributeMap of default key-value pairs
*/
public AttributeMap getDefaultValues() {
return fImpl.getDefaultValues();
}
/**
* This method inspects the character style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have different
* values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see MTextPanel#MULTIPLE_VALUES
*/
public Object getCharacterStyleOverSelection(Object key) {
return fImpl.getCharacterStyleOverSelection(key);
}
/**
* This method inspects the paragraph style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have
* different values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see MTextPanel#MULTIPLE_VALUES
*/
public Object getParagraphStyleOverSelection(Object key) {
return fImpl.getParagraphStyleOverSelection(key);
}
/**
* Remove the selected text from the document and place it
* on the clipboard. This method has no effect if the text
* is not editable, or if no text is selected.
*/
public void cut() {
fImpl.cut();
}
/**
* Place the selected text on the clipboard. This method has
* no effect if no text is selected.
*/
public void copy() {
fImpl.copy();
}
/**
* Replace the currently selected text with the text on the clipboard.
* This method has no effect if the text is not editable, or if no
* text is on the clipboard.
*/
public void paste() {
fImpl.paste();
}
/**
* Remove selected text from the document, without altering the clipboard.
* This method has no effect if the
* text is not editable.
*/
public void clear() {
fImpl.clear();
}
/**
* Undo the most recent text change. This method has no effect if
* there is no change to undo.
*/
public void undo() {
fImpl.undo();
}
/**
* Redo the most recent text change. This method has no effect if
* there is no change to redo.
*/
public void redo() {
fImpl.redo();
}
/**
* Return the number of commands the command log can hold.
* @return the number of commands the command log can hold
*/
public int getCommandLogSize() {
return fImpl.getCommandLogSize();
}
/**
* Set the number of commands the command log can hold. All
* redoable commands are removed when this method is called.
* @param size the number of commands kept in the command log
*/
public void setCommandLogSize(int size) {
fImpl.setCommandLogSize(size);
}
/**
* Remove all commands from the command log.
*/
public void clearCommandLog() {
fImpl.clearCommandLog();
}
/**
* Modify the character styles on the selected characters. If no characters
* are selected, modify the typing style.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyCharacterStyleOnSelection(StyleModifier modifier) {
fImpl.modifyCharacterStyleOnSelection(modifier);
}
/**
* Modify the paragraph styles in paragraphs containing selected characters, or
* the paragraph containing the insertion point.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyParagraphStyleOnSelection(StyleModifier modifier) {
fImpl.modifyParagraphStyleOnSelection(modifier);
}
/**
* Return the KeyRemap used to process key events.
* @return the key remap used to process key events
* @see #setKeyRemap
*/
public KeyRemap getKeyRemap() {
return fImpl.getKeyRemap();
}
/**
* Use the given KeyRemap to map key events to characters.
* Only key
* events are affected by the remap; other text entering the
* control (via the clipboard, for example) is not affected
* by the KeyRemap.
* <p>
* Do not pass <tt>null</tt> to this method to leave key
* events unmapped. Instead, use <tt>KeyRemap.getIdentityRemap()</tt>
* @param remap the KeyRemap to use for mapping key events to characters
* @exception java.lang.NullPointerException if parameter is null
* @see KeyRemap
*/
public void setKeyRemap(KeyRemap remap) {
fImpl.setKeyRemap(remap);
}
/**
* Return the modification flag of the current text change.
* @see #setModified
*/
public boolean isModified() {
return fImpl.isModified();
}
/**
* Set the modification flag of the current text change.
*/
public void setModified(boolean modified) {
fImpl.setModified(modified);
}
/**
* This method is for perf-testing only!
*/
void handleKeyEvent(java.awt.event.KeyEvent keyEvent) {
fImpl.handleKeyEvent(keyEvent);
}
}

View file

@ -0,0 +1,39 @@
/*
* @(#)$RCSfile: KeyEventForwarder.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.event.KeyEvent;
/**
* This class forwards key events to a TextPanel component for
* testing purposes.
* It's sole reason for existence is to prevent the key-event
* API from being public on TextPanel, and being mistaken for
* standard API. This class is only for testing!
*/
public final class KeyEventForwarder {
private TextPanel fRichText;
public KeyEventForwarder(TextPanel richText) {
fRichText = richText;
}
public void handleKeyEvent(KeyEvent keyEvent) {
fRichText.handleKeyEvent(keyEvent);
}
}

View file

@ -0,0 +1,118 @@
/*
* @(#)$RCSfile: KeyRemap.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
*
* (C) Copyright IBM Corp. 1998, All Rights Reserved
*/
package com.ibm.richtext.textpanel;
import java.awt.event.KeyEvent;
/**
* KeyRemap maps keys on a standard US keyboard to characters
* in other alphabets. Currently, mappings to Arabic, Hebrew
* and Thai are supported. In the future, clients may be
* to define their own mappings by subclassing this class.
* <P>
* @see TextPanel#setKeyRemap
*/
public class KeyRemap {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/**
* Create a new KeyRemap.
*/
protected KeyRemap() {
}
/**
* This method returns the character on the simulated keyboard
* which is (most likely) generated by typing the character c
* on the actual keyboard. For greater accuracy, use the remap
* method which takes a KeyEvent, since it can take modifier
* keys into account.
* @arg c a character on the actual keyboard
* @return the character on the simulated keyboard which would
* result from the key combination which produced the
* given character on the actual keyboard
*/
/*public*/ char remap(char c) {
return c;
}
/**
* Return the character on the simulated keyboard
* which keyEvent generates.
* @arg keyEvent a key event from the actual keyboard
* @return the character on the simulated keyboard generated by
* keyEvent
*/
/*public*/ char remap(KeyEvent keyEvent) {
return remap(keyEvent.getKeyChar());
}
private static final KeyRemap IDENTITY = new KeyRemap();
private static final KeyRemap ARABIC_TRANSLITERATION = new ArabicTransliteration();
private static final KeyRemap HEBREW_TRANSLITERATION = new HebrewTransliteration();
private static final KeyRemap ISRAEL_NIKUD = new IsraelNikudKeyboard();
private static final KeyRemap THAI = new ThaiKeyRemap();
/**
* Return a KeyRemap which maps every character to itself.
*/
public static KeyRemap getIdentityRemap() {
return IDENTITY;
}
/**
* Return a KeyRemap which maps keys to
* characters in the Arabic alphabet, using a simple transliteration.
*/
public static KeyRemap getArabicTransliteration() {
return ARABIC_TRANSLITERATION;
}
/**
* Return a KeyRemap which maps keys to
* characters in the Hebrew alphabet, using a simple transliteration.
*/
public static KeyRemap getHebrewTransliteration() {
return HEBREW_TRANSLITERATION;
}
/**
* Return a KeyRemap which emulates a standard Hebrew keyboard.
*/
public static KeyRemap getIsraelNikud() {
return ISRAEL_NIKUD;
}
/**
* Return a KeyRemap which emulates a Thai Ketmanee keyboard.
*/
public static KeyRemap getThaiKetmanee() {
return THAI;
}
}

View file

@ -0,0 +1,375 @@
/*
* @(#)$RCSfile: MTextPanel.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.richtext.styledtext.StyleModifier;
import com.ibm.richtext.styledtext.MConstText;
/**
* MTextPanel is implemented by Components which provide selectable
* editable styled text.
* <p>
* Implementations of MTextPanel provide a simple, standard user interface
* for text editing. MTextPanel supplies scrollable display, typing,
* arrow-key support, character selection, word-
* and sentence-selection (by double-clicking and triple-clicking,
* respectively), text styles, clipboard operations (cut, copy and paste)
* and a log of changes for undo-redo.
* <p>
* MTextPanel implementations do not provide user interface elements
* such as an edit menu or style menu. This support is provided in
* different packages, and is implemented with MTextPanel's API.
* MTextPanel includes methods for setting selections and styles on text,
* and using the clipboard and command-log functionality.
* MTextPanel's API for selection and text handling is similar to that
* of <tt>java.awt.TextArea</tt> and
* <tt>java.awt.TextComponent</tt>.
* <p>
* MTextPanel supports bidirectional and complex text. In bidirectional
* text, offsets at direction boundaries have dual carets. Logical selection
* is used, so selections across run directions may not be contiguous in
* display.
*/
public interface MTextPanel {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/**
* This value is returned from <tt>getCharacterStyleOverSelection</tt>
* and <tt>getParagraphStyleOverSelection</tt> to indicate that the
* selection range contains multiple values for a key.
* <p>
* There is no reason for this Object ever to appear in an AttributeMap
* as a value. Obviously, if it does there will be no way to distinguish
* between multiple values across the selection and a consistent value of
* <tt>MULTIPLE_VALUES</tt> for the key.
* @see #getCharacterStyleOverSelection
* @see #getParagraphStyleOverSelection
*/
public static final Object MULTIPLE_VALUES = new Object();
/**
* Add the given TextPanelListener to the listeners which will
* receive update notifications from this MTextPanel.
* @param listener the listener to add
*/
public void addListener(TextPanelListener listener);
/**
* Remove the given TextPanelListener from the listeners which will
* receive update notifications from this MTextPanel.
* @param listener the listener to remove
*/
public void removeListener(TextPanelListener listener);
/**
* Set the document to <tt>newText</tt>. This operation
* modifies the text in the MTextPanel. It does not modify or adopt
* <tt>newText</tt>. This method sets the selection an insertion point at
* the end of the text.
* @param newText the text which will replace the current text.
*/
public void setText(MConstText newText);
/**
* Append the given text to the end of the document. Equivalent to
* <tt>insert(newText, getTextLength())</tt>.
* @param newText the text to append to the document
*/
public void append(MConstText newText);
/**
* Insert the given text into the document at the given position.
* Equivalent to
* <tt>replaceRange(newText, position, position)</tt>.
* @param newText the text to insert into the document.
* @param position the position in the document where the
* text will be inserted
*/
public void insert(MConstText newText, int position);
/**
* Replace the given range with <tt>newText</tt>. After this
* operation the selection range is an insertion point at the
* end of the new text.
* @param newText the text with which to replace the range
* @param start the beginning of the range to replace
* @param end the end of the range to replace
*/
public void replaceRange(MConstText newText, int start, int end);
/**
* Return the length of the text document in the MTextPanel.
* @return the length of the text document in the MTextPanel
*/
public int getTextLength();
/**
* Return the text document in the MTextPanel.
* @return the text document in the MTextPanel.
*/
public MConstText getText();
//============
// Selection Access
//============
/**
* Return the offset of the start of the selection.
*/
public int getSelectionStart();
/**
* Return the offset of the end of the selection.
*/
public int getSelectionEnd();
/**
* Set the beginning of the selection range. This is
* equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionStart(int selectionStart);
/**
* Set the end of the selection range. This is
* equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionEnd(int selectionEnd);
/**
* Set the selection range to an insertion point at the given
* offset. This is equivalent to
* <tt>select(position, position)</tt>.
* @param position the offset of the new insertion point
*/
public void setCaretPosition(int position);
/**
* Set the selection range to the given range. The range start
* is pinned between 0 and the text length; the range end is pinned
* between the range start and the end of the text. These semantics
* are identical to those of <tt>java.awt.TextComponent</tt>.
* This method has no effect if the text is not selectable.
* @param selectionStart the beginning of the selection range
* @param selectionEnd the end of the selection range
*/
public void select(int selectionStart, int selectionEnd);
/**
* Select all of the text in the document. This method has no effect if
* the text is not selectable.
*/
public void selectAll();
//============
// Format Width
//============
/**
* Return the total format width, in pixels. The format width is the
* width to which text is wrapped.
* @return the format width
*/
public int getFormatWidth();
/**
* Return true if the paragraph at the given offset is left-to-right.
* @param offset an offset in the text
* @return true if the paragraph at the given offset is left-to-right
*/
public boolean paragraphIsLeftToRight(int offset);
/**
* Return true if there is a change which can be undone.
* @return true if there is a change which can be undone.
*/
public boolean canUndo();
/**
* Return true if there is a change which can be redone.
* @return true if there is a change which can be redone.
*/
public boolean canRedo();
/**
* Return true if the clipboard contains contents which could be
* transfered into the text.
* @return true if the clipboard has text content.
*/
public boolean clipboardNotEmpty();
//============
// Styles
//============
/**
* Return an AttributeMap of keys with default values. The default
* values are used when displaying text for values which are not
* specified in the text.
* @return an AttributeMap of default key-value pairs
*/
public AttributeMap getDefaultValues();
/**
* This method inspects the character style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have different
* values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see #MULTIPLE_VALUES
*/
public Object getCharacterStyleOverSelection(Object key);
/**
* This method inspects the paragraph style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have
* different values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see #MULTIPLE_VALUES
*/
public Object getParagraphStyleOverSelection(Object key);
/**
* Remove the selected text from the document and place it
* on the clipboard. This method has no effect if the text
* is not editable, or if no text is selected.
*/
public void cut();
/**
* Place the selected text on the clipboard. This method has
* no effect if no text is selected.
*/
public void copy();
/**
* Replace the currently selected text with the text on the clipboard.
* This method has no effect if the text is not editable, or if no
* text is on the clipboard.
*/
public void paste();
/**
* Remove selected text from the document, without altering the clipboard.
* This method has no effect if the
* text is not editable.
*/
public void clear();
/**
* Undo the most recent text change. This method has no effect if
* there is no change to undo.
*/
public void undo();
/**
* Redo the most recent text change. This method has no effect if
* there is no change to redo.
*/
public void redo();
/**
* Return the number of commands the command log can hold.
* @return the number of commands the command log can hold
*/
public int getCommandLogSize();
/**
* Set the number of commands the command log can hold. All
* redoable commands are removed when this method is called.
* @param size the number of commands kept in the command log
*/
public void setCommandLogSize(int size);
/**
* Remove all commands from the command log.
*/
public void clearCommandLog();
/**
* Modify the character styles on the selected characters. If no characters
* are selected, modify the typing style.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyCharacterStyleOnSelection(StyleModifier modifier);
/**
* Modify the paragraph styles in paragraphs containing selected characters, or
* the paragraph containing the insertion point.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyParagraphStyleOnSelection(StyleModifier modifier);
/**
* Return the KeyRemap used to process key events.
* @return the key remap used to process key events
* @see #setKeyRemap
*/
public KeyRemap getKeyRemap();
/**
* Use the given KeyRemap to map key events to characters.
* Only key
* events are affected by the remap; other text entering the
* control (via the clipboard, for example) is not affected
* by the KeyRemap.
* <p>
* Do not pass <tt>null</tt> to this method to leave key
* events unmapped. Instead, use <tt>KeyRemap.getIdentityRemap()</tt>
* @param remap the KeyRemap to use for mapping key events to characters
* @exception java.lang.NullPointerException if parameter is null
* @see KeyRemap
*/
public void setKeyRemap(KeyRemap remap);
/**
* Return the modification flag of the current text change.
* @see #setModified
*/
public boolean isModified();
/**
* Set the modification flag of the current text change.
*/
public void setModified(boolean modified);
}

View file

@ -0,0 +1,52 @@
/*
* @(#)$RCSfile: OffscreenBufferCache.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Image;
import java.awt.Component;
class OffscreenBufferCache {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private Image fOffscreenBuffer = null;
private Component fHost;
OffscreenBufferCache(Component host) {
fHost = host;
}
private Image makeBuffer(int width, int height) {
return fHost.createImage(Math.max(width, 1), Math.max(height, 1));
}
Image getBuffer(int width, int height) {
Image buffer = fOffscreenBuffer;
if (buffer != null) {
if (buffer.getWidth(fHost) >= width &&
buffer.getHeight(fHost) >= height) {
return buffer;
}
}
buffer = makeBuffer(width, height);
fOffscreenBuffer = buffer;
return buffer;
}
}

View file

@ -0,0 +1,100 @@
/*
* @(#)$RCSfile: PanelEventBroadcaster.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.util.Vector;
/**
* This class listens for text state change notifications
* and broadcasts them to all of its listeners.
*/
final class PanelEventBroadcaster {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private static final int FIRST = TextPanelEvent.TEXT_PANEL_FIRST;
private final Vector[] fListeners;
private final TextPanelEvent[] fEvents;
/**
* Construct a new PanelEventBroadcaster.
* @param panel the TextPanel for which events are broadcasted
*/
public PanelEventBroadcaster(MTextPanel panel) {
int count = TextPanelEvent.TEXT_PANEL_LAST - FIRST + 1;
fEvents = new TextPanelEvent[count];
fListeners = new Vector[count];
for (int i=0; i < fListeners.length; i++) {
fEvents[i] = new TextPanelEvent(panel, i+FIRST);
fListeners[i] = new Vector();
}
}
/**
* Add the given TextPanelListener to the TextPanelListeners to
* which notifications are forwarded.
* @param listener the listener to add
*/
public synchronized void addListener(TextPanelListener listener) {
for (int i=FIRST; i <= TextPanelEvent.TEXT_PANEL_LAST; i++) {
Vector listeners = fListeners[i-FIRST];
if (listener.respondsToEventType(i)) {
if (!listeners.contains(listener)) {
listeners.addElement(listener);
}
}
}
}
/**
* Remove the given TextPanelListener from the TextPanelListeners to
* which notifications are forwarded.
* @param listener the listener to remove
*/
public synchronized void removeListener(TextPanelListener listener) {
for (int i=FIRST; i <= TextPanelEvent.TEXT_PANEL_LAST; i++) {
Vector listeners = fListeners[i-FIRST];
if (listener.respondsToEventType(i)) {
listeners.removeElement(listener);
}
}
}
/**
* Receive a notification and forward it to all listeners.
* @changeCode one of the constants in the TextPanelListener class
*/
public synchronized void textStateChanged(int id) {
int index = id-FIRST;
TextPanelEvent event = fEvents[index];
Vector listeners = fListeners[index];
int size = listeners.size();
for (int i=0; i < size; i++) {
TextPanelListener listener =
(TextPanelListener) listeners.elementAt(i);
listener.textEventOccurred(event);
}
}
}

View file

@ -0,0 +1,22 @@
/*
* @(#)$RCSfile: RunStrategy.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
class RunStrategy {
void doIt(Runnable r) {
r.run();
}
}

View file

@ -0,0 +1,153 @@
/*
* @(#)$RCSfile: ScrollBarLayout.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
class ScrollBarLayout implements LayoutManager {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
Component fHorizScrollBar = null;
Component fVertScrollBar = null;
Component fChild = null;
public ScrollBarLayout() {
}
public void addLayoutComponent(String name, Component comp) {
if ("Center".equals(name))
fChild = comp;
else if ("South".equals(name))
fHorizScrollBar = comp;
else if ("East".equals(name))
fVertScrollBar = comp;
}
public void layoutContainer(Container target) {
Insets insets = target.getInsets();
Dimension targetSize = target.getSize();
int hsbHeight = (fHorizScrollBar != null) ? fHorizScrollBar.getPreferredSize().
height : 0;
int vsbWidth = (fVertScrollBar != null) ? fVertScrollBar.getPreferredSize().
width : 0;
if (fHorizScrollBar != null)
fHorizScrollBar.setBounds(insets.left, targetSize.height - insets.bottom -
hsbHeight, targetSize.width - vsbWidth, hsbHeight);
if (fVertScrollBar != null)
fVertScrollBar.setBounds(targetSize.width - insets.right - vsbWidth,
insets.top, vsbWidth, targetSize.height - hsbHeight);
if (fChild != null)
fChild.setBounds(insets.left, insets.top, targetSize.width - insets.right - vsbWidth,
targetSize.height - insets.bottom - hsbHeight);
}
public Dimension minimumLayoutSize(Container target) {
Dimension returnVal = new Dimension(0, 0);
Dimension hsbSize;
Dimension vsbSize;
Dimension childSize;
if (fHorizScrollBar != null && fHorizScrollBar.isVisible()) {
hsbSize = fHorizScrollBar.getMinimumSize();
}
else {
hsbSize = new Dimension(0, 0);
}
if (fVertScrollBar != null && fVertScrollBar.isVisible()) {
vsbSize = fVertScrollBar.getMinimumSize();
}
else {
vsbSize = new Dimension(0, 0);
}
if (fChild != null && fChild.isVisible()) {
childSize = fChild.getMinimumSize();
}
else {
childSize = new Dimension(0, 0);
}
returnVal.width = Math.max(childSize.width, hsbSize.width) + vsbSize.width;
returnVal.height = Math.max(childSize.height, vsbSize.height) + hsbSize.height;
Insets insets = target.getInsets();
returnVal.width += insets.left + insets.right;
returnVal.height += insets.top + insets.bottom;
return returnVal;
}
public Dimension preferredLayoutSize(Container target) {
Dimension returnVal = new Dimension(0, 0);
Dimension hsbSize;
Dimension vsbSize;
Dimension childSize;
if (fHorizScrollBar != null && fHorizScrollBar.isVisible()) {
hsbSize = fHorizScrollBar.getPreferredSize();
}
else {
hsbSize = new Dimension(0, 0);
}
if (fVertScrollBar != null && fVertScrollBar.isVisible()) {
vsbSize = fVertScrollBar.getPreferredSize();
}
else {
vsbSize = new Dimension(0, 0);
}
if (fChild != null && fChild.isVisible()) {
childSize = fChild.getPreferredSize();
}
else {
childSize = new Dimension(0, 0);
}
returnVal.width = Math.max(childSize.width, hsbSize.width) + vsbSize.width;
returnVal.height = Math.max(childSize.height, vsbSize.height) + hsbSize.height;
Insets insets = target.getInsets();
returnVal.width += insets.left + insets.right;
returnVal.height += insets.top + insets.bottom;
return returnVal;
}
public void removeLayoutComponent(Component comp) {
if (comp == fChild)
fChild = null;
else if (comp == fHorizScrollBar)
fHorizScrollBar = null;
else if (comp == fVertScrollBar)
fVertScrollBar = null;
}
}

View file

@ -0,0 +1,207 @@
/*
* @(#)$RCSfile: Scroller.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
/*
7/9/97 - changed some deprecated methods in Scrollbar
Also setting Unit and Block increment values. Maybe
it matters...
6/29/98 - reimplemented this class. Now this class talks to
any component which implements Scroller.Client.
ScrollHolder is gone, too.
2/4/99 - No longer a Panel. Also, doesn't create Scrollbars,
and in fact doesn't even use the Scrollbar class
directly.
*/
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.event.AdjustmentListener;
import java.awt.event.AdjustmentEvent;
import java.awt.Adjustable;
/**
* This class manages the interaction between a scrollable client
* and vertical and horizontal scrollbars. It calls the client's
* scrollTo method in response to manipulation of the scroll bars.
*
* This class used to be a Panel containing the scrollbars and
* the client panel. As part of the migration away from direct
* AWT dependencies, this class is no longer part of the view
* hierarchy. Instead it simply keeps a reference to its
* client and scroll bars. It is the responsibility of higher-
* level classes to set up the view hierarchy.
*/
final class Scroller implements AdjustmentListener
{
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
static interface Client {
void scrollTo(int x, int y);
Rectangle getScrollSize();
Rectangle getBounds();
}
private Adjustable fHorizScrollBar = null;
private Adjustable fVertScrollBar = null;
private Client fClient = null;
/**
* These are used if the respective Scrollbar is not present.
*/
private int fHorizValue, fVertValue;
private static final int DEFAULT_UNIT_INC = 10;
/**
* Construct a new Scroller with the given Adjustables,
* which really should be scrollbars of some ilk.
* Also, the Adjustables are required to be AWT Components,
* so the Scroller can enable and disable them.
* However, a Scroller can work with either AWT Scrollbars
* or JFC JScrollbars.
* @param horizScrollbar the horizontal scrollbar. null if
* there is no horizontal scrollbar.
* @param vertScrollbar the vertical scrollbar. null if
* there is no vertical scrollbar.
*/
public Scroller(Adjustable horizScrollBar,
Adjustable vertScrollBar) {
//setLayout(new ScrollBarLayout());
fHorizScrollBar = horizScrollBar;
fVertScrollBar = vertScrollBar;
if (fVertScrollBar != null) {
fVertScrollBar.setUnitIncrement(DEFAULT_UNIT_INC);
fVertScrollBar.addAdjustmentListener(this);
}
if (fHorizScrollBar != null) {
fHorizScrollBar.setUnitIncrement(DEFAULT_UNIT_INC);
fHorizScrollBar.addAdjustmentListener(this);
}
}
public void setClient(Client client) {
fClient = client;
clientScrollSizeChanged();
}
public void adjustmentValueChanged(AdjustmentEvent event) {
boolean horizontal;
if (event.getAdjustable() == fHorizScrollBar) {
int vertVal = fVertScrollBar == null? fVertValue :
fVertScrollBar.getValue();
scrollTo(event.getValue(), vertVal);
}
else {
int horizVal = fHorizScrollBar == null? fHorizValue :
fHorizScrollBar.getValue();
scrollTo(horizVal, event.getValue());
}
}
private void setValues(Adjustable scrollbar,
int visible,
int minimum,
int height) {
int maximum = minimum+height;
if (scrollbar != null) {
Component scrollbarToo = (Component) scrollbar;
if (maximum <= visible) {
scrollbarToo.setEnabled(false);
}
else {
scrollbarToo.setEnabled(true);
}
scrollbar.setMinimum(minimum);
scrollbar.setMaximum(maximum);
scrollbar.setVisibleAmount(visible);
scrollbar.setBlockIncrement(visible-DEFAULT_UNIT_INC);
}
}
public void clientScrollSizeChanged()
{
Rectangle bounds = fClient.getBounds();
Rectangle preferredSize = fClient.getScrollSize();
setValues(fHorizScrollBar, bounds.width, preferredSize.x, preferredSize.width);
setValues(fVertScrollBar, bounds.height, preferredSize.y, preferredSize.height);
}
public void setPosition(int x, int y) {
if (fHorizScrollBar != null) {
fHorizScrollBar.setValue(x);
}
else {
fHorizValue = x;
}
if (fVertScrollBar != null) {
fVertScrollBar.setValue(y);
}
else {
fVertValue = y;
}
}
private void scrollTo(int x, int y)
{
fClient.scrollTo(x, y);
}
public void setHorizLineDistance(int newDistance)
{
if (fHorizScrollBar != null) {
fHorizScrollBar.setUnitIncrement(newDistance);
}
}
public void setHorizPageOverlap(int newOverlap)
{
if (fHorizScrollBar != null) {
fHorizScrollBar.setBlockIncrement(
fHorizScrollBar.getVisibleAmount()-newOverlap);
}
}
public void setVertLineDistance(int newDistance)
{
if (fVertScrollBar != null) {
fVertScrollBar.setUnitIncrement(newDistance);
}
}
public void setVertPageOverlap(int newOverlap)
{
if (fVertScrollBar != null) {
fVertScrollBar.setBlockIncrement(
fVertScrollBar.getVisibleAmount()-newOverlap);
}
}
}

View file

@ -0,0 +1,206 @@
/*
* @(#)$RCSfile: SelectionDragInteractor.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.text.BreakIterator;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import com.ibm.richtext.textformat.TextOffset;
final class SelectionDragInteractor extends Behavior implements Runnable {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private TextComponent fTextComponent;
private TextSelection fSelection;
private RunStrategy fRunStrategy;
private TextOffset fAnchorStart; // aliases text offsets - client beware
private TextOffset fAnchorEnd;
private TextOffset fCurrent;
private final boolean fWasZeroLength;
private int fCurrentX;
private int fCurrentY;
private boolean fMouseOutside;
private Thread fAutoscrollThread = null;
private boolean fThreadRun = true;
private static final int kScrollSleep = 300;
public SelectionDragInteractor(TextSelection selection,
TextComponent textComponent,
RunStrategy runStrategy,
TextOffset anchorStart,
TextOffset anchorEnd,
TextOffset current,
int initialX,
int initialY,
boolean wasZeroLength) {
fTextComponent = textComponent;
fSelection = selection;
fRunStrategy = runStrategy;
fAnchorStart = anchorStart;
fAnchorEnd = anchorEnd;
fCurrent = current;
fCurrentX = initialX;
fCurrentY = initialY;
fMouseOutside = false;
fWasZeroLength = wasZeroLength;
setSelection();
}
public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
return true;
}
public boolean focusGained(FocusEvent event) {
return true;
}
public boolean focusLost(FocusEvent event) {
return true;
}
public boolean keyPressed(KeyEvent event) {
return true;
}
public boolean keyTyped(KeyEvent event) {
return true;
}
public boolean keyReleased(KeyEvent event) {
return true;
}
public synchronized boolean mouseDragged(MouseEvent e) {
int x = e.getX(), y = e.getY();
if (fCurrentX != x || fCurrentY != y) {
fCurrentX = x;
fCurrentY = y;
processMouseLocation();
}
return true;
}
public synchronized boolean mouseEnter(MouseEvent e) {
fMouseOutside = false;
return true;
}
public synchronized boolean mouseExited(MouseEvent e) {
if (fAutoscrollThread == null) {
fAutoscrollThread = new Thread(this);
fAutoscrollThread.start();
}
fMouseOutside = true;
notify();
return true;
}
public synchronized boolean mouseReleased(MouseEvent e) {
fMouseOutside = false;
fThreadRun = false;
if (fAutoscrollThread != null) {
fAutoscrollThread.interrupt();
}
removeFromOwner();
boolean isZeroLength = TextSelection.rangeIsZeroLength(fAnchorStart,
fAnchorEnd,
fCurrent);
fSelection.mouseReleased(isZeroLength != fWasZeroLength);
return true;
}
private void processMouseLocation() {
fTextComponent.scrollToShow(fCurrentX, fCurrentY);
fTextComponent.pointToTextOffset(fCurrent, fCurrentX, fCurrentY, null, true);
setSelection();
}
private void setSelection() {
if (fCurrent.greaterThan(fAnchorEnd)) {
fSelection.advanceToNextBoundary(fCurrent);
fSelection.setSelRangeAndDraw(fAnchorStart, fCurrent, fAnchorStart);
}
else if (fCurrent.lessThan(fAnchorStart)) {
fSelection.advanceToPreviousBoundary(fCurrent);
fSelection.setSelRangeAndDraw(fCurrent, fAnchorEnd, fAnchorStart);
}
else {
fCurrent.assign(fAnchorEnd);
fSelection.setSelRangeAndDraw(fAnchorStart, fAnchorEnd, fAnchorStart);
}
}
public void run() {
Runnable doMouseLoc = new Runnable() {
public void run() {
processMouseLocation();
}
};
while (fThreadRun) {
try {
Thread.sleep(kScrollSleep);
}
catch(InterruptedException e) {
return; // just quit scrolling
}
synchronized(this) {
while (!fMouseOutside) {
try {
wait();
}
catch(InterruptedException e) {
return; // just quit scrolling
}
}
fRunStrategy.doIt(doMouseLoc);
}
}
}
}

View file

@ -0,0 +1,177 @@
/*
* @(#)$RCSfile: SimpleCommandLog.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
final class SimpleCommandLog {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private Command fLastCommand = null;
private Command fCurrentCommand = null;
private PanelEventBroadcaster fListener;
private boolean fBaseIsModified;
private int fLogSize = 14;
public SimpleCommandLog(PanelEventBroadcaster listener) {
fListener = listener;
fBaseIsModified = false;
}
/** adds the specfied command to the top of the command stack
* (any undone commands on the stack are removed)
* This function assumes the command has already been executed (i.e., its execute() method
* has been called, or an equivalent action has been taken) */
void add(Command newCommand) {
// if there are commands on the stack that have been undone, they are
// dropped on the floor here
newCommand.setPreviousCommand(fCurrentCommand);
final Command oldLastCommand = fLastCommand;
fLastCommand = null;
fCurrentCommand = newCommand;
limitCommands(fLogSize);
if (oldLastCommand != null) {
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
}
/**
* If the command list is longer than logSize, truncate it.
* This method traverses the list each time, and is not a model
* of efficiency. It's a temporary way to plug this memory leak
* until I can implement a bounded command log.
*/
private void limitCommands(int logSize) {
if (logSize == 0) {
fCurrentCommand = null;
}
else {
Command currentCommand = fCurrentCommand;
int remaining = logSize-1;
while (currentCommand != null && remaining > 0) {
currentCommand = currentCommand.previousCommand();
remaining -= 1;
}
if (currentCommand != null) {
currentCommand.setPreviousCommand(null);
}
}
}
/** adds the specfied command to the top of the command stack and executes it */
void addAndDo(Command newCommand) {
add(newCommand);
newCommand.execute();
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
/** undoes the command on the top of the command stack, if there is one */
void undo() {
if (fCurrentCommand != null) {
Command current = fCurrentCommand;
current.undo();
fCurrentCommand = current.previousCommand();
current.setPreviousCommand(fLastCommand);
fLastCommand = current;
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
}
/** redoes the last undone command on the command stack, if there are any */
void redo() {
if (fLastCommand != null) {
Command last = fLastCommand;
last.redo();
fLastCommand = last.previousCommand();
last.setPreviousCommand(fCurrentCommand);
fCurrentCommand = last;
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
}
public boolean canUndo() {
return fCurrentCommand != null;
}
public boolean canRedo() {
return fLastCommand != null;
}
public boolean isModified() {
if (fCurrentCommand == null) {
return fBaseIsModified;
}
else {
return fCurrentCommand.isModified();
}
}
public void setModified(boolean modified) {
if (fCurrentCommand == null) {
fBaseIsModified = modified;
}
else {
fCurrentCommand.setModified(modified);
}
}
public void clearLog() {
if (fCurrentCommand != null) {
fBaseIsModified = fCurrentCommand.isModified();
}
boolean changed = fCurrentCommand != null || fLastCommand != null;
fCurrentCommand = null;
fLastCommand = null;
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
public void setLogSize(int size) {
if (size < 0) {
throw new IllegalArgumentException("log size cannot be negative");
}
if (size < fLogSize) {
limitCommands(size);
}
fLogSize = size;
if (fLastCommand != null || size == 0) {
fLastCommand = null;
fListener.textStateChanged(TextPanelEvent.UNDO_STATE_CHANGED);
}
}
public int getLogSize() {
return fLogSize;
}
}

View file

@ -0,0 +1,49 @@
/*
* @(#)$RCSfile: StyleChangeCommand.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import com.ibm.richtext.styledtext.StyleModifier;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.textformat.TextOffset;
class StyleChangeCommand extends TextCommand {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private boolean fCharacter;
private StyleModifier fModifier;
public StyleChangeCommand(TextEditBehavior behavior,
MText originalText,
TextOffset selStartBefore,
TextOffset selEndBefore,
StyleModifier modifier,
boolean character) {
super(behavior, originalText, selStartBefore.fOffset, selStartBefore, selEndBefore);
fModifier = modifier;
fCharacter = character;
}
public int affectedRangeEnd() {
return fSelEndBefore.fOffset;
}
public void execute() {
fBehavior.doModifyStyles(fAffectedRangeStart, fSelEndBefore.fOffset,
fModifier, fCharacter, fSelStartBefore, fSelEndBefore);
}
}

View file

@ -0,0 +1,219 @@
/*
* @(#)$RCSfile: StyledTextClipboard.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.InputStream;
import java.awt.Toolkit;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.StyledText;
/**
* Wrapper for java.awt.datatransfer.Clipboard
* Packages an MConstText in a transferable, and puts it on the clipboard.
*/
class StyledTextClipboard implements ClipboardOwner {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
// This class has a workaround for a bug in the Windows system clipboard.
// The system clipboard will only return String content, even
// though it has a reference to the contents. So if our
// clipboard is the system clipboard, we'll keep a reference
// to the content and use that instead of what the Clipboard returns.
private static Clipboard SYSTEM = null;
static {
try {
SYSTEM = Toolkit.getDefaultToolkit().getSystemClipboard();
}
catch(Throwable th) {
}
}
private static StyledTextClipboard fgSystemClipboard = null;
public static StyledTextClipboard getClipboardFor(Clipboard clipboard) {
if (clipboard == SYSTEM && SYSTEM != null) {
synchronized(SYSTEM) {
if (fgSystemClipboard == null) {
fgSystemClipboard = new StyledTextClipboard(SYSTEM, true);
}
}
return fgSystemClipboard;
}
else {
return new StyledTextClipboard(clipboard, false);
}
}
private Clipboard fClipboard;
private boolean fUseLocalContents;
private Transferable fContents = null;
private StyledTextClipboard(Clipboard clipboard, boolean useLocalContents) {
if (clipboard == null) {
fClipboard = new Clipboard("TextPanel clipboard");
}
else {
fClipboard = clipboard;
}
fUseLocalContents = useLocalContents;
}
public void lostOwnership(Clipboard clipboard,
Transferable contents) {
if (contents == fContents) {
this.fContents = null;
}
}
public void setContents(MConstText newContents) {
TransferableText contents = new TransferableText(newContents);
if (fClipboard == SYSTEM) {
fContents = contents;
}
fClipboard.setContents(contents, this);
}
private Transferable getClipboardContents() {
if (fUseLocalContents && fContents != null) {
return fContents;
}
return fClipboard.getContents(this);
}
/**
* Has contents - faster than getContents for finding out whether the
* clipboard has text.
*/
public boolean hasContents() {
Transferable contents = getClipboardContents();
if (contents == null) {
return false;
}
return contents.isDataFlavorSupported(MConstText.styledTextFlavor) ||
contents.isDataFlavorSupported(DataFlavor.stringFlavor) ||
contents.isDataFlavorSupported(DataFlavor.plainTextFlavor);
}
private String getString(InputStream inStream) throws IOException {
String value = new String();
int bytesRead;
do {
byte inBytes[] = new byte[inStream.available()];
bytesRead = inStream.read(inBytes);
if (bytesRead != -1)
value = value + new String(inBytes);
} while (bytesRead != -1);
return value;
}
/**
* If the Clipboard has text content, return it as an
* MConstText. Otherwise return null.
* @param defaultStyle the style to apply to unstyled
* text (such as a String). If the clipboard
* has styled text this parameter is not used.
*/
public MConstText getContents(AttributeMap defaultStyle) {
Transferable contents = getClipboardContents();
if (contents == null) {
return null;
}
DataFlavor flavors[] = contents.getTransferDataFlavors();
// search flavors for our flavor, String flavor and raw text flavor
Exception ex = null;
try {
int i;
for (i=0; i < flavors.length; i++) {
if (flavors[i].equals(MConstText.styledTextFlavor))
break;
}
if (i < flavors.length) {
Object data = contents.getTransferData(MConstText.styledTextFlavor);
if (data == null)
System.out.println("Data is null.");
return (MConstText) data;
}
for (i=0; i < flavors.length; i++) {
if (flavors[i].equals(DataFlavor.stringFlavor))
break;
}
if (i < flavors.length) {
Object data = contents.getTransferData(DataFlavor.stringFlavor);
return new StyledText((String) data, defaultStyle);
}
for (i=0; i < flavors.length; i++) {
if (flavors[i].equals(DataFlavor.plainTextFlavor))
break;
}
if (i < flavors.length) {
Object data = contents.getTransferData(DataFlavor.plainTextFlavor);
String textString = getString((InputStream) data);
return new StyledText(textString, defaultStyle);
}
}
catch(UnsupportedFlavorException e) {
ex = e;
}
catch(IOException e) {
ex = e;
}
System.out.println("Exception when retrieving data. Exception:" + ex);
return null;
}
}

View file

@ -0,0 +1,81 @@
/*
* @(#)$RCSfile: TextChangeCommand.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.textformat.TextOffset;
class TextChangeCommand extends TextCommand {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private MConstText fNewText;
private TextOffset fSelStartAfter;
private TextOffset fSelEndAfter;
public TextChangeCommand(TextEditBehavior behavior,
MText originalText,
MConstText newText,
int affectedRangeStart,
TextOffset selStartBefore,
TextOffset selEndBefore,
TextOffset selStartAfter,
TextOffset selEndAfter) {
super(behavior, originalText, affectedRangeStart, selStartBefore, selEndBefore);
fNewText = newText;
fSelStartAfter = new TextOffset();
fSelStartAfter.assign(selStartAfter);
fSelEndAfter = new TextOffset();
fSelEndAfter.assign(selEndAfter);
}
public int affectedRangeEnd() {
if (fNewText == null)
return fAffectedRangeStart;
else
return fAffectedRangeStart + fNewText.length();
}
public void execute() {
fBehavior.doReplaceText(fAffectedRangeStart, fAffectedRangeStart + fOriginalText.length(),
fNewText, fSelStartAfter, fSelEndAfter);
}
public int affectedRangeStart() {
return fAffectedRangeStart;
}
public void setNewText(MConstText newText) {
fNewText = newText;
}
public void setSelRangeAfter(TextOffset start, TextOffset end) {
if (fSelStartAfter == null)
fSelStartAfter = new TextOffset();
if (fSelEndAfter == null)
fSelEndAfter = new TextOffset();
fSelStartAfter.assign(start);
fSelEndAfter.assign(end);
}
public void prependToOldText(MConstText newText) {
fOriginalText.insert(0, newText);
fAffectedRangeStart -= newText.length();
}
public void appendToOldText(MConstText newText) {
fOriginalText.append(newText);
}
}

View file

@ -0,0 +1,51 @@
/*
* @(#)$RCSfile: TextCommand.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.textformat.TextOffset;
abstract class TextCommand extends Command {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
protected TextEditBehavior fBehavior;
protected MText fOriginalText;
protected int fAffectedRangeStart;
protected TextOffset fSelStartBefore;
protected TextOffset fSelEndBefore;
public TextCommand(TextEditBehavior behavior,
MText originalText,
int affectedRangeStart,
TextOffset selStartBefore,
TextOffset selEndBefore) {
fBehavior = behavior;
fOriginalText = originalText;
fAffectedRangeStart = affectedRangeStart;
fSelStartBefore = new TextOffset();
fSelStartBefore.assign(selStartBefore);
fSelEndBefore = new TextOffset();
fSelEndBefore.assign(selEndBefore);
}
public abstract int affectedRangeEnd();
public void undo() {
fBehavior.doReplaceText(fAffectedRangeStart, affectedRangeEnd(), fOriginalText,
fSelStartBefore, fSelEndBefore);
}
}

View file

@ -0,0 +1,969 @@
/*
* @(#)$RCSfile: TextComponent.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.textformat.TextOffset;
import com.ibm.richtext.textformat.MFormatter;
import com.ibm.textlayout.attributes.AttributeMap;
class TextComponent extends FakeComponent
implements BehaviorOwner,
FocusListener,
KeyListener,
MouseListener,
MouseMotionListener,
Scroller.Client {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public static final int WINDOW_WIDTH = -10;
public static final int DEFAULT_INSET = 10;
private static final Color STRONG_CARET_COLOR = Color.black;
private static final Color WEAK_CARET_COLOR = Color.darkGray;
private Behavior fBehavior;
private MText fText;
private StyledTextClipboard fClipboard;
private boolean fScrolls;
private Scroller fScroller;
private DocumentView fDocumentView = null;
// sigh - can't create DocumentView until addNotify() is called.
// These values hold DocumentView ctor args
private AttributeMap fDefaultValues;
private boolean fViewWraps;
private int fViewWrapWidth;
private int fViewInsetAmount;
private PanelEventBroadcaster fListener;
/**
* Create a new TextComponent.
* @param text the text model. This model will be used for
* the life of the component, even if setText is called
* @param wraps if true, the text is wrapped to the specified
* wrapping width. If false, the text wraps only at paragraph breaks.
* @param wrapWidth ignored if wraps is false. Text wraps to this width
* unless the width is WINDOW_WIDTH, in which case text wraps to width
* of this component. Should not be negative (unless it is WINDOW_WIDTH).
* @param insetAmount the size of the margins around the text
* @param clipboard the clipboard to use for cut/copy/paste operations.
* If null, the component will use its own clipboard.
*/
public TextComponent(MText text,
AttributeMap defaultValues,
boolean wraps,
int wrapWidth,
int insetAmount,
StyledTextClipboard clipboard,
boolean scrolls,
Scroller scroller,
PanelEventBroadcaster listener) {
fBehavior = null;
if (text == null) {
throw new IllegalArgumentException("Text is null.");
}
fText = text;
fDefaultValues = defaultValues;
if (clipboard == null) {
throw new IllegalArgumentException("Clipboard is null.");
}
fClipboard = clipboard;
fScrolls = scrolls;
fScroller = scroller;
fDocumentView = null;
fViewWrapWidth = wrapWidth;
fViewWraps = wraps;
fViewInsetAmount = insetAmount;
fListener = listener;
}
AttributeMap getDefaultValues() {
return fDefaultValues;
}
void setHost(Component component) {
super.setHost(component);
component.addFocusListener(this);
component.addKeyListener(this);
component.addMouseListener(this);
component.addMouseMotionListener(this);
component.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (fDocumentView != null) {
fDocumentView.hostSizeChanged();
scrollToShow(fDocumentView.getDocumentBounds());
}
}
});
}
// Create document view here. TextComponent isn't fully constructed
// until this is called.
// This must be called by host component!
void addNotify() {
Graphics g = getGraphics();
if (g == null) {
throw new Error("Graphics should be valid here but isn't.");
}
fDocumentView = new DocumentView(this,
fText,
fDefaultValues,
fViewWraps,
fViewWrapWidth,
fViewInsetAmount,
fListener);
documentSizeChanged();
fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED);
}
public Rectangle getBounds() {
if (fHost != null) {
return fHost.getBounds();
}
else {
return new Rectangle(0, 0, 0, 0);
}
}
Graphics getGraphics() {
return (fHost==null)? null : fHost.getGraphics();
}
void requestFocus() {
if (fHost != null) {
fHost.requestFocus();
}
}
// *** Behavior management ***
public Behavior getBehavior() {
return fBehavior;
}
public void setBehavior(Behavior b) {
fBehavior = b;
}
// *** Events - just forward to behavior ***
public void focusGained(FocusEvent event) {
if (fBehavior != null)
fBehavior.focusGained(event);
}
public void focusLost(FocusEvent event) {
if (fBehavior != null)
fBehavior.focusLost(event);
}
public void keyPressed(KeyEvent event) {
if (fBehavior != null)
fBehavior.keyPressed(event);
}
public void keyTyped(KeyEvent event) {
if (fBehavior != null) {
fBehavior.keyTyped(event);
}
}
public void keyReleased(KeyEvent event) {
if (fBehavior != null)
fBehavior.keyReleased(event);
}
public void mouseClicked(MouseEvent event) {
return; // no behavior method for this
}
public void mouseDragged(MouseEvent event) {
if (fBehavior != null)
fBehavior.mouseDragged(event);
}
public void mouseEntered(MouseEvent event) {
if (fBehavior != null)
fBehavior.mouseEntered(event);
}
public void mouseExited(MouseEvent event) {
if (fBehavior != null)
fBehavior.mouseExited(event);
}
public void mouseMoved(MouseEvent event) {
if (fBehavior != null)
fBehavior.mouseMoved(event);
}
public void mousePressed(MouseEvent event) {
if (fBehavior != null)
fBehavior.mousePressed(event);
}
public void mouseReleased(MouseEvent event) {
if (fBehavior != null)
fBehavior.mouseReleased(event);
}
public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
boolean handled = false;
if (fBehavior != null) {
handled = fBehavior.textControlEventOccurred(event, what);
}
return handled;
}
// *** Scroll methods - called by Behaviors
// viewStart, viewLimit is visible bounds of window
// targetStart, targetLimit is the region to scroll into view
private static int getScrollDifference(int viewStart,
int viewLimit,
int targetStart,
int targetLimit) {
if (viewStart <= targetStart) {
if (viewLimit >= targetLimit) {
return 0;
}
else {
return Math.max(viewStart-targetStart, viewLimit-targetLimit);
}
}
else if (viewLimit > targetLimit) {
return viewLimit - targetLimit;
}
else {
return 0;
}
}
void scrollToShow(Rectangle showRect) {
if (fDocumentView != null) {
Rectangle bounds = getBounds();
int dx = getScrollDifference(showRect.x, showRect.x + showRect.width,
bounds.x, bounds.x + bounds.width);
int dy = getScrollDifference(showRect.y, showRect.y + showRect.height,
bounds.y, bounds.y + bounds.height);
scrollSelf(dx, dy);
}
}
void scrollToShow(int showX, int showY) {
if (fDocumentView != null) {
int dx = 0, dy = 0;
Rectangle bounds = getBounds();
if (showX < bounds.x) {
dx = showX - bounds.x;
}
else if (showX > bounds.x + bounds.width) {
dx = showX - (bounds.x + bounds.width);
}
if (showY < bounds.y) {
dy = showY - bounds.y;
}
else if (showY > bounds.y + bounds.height) {
dy = showY - (bounds.y + bounds.height);
}
scrollSelf(dx, dy);
}
}
private int pinScrollOffset(int delta,
int contentStart,
int contentLength,
int viewStart,
int viewLength) {
if (delta > 0) {
int viewLimit = viewStart + viewLength;
int contentLimit = contentStart + contentLength;
if (viewLimit + delta > contentLimit) {
delta = Math.max(0, contentLimit-viewLimit);
}
}
else {
if (viewStart + delta < contentStart) {
delta = Math.min(0, contentStart-viewStart);
}
}
return delta;
}
private void scrollSelf(int dx, int dy) {
boolean scrolled = scrollBy(dx, dy);
if (scrolled && fScroller != null) {
Rectangle documentBounds = fDocumentView.getDocumentBounds();
fScroller.setPosition(-documentBounds.x,
-documentBounds.y);
}
}
private synchronized boolean scrollBy(int dx, int dy) {
boolean scrolled = false;
if (fScrolls) {
Rectangle documentBounds = fDocumentView.getDocumentBounds();
Rectangle viewBounds = getBounds();
int oldDx = dx;
dx = pinScrollOffset(dx,
documentBounds.x,
documentBounds.width,
viewBounds.x,
viewBounds.width);
dy = pinScrollOffset(dy,
documentBounds.y,
documentBounds.height,
viewBounds.y,
viewBounds.height);
if (dx != 0 || dy != 0) {
scrolled = true;
fDocumentView.moveBy(-dx, -dy);
}
}
return scrolled;
}
// implementation of Scroller.Client - called by Scroller
// they have to be public since they're in an interface
// no one else should call these methods
public Rectangle getScrollSize() {
if (fDocumentView != null) {
return fDocumentView.getScrollableArea();
}
else {
return new Rectangle(0, 0, 0, 0);
}
}
public void scrollTo(int x, int y) {
if (fDocumentView != null) {
scrollBy(x + fDocumentView.getDocX(), y + fDocumentView.getDocY());
}
}
// *** Text access ***
MConstText getText() {
return fText;
}
MText getModifiableText() {
return fText;
}
StyledTextClipboard getClipboard() {
return fClipboard;
}
public synchronized void paint(Graphics g) {
if (fDocumentView != null) {
fDocumentView.paint(g);
}
}
// *** Metric info - used by Behaviors
Rectangle getCaretRect(TextOffset offset) {
if (fDocumentView != null) {
return fDocumentView.getCaretRect(offset);
}
else {
return new Rectangle(0, 0);
}
}
TextOffset pointToTextOffset(TextOffset result,
int x,
int y,
TextOffset anchor,
boolean infiniteMode) {
if (fDocumentView != null) {
return fDocumentView.pointToTextOffset(result, x, y, anchor, infiniteMode);
}
else {
return new TextOffset();
}
}
// *** Other stuff used by Behaviors - mostly formatter exports
int lineContaining(TextOffset offset) {
if (fDocumentView != null) {
return fDocumentView.lineContaining(offset);
}
else {
return 0;
}
}
int lineRangeLow(int lineNumber) {
if (fDocumentView != null) {
return fDocumentView.lineRangeLow(lineNumber);
}
else {
return 0;
}
}
int lineRangeLimit(int lineNumber) {
if (fDocumentView != null) {
return fDocumentView.lineRangeLimit(lineNumber);
}
else {
return 0;
}
}
void stopBackgroundFormatting() {
if (fDocumentView != null) {
fDocumentView.stopBackgroundFormatting();
}
}
Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) {
if (fDocumentView != null) {
return fDocumentView.getBoundingRect(offset1, offset2);
}
else {
return new Rectangle(0, 0, 0, 0);
}
}
synchronized void reformatAndDrawText(int reformatStart,
int reformatLength,
TextOffset selStart,
TextOffset selEnd,
Rectangle additionalUpdateRect,
Color hiliteColor) {
if (fDocumentView != null) {
fDocumentView.reformatAndDrawText(reformatStart,
reformatLength,
selStart,
selEnd,
additionalUpdateRect,
hiliteColor);
}
}
TextOffset findNewInsertionOffset(TextOffset result,
TextOffset initialOffset,
TextOffset previousOffset,
short direction) {
if (fDocumentView != null) {
return fDocumentView.findNewInsertionOffset(result, initialOffset, previousOffset, direction);
}
else {
return new TextOffset(initialOffset);
}
}
synchronized void drawText(Graphics g,
Rectangle damagedRect,
boolean selectionVisible,
TextOffset selStart,
TextOffset selEnd,
Color hiliteColor) {
if (fDocumentView != null) {
fDocumentView.drawText(g, damagedRect, selectionVisible, selStart, selEnd, hiliteColor);
}
}
private void documentSizeChanged() {
if (fScroller != null) {
fScroller.clientScrollSizeChanged();
}
}
int getFormatWidth() {
if (fDocumentView != null) {
return fDocumentView.getFormatWidth();
}
else {
return 0;
}
}
/**
* Return true if the paragraph at the given offset is left-to-right.
* @param offset an offset in the text
* @return true if the paragraph at the given offset is left-to-right
*/
boolean paragraphIsLeftToRight(int offset) {
if (fDocumentView != null) {
return fDocumentView.paragraphIsLeftToRight(offset);
}
else {
return true;
}
}
private static final class DocumentView {
private TextComponent fHost;
private boolean fWraps;
private boolean fWrapToWindowWidth;
private int fInsetAmount;
private PanelEventBroadcaster fListener;
// fBounds is the total scrollable area of the document (including insets)
private Rectangle fBounds = new Rectangle();
private Point fOrigin;
private MFormatter fFormatter;
private OffscreenBufferCache fBufferCache;
// Note, when this is true the caret won't blink in 1.1. Looks like an AWT bug.
private static boolean fNoOffscreenBuffer =
Boolean.getBoolean("TextComponent.NoOffscreenBuffer");
// Amount by which to reduce the format width to allow for right-aligned carets.
private final int CARET_SLOP = 1;
DocumentView(TextComponent host,
MConstText text,
AttributeMap defaultValues,
boolean wraps,
int wrapWidth,
int insetAmount,
PanelEventBroadcaster listener) {
fHost = host;
fWrapToWindowWidth = wrapWidth == WINDOW_WIDTH;
fInsetAmount = insetAmount;
fListener = listener;
initFormatterAndSize(text, defaultValues, wraps, wrapWidth);
fBufferCache = new OffscreenBufferCache(host.fHost);
}
/**
* Note: this computes the bounds rectangle relative to fOrigin
*/
private void calcBoundsRect() {
final int insetDim = 2 * fInsetAmount;
final int minX = fFormatter.minX();
final int minY = fFormatter.minY();
fBounds.setBounds(fOrigin.x + minX - fInsetAmount,
fOrigin.y + minY - fInsetAmount,
fFormatter.maxX() - minX + insetDim,
fFormatter.maxY() - minY + insetDim);
//if (minX <= 0) {
// System.out.println("calcBoundsRect: minX="+minX+
// "; bounds.x="+fBounds.x+"; width="+fBounds.width);
//}
}
private void initFormatterAndSize(MConstText text,
AttributeMap defaultValues,
boolean wraps,
int wrapWidth) {
Rectangle hostBounds = fHost.getBounds();
int formatWidth;
if (!wraps || fWrapToWindowWidth) {
formatWidth = hostBounds.width - 2 * fInsetAmount;
if (formatWidth <= CARET_SLOP) {
formatWidth = CARET_SLOP+1;
}
}
else {
formatWidth = wrapWidth;
}
fFormatter = MFormatter.createFormatter(text,
defaultValues,
formatWidth-CARET_SLOP,
wraps,
fHost.getGraphics());
fFormatter.formatToHeight(hostBounds.height * 2);
fOrigin = new Point(fInsetAmount, fInsetAmount);
calcBoundsRect();
}
// notification method called by TextComponent
void hostSizeChanged() {
final boolean wrap = fFormatter.wrap();
if (fWrapToWindowWidth || !wrap) {
Rectangle hostBounds = fHost.getBounds();
final int insetDim = 2 * fInsetAmount;
int formatWidth = hostBounds.width - 2*fInsetAmount;
if (formatWidth <= CARET_SLOP) {
formatWidth = CARET_SLOP+1;
}
fFormatter.setLineBound(formatWidth-CARET_SLOP);
fFormatter.formatToHeight(hostBounds.y + (hostBounds.height*2) - fOrigin.y);
calcBoundsRect();
//System.out.println("Window bounds="+hostBounds+"; document bounds="+fBounds);
fHost.documentSizeChanged();
fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED);
//System.out.println("formatWidth="+formatWidth);
//System.out.println("document bounds="+fBounds);
//System.out.println();
}
//dumpWidthInfo();
}
int getFormatWidth() {
return fFormatter.lineBound();
}
boolean paragraphIsLeftToRight(int offset) {
int lineNumber = fFormatter.lineContaining(offset);
return fFormatter.lineIsLeftToRight(lineNumber);
}
private void textSizeMightHaveChanged() {
boolean changed = false;
final int insetDim = 2 * fInsetAmount;
int textHeight = fFormatter.maxY() - fFormatter.minY() + insetDim;
if (textHeight != fBounds.height) {
fBounds.height = textHeight;
changed = true;
}
if (!fFormatter.wrap()) {
int textWidth = fFormatter.maxX() - fFormatter.minX() + insetDim;
if (textWidth != fBounds.width) {
fBounds.width = textWidth;
changed = true;
}
}
if (changed) {
//System.out.println("Text size changed. fBounds: " + fBounds);
calcBoundsRect();
fHost.documentSizeChanged();
fHost.scrollToShow(getDocumentBounds());
}
}
private void doDrawText(Graphics g,
Rectangle drawRect,
boolean selectionVisible,
TextOffset selStart,
TextOffset selEnd,
Color hiliteColor) {
g.clearRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);
if (selectionVisible) {
fFormatter.draw(g, drawRect, fOrigin, selStart, selEnd, hiliteColor);
}
else {
fFormatter.draw(g, drawRect, fOrigin, null, null, null);
}
if (selStart != null && selStart.equals(selEnd) && selectionVisible) {
fFormatter.drawCaret(g, selStart, fOrigin,
STRONG_CARET_COLOR, WEAK_CARET_COLOR);
}
}
void drawText(Graphics g,
Rectangle drawRect,
boolean selectionVisible,
TextOffset selStart,
TextOffset selEnd,
Color hiliteColor) {
if (g != null) {
drawRect = drawRect.intersection(fHost.getBounds());
//System.out.println("drawText:drawRect: " + drawRect);
g.clipRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);
if (fNoOffscreenBuffer) {
doDrawText(g, drawRect, selectionVisible, selStart, selEnd, hiliteColor);
}
else {
Image offscreenBuffer = fBufferCache.getBuffer(drawRect.width, drawRect.height);
Graphics offscreenGraphics = offscreenBuffer.getGraphics();
offscreenGraphics.translate(-drawRect.x, -drawRect.y);
doDrawText(offscreenGraphics, drawRect, selectionVisible, selStart, selEnd, hiliteColor);
g.drawImage(offscreenBuffer, drawRect.x, drawRect.y, fHost.fHost);
}
}
textSizeMightHaveChanged();
}
void reformatAndDrawText(int reformatStart,
int reformatLength,
TextOffset selStart,
TextOffset selEnd,
Rectangle additionalUpdateRect,
Color hiliteColor) {
Rectangle visibleBounds = fHost.getBounds();
Rectangle redrawRect = fFormatter.updateFormat(reformatStart,
reformatLength,
visibleBounds,
fOrigin);
//System.out.println("[1] redrawRect: " + redrawRect);
if (additionalUpdateRect != null) {
redrawRect.add(additionalUpdateRect);
//System.out.println("[2] redrawRect: " + redrawRect);
}
boolean haveSelection;
if (selStart != null && selEnd != null) {
haveSelection = true;
redrawRect.add(fFormatter.getBoundingRect(selStart, selEnd, fOrigin, fFormatter.LOOSE));
//System.out.println("[3] redrawRect: " + redrawRect);
}
else {
haveSelection = false;
}
drawText(fHost.getGraphics(), redrawRect, haveSelection, selStart, selEnd, hiliteColor);
}
private void letBehaviorDraw(Graphics g, Rectangle drawRect) {
boolean result = false;
if (fHost.fBehavior != null) {
result = fHost.fBehavior.paint(g, drawRect);
}
if (!result) {
drawText(g, drawRect, false, null, null, null);
}
}
void moveBy(int dx, int dy) {
Rectangle visibleBounds = fHost.getBounds();
Graphics g = fHost.getGraphics();
fBounds.x += dx;
fBounds.y += dy;
fOrigin.x += dx;
fOrigin.y += dy;
Rectangle refreshRect = new Rectangle(visibleBounds);
if (dx == 0) {
if (g != null) {
g.copyArea(visibleBounds.x, visibleBounds.y, visibleBounds.width, visibleBounds.height, dx, dy);
}
if (dy < 0) {
refreshRect.y = visibleBounds.y + visibleBounds.height + dy;
}
refreshRect.height = Math.abs(dy);
//System.out.println("refreshRect=" + refreshRect);
}
letBehaviorDraw(g, refreshRect);
}
private Rectangle getInsetBounds() {
int insetDim = 2 * fInsetAmount;
return new Rectangle(fBounds.x-fInsetAmount,
fBounds.y-fInsetAmount,
fBounds.width+insetDim,
fBounds.height+insetDim);
}
void paint(Graphics g) {
Rectangle hostBounds = fHost.getBounds();
Rectangle textRefreshRect = hostBounds.intersection(getInsetBounds());
letBehaviorDraw(g, textRefreshRect);
}
Rectangle getCaretRect(TextOffset offset) {
return fFormatter.getCaretRect(offset, fOrigin);
}
TextOffset pointToTextOffset(TextOffset result,
int x,
int y,
TextOffset anchor,
boolean infiniteMode) {
return fFormatter.pointToTextOffset(result, x, y, fOrigin, anchor, infiniteMode);
}
Rectangle getScrollableArea() {
Rectangle area = new Rectangle(fBounds);
area.x += fInsetAmount - fOrigin.x;
area.y += fInsetAmount - fOrigin.y;
return area;
}
/**
* Doesn't clone so TextComponent needs to be nice. TextComponent
* is the only class which can access this anyway.
*/
Rectangle getDocumentBounds() {
return fBounds;
}
int getDocX() {
return fOrigin.x - fInsetAmount;
}
int getDocY() {
return fOrigin.y - fInsetAmount;
}
int lineContaining(TextOffset offset) {
return fFormatter.lineContaining(offset);
}
int lineRangeLow(int lineNumber) {
return fFormatter.lineRangeLow(lineNumber);
}
int lineRangeLimit(int lineNumber) {
return fFormatter.lineRangeLimit(lineNumber);
}
void stopBackgroundFormatting() {
fFormatter.stopBackgroundFormatting();
}
Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) {
Rectangle r = fFormatter.getBoundingRect(offset1, offset2, fOrigin, fFormatter.TIGHT);
//r.width += CARET_SLOP;
//System.out.println("offset1="+offset1+"; offset2="+offset2);
//System.out.println("bounds width="+r.width+"; host width="+(fHost.getBounds().width));
return r;
}
TextOffset findNewInsertionOffset(TextOffset result,
TextOffset initialOffset,
TextOffset previousOffset,
short direction) {
return fFormatter.findNewInsertionOffset(
result, initialOffset, previousOffset, direction);
}
}
}

View file

@ -0,0 +1,484 @@
/*
* @(#)$RCSfile: TextEditBehavior.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Rectangle;
import com.ibm.textlayout.attributes.AttributeMap;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.textformat.TextOffset;
import com.ibm.richtext.styledtext.StyleModifier;
// All changes to the text should happen in this class, or in
// its TypingInteractor.
class TextEditBehavior extends Behavior {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private TextComponent fTextComponent;
private TextSelection fSelection;
private MText fText;
private SimpleCommandLog fCommandLog;
private PanelEventBroadcaster fListener;
private TypingInteractor fTypingInteractor = null;
private KeyRemap fRemap;
private AttributeMap fSavedTypingStyle = null;
private int fSavedInsPt = 0;
public TextEditBehavior(TextComponent textComponent,
TextSelection selection,
PanelEventBroadcaster listener,
KeyRemap remap) {
fTextComponent = textComponent;
fSelection = selection;
fText = textComponent.getModifiableText();
fCommandLog = new SimpleCommandLog(listener);
fListener = listener;
fRemap = remap;
}
public KeyRemap getKeyRemap() {
return fRemap;
}
public void setKeyRemap(KeyRemap remap) {
fRemap = remap;
}
public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
boolean handled = true;
if (event == Behavior.CHARACTER_STYLE_MOD ||
event == Behavior.PARAGRAPH_STYLE_MOD) {
doStyleChange(event, what);
}
else if (event == Behavior.CUT) {
doCut();
}
else if (event == Behavior.PASTE) {
doPaste();
}
else if (event == Behavior.CLEAR) {
doClear();
}
else if (event == Behavior.REPLACE) {
doUndoableReplace((TextReplacement) what);
}
else if (event == Behavior.UNDO) {
fCommandLog.undo();
}
else if (event == Behavior.REDO) {
fCommandLog.redo();
}
else if (event == Behavior.SET_MODIFIED) {
fCommandLog.setModified(what == Boolean.TRUE);
}
else if (event == Behavior.CLEAR_COMMAND_LOG) {
fCommandLog.clearLog();
}
else if (event == Behavior.SET_COMMAND_LOG_SIZE) {
fCommandLog.setLogSize(((Integer)what).intValue());
}
else {
handled = super.textControlEventOccurred(event, what);
}
checkSavedTypingStyle();
return handled;
}
/**
* It's unfortunate that the text is modified and reformatted in
* three different methods. This method is the "common prologue"
* for all text modifications.
*
* This method should be called before modifying and reformatting
* the text. It does three things: stops caret blinking, stops
* background formatting, and returns the Rectangle containing the
* current (soon-to-be obsolete) selection.
*/
private Rectangle prepareForTextEdit() {
fSelection.stopCaretBlinking();
fTextComponent.stopBackgroundFormatting();
return fTextComponent.getBoundingRect(fSelection.getStart(), fSelection.getEnd());
}
private void doClear() {
TextRange selRange = fSelection.getSelectionRange();
if (selRange.start == selRange.limit)
return;
doUndoableTextChange(selRange.start, selRange.limit, null, new TextOffset(selRange.
start), new TextOffset(selRange.start));
}
private void doCut() {
TextRange selRange = fSelection.getSelectionRange();
if (selRange.start == selRange.limit)
return;
fTextComponent.getClipboard().setContents(fText.extract(selRange.start, selRange.limit));
doUndoableTextChange(selRange.start, selRange.limit, null, new TextOffset(selRange.start), new TextOffset(selRange.start));
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
}
private void doPaste() {
TextRange selRange = fSelection.getSelectionRange();
MConstText clipText = fTextComponent.getClipboard().getContents(AttributeMap.EMPTY_ATTRIBUTE_MAP);
if (clipText != null) {
doUndoableTextChange(selRange.start, selRange.limit, clipText,
new TextOffset(selRange.start + clipText.length()),
new TextOffset(selRange.start + clipText.length()));
}
else {
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
}
}
private void doUndoableReplace(TextReplacement replacement) {
doUndoableTextChange(replacement.getStart(),
replacement.getLimit(),
replacement.getText(),
replacement.getSelectionStart(),
replacement.getSelectionLimit());
}
/**
* Only TypingInteractor and TextCommand should call this!
*/
void doReplaceText(int start,
int limit,
MConstText newText,
TextOffset newSelStart,
TextOffset newSelEnd) {
int textLength;
fText.resetDamagedRange();
Rectangle oldSelRect = prepareForTextEdit();
if (newText == null) {
textLength = 0;
fText.remove(start, limit);
}
else {
textLength = newText.length();
fText.replace(start, limit, newText, 0, textLength);
}
fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
reformatAndDrawText(fSelection.getStart(),
fSelection.getEnd(),
oldSelRect);
}
/**
* Only the typing interactor should call this!
*/
void doReplaceSelectedText(char ch, AttributeMap charStyle) {
int start = fSelection.getStart().fOffset;
int limit = fSelection.getEnd().fOffset;
TextOffset newOffset = new TextOffset(start + 1);
doReplaceText(start, limit, ch, charStyle, newOffset, newOffset);
}
private void doReplaceText(int start,
int limit,
char ch,
AttributeMap charStyle,
TextOffset newSelStart,
TextOffset newSelEnd) {
fText.resetDamagedRange();
Rectangle oldSelRect = prepareForTextEdit();
fText.replace(start, limit, ch, charStyle);
fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
reformatAndDrawText(fSelection.getStart(),
fSelection.getEnd(),
oldSelRect);
}
private void doStyleChange(Behavior.EventType event, Object what) {
TextRange selRange = fSelection.getSelectionRange();
boolean character = (event == Behavior.CHARACTER_STYLE_MOD);
if (selRange.start != selRange.limit || !character) {
doUndoableStyleChange(what, character);
}
else {
TypingInteractor interactor =
new TypingInteractor(fTextComponent,
fSelection,
fSavedTypingStyle,
this,
fCommandLog,
fListener);
interactor.addToOwner(fTextComponent);
interactor.textControlEventOccurred(event, what);
}
}
/**
* Only text commands should call this method!
*/
void doModifyStyles(int start,
int limit,
StyleModifier modifier,
boolean character,
TextOffset newSelStart,
TextOffset newSelEnd) {
fText.resetDamagedRange();
Rectangle oldSelRect = prepareForTextEdit();
if (character) {
fText.modifyCharacterStyles(start, limit, modifier);
}
else {
fText.modifyParagraphStyles(start, limit, modifier);
}
fSelection.setSelectionRange(newSelStart, newSelEnd, newSelStart);
reformatAndDrawText(newSelStart,
newSelEnd,
oldSelRect);
}
private void doUndoableStyleChange(Object what,
boolean character) {
TextOffset selStart = fSelection.getStart();
TextOffset selEnd = fSelection.getEnd();
MText oldText = fText.extractWritable(selStart.fOffset, selEnd.fOffset);
StyleChangeCommand command = new StyleChangeCommand(
this, oldText, selStart, selEnd, (StyleModifier) what, character);
fCommandLog.addAndDo(command);
fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
}
private void doUndoableTextChange(int start,
int limit,
MConstText newText,
TextOffset newSelStart,
TextOffset newSelEnd) {
TextChangeCommand command = new TextChangeCommand(this, fText.extractWritable(start, limit),
newText, start, fSelection.getStart(), fSelection.getEnd(),
newSelStart, newSelEnd);
fCommandLog.addAndDo(command);
}
public boolean canUndo() {
boolean canUndo = false;
if (fTypingInteractor != null) {
canUndo = fTypingInteractor.hasPendingCommand();
}
if (!canUndo) {
canUndo = fCommandLog.canUndo();
}
return canUndo;
}
public boolean canRedo() {
return fCommandLog.canRedo();
}
public boolean isModified() {
if (fTypingInteractor != null) {
if (fTypingInteractor.hasPendingCommand()) {
return true;
}
}
return fCommandLog.isModified();
}
public int getCommandLogSize() {
return fCommandLog.getLogSize();
}
public AttributeMap getInsertionPointStyle() {
if (fTypingInteractor != null) {
return fTypingInteractor.getTypingStyle();
}
if (fSavedTypingStyle != null) {
return fSavedTypingStyle;
}
TextRange range = fSelection.getSelectionRange();
return typingStyleAt(fText, range.start, range.limit);
}
public boolean keyPressed(KeyEvent e) {
boolean handled = true;
if (TypingInteractor.handledByTypingInteractor(e)) {
TypingInteractor interactor = new TypingInteractor(fTextComponent,
fSelection,
fSavedTypingStyle,
this,
fCommandLog,
fListener);
interactor.addToOwner(fTextComponent);
interactor.keyPressed(e);
}
else {
handled = super.keyPressed(e);
checkSavedTypingStyle();
}
return handled;
}
public boolean keyTyped(KeyEvent e) {
boolean handled = true;
if (TypingInteractor.handledByTypingInteractor(e)) {
TypingInteractor interactor = new TypingInteractor(fTextComponent,
fSelection,
fSavedTypingStyle,
this,
fCommandLog,
fListener);
interactor.addToOwner(fTextComponent);
interactor.keyTyped(e);
}
else {
handled = super.keyTyped(e);
checkSavedTypingStyle();
}
return handled;
}
public boolean mouseReleased(MouseEvent e) {
boolean result = super.mouseReleased(e);
checkSavedTypingStyle();
return result;
}
private void reformatAndDrawText(TextOffset selStart,
TextOffset selLimit,
Rectangle oldSelRect)
{
if (!fSelection.enabled()) {
selStart = selLimit = null;
}
int reformatStart = fText.damagedRangeStart();
int reformatLength = fText.damagedRangeLimit() - reformatStart;
if (reformatStart != Integer.MAX_VALUE) {
fTextComponent.reformatAndDrawText(reformatStart,
reformatLength,
selStart,
selLimit,
oldSelRect,
fSelection.getHighlightColor());
}
fSelection.scrollToShowSelection();
// sometimes this should send SELECTION_STYLES_CHANGED
fListener.textStateChanged(TextPanelEvent.TEXT_CHANGED);
fSelection.restartCaretBlinking(true);
}
/**
* Only TypingInteractor should call this.
*/
void setTypingInteractor(TypingInteractor interactor) {
fTypingInteractor = interactor;
}
/**
* Only TypingInteractor should call this.
*/
void setSavedTypingStyle(AttributeMap style, int insPt) {
fSavedTypingStyle = style;
fSavedInsPt = insPt;
}
private void checkSavedTypingStyle() {
if (fSavedTypingStyle != null) {
int selStart = fSelection.getStart().fOffset;
int selLimit = fSelection.getEnd().fOffset;
if (selStart != fSavedInsPt || selStart != selLimit) {
fSavedTypingStyle = null;
}
}
}
/**
* Return the style appropriate for typing on the given selection
* range.
*/
public static AttributeMap typingStyleAt(MConstText text, int start, int limit) {
if (start < limit) {
return text.characterStyleAt(start);
}
else if (start > 0) {
return text.characterStyleAt(start - 1);
}
else {
return text.characterStyleAt(0);
}
}
}

View file

@ -0,0 +1,543 @@
/*
* @(#)$RCSfile: TextPanel.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.Scrollbar;
import java.awt.datatransfer.Clipboard;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.richtext.styledtext.StyleModifier;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.StyledText;
/**
* TextPanel is an implementation of MTextPanel in an AWT Panel.
* @see MTextPanel
*/
public final class TextPanel extends Panel implements MTextPanel {
private ATextPanelImpl fImpl;
/**
* Return a TextPanelSettings instance with all settings set
* to the default values. Clients can modify this object;
* modifications will not affect the default values.
* @return a TextPanelSettings instance set to default values
* @see TextPanelSettings
*/
public static TextPanelSettings getDefaultSettings() {
return ATextPanelImpl.getDefaultSettings();
}
/**
* Create a new TextPanel with the default settings.
* @param initialText the text document. If null document text is empty.
* @param clipboard the clipboard to use for cut, copy, and paste
* operations. If null this panel will use a private clipboard.
*/
public TextPanel(MConstText initialText,
Clipboard clipboard) {
this(ATextPanelImpl.fgDefaultSettings, initialText, clipboard);
}
/**
* Create a new TextPanel.
* @param settings the settings for this TextPanel
* @param initialText the text document. If null document text is empty.
* @param clipboard the clipboard to use for cut, copy, and paste
* operations. If null this panel will use a private clipboard.
* @see TextPanelSettings
*/
public TextPanel(TextPanelSettings settings,
MConstText initialText,
Clipboard clipboard) {
Scrollbar horzSb = null;
Scrollbar vertSb = null;
if (settings.getScrollable()) {
setLayout(new ScrollBarLayout());
boolean scrollBarsVisible = settings.getScrollBarsVisible();
if (scrollBarsVisible) {
horzSb = new Scrollbar(Scrollbar.HORIZONTAL);
vertSb = new Scrollbar(Scrollbar.VERTICAL);
add("South", horzSb);
add("East", vertSb);
}
}
else {
setLayout(new BorderLayout());
}
fImpl = new ATextPanelImpl(new RunStrategy(),
settings,
initialText,
clipboard,
this,
horzSb,
vertSb);
final FakeComponent textComponent = fImpl.getTextComponent();
Component textHost = new Component() {
{
textComponent.setHost(this);
}
public void addNotify() {
super.addNotify();
textComponent.addNotify();
}
public void paint(Graphics g) {
textComponent.paint(g);
}
};
add("Center", textHost);
textHost.requestFocus();
}
/**
* Add the given TextPanelListener to the listeners which will
* receive update notifications from this TextPanel.
* @param listener the listener to add
*/
public void addListener(TextPanelListener listener) {
fImpl.addListener(listener);
}
/**
* Remove the given TextPanelListener from the listeners which will
* receive update notifications from this TextPanel.
* @param listener the listener to remove
*/
public void removeListener(TextPanelListener listener) {
fImpl.removeListener(listener);
}
//============
// Text Access
//============
/**
* Set the document to <tt>newText</tt>. This operation
* modifies the text in the TextPanel. It does not modify or adopt
* <tt>newText</tt>. This method sets the selection an insertion point at
* the end of the text.
* @param newText the text which will replace the current text.
*/
public void setText(MConstText newText) {
fImpl.setText(newText);
}
/**
* Append the given text to the end of the document. Equivalent to
* <tt>insert(newText, getTextLength())</tt>.
* @param newText the text to append to the document
*/
public void append(MConstText newText) {
fImpl.append(newText);
}
/**
* Insert the given text into the document at the given position.
* Equivalent to
* <tt>replaceRange(newText, position, position)</tt>.
* @param newText the text to insert into the document.
* @param position the position in the document where the
* text will be inserted
*/
public void insert(MConstText newText, int position) {
fImpl.insert(newText, position);
}
/**
* Replace the given range with <tt>newText</tt>. After this
* operation the selection range is an insertion point at the
* end of the new text.
* @param newText the text with which to replace the range
* @param start the beginning of the range to replace
* @param end the end of the range to replace
*/
public void replaceRange(MConstText newText, int start, int end) {
fImpl.replaceRange(newText, start, end);
}
/**
* Return the length of the text document in the TextPanel.
* @return the length of the text document in the TextPanel
*/
public int getTextLength() {
return fImpl.getTextLength();
}
/**
* Return the text document in the TextPanel.
* @return the text document in the TextPanel.
*/
public MConstText getText() {
return fImpl.getText();
}
//============
// Selection Access
//============
/**
* Return the offset of the start of the selection.
*/
public int getSelectionStart() {
return fImpl.getSelectionStart();
}
/**
* Return the offset of the end of the selection.
*/
public int getSelectionEnd() {
return fImpl.getSelectionEnd();
}
/**
* Set the beginning of the selection range. This is
* equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionStart(int selectionStart) {
fImpl.setSelectionStart(selectionStart);
}
/**
* Set the end of the selection range. This is
* equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.
* @param selectionStart the start of the new selection range
*/
public void setSelectionEnd(int selectionEnd) {
fImpl.setSelectionEnd(selectionEnd);
}
/**
* Set the selection range to an insertion point at the given
* offset. This is equivalent to
* <tt>select(position, position)</tt>.
* @param position the offset of the new insertion point
*/
public void setCaretPosition(int position) {
fImpl.setCaretPosition(position);
}
/**
* Set the selection range to the given range. The range start
* is pinned between 0 and the text length; the range end is pinned
* between the range start and the end of the text. These semantics
* are identical to those of <tt>java.awt.TextComponent</tt>.
* This method has no effect if the text is not selectable.
* @param selectionStart the beginning of the selection range
* @param selectionEnd the end of the selection range
*/
public void select(int selectionStart, int selectionEnd) {
fImpl.select(selectionStart, selectionEnd);
}
/**
* Select all of the text in the document. This method has no effect if
* the text is not selectable.
*/
public void selectAll() {
fImpl.selectAll();
}
//============
// Format Width
//============
/**
* Return the total format width, in pixels. The format width is the
* width to which text is wrapped.
* @return the format width
*/
public int getFormatWidth() {
return fImpl.getFormatWidth();
}
/**
* Return true if the paragraph at the given offset is left-to-right.
* @param offset an offset in the text
* @return true if the paragraph at the given offset is left-to-right
*/
public boolean paragraphIsLeftToRight(int offset) {
return fImpl.paragraphIsLeftToRight(offset);
}
/**
* Return true if there is a change which can be undone.
* @return true if there is a change which can be undone.
*/
public boolean canUndo() {
return fImpl.canUndo();
}
/**
* Return true if there is a change which can be redone.
* @return true if there is a change which can be redone.
*/
public boolean canRedo() {
return fImpl.canRedo();
}
/**
* Return true if the clipboard contains contents which could be
* transfered into the text.
* @return true if the clipboard has text content.
*/
public boolean clipboardNotEmpty() {
return fImpl.clipboardNotEmpty();
}
/**
* Return an AttributeMap of keys with default values. The default
* values are used when displaying text for values which are not
* specified in the text.
* @return an AttributeMap of default key-value pairs
*/
public AttributeMap getDefaultValues() {
return fImpl.getDefaultValues();
}
/**
* This method inspects the character style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have different
* values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see MTextPanel#MULTIPLE_VALUES
*/
public Object getCharacterStyleOverSelection(Object key) {
return fImpl.getCharacterStyleOverSelection(key);
}
/**
* This method inspects the paragraph style runs in the selection
* range (or the typing style at the insertion point). It returns:
* <ul>
* <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
* is the same in all of the style runs in the selection, or</li>
* <li><tt>MULTIPLE_VALUES</tt>, if two or more style runs have
* different values for <tt>key</tt>.</li>
* </ul>
* If a style run does not contain <tt>key</tt>,
* its value is considered to be the default style for <tt>key</tt>,
* as defined by the default values AttributeMap. Note that if
* <tt>key</tt> does not have a default value this method may return
* null.
* This method is useful for configuring style menus.
* @param key the key used to retrieve values for comparison
* @see MTextPanel#MULTIPLE_VALUES
*/
public Object getParagraphStyleOverSelection(Object key) {
return fImpl.getParagraphStyleOverSelection(key);
}
/**
* Remove the selected text from the document and place it
* on the clipboard. This method has no effect if the text
* is not editable, or if no text is selected.
*/
public void cut() {
fImpl.cut();
}
/**
* Place the selected text on the clipboard. This method has
* no effect if no text is selected.
*/
public void copy() {
fImpl.copy();
}
/**
* Replace the currently selected text with the text on the clipboard.
* This method has no effect if the text is not editable, or if no
* text is on the clipboard.
*/
public void paste() {
fImpl.paste();
}
/**
* Remove selected text from the document, without altering the clipboard.
* This method has no effect if the
* text is not editable.
*/
public void clear() {
fImpl.clear();
}
/**
* Undo the most recent text change. This method has no effect if
* there is no change to undo.
*/
public void undo() {
fImpl.undo();
}
/**
* Redo the most recent text change. This method has no effect if
* there is no change to redo.
*/
public void redo() {
fImpl.redo();
}
/**
* Return the number of commands the command log can hold.
* @return the number of commands the command log can hold
*/
public int getCommandLogSize() {
return fImpl.getCommandLogSize();
}
/**
* Set the number of commands the command log can hold. All
* redoable commands are removed when this method is called.
* @param size the number of commands kept in the command log
*/
public void setCommandLogSize(int size) {
fImpl.setCommandLogSize(size);
}
/**
* Remove all commands from the command log.
*/
public void clearCommandLog() {
fImpl.clearCommandLog();
}
/**
* Modify the character styles on the selected characters. If no characters
* are selected, modify the typing style.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyCharacterStyleOnSelection(StyleModifier modifier) {
fImpl.modifyCharacterStyleOnSelection(modifier);
}
/**
* Modify the paragraph styles in paragraphs containing selected characters, or
* the paragraph containing the insertion point.
* @param modifier the StyleModifier with which to modify the styles
*/
public void modifyParagraphStyleOnSelection(StyleModifier modifier) {
fImpl.modifyParagraphStyleOnSelection(modifier);
}
/**
* Return the KeyRemap used to process key events.
* @return the key remap used to process key events
* @see #setKeyRemap
*/
public KeyRemap getKeyRemap() {
return fImpl.getKeyRemap();
}
/**
* Use the given KeyRemap to map key events to characters.
* Only key
* events are affected by the remap; other text entering the
* control (via the clipboard, for example) is not affected
* by the KeyRemap.
* <p>
* Do not pass <tt>null</tt> to this method to leave key
* events unmapped. Instead, use <tt>KeyRemap.getIdentityRemap()</tt>
* @param remap the KeyRemap to use for mapping key events to characters
* @exception java.lang.NullPointerException if parameter is null
* @see KeyRemap
*/
public void setKeyRemap(KeyRemap remap) {
fImpl.setKeyRemap(remap);
}
/**
* Return the modification flag of the current text change.
* @see #setModified
*/
public boolean isModified() {
return fImpl.isModified();
}
/**
* Set the modification flag of the current text change.
*/
public void setModified(boolean modified) {
fImpl.setModified(modified);
}
/**
* This method is for perf-testing only!
*/
void handleKeyEvent(java.awt.event.KeyEvent keyEvent) {
fImpl.handleKeyEvent(keyEvent);
}
}

View file

@ -0,0 +1,157 @@
/*
* @(#)$RCSfile: TextPanelEvent.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.util.EventObject;
/**
* TextPanelEvent is generated by an MTextPanel to notify listeners
* of changes. To receive TextPanelEvents from an MTextPanel, clients
* must implement TextPanelListener and add themselves to the MTextPanel's
* list of listeners.
* <p>
* Some event types are special cases of others. This is intentional - it
* allows notifications to be sent less often in certain common cases. For
* example, a change in the selection range generates a SELECTION_RANGE_CHANGED
* event. This is a very common occurrance, and if many clients listen for this
* event, there may be a significant performance penalty. By
* listening for a more specialized event (such as SELECTION_EMPTY_CHANGED), clients
* can reduce the number of notifications sent.
*
* @see MTextPanel
* @see TextPanelListener
*/
public final class TextPanelEvent extends EventObject {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/**
* The lower bound of TextPanelEvent ID's.
*/
public static final int TEXT_PANEL_FIRST = 11;
/**
* Events of this type indicate a change in the selection range.
* This occurs quite often. Most clients do not need to be
* notified every time the selection range changes.
*/
public static final int SELECTION_RANGE_CHANGED = 11;
/**
* Events of this type are sent when the selection range becomes
* 0-length after not being 0-length, or vice versa. This event
* is a special case of SELECTION_RANGE_CHANGED.
*/
public static final int SELECTION_EMPTY_CHANGED = 12;
/**
* Events of this type indicate that the text in the TextPanel changed.
* This type of event occurs often.
*/
public static final int TEXT_CHANGED = 13;
/**
* Events of this type are sent when the styles in the current
* selection change.
*/
public static final int SELECTION_STYLES_CHANGED = 14;
/**
* Events of this type are sent when the undo/redo state changes.
*/
public static final int UNDO_STATE_CHANGED = 15;
/**
* Events of this type are sent when the clipboard state changes.
*/
public static final int CLIPBOARD_CHANGED = 16;
/**
* Events of this type are sent when
* the wrap width of the text changes.
*/
public static final int FORMAT_WIDTH_CHANGED = 17;
/**
* Events of this type are sent when the key remap changes.
*/
public static final int KEYREMAP_CHANGED = 18;
/**
* The upper bound of TextPanelEvent ID's.
*/
public static final int TEXT_PANEL_LAST = 18;
private int fId;
/**
* Create a new TextPanelEvent.
* @param source the MTextPanel which generated the event
* @param id the ID for this event. Must be within
* [TEXT_PANEL_FIRST, TEXT_PANEL_LAST].
*/
TextPanelEvent(MTextPanel source, int id) {
super(source);
if (id < TEXT_PANEL_FIRST || id > TEXT_PANEL_LAST) {
throw new IllegalArgumentException("id out of range");
}
fId = id;
}
/**
* Return the event ID for this event. Event ID's are
* one of the class constants.
* @return the event ID for this event
*/
public int getID() {
return fId;
}
public String toString() {
String desc = null;
switch(fId) {
case SELECTION_RANGE_CHANGED:
desc = "SELECTION_RANGE_CHANGED";
break;
case SELECTION_EMPTY_CHANGED:
desc = "SELECTION_EMPTY_CHANGED";
break;
case TEXT_CHANGED:
desc = "TEXT_CHANGED";
break;
case SELECTION_STYLES_CHANGED:
desc = "SELECTION_STYLES_CHANGED";
break;
case UNDO_STATE_CHANGED:
desc = "UNDO_STATE_CHANGED";
break;
case CLIPBOARD_CHANGED:
desc = "CLIPBOARD_CHANGED";
break;
case FORMAT_WIDTH_CHANGED:
desc = "FORMAT_WIDTH_CHANGED";
break;
case KEYREMAP_CHANGED:
desc = "KEYREMAP_CHANGED";
break;
}
return "[TextPanelEvent:"+desc+"]";
}
}

View file

@ -0,0 +1,42 @@
/*
* @(#)$RCSfile: TextPanelListener.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
/**
* This interface is implemented by classes which
* receive change notifications from an MTextPanel.
* @see MTextPanel
* @see TextPanelEvent
*/
public interface TextPanelListener {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/**
* Notify listener of an MTextPanel change.
* @param event a TextPanelEvent indicating what happened
*/
public void textEventOccurred(TextPanelEvent event);
/**
* Return true if listener needs to be notified of
* the given event type. This allows a text panel to avoid
* sending events to uninterested parties.
* @param type an event ID from TextPanelEvent
* @return true if this listener needs to be notified of
* events of the given type
*/
public boolean respondsToEventType(int type);
}

View file

@ -0,0 +1,283 @@
/*
* @(#)$RCSfile: TextPanelSettings.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.Color;
import java.io.Serializable;
import java.util.Hashtable;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.textlayout.attributes.TextAttribute;
import com.ibm.richtext.styledtext.StandardTabRuler;
/**
* This class contains settings used when constructing an MTextPanel.
* The settings controled by this class include:
* <ul>
* <li>whether the text in the MTextPanel can be scrolled</li>
* <li>whether scroll bars in the MTextPanel are visible</li>
* <li>whether the text in the MTextPanel can be selected</li>
* <li>whether the text in the MTextPanel can be edited</li>
* <li>whether lines of text wrap to the MTextPanel's width, or
* only end at paragraph separators</li>
* <li>the default values for unspecified styles</li>
* </ul>
* Some settings are dependent on others. Scroll bars are visible
* only if the text is scrollable. Also, text which is not editable
* if it is not selectable.
* <p>
*
* @see MTextPanel
*/
public final class TextPanelSettings implements Cloneable, Serializable {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private static final AttributeMap DEFAULTS;
static {
final Float floatZero = new Float(0.0f);
Hashtable defaults = new Hashtable();
defaults.put(TextAttribute.FAMILY, "Serif");
defaults.put(TextAttribute.WEIGHT, new Float(1.0f));
defaults.put(TextAttribute.POSTURE, floatZero);
defaults.put(TextAttribute.SIZE, new Float(18.0f));
defaults.put(TextAttribute.SUPERSCRIPT, new Integer(0));
defaults.put(TextAttribute.FOREGROUND, Color.black);
defaults.put(TextAttribute.UNDERLINE, new Integer(-1));
defaults.put(TextAttribute.STRIKETHROUGH, Boolean.FALSE);
defaults.put(TextAttribute.EXTRA_LINE_SPACING, floatZero);
defaults.put(TextAttribute.FIRST_LINE_INDENT, floatZero);
defaults.put(TextAttribute.MIN_LINE_SPACING, floatZero);
defaults.put(TextAttribute.LINE_FLUSH, TextAttribute.FLUSH_LEADING);
defaults.put(TextAttribute.LEADING_MARGIN, floatZero);
defaults.put(TextAttribute.TRAILING_MARGIN, floatZero);
defaults.put(TextAttribute.TAB_RULER, new StandardTabRuler());
DEFAULTS = new AttributeMap(defaults);
}
private boolean fScrollable = true;
private boolean fScrollBarsVisible = true;
private boolean fSelectable = true;
private boolean fEditable = true;
private boolean fWraps = true;
private AttributeMap fDefaultValues = DEFAULTS;
/**
* Create a TextPanelSettings instance with all settings
* set to true.
*/
public TextPanelSettings() {
}
/**
* Return a new TextPanelSettings instance with the
* same settings as this.
* @return a new TextPanelSettings instance
*/
public Object clone() {
TextPanelSettings rhs = new TextPanelSettings();
rhs.fScrollable = fScrollable;
rhs.fScrollBarsVisible = fScrollBarsVisible;
rhs.fSelectable = fSelectable;
rhs.fEditable = fEditable;
rhs.fWraps = fWraps;
rhs.fDefaultValues = fDefaultValues;
return rhs;
}
/**
* Return the scrollable setting, which determines whether text
* in an MTextPanel can be scrolled.
* @return the scrollable setting
*/
public boolean getScrollable() {
return fScrollable;
}
/**
* Set the scrollable setting.
* @param scrollable the scrollable setting. If false,
* the scrollBarsVisible setting is also set to false.
*/
public void setScrollable(boolean scrollable) {
fScrollable = scrollable;
fScrollBarsVisible &= scrollable;
}
/**
* Return the scrollBarsVisible setting, which determines whether
* scroll bars in an MTextPanel are visible.
* @return the scrollBarsVisible setting
*/
public boolean getScrollBarsVisible() {
return fScrollBarsVisible;
}
/**
* Set the scrollBarsVisible setting.
* @param vis the scrollBarsVisible setting. If true,
* the scrollable setting is also set to true.
*/
public void setScrollBarsVisible(boolean vis) {
fScrollBarsVisible = vis;
fScrollable |= vis;
}
/**
* Return the selectable setting, which determines whether
* text in an MTextPanel can be selected.
* @return the selectable setting
*/
public boolean getSelectable() {
return fSelectable;
}
/**
* Set the selectable setting.
* @param selectable the selectable setting. If false,
* the editable setting is also set to false.
*/
public void setSelectable(boolean selectable) {
fSelectable = selectable;
fEditable &= selectable;
}
/**
* Return the editable setting, which determines whether
* text in an MTextPanel can be edited.
* @return the editable setting
*/
public boolean getEditable() {
return fEditable;
}
/**
* Set the editable setting.
* @param selectable the selectable setting. If true,
* the selectable setting is also set to true.
*/
public void setEditable(boolean editable) {
fEditable = editable;
fSelectable |= editable;
}
/**
* Return the wraps setting, which determines whether
* lines of text wrap to the length of the MTextPanel,
* or only at paragraph separators.
* @return the wraps setting
*/
public boolean getWraps() {
return fWraps;
}
/**
* Set the wraps setting.
* @param wraps the wraps setting
*/
public void setWraps(boolean wraps) {
fWraps = wraps;
}
/**
* Return the AttributeMap of default values for certain keys.
* When a key in this AttributeMap is not specified, its value
* is taken from this AttributeMap.
* @return the AttributeMap of default values
* @see MTextPanel#getDefaultValues
*/
public AttributeMap getDefaultValues() {
return fDefaultValues;
}
/**
* Add the key-value pairs in the given AttributeMap to the
* default values. If a key does not appear in the given
* AttributeMap, its value in the default value map is
* unchanged.
* @param map an AttributeMap containing new default values
*/
public void addDefaultValues(AttributeMap map) {
fDefaultValues = fDefaultValues.addAttributes(map);
}
/**
* Compare this to another Object. This is equal
* to another Object if the other Object is a
* TextPanelSettings instance with the same
* settings as this one.
* @param rhs the Object to compare to
*/
public boolean equals(Object rhs) {
if (rhs == this) {
return true;
}
if (rhs == null) {
return false;
}
TextPanelSettings other;
try {
other = (TextPanelSettings) rhs;
}
catch(ClassCastException e) {
return false;
}
return other.fScrollable == this.fScrollable &&
other.fScrollBarsVisible == this.fScrollBarsVisible &&
other.fSelectable == this.fSelectable &&
other.fEditable == this.fEditable &&
other.fWraps == this.fWraps &&
other.fDefaultValues.equals(this.fDefaultValues);
}
/**
* Return the hash code for this Object.
* @return the hash code for this Object
*/
public int hashCode() {
int code = fDefaultValues.hashCode();
code = code*2 + (fScrollable? 1:0);
code = code*2 + (fScrollBarsVisible? 1:0);
code = code*2 + (fSelectable? 1:0);
code = code*2 + (fEditable? 1:0);
code = code*2 + (fWraps? 1:0);
return code;
}
}

View file

@ -0,0 +1,47 @@
/*
* @(#)$RCSfile: TextRange.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
/**
* A <TT>TextRange</TT> represents a range of text bounded by a
* start (inclusive), and a limit (exclusive). [start,limit)
*/
final class TextRange
{
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/** the start of the range */
public int start = 0;
/** the end of the range */
public int limit = 0;
/**
* Create a text range from two ints.
* @param start the start of the run
* @param limit the end of the run
*/
public TextRange(int start, int limit)
{
this.start = start;
this.limit = limit;
}
/**
* Create a text range of 0, 0.
*/
public TextRange() {
this.start = this.limit = 0;
}
}

View file

@ -0,0 +1,70 @@
/*
* @(#)$RCSfile: TextReplacement.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.textformat.TextOffset;
/**
* This class is used to pass a REPLACE command to Behaviors.
*/
final class TextReplacement {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private int fStart;
private int fLimit;
private MConstText fText;
private TextOffset fSelStart;
private TextOffset fSelLimit;
TextReplacement(int start,
int limit,
MConstText text,
TextOffset selStart,
TextOffset selLimit) {
fStart = start;
fLimit = limit;
fText = text;
fSelStart = selStart;
fSelLimit = selLimit;
}
int getStart() {
return fStart;
}
int getLimit() {
return fLimit;
}
MConstText getText() {
return fText;
}
TextOffset getSelectionStart() {
return fSelStart;
}
TextOffset getSelectionLimit() {
return fSelLimit;
}
}

View file

@ -0,0 +1,926 @@
/*
* @(#)$RCSfile: TextSelection.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
/*
7/1/97 - caret blinks
7/3/97 - fAnchor is no longer restricted to the start or end of the selection. {jbr}
Also, removed fVisible - it was identical to enabled().
*/
package com.ibm.richtext.textpanel;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Point;
import java.text.BreakIterator;
import java.text.CharacterIterator;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.awt.event.FocusEvent;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.textformat.TextOffset;
import com.ibm.richtext.textformat.MFormatter;
class TextSelection extends Behavior implements Runnable {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
static final Color HIGHLIGHTCOLOR = Color.pink;
private TextComponent fTextComponent;
private MConstText fText;
private TextOffset fStart;
private TextOffset fLimit;
private TextOffset fAnchor;
private TextOffset fUpDownAnchor = null;
private BreakIterator fBoundaries = null;
private Color fHighlightColor = HIGHLIGHTCOLOR;
private PanelEventBroadcaster fListener;
private RunStrategy fRunStrategy;
private boolean fMouseDown = false;
private boolean fHandlingKeyOrCommand = false;
private boolean fCaretShouldBlink;
private boolean fCaretIsVisible;
private int fCaretCount;
// formerly in base class
private boolean fEnabled;
private MouseEvent fPendingMouseEvent = null;
private static final int kCaretInterval = 500;
public void run() {
final Runnable blinkCaret = new Runnable() {
public void run() {
fCaretIsVisible = !fCaretIsVisible;
Graphics g = fTextComponent.getGraphics();
if (g != null) {
//System.out.println("caretIsVisible: " + fCaretIsVisible);
drawSelection(g, fCaretIsVisible);
}
else {
// Not sure what else to do:
fCaretShouldBlink = false;
}
}
};
// blink caret
while (true) {
synchronized(this) {
while (!fCaretShouldBlink) {
try {
wait();
}
catch(InterruptedException e) {
System.out.println("Caught InterruptedException in caret thread.");
}
}
++fCaretCount;
if (fCaretCount % 2 == 0) {
fRunStrategy.doIt(blinkCaret);
}
}
try {
Thread.sleep(kCaretInterval);
}
catch(InterruptedException e) {
}
}
}
public TextSelection(TextComponent textComponent,
PanelEventBroadcaster listener,
RunStrategy runStrategy) {
fTextComponent = textComponent;
fText = textComponent.getText();
fListener = listener;
fRunStrategy = runStrategy;
fStart = new TextOffset();
fLimit = new TextOffset();
fAnchor = new TextOffset();
fMouseDown = false;
fCaretCount = 0;
fCaretIsVisible = true;
fCaretShouldBlink = false;
setEnabled(false);
Thread caretThread = new Thread(this);
caretThread.setDaemon(true);
caretThread.start();
}
boolean enabled() {
return fEnabled;
}
private void setEnabled(boolean enabled) {
fEnabled = enabled;
}
public boolean textControlEventOccurred(Behavior.EventType event, Object what) {
boolean result;
fHandlingKeyOrCommand = true;
if (event == Behavior.SELECT) {
select((TextRange) what);
result = true;
}
else if (event == Behavior.COPY) {
fTextComponent.getClipboard().setContents(fText.extract(fStart.fOffset, fLimit.fOffset));
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
result = true;
}
else {
result = false;
}
fHandlingKeyOrCommand = false;
return result;
}
protected void advanceToNextBoundary(TextOffset offset) {
// If there's no boundaries object, or if position at the end of the
// document, return the offset unchanged
if (fBoundaries == null) {
return;
}
int position = offset.fOffset;
if (position >= fText.length()) {
return;
}
// If position is at a boundary and offset is before position,
// leave it unchanged. Otherwise move to next boundary.
int nextPos = fBoundaries.following(position);
if (fBoundaries.previous() == position &&
offset.fPlacement==offset.BEFORE_OFFSET) {
return;
}
offset.setOffset(nextPos, TextOffset.AFTER_OFFSET);
}
protected void advanceToPreviousBoundary(TextOffset offset) {
advanceToPreviousBoundary(offset, false);
}
private void advanceToPreviousBoundary(TextOffset offset, boolean alwaysMove) {
// if there's no boundaries object, or if we're sitting at the beginning
// of the document, return the offset unchanged
if (fBoundaries == null) {
return;
}
int position = offset.fOffset;
if (position == 0) {
return;
}
// If position is at a boundary, leave it unchanged. Otherwise
// move to previous boundary.
if (position == fText.length()) {
fBoundaries.last();
}
else {
fBoundaries.following(position);
}
int prevPos = fBoundaries.previous();
if (prevPos == position) {
if (!alwaysMove && offset.fPlacement==offset.AFTER_OFFSET) {
return;
}
prevPos = fBoundaries.previous();
}
// and finally update the real offset with this new position we've found
offset.setOffset(prevPos, TextOffset.AFTER_OFFSET);
}
private void doArrowKey(KeyEvent e, int key) {
// when there's a selection range, the left and up arrow keys place an
// insertion point at the beginning of the range, and the right and down
// keys place an insertion point at the end of the range (unless the shift
// key is down, of course)
if (!fStart.equals(fLimit) && !e.isShiftDown()) {
if (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_UP)
setSelRangeAndDraw(fStart, fStart, fStart);
else
setSelRangeAndDraw(fLimit, fLimit, fLimit);
}
else {
if (!fAnchor.equals(fStart))
fAnchor.assign(fLimit);
TextOffset liveEnd = (fStart.equals(fAnchor)) ? fLimit : fStart;
TextOffset newPos = new TextOffset();
// if the control key is down, the left and right arrow keys move by whole
// word in the appropriate direction (we use a line break object so that we're
// not treating spaces and punctuation as words)
if (e.isControlDown() && (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT)) {
fUpDownAnchor = null;
fBoundaries = BreakIterator.getLineInstance();
fBoundaries.setText(fText.createCharacterIterator());
newPos.assign(liveEnd);
if (key == KeyEvent.VK_RIGHT)
advanceToNextBoundary(newPos);
else
advanceToPreviousBoundary(newPos, true);
}
// if we get down to here, this is a plain-vanilla insertion-point move,
// or the shift key is down and we're extending or shortening the selection
else {
// fUpDownAnchor is used to keep track of the horizontal position
// across a run of up or down arrow keys (this prevents accumulated
// error from destroying our horizontal position)
if (key == KeyEvent.VK_LEFT || key == KeyEvent.VK_RIGHT)
fUpDownAnchor = null;
else {
if (fUpDownAnchor == null) {
fUpDownAnchor = new TextOffset(liveEnd);
}
}
short direction = MFormatter.eRight; // just to have a default...
switch (key) {
case KeyEvent.VK_UP: direction = MFormatter.eUp; break;
case KeyEvent.VK_DOWN: direction = MFormatter.eDown; break;
case KeyEvent.VK_LEFT: direction = MFormatter.eLeft; break;
case KeyEvent.VK_RIGHT: direction = MFormatter.eRight; break;
}
// use the formatter to determine the actual effect of the arrow key
fTextComponent.findNewInsertionOffset(newPos, fUpDownAnchor, liveEnd, direction);
}
// if the shift key is down, the selection range is from the anchor point
// the site of the last insertion point or the beginning point of the last
// selection drag operation) to the newly-calculated position; if the
// shift key is down, the newly-calculated position is the insertion point position
if (!e.isShiftDown())
setSelRangeAndDraw(newPos, newPos, newPos);
else {
if (newPos.lessThan(fAnchor))
setSelRangeAndDraw(newPos, fAnchor, fAnchor);
else
setSelRangeAndDraw(fAnchor, newPos, fAnchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
}
private void doEndKey(KeyEvent e) {
// ctrl-end moves the insertsion point to the end of the document,
// ctrl-shift-end extends the selection so that it ends at the end
// of the document
TextOffset activeEnd, anchor;
if (fAnchor.equals(fStart)) {
activeEnd = new TextOffset(fStart);
anchor = new TextOffset(fLimit);
}
else {
activeEnd = new TextOffset(fLimit);
anchor = new TextOffset(fStart);
}
if (e.isControlDown()) {
TextOffset end = new TextOffset(fText.length(), TextOffset.BEFORE_OFFSET);
if (e.isShiftDown())
setSelRangeAndDraw(anchor, end, anchor);
else
setSelRangeAndDraw(end, end, end);
}
// end moves the insertion point to the end of the line containing
// the end of the current selection
// shift-end extends the selection to the end of the line containing
// the end of the current selection
else {
int oldOffset = activeEnd.fOffset;
activeEnd.fOffset = fTextComponent.lineRangeLimit(fTextComponent.lineContaining(activeEnd));
activeEnd.fPlacement = TextOffset.BEFORE_OFFSET;
if (fText.paragraphLimit(oldOffset) == activeEnd.fOffset &&
activeEnd.fOffset != fText.length() && activeEnd.fOffset > oldOffset) {
activeEnd.fOffset--;
activeEnd.fPlacement = TextOffset.AFTER_OFFSET;
}
if (!e.isShiftDown())
setSelRangeAndDraw(activeEnd, activeEnd, activeEnd);
else {
if (activeEnd.lessThan(anchor))
setSelRangeAndDraw(activeEnd, anchor, anchor);
else
setSelRangeAndDraw(anchor, activeEnd, anchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
fUpDownAnchor = null;
}
private void doHomeKey(KeyEvent e) {
// ctrl-home moves the insertion point to the beginning of the document,
// ctrl-shift-home extends the selection so that it begins at the beginning
// of the document
TextOffset activeEnd, anchor;
if (fAnchor.equals(fStart)) {
activeEnd = new TextOffset(fStart);
anchor = new TextOffset(fLimit);
}
else {
activeEnd = new TextOffset(fLimit);
anchor = new TextOffset(fStart);
}
if (e.isControlDown()) {
TextOffset start = new TextOffset(0, TextOffset.AFTER_OFFSET);
if (e.isShiftDown())
setSelRangeAndDraw(start, anchor, anchor);
else
setSelRangeAndDraw(start, start, start);
}
// home moves the insertion point to the beginning of the line containing
// the beginning of the current selection
// shift-home extends the selection to the beginning of the line containing
// the beginning of the current selection
else {
activeEnd.fOffset = fTextComponent.lineRangeLow(fTextComponent.lineContaining(activeEnd));
activeEnd.fPlacement = TextOffset.AFTER_OFFSET;
if (!e.isShiftDown())
setSelRangeAndDraw(activeEnd, activeEnd, activeEnd);
else {
if (activeEnd.lessThan(anchor))
setSelRangeAndDraw(activeEnd, anchor, anchor);
else
setSelRangeAndDraw(anchor, activeEnd, anchor);
}
}
scrollToShowSelectionEnd();
fBoundaries = null;
fUpDownAnchor = null;
}
/** draws or erases the current selection
* Draws or erases the highlight region or insertion caret for the current selection
* range.
* @param g The graphics environment to draw into
* @param visible If true, draw the selection; if false, erase it
*/
protected void drawSelection(Graphics g, boolean visible) {
drawSelectionRange(g, fStart, fLimit, visible);
}
/** draws or erases a selection highlight at the specfied positions
* Draws or erases a selection highlight or insertion caret corresponding to
* the specified selecion range
* @param g The graphics environment to draw into. If null, this method does nothing.
* @param start The beginning of the range to highlight
* @param limit The end of the range to highlight
* @param vsible If true, draw; if false, erase
*/
protected void drawSelectionRange( Graphics g,
TextOffset start,
TextOffset limit,
boolean visible) {
if (g == null) {
return;
}
Rectangle selBounds = fTextComponent.getBoundingRect(start, limit);
selBounds.width = Math.max(1, selBounds.width);
selBounds.height = Math.max(1, selBounds.height);
fTextComponent.drawText(g, selBounds, visible, start, limit, fHighlightColor);
}
protected TextOffset getAnchor() {
return fAnchor;
}
public TextOffset getEnd() {
return fLimit;
}
public Color getHighlightColor() {
return fHighlightColor;
}
public TextOffset getStart() {
return fStart;
}
public TextRange getSelectionRange() {
return new TextRange(fStart.fOffset, fLimit.fOffset);
}
public boolean focusGained(FocusEvent e) {
setEnabled(true);
drawSelection(fTextComponent.getGraphics(), true);
restartCaretBlinking(true);
if (fPendingMouseEvent != null) {
mousePressed(fPendingMouseEvent);
fPendingMouseEvent = null;
}
fListener.textStateChanged(TextPanelEvent.CLIPBOARD_CHANGED);
return true;
}
public boolean focusLost(FocusEvent e) {
stopCaretBlinking();
setEnabled(false);
drawSelection(fTextComponent.getGraphics(), false);
return true;
}
/**
* Return true if the given key event can affect the selection
* range.
*/
public static boolean keyAffectsSelection(KeyEvent e) {
if (e.getID() != e.KEY_PRESSED) {
return false;
}
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_HOME:
case KeyEvent.VK_END:
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
return true;
default:
return false;
}
}
public boolean keyPressed(KeyEvent e) {
fHandlingKeyOrCommand = true;
int key = e.getKeyCode();
boolean result = true;
switch (key) {
case KeyEvent.VK_HOME:
doHomeKey(e);
break;
case KeyEvent.VK_END:
doEndKey(e);
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
doArrowKey(e, key);
break;
default:
fUpDownAnchor = null;
result = false;
break;
}
fHandlingKeyOrCommand = false;
return result;
}
public boolean mousePressed(MouseEvent e) {
if (!enabled()) {
fPendingMouseEvent = e;
fTextComponent.requestFocus();
return false;
}
if (fMouseDown)
throw new Error("fMouseDown is out of sync with mouse in TextSelection.");
fMouseDown = true;
stopCaretBlinking();
int x = e.getX(), y = e.getY();
boolean wasZeroLength = rangeIsZeroLength(fStart, fLimit, fAnchor);
TextOffset current = fTextComponent.pointToTextOffset(null, x, y, null, true);
TextOffset anchorStart = new TextOffset();
TextOffset anchorEnd = new TextOffset();
fUpDownAnchor = null;
// if we're not extending the selection...
if (!e.isShiftDown()) {
// if there are multiple clicks, create the appopriate type of BreakIterator
// object for finding text boundaries (single clicks don't use a BreakIterator
// object)
if (e.getClickCount() == 2)
fBoundaries = BreakIterator.getWordInstance();
else if (e.getClickCount() == 3)
fBoundaries = BreakIterator.getSentenceInstance();
else
fBoundaries = null;
// if we're using a BreakIterator object, use it to find the nearest boundaries
// on either side of the mouse-click position and make them our anchor range
if (fBoundaries != null)
fBoundaries.setText(fText.createCharacterIterator());
anchorStart.assign(current);
advanceToPreviousBoundary(anchorStart);
anchorEnd.assign(current);
advanceToNextBoundary(anchorEnd);
}
// if we _are_ extending the selection, determine our anchor range as follows:
// fAnchor is the start of the anchor range;
// the next boundary (after fAnchor) is the limit of the anchor range.
else {
if (fBoundaries != null)
fBoundaries.setText(fText.createCharacterIterator());
anchorStart.assign(fAnchor);
anchorEnd.assign(anchorStart);
advanceToNextBoundary(anchorEnd);
}
SelectionDragInteractor interactor = new SelectionDragInteractor(this,
fTextComponent,
fRunStrategy,
anchorStart,
anchorEnd,
current,
x,
y,
wasZeroLength);
interactor.addToOwner(fTextComponent);
return true;
}
public boolean mouseReleased(MouseEvent e) {
fPendingMouseEvent = null;
return false;
}
// drag interactor calls this
void mouseReleased(boolean zeroLengthChange) {
fMouseDown = false;
if (zeroLengthChange) {
fListener.textStateChanged(TextPanelEvent.SELECTION_EMPTY_CHANGED);
}
fListener.textStateChanged(TextPanelEvent.SELECTION_RANGE_CHANGED);
fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
// if caret drawing during mouse drags is supressed, draw caret now.
restartCaretBlinking(true);
}
/** draws the selection
* Provided, of course, that the selection is visible, the adorner is enabled,
* and we're calling it to adorn the view it actually belongs to
* @param g The graphics environment to draw into
* @return true if we actually drew
*/
public boolean paint(Graphics g, Rectangle drawRect) {
// don't draw anything unless we're enabled and the selection is visible
if (!enabled())
return false;
fTextComponent.drawText(g, drawRect, true, fStart, fLimit, fHighlightColor);
return true;
}
/** scrolls the view to reveal the live end of the selection
* (i.e., the end that moves if you use the arrow keys with the shift key down)
*/
public void scrollToShowSelection() {
Rectangle selRect = fTextComponent.getBoundingRect(fStart, fLimit);
fTextComponent.scrollToShow(selRect);
}
/** scrolls the view to reveal the live end of the selection
* (i.e., the end that moves if you use the arrow keys with the shift key down)
*/
public void scrollToShowSelectionEnd() {
TextOffset liveEnd;
Point[] points;
Rectangle caret;
if (fAnchor.equals(fStart))
liveEnd = fLimit;
else
liveEnd = fStart;
//points = fTextComponent.textOffsetToPoint(liveEnd);
//caret = new Rectangle(points[0]);
//caret = caret.union(new Rectangle(points[1]));
caret = fTextComponent.getCaretRect(liveEnd);
fTextComponent.scrollToShow(caret);
}
private void select(TextRange range) {
int textLength = fTextComponent.getText().length();
TextOffset start = new TextOffset(range.start);
stopCaretBlinking();
setSelRangeAndDraw(start, new TextOffset(range.limit), start);
restartCaretBlinking(true);
}
public void setHighlightColor(Color newColor) {
fHighlightColor = newColor;
if (enabled())
drawSelection(fTextComponent.getGraphics(), true);
}
static boolean rangeIsZeroLength(TextOffset start, TextOffset limit, TextOffset anchor) {
return start.fOffset == limit.fOffset && anchor.fOffset == limit.fOffset;
}
// sigh... look out for aliasing
public void setSelectionRange(TextOffset newStart, TextOffset newLimit, TextOffset newAnchor) {
boolean zeroLengthChange = rangeIsZeroLength(newStart, newLimit, newAnchor) !=
rangeIsZeroLength(fStart, fLimit, fAnchor);
TextOffset tempNewAnchor;
if (newAnchor == fStart || newAnchor == fLimit) {
tempNewAnchor = new TextOffset(newAnchor); // clone in case of aliasing
}
else {
tempNewAnchor = newAnchor;
}
// DEBUG {jbr}
if (newStart.greaterThan(newLimit))
throw new IllegalArgumentException("Selection limit is before selection start.");
if (newLimit != fStart) {
fStart.assign(newStart);
fLimit.assign(newLimit);
}
else {
fLimit.assign(newLimit);
fStart.assign(newStart);
}
fAnchor.assign(tempNewAnchor);
if (fStart.fOffset == fLimit.fOffset) {
fStart.fPlacement = fAnchor.fPlacement;
fLimit.fPlacement = fAnchor.fPlacement;
}
if (!fMouseDown) {
if (zeroLengthChange) {
fListener.textStateChanged(TextPanelEvent.SELECTION_EMPTY_CHANGED);
}
fListener.textStateChanged(TextPanelEvent.SELECTION_RANGE_CHANGED);
if (fHandlingKeyOrCommand) {
fListener.textStateChanged(TextPanelEvent.SELECTION_STYLES_CHANGED);
}
}
}
private void sortOffsets(TextOffset offsets[]) {
int i, j;
for (i=0; i < offsets.length-1; i++) {
for (j=i+1; j < offsets.length; j++) {
if (offsets[j].lessThan(offsets[i])) {
TextOffset temp = offsets[j];
offsets[j] = offsets[i];
offsets[i] = temp;
}
}
}
// DEBUG {jbr}
for (i=0; i < offsets.length-1; i++)
if (offsets[i].greaterThan(offsets[i+1]))
throw new Error("sortOffsets failed!");
}
private Rectangle getSelectionChangeRect(
TextOffset rangeStart, TextOffset rangeLimit,
TextOffset oldStart, TextOffset oldLimit,
TextOffset newStart, TextOffset newLimit,
boolean drawIfInsPoint) {
if (!rangeStart.equals(rangeLimit))
return fTextComponent.getBoundingRect(rangeStart, rangeLimit);
// here, rangeStart and rangeLimit are equal
if (rangeStart.equals(oldLimit)) {
// range start is OLD insertion point. Redraw if caret is currently visible.
if (fCaretIsVisible)
return fTextComponent.getBoundingRect(rangeStart, rangeStart);
}
else if (rangeStart.equals(newLimit)) {
// range start is NEW insertion point.
if (drawIfInsPoint)
return fTextComponent.getBoundingRect(rangeStart, rangeStart);
}
return null;
}
private static boolean rectanglesOverlapVertically(Rectangle r1, Rectangle r2) {
if (r1 == null || r2 == null) {
return false;
}
return r1.y <= r2.y + r2.height || r2.y <= r1.y + r1.height;
}
// Update to show new selection, redrawing as little as possible
private void updateSelectionDisplay(
TextOffset oldStart, TextOffset oldLimit,
TextOffset newStart, TextOffset newLimit, boolean drawIfInsPoint) {
//System.out.println("newStart:" + newStart + "; newLimit:" + newLimit);
TextOffset off[] = new TextOffset[4];
off[0] = oldStart;
off[1] = oldLimit;
off[2] = newStart;
off[3] = newLimit;
sortOffsets(off);
Rectangle r1 = getSelectionChangeRect(off[0], off[1], oldStart, oldLimit, newStart, newLimit, drawIfInsPoint);
Rectangle r2 = getSelectionChangeRect(off[2], off[3], oldStart, oldLimit, newStart, newLimit, drawIfInsPoint);
boolean drawSelection = drawIfInsPoint || !newStart.equals(newLimit);
if (rectanglesOverlapVertically(r1, r2)) {
fTextComponent.drawText(fTextComponent.getGraphics(), r1.union(r2), drawSelection, newStart, newLimit, fHighlightColor);
}
else {
if (r1 != null)
fTextComponent.drawText(fTextComponent.getGraphics(), r1, drawSelection, newStart, newLimit, fHighlightColor);
if (r2 != null)
fTextComponent.drawText(fTextComponent.getGraphics(), r2, drawSelection, newStart, newLimit, fHighlightColor);
}
}
public void setSelRangeAndDraw(TextOffset newStart, TextOffset newLimit, TextOffset newAnchor) {
// if the old and new selection ranges are the same, don't do anything
if (fStart.equals(newStart) && fLimit.equals(newLimit) && fAnchor.equals(newAnchor))
return;
if (enabled())
stopCaretBlinking();
// update the selection on screen if we're enabled and visible
TextOffset oldStart = new TextOffset(fStart), oldLimit = new TextOffset(fLimit);
setSelectionRange(newStart, newLimit, newAnchor);
if (enabled()) {
// To supress drawing a caret during a mouse drag, pass !fMouseDown instead of true:
updateSelectionDisplay(oldStart, oldLimit, fStart, fLimit, true);
}
if (!fMouseDown && enabled())
restartCaretBlinking(true);
}
public void stopCaretBlinking() {
synchronized(this) {
fCaretShouldBlink = false;
}
}
/**
* Resume blinking the caret, if the selection is an insertion point.
* @param caretIsVisible true if the caret is displayed when this is called.
* This method relies on the client to display (or not display) the caret.
*/
public void restartCaretBlinking(boolean caretIsVisible) {
synchronized(this) {
fCaretShouldBlink = fStart.equals(fLimit);
fCaretCount = 0;
fCaretIsVisible = caretIsVisible;
if (fCaretShouldBlink) {
try {
notify();
}
catch (IllegalMonitorStateException e) {
System.out.println("Caught IllegalMonitorStateException: "+e);
}
}
}
}
public void removeFromOwner() {
stopCaretBlinking();
super.removeFromOwner();
}
}

View file

@ -0,0 +1,106 @@
package com.ibm.richtext.textpanel;
final class ThaiKeyRemap extends KeyRemap {
public char remap(char c) {
switch (c) {
case '`': return '\u005f';
case '~': return '\u0025';
case '1': return '\u0e45';
case '!': return '\u002b';
case '2': return '\u002f';
case '@': return '\u0e51';
case '3': return '\u002d';
case '#': return '\u0e52';
case '4': return '\u0e20';
case '$': return '\u0e53';
case '5': return '\u0e16';
case '%': return '\u0e54';
case '6': return '\u0e38';
case '^': return '\u0e39';
case '7': return '\u0e36';
case '&': return '\u0e3f';
case '8': return '\u0e04';
case '*': return '\u0e55';
case '9': return '\u0e15';
case '(': return '\u0e56';
case '0': return '\u0e08';
case ')': return '\u0e57';
case '-': return '\u0e02';
case '_': return '\u0e58';
case '=': return '\u0e08';
case '+': return '\u0e59';
case 'q': return '\u0e46';
case 'Q': return '\u0e50';
case 'w': return '\u0e44';
case 'W': return '\u0022';
case 'e': return '\u0e33';
case 'E': return '\u0e0e';
case 'r': return '\u0e1e';
case 'R': return '\u0e11';
case 't': return '\u0e30';
case 'T': return '\u0e18';
case 'y': return '\u0e31';
case 'Y': return '\u0e4d';
case 'u': return '\u0e35';
case 'U': return '\u0e4a';
case 'i': return '\u0e23';
case 'I': return '\u0e13';
case 'o': return '\u0e19';
case 'O': return '\u0e2f';
case 'p': return '\u0e22';
case 'P': return '\u0e0d';
case '[': return '\u0e1a';
case '{': return '\u0e10';
case ']': return '\u0e25';
case '}': return '\u002c';
case '\\': return '\u0e03';
case '|': return '\u0e05';
case 'a': return '\u0e1f';
case 'A': return '\u0e24';
case 's': return '\u0e2b';
case 'S': return '\u0e06';
case 'd': return '\u0e01';
case 'D': return '\u0e0f';
case 'f': return '\u0e14';
case 'F': return '\u0e42';
case 'g': return '\u0e40';
case 'G': return '\u0e0c';
case 'h': return '\u0e49';
case 'H': return '\u0e47';
case 'j': return '\u0e48';
case 'J': return '\u0e4b';
case 'k': return '\u0e32';
case 'K': return '\u0e29';
case 'l': return '\u0e2a';
case 'L': return '\u0e28';
case ';': return '\u0e27';
case ':': return '\u0e0b';
case '\'': return '\u0e07';
case '\"': return '\u002e';
case 'z': return '\u0e1c';
case 'Z': return '\u0028';
case 'x': return '\u0e1b';
case 'X': return '\u0029';
case 'c': return '\u0e41';
case 'C': return '\u0e09';
case 'v': return '\u0e2d';
case 'V': return '\u0e2e';
case 'b': return '\u0e34';
case 'B': return '\u0e3a';
case 'n': return '\u0e37';
case 'N': return '\u0e4c';
case 'm': return '\u0e17';
case 'M': return '\u003f';
case ',': return '\u0e21';
case '<': return '\u0e12';
case '.': return '\u0e43';
case '>': return '\u0e2c';
case '/': return '\u0e1d';
case '?': return '\u0e26';
}
return c;
}
}

View file

@ -0,0 +1,90 @@
/*
* @(#)$RCSfile: TransferableText.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.
*/
package com.ibm.richtext.textpanel;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import com.ibm.richtext.styledtext.MConstText;
/**
* This class allows MConstText instances to be the contents
* of a Clipboard. To store an MConstText on the clipboard,
* construct a TransferableText from the MConstText, and make
* the TransferableText the clipboard contents.
*
*/
/*
* Note: this class inherits from StringSelection because of
* a bug in the 1.1.7 system clipboard implementation. The
* system clipboard won't put text on the OS clipboard unless
* the content is a StringSelection.
*/
final class TransferableText extends StringSelection
{
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private MConstText fText;
private static String textToString(MConstText text) {
char[] chars = new char[text.length()];
text.extractChars(0, chars.length, chars, 0);
return new String(chars);
}
/**
* Create a TransferableText for the given text.
* @param text the text to go on the Clipboard. The text is
* adopted by this object.
*/
public TransferableText(MConstText text) {
super(textToString(text));
fText = text;
}
public DataFlavor[] getTransferDataFlavors() {
DataFlavor[] flavors = super.getTransferDataFlavors();
DataFlavor[] result = new DataFlavor[flavors.length+1];
result[0] = MConstText.styledTextFlavor;
System.arraycopy(flavors, 0, result, 1, flavors.length);
return result;
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
if (flavor.equals(MConstText.styledTextFlavor)) {
return true;
}
else {
return super.isDataFlavorSupported(flavor);
}
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.equals(MConstText.styledTextFlavor)) {
return fText;
}
else {
return super.getTransferData(flavor);
}
}
}

View file

@ -0,0 +1,356 @@
/*
* @(#)$RCSfile: TypingInteractor.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:51:23 $
*
* (C) Copyright IBM Corp. 1998-1999. 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.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 == event.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;
}
}

View file

@ -0,0 +1,7 @@
<html>
<body bgcolor="white">
Provides components (for both AWT and Swing)
in which styled text is displayed and edited, and
related classes.
</body>
</html>