⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chatbox.java

📁 用java实现的
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/*
* 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;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;

import edu.tsinghua.lumaqq.IconHolder;
import edu.tsinghua.lumaqq.LumaQQ;

/**
 * 支持输入文字和图像的编辑框,并且有垂直卷滚条,主要用于聊天
 * 
 * @author 马若劼
 */
public class ChatBox extends Canvas implements PaintListener, DisposeListener, MouseListener, KeyListener, TraverseListener, MouseMoveListener, ControlListener {
	// 一行的缓冲类
	private class LineCache {
		// 超出的表情,如果有的话
		private int[] exceedSmiley;
		// 本行是否是硬分行,所谓硬分行是指用户在行的末尾输入了回车符,
		//     而软分行则是因为长度太大,warp造成的
		private boolean hardLine;
		// 是否包含了表情
		private boolean hasSmiley;
		private StringBuffer sb;
		// 表情序号的数组
		private int[] smileyArray;
		// 表情的个数
		private int smileyNum;
		// 当前行是否收到鼠标选择影响,以及影响的字符开始和结束范围
		private boolean affected;
		private int affectedBegin, affectedEnd;
		private int temp;
		
		/**
		 * 构造函数
		 */
		public LineCache() {
			sb = new StringBuffer();
			hasSmiley = false;
			smileyNum = 0;
			smileyArray = new int[10];
			hardLine = true;
			affected = false;
		}
		
		// 返回这一行包含的字符数,这个字符数包含了表情数,其他语言的字符也只算一个字符
		public int getCharSize() {
			int i = 0, j, size = 0;
			while((j = sb.indexOf("\0", i)) != -1) {
				String sub = sb.substring(i, j);
				size += sub.length();
				i = j + 1;
			}
			if(i < sb.length()) {
				String sub = sb.substring(i);
				size += sub.length();				
			}
			size += smileyNum;
			return size;
		}
		
		// 追加一个字符
		public void append(char c) {
			sb.append(c);
		}
		
		// 追加一个字符串
		public void append(String s) {
			if(s == null) return;
			sb.append(s);
		}
		
		// 追加一个字符数组
		public void append(char[] c) {
			if(c == null) return;
			sb.append(c);
		}
		
		// 追加一个表情
		public void appendSmiley(int smileyId) {
			sb.append((char)0);
			if(smileyNum + 1 > smileyArray.length)
				smileyArray = enlargeArray(smileyArray);
			smileyArray[smileyNum++] = smileyId;
			hasSmiley = true;
		}

		// 追加一串表情
		public void appendSmiley(int[] smiley) {
			if(smiley == null) return;
			while(smileyNum + smiley.length > smileyArray.length)
				smileyArray = enlargeArray(smileyArray);
			System.arraycopy(smiley, 0, smileyArray, smileyNum, smiley.length);
			smileyNum += smiley.length;
			hasSmiley = true;
		}
		
		// 删除index之后的(包括index)的字符,并把其后的smiley置于
		//     exceedSmiley中
		public char[] delete(int index) {
			return delete(index, sb.length());
		}
		
		// 删除从begin到end的字符,不包括end
		public char[] delete(int begin, int end) {
			exceedSmiley = null;
			if(sb.length() == 0) return null;
			if(begin > sb.length()) begin = sb.length();
			if(end < 0) end = 0;
			if(begin >= end) return null;
			char[] ret = new char[end - begin];
			sb.getChars(begin, end, ret, 0);
			int smileyDeleted = getSmileyNum(begin, end);
			if(smileyDeleted > 0) {
				int smileyBegin = getSmileyNumBefore(begin);
				exceedSmiley = new int[smileyDeleted];
				System.arraycopy(smileyArray, smileyBegin, exceedSmiley, 0, smileyDeleted);
				System.arraycopy(smileyArray, smileyBegin + smileyDeleted, smileyArray, smileyBegin, smileyNum - smileyBegin - smileyDeleted);
			}
			smileyNum -= smileyDeleted;
			if(smileyNum == 0) hasSmiley = false;
			// 删除后面的字符
			sb.delete(begin, end);
			return ret;
		}
		
		// 画出这一行,起始点为x, y
		public void drawLine(GC gc, int x, int y) {
			// 表情和文字哪种高,另外一种的y坐标就要下沉
			int textY = y, smileyY = y, textX = x;
			if(hasSmiley) {
				int h = gc.getFontMetrics().getHeight();
				if(h >= 20)
					smileyY += h - 20;
				else
					textY += 20 - h;
			}
			// 开始画
			temp = 0;
			if(affected) {
				if(affectedBegin > 0)
					textX = drawString(gc, sb.substring(0, affectedBegin), textX, textY, smileyY, false);
				if(affectedEnd > affectedBegin)
					textX = drawString(gc, sb.substring(affectedBegin, affectedEnd), textX, textY, smileyY, true);
				if(sb.length() > affectedEnd)
					drawString(gc, sb.substring(affectedEnd, sb.length()), textX, textY, smileyY, false);				
			} else
				drawString(gc, sb.toString(), textX, textY, smileyY, false);
		}
		
		// 画一个字符串
		private int drawString(GC gc, String str, int x, int y, int smileyY, boolean reverse) {
			Color fg = null;
			Color bg = null;
			if(reverse) {
				fg = gc.getForeground();
				bg = gc.getBackground();
				gc.setForeground(bg);
				gc.setBackground(fg);
			}
			int i = 0, j;
			while((j = str.indexOf("\0", i)) != -1) {
				String sub = str.substring(i, j);
				if(!sub.equals("")) {
					gc.drawString(sub, x, y);
					x += gc.textExtent(sub).x;					
				}
				Image smileyImage = icons.getSmiley(smileyArray[temp++]);
				if(smileyImage != null) {
					gc.drawImage(smileyImage, x, smileyY);
					x += 20;
				}
				i = j + 1;
			}
			// 最后一串字符
			if(i < str.length()) {
				String sub = str.substring(i);
				if(!sub.equals("")) {
					gc.drawString(sub, x, y);
					x += gc.textExtent(sub).x;
				}
			}
			if(reverse) {
				gc.setForeground(fg);
				gc.setBackground(bg);
			}
			return x;
		}
		
		// 数组扩容
		private int[] enlargeArray(int[] array) {
			int[] ret = new int[array.length + 10];
			System.arraycopy(array, 0, ret, 0, array.length);
			return ret;
		}
		
		// 从头部得到一个字符偏移,偏移之前(不包括偏移)的总宽度要刚好小于等于width
		public int getBeginCharIndexLessThanWidth(GC gc, int width) {
			if(width < 0) return 0;
			// 我这里先暂且用一种近似的算法来计算这个索引
			//     先根据width和clientArea的比值得到一个索引的相对位置
			//     然后再一个字符一个字符的调整,就这样
			int len = sb.length();
			int init = (len * width) / (getWidth(gc, 0, sb.length()) + 1); // 加1是为了防止他等于0
			if(init > len) init = len;
			int w = getWidthBefore(gc, init);
			if(w < width && init < len) {
				while(w < width && init < len) {
					init++;
					w = getWidthBefore(gc, init);
				}
				if(w != width)
					init--;
			} else if(w > width) {
				while(w > width) {
					init--;
					w = getWidthBefore(gc, init);					
				}
			}
			return init;
		}
		
		// 返回string,这个string不会包含表情的占位符
		public String getString() {
			return getString(0, sb.length());
		}
		
		// 返回从begin开始到end结束的不包含表情占位符号的字符串
		public String getString(int begin, int end) {
			StringBuffer ret = new StringBuffer();
			for(int i = begin; i < end; i++) {
				if(sb.charAt(i) != 0)
					ret.append(sb.charAt(i));
			}
			return ret.toString();			
		}
		
		// 返回所有的字符
		public char[] getChars() {
			return getChars(0, sb.length());
		}
		
		// 返回从begin到end的字符串,不包括end
		public char[] getChars(int begin, int end) {
			if(sb.length() == 0) return null;
			if(begin < 0) begin = 0;
			if(end > sb.length()) end = sb.length();
			if(begin >= end) return null;
			char[] c = new char[end - begin];
			sb.getChars(begin, end, c, 0);
			return c;
		}
		
		// 从尾部得到一个字符偏移,偏移之后(包括偏移)的总宽度要刚好大于等于width
		public int getEndCharIndexLargerThanWidth(GC gc, int width) {
			if(width < 0) return sb.length() - 1;
			int len = sb.length();
			int init = (len * width) / (getWidth(gc, 0, sb.length()) + 1); // 加1是为了防止他等于0
			if(init > len) init = 0;
			int w = getWidthAfter(gc, init);
			if(w < width) {
				while(w < width && init > 0) {
					init--;
					w = getWidthAfter(gc, init);
				}
			} else if(w > width) {
				while(w > width) {
					init++;
					w = getWidthAfter(gc, init);
				}
				if(w != width)
					init--;
			}
			return init;
		}
		
