Java Drawing DrawTop

Language

JP  US  UK

 

テキストボックス

 H. Jyounishi, Tokyo Japan
 

Frame (Index), No frame                 version:0.3(latest)  

要旨:テキストボックス内にテキストを入力し編集するテキストエディタ。テキストには、フォントファミリィ, フォントサイズ、Plain∙ Bold∙ Italicの属性、アンダーライン、上付き/下付きの属性、色属性が設定できる。

このページで説明するクラス: TextBox, TextUndoSetup
Unit Test =>DrawTest: Input Method Frameworkによる テキストエディター


1. 概要
1.1 Input Method Framework, Java 2D Graphics

SwingのJTextComponentの拡張クラスを使えば、簡単にテキストエディターを実現できるのだが、 任意倍率でテキストを含めた拡大/縮小表示ができそうにない(?)。 このためInput Method framework, Java 2D Graphics を使って簡単なテキストエディターを作成した。


∙ Input Method Framework

英字の場合は、KeyListenerインターフェイスのkeyTypedメソッドで直接入力文字を受け取るが、 日本語の場合はInputMethodListenerのinputMethodTextChangedメソッドでカナ∙ 漢字変換された文字列を受け取ることができる。


∙ Java 2D Graphics

属性つき文字列(java.text.AttributedString)の処理、表示に使う。 特に、属性つき文字列を指定幅の領域に描画するために、複数行に分割するLineBreakMeasurerが重要である。
テキストボックスの、かなりの部分を占めかつ面倒な処理は、属性つき文字列の処理と、複数行に対する処理である。


1.2 本アプリケーションのテキスト入力方法
(1)操作手順
∙ テキスト入力をするためには、一度TextBox内部をクリックする。 テキスト入力が可能なTextBoxにはテキスト領域にグリーンの枠が表示される。

灰色の枠が表示されているTextBoxには入力できない。一度クリックして枠をグリーンに変える。 なおメニューボタンを押してTextBoxを作成したときは、グリーンの枠が表示されている。


∙ カナ∙ 漢字変換中の文字列には、点線の下線(TextAttribute.INPUT_METHOD_HIGHLIGHT)が表示される。

∙ 表示されているテキストの途中にテキストを追加するときは、その位置をクリックすると、 キャレット(入力位置を示す赤い縦線)が表示される。

∙ テキストを選択するには、TextBox内でマウスをドラッグする。選択されたテキストはピンク色のハイライトが表示される。
=>操作説明書  テキスト入力∙ 編集
 
(2)Text入力の全体構成

英字入力の時は、ListenerPanelのkeyTypedメソッドでアルファベットを受け取り、TextBoxのkeyTypedメソッドへ送る。カナ∙ 漢字入力モードの時は、ListenerPanelのinputMethodTextChangedメソッドでKeyEventを受け取り、 TextBoxのinputMethodTextChangedメソッドへ送る。


TextBoxのkeyTypedメソッド: insertTextでCommittedTextContainerに格納。

TextBoxのinputMethodTextChangedメソッド:

確定テキストはCommittedTextContainerに格納し、未確定テキストは確定テキストと合成して画面に表示する処理を行う。ここで確定テキストはEnterを押してカナ∙


: 未確定テキスト(Composed text)と確定テキスト(Committed text)

未確定テキスト - カナ・漢字変換中のテキスト。テキストボックスに点線の下線をつけてテ表示される。
確定テキスト- カナ・漢字変換で確定したテキストまたはカナ・漢字変換を使わずに入力したテキスト

 

図1 テキスト入力手順


1.3 入力テキストおよびフォントメニューへのフォントスタイル設定
項目 説明
入力テキストへのフォントスタイル設定 対象とするテキスト

(1)キーボードでタイプ入力したテキスト

処理メソッド:TextBox.keyTyped -> TextBox.insertText -> FontStyle.setTo


(2)カナ漢字変換で入力したテキスト

処理メソッド:TextBox.inputMethodTextChanged -> TextBox.insertText -> FontStyle.setTo


(3)テキストボックスからのコピー&ペースト

このアプリケーション(DrawTop)のテキストボックスからコピーして、 選択されているテキストボックスにペーストしたテキスト
処理メソッド:Edit.paste -> Edit.pasteAttributedString -> TextBox.insertText -> FontStyle.setTo


(4)他のアプリケーションからのピー&ペースト

他のアプリケーション(MicroSoft wordなど)からコピーして選択されているテキストボックスにペーストしたテキスト
処理メソッド:Edit.paste -> Edit.pasteString -> TextBox.insertText -> FontStyle.setTo

フォントメニューへのフォントスタイル設定 フォントメニュー

フォントメニューへのフォントスタイル設定のタイミング

(1)文字列クリックによるスタイルのフォントメニューへの設定

テキストボックス内部をクリックしたとき、テキストカーソル(キャレット)の手前の文字のスタイルを フォントメニューに設定する


(a)テキストボックスが編集不能とき

処理メソッド:SelectionLS.mouseClicked -> SelectionLS.execSelection -> FontStyle.setFontStyleToMenu


(b)テキストボックスが編集可能(editable)なとき

処理メソッド:TextBox.mouseClicked -> TextBox.setTextSelection -> FontStyle.setFontStyleToMenu
SelectionLSのマウスリスナーは一時的に無効にされている。


(2)文字列選択による共通スタイルの設定

編集可能(editable)なテキストボックス内部をマウスでドラッグして文字列を選択したとき、 選択した文字列に共通するスタイルがあれば、それをフォントメニューに設定する
処理メソッド:TextBox.mouseDragged -> TextBox.setTextSelection -> FontStyle.setFontStyleToMenu


(3)Up, Dn(Down), Left, Rightキーによるカーソル移動

編集可能(editable)なテキストボックスのテキストカーソル(キャレット)を、 Up, Dn(Down), Left, Rightキーで動かした場合の処理。
処理メソッド:TextBox.keyPressed -> FontStyle.setFontStyleToMenu

:

テキストボックスが編集可能内側に緑色の枠が表示されているテキストボックスがアクティブ。 他のテキストボックスはアクティブではない。同時にひとつのテキストボックスだけがアクティブになり得る。 アクティブにするためにはテキストボックス内部をクリックする。




2. TextBoxクラス 戻る=>page top
public class TextBox implements MouseListener, MouseMotionListener

フィールド 説明
shapeContainer ShapeContainer shapeContainer
このTextBoxを持つ図形コンテナオブジェクト。
textArea public Rectangle2D textArea=null
TextBoxのテキスト領域。
∙ textAreaの設定

ShapeContainer.addTextBoxから ShapeElement.createTextArea抽象(abstract)メソッドを呼んで textAreaを取得し、addTextBoxメソッドでTextBoxに設定する。

∙ textAreaを移動/リサイズする

ShapeElement.moveResizeメソッドから TextBox.resizeTextAreaメソッドを呼んでtextAreaの移動/リサイズを行う。

textBoxInsets public Insets textBoxInsets= new Insets(2, 2, 2, 2)
TextBox内部の余白部分
textAlign public int textAlign
テキストの配置 右寄せ(0)/中央寄せ(1)/左寄せ(2)
lineSpace public int lineSpace
テキストの行間隔
currentFontStyle private FontStyle currentFontStyle
keyTypedメソッド、inputMethodTextChangedメソッドにより入力した文字列には、 このフィールド変数のFontStyleオブジェクトを FontStyle.setToメソッドで設定する。
committedTextContainer private CommittedTextContainer committedTextContainer
確定テキスト(committed text)を格納する CommittedTextContainerオブジェクトを設定する。
composedTextIterator private AttributedCharacterIterator composedTextIterator
composedTextは未確定テキストのこと。日本語入力の場合はカナ∙ 漢字変換が確定する前の変換候補の文字列。 未確定テキストをcomposedTextIteratorに設定する。
lineBreaker private LineBreaker lineBreaker
LineBreakerオブジェクトを設定する。 LineBreakerは属性つきテキストを、指定幅の行に割り付けるクラスである。
validTextLayout private boolean validTextLayout
LineBreakerオブジェクトを作り直す必要があるとき(テキスト挿入∙ 削除, FontStyle変更時など)にfalseを設定する。 falseが設定されているとLineBreakerを呼んでテキストを行に再割付する。
caretPosition public CaretPosition caretPosition
CaretPositionオブジェクトを設定する。 現在のキャレット位置を表す。
初期値はnew CaretPosition(0 , 0, 0)。
(注)キャレットの位置
テキストをLineBreakerで複数行に割り付けた際の2次元的な位置(行番号と行での位置で表す)と、 テキストの先頭から数えた1次元的な位置の両方が使われ、これらは同期が取られる必要がある。 同期を取るためには、LineBreakerが作成するTextLayoutデータが必要である。
=> CaretPosition
selStart private CaretPosition selStart
選択文字列の先頭のキャレット位置。初期値はnew CaretPosition(-1 , -1)。
selEnd private CaretPosition selEnd
選択文字列の後のキャレット位置を。初期値はnew CaretPosition(-1 , -1)。
: selStartは画面で見て左上、selEndは右か右下方向になっていること。
これが逆転していると、選択文字列のハイライト表示、選択文字列への属性設定など、多くの部分でエラーになる。 このクラスのsetTextSelectionメソッドで selStartとselEndの順番をチェックする。
dragStart private CaretPosition dragStart
ドラッグ開始キャレット位置。mousePressedで設定。初期値はnew CaretPosition(-1 , -1)。
dragEnd private CaretPosition dragEnd
ドラッグ中または終了時のキャレット位置。mouseDragged, mouseReleasedで設定。
初期値はnew CaretPosition(-1 , -1)。
mousePositionInfo MousePositionInfo mousePositionInfo
現在のマウス位置がTEXTBOX_BOUNDARY(マウス位置コード)ならば、 MousePositionInfoオブジェクトをこのフィールドに設定する。 このフィールドは mousePressed メソッドで参照される。
=> 操作説明書 テキストボックス境界の移動
modified boolean modified
テキストボックス境界の移動が行われたら、このフィールドをtrueにする。 このフィールドはmousePressedmouseReleaed メソッドで参照される。
oldPoint Point2D oldPoint
マウス位置をセット。
newPoint Point2D newPoint
マウス位置をセット。
connectionUtil ConnectionUtil connectionUtil
テキストボックス境界の移動が行われるときにConnectionUtil オブジェクトをセット。
UndoSetupDeleteText private TextUndoSetup.DeleteText UndoSetupDeleteText
TextUndoSetup.DeleteTextオブジェクト
UndoSetupInsertText private TextUndoSetup.InsertText UndoSetupInsertText
TextUndoSetup.InsertTextオブジェクト
KEY_BOARD final public static int KEY_BOARD=0
テキスト挿入/削除方法の識別子。TextBox.keyTypedメソッドで指定する。
INPUTMETHOD
_TEXTCHANGED
final public static int INPUTMETHOD_TEXTCHANGED=1
テキスト挿入/削除方法の識別子。TextBox.inputMethodTextChangedメソッドで指定する。
COMMAND final public static int COMMAND=3
テキスト挿入/削除方法の識別子。Edit.copyString, Edit.pasteString, Edit.pasteAttributedString, Edit.deleteメソッドなどで指定する。
UNDO_REDO final public static int UNDO_REDO=4
テキスト挿入/削除方法の識別子。TextBox.insertText, TextBox.deleteText, UndoableDrawEdit.ChangeText, UndoableDrawEdit.InsertText, UndoableDrawEdit.DeleteTextで指定する。
: 当たり前であるが、undo、redoのテキスト挿入/削除操作に対してはundo設定は行わない。

メソッド 説明
コンストラクタ public TextBox ()
ListenerPanel.requestFocus()を呼ぶ。
getShapeContainer public ShapeContainer getShapeContainer()
フィールド変数shapeContainerを返す。
setShapeContainer public void setShapeContainer(ShapeContainer container)
フィールド変数shapeContainerに引数を設定。
activateMouseListener public void activateMouseListener(boolean activate)
引数:
activate - trueならばこのテキストボックスを編集可能にする。falseならば編集不能にする。
処理:
ElementeContainer.makeTextBoxEditableメソッドから呼ばれる。
∙ activate=trueのとき

ListenerPanelにこのTextBoxをマウスリスナー、マウスモーションリスナーとして設定する。 listenerPanel.addMouseListener(this);
listenerPanel.addMouseMotionListener(this);

∙ activate=false

ListenerPanelからこのTextBoxを削除する。
listenerPanel.removeMouseListener(this);
listenerPanel.removeMouseMotionListener(this);

isEditable public boolean isEditable
このテキストボックスが編集可能ならばtrueを返す。
getBoundingBox public Rectangle2D getBoundingBox()
このTextBoxを囲むBoundingBoxを返す。
getTextArea public Rectangle2D getTextArea()
フィールド変数textAreaを返す。
setTextArea public void setTextArea(Rectangle2D textArea)
フィールド変数textAreaに引数を設定する。
getMarginlessTextArea public Rectangle2D getMarginlessTextArea()
textAreaからtextBoxInsetsを差し引いた領域を返す。
getTextLayoutArea public Rectangle2D getTextLayoutArea()
LineBreaker.getBoundsメソッドでテキストを折り返し表示する行の矩形領域を配列で受け取り、その矩形領域をすべて覆う矩形領域を返す。
getCommittedTextContainer public CommittedTextContainer getCommittedTextContainer()
フィールド変数committedTextContainerを返す。
isCommittedText public boolean isCommittedText()
committedTextがnullでなければtrueを返す。
getTextLocation public Rectangle getTextLocation()
ListenerPanel.getTextLocationから呼ばれる。 LineBreaker.getCaretRectangleの戻り値を返す。
resizeTextArea public void resizeTextArea(Rectangle2D oldBox, Rectangle2D newBox)
setTextAreaメソッドでテキスト領域をリサイズする。
このTextBoxを所有するShapeContainerの図形要素のリサイズメソッド (ShapeElement.moveResize)から呼ばれる。
getTextBoxInsets public Insets getTextBoxInsets()
フィールド変数textBoxInsetsを返す。
setTextBoxInsets public void setTextBoxInsets(Insets insets)
textBoxInsetsに引数を設定する。
getTextLineSpace public int getTextLineSpace()
フィールド変数lineSpaceを返す。
setTextLineSpace public void setTextLineSpace(double lineSpace)
フィールド変数lineSpaceに引数を設定する。
getTextAlign public int getTextAlign()
フィールド変数textAlignを返す。
setTextAlign public void setTextAlign(int textAlign)
フィールド変数textAlignに引数を設定。
setTextBoxLayout public void setTextBoxLayout(Insets textBoxInsets, int textAlign, double lineSpace)
フィールド変数textBoxInsets、lineSpace、textAlignに引数の値を設定。
setFontStyle public void setFontStyle(FontStyle newFontStyle)
ExecCommandから呼ばれる。
∙ 選択テキストがあるとき

選択テキストに引数のnewFontStyleを設定する。 設定メソッドはFontStyle.setTo

∙ 選択テキストがないとき

CommittedTextContainerが保持する確定テキスト全体にnewFontStyleを設定する。

getFontColors public Color[] getFontColors()
このメソッドはShapeContainer.getColorsメソッドから呼ばれる。 このオブジェクトのCommittedTextContainer オブジェクトで使われている全ての色情報を配列で返す。
getSerializableTextBox public SerializableTextBox getSerializableTextBox()
このTextBoxからフィールド変数だけを取り出したSerializableTextBoxを返す。
このメソッドはUndoableDrawEdit.ChangeTextBoxで使われる。
setSerializableTextBox public void setSerializableTextBox(SerializableTextBox data)
SerializableTextBoxのデータをこのTextBoxに設定。
このメソッドはUndoableDrawEdit.ChangeTextBoxで使われる。
toString public String toString()
このTextBoxの文字列表現を返す。
setValidTextLayout private void setValidTextLayout(boolean valid) フィールド変数validTextLayoutに引数を設定する。
TextBox内のテキストが変更された場合valid=falseを指定する。 valid=falseが設定されている場合、drawText でLineBreakerオブジェクトが作り直され、テキストが再配置される。
keyTyped public void keyTyped(char keyChar)
ListenerPanelにインプリメントされているkeyTypedから呼び出される。
FontStyleの設定されたキャラクターを、このクラスのinsertTextメソッドで、 CommittedTextContainerに挿入し、undo設定を行う。
引数:
keyChar - 入力文字.
処理:
このメソッドは ListenerPanel.keyTyped メソッドから呼ばれる。
deleteSelectedTextメソッドを呼んで、選択されたテキストがあれば削除。

this.deleteSelectedText(TextBox.KEY_BOARD);

∙ 引数 keyCharをAttributedStringに変換。

AttributedString attribChar=new AttributedString(String.valueOf(keyChar));

∙ 上記attribChar をCommittedTextContainerに挿入する。

this.insertText (TextBox.KEY_BOARD, insertionIndex, attribChar.getIterator());

∙ this.setValidTextLayout(false);

=> 図1 テキスト入力手順
keyPressed public void keyTyped(int keyCode)
引数:
keyCode - 入力文字コード.
処理:
このメソッドは ListenerPanel.keyPressedから呼ばれ、 テキストカーソルを左右、上下に動かす。
∙ KeyCodeがKeyEvent.VK_LEFTまたはKeyEvent.VK_RIGHTの場合

CaretPosition.columnOffsetメソッドを呼んで、 キャレットの位置を左右にひとつ動かす。

∙ KeyCodeがKeyEvent.VK_UPまたはKeyEvent.VK_DOWNの場合

CaretPosition.lineOffsetメソッドを呼んで、 キャレットの位置を上下にひとつ動かす。

=> ListenerPanel java.awt.event.KeyEventで受け取る値
inputMethodTextChanged public void inputMethodTextChanged(InputMethodEvent event)
InputMethod Frame WorkのInputMethodListenerの定義するメソッド。
このメソッドはListenePaneのinputMethodTextChangedから InputMethodEvent eventを受け取る。
∙ eventの確定テキスト

このクラスのinsertTextメソッドでCommittedTextContainerに挿入する。

∙ eventの未確定テキスト

TextAttribute.INPUT_METHOD_HIGHLIGHT属性を付けてフィールド変数 composedTextIteratorに設定。 さらにフィールド変数のcurrentFontStyleを設定する。

composedTextIteratorはgetDisplayTextで 確定テキストに挿入され画面に表示される。 このとき選択テキストがあれば、このクラスのdeleteSelectedTextで削除する (選択テキストへの上書き)。

=> 図1 テキスト入力手順
printInputMethodStatus private void printInputMethodStatus(InputMethodEvent event)
inputMethodTextChangedの状態をプリントする。デバッグ用。
insertText public void insertText(int method, int position, AttributedCharacterIterator attribStr)
CommittedTextContainer.insertTextでCommittedTextContainerにテキストを挿入する。
引数:
method - TextBox.KEY_BOARD, TextBox.INPUTMETHOD_TEXTCHANGED、TextBox.PASTE, TextBox.UNDO_REDOのどれかを指定。
position - 挿入位置
attribStr - 挿入テキスト
処理:

(a)method がTextBox.KEY_BOARDまたは TextBox.INPUTMETHOD_TEXTCHANGEDの場合
∙ committedTextを取り出し、挿入位置(position)の手前の文字のFontStyle(fontStyle)を取得する。

AttributedCharacterIterator iterator=committedText.getIterator();
FontStyle fontStyleAt=FontStyle.getFontStyleAt(iterator, position);
if(!FontStyle.isDefaultFontStyle(fontStyleAt)) fontStyle=fontStyleAt;

∙ 挿入テキストattribStrにfontStyleを設定する。

insertStr = fontStyle.setTo(attribStr, 0, attribStr.getEndIndex());


(b)method がTextBox.COMMANDの場合

挿入テキストattribStrにFontStyleが設定されているか否か調べる。
boolean isFontStyle=FontStyle.isFontStyle(attribStr, attribStr.getBeginIndex(), attribStr.getEndIndex()) 。
設定されていなければ、(a)と同様にcommittedTextの挿入位置(position)の手前の文字のFontStyleをattribStrに設定する。


(c)後処理 - (a)(b)共通
∙ committedTextContainerに挿入文字列(insertStr)を挿入

this.committedTextContainer.insertText(insertionIndex, insertStr)

∙ LineBreakerで文字列を複数行に配置しなおす。
∙ キャレット(文字カーソル)位置を更新。

キャレットのテキスト位置を挿入テキストの後に設定し、 CaretPosition.updateCaretPosition メソッドで更新。

∙ methodがTextBox.UNDO_REDOでなければundo設定を行う TextUndoSetup.InsertText)。

: テキストの挿入は、TextBox内部、外部を問わず、全てこのメソッドを通るようにし、ここでundoの設定を行う。
deleteSelectedText private void deleteSelectedText(int method)
フィールド変数selStart, selEndから選択テキストの範囲を取得しdeleteTextメソッドで削除。
deleteText public void deleteText(int method, int start, int end)
CommittedTextContainerの確定テキストのstart, end間を削除する。
引数:
method - TextBox.KEY_BOARD, TextBox.INPUTMETHOD_TEXTCHANGED、TextBox.PASTE, TextBox.UNDO_REDOのどれかを指定。
start, end - 削除文字列の開始、終了位置
処理:
CommittedTextContainer.deleteTextでstart, end間のテキストを削除。
∙ キャレット(文字カーソル)位置を更新。

キャレットのテキスト位置をstartに設定し、 LineBreaker.updateCaretPositionメソッドで更新。

∙ methodがTextBox.UNDO_REDOでなければ、undoの設定を行う (TextUndoSetup.deleteText)。
: テキストの削除は、TextBox内部、外部を問わず、全てこのメソッドを通るようにし、 ここでundoの設定を行う
deleteTextByDelCommandOrBSkey public int deleteTextByDelCommandOrBSkey(int method)
Edit.deleteだけから呼ばれる。 deleteコマンドでテキストを削除する場合の処理メソッド。
テキストを選択しておいてdeleteコマンドまたはBack spaceキーで一括削除する場合と、 Back spaceキーでカーソルの前の文字を一文字づつ削除する場合がある。
削除する文字列のstart, endを決め、上のdeleteTextメソッドを呼んで削除。
戻り値は削除した文字数を返す。
replaceText public void replaceText(int method, AttributedString attribStr)
UndoableEdit.ChangeTextのundo、redoメソッドから呼ばれる。undo、redoでCommittedTextContainerの確定テキストを全部リプレースする。CommittedTextContainer setCommittedTextで処理。
getComposedText public AttributedCharacterIterator getComposedText()
未確定テキスト(フィールド変数committedTextIterator)を返す。
getDisplayText public AttributedCharacterIterator getDisplayText()
画面に表示するテキストを取得する。
未確定テキスト(ComposedText)が存在しない場合、CommittedTextContainerの確定テキストだけを表示テキストとする。
未確定テキスト(ComposedText)が存在する場合、 CommittedTextContainer.getDisplayTextメソッドに 未確定テキストを引数で渡し、確定テキストと未確定テキストを合成したテキストを作成し表示テキストとする。
: 確定テキストと未確定テキストの合成はCommittedTextContainer.getDisplayTextから呼ばれる AttributedStringUtil.createCompositeTextメソッドで行う。
drawTextBox public void drawTextBox(Graphics g)
TextBox描画のメインメソッド。 TextBoxを所有する図形(ShapeContainer)はShapeContainer.drawShapeメソッドで描画するので、 このメソッドではTextBox内部を描画する。
∙ テキスト領域:drawTextArea

テキストを囲む枠を表示する。 TextBoxがactive(テキスト入力∙ 編集可能)の場合は緑色の枠、そうでないときは灰色の枠を描画する。
∙ テキスト:drawText

LineBreakerで複数行に分割。LineBreakerから複数行に対応するTextLayoutの配列を取得して、 TextLayout.drawメソッドでテキストを描画する。
∙ キャレット(文字カーソル):drawCaret

TextBoxがactiveの場合に描画する。
∙ キャレット(文字カーソル):drawTextSelection

TextBoxがactiveの場合で、テキストが選択されているときに選択文字列の背景にハイライトを描画。

drawTextArea public void drawTextArea(Graphics g)
テキスト領域を示す枠を表示する。枠の色は、このTextBoxがactive(テキスト入力∙ 編集可能)なときは Color.GREEN、 そうでない時はColor.LIGHT_GRAY。
枠は、getShrinkedRectangleを使いTextBox領域の少し内側に描画する。
drawText public void drawText(Graphics g)
TextBox内のテキストを表示する。

validTextLayoutがfalseの場合:

LineBreakerを呼んでテキストを枠で折り返し複数行に割り付ける。 CaretPosition.updateCaretPositionメソッドで selStart, selEnd, caretPositionフィールドを更新する。

: LineBreakerによる行の再割り付け

選択テキストのフォントスタイルを変更する場合など、例えば太字、イタリックに変えると文字の幅が変わる。このためselStart, selEndのフィールド変数値lineIndex, columnIndexは無効になるが、textIndex値は有効である。そこでCaretPosition.updateCaretPositionメソッドでselStart, selEndのtextIndex値からlineIndex, columnIndex値を再計算してselStart, selEndに設定しなおす。 これでdrawTextSelectionでテキスト選択範囲が正しく表示される。


∙ LineBreaker結果はjava.awt.font.TextLayoutの配列に格納される。

TextLayoutの配列は LineBreaker.getTextLayoutsで取得。 各配列は1行に対応しており、属性付き文字列(AttributedCharacterIterator形式)が格納されている。 また各TextLayoutの表示位置は、 LineBreaker.getTextLayoutPositionsで取得する。


∙ 各配列をjava.awt.font.TextLayout.drawメソッドで表示する。

textLayouts[i].draw(g2, (float)positions[i].getX(), (float)positions[i].getY())

各TextLayoutの表示位置は、TextLayoutのベースラインの左端。
表示位置: => LineBreaker.createMultipleLines


DrawParameters.DRAW_TEXTLAYOUTがtrueの場合:

java.awt.font.TextLayout boundsを描画する。


通常表示Draw TextLayout bounds

drawCaret public void drawCaret(Graphics g, int offset)
キャレットを表示する。引数offsetは現在のキャレット位置からのオフセット。
未確定テキスト(カナ∙ 漢字変換候補テキスト)が入力された場合、未確定テキストの後ろにキャレットを表示したい。
一方キャレットの位置(フィールド変数caretPosition)は、 確定テキストの挿入、削除で更新するようにしているため、未確定テキストが入力された時点では、 未確定テキストの手前に位置している。
引数offsetには未確定テキストの長さを指定し、未確定テキストの後ろにキャレットを表示するためのものである。
∙ オフセットしたキャレット図形を取得。

LineBreaker.getCaretRectangleメソッドに キャレットの現在位置とオフセット量を渡し、キャレットを表す矩形を取得する。

∙ g2.draw(caretRectangle)でキャレットを描画。 
:

LineBreaker.getCaretRectangleについて

キャレット図形は、java.awt.font.TextLayoutのメソッドで取得する。

textLayout.getCaretShapes(position);

TextBoxにまだテキストが入力されていない段階では、TextLayoutオブジェクトは作成されていない。 この時getCaretRectangleメソッドでは、仮のTextLayoutを作成してキャレット図形を取得するなどの特殊処理が必要になる。 またenterキーを押して改行した場合にも、 その行に対応するTextLayoutオブジェクトが作成されていないので同様な処理が必要になる。結構面倒である。
drawTextSelection public void drawTextSelection(Graphics g)
選択テキストにハイライトを表示する。テキストの選択は複数行にまたがるので、 ハイライトの表示も複数行にまたがる。 ハイライト図形(java.awt.Shape)はjava.awt.font.TextLayoutクラスのgetLogicalHighlightShapeメソッドで取得する。
LineBreaker.getTextLayoutsLineBreaker.getTextLayoutPositionsメソッドにより、 複数行に対応するTextLayout配列と各TextLayoutの位置を格納したPoint2D配列(positions)を取得。
各TextLayoutからjava.awt.font.TextLayout.getLogicalHighlightShapeメソッドでハイライトを取得し描画する。
∙ テキスト選択範囲の開始行(startLine)、終了行(endLine)を CaretPosition.getLineIndexメソッドで取得

またstartLineとendLineでの開始、終了文字インデックス(startColumnIndexとendColumnIndex)を CaretPosition.getColumnIndexで取得。

∙ TextLayoutオブジェクトの配列を LineBreaker.getTextLayoutsメソッドで取得。

選択範囲を表すハイライト形状をつぎのように取得
Shape highlight=textLayouts[i].getLogicalHighlightShape(start, end);

i=startLineならば, start=startColumnIndex
i=endLineならば, end=endColumnIndex
startLine<i<endLineならば、start=0, end= (textLayouts[i]の文字数)
∙ ハイライトの描画

if(highlight!=null) g2.fill(highlight);

∙ 選択テキストの描画

textLayouts[i].draw(g2, (float)positions[i].getX(), (float)positions[i].getY());
テキストはdrawTextメソッドで描画されるが、ハイライトで上書きされるので、ここで再度描画する。

getTextIndex public int getTextIndex() キャレットの位置をcommittedTextのテキスト位置で返す。Editオブジェクト DialogOfShapeFormatオブジェクトから呼ばれる。
hasSelectedText public boolean hasSelectedText()
選択されたテキストがあるか時trueを返す。
getSelectedTextStart public int getSelectedTextStart()
選択されたテキストの先頭位置を返す。DialogOfShapeFormatオブジェクトから呼ばれる。
getSelectedTextEnd public int getSelectedTextEnd()
選択されたテキストの終了位置を返す。
getSelectedText public AttributedString getSelectedText()
選択されたテキストの取り出し。Editオブジェクトから呼ばれる。
mousePressed public void mousePressed(MouseEvent e)
次の2つの操作が mousePressed, mouseDragged, mouseReleased メソッドで実行される。第1の操作はテキストボックスの境界移動 (操作説明書 テキストボックス境界の移動). 第2の操作はマウスドラッグによるテキストの選択 (操作説明書 テキスト選択).

(1)テキストボックス境界の移動
∙ 現在の MousePositionInfo オブジェクトを MousePositionLS.getAllMousePositionInfo メソッドで取得。

MousePositionInfoの マウス位置コード が TEXTBOX_BOUNDARY ならば、その MousePositionInfo オブジェクトを mousePositionInfo フィールドに設定する。

mousePositionInfo がnullでなければ次の処理を実行する。

(a)modifiedフィールドにtrueをセット
(b)ConnectionUtil オブジェクトを作成し、 ConnectionUtil.setTargetsメソッドを呼ぶ。
(c)ContainerManager.undoSetupStart メソッドを呼んでundoの設定を行う。


(2)テキスト選択
∙ 未確定テキストがフィールド変数composedTextIteratorに セットされているとき

カナ∙ 漢字変換中なので警告メッセージのダイアログを表示してリターン。

∙ このクラスのgetCaretPositionAtMouseメソッドにマウス位置(X,Y)を渡し、 キャレット位置を取得。

