📄 simplenote.java
字号:
package net.sf.component.simplenote;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.util.StringUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.PaintObjectEvent;
import org.eclipse.swt.custom.PaintObjectListener;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.GlyphMetrics;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
/**
* 在styledtext的基础构建支持简单图文组合,支持文件附件。
* 进一步可以利用styledtext的格式功能
* 存储内容尽量与html规范兼容
* @author levin
* @since 20070311
*/
public class SimpleNote extends StyledText {
private UndoManager undoManager = null; //undo管理器
protected SortedMap<Integer,ElementProxy> elements; //文件中图片或文件列表,为图片位置和图片的map
protected String basePath; //文件的基准路径
private boolean firstTime; //是否首次设置内容,如果是,则不校正offset
public SimpleNote(Composite parent, int style) {
super(parent, style);
//增加undo支持
undoManager = new UndoManager(255);
undoManager.connect(this);
//增加快捷键
setKeyBinding('A'|SWT.MOD1, ST.SELECT_ALL);
setKeyBinding('Z'|SWT.CTRL, UndoManager.UNDO);
setKeyBinding('Y'|SWT.CTRL, UndoManager.REDO);
elements=new TreeMap<Integer,ElementProxy>();
//跟踪offset的娈化
addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent e) {
if((((SimpleNote)e.widget)).isFirstTime()){
(((SimpleNote)e.widget)).setFirstTime(false);
return ;
}
//校正ElementProxy的位置
int start = e.start;
int replaceCharCount = e.end - e.start;
int newCharCount = e.text.length();
Set<Integer> set=new HashSet<Integer>(elements.keySet());
for (int offset : set) {
if (start <= offset && offset < start + replaceCharCount) {
ElementProxy proxy=(ElementProxy) elements.remove(offset);
proxy.dispose();
if(proxy.src != null){
//直接删除文件,不能后悔
new File(basePath,proxy.src).delete();
}
}else if (offset >= start) {
//换key
ElementProxy proxy=(ElementProxy) elements.remove(offset);
elements.put(offset +newCharCount - replaceCharCount, proxy);
}
}
}
});
addPaintObjectListener(new PaintObjectListener() {
public void paintObject(PaintObjectEvent event) {
int start = event.style.start;
//显示图片或文件
if(elements.get(start) != null){
elements.get(start).draw(basePath, event, SimpleNote.this);
}
}
});
}
/**
* 重载以支持粘接图片及图片文件
*@Override
*/
public void paste() {
try {
Field field=StyledText.class.getDeclaredField("clipboard");
field.setAccessible(true);
Clipboard clipboard=(Clipboard) field.get(this);
//直接粘贴一个图片源
ImageData imageData=(ImageData) clipboard.getContents(ImageDataTransfer.getInstance());
if(imageData != null){
ImageProxy imageProxy = new ImageProxy(imageData);
addElement(imageProxy);
return;
}
//粘贴一个图片或文件
String[] fileData = (String[])clipboard.getContents(SimpleFileTransfer.getInstance());
if(fileData !=null){
for(String file:fileData){
if(maybeImageFile(file)){
ImageProxy imageProxy = new ImageProxy(new ImageData(file));
addElement(imageProxy);
}else{
FileProxy fileProxy=new FileProxy(new File(file));
addElement(fileProxy);
}
}
return;
}
} catch (Exception e) {
e.printStackTrace();
}
super.paste();
}
public String getText() {
StringBuffer text = new StringBuffer(super.getText());
int delta=0;
for(int offset:elements.keySet()){
ElementProxy elementProxy = elements.get(offset);
//还没有保存
if(elementProxy.src == null){
elementProxy.save(getBasePath());
}
text.replace(offset+delta, offset+delta+1, elementProxy.getHtml());
delta += elementProxy.getHtml().length() -1;
}
return text.toString();
}
public void setText(String html) {
//清除状态
for(ElementProxy element:elements.values())
element.dispose();
elements.clear();
int delta =0;
//考虑到同一行可能会同时有图片、文件的组合,采用非贪婪做法
Pattern p=Pattern.compile("<a.*?>.*?</a>|<img.*?></img>");
Matcher m=p.matcher(html);
while(m.find()){
String group = m.group();
int offset = m.start();
ElementProxy elementProxy=ElementProxy.getInstance(group);
elements.put(offset - delta, elementProxy);
delta += group.length() -1;
}
//替换为占位符
html=html.replaceAll("<a.*?>.*?</a>", new FileProxy().getPlaceholder());
html=html.replaceAll("<img.*?></img>", new ImageProxy().getPlaceholder());
//设置为内部格式
firstTime=true;
super.setText(html);
//还需要强制一下,以显示图片
for(int offset:elements.keySet()){
showElement(elements.get(offset),offset);
}
}
//追加图文
public void appendHtml(String html){
if(html.indexOf("<a") != -1 || html.indexOf("<img") != -1 ){
String text = this.getText();
this.setText(text+html);
this.redraw();
}else{
super.append(html);//普通字串
}
scrollToLast();
}
//滚动到最后
public void scrollToLast(){
if(getCharCount() < 10 ) return;
try {
Rectangle lastBounds = getTextBounds(getCharCount()-5,getCharCount()-1);
Method m = StyledText.class.getDeclaredMethod("showLocation", Rectangle.class,boolean.class);
m.setAccessible(true);
m.invoke(this, lastBounds,true);
} catch (Exception e) {
e.printStackTrace();
}
}
//简单通过后缀,判断一下是否图片文件,提高后来用内容判断图片的效率
public static boolean maybeImageFile(String string) {
String suffixs=".bmp.jpgjpeg.png.ico.tif.gif";
if(StringUtil.isNull(string) || string.length() < 4)
return false;
if(suffixs.indexOf(string.substring(string.length()-4,string.length()-1).toLowerCase()) != -1)
return true;
return false;
}
//增加一个非文字元素
public void addElement(ElementProxy proxy) {
int offset = getCaretOffset();
replaceTextRange(offset, 0, proxy.getPlaceholder());
elements.put(offset, proxy);
showElement(proxy, offset);
setCaretOffset(offset+1);
}
private void showElement(ElementProxy element, int offset) {
StyleRange style = new StyleRange ();
style.start = offset;
style.length = 1;
int[] metrics=element.getMetrics();
style.metrics = new GlyphMetrics(metrics[0],metrics[1],metrics[2]);
try{
setStyleRange(style);
}catch(Exception ex){
ex.printStackTrace();
}
}
public String getBasePath() {
return basePath;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
public boolean isFirstTime() {
return firstTime;
}
public void setFirstTime(boolean firstTime) {
this.firstTime = firstTime;
}
public void invokeAction(int action)
{
super.invokeAction(action);
switch (action)
{
case UndoManager.UNDO:
undo();
break;
case UndoManager.REDO:
redo();
break;
}
}
private void undo()
{
if (undoManager != null)
undoManager.undo();
}
private void redo()
{
if (undoManager != null)
undoManager.redo();
}
@Override
public void dispose() {
if(undoManager != null)
undoManager.disconnect();
super.dispose();
}
//测试实例
public static void main(String[] args) {
String text =
"This snippet <a href=embed/1115b2306b6.txt width=50 height=17>测试.txt</a>shows how to <a href=embed/1115b2306b6.txt width=50 height=17>测试.txt</a>embed 中华人民共和国images in a StyledText.\n"+
"Here is one:,\nto add an image from your filesystem to the StyledText"+
"Use the add button to add an image from your filesystem to the StyledText<a href=embed/1115b2306b6.txt width=50 height=17>测试.txt</a> at the current caret offset.";
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
final SimpleNote note = new SimpleNote(shell, SWT.WRAP | SWT.BORDER|SWT.V_SCROLL);
note.setText(text);
note.setBasePath(System.getProperty("java.io.tmpdir"));
note.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Button button = new Button (shell, SWT.PUSH);
button.setText("Add Image");
button.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
button.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
FileDialog dialog = new FileDialog(shell);
String filename = dialog.open();
if (filename != null) {
try {
if(maybeImageFile(filename))
note.addElement(new ImageProxy(new ImageData(filename)));
else
note.addElement(new FileProxy(new File(filename)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
shell.setSize(400, 400);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -