进一步完善 -- GEF创建助手工具条

昨天讨论了在图形元素上显示工具条的方法,应该说工作的还不是很完美,因为在选定了创建Connection的工具后,并不能像使用palette那样,在鼠标移动的过程中,有一个连接动态跟随,当鼠标释放后,如果释放位置在一个图形元素之上,那么将建立这个Connection,如果不在,那么这个连接将自动消失;今天想讨论一下如何实现这个功能;

我们知道GEF是以Draw2DLightweightSystem作为实现的基础的,在LightweightSystm中对于mouseDownmouseMove的实现如下:

事件发布
 1           /**   @see  MouseListener#mouseDoubleClick(MouseEvent)  */
 2           public   void  mouseDoubleClick(MouseEvent e) {
 3              getEventDispatcher().dispatchMouseDoubleClicked(e);
 4          }
 5 
 6           /**   @see  MouseListener#mouseDown(MouseEvent)  */
 7           public   void  mouseDown(MouseEvent e) {
 8              getEventDispatcher().dispatchMousePressed(e);
 9          }
10 
11           /**   @see  MouseTrackListener#mouseEnter(MouseEvent)  */
12           public   void  mouseEnter(MouseEvent e) {
13              getEventDispatcher().dispatchMouseEntered(e);
14          }
15 
16           /**   @see  MouseTrackListener#mouseExit(MouseEvent)  */
17           public   void  mouseExit(MouseEvent e) {
18              getEventDispatcher().dispatchMouseExited(e);
19          }
20 
21           /**   @see  MouseTrackListener#mouseHover(MouseEvent)  */
22           public   void  mouseHover(MouseEvent e) {
23              getEventDispatcher().dispatchMouseHover(e);
24          }
25 
26           /**   @see  MouseMoveListener#mouseMove(MouseEvent)  */
27           public   void  mouseMove(MouseEvent e) {
28              getEventDispatcher().dispatchMouseMoved(e);
29          }
30 
31           /**   @see  MouseListener#mouseUp(MouseEvent)  */
32           public   void  mouseUp(MouseEvent e) {
33              getEventDispatcher().dispatchMouseReleased(e);
34          }
35 
36           /**   @see  DisposeListener#widgetDisposed(DisposeEvent)  */
37           public   void  widgetDisposed(DisposeEvent e) {
38              getUpdateManager().dispose();
39          }
40 

由此可见,Draw2D对于鼠标事件的处理是由Event Dispacher进行派发的。那么这些派发的事件是怎么处理的呢?在org.eclipse.gef.tools.AbstractTool类中有很多相应的handleXXX方法,例如:handleDoubleClickhandleDrag方法等等,这就是处理这些鼠标事件的位置,当然这些方法在这个类中,没有做什么处理,仅仅返回了false;但是如果我们继续向下查找的话,会发现其子类AbstractConnectionCreationTool重写了一些handle方法,其中之一就是handleDrag,而handleDrag调用了handleMove 

handleXXX的实现
/**
     * 
@see  org.eclipse.gef.tools.AbstractTool#handleDrag()
     
*/
    
protected   boolean  handleDrag() {
        
if  (isInState(STATE_CONNECTION_STARTED))
            
return  handleMove();
        
return   false ;
    }

    
/**
     * 
@see  org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
     
*/
    
protected   boolean  handleDragInProgress() {
        
if  (isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS))
            
return  handleMove();
        
return   false ;
    }

    
/**
     * 
@see  org.eclipse.gef.tools.AbstractTool#handleFocusLost()
     
*/
    
protected   boolean  handleFocusLost() {
        
if  (isInState(STATE_CONNECTION_STARTED)) {
            eraseSourceFeedback();
            eraseTargetFeedback();
            setState(STATE_INVALID);
            handleFinished();
        }
        
return   super .handleFocusLost();
    }

    
/**
     * 
@see  org.eclipse.gef.tools.TargetingTool#handleHover()
     
*/
    
protected   boolean  handleHover() {
        
if  (isInState(STATE_CONNECTION_STARTED))
            updateAutoexposeHelper();
        
return   true ;
    }

    
/**
     * 
@see  org.eclipse.gef.tools.TargetingTool#handleInvalidInput()
     
*/
    
protected   boolean  handleInvalidInput() {
        eraseSourceFeedback();
        setConnectionSource(
null );
        
return   super .handleInvalidInput();
    }

    
/**
     * 
@see  org.eclipse.gef.tools.AbstractTool#handleMove()
     
*/
    
protected   boolean  handleMove() {
        
if  (isInState(STATE_CONNECTION_STARTED)  &&  viewer  !=  getCurrentViewer())
            
return   false ;
        
if  (isInState(STATE_CONNECTION_STARTED  |  STATE_INITIAL
                
|  STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) {
            updateTargetRequest();
            updateTargetUnderMouse();
            showSourceFeedback();
            showTargetFeedback();
            setCurrentCommand(getCommand());
        }
        
return   true ;
    }

看到handleMove中的那个showSourceFeedback()方法了吧,其实我们今天讨论的问题,在GEF框架中就是通过它实现的;爬源码,我们可以知道这个方法事实上是调用了AbstractEditPartshowSourceFeedback()方法,而后者又调用了GraphicalNodeEditPolicyshowSourceFeedback方法,这个方法又调用了showCreationFeedback方法;

     /**
     * Shows feedback during a creation.
     * 
     * 
@param  request
     *            CreateConnectionRequest
     
*/
    
protected   void  showCreationFeedback(CreateConnectionRequest request) {
        FeedbackHelper helper 
=  getFeedbackHelper(request);
        Point p 
=   new  Point(request.getLocation());
        helper.update(getTargetConnectionAnchor(request), p);
    }

    
/**
     * calls {
@link  #showCreationFeedback(CreateConnectionRequest)} when
     * appropriate.
     * 
     * 
@see  org.eclipse.gef.EditPolicy#showSourceFeedback(Request)
     
*/
    
public   void  showSourceFeedback(Request request) {
        
if  (REQ_CONNECTION_END.equals(request.getType()))
            showCreationFeedback((CreateConnectionRequest) request);
    }

 

这下我们就明白了,我们只要照猫画虎,在我们的工具条的mouse move方法中实现上面的那个showCreationFeedback方法就行了;以下就是我们的实现:

 1  public   void  mouseDragged(MouseEvent me) {
 3      request  =   new  CreateConnectionRequest();
 4      ScalableFreeformRootEditPart root  =  (ScalableFreeformRootEditPart) getSourceEditPart().getRoot();
 5       double  zoom  =  root.getZoomManager().getZoom();
 6 
 7      request.setLocation( new  Point(hoverX  *  zoom, hoverY  *  zoom));
 8      request.setType( " connection start " );
 9 
10      FeedbackHelper feedbackHelper  =   new  FeedbackHelper();
11      Point p  =   new  Point(me.x, me.y);
12      Connection connectionFeedback  =  FigureFactory.createNewWorkflowPath( null );
13 
14      connectionFeedback.setConnectionRouter(((ConnectionLayer) getLayer(LayerConstants.CONNECTION_LAYER)).getConnectionRouter());
15      connectionFeedback.setSourceAnchor(getSourceConnectionAnchor(request));
16      feedbackHelper.setConnection(connectionFeedback);
17 
18      IFigure figure  =  getLayer(LayerConstants.FEEDBACK_LAYER);
19       for  ( int  i  =   0 ; i  <  figure.getChildren().size(); i ++ ) {
20           if  (figure.getChildren().get(i)  instanceof  Connection) {
21              figure.remove((IFigure) figure.getChildren().get(i));
22          }
23      }
24      getLayer(LayerConstants.FEEDBACK_LAYER).add(connectionFeedback);
25      request.setLocation( new  Point(me.x  *  zoom, me.y  *  zoom));
26      feedbackHelper.update( null new  Point(me.x  +  offsetX  *  zoom, me.y  +  offsetY  *  zoom));
27  }
28 

至此,我们的问题就解决了。但是还有一个问题,那就是,如果鼠标被释放了,那么这个被放置在反馈层的Connection应该被擦除掉,而同时还应该根据鼠标的位置决定是不是应该创建Connection;所以我们还应该再改一下mouse released方法,增加如下处理:
IFigure feedBackLayer = getLayer(LayerConstants.FEEDBACK_LAYER);
feedBackLayer.getChildren().clear();

2010-08-05更正,以上部分不正确,应该是:

1  IFigure feedBackLayer  =  getLayer(LayerConstants.FEEDBACK_LAYER);
2                  
3  for  ( int  i  =   0 ; i  <  feedBackLayer.getChildren().size(); i ++ ) {
4       if  (feedBackLayer.getChildren().get(i)  instanceof  Connection) {
5          feedBackLayer.remove((IFigure) feedBackLayer.getChildren().get(i));
6      }
7  }
8  ......

 

通过以上的处理,我们就可以实现类似于paletteConnection Tool的功能了。当我们在一个图形元素上Hover时,首先程序先将与悬停位置最近的锚点作为源锚点,并显示工具条,当我们点击工具条上的Connection图标时,随着鼠标的拖动,一个Connection将动态跟随鼠标的移动被画出来,随着鼠标的释放,Connection将根据释放位置决定是否建立一个Connection,还是将这个动态的Connection清除掉;

你可能感兴趣的:(gef)