4 * License : The MIT License
\r
5 * Copyright(c) 2008 olyutorskii
\r
8 package jp.sourceforge.jindolf;
\r
10 import java.awt.Rectangle;
\r
11 import java.awt.event.InputMethodEvent;
\r
12 import java.awt.event.InputMethodListener;
\r
13 import java.nio.CharBuffer;
\r
14 import java.text.AttributedCharacterIterator;
\r
15 import javax.swing.JTextArea;
\r
16 import javax.swing.text.AbstractDocument;
\r
17 import javax.swing.text.AttributeSet;
\r
18 import javax.swing.text.BadLocationException;
\r
19 import javax.swing.text.Document;
\r
20 import javax.swing.text.DocumentFilter;
\r
21 import javax.swing.text.DocumentFilter.FilterBypass;
\r
22 import javax.swing.text.PlainDocument;
\r
25 * 原稿作成支援用テキストコンポーネント。
\r
27 @SuppressWarnings("serial")
\r
28 public class TextEditor extends JTextArea
\r
29 implements InputMethodListener {
\r
31 private static final int MAX_DOCUMENT = 10 * 1000;
\r
33 private final DocumentFilter documentFilter = new CustomFilter();
\r
35 private boolean onIMEoperation = false;
\r
40 public TextEditor(){
\r
44 setWrapStyleWord(false);
\r
46 Document document = new PlainDocument();
\r
47 setDocument(document);
\r
49 addInputMethodListener(this);
\r
55 * エディタが現在IME操作中か判定する。
\r
56 * @return IME操作中ならtrue
\r
58 public boolean onIMEoperation(){
\r
59 return this.onIMEoperation;
\r
63 * 現在のカーソルが表示されるようスクロールエリアを操作する。
\r
65 public void scrollCaretToVisible(){
\r
66 int caretPosition = getCaretPosition();
\r
68 Rectangle caretBounds;
\r
70 caretBounds = modelToView(caretPosition);
\r
71 }catch(BadLocationException e){
\r
76 scrollRectToVisible(caretBounds);
\r
83 * Document変更をフックしてフィルタを仕込む。
\r
84 * @param document {@inheritDoc}
\r
87 public final void setDocument(Document document){
\r
88 Document oldDocument = getDocument();
\r
89 if(oldDocument instanceof AbstractDocument){
\r
90 AbstractDocument abstractDocument =
\r
91 (AbstractDocument) oldDocument;
\r
92 abstractDocument.setDocumentFilter(null);
\r
95 super.setDocument(document);
\r
97 if(document instanceof AbstractDocument){
\r
98 AbstractDocument abstractDocument = (AbstractDocument) document;
\r
99 abstractDocument.setDocumentFilter(this.documentFilter);
\r
107 * このエディタ中の指定領域が表示されるようスクロールエリアを操作する。
\r
108 * キーボードフォーカスを保持しないときは無視。
\r
109 * @param rect {@inheritDoc}
\r
112 public void scrollRectToVisible(Rectangle rect){
\r
113 if( ! hasFocus() ) return;
\r
114 super.scrollRectToVisible(rect);
\r
120 * @param event {@inheritDoc}
\r
122 public void caretPositionChanged(InputMethodEvent event){
\r
129 * このテキストエディタで現在IMEの変換中か否か判定する処理を含む。
\r
130 * @param event {@inheritDoc}
\r
132 public void inputMethodTextChanged(InputMethodEvent event){
\r
133 int committed = event.getCommittedCharacterCount();
\r
134 AttributedCharacterIterator aci = event.getText();
\r
136 this.onIMEoperation = false;
\r
139 int begin = aci.getBeginIndex();
\r
140 int end = aci.getEndIndex();
\r
141 int span = end - begin;
\r
143 if(committed >= span) this.onIMEoperation = false;
\r
144 else this.onIMEoperation = true;
\r
150 * 入力文字列に制限を加えるDocumentFilter。
\r
151 * \n,\f 以外の制御文字はタブも含め入力禁止。
\r
152 * U+FFFF はjava.textパッケージで特別扱いなのでこれも入力禁止。
\r
153 * ※ ただしIME操作中は制限なし。
\r
155 private class CustomFilter extends DocumentFilter{
\r
160 * @return 入力禁止ならfalse。ただしIME操作中は必ずtrue。
\r
162 private boolean isValid(char ch){
\r
163 if(onIMEoperation()) return true;
\r
165 if(ch == '\n') return true;
\r
166 // if(ch == '\f') return true;
\r
168 if(ch == '\uffff') return false;
\r
169 if(Character.isISOControl(ch)) return false;
\r
171 // if( ! CodeX0208.isValid(ch) ) return false;
\r
172 if(Character.isHighSurrogate(ch)) return false;
\r
173 if(Character.isLowSurrogate(ch) ) return false;
\r
179 * 与えられた文字列から入力禁止文字を除いた文字列に変換する。
\r
180 * @param input 検査対象文字列
\r
183 private String filter(CharSequence input){
\r
184 if(onIMEoperation()) return input.toString();
\r
186 int length = input.length();
\r
187 CharBuffer buf = CharBuffer.allocate(length);
\r
189 for(int pos = 0; pos < length; pos++){
\r
190 char ch = input.charAt(pos);
\r
191 if(ch == '\u2211') ch = '\u03a3'; // Σ変換
\r
192 if(ch == '\u00ac') ch = '\uffe2'; // ¬変換
\r
193 // if(ch == 0x005c ) ch = '\u00a5'; // バックスラッシュから円へ
\r
194 if(isValid(ch)) buf.append(ch);
\r
198 return buf.toString();
\r
204 public CustomFilter(){
\r
211 * @param fb {@inheritDoc}
\r
212 * @param offset {@inheritDoc}
\r
213 * @param text {@inheritDoc}
\r
214 * @param attrs {@inheritDoc}
\r
215 * @throws javax.swing.text.BadLocationException {@inheritDoc}
\r
218 public void insertString(FilterBypass fb,
\r
221 AttributeSet attrs)
\r
222 throws BadLocationException{
\r
223 String filtered = filter(text);
\r
225 if( ! onIMEoperation() ){
\r
226 Document document = fb.getDocument();
\r
227 int docLength = document.getLength();
\r
228 int rest = MAX_DOCUMENT - docLength;
\r
231 }else if(rest < filtered.length()){
\r
232 filtered = filtered.substring(0, rest);
\r
236 fb.insertString(offset, filtered, attrs);
\r
243 * @param fb {@inheritDoc}
\r
244 * @param offset {@inheritDoc}
\r
245 * @param length {@inheritDoc}
\r
246 * @param text {@inheritDoc}
\r
247 * @param attrs {@inheritDoc}
\r
248 * @throws javax.swing.text.BadLocationException {@inheritDoc}
\r
251 public void replace(FilterBypass fb,
\r
255 AttributeSet attrs)
\r
256 throws BadLocationException{
\r
257 String filtered = filter(text);
\r
259 if( ! onIMEoperation() ){
\r
260 Document document = fb.getDocument();
\r
261 int docLength = document.getLength();
\r
262 docLength -= length;
\r
263 int rest = MAX_DOCUMENT - docLength;
\r
266 }else if(rest < filtered.length()){
\r
267 filtered = filtered.substring(0, rest);
\r
271 fb.replace(offset, length, filtered, attrs);
\r
277 // TODO 禁則チェック。20文字を超える長大なブレーク禁止文字列の出現の監視。
\r
278 // TODO 連続したホワイトスペースに対する警告。
\r
279 // TODO 先頭もしくは末尾のホワイトスペース出現に対する警告。
\r