1。图形拖动
图形的拖动就是图形选中的图形跟着鼠标的移动而不断的相应改变位置,这是在图形界面中是经常见的一个操作,但是在SWT/JFace中来实现却不是意见容易的事。在这里底板是一个Canvas,图形也是建立在一个Canvas上,当然位置的改变是建立在鼠标移动的监听下.
1、首先建立几个变量:
Int oldx = -1;// 拖动前的x
Int oldy = -1;//拖动前的y
Int xoffset = 0; //拖动后的偏移量x
Int yoffset =0 ;// 拖动后的偏移量y;
Int x,y;// 拖动后图形应该在的位置
2、 在第一次拖动时,取得oldx和oldy
if(oldX==-1&&oldY==-1){
oldX=e.x;
oldY=e.y;
}
3、 获得偏移量
xoffset=e.x-oldX;
yoffset=e.y-oldY;
4、得到新的图形位置
x=ca.getBounds().x+xoffset;
y=ca.getBounds().y+yoffset;
5、 重新设置图形的位置
ca.setBounds(x,y,ca.getSize.x,ca.getSize.y)
这样就实现了图形的拖动,不仅算法简洁,而且效果也很好。当然为了更好的效果,还可以加上边界控制,以保证图形不超出边界
2。图形连线与画线
2.1 图形的连线
整个SWT中只有一个类GC与之有关,就是在一个画板上画线,这样简单的线根本无法满足复杂连线的要求,要实现比较复杂的图形间的连线,我们使用了eclipse的另一个插件——draw2D,在draw2D的图形中比较容易实现连线,但是draw2D中连线的图形都是IFigure类的图形,否则就无法实现draw2D内的连线,然而我们的图形使用SWT设计的,因此出现了两者的兼容问题。因此我们自行设计了综合使用两者来实现图形连线的方案,就是图形是用SWT设计的,而线是用draw2D来设计的。这样设计表面看起来能够达到draw2D的两线的完美效果,而又不改变我们需要是用的SWT设计的图形。
1、 连线的简化流程
获得起始图形,计算出连线的起点
获得指向图形,计算出连线的终点
根据起点和终点来建立连线
2、 解决Canvas与Panel之间的兼容问题
因为我们的建立在一个画布(Canvas)上的,而连线则要建立在一个(Panel)上。因此我们要将两者联系起来,才能实现图形间的连线。这里需要用到轻量级系统(LightweightSystem)
lws=new LightweightSystem(editPlace);
图形所在的面板
然后就可以新建Panel,并将他设为轻量级系统的内容
panel=new Figure();
lws.setContents(panel);
这样我们就可以在panel上添加我们的连线了
2.2 在面板上画曲线
1、 这个曲线也同样要建立在Panel之上,因此也要像上面一样建立Panle,这里不再重复
2、要创建曲线则要用到PolylineConnection(),我们先创建一个PolylineConnection的对象pc.
3、 如果要显示箭头,则可以给pc添加修饰,
p2.setTargetDecoration(new PolygonDecoration());
4、接下来就是设置pc上的关键点,包括起点,终点,拐点。确定这些点后,从起点到终点依次排列即可
p2.setPoints(new PointList(new int[]{p1, p2,p3,…}));
5、 这样就可以建立了曲线,不过线上的个关键点的确定是一个难点,根据不同的情况会有不同的确定方法,这里不能一一列出了
3。图像常用处理与Canvas
3.1 如何在Canvas上添加图像
要在一个画布Canvas上添加,需要注册画布的画图事件,代码如下:
canvas.addPainListener(new PaintListener(){
public void paintControl(PaintEvent e){
e.gc.drawImage(image,10,10);
}
}
3.2 图像的创建
1. Image(display, “eclipse.gif”)
2. Image (display , “eclipse.gif”,SWT.IMAGE_FRAY)
3. ImageData data = new ImageData(“eclipse.gif);
Image image = new Image(display, data)
3.3 图像的放大或缩小
通过ImageData的scaledTo可意见图像放大或缩小,在这里是新建另一个图像,而不是直接改变原来的图像。
ImageData data = image.getImageData();
ImageDate newData = data.scaledTo(newWidth,newHeight);
Image newImage = new Image(display, newData);
3.4 图像的保存
因为一个动态的图片可能有多个图片组成,所以用数组
4文件的过滤
这里所说的文件的过滤不是上面说的文件对话框中的文件的过滤,在这里我们设置了一个树,让它来显示工程下已有的文件,在这里我们只希望看到(.grh)的文件,因此需要给它设置过滤器。先来看看我们怎么实现显示文件的;
在这里使用的是TreeViewer,TreeViewer的构建步骤如下:
1、 创建新对象,例如TreeViewer tv=new TreeViewer(fileComposite,SWT.NONE);
2、 设置内容管理器,如tv.setContentProvider( new FileTreeContentProvider());
3、设置标签提供器,如tv.setLabelProvider(new FileTreeLabelProvider());
4、设定TreeViewer的输入数据,如tv.setInput(new File("e:\"));
其中FileTreeContentProvide,FileTreeLabelProvider分别是implements了ItreeContentProvider和ILabelProvider两个接口的。
FileTreeContentProvide中有几个重要的方法。
① getElements();"public Object[] getElements(Object inputElement)",当程序开始构建树时,首先调用它返回一个数组,此数组对象表示树的根节点,
② hasChildren();"public boolean hasChildren(Object element)",当TreeViewer显示一个节点(element)后会调用该函数判断当前节点是否有子节点,若有则显示“+”;
③ getChildren();"public Object[] getChildren(Object parentElement)",当用户选择节点打开子节点时,会调用此函数返回下一层子节点,parentElement参数为选择的节点;
④ getparent();"public Object getParent(Object element)"
还有几个方法就不在此叙述,
在程序里不需要显示根目录的位置,只要显示在工程目录下有用的文件,在getElements()方法中,我们返回getChildren()中的值,这样,我们就可以不得到输入的路径这个根节点,而把该路径下的符合条件的文件作为根节点;
public Object[] getElements(Object element) {
return getChildren(element);
}
在getChildren方法中,我们通过一个数组列出给定路径下的文件,然后通过一个ArrayList来存储符合条件的文件对象:
Object[] kids = ((File) element).listFiles();
ArrayList<Object> outs = new ArrayList<Object>();
当kids不是空的时候,我们就看kids中的文件后缀是不是符合我们的要求,如果是,就添加到outs中
for(int i = 0;i<kids.length;i++){
if(((File)kids[i]).getName().endsWith(".wsdl")||
((File)kids[i]).getName().endsWith(".bpel")||
((File)kids[i]).getName().endsWith(".grh")){
int j = 0;
outs.add(j,kids[i]);
j++;
}
}
现在我们已经得到了我们需要得到的所有的对象,因为这个函数的返回值是个数组,再把outs中的元素复制到一个数组中即可;
f(outs.size()!=0){
Object[] out = new Object[outs.size()];
for(int i= 0;i<outs.size();i++){
out[i] = outs.get(i);
}
return out;
}
5。关于序列化保存
1) 关于保存
序列化的问题是由保存引起的,要保存一个对象就必须为相关的类实现序列化,这本没有什么问题,只需要把相关的类继承并实现Serializable接口就可以了,但是,在工程里用到了一些类。例如org.eclipse.swt.widgets.graphics.Image这个类,它是一个final类,不可以被继承,但是他是节点的一部分,必须要显示出来;我们可以把它作为一个参数传到节点的setNodeLocation函数中,它是初始化节点时负责显示的函数。每创建一个新的节点时就需要先创建一个Image实例。
2) 关于恢复
保存成功了还需要恢复图像,恢复图像是个比较复杂的过程,我们要保证图像的位置和数据等许多东西保持不变;首先找到开始节点,从开始节点开始逐个恢复,如没有开始节点,那么这个图是不完整的,就没有办法完整的恢复它;恢复时,不能用原来的对象,因为原有的对象都已经被dispose();了,并且不可以用到任何有关显示的方法,否则就会出现促发异常;所以要得到节点的大小和位置就要另想办法;
为了得到node原来的位置,在node中设置了一个point变量来记载node的位置,并在node的位置更改后更改point的值,这样就可以得到node原来的位置;同理,我们可以得到保存时node的大小了;
我们可以从保存的图中找到与开始节点相关联的下一个节点,回复这个节点后再找下一个节点,这样可以通过一个循环一直找到结束节点,并把这些节点加到一个新建的graph中,然后把相关的信息都通过已有的set和get方法添加的节点中去,这样,整个图的所有的节点都恢复出了;
因为每个节点的下一个节点有不同的可能,用一个if else组合来判断到底是哪一个节点,然后不同的节点有不同的代码;简单的节点如taskNode的恢复比较简单,基本的过程和创建一个新的节点相同,只是注意把原节点的信息传递给新的节点即可。
具体的过程可以简化为
获得要复制的节点的引用
点击粘贴,新建一个与要复制节点类型相同的节点
把要复制节点的信息传递到新的节点中去
判断节点是不是容器节点,是继续,不是结束
新建容器节点中的子节点,给子节点传递信息
以下是粘贴后初始化一个工作节点的例子;
wfNode = new TaskNode(node.getName(), gra, web);
wfNode.setId(node.getId());
wfNode.setNodeInView(c, workspace.getPanel(), attr,workspace,web); BaseWFNodeText.setCanvasText(wfNode);
wfNode.setNodeLocation(node.getPoint().x, node.getPoint().y, image1);
gra.addNode(wfNode);
若下一个节点是复杂节点,首先恢复复杂节点本身,然后判断该节点是否有子节点,若存在子节点,则依次恢复其子节点,通过一个foreach循环就可以把所有的子节点恢复;注意的是每个父节点的子节点形式可能不同,而且他们需要传递的信息也不完全相同。部分代码如下
wfNode.getCanvas().setSize(node.getSize());
//设置节点的大小
wfNode.setSize(node.getSize().x, node.getSize().y);
//记录节点的大小
gra.addNode(wfNode);
节点的信息如下
wfNode.setBpelOperation(node.getBpelOperation());
wfNode.setSuppressJionFaliare(node.getSuppressJionFaliare());
wfNode.setJionCondition(node.isJionCondition());
wfNode.setOporation(node.getOporation());
wfNode.setLink(node.getLink());
//以上为所有节点都有的属性
wfNode.setCondition(node.getCondition());
//FlowNode和WhileNode有的属性
wfNode.setCaseCondiction(node.getCaseCondiction());
//FlowNodeµÄ×Ó½ÚµãÓеÄÊôÐÔ
childNode.setFromPart(element.getFromPart());
childNode.setFromVar(element.getFromVar());
childNode.setToPart(element.getToPart());
childNode.setToVar(element.getToVar());
//以上四个是copy有的节点
6。图形的复制与粘贴
1) 为各个节点以及底层面板创建菜单项,包括复制,粘贴,删除;在工具栏以及菜单栏创建相应的项;
2) 关于复制
复制的方法比较简单,就是把一个新的变量指向要复制的对象,然后把这个变量通过get方法,让外界可以获得它;
public void copy(BaseWFNode node){
midNode = node;
}
3) 关于粘贴
粘贴的过程是创建一个新的节点,他所携带的信息和复制的节点相同
1、 获得要粘贴节点的引用;判断是否为空,是就继续,否则就什么也不做;
2、 构建一个新的节点,确定节点的id,位置等于声称代码无关的信息;
3、 判断节点具体是哪一种节点,并把原节点的信息赋给新的节点;
4、 Gragh中添加这个节点;
5、 根据第三步,若这个节点是某个复杂节点,就把它的子节点也构建出来,并赋予相应的信息;实现的过程和恢复图形类似;
4) 工具栏复制的实现
1、 这里添加监听的时候用了一个标记,当双击一个节点的时候,就记录这个标记为1,然后让一个中间的变量指向这个节点,
public void mouseDoubleClick(MouseEvent e){
WSCAttribute.showWind(work, node);
deleteFlag = 1;//当点击其他地方时设为0;
preDelNode = node;
}
2、点击复制的时候判断标记是否为1,判断PreDelNode是不是空,不是就根据上面说的复制来执行;删除节点也是用这个思想;
3、 点击粘贴,判断中间节点是否时空,不是就在底层面板的起始处将节点复制下来;否则就什么也不做;
客户虐我千百遍,我待客户如初恋!