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

📄 chatbox.java

📁 用java实现的
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
		}
	}
	
	// 设置被影响的行,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 + -