Mon, February 4, 2008
マウスカーソル位置の単語を自動選択してハイライトするJEditorPane
JEditorPaneは、簡易HTMLブラウザとして使うことができます。 JEditorPane上に表示されている文字列をコピーして使いたいのですが、 いちいちマウスで該当する文字列を選択させたくなかったので、 マウスを該当の単語の上に重ねるだけで、単語を選択したように 見せかける機能を GlassPaneの機能を使って実現しました。
コード
TestFrame.java
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
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 JEditorPane editorPane;
private ChunkManager chunkManager;
public TestFrame() {
super();
chunkManager=new ChunkManager();
glassPane=new MyGlassPane();
setGlassPane(glassPane);
glassPane.setVisible(false);
editorPane=new JEditorPane();
editorPane.setEditable(false);
editorPane.setContentType("text/html");
editorPane.addMouseMotionListener(new MouseMotionAdapter(){
//@Override
public void mouseMoved(MouseEvent e) {
Point pt=e.getPoint();
int offset=editorPane.viewToModel(pt);
if(offset>0){
offset--;
}
Chunk myChunk=chunkManager.getChunk(offset);
if(myChunk!=null){
Rectangle rect1=null;
Rectangle rect2=null;
try{
rect1=editorPane.modelToView(myChunk.start);
rect2=editorPane.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);
SwingUtilities.convertPointToScreen(loc, editorPane);
SwingUtilities.convertPointFromScreen(loc, glassPane);
glassPane.setImgLocation(loc);
glassPane.setImgSize(new Dimension(w,h));
glassPane.setVisible(true);
TestFrame.this.repaint();
}
else{
hideGlassPane();
return ;
}
}
else{
hideGlassPane();
return ;
}
}
});
// layout
doMyLayout();
// init
editorPane.setText(SAMPLE_HTML);
try{
Document doc=editorPane.getDocument();
int len=doc.getLength();
String text=doc.getText(0, len);
chunkManager.setText(text);
}
catch(Exception ex){
System.out.println(ex);
}
}
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);
}
private void hideGlassPane(){
glassPane.setVisible(false);
repaint();
}
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;
*/
}
}
MyGlassPane.java
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JComponent;
class MyGlassPane extends JComponent{
private static Composite composite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
private Point loc;
void setImgLocation(Point loc){
this.loc=loc;
}
private Dimension size;
void setImgSize(Dimension size){
this.size=size;
}
//@Override
protected void paintComponent(Graphics g) {
if(loc==null || size==null)
return ;
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
Composite keepComp=g2.getComposite();
Color keepC=g2.getColor();
{
g2.setComposite(composite);
g2.setColor(Color.BLUE);
g2.fill(new Rectangle(loc.x,loc.y,size.width,size.height));
}
g2.setComposite(keepComp);
g2.setColor(keepC);
}
}