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

📄 chatbox.java

📁 用java实现的
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
		}
	}
	// 光标
	private Cursor beamCursor;
	
	private Caret caret;
	// 当前光标所在字符数
	private int caretChar;
	// 当前光标所在行数
	private int caretLine;
	// 客户区域大小
	private Rectangle clientArea;
	private Display display;
	// 当前鼠标是否按下,和按下时的坐标
	private boolean down;
	private int downX, downY;
	// 当前是否有区域被选择
	private boolean selected;
	// 当前字体的描述信息
	private FontMetrics fontMetrics;
	// 用户输入缓冲区对象数组
	private List lineBufs;
	// 当前总行数
	private int lines;
	private int margin = 5;
	// 最大字符数限制
	private int maxChar;
	private ScrollBar vBar;
	// view port看到的第一行的行号和最后一行的行号
	private int viewBeginLine;
	private int viewEndLine;
	// 当前被影响的行
	private int affectedBeginLine, affectedEndLine;
	// 拷贝缓冲区,不带标签占位符
	private String regularCopy;
	// 拷贝缓冲区,带表情,可以用parseText方法解析出若干linecache
	private String extendCopy;
	// 剪贴板
	private Clipboard clipboard;
	// IconHolder实例
	private IconHolder icons = IconHolder.getInstance();
	// 当前是否enable,对于disable的,只响应鼠标消息和ctrl + a, ctrl + c事件
	private boolean enabled;
	// disable时的背景色
	private Color color;
	// 右键菜单,有两个,一个在enable时显示,一个在disable时显示,也可以搞一个,我觉得搞两个好些
	private Menu enableMenu, disableMenu;
	// 表示换行是用Enter还是用Ctrl + Enter,不用的一方将忽略不做处理
	//     注意这个useEnter和OptionUtils中的useEnter并不是一个意思,这个是用来
	//     换行的,option中的是用来发送消息的,他们两个在实际中将会是相反的值
	private boolean useEnter;

	/**
	 * @param parent
	 * @param style
	 */
	public ChatBox(Composite parent, int style) {
		super(parent, SWT.V_SCROLL);
		initVariable();
		initMenu();
	}
	
	/**
	 * 初始化菜单项
	 */
	private void initMenu() {
		// enable时的弹出菜单
		enableMenu = new Menu(this);
		// 剪切菜单
		MenuItem mi = new MenuItem(enableMenu, SWT.PUSH);
		mi.setText(LumaQQ.getResourceString("chatbox.menu.cut"));
		mi.addSelectionListener(
			new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					handleCtrlX();
				}
			}
		);
		// 复制菜单
		mi = new MenuItem(enableMenu, SWT.PUSH);
		mi.setText(LumaQQ.getResourceString("chatbox.menu.copy"));
		mi.addSelectionListener(
			new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					handleCtrlC();
				}
			}
		);
		// 粘贴菜单
		mi = new MenuItem(enableMenu, SWT.PUSH);
		mi.setText(LumaQQ.getResourceString("chatbox.menu.paste"));
		mi.addSelectionListener(
			new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					handleCtrlV();
				}
			}
		);
		// 添加菜单显示监听器
		enableMenu.addMenuListener(
			new MenuAdapter() {
				public void menuShown(MenuEvent e) {
					enableMenu.getItem(0).setEnabled(selected);
					enableMenu.getItem(1).setEnabled(selected);
				}
			}
		);
		
		// disable时的菜单
		disableMenu = new Menu(this);
		// 复制菜单
		mi = new MenuItem(disableMenu, SWT.PUSH);
		mi.setText(LumaQQ.getResourceString("chatbox.menu.copy"));
		mi.addSelectionListener(
			new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					handleCtrlC();
				}
			}
		);
		// 全选菜单
		mi = new MenuItem(disableMenu, SWT.PUSH);
		mi.setText(LumaQQ.getResourceString("chatbox.menu.select.all"));
		mi.addSelectionListener(
			new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					selectAll();
				}
			}
		);
		// 添加菜单显示监听器
		disableMenu.addMenuListener(
			new MenuAdapter() {
				public void menuShown(MenuEvent e) {
					disableMenu.getItem(0).setEnabled(selected);
				}
			}
		);
	}

	/**
	 * 在当前光标位置添加一个表情
	 * @param smileyId
	 */
	public void addSmiley(int smileyId) {
		handleCharacter((char)0, smileyId);
	}
	
	// 调整从lineCache开始的一系列软分行行
	private int adjustLine(GC gc, LineCache lineCache) {
		// 调整过程中新增了多少行
		int newLine = 0;
		// 当前行
		int curLine = lineBufs.indexOf(lineCache);
		// 得到当前行宽
		int width = lineCache.getWidth(gc);
		// 检查是否超出边界
		recalculateClientArea();
		if(clientArea.width == 0) return 0;
		width -= clientArea.width - 2 * margin;
		while(width > 0 || width <= 0 && !lineCache.hardLine) {
			if(width > 0) {
				// 得到下一行,如果没有且当前行是硬分行,则创建一行
				LineCache nextLine = null;
				if(lineCache.hardLine) {
					nextLine = new LineCache();
					lineBufs.add(curLine + 1, nextLine);
					newLine++;
					lineCache.hardLine = false;
				} else
					nextLine = (LineCache)lineBufs.get(curLine + 1);
				// 得到维持宽度不超出所需要删掉的字符数
				int index = lineCache.getEndCharIndexLargerThanWidth(gc, width);
				nextLine.insert(0, lineCache.delete(index));
				nextLine.insertSmiley(0, lineCache.exceedSmiley);
				// 置下一轮循环条件
				lineCache = nextLine;
				curLine++;				
			} else {
				LineCache nextLine = (LineCache)lineBufs.get(curLine + 1);
				lineBufs.remove(curLine + 1);
				lineCache.append(nextLine.getChars());
				lineCache.appendSmiley(nextLine.getSmiley());
				lineCache.hardLine = nextLine.hardLine;
				newLine--;
			}
			width = lineCache.getWidth(gc) - clientArea.width + 2 * margin;
		}
		return newLine;
	}

	// 调整垂直卷滚条的参数
	private void adjustVerticalBar() {
		vBar.setMaximum(lines);
		vBar.setThumb(viewEndLine - viewBeginLine + 1);
		vBar.setSelection(viewBeginLine);
		if(viewBeginLine > 0 || viewEndLine < lines - 1)
			vBar.setVisible(true);
		else
			vBar.setVisible(false);
	}
	
	// 根据鼠标位置计算光标所在字符
	private int calcCaretChar(GC gc, int x, int line) {
		LineCache lineCache = (LineCache)lineBufs.get(line);
		return lineCache.getBeginCharIndexLessThanWidth(gc, x);
	}
	
	// 根据鼠标位置计算光标所在行
	private int calcCaretLine(int y) {
		y -= margin;
		int cl, i = 0;
		while(y > 0) 
			y -= getLineHeight(i++) + margin;		
		cl = viewBeginLine + i - 1;
		if(cl < viewBeginLine)
			cl = viewBeginLine;
		else if(cl > viewEndLine)
			cl = viewEndLine;
		return cl;
	}
    
    // 画一个Bevel矩形
    private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright)
    {
        Color old = gc.getForeground();
        gc.setForeground(bottomright);
        gc.drawLine(x + w, y, x + w, y + h);
        gc.drawLine(x, y + h, x + w, y + h);
        gc.setForeground(topleft);
        gc.drawLine(x, y, (x + w) - 1, y);
        gc.drawLine(x, y, x, (y + h) - 1);
        gc.setForeground(old);
    }
	
	// 给定一个结束行,根据客户区的大小判断开始行的行号
	private int getBeginLine(int end) {
		int h = margin;
		int begin = end + 1;
		while(h < clientArea.height && begin >= 0)
			h += getLineHeight(--begin) + margin;
		return begin + 1;
	}
	
	// 给定一个开始行,根据客户区的大小判断结束行的行号
	private int getEndLine(int begin) {
		int h = margin;
		int end = begin - 1;
		while(h < clientArea.height && end <= lines - 1)
			h += getLineHeight(++end) + margin;
		 return end - 1;
	}
	
	// 计算某行的高度
	private int getLineHeight(int line) {
		LineCache lineCache;
		if(line < 0 || line >= lineBufs.size()) return 0;
		else lineCache = (LineCache)lineBufs.get(line);
		if(lineCache.hasSmiley)
			return Math.max(fontMetrics.getHeight(), 20);
		else
			return fontMetrics.getHeight();
	}
    
    // 返回给定行号的Y坐标,这个坐标是相对于视口的坐标,行号也是一个相对行号
    private int getLineY(int line) {
    	if(line < viewBeginLine) return -100;
    	int y = margin;
    	for(int i = viewBeginLine; i < line; i++)
    		y += getLineHeight(i) + margin;
    	return y;
    }

	// 处理back space按键
	private void handleBackSpaceKey() {
		GC gc = new GC(this);
		LineCache lineCache = (LineCache)lineBufs.get(caretLine);
		// 如果当前有选择部分则删除
		if(selected) {
			handleCharacter(lineCache, (char)0, 0, null, true, false);
			return;
		}
		
		// 如果光标处于最前,则直接返回
		if(caretChar == 0 && caretLine == 0) return;
		
		if(lineCache.hardLine && caretChar > 0) {
			lineCache.delete(caretChar - 1, caretChar);
			caretChar--;
			setCaretLine(caretLine);
		} else {
			if(caretChar == 0 && caretLine > 0) {
				lineCache = (LineCache)lineBufs.get(caretLine - 1);
				caretLine--;
				caretChar = lineCache.size();
				if(caretLine < viewBeginLine)
					viewBeginLine--;
			}
			// 删除一个字符
			if(!lineCache.hardLine) {
				lineCache.delete(caretChar - 1, caretChar);
				caretChar--;				
			}
			// 删除下一行
			LineCache nextLine = (LineCache)lineBufs.get(caretLine + 1);
			lineBufs.remove(caretLine + 1);
			lineCache.append(nextLine.getChars());
			lineCache.appendSmiley(nextLine.getSmiley());
			lineCache.hardLine = nextLine.hardLine;
			// 调整行
			int i = adjustLine(gc, lineCache) - 1;
			
			lines += i;
			if(i < 0)
				viewEndLine = getEndLine(viewBeginLine);
			// 调整卷滚条
			adjustVerticalBar();
			setCaretLine(caretLine);
		}
		
		gc.dispose();
		redraw();
	}
	
	// 在一行中插入一个字符,可以选择不插入,如果有选择部分,何以选择保存选择部分
	//     方法返回删除的行数
	private int insertChar(LineCache lineCache, char c, int smileyId, LineCache insertLine, boolean noInsert, boolean saveDelete) {
		int lineDeleted = 0;
		// 如果有选择部分,删除
		if(selected) {
			if(saveDelete) {
				LineCache copyCache = deleteSelected();
				regularCopy = copyCache.getString();
				clipboard.setContents(new Object[] { regularCopy }, new Transfer[] { TextTransfer.getInstance()});
			} else
				deleteSelected();
			
			for(int i = affectedEndLine - 1; i > affectedBeginLine; i--) {
				LineCache line = (LineCache)lineBufs.get(i);
				if(line.size() == 0) {
					lineBufs.remove(i);
					lineDeleted--;
				}
			}
		}
		// 插入这个字符
		if(!noInsert) {
			if(insertLine != null) {
				lineCache.insert(caretChar, insertLine.getChars());
				lineCache.insertSmiley(caretChar, insertLine.getSmiley());
			} else if(c == 0)
				lineCache.insertSmiley(caretChar, smileyId);			
			else
				lineCache.insert(caretChar, c);			
		}
		// 把最后一行选择的行的内容附加到当前行后,只有一种情况不需要附加
		//     就是选择的行只有一行(就是当前行),且此行为硬分行
		if(selected) {
			selected = false;
			if(!(affectedEndLine == affectedBeginLine && lineCache.hardLine)) {
				if(caretLine + 1 < lineBufs.size()) {
					LineCache line = null;
					for(line = (LineCache)lineBufs.get(caretLine + 1); !line.hardLine; line = (LineCache)lineBufs.get(caretLine + 1)) {
						lineCache.append(line.getChars());
						lineCache.appendSmiley(line.getSmiley());
						lineBufs.remove(caretLine + 1);
						lineDeleted--;					
					}
					lineCache.append(line.getChars());
					lineCache.appendSmiley(line.getSmiley());
					lineBufs.remove(caretLine + 1);
					lineCache.hardLine = true;
					lineDeleted--;					
				} else
					lineCache.hardLine = true;
			}
		}
		return lineDeleted;
	}
	
	/**
	 * 处理字符输入事件,但是也可以用来处理删除选择内容,copy cut等等
	 * @param lineCache 这是当前行
	 * @param c 这是要插入的字符
	 * @param smileyId 要插入的表情
	 * @param insertLine 要插入的行
	 * @param noInsert 是否插入
	 * @param saveDelete 是否保存删除的内容到剪贴板
	 */
	private void handleCharacter(LineCache lineCache, char c, int smileyId, LineCache insertLine, boolean noInsert, boolean saveDelete) {
		GC gc = new GC(this);

		// 调整行
		int newLine = insertChar(lineCache, c, smileyId, insertLine, noInsert, saveDelete) + adjustLine(gc, lineCache);
		// 如果光标本来是在最后,则下移一行,要注意判断粘贴的情况
		if(!noInsert) {
			if(insertLine != null) {
				int t = caretChar + insertLine.size();
				t -= lineCache.size();
				int i = 0;
				while(t > 0) {
					i++;
					t -= ((LineCache)lineBufs.get(caretLine + i)).size();				
				}
				setCaretLine(caretLine + i);
				caretChar = t + ((LineCache)lineBufs.get(caretLine)).size();
			} else if(caretChar + 1 > lineCache.size()) {
				caretChar = 1;
				caretLine++;
			} else {
				setCaretLine(caretLine);
				caretChar++;
			}			
		}
		
		// 如果调整过程中产生了新行,则调整总行数和视口
		if(newLine != 0) {
			lines += newLine;
			if(caretLine < viewBeginLine) {
				viewBeginLine = caretLine;
				viewEndLine = getEndLine(viewBeginLine);
			} else if(caretLine > viewEndLine) {
				viewEndLine = caretLine;
				viewBeginLine = getBeginLine(viewEndLine);
			} else
				viewEndLine = getEndLine(viewBeginLine);
			adjustVerticalBar();
		}
		
		gc.dispose();
		redraw();	
	}
	
	// 处理普通输入事件
	private void handleCharacter(char c, int smileyId) {
		LineCache lineCache = (LineCache)lineBufs.get(caretLine);
		handleCharacter(lineCache, c, smileyId, null, false, false);
	}

	// 处理delete按键
	private void handleDeleteKey() {
		// 如果光标处于最末尾,则直接返回
		LineCache lineCache = (LineCache)lineBufs.get(caretLine);
		if(caretChar == lineCache.size() && caretLine == lines - 1) return;
		
		// 如果当前有选择的内容,则删除选择内容
		if(selected) {
			handleCharacter(lineCache, (char)0, 0, null, true, false);
			return;
		}

		int i;
		if(lineCache.hardLine && caretChar < lineCache.size()) {
			lineCache.delete(caretChar, caretChar + 1);
		} else {
			GC gc = new GC(this);
			// 得到下一行
			LineCache nextLine = (LineCache)lineBufs.get(caretLine + 1);
			// 如果是软分行,删除这个字符,硬分行不删,因为光标在末尾,等于删掉了回车
			//     如果是软分行且在末尾,则删掉下一行的第一个
			if(!lineCache.hardLine) {
				if(caretChar < lineCache.size())				

⌨️ 快捷键说明

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