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、 點擊粘貼,判斷中間節點是否時空,不是就在底層面板的起始處將節點復制下來;否則就什么也不做;
客戶虐我千百遍,我待客戶如初戀!