Tue, February 5, 2008
マウスカーソル位置の単語を自動選択してハイライトするJEditorPane その2(改良版)(swing)

前回の改良版。
前回は、ハイライトの描画をGlassPaneを使って行っていたが、 それを直接 JEditorPane に描画する方法に改良。
この方法では、 GlassPaneのことを気にしなくていい(GlassPaneはJFrameかJAppletにしかない) のが利点です。
GlassPaneは何か別の目的...たとえば、DragAndDropの描画に使っている場合
など、自由にコントロールできないときがあります。
そんな場合でも、JEditorPane 内でハイライト描画も完結して記述していれば、
問題ありません。
コード
TestFrame.java
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestFrame extends JFrame{
private static final String SAMPLE_HTML=("<html><h1>Hello world!</h1><p>There was a farmer had a dog and BINGO was his name, oh.</p>");
//private MyGlassPane glassPane;
private DraggableBrowser editorPane;
// private ChunkManager chunkManager;
public TestFrame() {
super();
//chunkManager=new ChunkManager();
editorPane=new DraggableBrowser();
// layout
doMyLayout();
// init
editorPane.setText(SAMPLE_HTML);
}
private void doMyLayout(){
JPanel panel=new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
panel.add(new JLabel("Test"),BorderLayout.NORTH);
panel.add(editorPane,BorderLayout.CENTER);
Container pane=getContentPane();
pane.add(panel,BorderLayout.CENTER);
}
public static void main(String[] args) {
TestFrame frame = new TestFrame();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setSize(320, 360);
frame.setVisible(true);
}
}
Chunk.java
class Chunk {
public int start;
public int end;
public String word;
public Chunk(String word,int start,int end){
this.word=word;
this.start=start;
this.end=end;
}
public boolean contains(int pos){
if(start<=pos && pos<end){
return true;
}
return false;
}
}
ChunkManager.java
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Locale;
class ChunkManager{
private ArrayList chunkList=new ArrayList();
//private ArrayList<Chunk> chunkList=new ArrayList<Chunk>();
public void setText(String text){
chunkList.clear();
//BreakIterator
BreakIterator boundary = BreakIterator.getWordInstance(Locale.ENGLISH);
boundary.setText(text);
int start = boundary.first();
for (int end = boundary.next(); end != BreakIterator.DONE;start = end, end = boundary.next()) {
String word = text.substring(start, end);
chunkList.add( new Chunk(word,start,end) );
}
}
public Chunk getChunk(int offset){
Chunk myChunk=null;
for(int i=0; i<chunkList.size(); i++){
Chunk c=(Chunk)chunkList.get(i);
if(c.contains(offset)){
myChunk=c;
break;
}
}
return myChunk;
/*
for(Chunk c : chunkList){
if(c.contains(offset)){
myChunk=c;
}
}
return myChunk;
*/
}
}
DraggableBrowser.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JApplet;
import javax.swing.JEditorPane;
import javax.swing.text.Document;
//import mikan.dnd.GhostGlassPane;
public class DraggableBrowser extends JEditorPane{
private ChunkManager chunkManager;
public DraggableBrowser(){
super();
setEditable(false);
setContentType("text/html");
chunkManager=new ChunkManager();
addMouseMotionListener(new MouseMotionAdapter(){
//@Override
public void mouseMoved(MouseEvent e) {
Point pt=e.getPoint();
int offset=viewToModel(pt);
/*
int endOffset=getDocument().getEndPosition().getOffset();
if(offset<endOffset-1){
offset=offset+1;
}
*/
Chunk myChunk=chunkManager.getChunk(offset);
if(myChunk!=null){
Rectangle rect1=null;
Rectangle rect2=null;
try{
rect1=modelToView(myChunk.start);
rect2=modelToView(myChunk.end);
}
catch(Exception ex){
hideGlassPane();
return ;
}
int w=rect2.x-rect1.x;
int h=rect1.height;
if(w>0 && h>0){
Point loc=new Point(rect1.x,rect1.y);
setSelectedWordRectangle(new Rectangle(loc.x,loc.y,w,h));
DraggableBrowser.this.repaint();
}
else{
hideGlassPane();
return ;
}
}
else{
hideGlassPane();
return ;
}
}
});
}
private Rectangle selectedWordRect;
private void setSelectedWordRectangle(Rectangle rect){
this.selectedWordRect=rect;
}
private Rectangle getSelectedWordRectangle(){
return selectedWordRect;
}
private void hideGlassPane(){
selectedWordRect=null;
repaint();
/*
Component c=applet.getGlassPane();
if(c!=null)
c.setVisible(false);
*/
}
//@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect=getSelectedWordRectangle();
if(rect==null){
return ;
}
Graphics2D g2=(Graphics2D)g;
Color keepC=g2.getColor();
{
g2.setColor(Color.RED);
g2.draw(rect);
}
g2.setColor(keepC);
}
//@Override
public void setText(String t) {
super.setText(t);
try{
Document doc=getDocument();
int len=doc.getLength();
String text=doc.getText(0, len);
chunkManager.setText(text);
}
catch(Exception ex){
System.out.println(ex);
}
}
}
※ドラッグ機能を追加するつもりで書いていたので、このようなクラス名になっていますが、 実際にはドラッグ可能ではありません。 単にマウスオーバーしている単語がハイライト(囲みを描画)するだけです。