📄 qitem.java
字号:
/*
* 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 edu.tsinghua.lumaqq.widgets.qstyle.QTree.*;
import static java.lang.Math.max;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
/**
* QTree的item
*
* @author luma
*/
public class QItem extends Item {
static final String ELLIPSIS = "...";
static final int MAX_ATTACHMENT = 4;
static final int ATTACHMENT_SPACING = 3;
static final Rectangle DUMMY_BOUND = new Rectangle(0, 0, 0, 0);
private QTree parent;
private QItem parentItem;
private QItem[] items;
private int itemCount;
private boolean expanded;
private int level;
private Color foreground;
private boolean mouseOnIcon;
private int mouseOnAttachment;
private boolean mouseOnText;
private int textWidth;
boolean needRedraw;
boolean animating;
IEffect effect;
// 装饰图标,装饰图标是指位于原始图标边框范围之内,一般放置于右下角的小图片
private Image decorationImage;
// 附件,附件不和原始图标重合,而是显示在其他地方,做为对原始图标的补充
private Image[] attachment;
private Rectangle[] attachmentBound;
// 前缀,放在图标的前面,可以用来放置如加号减号表示展开收起
private Image prefix;
// 主图标边框
Rectangle imageBound;
/**
* 创建一个新的QTree根节点
*
* @param parent
* QTree
* @param style
* 样式
*/
public QItem(QTree parent, int style) {
super(parent, style);
this.parent = parent;
this.parentItem = null;
this.level = 0;
this.itemCount = 0;
this.items = new QItem[16];
this.expanded = false;
this.attachment = new Image[MAX_ATTACHMENT];
this.attachmentBound = new Rectangle[MAX_ATTACHMENT];
this.prefix = null;
this.mouseOnIcon = false;
this.needRedraw = false;
this.mouseOnAttachment = -1;
this.mouseOnText = false;
this.animating = false;
this.textWidth = 0;
this.imageBound = (getImage() == null) ? DUMMY_BOUND : getImage().getBounds();
parent.addItem(this);
}
/**
* 创建一个新的QTree子项
*
* @param parentItem
* 父item
* @param style
* 样式
*/
public QItem(QItem parentItem, int style) {
super(checkNull(parentItem).parent, style);
this.parent = parentItem.parent;
this.parentItem = parentItem;
this.level = parentItem.getLevel() + 1;
this.itemCount = 0;
this.items = new QItem[16];
this.expanded = false;
this.attachment = new Image[MAX_ATTACHMENT];
this.attachmentBound = new Rectangle[MAX_ATTACHMENT];
this.prefix = null;
this.mouseOnIcon = false;
this.mouseOnAttachment = -1;
this.animating = false;
this.imageBound = (getImage() == null) ? DUMMY_BOUND : getImage().getBounds();
parentItem.addItem(this);
}
/**
* 添加一个子item
*
* @param item
*/
private void addItem(QItem item) {
internalAddItem(item);
}
/**
* 检查是否为空
*
* @param item
* QItem
* @return
* 如果不为空,返回被检查的对象
* @exception SWTException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
*/
static QItem checkNull(QItem item) {
if (item == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
return item;
}
/**
* 画item
*
* @param gc
* GC对象
* @param y
* item左上角y坐标,相对于父控件来说
* @param frame
* 帧数,动画状态时才有意义
*/
void onPaint(GC gc, int y, int frame) {
textWidth = gc.textExtent(getText()).x;
ItemLayout layout = parent.getLevelLayout(level);
if(animating && effect != null)
effect.onPaint(this, gc, layout, y, frame);
else {
switch(layout) {
case HORIZONTAL:
paintHorizontal(gc, y);
break;
case VERTICAL:
paintVertical(gc, y);
break;
default:
SWT.error(SWT.ERROR_INVALID_RANGE);
break;
}
}
}
/**
* 画垂直样式的item
*
* @param gc
* GC
* @param y
* item边框左上角y坐标
*/
void paintVertical(GC gc, int y) {
int size = parent.getLevelImageSize(level);
int x = parent.getItemIndent(level);
int itemHeight = parent.getItemHeight(level);
int itemWidth = parent.getItemWidth(level);
int imageX = x + (max(0, itemWidth - size) >>> 1);
int imageY = y + ITEM_TOP_MARGIN;
int prefixX = 0;
int prefixY = 0;
if(prefix != null) {
Rectangle prefixBound = prefix.getBounds();
prefixX = imageX - PREFIX_ICON_SPACING - prefixBound.width;
prefixY = imageY + ((size - prefixBound.height) >> 1);
}
// 画前缀
if(prefix != null)
paintPrefix(gc, getPrefix(), prefixX, prefixY);
// 画图标
paintIcon(gc, getImage(), imageBound, imageX, imageY, size);
// 画文本
paintVerticalText(gc, x, y, itemWidth, itemHeight, foreground);
// 画附件
paintVerticalAttachment(gc, x, y, itemWidth, itemHeight, imageX, imageY, size);
}
/**
* 画水平样式的item
*
* @param gc
* GC
* @param y
* item边框左上角y坐标
*/
void paintHorizontal(GC gc, int y) {
int size = parent.getLevelImageSize(level);
int x = parent.getItemIndent(level);
int itemHeight = parent.getItemHeight(level);
int itemWidth = parent.getItemWidth(level);
int imageX = x + ITEM_LEFT_MARING;
int imageY = y + (max(0, itemHeight - size) >>> 1);
int prefixX = 0;
int prefixY = 0;
if(prefix != null) {
Rectangle prefixBound = prefix.getBounds();
prefixX = imageX;
prefixY = imageY + ((itemHeight - prefixBound.height) >> 1);
imageX += prefixBound.width + PREFIX_ICON_SPACING;
}
// 画前缀
if(prefix != null)
paintPrefix(gc, getPrefix(), prefixX, prefixY);
// 画图标
paintIcon(gc, getImage(), imageBound, imageX, imageY, size);
// 画文本
paintHorizontalText(gc, x, y, itemWidth, itemHeight, foreground);
// 画附件
paintHorizontalAttachment(gc, x, y, itemWidth, itemHeight, imageX, imageY, size);
}
/**
* 画前缀图标
*
* @param gc
* @param image
* @param prefixX
* @param prefixY
*/
void paintPrefix(GC gc, Image image, int prefixX, int prefixY) {
if(image != null)
gc.drawImage(image, prefixX, prefixY);
}
/**
* 画垂直模式时的文本
*
* @param gc
* GC
* @param x
* item左上角x坐标
* @param y
* item左上角y坐标
* @param itemWidth
* item宽度
* @param itemHeight
* item高度
* @param fg
* 前景色
*/
void paintVerticalText(GC gc, int x, int y, int itemWidth, int itemHeight, Color fg) {
if(getText() != null) {
// 调整文本的宽度
String text = getText();
Color oldForeground = gc.getForeground();
if(fg != null)
gc.setForeground(fg);
gc.drawString(text,
Math.max(5, (itemWidth - textWidth) >> 1),
y + ITEM_TOP_MARGIN + parent.getLevelImageSize(level) + ITEM_IMAGE_TEXT_SPACING,
true);
gc.setForeground(oldForeground);
}
}
/**
* 画水平模式时的文本
*
* @param gc
* GC
* @param x
* item左上角x坐标
* @param y
* item左上角y坐标
* @param itemWidth
* item宽度
* @param itemHeight
* item高度
* @param fg
* 前景色
*/
void paintHorizontalText(GC gc, int x, int y, int itemWidth, int itemHeight, Color fg) {
if(getText() != null) {
// 调整文本的宽度
int textX = x + ITEM_LEFT_MARING + parent.getLevelImageSize(level) + ITEM_IMAGE_TEXT_SPACING;
if(prefix != null)
textX += PREFIX_ICON_SPACING + prefix.getBounds().width;
String text = getText();
Color oldForeground = gc.getForeground();
if(fg != null)
gc.setForeground(fg);
gc.drawString(text,
textX,
y + ((itemHeight - parent.fontHeight) >>> 1),
true);
gc.setForeground(oldForeground);
}
}
/**
* 画主图标
*
* @param gc
* GC
* @param imgBound
* 主图标矩形
* @param imageX
* 主图标左上角
* @param imageY
* 主图标右上角
* @param size
* 图标实际尺寸
*/
void paintIcon(GC gc, Image image, Rectangle imgBound, int imageX, int imageY, int size) {
// 画图标
if(image != null) {
gc.drawImage(image,
0,
0,
imgBound.width,
imgBound.height,
imageX,
imageY,
size,
size);
paintDecoration(gc, imgBound, imageX, imageY, size);
}
// 画图标边框
if(parent.isEnableIconHover() && mouseOnIcon)
paintHoverBorder(gc, imageX - 1, imageY - 1, size + 1, size + 1);
}
/**
* 画附件图标
*
* @param gc
* GC
* @param x
* item的左上角x坐标
* @param y
* item的左上角y坐标
* @param itemWidth
* item的宽度
* @param itemHeight
* item的高度
* @param imageX
* 图标的x位置
* @param imageY
* 图标的y位置
* @param size
* 最终的图标大小
*/
void paintHorizontalAttachment(GC gc, int x, int y, int itemWidth, int itemHeight, int imageX, int imageY, int size) {
int attX = x + itemWidth - ITEM_RIGHT_MARGIN;
for(int i = 0; i < MAX_ATTACHMENT; i++) {
if(attachment[i] == null)
continue;
// 计算附件图标的位置
int attY = y + ((itemHeight - attachmentBound[i].height) >>> 1);
attX -= attachmentBound[i].width;
// 如果附件图标和原始图标出现了重叠,不再画
if(attX < imageX + size)
break;
// 画附件
gc.drawImage(attachment[i],
0,
0,
attachmentBound[i].width,
attachmentBound[i].height,
attX,
attY,
attachmentBound[i].width,
attachmentBound[i].height);
// 画边框
if(mouseOnAttachment == i)
paintHoverBorder(gc, attX - 1, attY - 1, attachmentBound[i].width + 1, attachmentBound[i].height + 1);
attX -= ATTACHMENT_SPACING;
}
}
/**
* 画附件图标
*
* @param gc
* GC
* @param x
* item的左上角x坐标
* @param y
* item的左上角y坐标
* @param itemWidth
* item的宽度
* @param itemHeight
* item的高度
* @param imageX
* 图标的x位置
* @param imageY
* 图标的y位置
* @param size
* 最终的图标大小
*/
void paintVerticalAttachment(GC gc, int x, int y, int itemWidth, int itemHeight, int imageX, int imageY, int size) {
int evenX = imageX - ATTACHMENT_SPACING;
int oddX = imageX + size + ATTACHMENT_SPACING;
for(int i = 0; i < MAX_ATTACHMENT; i++) {
if(attachment[i] == null)
continue;
// 计算附件图标的位置
int attX, attY;
if(i % 2 == 0) {
evenX -= attachmentBound[i].width;
attX = evenX;
} else {
attX = oddX;
oddX += attachmentBound[i].width + ATTACHMENT_SPACING;
}
attY = imageY + size - attachmentBound[i].height;
// 画附件
gc.drawImage(attachment[i],
0,
0,
attachmentBound[i].width,
attachmentBound[i].height,
attX,
attY,
attachmentBound[i].width,
attachmentBound[i].height);
// 画边框
if(mouseOnAttachment == i)
paintHoverBorder(gc, attX - 1, attY - 1, attachmentBound[i].width + 1, attachmentBound[i].height + 1);
}
}
/**
* 画装饰图标
*
* @param gc
* GC
* @param imgBound
* 原始图标尺寸
* @param imageX
* 实际图标x位置
* @param imageY
* 实际图标y位置
* @param size
* 实际图标尺寸
*/
void paintDecoration(GC gc, Rectangle imgBound, int imageX, int imageY, int size) {
if(decorationImage == null)
return;
Rectangle decorationBound = decorationImage.getBounds();
int visualWidth, visualHeight;
if(decorationBound.width > size || decorationBound.height > size) {
visualWidth = decorationBound.width * size / imgBound.width;
visualHeight = decorationBound.height * size / imgBound.height;
} else {
visualWidth = decorationBound.width;
visualHeight = decorationBound.height;
}
int decorationX = imageX + size - visualWidth;
int decorationY = imageY + size - visualHeight;
gc.drawImage(decorationImage,
0,
0,
decorationBound.width,
decorationBound.height,
decorationX,
decorationY,
visualWidth,
visualHeight);
}
/**
* 得到某个子item的索引
*
* @param item
* 子item对象
* @return
* 索引
*/
public int indexOf(QItem item) {
checkWidget();
for(int i = 0; i < itemCount; i++) {
if(items[i] == item)
return i;
}
return -1;
}
/**
* @return Returns the parent.
*/
public QTree getParent() {
return parent;
}
/**
* @return Returns the parentItem.
*/
public QItem getParentItem() {
return parentItem;
}
/**
* @return
* true表示这个item处于一个可见状态,可见状态也就是指它的
* 所有父节点都是展开的
*/
boolean isLogicalVisible() {
QItem item = parentItem;
while(item != null) {
if(!item.isExpanded())
return false;
item = item.getParentItem();
}
return true;
}
/**
* @return Returns the expanded.
*/
public boolean isExpanded() {
checkWidget();
return expanded;
}
/**
* 改变item的展开状态
*
* @param expanded
* true表示展开item
*/
public void setExpanded(boolean expanded) {
setExpanded(expanded, true);
}
/**
* 改变item的展开状态
*
* @param expanded
* true表示展开item
* @param redraw
* true表示立刻重画
*/
public void setExpanded(boolean expanded, boolean redraw) {
checkWidget();
if(this.expanded != expanded) {
this.expanded = expanded;
if(redraw)
parent.updateExpanded(this);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -