📄 chatbox.java
字号:
}
}
// 设置被影响的行,endline一定要大于beginline
private void setAffected(int beginLine, int beginChar, int endLine, int endChar) {
LineCache lineCache = (LineCache)lineBufs.get(beginLine);
lineCache.affected = true;
lineCache.affectedBegin = beginChar;
lineCache.affectedEnd = lineCache.size();
for(int i = beginLine + 1; i < endLine; i++) {
lineCache = (LineCache)lineBufs.get(i);
lineCache.affected = true;
lineCache.affectedBegin = 0;
lineCache.affectedEnd = lineCache.size();
}
lineCache = (LineCache)lineBufs.get(endLine);
lineCache.affected = true;
lineCache.affectedBegin = 0;
lineCache.affectedEnd = endChar;
}
/**
* 全选
*/
public void selectAll() {
setAffected(0, 0, lines - 1, ((LineCache)lineBufs.get(lines - 1)).size());
selected = true;
caretChar = caretLine = 0;
affectedBeginLine = 0;
affectedEndLine = lines - 1;
redraw();
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent e) {
if(e.button == 1) {
down = false;
setCapture(false);
GC gc = new GC(this);
if(selected) {
setCaretLine(affectedBeginLine);
caretChar = ((LineCache)lineBufs.get(affectedBeginLine)).affectedBegin;
} else {
// 判断行
setCaretLine(calcCaretLine(e.y));
// 判断列
caretChar = calcCaretChar(gc, e.x, caretLine);
}
gc.dispose();
redraw();
}
}
// 检查是否有不允许的键按下,如果是,返回
private boolean noLaithKey(KeyEvent e) {
return (e.stateMask & SWT.CTRL) == 0 &&
(e.stateMask & SWT.ALT) == 0 &&
(e.keyCode != SWT.TAB);
}
// 画Tab的边界
private void paintBorder(GC gc, Rectangle r) {
Display disp = getDisplay();
Color c1 = disp.getSystemColor(SWT.COLOR_WIDGET_BORDER);
if(c1 != null)
drawBevelRect(gc, r.x, r.y, r.width - 1, r.height - 1, c1, c1);
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(PaintEvent e) {
// 隐藏光标
caret.setVisible(false);
// 重新计算客户区大小
GC gc = e.gc;
recalculateClientArea();
// 只重画视口内的行
int x = margin, y = margin, w = clientArea.width - 2 * margin;
for(int i = viewBeginLine; i <= viewEndLine; i++) {
int h = getLineHeight(i);
LineCache lineCache = (LineCache)lineBufs.get(i);
gc.fillRectangle(x, y, w, h);
lineCache.drawLine(gc, x, y);
y += h + margin;
}
// 文字的其他部分清空
gc.fillRectangle(x, y, w, clientArea.height - y);
// 画边界
paintBorder(gc, clientArea);
// 设置光标大小和位置
caret.setSize(1, getLineHeight(caretLine));
setCaretLocation(gc);
// 显示光标
caret.setVisible(true);
}
// 设置caret line
private void setCaretLine(int i) {
caretLine = i;
}
// 根据caretChar和caretLine的值设置光标位置
private void setCaretLocation(GC gc) {
LineCache lineCache = (LineCache)lineBufs.get(caretLine);
int x = lineCache.getWidthBefore(gc, caretChar) + margin;
int y = getLineY(caretLine);
caret.setLocation(x, y);
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
*/
public void setFont(Font font) {
super.setFont(font);
GC gc = new GC(this);
// 根据字体大小设置光标大小
fontMetrics = gc.getFontMetrics();
// 得到当前光标的字符偏移,这个偏移是相对于第一个字符而言,为绝对字符偏移
int offset = 0;
for(int i = 0; i < caretLine; i++) {
LineCache lineCache = (LineCache)lineBufs.get(i);
offset += lineCache.getCharSize();
}
offset += caretChar;
// 调整各行,只调整没一个软分行的最先一行
int size = lineBufs.size();
for(int i = size - 1; i >= 1; i--) {
LineCache prevLine = (LineCache)lineBufs.get(i - 1);
if(prevLine.hardLine) {
LineCache lineCache = (LineCache)lineBufs.get(i);
adjustLine(gc, lineCache);
}
}
// 调整剩下的第一行
adjustLine(gc, (LineCache)lineBufs.get(0));
// 重新调整其他参数
lines = lineBufs.size();
if(viewBeginLine >= lines)
viewBeginLine = 0;
viewEndLine = getEndLine(viewBeginLine);
adjustVerticalBar();
// 计算因为字体改变而改变的光标位置
caretLine = 0;
size = lineBufs.size();
for(int i = 0; i < size; i++) {
LineCache lineCache = (LineCache)lineBufs.get(i);
int len = lineCache.getCharSize();
offset -= len;
if(offset > 0)
caretLine++;
else {
caretChar = offset + len;
break;
}
}
if(caretLine > viewEndLine) {
caretLine = viewEndLine;
caretChar = 0;
}
gc.dispose();
redraw();
}
/**
* @param margin The margin to set.
*/
public void setMargin(int margin) {
this.margin = margin;
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*/
public void widgetDisposed(DisposeEvent e) {
beamCursor.dispose();
color.dispose();
clipboard.dispose();
}
// 删除被选择的内容
private LineCache deleteSelected() {
LineCache deleted = null;
if(selected) {
deleted = new LineCache();
for(int i = affectedBeginLine; i <= affectedEndLine; i++) {
LineCache lineCache = (LineCache)lineBufs.get(i);
lineCache.affected = false;
deleted.append(lineCache.delete(lineCache.affectedBegin, lineCache.affectedEnd));
deleted.appendSmiley(lineCache.exceedSmiley);
if(i < affectedEndLine && lineCache.hardLine)
deleted.append("\r\n");
}
}
return deleted;
}
// 拷贝被选择的内容
private LineCache copySelected() {
LineCache copied = null;
if(selected) {
copied = new LineCache();
for(int i = affectedBeginLine; i <= affectedEndLine; i++) {
LineCache lineCache = (LineCache)lineBufs.get(i);
copied.append(lineCache.getChars(lineCache.affectedBegin, lineCache.affectedEnd));
int sb = lineCache.getSmileyNumBefore(lineCache.affectedBegin);
int se = sb + lineCache.getSmileyNumAfter(lineCache.affectedBegin) - lineCache.getSmileyNumAfter(lineCache.affectedEnd);
copied.appendSmiley(lineCache.getSmiley(sb, se));
if(i < affectedEndLine && lineCache.hardLine)
copied.append("\r\n");
}
}
return copied;
}
/**
* 清楚所有内容
*/
public void clear() {
lineBufs.clear();
lineBufs.add(new LineCache());
lines = 1;
viewBeginLine = viewEndLine = 0;
caretLine = caretChar = 0;
adjustVerticalBar();
redraw();
}
/**
* 设置收到的消息,转换成内部表示
* @param msg
*/
public void setMessage(String msg) {
// 解析行
lineBufs = parseText(msg);
// 调整各行
GC gc = new GC(this);
int size = lineBufs.size();
for(int i = size - 1; i >=0; i--)
adjustLine(gc, (LineCache)lineBufs.get(i));
gc.dispose();
// 设置其他参数
lines = lineBufs.size();
viewBeginLine = 0;
viewEndLine = getEndLine(viewBeginLine);
setCaretLine(0);
caretChar = 0;
adjustVerticalBar();
redraw();
}
/**
* 在当前消息框内追加消息
* @param msg
*/
public void appendMessage(String msg) {
List list = parseText(msg);
if(list.size() == 0) return;
// 得到当前行数
int numLine = lineBufs.size();
// 把生成的行,除了第一行,都追加到最后一行之后
int size = list.size();
for(int i = 1; i < size; i++)
lineBufs.add(list.get(i));
// 把解析得到的第一行添加到原先最后一行的末尾,然后从该行开始,调整后面的
// 所有行
LineCache lineCache = (LineCache)lineBufs.get(numLine - 1);
LineCache first = (LineCache)list.get(0);
lineCache.append(first.getChars());
lineCache.appendSmiley(first.getSmiley());
GC gc = new GC(this);
for(int i = lineBufs.size() - 1; i >= numLine - 1; i--) {
adjustLine(gc, (LineCache)lineBufs.get(i));
}
gc.dispose();
// 调整完毕,把最后一行设成当前视口的最后一行
lines = lineBufs.size();
viewEndLine = lines - 1;
viewBeginLine = getBeginLine(viewEndLine);
setCaretLine(viewEndLine);
caretChar = 0;
adjustVerticalBar();
redraw();
}
// 解析一个字符串成为一个或多个(如果有多行的话)LineCache对象
private List parseText(String msg) {
/*
* 使用了状态机算法,一共三个状态,期待换行符,期待回车符,期待字符串
* 2和3状态都有三种改变动作,即遇到换行符,遇到回车符,遇到字符串,1
* 只有两种,遇到换行符,遇到回车符,不可能遇到字符串,因为2和3都要
* 遇到字符串才能到状态1,如果再遇到字符串,那肯定是错误的数据
* 状态变换图示如下
* /--\ \n(空行)
* \ /
* Expect String __
* / /\ \ \
* \r(空行) / / Str(行) \ \ \n
* / / \n \ \
* \/ / \r _\ \
* Expect \n <------------------ Expect \r
* / \ ------------------>
* \__/ Str(行)
* \r
*/
List bufs = new ArrayList();
// 0表示期待字符串,1表示期待换行符,也就是\r,2表示期待回车符,也就是\n
int expected = 0;
// 这个tokenizer的第三个为true是重要的,如果为false,空行将无法返回
StringTokenizer st = new StringTokenizer(msg, "\r\n", true);
while(st.hasMoreTokens()) {
String str = st.nextToken();
if(expected == 0) {
if(str.equals("\r")) {
LineCache lineCache = new LineCache();
bufs.add(lineCache);
expected = 2;
} else if(str.equals("\n")) {
LineCache lineCache = new LineCache();
bufs.add(lineCache);
} else {
LineCache lineCache = new LineCache();
int i = 0, j;
while((j = str.indexOf("\024", i)) != -1) {
lineCache.append(str.substring(i, j));
lineCache.appendSmiley(str.charAt(j + 1) & 0xFF);
i = j + 2;
}
lineCache.append(str.substring(i));
bufs.add(lineCache);
expected = 1;
}
} else if(expected == 1) {
if(str.equals("\n")) {
expected = 0;
} else if(str.equals("\r")) {
expected = 2;
}
} else {
if(str.equals("\n")) {
expected = 0;
} else if(str.equals("\r")) {
expected = 2;
} else {
LineCache lineCache = new LineCache();
int i = 0, j;
while((j = str.indexOf("\024", i)) != -1) {
lineCache.append(str.substring(i, j));
lineCache.appendSmiley(str.charAt(j + 1) & 0xFF);
i = j + 2;
}
lineCache.append(str.substring(i));
bufs.add(lineCache);
expected = 1;
}
}
}
return bufs;
}
/**
* 把内部表示转换成可发送的消息
* @return
*/
public String getMessage() {
StringBuffer sb = new StringBuffer();
int from = 0, j, s;
for(int i = 0; i < lines; i++) {
LineCache lineCache = (LineCache)lineBufs.get(i);
// 如果是空行,添加一个回车符号继续
if(lineCache.size() == 0) {
if(i < lines - 1) {
sb.append('\r');
sb.append('\n');
from += 2;
}
continue;
}
// 开始添加这一行,注意处理表情,表情是0x14开头后面跟表情id
s = 0;
int[] smiley = lineCache.getSmiley();
sb.append(lineCache.getChars());
while((j = sb.indexOf("\0", from)) != -1) {
sb.deleteCharAt(j);
sb.insert(j, (char)smiley[s++]);
sb.insert(j, (char)0x14);
from = j + 2;
}
if(lineCache.hardLine && i < lines - 1) {
sb.append('\r');
sb.append('\n');
}
from = sb.length();
}
return sb.toString();
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Control#setEnabled(boolean)
*/
public void setEnabled(boolean enabled) {
if(this.enabled = enabled) return;
this.enabled = enabled;
if(enabled)
setBackground(display.getSystemColor(SWT.COLOR_WHITE));
else
setBackground(color);
redraw();
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt.events.TraverseEvent)
*/
public void keyTraversed(TraverseEvent e) {
if(e.keyCode == SWT.CR) {
e.detail = SWT.TRAVERSE_RETURN;
e.doit = true;
}
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
*/
public void controlMoved(ControlEvent e) {
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
*/
public void controlResized(ControlEvent e) {
recalculateClientArea();
}
// 计算客户区大小,考虑垂直卷滚条的因素
private void recalculateClientArea() {
Rectangle ca = getClientArea();
if(ca.width > 0)
clientArea = ca;
}
/**
* @return Returns the useEnter.
*/
public boolean isUseEnter() {
return useEnter;
}
/**
* @param useEnter The useEnter to set.
*/
public void setUseEnter(boolean useEnter) {
this.useEnter = useEnter;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -