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

📄 richbox.java

📁 java写的qq代码实现qq的部分功能
💻 JAVA
📖 第 1 页 / 共 5 页
字号:
/*
* 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.rich;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
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.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;

import edu.tsinghua.lumaqq.LumaQQ;

/**
 * 新的聊天输入框,支持不同大小字体,颜色,显示任意大小图像
 * 
 * @author luma
 */
public class RichBox extends Canvas {
	// 光标
	private Cursor beamCursor;
	// 游标
	private Caret caret;
	// 只读标志
    private boolean readonly;
    // 事件监听器
    private Listener listener;
    // 垂直偏移
	private int verticalScrollOffset;
    // view顶部的行号
	private int topLine; 
	// 上次重画时的最顶行
	private int lastPaintTopLine;
	// content and other tools
	private IWrappedRichContent content;
	private ContentHelper contentHelper;
	// GC和gc引用次数
	private GC gc;
	private int gcReference;
	
	// the client area height. Needed to calculate
	// content width for new
	private int clientAreaHeight;
	// the client area width. Needed during Resize
	// callback to determine
	private int clientAreaWidth; 
	
	// margins
	private int leftMargin;
	private int topMargin;
	private int rightMargin;
	private int bottomMargin;
	
	// 光标的X偏移位置,不包括左边缘,这个变量用来保证光标上下移动时有一个基本的基准点
	private int columnX;
	
	// 选择的起始和结束偏移,不包括结束位置
	private int selectionStart, selectionEnd;
	// 选择的锚点,选择开始的参考点
	private int selectionAnchor;
	// true表示运行用双击选择行
	private boolean doubleClickEnabled;

	// 右键菜单
	private boolean enableContextMenu;
	private Menu contextMenu;
	
	// true表示禁止动画
	private boolean forbiddenAnimation;
	
	// caret offset,注意要处理图片的情况,过一个图片我们要把offset增加2
	private int caretOffset;
	
	// 自动滚动的方向
	private int autoScrollDirection;
	
	// 通过用这个颜色在图片上画一次,使图片成为被选择状态
	private Color backXorColor;
	
	// 剪贴板
	private Clipboard clipboard;
	
	static final char TAB = '\t';
	
	// 按键处理的映射表,key是按键值,value是动作的id
	private Map keyActionMap;
	
	// 用户自定义按键的映射表,key是按键值,vlaue是IAction对象
	private Map userActionMap;
	
	// 按键动作id
	/** 对应于Home */
	public static final int LINE_START = 1;
	/** 对应于End */
	public static final int LINE_END = 2;
	/** 上箭头 */
	public static final int LINE_UP = 3;
	/** 下箭头 */
	public static final int LINE_DOWN = 4;
	/** 左箭头 */
	public static final int COLUMN_LEFT = 5;
	/** 右箭头 */
	public static final int COLUMN_RIGHT = 6;
	/** Ctrl + Home */
	public static final int TEXT_START = 7;
	/** Ctrl + End */
	public static final int TEXT_END = 8;
	/** 上翻页 */
	public static final int PAGE_UP = 9;
	/** 下翻页 */
	public static final int PAGE_DOWN = 10;
	/** 向左选择一个字符 */
	public static final int SELECT_COLUMN_LEFT = 11;
	/** 向右选择一个字符 */
	public static final int SELECT_COLUMN_RIGHT = 12;
	/** 向上选择一行 */
	public static final int SELECT_LINE_UP = 13;
	/** 向下选择一行 */
	public static final int SELECT_LINE_DOWN = 14;
	/** 剪切 */
	public static final int CUT = 15;
	/** 拷贝 */
	public static final int COPY = 16;
	/** 粘贴 */
	public static final int PASTE = 17;
	/** 删除光标前一个字符 */
	public static final int DELETE_PREVIOUS = 18;
	/** 删除光标后一个字符 */
	public static final int DELETE_NEXT = 19;
	/** 全选 */
	public static final int SELECT_ALL = 20;
	/** 用户自定义按键 */
	public static final int CUSTOM_ACTION = 21;
	/** 回车换行 */
	public static final int NEW_LINE = 22;
	
	// 回车换行
	private static final String CRLF = System.getProperty("line.separator");
	
	// 自动滚动的间隔
	private static final int AUTO_SCROLL_INTERVAL = 100;
	
	// 是否是Mac
	private static final boolean IS_CARBON;
	// 是否windows
	private static final boolean IS_WIN32;
	// 是否做双缓冲
	private static final boolean DOUBLE_BUFFERED;
	static {
	    // 如果不是Mac,底层也不是gtk,则做双缓冲
		String platform = SWT.getPlatform();
		IS_CARBON = "carbon".equals(platform);
		IS_WIN32 = "win32".equals(platform);
		DOUBLE_BUFFERED = !IS_CARBON && !"gtk".equals(platform);
	}
	
	// 自动滚动的任务
	private Runnable autoScrollTimer = new Runnable() {
        public void run() {
			if (autoScrollDirection == SWT.UP) {
				doLineUp();
				getDisplay().timerExec(AUTO_SCROLL_INTERVAL, this);
			} else if (autoScrollDirection == SWT.DOWN) {
				doLineDown();
				getDisplay().timerExec(AUTO_SCROLL_INTERVAL, this);
			}
        }
	};
	
	// 事件类型定义
	static final int VerifyKey = 3005;
	
	private AnimationManager animationManager;
    private ImageResolver imageResolver;
	
    /**
     * @param parent
     * @param style
     */
    public RichBox(Composite parent) {
		super(parent, SWT.V_SCROLL | SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND);
		initialVariables();
		initialControl();
		initialContextMenu();
		installListeners();
		setScrollBars();
		createKeyBindings();
    }
    
