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. 閱讀(2546) 評論(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
          您好!
          我對這個功能很敢興趣,不知可否發份源碼給我?  回復  更多評論
            

          主站蜘蛛池模板: 惠东县| 扶余县| 静宁县| 涿州市| 托克逊县| 宁城县| 增城市| 合水县| 定州市| 日土县| 庆元县| 波密县| 班戈县| 台南县| 淮安市| 灵寿县| 长春市| 南宫市| 长顺县| 吴忠市| 冕宁县| 公安县| 全椒县| 顺义区| 盐边县| 太仆寺旗| 汨罗市| 鞍山市| 江川县| 吉林省| 都江堰市| 濉溪县| 仁寿县| 射阳县| 文登市| 曲水县| 西吉县| 航空| 元江| 城步| 永宁县|