📄 styledtextdelegate.java
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.widgets.rich2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.PaintObjectEvent;
import org.eclipse.swt.custom.PaintObjectListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.GlyphMetrics;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ScrollBar;
import edu.tsinghua.lumaqq.widgets.rich.LineStyle;
/**
* StyledText的代理类,封装一些方法
*
* @author luma
*/
public class StyledTextDelegate {
public static final String DEFAULT_FACE_TAG = "\u0014";
public static final String CUSTOM_HEAD_TAG = "\u0016";
public static final String CUSTOM_FACE_TAG = "\u0015";
private StyledText st;
private Map<Point, StyleObject> objects;
private List<Entry<Point, StyleObject>> temp;
private List<StyleObject> copyObject;
private String copyString;
private List<Integer> objectOffset;
private List<StyleObject> deferredDispose;
private Clipboard clipboard;
private Font font;
// 用户自定义按键的映射表,key是按键值,vlaue是Runnable对象
private Map<Integer, Runnable> userActionMap;
// 垂直卷滚条的上一次位置
private Map<StyleObject, Integer> objectY;
private Composite container;
/**
* 构造函数
*/
public StyledTextDelegate() {
objects = new HashMap<Point, StyleObject>();
temp = new ArrayList<Entry<Point,StyleObject>>();
copyObject = new ArrayList<StyleObject>();
objectOffset = new ArrayList<Integer>();
deferredDispose = new ArrayList<StyleObject>();
userActionMap = new HashMap<Integer, Runnable>();
clipboard = new Clipboard(Display.getCurrent());
objectY = new HashMap<StyleObject, Integer>();
}
/**
* @return
* StyledText对象
*/
public StyledText getStyledText() {
return st;
}
/**
* @return
* 一个可发送的字符串
*/
public String getText() {
return st.getText();
}
/**
* @return
* st的容器
*/
public Composite getContainer() {
return container;
}
/**
* 创建StyledText
*
* @param parent
* 父容器
* @param style
* 样式
* @param layoutData
* 布局
*/
public void createStyledText(Composite parent, int style, Object layoutData) {
container = new Composite(parent, SWT.NONE);
if(layoutData != null)
container.setLayoutData(layoutData);
container.setLayout(new GridLayout());
st = new StyledText(container, style);
st.setLayoutData(new GridData(GridData.FILL_BOTH));
st.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent e) {
int start = e.start;
int replaceCharCount = e.end - e.start;
int newCharCount = e.text.length();
temp.clear();
for(Iterator<Entry<Point, StyleObject>> i = objects.entrySet().iterator(); i.hasNext(); ) {
Entry<Point, StyleObject> entry = i.next();
Point range = entry.getKey();
if(start + replaceCharCount <= range.x) {
i.remove();
range.x += newCharCount - replaceCharCount;
temp.add(entry);
} else if(start < range.x + range.y) {
StyleObject so = entry.getValue();
if(so != null && !so.isDisposed()) {
// 如果这个对象正在拷贝缓冲中,延迟对他的释放
if(copyObject.contains(so)) {
deferredDispose.add(so);
so.hide();
} else
so.dispose();
}
// 调整偏移
if(start > range.x) {
e.start = range.x;
replaceCharCount = e.end - e.start;
}
i.remove();
}
}
for(Entry<Point, StyleObject> entry : temp)
objects.put(entry.getKey(), entry.getValue());
}
});
st.addPaintObjectListener(new PaintObjectListener() {
public void paintObject(PaintObjectEvent event) {
GC gc = event.gc;
StyleRange style = event.style;
int start = style.start;
for(Point range : objects.keySet()) {
if(start == range.x) {
StyleObject so = objects.get(range);
switch(so.objectType) {
case StyleObject.IMAGE:
Image image = (Image)so.object;
int x = event.x;
int y = event.y + event.ascent - style.metrics.ascent;
gc.drawImage(image, x, y);
break;
case StyleObject.CONTROL:
Control control = (Control)so.object;
so.show();
y = event.y + event.ascent - so.getBounds().height;
control.setLocation(event.x, y);
objectY.put(so, st.getVerticalBar().getSelection());
break;
}
}
}
}
});
st.getVerticalBar().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
ScrollBar vBar = st.getVerticalBar();
for(StyleObject so : objects.values()) {
Integer oldY = objectY.get(so);
if(oldY == null)
continue;
Rectangle rect = so.getBounds();
if(vBar.getSelection() + vBar.getThumb() <= oldY + rect.y - 5)
so.hide();
else if(vBar.getSelection() >= oldY + rect.y + rect.height - 5)
so.hide();
}
}
});
st.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int key;
if (e.keyCode != 0) {
// special key pressed (e.g., F1)
key = e.keyCode | e.stateMask;
} else {
// character key pressed
key = e.character | e.stateMask;
}
Runnable action = getUserKeyAction(key);
if(action != null)
action.run();
}
});
st.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if(font != null)
font.dispose();
clipboard.dispose();
disposeStyleObjects();
}
});
// 重定向一些按键
st.setKeyBinding(SWT.MOD1 | 'C', SWT.NULL);
st.setKeyBinding(SWT.MOD1 | 'V', SWT.NULL);
st.setKeyBinding(SWT.MOD1 | 'X', SWT.NULL);
st.setKeyBinding(SWT.MOD1 | 'A', SWT.NULL);
setUserKeyAction(SWT.MOD1 | 'C', new Runnable() {
public void run() {
copy();
}
});
setUserKeyAction(SWT.MOD1 | 'V', new Runnable() {
public void run() {
paste();
}
});
setUserKeyAction(SWT.MOD1 | 'X', new Runnable() {
public void run() {
cut();
}
});
setUserKeyAction(SWT.MOD1 | 'A', new Runnable() {
public void run() {
st.selectAll();
}
});
container.setBackground(st.getBackground());
}
/**
* 设置背景色
*
* @param c
*/
public void setBackground(Color c) {
st.setBackground(c);
container.setBackground(c);
}
/**
* 释放资源
*/
public void dispose() {
st.dispose();
}
/**
* 添加一个内嵌对象
*
* @param so
* 内嵌对象描述类
* @param offset
* 偏移
* @param length
* 长度
*/
void addObject(StyleObject so, int offset, int length) {
Point range = new Point(offset, length);
objects.put(range, so);
StyleRange style = new StyleRange();
style.start = offset;
style.length = length;
Rectangle rect = so.getBounds();
style.metrics = new GlyphMetrics(rect.height, 0, rect.width);
st.setStyleRange(style);
}
/**
* 在当前位置插入一个图像
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -