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

📄 qtree.java

📁 lumaQQ的源文件
💻 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.qstyle;

import static java.lang.Math.max;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Text;

import edu.tsinghua.lumaqq.widgets.BaseComposite;

/**
 * 实现一个QQ那样的树型控件
 * 
 * @author luma
 */
public class QTree extends BaseComposite {
	// 动画效果的runnable
	private Runnable animateRunnable = new Runnable() {
		public void run() {
			synchronized(animateList) {
				int size = animateList.size();
				for(int i = 0; i < size; i++) 
					updateItem(animateList.get(i));
				
				frame++;
				if(size > 0)
					getDisplay().timerExec(ANIMATION_INTERVAL, this);
			}
		}
	};
	
	static final QItem[] EMPTY_ITEMS = new QItem[0];
	static final int MAX_DEEPTH = 16;
	static final int ITEM_TOP_MARGIN = 3;
	static final int ITEM_BOTTOM_MARGIN = 3;
	static final int ITEM_IMAGE_TEXT_SPACING = 3;
	static final int ITEM_LEFT_MARING = 2;
	static final int ITEM_RIGHT_MARGIN = 2;
	static final int PREFIX_ICON_SPACING = 2; 
	static final char TAB = '\t';
	
    // 汉字声母与GB2312编码对照表
    private static char[][] pyTable = new char[][] {
            {0xB0A1, 0xB0C4, 'a'},
            {0xB0C5, 0xB2C0, 'b'},
            {0xB2C1, 0xB4ED, 'c'},
            {0xB4EE, 0xB6E9, 'd'},
            {0xB6EA, 0xB7A1, 'e'},
            {0xB7A2, 0xB8C0, 'f'},
            {0xB8C1, 0xB9FD, 'g'},
            {0xB9FE, 0xBBF6, 'h'},
            {0xBBF7, 0xBFA5, 'j'},
            {0xBFA6, 0xC0AB, 'k'},
            {0xC0AC, 0xC2E7, 'l'},
            {0xC2E8, 0xC4C2, 'm'},
            {0xC4C3, 0xC5B5, 'n'},
            {0xC5B6, 0xC5BD, 'o'},
            {0xC5BE, 0xC6D9, 'p'},
            {0xC6DA, 0xC8BA, 'q'},
            {0xC8BB, 0xC8F5, 'r'},
            {0xC8F6, 0xCBF9, 's'},
            {0xCBFA, 0xCDD9, 't'},
            {0xCDDA, 0xCEF3, 'w'},
            {0xCEF4, 0xD1B8, 'x'},
            {0xD1B9, 0xD4D0, 'y'},
            {0xD4D1, 0xD7F9, 'z'}
    };
	
	private static final int ANIMATION_INTERVAL = 500;
	
	private QItem[] items;
	private int itemCount;
	private List<QItem> expandCache;
	
	// item的图标文字布局和图标大小数组,按照层次关系保存,顶层的由itemLayouts[0]指定,等等
	private ItemLayout[] itemLayouts;
	private int[] itemImageSizes;
	
	int verticalOffset;
	int fontHeight;
	
	private int clientAreaHeight;
	private int clientAreaWidth;
	private int levelIndent;
	
	private int deferredHeight;
	
    // 事件监听器
    private Listener listener;
    private List<IQTreeListener> qtreeListeners;
	
	private QItem itemUnderMouse;
	private boolean enableIconHover;
	
	private int frame;
	List<QItem> animateList;
	
	private boolean editable;
	private QTreeEditor editor;
	
	// 快速查找结果缓冲
	private List<QItem> findResult;
	private int nextResult;
	// 上一次快速查找所用的字符
	private char lastChar;
	
	// 用什么键打开/收起item
	private int itemOpenOnButton;
	private boolean expandItemOnSingleClick;
	
	// 按键处理的映射表,key是按键值,value是动作的id
	private Map<Integer, Integer> keyActionMap;	
	// 用户自定义按键的映射表,key是按键值,vlaue是Runnable对象
	private Map<Integer, Runnable> userActionMap;
	
	/** 用户自定义按键 */
	public static final int CUSTOM_ACTION = 1;
	/** 下翻页 */
	public static final int PAGE_DOWN = 2;
	/** 上翻页 */
	public static final int PAGE_UP = 3;
	/** 到最顶 */
	public static final int GO_TOP = 4;
	/** 到最底 */
	public static final int GO_BOTTOM = 5;
	
	// 是否是Mac
	private static final boolean IS_CARBON;
	static {
	    // 如果不是Mac,底层也不是gtk,则做双缓冲
		String platform = SWT.getPlatform();
		IS_CARBON = "carbon".equals(platform);
	}
	
	/**
	 * 创建一个QTree对象
	 * 
	 * @param parent
	 * 		父容器
	 */
	public QTree(Composite parent) {
		super(parent, SWT.V_SCROLL | SWT.DOUBLE_BUFFERED);
		itemCount = 0;
		items = new QItem[16];
		itemLayouts = new ItemLayout[MAX_DEEPTH];
		itemImageSizes = new int[MAX_DEEPTH];
		verticalOffset = 0;
		fontHeight = calculateFontHeight();
		clientAreaHeight = clientAreaWidth = 0;
		deferredHeight = 0;
		enableIconHover = true;
		itemUnderMouse = null;
		animateList = new ArrayList<QItem>();
		findResult = new ArrayList<QItem>();
		lastChar = 0;
		nextResult = 0;
		levelIndent = 16;
		keyActionMap = new HashMap<Integer, Integer>();
		userActionMap = new HashMap<Integer, Runnable>();		
		itemOpenOnButton = SWT.BUTTON1;
		expandItemOnSingleClick = true;
		qtreeListeners = new ArrayList<IQTreeListener>();
		editable = true;
		expandCache = new ArrayList<QItem>();
		
		for(int i = 0; i < MAX_DEEPTH; i++) {
			itemLayouts[i] = ItemLayout.HORIZONTAL;
			itemImageSizes[i] = 16;
		}
		
		installListeners();
		createKeyBindings();
		
		editor = new QTreeEditor(this);
	}
	
    /**
     * 装载事件监听器
     */
    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.MouseExit:
					handleMouseExit(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.MouseExit, listener);
		addListener(SWT.Paint, listener);
		addListener(SWT.Resize, listener);
		addListener(SWT.Traverse, listener);
    }
	
	/**
	 * 处理鼠标按下事件
	 * 
	 * @param event
	 */
	protected void handleMouseDown(Event event) {
		if(editor.getEditor() != null) {
			Text t = (Text)editor.getEditor();
			editor.getItem().setText(t.getText());
		}
		disposeEditor();
		setFocus();
	}
	
	/**
	 * 如果有编辑器打开,关闭它
	 */
	private void disposeEditor() {
		if(editor.getEditor() != null)
			editor.getEditor().dispose();
		editor.setEditor(null);
		editor.setItem(null);
	}

	/**
	 * 处理鼠标移出事件
	 * 
	 * @param event
	 */
	protected void handleMouseExit(Event event) {
		// 恢复旧item的状态
		if(itemUnderMouse != null) {
			itemUnderMouse.clearHitTestFlag();
			itemUnderMouse = null;
		}
	}

	/**
	 * 处理垂直滚动事件
	 * 
	 * @param event
	 */
	protected void handleVerticalScroll(Event event) {
		// 检查是否和现有值一样
		ScrollBar verticalBar = getVerticalBar();
		if(verticalBar == null)
			return;		
		int newOffset = verticalBar.getSelection();
		if(newOffset == verticalOffset)
			return;
		
		setVerticalOffset(newOffset, false);
	}

	/**
	 * 设置垂直位移
	 * 
	 * @param newOffset
	 * 		新的位移值
	 */
	private void setVerticalOffset(int newOffset, boolean adjustVerticalBar) {
		// 设置新位置
		ScrollBar bar = getVerticalBar();
		if(bar == null)
			return;
		if(adjustVerticalBar)
			bar.setSelection(newOffset);
		
		// 校验范围
		if(newOffset > bar.getMaximum() - bar.getThumb())
			newOffset = bar.getMaximum() - bar.getThumb();
		if(newOffset < 0)
			newOffset = 0;
		
		// 滚动		
		int delta = newOffset - verticalOffset;
		int srcY = (delta > 0) ? delta : 0;
		int destY = (delta > 0) ? 0 : -delta;
		delta = Math.abs(delta);
		int copyHeight = clientAreaHeight - delta;
		scroll(0, destY, 0, srcY, clientAreaWidth, copyHeight);
	    int gap = (delta << 1) - clientAreaHeight;
	    if(gap > 0)
	        redraw(0, copyHeight, clientAreaWidth, gap, true);

		verticalOffset = newOffset;
		if(verticalOffset > 0 && !bar.getVisible()) {
			bar.setMaximum(getTotalHeight() + clientAreaHeight - getItemHeight(0));
			bar.setPageIncrement(clientAreaHeight);
			bar.setThumb(clientAreaHeight);
			bar.setIncrement(10);
			bar.setVisible(true);
		} else if(verticalOffset == 0 && bar.getVisible() && getTotalHeight() <= clientAreaHeight) {
			bar.setVisible(false);
		}
	}

	/**
	 * 滚动客户区
	 * 
	 * @param destX
	 * @param destY
	 * @param srcX
	 * @param srcY
	 * @param width
	 * @param height
	 */
	private void scroll(int destX, int destY, int srcX, int srcY, int width, int height) {
		GC gc = new GC(this);
		gc.copyArea(srcX, srcY, width, height, destX, destY);
		gc.dispose();
		
		if(clientAreaHeight - height >= height)
			redraw(srcX, srcY, width, height, true);
		else {
			int h = clientAreaHeight - height;
			int oldDeferredHeight = deferredHeight;
			deferredHeight += h;
			redraw(srcX, (srcY >= destY) ? (height - oldDeferredHeight) : srcY, width, h + oldDeferredHeight, true);
		}
	}

	/**
	 * 处理按钮遍历事件
	 * 
	 * @param event
	 */
	protected void handleTraverse(Event event) {
		switch (event.detail) {
			case SWT.TRAVERSE_PAGE_NEXT:
			case SWT.TRAVERSE_PAGE_PREVIOUS:
				event.doit = false;
				break;
			default:
				event.doit = true;
				break;
		}
	}

	/**
	 * 处理resize事件
	 * 
	 * @param event
	 */
	protected void handleResize(Event event) {		
		Rectangle clientArea = getClientArea();
		int oldHeight = clientAreaHeight;		
		clientAreaHeight = clientArea.height;
		clientAreaWidth = clientArea.width;
		
		if(oldHeight != clientAreaHeight)
			setScrollBar();
	}

	/**
	 * 处理重画事件
	 * 
	 * @param event
	 */
	protected void handlePaint(Event event) {
		// Check if there is work to do
		Rectangle clientArea = getClientArea();
		if (event.height <= 0)
			return;
		if (clientArea.width == 0 || clientArea.height == 0)
			return;
		
		// get gc and other info
		QItem item = getItemAtY(event.y + verticalOffset);
		int paintY = (item == null) ? event.y : (getYAtItem(item) - verticalOffset);
		int endY = event.y + event.height;			
		GC gc = event.gc;
		
		// 如果gtk设置颜色时出现错误,则先用背景色重画所有部分
		if(isGTKColorError()) {
			gc.setBackground(getBackground());
			gc.fillRectangle(0, paintY, clientArea.width, endY - paintY);
		}
		
		// draw item
		while(paintY <= endY && item != null) {
			item.onPaint(gc, paintY, frame);
			paintY += getItemHeight(item.getLevel());
			item = nextItem(item);
		}
		if(!isGTKColorError() && paintY < endY) {

⌨️ 快捷键说明

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