GEF常见问题6:使用对话框

除了利用Eclipse提供的属性视图以外,GEF应用程序里当然也可以通过弹出对话框修改模型信息。

要实现双击一个节点打开对话框,在NodePart里要增加的代码如下:

public void performRequest(Request req) {
    if(req.getType().equals(RequestConstants.REQ_OPEN)){
        MessageDialog.openInformation(getViewer().getControl().getShell(),"Gef Practice","A Dialog");
    }
}

作为例子,上面这段代码只打开一个显示信息的对话框,你可以替换成自己实现的对话框显示/修改节点信息。

在CreateNodeCommand里增加下面的代码,可以在每次创建一个节点时通过对话框指定节点的名称:

public void execute() {
    InputDialog dlg = new InputDialog(shell, "Gef Practice", "New node's name:", "Node", null);
    if (Window.OK == dlg.open()) {
        this.node.setName(dlg.getValue());
    }
    this.diagram.addNode(this.node);
}

因为打开对话框时需要用到Shell,所以要在CreateNodeCommand里增加一个Shell类型的成员变量,并在DiagramLayoutEditPolicy里创建CreateNodeCommand时把一个shell实例传递给它。

file
创建节点时先弹出对话框

代码下载

点此下载工程,此工程修改自GEF应用实例中的GefPractice,目标文件的扩展名改为.gefpracticedlg。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/07/07/445603.html

GEF常见问题5:自动布局

利用自动布局功能,我们可以把本来不包含图形信息的文件以图形化的方式展示出来,典型的例子比如将一组Java接口反向工程为类图,那么图中每个图元的坐标应该必须都是自动生成的。GEF里提供了DirectedGraphLayout类用来实现自动布局功能,下面介绍一下怎样在程序里使用它。

DirectedGraphLayout提供的visit()方法接受一个org.eclipse.draw2d.graph.DirectedGraph实例,它遍历这个有向图的所有节点和边,并按照它自己的算法计算出每个节点布局后的新位置。所以在使用它布局画布上的图元分为两个步骤:1、构造有向图,2、将布局信息应用到图元。

还是以gefpractice为基础,我们在主工具条上增加了一个自动布局按钮,当用户按下它时自动布局编辑器里的图形,再次按下时恢复以前的布局。为了完成步骤1,我们要在DiagramPart里添加以下两个方法:

/**
 * 将图元(NodePart)转换为节点(Node)到有向图
 * @param graph
 * @param map
 */
public void contributeNodesToGraph(DirectedGraph graph, Map map) {
    for (int i = 0; i < getChildren().size(); i++) {
        NodePart node = (NodePart)getChildren().get(i);
        org.eclipse.draw2d.graph.Node n = new org.eclipse.draw2d.graph.Node(node);
        n.width = node.getFigure().getPreferredSize().width;
        n.height = node.getFigure().getPreferredSize().height;
        n.setPadding(new Insets(10,8,10,12));
        map.put(node, n);
        graph.nodes.add(n);
    }
}

/**
 * 将连接(ConnectionPart)转换为边(Edge)添加到有向图
 * @param graph
 * @param map
 */
public void contributeEdgesToGraph(DirectedGraph graph, Map map) {
    for (int i = 0; i < getChildren().size(); i++) {
        NodePart node = (NodePart)children.get(i);
        List outgoing = node.getSourceConnections();
        for (int j = 0; j < outgoing.size(); j++) {
            ConnectionPart conn = (ConnectionPart)outgoing.get(j);
            Node source = (Node)map.get(conn.getSource());
            Node target = (Node)map.get(conn.getTarget());
            Edge e = new Edge(conn, source, target);
            e.weight = 2;
            graph.edges.add(e);
            map.put(conn, e);
        }
    }
}

要实现步骤2,在DiagramPart里添加下面这个方法:

/**
 * 利用布局后的有向图里节点的位置信息重新定位画布上的图元
 * @param graph
 * @param map
 */
protected void applyGraphResults(DirectedGraph graph, Map map) {
    for (int i = 0; i < getChildren().size(); i++) {
        NodePart node = (NodePart)getChildren().get(i);
        Node n = (Node)map.get(node);
        node.getFigure().setBounds(new Rectangle(n.x, n.y, n.width, n.height));
    }
}

为了以最少的代码说明问题,上面的方法里只是简单的移动了图形,而没有改变模型里Node的属性值,在大多情况下这里使用一个CompoundCommand对模型进行修改更为合适,这样用户不仅可以撤消(Undo)这个自动布局操作,还可以在重新打开文件时看到关闭前的样子。注意,DirectedGraphLayout是不保证每次布局都得到完全相同的结果的。

因为Draw2D里是用LayoutManager管理布局的,而DirectedGraphLayout只是对布局算法的一个包装,所以我们还要创建一个布局类。GraphLayoutManager调用我们在上面已经添加的那几个方法生成有向图(partsToNodes变量维护了编辑器图元到有向图图元的映射),利用DirectedGraphLayout对这个有向图布局,再把结果应用到编辑器里图元。如下所示:

class GraphLayoutManager extends AbstractLayout {

    private DiagramPart diagram;

    GraphLayoutManager(DiagramPart diagram) {
        this.diagram = diagram;
    }

    protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
        container.validate();
        List children = container.getChildren();
        Rectangle result = new Rectangle().setLocation(container.getClientArea().getLocation());
        for (int i = 0; i < children.size(); i++)
            result.union(((IFigure) children.get(i)).getBounds());
        result.resize(container.getInsets().getWidth(), container.getInsets().getHeight());
        return result.getSize();
    }

    public void layout(IFigure container) {
        DirectedGraph graph = new DirectedGraph();
        Map partsToNodes = new HashMap();
        diagram.contributeNodesToGraph(graph, partsToNodes);
        diagram.contributeEdgesToGraph(graph, partsToNodes);
        new DirectedGraphLayout().visit(graph);
        diagram.applyGraphResults(graph, partsToNodes);
    }

}

当用户按下自动布局按钮时,只要设置DiagramPart对应的图形的布局管理器为GraphLayoutManager就可以实现自动布局了,布局的结果如图所示。

file
自动布局的结果

最后有几点需要说明:

1、DirectedGraphLayout只能对连通的有向图进行布局,否则会产生异常graph is not fully connected,参考Eclipse.org文章Building a Database Schema Diagram Editor中使用的NodeJoiningDirectedGraphLayout可以解决这个问题;

2、如果要对具有嵌套关系的有向图自动布局,应使用SubGraphCompoundDirectedGraphLayout

3、这个版本的gefpractice在自动布局后,没有对连接线进行处理,所以可能会出现连接线穿过图元的情况,这个问题和上一个问题都可以参考GEF提供的flow例子解决。

Update(2007/4/9):如果diagram是放在ScrollPane里的,则要修改一下applyGraphResults()方法,增加container作为参数以使整个diagram能正确滚动。

protected void applyGraphResults(DirectedGraph graph, Map map, IFigure container) {
    for (Iterator iterator = this.nodeParts.iterator(); iterator.hasNext();) {
        NodePart element = (NodePart) iterator.next();
        Node n = (Node) map.get(element);
        Rectangle containerBounds=container.getBounds();
        Rectangle elementBounds=new Rectangle(n.x, n.y, n.width, n.height);
        element.getFigure().setBounds(elementBounds.translate(containerBounds.getLocation()));
    }
}

代码下载

点此下载工程,此工程修改自GEF应用实例中的GefPractice,目标文件的扩展名改为.gefpracticeal。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/07/02/440896.html

GEF常见问题4:非矩形图元

现在假设要把原来GefPractice例子里的矩形图元节点换成用椭圆形表示,都需要做哪些改动呢?很显然,首先要把原来继承RectangleFigure的NodeFigure类改为继承Ellipse类:

public class NodeFigure extends Ellipse {
    ...
}

这样修改后可以看到编辑器中的图元已经变成椭圆形了。但如果用户点选一个图元,表示选中的边框(选择框)仍然是矩形的,如图1所示:

file
图1 椭圆形的节点和矩形选择框

如果觉得矩形的选择框不太协调,可以通过覆盖DiagramLayoutEditPolicy的createChildEditPolicy()方法修改。缺省情况下这个方法返回一个ResizableEditPolicy,我们要定义自己的子类(EllipseResizableEditPolicy)来替代它作为返回值。

EllipseResizableEditPolicy里需要覆盖ResizableEditPolicy的两个方法。第一个是createSelectionHandles()方法,它决定“控制柄”(ResizeHandle)和“选择框”(MoveHandle)的相关情况,我们的实现如下:

protected List createSelectionHandles() {
    List list = new ArrayList();
    //添加选择框
    //ResizableHandleKit.addMoveHandle((GraphicalEditPart) getHost(), list);
    list.add(new MoveHandle((GraphicalEditPart) getHost()) {
        protected void initialize() {
            super.initialize();
            setBorder(new LineBorder(1) {
                public void paint(IFigure figure, Graphics graphics, Insets insets) {
                    tempRect.setBounds(getPaintRectangle(figure, insets));
                    if (getWidth() % 2 == 1) {
                        tempRect.width--;
                        tempRect.height--;
                    }
                    tempRect.shrink(getWidth() / 2, getWidth() / 2);
                    graphics.setLineWidth(getWidth());
                    if (getColor() != null)
                        graphics.setForegroundColor(getColor());
                    //用椭圆形替代矩形
                    //graphics.drawRectangle(tempRect);
                    graphics.drawOval(tempRect);
                }
            });
        }
    });

    //添加控制柄
    ResizableHandleKit.addHandle((GraphicalEditPart) getHost(), list, PositionConstants.EAST);
    ResizableHandleKit.addHandle((GraphicalEditPart) getHost(), list, PositionConstants.SOUTH);
    ResizableHandleKit.addHandle((GraphicalEditPart) getHost(), list, PositionConstants.WEST);
    ResizableHandleKit.addHandle((GraphicalEditPart) getHost(), list, PositionConstants.NORTH);
    return list;
}

第二个是createDragSourceFeedbackFigure()方法,它决定用户拖动图形时,随鼠标移动的半透明图形(即“鬼影”)的形状和颜色,因此我们覆盖这个方法以显示椭圆形的鬼影。

protected IFigure createDragSourceFeedbackFigure() {
    //用椭圆替代矩形
    //RectangleFigure r = new RectangleFigure();
    Ellipse r = new Ellipse();
    FigureUtilities.makeGhostShape(r);
    r.setLineStyle(Graphics.LINE_DOT);
    r.setForegroundColor(ColorConstants.white);
    r.setBounds(getInitialFeedbackBounds());
    addFeedback(r);
    return r;
}

经过以上这些修改,可以看到选择框和鬼影都是椭圆的了,如图2所示。

file
图2 与节点形状相同的选择框和鬼影

代码下载

点此下载工程,此工程修改自GEF应用实例中的GefPractice,目标文件的扩展名改为.gefpracticeel。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/06/26/436455.html

GEF常见问题3:自身连接

在类图里能看到一些对象具有对自己的引用,通常这些引用用于表达树状结构,即父子节点都是同一类对象。用GEF绘制这样的连接线一般是通过转折点(Bendpoint)实现的,如果你的GEF应用程序里还不能使用Bendpoint,请按照上一篇介绍的步骤添加对Bendpoint的支持。

原先我们的GefPractice应用程序是不允许一条连接线的起点和终点都是同一个图形的,因为这样会导致连接线缩成一个点隐藏在图形下方,用户并不知道它的存在。当时我们在CreateConnectionCommand类的canExecute()方法里进行了如下判断:

public boolean canExecute() {
    if (source.equals(target))
        return false;
    ...
}

因此现在首先要把这两句删除。然后在execute()方法里对自身连接的这种情况稍做处理,处理的方法是给这条连接线在适当位置增加三个Bendpoint,你也可以根据想要的连接线形状修改Bendpoint的数目和位置。

public void execute() {
    connection = new Connection(source, target);
    if (source == target) {
        //The start and end points of our connection are both at the center of the rectangle,
        //so the two relative dimensions are equal.
        ConnectionBendpoint cbp = new ConnectionBendpoint();
        cbp.setRelativeDimensions(new Dimension(0, -60), new Dimension(0, -60));
        connection.addBendpoint(0, cbp);
        ConnectionBendpoint cbp2 = new ConnectionBendpoint();
        cbp2.setRelativeDimensions(new Dimension(100, -60), new Dimension(100, -60));
        connection.addBendpoint(1, cbp2);
        ConnectionBendpoint cbp3 = new ConnectionBendpoint();
        cbp3.setRelativeDimensions(new Dimension(100, 0), new Dimension(100, 0));
        connection.addBendpoint(2, cbp3);
    }
}

现在用户只要选择连接工具,然后在一个节点上连续点两下就可以创建自身连接了,如下图所示。

file
图:自身连接

代码下载

点此下载工程,此工程修改自GEF常见问题2中的GefPractice-bp,目标文件扩展名为.gefpracticesc。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/06/22/432553.html

GEF常见问题2:具有转折点的连接线

从直线连接转换到可以任意增减转折点的折线连接,因为模型里要增加新的元素,所以模型、editpart和图形部分都要有所修改,显得稍微有些烦琐,但其实很多代码是通用的。这个过程主要分为以下几个部分:

1、在模型里增加转折点对应的类(这些转折点在GEF里称作Bendpoint),在类里要具有两个Dimension类型用来记录Bendpoint相对连接线起止点的位置。在连接类里要维护一个Bendpoint列表,并提供访问方法,由于篇幅关系这里只列出连接类中的这几个方法。

public void addBendpoint(int index, ConnectionBendpoint point) {
    getBendpoints().add(index, point);
    firePropertyChange(PROP_BENDPOINT, null, null);
}

/**
 * zhanghao: 为了在更新两个dimension后能发送事件,在MoveBendpointCommand要在用这个方法设置新坐标,
 * 而不是直接用BendPoint里的方法。
 */
public void setBendpointRelativeDimensions(int index, Dimension d1, Dimension d2){
    ConnectionBendpoint cbp=(ConnectionBendpoint)getBendpoints().get(index);
    cbp.setRelativeDimensions(d1,d2);
    firePropertyChange(PROP_BENDPOINT, null, null);
}

public void removeBendpoint(int index) {
    getBendpoints().remove(index);
    firePropertyChange(PROP_BENDPOINT, null, null);
}

2、在原来的连接方式里,由于连接线本身不需要刷新,所以现在要确保这个editpart实现了PropertyChangeListener接口,并像其他editpart一样覆盖了activate()deactivate()这两个方法,以便接收Bendpoint发生改变的事件。

public void activate() {
    super.activate();
    ((Connection)getModel()).addPropertyChangeListener(this);
}

public void deactivate() {
    super.deactivate();
    ((Connection)getModel()).removePropertyChangeListener(this);
}

public void propertyChange(PropertyChangeEvent event) {
    String property = event.getPropertyName();
    if(Connection.PROP_BENDPOINT.equals(property)){
        refreshBendpoints();
    }
}

为模型连接类对应的editpart里增加一个继承自BendpointEditPolicy的子类ConnectionBendPointEditPolicy,这个类的内容后面会说到。

protected void createEditPolicies() {
    ...
    installEditPolicy(EditPolicy.CONNECTION_BENDPOINTS_ROLE, new ConnectionBendPointEditPolicy());

直线连接的情况下,连接的刷新不需要我们负责,但增加了Bendpoint以后,必须在Bendpoint发生改变时刷新连接线的显示。所以在上面这个editpart的refreshVisuals()方法里需要增加一些代码,以便把模型里的Bendpoint转换为图形上的relativeBendpoint。

protected void refreshVisuals() {
    Connection conn = (Connection) getModel();
    List modelConstraint = conn.getBendpoints();
    List figureConstraint = new ArrayList();
    for (int i = 0; i < modelConstraint.size(); i++) {
        ConnectionBendpoint cbp = (ConnectionBendpoint) modelConstraint
                .get(i);
        RelativeBendpoint rbp = new RelativeBendpoint(getConnectionFigure());
        rbp.setRelativeDimensions(cbp.getFirstRelativeDimension(), cbp
                .getSecondRelativeDimension());
        rbp.setWeight((i + 1) / ((float) modelConstraint.size() + 1));
        figureConstraint.add(rbp);
    }
    getConnectionFigure().setRoutingConstraint(figureConstraint);
}

3、创建CreateBendpointCommand、MoveBendpointCommand和DeleteBendpointCommand这三个类,可以像Logic例子那样创建一个基类BendPointCommand让它们来继承。作为例子,BendpointCommand的内容如下。

public class BendpointCommand extends Command {

    protected int index;
    protected Connection connection;
    protected Dimension d1, d2;

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public void redo() {
        execute();
    }

    public void setRelativeDimensions(Dimension dim1, Dimension dim2) {
        d1 = dim1;
        d2 = dim2;
    }

    public void setIndex(int i) {
        index = i;
    }
}

4、在ConnectionBendPointEditPolicy里实现BendpointEditPolicy定义的创建、移动和删除Bendpoint的三个方法。

public class ConnectionBendPointEditPolicy extends BendpointEditPolicy {

    protected Command getCreateBendpointCommand(BendpointRequest request) {
        CreateBendpointCommand cmd = new CreateBendpointCommand();
        Point p = request.getLocation();
        Connection conn = getConnection();

        conn.translateToRelative(p);

        Point ref1 = getConnection().getSourceAnchor().getReferencePoint();
        Point ref2 = getConnection().getTargetAnchor().getReferencePoint();

        conn.translateToRelative(ref1);
        conn.translateToRelative(ref2);

        cmd.setRelativeDimensions(p.getDifference(ref1), p.getDifference(ref2));
        cmd.setConnection((com.example.model.Connection) request.getSource()
                .getModel());
        cmd.setIndex(request.getIndex());
        return cmd;
    }

    protected Command getDeleteBendpointCommand(BendpointRequest request) {
        BendpointCommand cmd = new DeleteBendpointCommand();
        Point p = request.getLocation();
        cmd.setConnection((com.example.model.Connection) request.getSource().getModel());
        cmd.setIndex(request.getIndex());
        return cmd;
    }

    protected Command getMoveBendpointCommand(BendpointRequest request) {
        MoveBendpointCommand cmd = new MoveBendpointCommand();
        Point p = request.getLocation();
        Connection conn = getConnection();

        conn.translateToRelative(p);

        Point ref1 = getConnection().getSourceAnchor().getReferencePoint();
        Point ref2 = getConnection().getTargetAnchor().getReferencePoint();

        conn.translateToRelative(ref1);
        conn.translateToRelative(ref2);

        cmd.setRelativeDimensions(p.getDifference(ref1), p.getDifference(ref2));
        cmd.setConnection((com.example.model.Connection) request.getSource()
                .getModel());
        cmd.setIndex(request.getIndex());
        return cmd;
    }
}

修改完成后的编辑器如下图所示。

file
图:编辑器中的转折连接线

代码下载

点此下载工程,此工程修改自GEF应用实例中的GefPractice,目标文件的扩展名改为.gefpracticebp。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/06/22/432227.html

GEF常见问题1:为图形编辑器设置背景图片

GEF的RootEditPart对应的Figure是一个由多个Layer组成的LayeredPane,每个Layer负责包含不同类型的图形元素,如节点、连接、网格线等等。所以要让图形编辑器显示一个图片作为背景,可以在它们其中一个层里绘制这个图片,也可以添加一个层专门放置背景图片。我推荐使用后者,以下代码是在前面的GefPractice项目基础上做了简单修改得到的:

static Image BG_IMAGE=new Image(null,"c:\\bg.jpg");

protected void configureGraphicalViewer() {
    super.configureGraphicalViewer();
    getGraphicalViewer().setRootEditPart(new ScalableFreeformRootEditPart() {

        //覆盖ScalableFreeformRootEditPart的createlayers方法以便增加自己的层
        protected void createLayers(LayeredPane layeredPane) {
            Layer layer = new FreeformLayer() {
                protected void paintFigure(Graphics graphics) {
                    super.paintFigure(graphics);
                    //在层上绘制图片,也可以绘制其他图形作为背景,GEF的网格线就是一例
                    graphics.drawImage(BG_IMAGE,0,0);
                }
            };
            layeredPane.add(layer);
            super.createLayers(layeredPane);
        }
    });
    getGraphicalViewer().setEditPartFactory(new PartFactory());
}

这样得到的背景图片只显示编辑器可见区域的部分,也就是会随滚动条滚动,见下图。

file
具有背景图片的图形编辑器

代码下载

工程下载(默认背景图片名为c:\bg.jpg可根据实际情况修改)

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/06/21/431774.html

自动换行的draw2d标签

Draw2D里的Label不支持自动换行,虽然可以插入换行符手动换行。用TextFlow和适当的Layout可以实现文字的自动换行。以下代码由sean朋友贡献,原文链接

class LabelEx extends FlowPage {

    private TextFlow contents;

    public LabelEx() {
        this("");
    }

    public LabelEx(String text) {
        contents = new TextFlow();
        contents.setLayoutManager(new ParagraphTextLayout(contents, ParagraphTextLayout.WORD_WRAP_SOFT));
        contents.setText(text);
        add(contents);
    }

    public void setText(String text) {
        contents.setText(text);
    }

    public String getText() {
        return contents.getText();
    }
}

[Eclipse]通过TreeColumn实现“表格树”TableTree

Eclipse 3.1里deprecate了TableTree这个控件,与之对应的jface的TableTreeViewer虽然没有deprecate,但使用它会得到很多警告。在TableTreeViewer的第一列里是不能显示图标的,因为这个位置被+/-符号占用了,而且TableTree是显示不出 Tree的层次的,也就是没有缩进。

SWT 3.1里的Tree控件新支持了列的显示,是通过TreeColumn来实现的。在jface里则没有添加新的viewer,使用原先的TreeViewer即可支持,下面是一段例子代码,注意如果在windows里运行要修改一下setInput()这条语句的参数,例如改为setInput(new File("c:\\"))

import java.io.File;

import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TreeColumn;

public class TreeColumnTest {

    public void run(){
        final Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());

        final TreeViewer viewer = new TreeViewer(shell, SWT.FULL_SELECTION);
        viewer.getTree().setHeaderVisible(true);
        TreeColumn column = new TreeColumn(viewer.getTree(), SWT.LEFT);
        column.setText("Name");
        column.setWidth(200);
        column = new TreeColumn(viewer.getTree(), SWT.LEFT);
        column.setText("Size");
        column.setWidth(100);
        column = new TreeColumn(viewer.getTree(), SWT.LEFT);
        column.setText("Hidden");
        column.setWidth(100);
        viewer.setContentProvider(new MyTreeContenetProvider());
        viewer.setLabelProvider(new MyTableLableProvider());
        viewer.setInput(new File("/"));

        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    public static void main(String[] args) {
        new TreeColumnTest().run();
    }

    class MyTreeContenetProvider implements ITreeContentProvider{

        public Object[] getChildren(Object parentElement) {
            File file=(File)parentElement;
            if(file.isDirectory())
                return file.listFiles();
            else
                return null;
        }

        public Object getParent(Object element) {
            File file=(File)element;
            return file.getParentFile();
        }

        public boolean hasChildren(Object element) {
            File file=(File)element;
            return file.isDirectory()/*&&file.list().length>0*/;
        }

        public Object[] getElements(Object inputElement) {
            File file=(File)inputElement;
            return file.isDirectory()?file.listFiles():new Object[]{file};
        }

        public void dispose() {

        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

        }

    }

    class MyTableLableProvider implements ITableLabelProvider{

        public Image getColumnImage(Object element, int columnIndex) {
            return null;
        }

        public String getColumnText(Object element, int columnIndex) {
            File file=(File)element;
            switch (columnIndex) {
            case 0:
                return file.getName();
            case 1:
                return ""+file.length();
            case 2:
                return ""+file.isHidden();
            default:
                return "";
            }
        }

        public void addListener(ILabelProviderListener listener) {

        }

        public void dispose() {

        }

        public boolean isLabelProperty(Object element, String property) {
            return false;
        }

        public void removeListener(ILabelProviderListener listener) {

        }

    }
}

下面是运行画面:

file

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/03/07/344853.html

给表格的单元格增加编辑功能(补充)

这一篇是对“给表格的单元格增加编辑功能”的补充,目的是让表格列显示Checkbox并允许单击改变选中状态,例子中的表格共有三列,其中后两列均需要显示为Checkbox。

步骤一,构造TableViewer;

final String[] columnNames = new String[] { "Project", "Must", "Must Not" };//columnNames在后面也要用到,所以专门定义为一个数组
TableColumn column = new TableColumn(tbv.getTable(), SWT.NONE);
column.setText(columnNames[0]);
tbv.setColumnProperties(columnNames);//给每个列指定一个字符串属性值
column.setWidth(200);
column = new TableColumn(tbv.getTable(), SWT.NONE);
column.setText(columnNames[1]);
column.setWidth(100);
column = new TableColumn(tbv.getTable(), SWT.NONE);
column.setText(columnNames[2]);
column.setWidth(100);
tbv.setContentProvider(...);
tbv.setLabelProvider(...);
tbv.setInput(...);

步骤二,定义CellEditor数组并指定给前面的TableViewer:

final CellEditor[] editors = new CellEditor[tbv.getTable().getColumnCount()];
//editors[0]保留为空,因为第一列不需要显示为Checkbox
editors[1] = new CheckboxCellEditor(tbv.getTable());
editors[2] = new CheckboxCellEditor(tbv.getTable());
tbv.setCellEditors(editors);

步骤三,定义TableViewer的CellModifier,作用是告诉表格如何改变对象的属性值,注意在modify()方法里参数element可能是org.eclipse.swt.widgets.Item类型,如果是这种情况要通过Item#getData()得到实际的对象:

tbv.setCellModifier(new ICellModifier() {
    public boolean canModify(Object element, String property) {
        return property.equals(columnNames[1]) || property.equals(columnNames[2]);
    }

    public Object getValue(Object element, String property) {
        PortfolioItem item = (PortfolioItem) element;
        if (property.equals(columnNames[1])) {
            return Boolean.valueOf(item.isMust());
        }
        if (property.equals(columnNames[2])) {
            return Boolean.valueOf(item.isMustNot());
        }
        return null;
    }

    public void modify(Object element, String property, Object value) {
        if (element instanceof Item)
            element = ((Item) element).getData();
        PortfolioItem item = (PortfolioItem) element;
        if (property.equals(columnNames[1])) {
            item.setMust(((Boolean) value).booleanValue());
        }
        if (property.equals(columnNames[2])) {
            item.setMustNot(((Boolean) value).booleanValue());
        }
    }
});

步骤四,这时单击表格可以改变选中状态了,但显示的是True/False(或其他在LableProvider里定义的内容),见图1,而非Checkbox控件选中/清空的样子。

file
图1 单击表格单元可改变T/F

解决的办法很简单,在LabelProvider里根据属性值True/False显示不同的图片即可,这两个图片可以在这里下载(鼠标右键另存为):

public Object getColumnImage(Object object, int columnIndex) {
    PortfolioItem item=(PortfolioItem)object;
    switch (columnIndex) {
    case 1:
        return item.isMust()?PortfolioEditPlugin.getPlugin().getImage("checked"):PortfolioEditPlugin.getPlugin().getImage("unchecked");
    case 2:
        return item.isMustNot()?PortfolioEditPlugin.getPlugin().getImage("checked"):PortfolioEditPlugin.getPlugin().getImage("unchecked");
    default:
        return null;
    }        
}

public String getColumnText(Object object, int columnIndex) {
    PortfolioItem item=(PortfolioItem)object;
    switch (columnIndex) {
    case 0:
        return item.getProject()==null?"N/A":item.getProject().getName();
//在显示为Checkbox的两列里不需要文字
//        case 1:
//            return item.isMust()?"True":"False";
//        case 2:
//            return item.isMustNot()?"True":"False";
    default:
        return "";
    }
}

最后是运行结果:

file

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/02/25/337367.html

在Eclipse的About对话框上添加自己的图标

在Eclipse的About对话框上添加自己的图标的步骤如下:

1、建立一个feature;

2、在feature.xml的Overview属性页里设定一个Branding Plug-in

3、把一个32x32的图片(名字可以任意起,例如mylogo.gif)放在Branding Plug-in的根目录下;

4、在Branding Plug-in的根目录下建立about.ini文件,内容为featureImage=mylogo.gif

5、最后,输出这个feature即可。

file
图1 添加在About对话框里的图标

注意1:Branding Plug-in的MANIFEST.MF的Build页里一定要选择输出mylogo.gifabout.ini这两个文件

注意2:我输出feature时如果选中Package features and plug-ins as individual JAR archives则feature不会被安装(在About的Feature Details列表里没有出现),勾掉这个选项则正常。

参考资料

How Can I Give My Eclipse Blob An Icon In The Flippin' About Dialog?

代码下载

包含feature和branding plug-in的工程打包

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/01/20/320759.html