插件开发: 将View里面的对象托拽到Editor步骤

插件开发: 将View里面的对象托拽到Editor步骤
在桌面系统中,拖拽是一个用户很喜欢的功能。Eclipse主要由view和Editor组成,相互之间的拖拽需求很常见,一般主要是将view(tree/table)的东西拖到text/graphical editor。我写一个简单的例子,将一个view里的对象拖到text editor和graphical editor完成插入,其中text editor使用CDT提供的C++ Editor,而graphical editor使用shapes example(GEF SDK) 提供的shapes editor(为方便稍加改造)。

第一步:建立domain model,这个model里只包含block,一个GenericBlock和它的两个子类ConstantBlock和LoopBlock。在C++编辑器拖拽中,ConstantBlock用来插入 const int XX = 0;之类的语句,LoopBlock用来插入 for循环;图形模式下,ConstantBlock插入一个矩形,而LoopBlock插入一个椭圆形,正好对应shaps example的两种图形。

Generic Block

public   abstract   class  GenericBlock  implements  IAdaptable {
    
protected  String name;
    
//
     abstract   protected  String getNativeStatement();

    
public  Object getAdapter(Class adapter) {
        
return  Platform.getAdapterManager().getAdapter( this , adapter);
    }
}

  ConstantBlock常量块:

public   class  ConstantBlock  extends  GenericBlock {
     .
//
     protected  String getNativeStatement() {
        
return   " const int  " + name + "  = 999; " ;
    }

}

LoopBlock循环块:

public   class  LoopBlock  extends  GenericBlock {
    .
//
     protected  String getNativeStatement() {
        
return   " for (int i = 0; i < 100; i++) \n\t for (int j = i; j > 0; j--) \n\t\tprintf(\ " i + j =% d\\n\ " ,i*j); " ;
    }
}


第二步:通过Eclipse adapter factory,将block适配成text editor和graphica editor想要的对象,分别为string和产生shape对象的CreationFactory。

Extension:

    < extension
         
id ="com.lifesting.scratch.blockadapter"
         name
="BLOCkAdapter"
         point
="org.eclipse.core.runtime.adapters" >
      
< factory
            
adaptableType ="com.lifesting.scratch.views.GenericBlock"
            class
="com.lifesting.scratch.ExtractCAdapterFactory" >
         
< adapter
               
type ="com.lifesting.scratch.views.IRetriveCStructure" >
         
</ adapter >
         
< adapter
               
type ="org.eclipse.gef.requests.CreationFactory" >
         
</ adapter >
      
</ factory >
   
</ extension >

Adapter Factory:

public   class  ExtractCAdapterFactory  implements  IAdapterFactory {

    @Override
    
public  Object getAdapter(Object adaptableObject, Class adapterType) {
        
if  (adapterType  ==  IRetriveCStructure. class )
            
return   new  DspExtractAdapter((GenericBlock) adaptableObject);
        
if  (adapterType  ==  CreationFactory. class )
            
return   new  BlockCreationFactoryAdapter((GenericBlock)adaptableObject);
        
return   null ;
    }

    @Override
    
public  Class[] getAdapterList() {
        
return   new  Class[]{IRetriveCStructure. class ,CreationFactory. class };
    }
}

DspExtractAdatper只是简单调用block.getNativeStatement,而传递给GEF Editor的将是CreationFactory,它被TemplateTransferDropTargetListener用来完成模型插入/图形更新。

DspExtractAdatper

// IRetriveCStructure只定义了一个getStructure操作,用来得到C代码
public   class  DspExtractAdapter  implements  IRetriveCStructure {
    
private  GenericBlock block;
    
    
public  DspExtractAdapter(GenericBlock block) {
        
super ();
        
this .block  =  block;
    }

    @Override
    
public  String getStructure() {
        
return  block.getNativeStatement();
    }
}


BlockCreationFactoryAdatper:(常量块--矩形,循环块--椭圆形)

public   class  BlockCreationFactoryAdapter  implements  CreationFactory {
    
private  GenericBlock block;

    
public  BlockCreationFactoryAdapter(GenericBlock adaptableObject) {
        block 
=  adaptableObject;
    }

    @Override
    
public  Object getNewObject() {
        Shape shape;
        
if  (block  instanceof  ConstantBlock)
            shape 
=   new  RectangularShape();
        
else
            shape 
=   new  EllipticalShape();
        shape.setName(block.getName());
        
return  shape;
    }

    @Override
    
public  Object getObjectType() {
        
if  (block  instanceof  ConstantBlock)
            
return  RectangularShape. class ;
        
else
            
return  EllipticalShape. class ;
    }
}


第三步:建一个view,完成拖拽的源,这个view里面包含一个tree viewer,使用的是一个简单的tree input(见下效果图)。首先是把它显示出来:
     public   void  createPartControl(Composite parent) {
        viewer 
=   new  TreeViewer(parent, SWT.MULTI  |  SWT.H_SCROLL  |  SWT.V_SCROLL);
        viewer.setContentProvider(
new  TreeNodeContentProvider(){});
        viewer.setLabelProvider(
new  LabelProvider(){
            @Override
            
public  String getText(Object element) {
                Object v 
=  ((TreeNode)element).getValue();
                
if  (v  instanceof  String)
                    
return  (String) v;
                
return  ((GenericBlock)v).getName();
            }
        });
        viewer.setInput(getTreeModel());
        hookDrag(viewer);   
    }
在swt中,拖拽(drag-drop)有三要素 drag source, transfer, drop target,下面依次定义:

drag(使用了两个transfer,分别给text和graphics使用的,一般drag过程中应保持domain model即block的纯洁性,然后根据不同目标适配):

     private   void  hookDrag( final  TreeViewer viewer2) {
        viewer2.addDragSupport(DND.DROP_COPY 
| DND.DROP_DEFAULT,  new  Transfer[]{BlockTransfer.getInstance(),TemplateTransfer.getInstance()},  new  DragSourceListener(){
            @Override
            
public   void  dragFinished(DragSourceEvent event) {
            }
            @Override
            
public   void  dragSetData(DragSourceEvent event) {
                TreeNode object 
=  (TreeNode) ((IStructuredSelection)viewer2.getSelection()).getFirstElement();
                event.data 
=  object.getValue();
            }
            @Override
            
public   void  dragStart(DragSourceEvent event) {
                TreeNode object 
=  (TreeNode) ((IStructuredSelection)viewer2.getSelection()).getFirstElement();
                
boolean  drag_block  =   object.getValue()  instanceof  GenericBlock;
                event.doit 
=  drag_block;
            }});
    }

Transfer,没有什么特殊的,所有Transfer的写法都是一个套路。

public   class  BlockTransfer  extends  ByteArrayTransfer {
    .
    @Override
    
protected  Object nativeToJava(TransferData transferData) {
        
if  ( ! isSupportedType(transferData))  return   null ;
        
byte [] bts  =  ( byte [])  super .nativeToJava(transferData);
            
// 略,将byte[]转化为Java对象
    }
    @Override
    
protected   void  javaToNative(Object object, TransferData transferData) {
        
if  ( ! (object  instanceof  GenericBlock))
            
return ;
        GenericBlock block 
=  (GenericBlock) object;
        
// 略,将block转化为byte[]
        
    }
        .
}
要使用drop,首先必须得在target(text editor/graphical editor)上注册才能使用。这儿使用Eclipse提供PartListener,每当一个编辑器打开或者激活是,判断能不能成为drop target,能的话就把drop注册上。

 1       private  IPartListener listener  =   new  IPartListener(){
 2          @Override
 3           public   void  partActivated(IWorkbenchPart part) {
 4               if  (part  instanceof  ITextEditor)
 5              {
 6                  ITextEditor editor  =  (ITextEditor) part;
 7                  Control editor_control  =  (Control) editor.getAdapter(Control. class );
 8                  DropTarget dropTarget =  (DropTarget)editor_control.getData(DND.DROP_TARGET_KEY);
 9                   if  (dropTarget  ==   null )
10                      dropTarget =   new  DropTarget(editor_control, DND.DROP_DEFAULT  |  DND.DROP_COPY );
11                   if  (Boolean.TRUE  !=  dropTarget.getData(KEY))
12                          hookDrop(dropTarget);                
13              }
14          }
15          @Override
16           public   void  partBroughtToTop(IWorkbenchPart part) {}
17          @Override
18           public   void  partClosed(IWorkbenchPart part) {}
19          @Override
20           public   void  partDeactivated(IWorkbenchPart part) {}        
21           void  hookDrop(DropTarget dropTarget)
22          {
23              Transfer[] currentTransfers =  dropTarget.getTransfer();
24               int  currentLength =  currentTransfers.length;
25              Transfer[] newTransfers =   new  Transfer[currentLength  +   1 ];
26              System.arraycopy(currentTransfers,  0 , newTransfers,  0 , currentLength);
27              newTransfers[currentLength] =  BlockTransfer.getInstance();
28              dropTarget.setTransfer(newTransfers);
29              dropTarget.addDropListener(drop_listener);
30              dropTarget.setData(KEY, Boolean.TRUE);
31          }
32          @Override
33           public   void  partOpened(IWorkbenchPart part) {
34               if  (part  instanceof  ITextEditor)
35              {
36                  ITextEditor editor  =  (ITextEditor) part;
37                  Control editor_control  =  (Control) editor.getAdapter(Control. class );
38                  DropTarget dropTarget =  (DropTarget)editor_control.getData(DND.DROP_TARGET_KEY);
39                   if  (dropTarget  ==   null )
40                      dropTarget =   new  DropTarget(editor_control, DND.DROP_DEFAULT  |  DND.DROP_COPY );
41                  hookDrop(dropTarget);                
42              }
43          }
44      };

29行加了一个drop listener,即target响应drop操作,最终实现拖拽效果。
 1       private  DropTargetListener drop_listener  =    new  DropTargetAdapter(){
 2          @Override
 3           public   void  drop(DropTargetEvent event) {
 4               if  ( ! BlockTransfer.getInstance().isSupportedType(event.currentDataType))  return ;
 5              GenericBlock block  =  (GenericBlock) event.data;
 6              IRetriveCStructure cs  =  (IRetriveCStructure) block.getAdapter(IRetriveCStructure. class );
 7               if  (cs  !=   null )
 8              {
 9                  Control ctrl  =  ((DropTarget)event.widget).getControl();
10                   if  (ctrl  instanceof  StyledText)
11                  {
12                      ((StyledText)ctrl).insert(cs.getStructure());
13                  }
14              }
15          }
16          @Override
17           public   void  dragOver(DropTargetEvent event) {
18              event.feedback  =  DND.FEEDBACK_SELECT;
19          }
20      };
注意18行的feedback,没有它就不能完成在text editor的插入。
等等,怎么drop listener里面没有关于shapes edtior的东西,怎样在shapes editor里面插入shapes呢?

第四步:改造shapes example。GEF SDK里面提供了一个很好的drop listener-- TemplateTransferDropTargetListener,当从palette 往diragam拖拽得时候使用的就是它,而这里从自定义view往diagram拖拽还是要用到它,为了更形象,在shape里面加了一个属性name,把name显示在每个shape的中央。

public   abstract   class  Shape  extends  ModelElement {
    
private   static  IPropertyDescriptor[] descriptors;
    
//
     protected  String name = " Null " ;

    
public  String getName() {
        
return  name;
    }
    
public   void  setName(String name) {
        
this .name  =  name;
        }
    
//
}

class  ShapeEditPart  extends  AbstractGraphicalEditPart {
     
//
    
// 修改这个方法,加入shape name
     private  IFigure createFigureForModel() {
    IFigure figure;
    
if  (getModel()  instanceof  EllipticalShape) {
        figure 
=   new  Ellipse();
    } 
else   if  (getModel()  instanceof  RectangularShape) {
        figure 
=   new  RectangleFigure();
        
    } 
else  {
        
//  if Shapes gets extended the conditions above must be updated
         throw   new  IllegalArgumentException();
    }
    figure.setLayoutManager(
new  BorderLayout());
    figure.add(
new  Label(((Shape)getModel()).getName()),BorderLayout.CENTER);
    
return  figure;
        
//
}

为了让shapes editor使用block适配的creation factory,还需要修改一下shapes editor。

public   class  ShapesEditor 
    
extends  GraphicalEditorWithFlyoutPalette 
{
     
//
     private  TransferDropTargetListener createTransferDropTargetListener() {
    
return   new  TemplateTransferDropTargetListener(getGraphicalViewer()) {
        
protected  CreationFactory getFactory(Object template) {
            
if  (template  instanceof  IAdaptable)
            {
                CreationFactory factory 
=  (CreationFactory) ((IAdaptable)template).getAdapter(CreationFactory. class );
                
if  (factory  !=   null return  factory;
            }
            
return   new  SimpleFactory((Class) template);
        }
    };
    
//
}

这样一个非常完整的例子就完成了。效果图如下,其中曲线表示由某一端拖拽而成,可以看出,自定义view并不影响palette拖拽。






你可能感兴趣的:(插件开发: 将View里面的对象托拽到Editor步骤)