ドラッグ開始点をフィールド変数dragStartに設定。

mouseDragged public void mouseDragged(MouseEvent e)

(1)テキストボックス境界の移動
MousePositionInfo.getInformation メソッドでテキストボックスの境界のエッジを取得し、textAreaの 矩形のデータを設定しなおすことでそのエッジを動かす。
∙ 境界のエッジを大きく動かしたときは ShapeElement.moveResize メソッドで図形自体をリサイズする。 もしコネクターが接続しているときは、 ConnectionUtil.resizeConnectorsメ ソッドでコネクターの接続を維持する。

(2)テキスト選択
∙ 未確定テキストがフィールド変数composedTextIteratorにセットされているときmousePressedと同様リターン。
∙ このクラスのgetCaretPositionAtMouseメソッドにマウス位置(X,Y)を渡し、 キャレット位置を取得。

ドラッグ現在点としてフィールド変数endCaretPositionに設定。

∙ このクラスのsetTextSelectionメソッドを呼んで選択区間を設定する。

フィールド変数dragStartdragEndの 間をテキスト選択区間として設定する。
テキスト選択区間はフィールド変数selStart, selEndで表す。

: setTextSelectionではマウスを左下から右上に動かした場合、 選択範囲が逆転するので、それを防止する処理を行う。
: テキスト選択区間はdrawSelectionメソッドによりハイライトを表示する。
mouseReleased public void mouseReleased(MouseEvent e)

(1)テキストボックス境界の移動

もし modifiedフィールドがtrueならば、 変更コード (UndoConstants.CONTAINER) を ShapeContainerに設定し、 ContainerManager.undoSetupEndメソッドを呼ぶ。


(2)テキスト選択
マウスボタンが離される時に、テキスト選択範囲が確定する。
∙ このクラスのgetCaretPositionAtMouseメソッドにマウス位置(X,Y)を渡し、 キャレット位置を取得。

ドラッグ現在点としてフィールド変数endCaretPositionに設定。

∙ このクラスのsetTextSelectionメソッドを呼ぶ

フィールド変数dragStartdragEndの間をテキスト選択区間として設定する。

mouseClicked public void mouseClicked(MouseEvent e)
クリックした位置を現在のキャレット位置とし、フィールド変数caretPositionに設定する。
∙ このクラスのgetCaretPositionAtMouseメソッドにマウス位置(X,Y)を渡し、キャレット位置を取得。
∙ このクラスのsetTextSelectionメソッドを呼んでキャレット位置を設定する。
∙ もし未確定テキストがあればリターン。
mouseEntered public void mouseEntered(MouseEvent e) :何もしない。
mouseExited public void mouseExited(MouseEvent e) :何もしない。
mouseMoved public void mouseMoved(MouseEvent e) :何もしない。
getCaretPositionAtMouse public CaretPosition getCaretPositionAtMouse(double X, double Y)
(X,Y)点が複数行のTextLayout配列の何処に属するかをCaretPositionで返す。
∙ LineBreaker.getTextLayoutsgetTextLayoutPositionsgetBoundsを呼ぶ

複数行に対応するTextLayout配列(textLayouts)、各TextLayoutの位置を格納したPoint2D配列(positions)、 各TextLayoutを囲むRectangle2D配列(bounds)を取得。

∙ (X,Y)点を含むRectangle2D配列(bounds)の要素を見つけ、その配列indexをlineIndexに設定。
∙ textLayouts[lineIndex]から、java.awt.font.TextHitInfo(java.awt.font.TextHitInfo)を取得。

これからtextLayouts[lineIndex]内での文字列位置positionInTextLayoutを得る。
int positionInTextLayout=hitInfo.getInsertionIndex();

∙ lineIndexとpositionInTextLayoutからCaretPositionを取得し返す。

CaretPosition caret P=this.lineBreaker.getCaretPosition (lineIndex, positionInTextLayout);

setTextSelection public void setTextSelection(CaretPosition caretPos1, CaretPosition caretPos2, int ctrl)
引数:
caretPos1 - テキスト選択のドラッグ開始位置。
caretPos2 - テキスト選択のドラッグ終了位置。マウスをクリックしたときは、この引数にnullを指定する。
ctrl - Shift/Ctrlを押しながらマウスをクリックしたとき ctrl は 1/2。
処理:
caretPos1, caretPos2はドラッグ開始点と現在点のCaretPosition。
∙ マウスがクリックされ (caretPos2=null)、 ctrl=0ならば、caretPos1 を現在のマウス位置 (caretPosition)にセットする。

this.caretPosition = CaretPosition.getCaretPosition(mouseP1.getLineIndex(), mouseP1.getColumnIndex(), true, this.lineBreaker, "setTextSelection"); 

∙ マウスがクリックされ (caretPos2=null)、 ctrl>0ならば、 caretPositionと caretPos1.の間のテキストを選択する。
∙ caretPos1, caretPos2ともにnullでは無いとき、次のように処理する。

通常、caretPos1, caretPos2をthis.selStartとthis.selEndに設定する。 しかし文字列を選択する時に、マウスを文字列の後方から前方に動かすと、 this.selStartとthis.selEndの順序は逆転するので、 caretPos1, caretPos2の順序をチェックして逆転を防止する。
またcaretPos1, caretPos2が同じ位置を指しているときは、 テキストの選択ではなくてmouseClickedの場合と同様、キャレットの位置を指定したと見なす。 いずれの場合もFontStyle.setFontStyleToMenuメソッドで、 選択またはクリックされたテキストの属性をメニューに反映させる。

displayMousePosition private void displayMousePosition(String mouseAction, double X, doubleY)
画面の下のパネルの2行目にTextBoxでのマウスの位置情報を表示する。キャレット位置、 テキスト選択start,endの表示。
contain private int contain(int iX, int iY)
(iX,iY)点がテキスト領域に入っているときtrueを返す。
resetCaret public void resetCaret()
現在のキャレット位置を解除する。 フィールド変数caretPositionに(new CaretPosition(0, 0, 0, true))オブジェクトを設定。
resetSelection public void resetSelection()
テキスト選択を解除する。フィールド変数selStart, selEndにCaretPositionオブジェクトを設定する。
clone public Object clone()
このオブジェクトのクローンを返す。


3. TextUndoSetup 戻る=>page top

テキストの挿入/削除のundo設定を行う。 1文字ごとにundo設定を行うのは不経済なので、連続的に入力された文字列や、 Back spaceキーで連続的に削除された文字列には一括してundo設定を行う。
このクラスはTextBox.insertTextTextBox.deleteTextメソッドだけから呼ばれる。
=> コマンドに対するUndo設定一覧表


3.1 InsertTextサブクラス 戻る=>page top

一度作成した、UndoableDrawEdit.InsertText オブジェクトの文字列に挿入文字列が追加できるときは追加する。 追加できないときは新しいUndoableDrawEdit.InsertTextオブジェクトを作成する。
挿入文字列が追加できるか否かは、基本的にはつぎのように挿入位置によって決まる。
∙ 挿入文字列を追加して良いのは次の3つの条件が満たされる場合。
①UndoableDrawEdit.InsertTextオブジェクトがnullでない。
②UndoableDrawEdit.InsertTextオブジェクトに登録されているShapeContainerが一致する。
③挿入文字列の開始位置(注)が、UndoableDrawEdit.InsertTextオブジェクトに登録されている挿入済み文字列の終了位置に一致する。
:開始、終了位置は、TextBox内での位置。

フィールド 説明
undoDrawManager private UndoDrawManager undoDrawManager
UndoDrawManagerオブジェクトを設定。
InsertText UndoableDrawEdit.InsertText InsertText
UndoableDrawEdit.InsertTextオブジェクトを設定する。

メソッド 説明
setInsertedText public void setInsertedText(ShapeContainer shapeContainer, AttributedString insString, int start, boolean delimit)
引数:
shapeContainer - TextBoxをもつShapeContainerオブジェクト。
insString - 挿入文字列.
start - 挿入文字列の位置.
delmit - insStringを挿入した後で区切り、 現在のUndoableDrawEdit.InsertTextオブジェクトの 最後の文字列とする。
処理:
挿入文字列を設定する。 この挿入文字列が、UndoableDrawEdit.InsertTextオブジェクトに 登録されている挿入文字列に追加できる時は追加する。
テキストの追加は、属性付き文字列なので簡単ではない。 AttributedStringUtil.createCompositeText メソッドを使う。
initialSet private void initialSet(ShapeContainer shapeContainer, AttributedString insString, int start)
指定された文字列がUndoableDrawEdit.InsertTextオブジェクトに追加できない場合の処理を行う。
新しいUndoableDrawEdit.InsertTextオブジェクトを作成し、 UndoDrawManager.addEditメソッドで挿入文字列を登録する。
新しく作成したオブジェクトはフィールド変数InsertTextに設定。 次回setInsertedTextが呼ばれたときに挿入文字列が追加できる場合、このオブジェクトに追加する。


3.2 DeleteTextサブクラス 戻る=>page top

一度作成した、UndoableDrawEdit.DeleteTextオブジェクトに 削除文字列が追加できるときは追加する。 追加できないときは新しいUndoableDrawEdit.DeleteTextオブジェクトを作成する。 方法はInsertTextサブクラスと同じ。

フィールド 説明
undoDrawManager private UndoDrawManager undoDrawManager
UndoDrawManagerオブジェクトを設定。
DeleteText UndoableDrawEdit.DeleteText DeleteText
UndoableDrawEdit.DeleteTextオブジェクトを設定する。

メソッド 説明
setDeletedText public void setDeletedText(ShapeContainer shapeContainer, AttributedString delString, int start, int end)
削除文字列を設定する。 この削除文字列が、UndoableDrawEdit.DeleteTextオブジェクトに 登録されている削除文字列に追加できる時は追加する。
テキストの追加は、 AttributedStringUtil.createCompositeText メソッドを使う。
initialSet private void initialSet(ShapeContainer shapeContainer, AttributedString delString, int start)
指定された文字列がUndoableDrawEdit.DeleteTextオブジェクトに追加できない場合の処理。
新しいUndoableDrawEdit.DeleteTextオブジェクトを作成し、 UndoDrawManager.addEditメソッドで削除文字列を登録する。
新しく作成したオブジェクトはフィールド変数DeleteTextに設定。 次回setDeletedTextが呼ばれたときに削除文字列が追加できる場合、このオブジェクトに追加する。




Copyright (c) 2009-2013
All other trademarks are property of their respective owners.