		// 返回超出长度的部分,并把这部分从这行删除
		public char[] getExceed(GC gc) {
			// 计算现在的总长度
			int width = getWidth(gc, 0, sb.length());
			// 计算超出的长度
			int exceed = width + 2 * margin - clientArea.width;
			// 如果超出的长度小于等于0,则返回null
			if(exceed <= 0)
				return null;
			else { // 如果超出了,计算需要减少几个字符才能不超出
				int w = 0, i = sb.length(), s = 0;
				while(exceed > w) {
					char c = sb.charAt(--i);
					if(c == 0) { // 注意字符里面有表情的代表符号,0x0,碰到表情,算20的宽度
						w += 20;					
						// 注意要表情数加1,为什么不直接把smileyNum减1?因为还需要得到被踢出
						//     这一行的表情
						s++;
					}
					else // 如果不是表情,则计算字符宽度
						w += gc.textExtent(new String(new char[] { c })).x;
				}
				// 删除超出的部分
				return delete(i);
			}
		}
		
		// 返回所有的表情
		public int[] getSmiley() {
			return getSmiley(0, smileyNum);
		}
		
		// 返回第begin到end个表情,不包括第end个
		public int[] getSmiley(int begin, int end) {
			if(begin < 0) begin = 0;
			if(end > smileyNum) end = smileyNum;
			if(begin >= end) return null;
			int[] ret = new int[end - begin];
			System.arraycopy(smileyArray, begin, ret, 0, end - begin);
			return ret;
		}
		
		// 找出从begin到end之间的表情数,不包括end
		private int getSmileyNum(int begin, int end) {
			int ret = 0;
			while(true) {
				int index = sb.indexOf("\0", begin);
				if(index != -1 && index < end) {
					ret++;
					begin = index + 1;
				} else
					break;
			}
			return ret;
		}
		
		// 找出从index开始往后数,表情的个数,包括index
		private int getSmileyNumAfter(int index) {
			return getSmileyNum(index, sb.length());
		}
		
		// 找出从index开始往前数,表情的个数,不包括index
		private int getSmileyNumBefore(int index) {
			return getSmileyNum(0, index);
		}
		
		// 返回整个串的长度
		public int getWidth(GC gc) {
			return getWidth(gc, 0, sb.length());
		}
		
		// 返回从index开始到end为止的字符宽度,不包括end
		public int getWidth(GC gc, int begin, int end) {
			if(end > sb.length()) end = sb.length();
			if(begin < 0) begin = 0;
			if(begin >= end) return 0;
			String str = getString(begin, end);
			int x = gc.textExtent(str).x;
			int s = getSmileyNum(begin, end);
			return x + s * 20;
		}
		
		// 返回从index开始到结尾的字符宽度,包括index
		public int getWidthAfter(GC gc, int index) {
			return getWidth(gc, index, sb.length());
		}
		
		// 返回index开始之前的字符总宽度,不包括index
		public int getWidthBefore(GC gc, int index) {
			return getWidth(gc, 0, index);
		}
		
		// 插入一个字符
		public void insert(int index, char c) {
			sb.insert(index, c);
		}
		
		//插入一个字符数组
		public void insert(int index, char[] c) {
			if(c == null) return;
			sb.insert(index, c);
		}
		
		// 插入一个表情
		public void insertSmiley(int index, int smileyId) {
			sb.insert(index, (char)0);
			if(smileyNum + 1> smileyArray.length)
				smileyArray = enlargeArray(smileyArray);
			int s = getSmileyNumBefore(index);
			System.arraycopy(smileyArray, s, smileyArray, s + 1, smileyNum - s);
			smileyArray[s] = smileyId;
			smileyNum++;
			hasSmiley = true;
		}
		
		// 在index字符处,插入表情
		public void insertSmiley(int index, int[] smiley) {
			if(smiley == null) return;
			while(smileyNum + smiley.length > smileyArray.length)
				smileyArray = enlargeArray(smileyArray);
			int s = getSmileyNumBefore(index);
			System.arraycopy(smileyArray, s, smileyArray, s + smiley.length, smileyNum - s);
			System.arraycopy(smiley, 0, smileyArray, s, smiley.length);
			smileyNum += smiley.length;
			hasSmiley = true;
		}
		
		// 返回字符总数
		public int size() {
			return sb.length();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -