Ứng dụng Eclipse RCP 3 đơn giản - Tương tác View và Editor
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Giới thiệu

Tài liệu được viết dựa trên:
  • Eclipse: 4.4 (LUNA)

Trong tài liệu này tôi sẽ tạo một ứng dụng RCP với các chức năng nâng cao. Các kiến thức cơ bản của RCP sẽ không được nhắc lại. Vì vậy nếu bạn mà một người mới làm quen với RCP bạn nên xem trước tài liệu "RCP cho những người mới bắt đầu" trước khi thực hành bài hướng dẫn này.
Các vấn đề được đề cập:
  1. Mỗi khi có thay đổi dữ liệu trên Editor, nút SAVE sẽ sáng lên cho phép người dùng click để lưu dữ liệu. Xử lý việc lưu dữ liệu của Editor.
  2. View hiển thị danh sách các phòng ban. Người dùng click chọn 1 phòng ban trên View này nó sẽ được hiển thị trên Editor.
  3. Cập nhập lại View khi dữ liệu trên Editor thay đổi.

2- Tạo Project RCPTarget

Chúng ta tạo nhanh một project RCPTarget, dùng để khai báo các thư viện RCP và môi trường chạy.
  • File/New/Project
  • File/New/Other...
Nhập vào:
  • Name: Eclipse RCP
  • Location: http://download.eclipse.org/eclipse/updates/4.4/
Nhấn "Set as Target Platform" để các thư viện có tác dụng trên toàn bộ các project nằm trong workspace.

3- Tạo RCP Project

Tạo nhanh một RCP Workbench project.
  • File/New/Other...
Nhập vào:
  • Project Name: RCPApplication
Nhập vào:
  • Activator: org.o7planning.tutorial.rcpapplication.Activator
Nhập vào:
  • Package Name: org.o7planning.tutorial.rcpapplication
Project đã được tạo ra:
Bạn có thể chạy thử bằng cách nhấn phải chuột vào project, và chọn Run As/Eclipse Application.

4- Xem trước Project

Đây là hình ảnh Project sau khi hoàn thành.

5- Menu, Toolbar và Action

Thông thường bạn có thể tạo Menu Item, Toolbar Item từ các Command, khai báo trong plugin.xml. Tuy nhiên bạn cũng có thể tạo chúng thông qua các class Action tùy biến, hoặc các Action có sẵn trong thư viện RCP Workbench.
ApplicationActionBarAdvisor.java
package org.o7planning.tutorial.rcpapplication;

import org.eclipse.jface.action.ICoolBarManager;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.eclipse.ui.application.ActionBarAdvisor;
import org.eclipse.ui.application.IActionBarConfigurer;

public class ApplicationActionBarAdvisor extends ActionBarAdvisor {

  private IWorkbenchAction saveAction;
  private IWorkbenchAction exitAction;
  private IWorkbenchAction aboutAction;

  public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {
      super(configurer);
  }

  @Override
  protected void makeActions(IWorkbenchWindow window) {

      saveAction = ActionFactory.SAVE.create(window);
      this.register(saveAction);

      exitAction = ActionFactory.QUIT.create(window);
      this.register(exitAction);

      aboutAction = ActionFactory.ABOUT.create(window);
      this.register(aboutAction);

      super.makeActions(window);
  }

  @Override
  protected void fillMenuBar(IMenuManager menuBar) {
      // File
      MenuManager fileMenu = new MenuManager("&File", "file");
      menuBar.add(fileMenu);
      fileMenu.add(exitAction);

      // Help
      MenuManager helpMenu = new MenuManager("&Help", "help");
      menuBar.add(helpMenu);
      helpMenu.add(aboutAction);

      super.fillMenuBar(menuBar);
  }

  @Override
  protected void fillCoolBar(ICoolBarManager coolBar) {
      IToolBarManager toolBar1 = new ToolBarManager(SWT.FLAT | SWT.RIGHT );
      toolBar1.add(saveAction);

      coolBar.add(toolBar1);

      IToolBarManager toolBar2 = new ToolBarManager(SWT.FLAT | SWT.RIGHT );

      toolBar2.add(exitAction);
      coolBar.add(toolBar2);
  }
}

6- Toàn bộ Code của Project

Data Model:

Department.java
package org.o7planning.tutorial.rcpapplication.model;

public class Department {

  private Integer deptId;
  private String deptNo;
  private String deptName;
  private String location;

  public Department() {

  }

  public Department(Integer deptId, String deptNo, String deptName,
          String location) {
      this.deptId = deptId;
      this.deptNo = deptNo;
      this.deptName = deptName;
      this.deptName = deptName;
  }

  public Integer getDeptId() {
      return deptId;
  }

  public void setDeptId(Integer deptId) {
      this.deptId = deptId;
  }

  public String getDeptNo() {
      return deptNo;
  }

  public void setDeptNo(String deptNo) {
      this.deptNo = deptNo;
  }

  public String getDeptName() {
      return deptName;
  }

  public void setDeptName(String deptName) {
      this.deptName = deptName;
  }

  public String getLocation() {
      return location;
  }

  public void setLocation(String location) {
      this.location = location;
  }
}
DepartmentDAO.java
package org.o7planning.tutorial.rcpapplication.model;

import java.util.ArrayList;
import java.util.List;

public class DepartmentDAO {

  private static final List<Department> list = new ArrayList<Department>();

  static {
      list.add(new Department(10, "D10", "ACCOUNTING", "NEW YORK"));
      list.add(new Department(20, "D20", "RESEARCH", "DALLAS"));
      list.add(new Department(30, "D30", "SALES", "CHICAGO"));
      list.add(new Department(40, "D40", "OPERATIONS", "BOSTON"));
  }

  public static List<Department> listDepartment() {
      return list;
  }

  public static int getMaxDeptId() {
      int max = 0;
      for (Department dept : list) {
          if (dept.getDeptId() > max) {
              max = dept.getDeptId();
          }
      }
      return max;
  }

  public static Department findDepartment(int deptId) {
      for (Department dept : list) {
          if (dept.getDeptId() == deptId) {
              return dept;
          }
      }
      return null;
  }

  public static Department findDepartment(String deptNo) {
      for (Department dept : list) {
          if (dept.getDeptNo().equals(deptNo)) {
              return dept;
          }
      }
      return null;
  }

  public static void deleteDepartment(int deptId) {
      Department dept = findDepartment(deptId);
      if (dept == null) {
          return;
      }
      list.remove(dept);
  }

  public static Department updateDepartment(int deptId, String deptNo,
          String deptName, String location) throws DataException {
      Department dept = findDepartment(deptId);
      if (dept == null) {
          return null;
      }
      Department dept2 = findDepartment(deptNo);
      if (dept2 != null && dept2.getDeptId().intValue() != dept.getDeptId()) {
          throw new DataException("Unique Constraints error - deptNo: "
                  + deptNo);
      }
      dept.setDeptNo(deptNo);
      dept.setDeptName(deptName);
      dept.setLocation(location);
      return dept;
  }

  public static Department insertDepartment(String deptNo, String deptName,
          String location) throws DataException {
      Department dept = findDepartment(deptNo);
      if (dept != null) {
          throw new DataException("Unique Constraints error - deptNo: "
                  + deptNo);
      }
      dept = new Department();
      int deptId = getMaxDeptId() + 1;
      dept.setDeptId(deptId);
      dept.setDeptNo(deptNo);
      dept.setDeptName(deptName);
      dept.setLocation(location);
      list.add(dept);
      return dept;
  }
}
DataException.java
package org.o7planning.tutorial.rcpapplication.model;

public class DataException extends Exception {

  private static final long serialVersionUID = 7155764478659670087L;

  public DataException(String message) {
      super(message);
  }
}

Command:

DeptCommand.java
package org.o7planning.tutorial.rcpapplication.command;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.handlers.HandlerUtil;
import org.o7planning.tutorial.rcpapplication.editor.DeptEditor;
import org.o7planning.tutorial.rcpapplication.editor.DeptEditorInput;
import org.o7planning.tutorial.rcpapplication.model.Department;

public class DeptCommand extends AbstractHandler {

   public static final String ID = "command.dept";

   @Override
   public Object execute(ExecutionEvent event) throws ExecutionException {
       // get the page
       IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
       IWorkbenchPage page = window.getActivePage();
       // get the selection
       ISelection selection = HandlerUtil.getCurrentSelection(event);

       Object selectObj = null;

       // Có lựa chọn trên DeptListView
       if (selection != null && !selection.isEmpty() && selection instanceof IStructuredSelection ) {
           selectObj = ((IStructuredSelection) selection).getFirstElement();
       }
       
       // Không có lựa chọn trên DeptListView
       // (Coi như tạo mới một Deptpartment).
       else {
           // Tạo mới Department.
           selectObj = new Department();
       }

       Department dept = (Department) selectObj;
       DeptEditorInput input = new DeptEditorInput(dept);

       boolean found = false;
       // Các Editor refrence đang mở.
       IEditorReference[] eRefs = page.getEditorReferences();
       for (IEditorReference ref : eRefs) {
           IEditorPart editor = ref.getEditor(false);
           if (editor != null && editor instanceof DeptEditor) {
               // Restore
               DeptEditor deptEditor = (DeptEditor) ref.getEditor(true);
               found = true;

               boolean saved = true;
               // Nếu editor có thay đổi, save lại.
               if (deptEditor.isDirty()) {
                   saved = page.saveEditor(deptEditor, true);
               }
               if (saved) {
                   // Sét đặt Input mới cho DeptEditor.
                   page.reuseEditor(deptEditor, input);
                   deptEditor.showData();
               }
           }
       }
       if (!found) {
           try {
               page.openEditor(input, DeptEditor.ID);
           } catch (PartInitException e) {
               throw new RuntimeException(e);
           }
       }

       return null;
   }
}
MyConstants.java
package org.o7planning.tutorial.rcpapplication.constant;

public class MyConstants {

  public static final int EDITOR_DATA_CHANGED = 999999;  

}

Editor

AbstractBaseEditor.java
package org.o7planning.tutorial.rcpapplication.editor;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.o7planning.tutorial.rcpapplication.constant.MyConstants;
import org.o7planning.tutorial.rcpapplication.other.DirtyListener;
import org.o7planning.tutorial.rcpapplication.other.DirtyUtils;

public abstract class AbstractBaseEditor extends EditorPart {

    private boolean dirty;

    // Will be called before createPartControl
    // Được gọi trước createPartControl
    @Override
    public void init(IEditorSite site, IEditorInput input)
            throws PartInitException {
        // Very Important!!
        setSite(site);
        setInput(input);
    }

    // Khi thuộc tính PROP_DIRTY thay đổi, method này sẽ được gọi bởi Workbench.
    // Workbench kiểm tra Editor có thay đổi gì không.
    // Nếu method trả về true, nút SAVE sẽ sáng, ngược lại nút SAVE tối.
    @Override
    public boolean isDirty() {
        return this.dirty;
    }

    protected void setDirty(boolean dirty) {
        if (this.dirty != dirty) {
            this.dirty = dirty;
            
            // Thông báo có thuộc tính PROP_DIRTY thay đổi cho Workbench.
            this.firePropertyChange(IEditorPart.PROP_DIRTY);
        }
    }

    @Override
    public void doSaveAs() {

    }

    @Override
    public boolean isSaveAsAllowed() {
        return false;
    }

    @Override
    public void setFocus() {

    }

    // Write code in createPartControl2(Composite)
    @Override
    public final void createPartControl(Composite parent) {
        this.createPartControl2(parent);

        this.showData();
        this.setDirty(false);
        this.firePropertyChange(MyConstants.EDITOR_DATA_CHANGED);

        Control[] controls = this.registryDirtyControls();
        DirtyListener listener = new DirtyListenerImpl();
        DirtyUtils.registryDirty(listener, controls);
    }

    public abstract void showData( );

    // Create controls in this method.
    protected abstract void createPartControl2(Composite parent);

    // If data in Control changed, it fire to Workbench.
    // Đăng ký các Control, nếu các Control này thay đổi, nó sẽ thông báo lên Workbench.
    protected abstract Control[] registryDirtyControls();

    class DirtyListenerImpl implements DirtyListener {

        @Override
        public void fireDirty() {
            // If has any change, fire to Editor.
            AbstractBaseEditor.this.setDirty(true);
        }

    }
}
DeptEditor.java
package org.o7planning.tutorial.rcpapplication.editor;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.o7planning.tutorial.rcpapplication.constant.MyConstants;
import org.o7planning.tutorial.rcpapplication.model.Department;
import org.o7planning.tutorial.rcpapplication.model.DepartmentDAO;
import org.o7planning.tutorial.rcpapplication.other.DirtyUtils;

// IReusableEditor có thể sét đặt giá trị input mới cho Editor này.
public class DeptEditor extends AbstractBaseEditor implements IReusableEditor {

   public static final String ID = "deptEditor";
   private Integer deptId;
   private Text text_deptNo;
   private Text text_deptName;
   private Text text_location;

   public DeptEditor() {
   }

   /**
    * Create contents of the editor part.
    *
    * @param parent
    */
   @Override
   public void createPartControl2(Composite parent) {
       Composite container = new Composite(parent, SWT.NONE);
       container.setLayout(new GridLayout(2, false));

       Label lblDeptNo = new Label(container, SWT.NONE);
       lblDeptNo.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
               false, 1, 1));
       lblDeptNo.setText("Dept No");

       text_deptNo = new Text(container, SWT.BORDER);
       text_deptNo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
               false, 1, 1));

       Label lblDeptName = new Label(container, SWT.NONE);
       lblDeptName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
               false, 1, 1));
       lblDeptName.setText("Dept Name");

       text_deptName = new Text(container, SWT.BORDER);
       text_deptName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
               false, 1, 1));

       Label lblLocation = new Label(container, SWT.NONE);
       lblLocation.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
               false, 1, 1));
       lblLocation.setText("Location");

       text_location = new Text(container, SWT.BORDER);
       text_location.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
               false, 1, 1));
       new Label(container, SWT.NONE);

       DirtyListenerImpl dirtyListener = new DirtyListenerImpl();
       DirtyUtils.registryDirty(dirtyListener, this.text_deptName,
               this.text_deptNo, this.text_location);
   }

   @Override
   public void doSave(IProgressMonitor monitor) {
       try {
           String deptNo = this.text_deptNo.getText();
           String deptName = this.text_deptName.getText();
           String location = this.text_location.getText();
           if (this.deptId != null) {
               Department dept = DepartmentDAO.updateDepartment(deptId,
                       deptNo, deptName, location);

               this.setInput(new DeptEditorInput(dept));
               this.setDirty(false);
               this.firePropertyChange(MyConstants.EDITOR_DATA_CHANGED);
           } else {
               Department dept = DepartmentDAO.insertDepartment(deptNo,
                       deptName, location);
               this.setInput(new DeptEditorInput(dept));
               this.setDirty(false);
               this.firePropertyChange(MyConstants.EDITOR_DATA_CHANGED);
           }
       } catch (Exception e) {
           MessageDialog.openError(this.getSite().getShell(), "Error",
                   e.getMessage());
           e.printStackTrace();
       }
   }

   @Override
   protected Control[] registryDirtyControls() {
       return new Control[] { this.text_deptName, this.text_deptNo,
               this.text_location };
   }

   @Override
   public void showData() {
       DeptEditorInput ip = (DeptEditorInput) this.getEditorInput();
       Department dept = ip.getDept();

       this.deptId = dept.getDeptId();
       this.text_deptName.setText(dept.getDeptName() == null ? "" : dept
               .getDeptName());
       this.text_deptNo.setText(dept.getDeptNo() == null ? "" : dept
               .getDeptNo());
       this.text_location.setText(dept.getLocation() == null ? "" : dept
               .getLocation());
       // Clear dirty.
       this.setDirty(false);
   }

   // Làm setInput(..) thành public (IReusableEditor)
   @Override
   public void setInput(IEditorInput input) {
       super.setInput(input);
       firePropertyChange(IWorkbenchPartConstants.PROP_INPUT);
   }

   public String getDeptInfo() {
       DeptEditorInput input = (DeptEditorInput) this.getEditorInput();
       Department dept = input.getDept();
       if (dept == null) {
           return "";
       }
       String info = dept.getDeptNo() + " - " + dept.getDeptName() + " - "
               + dept.getLocation();
       return info;
   }

}
DeptEditorInput.java
package org.o7planning.tutorial.rcpapplication.editor;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPersistableElement;
import org.o7planning.tutorial.rcpapplication.model.Department;

public class DeptEditorInput implements IEditorInput {

  private Department dept;

  public DeptEditorInput(Department dept) {
      this.dept = dept;
  }

  public Department getDept() {
      return dept;
  }

  @SuppressWarnings("rawtypes")
  @Override
  public Object getAdapter(Class adapter) {
      return null;
  }

  @Override
  public boolean exists() {
      return false;
  }

  @Override
  public ImageDescriptor getImageDescriptor() {
      return null;
  }

  @Override
  public String getName() {
      // Required!!
      return "Department";
  }

  @Override
  public IPersistableElement getPersistable() {
      return null;
  }

  @Override
  public String getToolTipText() {
      // Required!!
      return "Department";
  }

}

Dirty Manager

DirtyListener.java
package org.o7planning.tutorial.rcpapplication.other;

public interface DirtyListener {
   
   public void fireDirty();
}
DirtyUtils.java
package org.o7planning.tutorial.rcpapplication.other;

import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;

public class DirtyUtils {

  public static void registryDirty(DirtyListener listener,
          Control... controls) {
      if (controls == null) {
          return;
      }

      for (Control control : controls) {

          if (control instanceof Text) {
              Text text = (Text) control;
              text.addVerifyListener(new VerifyListenerImpl(listener));
          }
          // Checkbox or Radio button
          else if (control instanceof Button) {
              Button button = (Button) control;
              button.addSelectionListener(new SelectionListenerImpl(listener));
          }
          // Not support
          else {
              throw new UnsupportedOperationException("Not support for "
                      + control.getClass().getSimpleName());
          }
      }
  }

  static class VerifyListenerImpl implements VerifyListener {
      private DirtyListener listener;

      public VerifyListenerImpl(DirtyListener listener) {
          this.listener = listener;
      }

      @Override
      public void verifyText(VerifyEvent arg0) {
          listener.fireDirty();
      }

  }

  // For Button (Checkbox, Radio).
  static class SelectionListenerImpl implements SelectionListener {

      private DirtyListener listener;

      public SelectionListenerImpl(DirtyListener listener) {
          this.listener = listener;
      }

      @Override
      public void widgetDefaultSelected(SelectionEvent e) {

      }

      @Override
      public void widgetSelected(SelectionEvent e) {
          listener.fireDirty();
      }

  }

}

Content Provider & Label Provider:

AbstractTableContentLabelProvider.java
package org.o7planning.tutorial.rcpapplication.provider;

import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Image;

public abstract class AbstractTableContentLabelProvider implements
      ITableLabelProvider, IStructuredContentProvider {

  @Override
  public void dispose() {

  }

  @Override
  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

  }

  @Override
  public void addListener(ILabelProviderListener listener) {

  }

  @Override
  public boolean isLabelProperty(Object element, String property) {
      return false;
  }

  @Override
  public void removeListener(ILabelProviderListener listener) {

  }

  @Override
  public Image getColumnImage(Object element, int columnIndex) {
      return null;
  }

}
ApplicationWorkbenchWindowAdvisor.java
package org.o7planning.tutorial.rcpapplication;

import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.application.ActionBarAdvisor;
import org.eclipse.ui.application.IActionBarConfigurer;
import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
import org.eclipse.ui.application.WorkbenchWindowAdvisor;
import org.o7planning.tutorial.rcpapplication.editor.DeptEditor;
import org.o7planning.tutorial.rcpapplication.view.DeptListView;
import org.o7planning.tutorial.rcpapplication.view.DeptView;

public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {

   public ApplicationWorkbenchWindowAdvisor(
           IWorkbenchWindowConfigurer configurer) {
       super(configurer);
   }

   public ActionBarAdvisor createActionBarAdvisor(
           IActionBarConfigurer configurer) {
       return new ApplicationActionBarAdvisor(configurer);
   }

   public void preWindowOpen() {
       IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
       configurer.setInitialSize(new Point(400, 300));
       configurer.setShowCoolBar(true);
       configurer.setShowStatusLine(true);
       configurer.setShowPerspectiveBar(true);
       configurer.setTitle("Hello RCP");
   }

   @Override
   public void postWindowOpen() {
       Shell shell = getWindowConfigurer().getWindow().getShell();
       shell.setMaximized(true);

       // Đăng ký khi có sự kiện liên quan đến các thành phần của trang
       // (View, Editor,..)
       PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
               .addPartListener(new IPartListener2() {

                   @Override
                   public void partActivated(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partBroughtToTop(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partClosed(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partDeactivated(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partHidden(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partVisible(IWorkbenchPartReference partRef) {

                   }

                   @Override
                   public void partInputChanged(IWorkbenchPartReference partRef) {

                   }

                   // Khi một View hoặc một Editor được mở.
                   @Override
                   public void partOpened(IWorkbenchPartReference partRef) {
                       System.out.println("Part OPENED: "
                               + partRef.getPartName());
                       IWorkbenchPart part = partRef.getPart(false);
                       if (part instanceof DeptListView) {
                           DeptListView deptListView = (DeptListView) part;

                           DeptEditor deptEditor = (DeptEditor) partRef
                                   .getPage().findView(DeptEditor.ID);
                           
                           // DeptListView lắng nghe thay đổi các thuộc tính từ
                           // DeptEditor.
                           if (deptEditor != null) {
                               deptEditor.addPropertyListener(deptListView);
                           }
                       } else if (part instanceof DeptView) {
                           DeptView deptView = (DeptView) part;

                           DeptEditor deptEditor = (DeptEditor) partRef
                                   .getPage().findView(DeptEditor.ID);
                           // DeptView lắng nghe thay đổi các thuộc tính từ
                           // DeptEditor.
                           if (deptEditor != null) {
                               deptEditor.addPropertyListener(deptView);
                           }
                       } else if (part instanceof DeptEditor) {
                           DeptEditor deptEditor = (DeptEditor) part;
                           DeptView deptView = (DeptView) partRef.getPage()
                                   .findView(DeptView.ID);

                           // DeptView lắng nghe thay đổi các thuộc tính từ
                           // DeptEditor.
                           if (deptView != null) {
                               deptEditor.addPropertyListener(deptView);
                           }

                           DeptListView deptListView = (DeptListView) partRef
                                   .getPage().findView(DeptListView.ID);

                           // DeptListView lắng nghe thay đổi các thuộc tính từ
                           // DeptEditor.
                           if (deptListView != null) {
                               deptEditor.addPropertyListener(deptListView);
                           }
                       }
                   }
               });
   }
}
  • plugin.xml
plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>

  <extension
        id="application"
        point="org.eclipse.core.runtime.applications">
     <application>
        <run
              class="org.o7planning.tutorial.rcpapplication.Application">
        </run>
     </application>
  </extension>
  <extension
        point="org.eclipse.ui.perspectives">
     <perspective
           name="RCP Perspective"
           class="org.o7planning.tutorial.rcpapplication.Perspective"
           id="RCPApplication.perspective">
     </perspective>
  </extension>
  <extension
        point="org.eclipse.ui.views">
     <view
           class="org.o7planning.tutorial.rcpapplication.view.DeptView"
           id="view.dept"
           name="Dept"
           restorable="true">
     </view>
     <view
           class="org.o7planning.tutorial.rcpapplication.view.DeptListView"
           id="view.deptList"
           name="Dept List"
           restorable="true">
     </view>
  </extension>
  <extension
        point="org.eclipse.ui.editors">
     <editor
           class="org.o7planning.tutorial.rcpapplication.editor.DeptEditor"
           default="false"
           id="deptEditor"
           name="Dept Editor">
     </editor>
  </extension>
  <extension
        point="org.eclipse.ui.commands">
     <command
           defaultHandler="org.o7planning.tutorial.rcpapplication.command.DeptCommand"
           id="command.dept"
           name="Dept Command">
     </command>
  </extension>
  <extension
        point="org.eclipse.ui.perspectiveExtensions">
     <perspectiveExtension
           targetID="*">
        <view
              id="view.deptList"
              minimized="false"
              relationship="left"
              relative="org.eclipse.ui.editorss"
              visible="true">
        </view>
        <view
              id="view.dept"
              minimized="false"
              relationship="right"
              relative="org.eclipse.ui.editorss"
              visible="true">
        </view>
     </perspectiveExtension>
  </extension>
  <extension
        point="org.eclipse.ui.menus">
     <menuContribution
           allPopups="false"
           locationURI="menu:org.eclipse.ui.main.menu">
        <menu
              label="Functions">
           <command
                 commandId="command.dept"
                 label="Open Dept"
                 style="push">
           </command>
        </menu>
     </menuContribution>
  </extension>

</plugin>

7- Các thành phần trong Project và giải thích

7.1- Đăng ký lắng nghe các thay đổi từ Editor, View

Khi DeptEditor thay đổi, nó cần thông báo tới DeptListViewDeptView để update lại thông tin. DeptListView & DeptView cần implements  IPropertyListener. View & Editor lắng nghe các sự kiện của nhau, bạn có thể viết code trong method postWindowOpen của ApplicationWorkbenchWindowAdvisor:
deptEditor.addPropertyListener(deptView);

deptEditor.addPropertyListener(deptListView);
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
   .addPartListener(new IPartListener2() {

        .....

       // Khi một View hoặc một Editor được mở.
       @Override
       public void partOpened(IWorkbenchPartReference partRef) {
           IWorkbenchPart part = partRef.getPart(false);
           if (part instanceof DeptListView) {
               DeptListView deptListView = (DeptListView) part;

               DeptEditor deptEditor = (DeptEditor) partRef
                       .getPage().findView(DeptEditor.ID);
               
               // DeptListView lắng nghe thay đổi các thuộc tính từ
               // DeptEditor.
               if (deptEditor != null) {
                   deptEditor.addPropertyListener(deptListView);
               }
           } else if (part instanceof DeptView) {
               DeptView deptView = (DeptView) part;

               DeptEditor deptEditor = (DeptEditor) partRef
                       .getPage().findView(DeptEditor.ID);
               // DeptView lắng nghe thay đổi các thuộc tính từ
               // DeptEditor.
               if (deptEditor != null) {
                   deptEditor.addPropertyListener(deptView);
               }
           } else if (part instanceof DeptEditor) {
               DeptEditor deptEditor = (DeptEditor) part;
               DeptView deptView = (DeptView) partRef.getPage()
                       .findView(DeptView.ID);

               // DeptView lắng nghe thay đổi các thuộc tính từ
               // DeptEditor.
               if (deptView != null) {
                   deptEditor.addPropertyListener(deptView);
               }

               DeptListView deptListView = (DeptListView) partRef
                       .getPage().findView(DeptListView.ID);

               // DeptListView lắng nghe thay đổi các thuộc tính từ
               // DeptEditor.
               if (deptListView != null) {
                   deptEditor.addPropertyListener(deptListView);
               }
           }
       }
   });

7.2- DeptListView

DeptListViewer: Chứa danh sách các phòng ban, khi kích đúp chuột vào một phòng ban nó sẽ được hiển thị trên DeptEditor.
Màn hình thiết kế:
Trên DeptListView bạn cần sét đặt SelectionProvider:
// Đăng ký Selection Provider.
// (Mục đích để tại các nơi khác biết View này đang chọn gì).

this.getSite().setSelectionProvider(tableViewer);
Chú ý:
  • TableViewer, TreeViewer,.. là các class thi hành interface ISelectionProvider 
Bạn có thể tìm hiểu thêm về cách sử dụng các class này tại:

7.3- DeptEditor

  • AbstractBaseEditor.java  (DeptEditor extends AbstractBaseEditor).
private boolean dirty;

// Khi thuộc tính PROP_DIRTY thay đổi, method này sẽ được gọi bởi Workbench.
// Workbench kiểm tra Editor có thay đổi gì không.
// Nếu method trả về true, nút SAVE sẽ sáng, ngược lại nút SAVE tối.
@Override
public boolean isDirty() {
   return this.dirty;
}

// Khi editor thay đổi dữ liệu, gọi method này để thông báo lên Workbench.
protected void setDirty(boolean dirty) {
   if (this.dirty != dirty) {
       this.dirty = dirty;
       
       // Thông báo có thuộc tính PROP_DIRTY thay đổi cho Workbench.
       this.firePropertyChange(IEditorPart.PROP_DIRTY);
   }
}

7.4- DeptCommand

DeptCommand là command sử dụng để mở ra DeptEditor.

Thông thường một command sẽ tạo một đối tượng IEditorInput, mở ra một Editor mới và truyền Input vào. Bạn cũng có thể tìm kiếm các Editor đang mở sẵn trên trang, và truyền mới IEditorInput, các editor này là các Editor có thể tái sử dụng (với một Input mới), và nó cần implements IReusableEditor.
Lấy ra trang hiện tại trên workbench window:
// Get the page
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
IWorkbenchPage page = window.getActivePage();
Department dept = (Department) selectObj;
DeptEditorInput input = new DeptEditorInput(dept);

boolean found = false;
// Các Editor refrence đang mở.
IEditorReference[] eRefs = page.getEditorReferences();
for (IEditorReference ref : eRefs) {
   IEditorPart editor = ref.getEditor(false);
   if (editor != null && editor instanceof DeptEditor) {
       // Restore
       DeptEditor deptEditor = (DeptEditor) ref.getEditor(true);
       found = true;

       boolean saved = true;
       // Nếu editor có thay đổi, save lại.
       if (deptEditor.isDirty()) {
           saved = page.saveEditor(deptEditor, true);
       }
       if (saved) {
           // Sét đặt Input mới cho DeptEditor.
           page.reuseEditor(deptEditor, input);
           deptEditor.showData();
       }
   }
}

8- Chạy ứng dụng

Chú ý: Nếu trong lần đầu tiên chạy ứng dụng không hiển thị các view, nhấn phải vào Perspective Bar, chọn Reset.