Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

JTextPane 現在の行数を強調したい

Java - エディタを作る
http://abebas.sub.jp/java/JavaPrograming/01_Editor/005.html

を参考に、エディターであるJTextPaneの行数を表示させるJTextPaneを作りました。
そして、エディターの現在のキャレットの位置をもとに現在の行あるいは選択されている行の番号を太字に強調したいと思いました。
そこで、以下のサイトも参考にというか上のサイトを含めたコードを組み合わせて自分なりにコードを書いてみました。

JTextPaneで現在の行番号を取得するには|普通に仕事しながら趣味でソフト開発する37歳いろいろメモ
http://ameblo.jp/ogitsu-hama/entry-10066055548.html

AttributeSetインターフェース - スタイル機能付きのメモ帳 - Swing
http://www.javadrive.jp/tutorial/appli_word/index2.html

以下が書いてみたコードです。
@Override
public void caretUpdate(CaretEvent event) {
    //強調を解除するために全体に太文字解除
    SimpleAttributeSet attr = new SimpleAttributeSet();
    attr.addAttribute(StyleConstants.Bold,false);
    doc.setCharacterAttributes(0, line.getText().length()-1, attr, false);
    DefaultStyledDocument doc = (DefaultStyledDocument) line.getDocument();
    int maxLine = doc.getDefaultRootElement().getElementIndex(doc.getLength())+1; //全体の行数を求める
    int tarSpace = (int)Math.log10(maxLine); //桁数を求める
    String[] lines=sorce.getText().substring(0, sorce.getSelectionStart()).split("\n"); //選択の先頭までの文字列を改行コードごとに分ける
    String no=""+lines.length; //その行数を求める
    int curSpace = (int)Math.log10(lines.length+1); //その桁数を求める
    for( int j = curSpace; j < tarSpace; j++ )
    {
        no="0"+no; //船体の行数の桁数と比べて、不足分だけ先頭に0を追加
    }
    //選択の最後においても同様に行う
    String[] lines2=sorce.getText().substring(0, sorce.getSelectionEnd()).split("\n");
    String no2=""+lines.length;
    curSpace = (int)Math.log10(lines.length+1);
    for( int j = curSpace; j < tarSpace; j++ )
    {
        no2="0"+no2;
    }
    //選択の先頭の行数と最後の行数のところだけ太字に
    attr.addAttribute(StyleConstants.Bold,true);
    doc.setCharacterAttributes(line.getText().indexOf(no), line.getText().indexOf(no2)+no2.length(), attr, false);
}

ですが、行数の強調が思うようにうまくいきません。
たとえば行の先頭にキャレットがある時とその隣にキャレットを移動させたときとで強調される行数が違ったり、選択しているわけでもないのに複数行が強調されることもあります。
どのようにすればいいのでしょうか?

5

回答

94998

閲覧

5件の回答

評価

0

元にしたソースが最適かどうか若干疑問がありますが、ま
ずは動くようにしたいのですよね。

選択開始位置、終了位置とは文字と文字の間を示すもので
あることは理解されているでしょうか?またサンプルコー
ドがどのように行番号を求めているかはわかりますか?上
のコードでno,no2がどういう値になれば期待動作をするか
はわかりますね?

そういったことを考えながらまずデバッグしてみてくださ
い。デバッガで変数の値をみるなりデバッグプリントの行
を挿入するなどして変数の値が期待通りか調べるところか
らデバッグは始められるかと思います。

プログラミングすることは「どうコードを記述するか」と
同様、またはそれ以上に「どうデバッグするか」が必須の
技術になります。

デバッグするうちになぜこういう値になるかわからないと
いうことが出てくるかもしれません。色々な知識がついて
くるとAPI Documentをみたりデバッグしたりすることで何
が原因か自分で見つけられるようになってきますが最初は
色々な前提知識が不足しているためにそれが難しいと思い
ます。そういうときに「なぜここの値がこうなるかわから
ない」というような具体的な疑問を質問してみるとよいと
思います。

評価

0

返信が遅くなってすいません。

行の先頭とその次とで協調が異なる問題は、改行コードの次にキャレットがある時には行数を+1することによって対処できました。

それから、setCharacterAttributesって初めと終わりの位置を指定するのだと思っていたら、きちんと読んでみたら初めの位置と長さを指定するんですね。

ただ太字にするだけではもしかしたら勘違いしてる部分もあるかも?ともっとわかりやすく元とは違う色に変えることにしました。
現在のコードは以下の通りです。

@Override
public void caretUpdate(CaretEvent event) {
    DefaultStyledDocument doc = (DefaultStyledDocument) line.getDocument();
    SimpleAttributeSet attr = new SimpleAttributeSet();
    attr.addAttribute(StyleConstants.Bold,false);
    attr.addAttribute(StyleConstants.Foreground,new Color(100,100,255));
    doc.setCharacterAttributes(0, line.getText().length()-1, attr, false);
    int maxLine = doc.getDefaultRootElement().getElementIndex(doc.getLength())+1;
    int tarSpace = (int)Math.log10(maxLine);
    String[] lines=sorce.getText().substring(0, sorce.getSelectionStart()).split("\n");
    String no=""+lines.length;
    if(sorce.getText().substring(0, sorce.getSelectionStart()).endsWith("\n")){
        no=""+(lines.length+1);
    }
    int curSpace = (int)Math.log10(lines.length+1);
    for( int j = curSpace; j < tarSpace; j++ )
    {
        no="0"+no;
    }
    String[] lines2=sorce.getText().substring(0, sorce.getSelectionEnd()).split("\n");
    String no2=""+lines2.length;
    if(sorce.getText().substring(0, sorce.getSelectionEnd()).endsWith("\n")){
        no2=""+(lines.length+1);
    }
    curSpace = (int)Math.log10(lines2.length+1);
    for( int j = curSpace; j < tarSpace; j++ )
    {
        no2="0"+no2;
    }
    System.out.println(lines.length+":"+no+":"+line.getText().indexOf(no)+"to"+lines2.length+":"+no2+":"+(line.getText().indexOf(no2)+no2.length()-1));
    attr.addAttribute(StyleConstants.Bold,true);
    attr.addAttribute(StyleConstants.Foreground,new Color(0,0,255));
    System.out.println(line.getText().substring(line.getText().indexOf(no), line.getText().indexOf(no2)+no2.length()));
    doc.setCharacterAttributes(line.getText().indexOf(no), line.getText().indexOf(no2)+no2.length()-line.getText().indexOf(no)+1, attr, false);
}

1行目にキャレットがある時は001だけ強調されるのですが、2行目以降にキャレットがある時はその行数と外れたところが強調されてしまいます。
たとえば、2行目のときは002から1文字ずれたところ、3行目のときは003から2文字ずれたところでした。
行数表示の文字列を見てみると以下のようになっていました。

001\r\n
002\r\n
003\r\n
004\r\n
以下略

たとえば2行目の先頭にキャレットがある時、以下のように出力されました。

1:002:5to1:002:7
002

上のところで5文字目ってちゃんと002の先頭の0のところですよね?
setCharacterAttributesって第1引数番目の文字から第2引数文字文適応されるんですよね?
ということは2行目の先頭にキャレットがあった場合5文字目から3文字分の002の部分が強調されるはずですよね?
それから、表示させている行数は以下のShift-JISで書いているコード(先頭から一部抜粋)を数えたものになるのですが、6行目の先頭とと7行目の先頭にキャレットがある時とで強調部分が変化しません。
行数が分かりやすいようにとコメントをと付けていますが、実際はコメントは付いていません。
なので、たとえば6行目は空行です。

options//1行目
{//2行目
    static=false;//3行目
    UNICODE_INPUT=true;//41行目
}//5行目
//6行目
PARSER_BEGIN(Programing)//7行目
以下略

あとはどこを間違えているのでしょうか?

評価

0

自分でもよくわからなかったのでAPI documentを見てみま
した。

[JTextPane API document]
http://docs.oracle.com/javase/jp/6/api/javax/swing/J
TextPane.html
[DefaultEditorKit API document]
http://docs.oracle.com/javase/jp/6/api/javax/swing/t
ext/DefaultEditorKit.html

このAPI Documentを見るとJTextPaneとDocumentでは,その
内容を表す文字列での改行の扱いが違っているようです。
前者はプラットフォーム依存で例えばlinuxでは'\n'一文
字、Windowsでは"\r\n"の二文字だったりするみたいで
す。一方後者はプラットフォーム非依存で改行は常
に'\n'一文字です。

StyledDocumentもDocumentの派生ですので、中身のテキス
トは改行を'\n'一文字で表現するものとして設計されてい
ます。

以上をみるかぎり

JTextPane.getText()で返される文字列を調べることで得
た文字位置は、StyledDocumentのメソッドに渡す文字位置
として使えないことになります。

とりあえずは 


JStyledDocument lineDoc = 
(JStyledDocument)line.getDocument();
JStyledDocument sourceDoc = 
(JStyledDocument)source.getDocument();

String lineText = lineDoc.getText(0, 
lineDoc.getLength());
String sourceText = sourceDoc.getText(0, 
sourceDoc.getLength());

のようにして求めたテキストを用いて位置を計算するよう
に処理を書き直してみてはどうでしょうか。

評価

0

上のコメントはloginしそこねたため匿名になっちゃいまし
た。失礼致しました。

評価

0

JTextPaneとDocumentとで改行コードが異なってるんですね。
setCharacterAttributesを以下のように変えてみたらうまくいきました。

doc.setCharacterAttributes(line.getText().replaceAll("\r\n", "\n").indexOf(no), line.getText().replaceAll("\r\n", "\n").indexOf(no2)+no2.length()-line.getText().replaceAll("\r\n", "\n").indexOf(no)+1, attr, false);

でも、空行の次の行にキャレットを置いたときにはうまくいきませんでした。
改行コードが連続していることが原因のようで、以下のように分割する文字列の改行コードの前に半角スペースを付けてみたらすべてにおいてうまくいきました。

String[] lines=sorce.getText().substring(0, sorce.getSelectionStart()).replaceAll("\n", " \n").split("\n");

KSwordOfHasteさん、匿名さん、ありがとうございました。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。