	/**
     * 初始化右键菜单
     */
    private void initialContextMenu() {
        contextMenu = new Menu(this);
        
        MenuItem mi = new MenuItem(contextMenu, SWT.PUSH);
        mi.setText(LumaQQ.getString("richbox.menu.copy"));
        mi.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                copy();
            }
        });
        mi = new MenuItem(contextMenu, SWT.PUSH);
        mi.setText(LumaQQ.getString("richbox.menu.cut"));
        mi.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                cut();
            }
        });
        mi = new MenuItem(contextMenu, SWT.PUSH);
        mi.setText(LumaQQ.getString("richbox.menu.paste"));
        mi.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                paste();
            }
        });
        mi = new MenuItem(contextMenu, SWT.SEPARATOR);
        mi = new MenuItem(contextMenu, SWT.PUSH);
        mi.setText(LumaQQ.getString("richbox.menu.select.all"));
        mi.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                doSelectAll();
            }
        });
        mi = new MenuItem(contextMenu, SWT.PUSH);
        mi.setText(LumaQQ.getString("richbox.menu.clear"));
        mi.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                clear();
            }
        });
        
        contextMenu.addMenuListener(new MenuAdapter() {
            public void menuShown(MenuEvent e) {
                contextMenu.getItem(0).setEnabled(hasSelection());
                contextMenu.getItem(1).setEnabled(!readonly & hasSelection());
                contextMenu.getItem(2).setEnabled(!readonly);
            }
        });
    }

    /**
	 * Adds a verify key listener. A VerifyKey event is sent by the widget when
	 * a key is pressed. The widget ignores the key press if the listener sets
	 * the doit field of the event to false.
	 * <p>
	 * 
	 * @param listener
	 *            the listener
	 * @exception SWTException
	 *                <ul>
	 *                <li>ERROR_WIDGET_DISPOSED - if the receiver has been
	 *                disposed</li>
	 *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
	 *                thread that created the receiver</li>
	 *                </ul>
	 * @exception IllegalArgumentException
	 *                <ul>
	 *                <li>ERROR_NULL_ARGUMENT when listener is null</li>
	 *                </ul>
	 */
	public void addVerifyKeyListener(VerifyKeyListener listener) {
		checkWidget();
		if (listener == null) {
			SWT.error(SWT.ERROR_NULL_ARGUMENT);
		}
		RichBoxListener typedListener = new RichBoxListener(listener);
		addListener(VerifyKey, typedListener);
	}

    /**
     * 装载事件监听器
     */
    private void installListeners() {
		listener = new Listener() {
			public void handleEvent(Event event) {
				switch (event.type) {
				case SWT.Dispose:
					handleDispose(event);
					break;
				case SWT.KeyDown:
					handleKeyDown(event);
					break;
				case SWT.MouseDown:
					handleMouseDown(event);
					break;
				case SWT.MouseUp:
					handleMouseUp(event);
					break;
				case SWT.MouseDoubleClick:
					handleMouseDoubleClick(event);
					break;
				case SWT.MouseMove:
					handleMouseMove(event);
					break;
				case SWT.Paint:
					handlePaint(event);
					break;
				case SWT.Resize:
					handleResize(event);
					break;
				case SWT.Traverse:
					handleTraverse(event);
					break;
				}
			}
		};
		ScrollBar verticalBar = getVerticalBar();
		if (verticalBar != null) {
			verticalBar.addListener(SWT.Selection, new Listener() {
				public void handleEvent(Event event) {
					handleVerticalScroll(event);
				}
			});
		}
		addListener(SWT.Dispose, listener);
		addListener(SWT.KeyDown, listener);
		addListener(SWT.MouseDown, listener);
		addListener(SWT.MouseUp, listener);
		addListener(SWT.MouseDoubleClick, listener);
		addListener(SWT.MouseMove, listener);
		addListener(SWT.Paint, listener);
		addListener(SWT.Resize, listener);
		addListener(SWT.Traverse, listener);
		addDisposeListener(animationManager);
		addDisposeListener(imageResolver);
    }

    /**
     * @param event
     */
    protected void handleVerticalScroll(Event event) {
		setVerticalScrollOffset(getVerticalBar().getSelection(), false);
    }

	/**
	 * Scrolls the widget vertically.
	 * <p>
	 * 
	 * @param pixelOffset
	 *            the new vertical scroll offset
	 * @param adjustScrollBar
	 *            true= the scroll thumb will be moved to reflect the new scroll
	 *            offset. false = the scroll thumb will not be moved
	 * @return true=the widget was scrolled false=the widget was not scrolled,
	 *         the given offset is not valid.
	 */
	private boolean setVerticalScrollOffset(int pixelOffset, boolean adjustScrollBar) {
		Rectangle clientArea;
		ScrollBar verticalBar = getVerticalBar();

		if (pixelOffset == verticalScrollOffset) {
			return false;
		}
		if (verticalBar != null && adjustScrollBar) {
			verticalBar.setSelection(pixelOffset);
		}
		int h = clientAreaHeight - topMargin - bottomMargin;
		int w = clientAreaWidth - leftMargin - rightMargin;
		int delta = pixelOffset - verticalScrollOffset;
		int srcY = (delta > 0) ? delta : 0;
		int destY = (delta > 0) ? 0 : -delta;
		delta = Math.abs(delta);
		int copyHeight = h - delta;
	    scroll(leftMargin, topMargin + destY, leftMargin, topMargin + srcY, w, copyHeight, true);
	    int gap = (delta << 1) - h;
	    if(gap > 0)
	        redraw(leftMargin, copyHeight + topMargin, w, gap, true);

		verticalScrollOffset = pixelOffset;
		calculateTopLine();
		setCaretLocation();
		
		if(verticalScrollOffset == 0)
		    setScrollBars();
		return true;
	}
	
	/**
	 * 设置光标位置, 这个方法会修改光标X位置
	 */
	private void setCaretLocation() {
		int lineIndex = content.getLineAtOffset(caretOffset);
		int lineOffset = content.getLineStartOffset(lineIndex);
		int offsetInLine = caretOffset - lineOffset;
		int newCaretX = contentHelper.getXAtOffset(getGC(), lineIndex, offsetInLine);
		releaseGC();
		setCaretLocation(newCaretX, lineIndex);
	}
	
    /**
     * 设置光标位置和大小,这个方法会修改光标X位置
     * 
     * @param newCaretX
     * 		光标的X象素位置,不包括左边缘
     * @param lineIndex
     * 		光标所在行号
     */
    private void setCaretLocation(int newCaretX, int lineIndex) {
        int y = contentHelper.getHeightOfLines(getGC(), 0, lineIndex) - verticalScrollOffset + topMargin;
        int h = contentHelper.getLineHeight(getGC(), lineIndex);
        releaseGC();
        releaseGC();
        // 如果光标和上下边缘有所重叠,则调整光标的大小
        if(y < topMargin && topMargin - y < h) {
            h -= topMargin - y;

⌨️ 快捷键说明

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