BaNg@taobao

Just Do It!

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  20 Posts :: -1 Stories :: 202 Comments :: 0 Trackbacks
在桌面系统中,拖拽是一个用户很喜欢的功能。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 != nullreturn factory;
            }
            
return new SimpleFactory((Class) template);
        }
    };
    
//
}

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






posted on 2008-11-22 01:27 Always BaNg. 阅读(2500) 评论(3)  编辑  收藏 所属分类: JavaEclipseC++

Feedback

# re: 插件开发: 将View里面的对象托拽到Editor步骤 2012-04-13 14:19 Kako
您好
因為本人最近也在研究這方面的開發
不支能否和您要這個程式的source code參考一下?
感激不盡
我的email: kako0507@gmail.com  回复  更多评论
  

# re: 插件开发: 将View里面的对象托拽到Editor步骤[未登录] 2013-04-01 23:18 **
IRetriveCStructure 没有定义!!!!!!!!
  回复  更多评论
  

# re: 插件开发: 将View里面的对象托拽到Editor步骤 2014-09-05 10:24 reader
您好!
我对这个功能很敢兴趣,不知可否发份源码给我?  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: