RCP 에디터 정리

출처: http://blog.naver.com/PostView.nhn?blogId=unbraid&logNo=124922134 

Editor Overview#

 

  • Editor는 사용자가 Resource(예 : 파일)를 생성하고 편집하는데 이용하는 기본적인 메커니즘이다.

    • 이클립스는 Text Editor, Java Source Editor 같은 기본적인 편집기 뿐만 아니라
    • 플러그인 내역서 편집기처럼 복잡한 MutilPage Editor도 제공한다.
    • 또한, Eclipse Editor와 Extention을 사용해 자신만의 Editor를 만들 수 있다.

 

 

 

  • Editor는 org.eclipse.ui.IEditorPart 인터페이스를 구현해야 한다.

    • 일반적으로 Editor는 EditorPart의 하위 클래스이므로 간접적으로 WrokbenchPart의 하위 클래스이다.
    1. public abstract class EditorPart extends WorkbenchPart implements IEditorPart{
    2.  

 

  • Editor는 IEditorSite를 포함되며, IEditorSite는 IWorkbenchPage에 포함된다.

    • IEditorPart

      1. public abstract class EditorPart extends WorkbenchPart implementsIEditorPart {
      2.  

        public abstract class WorkbenchPart extends EventManager implements
                IWorkbenchPart2, IExecutableExtension, IWorkbenchPartOrientation {

      3.  
      4. public interface IEditorPart extends IWorkbenchPart, ISaveablePart {
      5.  
      6. public interface IWorkbenchPart extends IAdaptable {
      7.  
  • IWorkbenchPage는 Editor 자체보다는 IEditorReference의 인스턴스를 참조하고, 이를 통해 Editor를 정의하는 플러그인을 로딩하지 않고도 Editor의 목록을 관리할 수 있다.

 

Editor와 View 차이#

 

  • Editor와 View 모두 WorkbenchPart 클래스와 IWorkbenchPart 인터페이스는 몇 가지 공통 부분을 가지고 있다.
  • 하지만 여기에 중요한 차이점이 있는데 아래와 같다.

    • Editor는 'Open-Modify-Save' 패러다임을 따르는 반면, View는 수행된 Action은 즉시 작업공간과 하위 리소스의 상태에 영향을 미친다.
    • Editor는 이클립스 내에서 공통 영역에 사용되는 반면에, View는 각 Perspective에 다른 영역을 사용한다.
    • Editor는 일반적으로 리소스 기반으로 동작하지만, View는 단일 리소스나 복수의 리소스 또는 Memory, Network 상태, Error 등의 정보를 다루는데 사용된다.

 

 

 

Editor 선언#

 

  • Editor 생성은 Plugin Project Wizard에서 Editor 지정을 통해서 만들어 질 수 있다.

 

  • 만약 이미 플러그인이 존재한 상태라면, 아래와 같은 방법을 사용해야 한다.

    • Plugin 내역서 파일에서 Editor 정의
    • EditorPart 코드 작성

 

  • plugin.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
    <?eclipse version="3.2"?>
    <plugin>
       <!-- 앞에서 했던 부분 -->
       <extension
             point="org.eclipse.ui.views">
          <category
                name="QualityEclipse"
                id="com.qualityeclipse.favorites">
          </category>
          <view
                name="Favorites"
                icon="icons/sample.gif"
                category="com.qualityeclipse.favorites"
                class="com.qualityeclipse.favorites.views.FavoritesView"
                id="com.qualityeclipse.favorites.views.FavoritesView">
          </view>
       </extension>

       <extension point="org.eclipse.ui.commands">
          <category
                description="Commands related to the Favorites View"
                id="com.qualityeclipse.favorites.commands.category"
                name="Favorites">
          </category>
          <command
                categoryId="com.qualityeclipse.favorites.commands.category"
                description="Open the Favorites view if it is not already visible"
                id="com.qualityeclipse.favorites.commands.openView"
                name="Open Favorites View">
          </command>
          <command
                categoryId="com.qualityeclipse.favorites.commands.category"
                description="Add selected items to the Favorites view"
                id="com.qualityeclipse.favorites.commands.add"
                name="Add">
          </command>
          ...
       </extension>
       <extension
             point="org.eclipse.ui.menus">
          <menuContribution
                locationURI="menu:org.eclipse.ui.main.menu?after=additions">
             <menu
                   id="com.qualityeclipse.favorites.menus.favoritesMenu"
                   label="Favorites"
                   mnemonic="v">
                <command
                      commandId="com.qualityeclipse.favorites.commands.openView"
                      icon="icons/sample.gif"
                      id="com.qualityeclipse.favorites.menus.openFavoritesView"
                      mnemonic="O">
                   <visibleWhen
                         checkEnabled="false">
                      <with
                            variable="activeContexts">
                         <iterate
                               ifEmpty="false"
                               operator="or">
                            <equals
                                  value="com.qualityeclipse.favorites.workbenchActionSet">
                            </equals>
                         </iterate>
                      </with>
                   </visibleWhen>
                </command>
             </menu>
          </menuContribution>
          ...
       </extension>
       <extension point="org.eclipse.ui.handlers">
          <handler class="com.qualityeclipse.favorites.handlers.OpenFavoritesViewHandler"
                   commandId="com.qualityeclipse.favorites.commands.openView">
          </handler>
          ...
       </extension>
       <extension
             point="org.eclipse.ui.actionSets">
          <actionSet
                id="com.qualityeclipse.favorites.workbenchActionSet"
                label="Favorites ActionSet"
                visible="true">
          </actionSet>
       </extension>
       <extension
             point="org.eclipse.core.expressions.propertyTesters">
          <propertyTester
                class="com.qualityeclipse.favorites.propertyTester.FavoritesTester"
                id="com.qualityeclipse.favorites.propertyTester"
                namespace="com.qualityeclipse.favorites"
                properties="isFavorite, notFavorite"
                type="java.lang.Object">
          </propertyTester>
       </extension>
       <extension
             point="org.eclipse.ui.contexts">
          <context
                id="com.qualityeclipse.properties.editor.context"
                name="Properties Editor Context"
                parentId="org.eclipse.ui.textEditorScope">
          </context>
       </extension>
  2.    <!-- 단축키 지정 -->
       <extension
             point="org.eclipse.ui.bindings">
          <key
                commandId="com.qualityeclipse.favorites.commands.add"
                contextId="org.eclipse.ui.textEditorScope"
                schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
                sequence="Ctrl+Shift+A">
          </key>
          ...
       </extension>
       <extension
             point="org.eclipse.ui.editors">
          <editor
                class="com.qualityeclipse.favorites.editors.PropertiesEditor"
                default="false"
                extensions="properties"
                icon="icons/sample.gif"
                id="com.qualityeclipse.properties.editor"
                name="Properties Editor">
          </editor>
       </extension>
      

    </plugin>

 

 

  • class

    • Editor에 해당 하는 클래스로 IEditorPart를 구현하고 있음.
  • contributorClass

    • Workbench 메뉴와 Toolbar에 Action을 추가하는 클래스를 지정.
  • extensions

    • Editor가 지원할 파일 type을 지정한다.
    • 여러개는 ','로 구분하여 입력한다.
  • icon

    • Editor의 title bar에 나타나는 이미지
  • id
  • name

 

  • command

    • 외부 Editor를 실행하기 위해 사용되는 명령어.
    • 실행될 프로그램은 시스템 path에 잡혀있거나 plugin 디렉토리에 있어야한다.
    • 이 속성과 class, luncher 속성은 상호 배타적이다 (?)

 

  • default (true, false)

    • 생성된 editor가 extension으로 지정된 파일 유형에 대한 Default Editor로 사용할지 여부.
    • 당연히 여러개의 Editor가 등록되어있을 때 의미가 있음.

 

  • filenames
  • luncher
  • matchingStrategy

 

 

  • 이들 지정된 값들은 아래 UI를 통해서도 수정가능하다.

 

 

 

EditorPart#

 

  • Editor를 정의하는 코드는 IEditorPart 인터페이스를 구현하는 클래스가 되어야한다.

    • 이 장의 샘플로 제공되는 PropertiesEditor는 MultiPageEditorPart를 상속받고 있다.
    1. /**
       * Editor for modifying *.property files containing content in the
       * typical key=value format
       */
      public class PropertiesEditor extends MultiPageEditorPart {

 

 

Editor Method #

 

EditorPart #

 

EditorPart는 MVC 모델의 Control 이라 생각하면 좋을 듯하다.

 

  • createPartControl(Composite)

    • Editor의 Control들을 생성한다.
    1. /**
           * The <code>MultiPageEditor</code> implementation of this
           * <code>IWorkbenchPart</code> method creates the control for the
           * multi-page editor by calling <code>createContainer</code>, then
           * <code>createPages</code>. Subclasses should implement
           * <code>createPages</code> rather than overriding this method.
           *
           * @param parent
           *            The parent in which the editor should be created; must not be
           *            <code>null</code>.
           */
          public final void createPartControl(Composite parent) {
    • createContainer()
    1.     /**
           * Creates an empty container. Creates a CTabFolder with no style bits set,
           * and hooks a selection listener which calls <code>pageChange()</code>
           * whenever the selected tab changes.
           *
           * @param parent
           *            The composite in which the container tab folder should be
           *            created; must not be <code>null</code>.
           * @return a new container
           */
          private CTabFolder createContainer(Composite parent) {

 

  • dispose()

    • Editor가 닫힐 때 callback된다.
    • 보통 Editor가 사용하고 있는 Resource를 반환하는 작업을 수행한다.

 

  • doSave(IProgressMonitor)

    • 기능

      1.     /* (non-Javadoc)
             * Saves the contents of this editor.
             * <p>
             * Subclasses must override this method to implement the open-save-close lifecycle
             * for an editor.  For greater details, see <code>IEditorPart</code>
             * </p>
             *
             * @see IEditorPart
             */
            public abstract void doSave(IProgressMonitor monitor);
    • 내 용이 수정되면 Editor는 firePropertyChange(int) 메소드를 호출해서 수정되었음을 다른 구성요소에게 알려야 한다. 저장되면 다시 firePropertyChange(int)를 호출하여 Editor의 내용과 저장내용이 일치함을 등록된 Listener들에게 알린다.

 

  • doSaveAs()

    • optional.

 

  • gotoMarker(IMarker)

    • Marker가 지정한 곳으로 Editor의 cursor와 선택사항들을 반영시킨다.
    1.    /**
          * Sets the cursor and selection state for an editor to
          * reveal the position of the given marker.
          */
         public void gotoMarker(IMarker marker) {
    • IMarker

      1. /**
         * Markers are a general mechanism for associating notes and meta-data with
         * resources.
      2.  * ...
      3.  */
      4. public interface IMarker extends IAdaptable {

            /**
             * Base marker type.
             *
             * @see #getType()
             */
            public static final String MARKER = ResourcesPlugin.PI_RESOURCES + ".marker"; //$NON-NLS-1$

            public static final String TASK = ResourcesPlugin.PI_RESOURCES + ".taskmarker"; //$NON-NLS-1$

            public static final String PROBLEM = ResourcesPlugin.PI_RESOURCES + ".problemmarker"; //$NON-NLS-1$

            public static final String TEXT = ResourcesPlugin.PI_RESOURCES + ".textmarker"; //$NON-NLS-1$

            public static final String BOOKMARK = ResourcesPlugin.PI_RESOURCES + ".bookmark"; //$NON-NLS-1$

 

  • init(IEditorSite, IEditorInput) 

    • 설명
    1. /**
          * Initializes this editor with the given editor site and input.
          * This method is automatically called shortly after the part is
          * instantiated, marking the start of the part's lifecycle.
          * In our case, we assert that we are editing a file.
          */
         public void init(IEditorSite site, IEditorInput input)
    • IEditorSite

      • 으음... (?)
      1. /**
         * The primary interface between an editor part and the workbench.
         * <p>
         * The workbench exposes its implemention of editor part sites via this
         * interface, which is not intended to be implemented or extended by clients.
         * </p>
         */
        public interface IEditorSite extends IWorkbenchPartSite {
    • IEditorInput

 

  • isDirty()
  • isSaveAsAllowed()
  • setFocus()

 

 

MultiPageEditorPart Method#

 

  • addPage(Control)

    • 주어진 Control을 포함하는 새 페이지를 생성하고 추가한다.
    • 이 Control 인자값으로 null을 줄고, 나중에 setControl로 지정할 수 있다.

 

  • addPage(IEditorPart, IEditorInput)

    • 설명
    1.     /**
           * Creates and adds a new page containing the given editor to this
           * multi-page editor. This also hooks a property change listener on the
           * nested editor.
           *
           * @param editor
           *            the nested editor
           * @param input
           *            the input for the nested editor
           * @return the index of the new page
           * @exception PartInitException
           *                if a new page could not be created
           *
           * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
           *      property change events from the nested editor
           */
          public int addPage(IEditorPart editor, IEditorInput input)

 

  • createPages()
  • getContainer()

    • 추가 설명 : 이 클래스를 override 하지마라.
    1. /**
           * Returns "the composite control" containing this multi-page editor's pages.
           * This should be used as the parent when creating controls for the
           * individual pages. That is, when calling <code>addPage(Control)</code>,
           * the passed control should be a child of this container.
           * <p>
           * Warning: Clients should not assume that the container is any particular
           * subclass of Composite. The actual class used may change in order to
           * improve the look and feel of multi-page editors. Any code making
           * assumptions on the particular subclass would thus be broken.
           * </p>
           * <p>
           * Subclasses should not override this method
           * </p>
           *
           * @return the composite, or <code>null</code> if
           *         <code>createPartControl</code> has not been called yet
           */
          protected Composite getContainer() {

 

 

  • setPageImage(int, Image)
  • setPageText(int, String)

 

 

Editor Control(페이지 생성) #

 

  • 이 장의 예제에서 작성하는 PropertiesEditor 코드에서 'Properties'와 'Source' 페이지를 생성한다.

    • Properties 페이지

      • key, value로 구성된 데이터를 가진 Tree 구조.
    • Source 페이지

      • 파일 내용을 그냥 텍스트로 보여준다.
    • 위의 두 페이지는 서로 동기화 된다.

 

 

  • 먼저 MultiPageEditorPart 클래스를 상속 받는 PropertiesEditor 클래스 생성.

    • init() 메소드에서 Content Type이 생각했던 (IFileEditorInput) 타입과 같은지 체크해준다.

 

  • 다음으로 createPages()를 overriding 하여, Source와 Properties 페이지를 넣다.

    1. /**
          * Creates the Source and Properties pages
          * of this multi-page editor.
          */
         protected void createPages() {
            createPropertiesPage();
            createSourcePage();
            updateTitle();
            initTreeContent();
            initTreeEditors();
            createContextMenu();
            initKeyBindingContext();
            initUndoRedo();
         }

 

  • createPropertiesPage()

    • Properties 페이지를 생성하고,
    • Column에 대해 설정하자.
    1. void createPropertiesPage() {
            Composite treeContainer = new Composite(getContainer(), SWT.NONE);
            TreeColumnLayout layout = new TreeColumnLayout();
            treeContainer.setLayout(layout);
           
            treeViewer = new TreeViewer(treeContainer, SWT.MULTI | SWT.FULL_SELECTION);
            Tree tree = treeViewer.getTree();
            tree.setHeaderVisible(true);
           
            keyColumn = new TreeColumn(tree, SWT.NONE);
            keyColumn.setText("Key");
            layout.setColumnData(keyColumn, new ColumnWeightData(2));
        
            valueColumn = new TreeColumn(tree, SWT.NONE);
            valueColumn.setText("Value");
            layout.setColumnData(valueColumn, new ColumnWeightData(3));
           
            int index = addPage(treeContainer);
            setPageText(index, "Properties");
            getSite().setSelectionProvider(treeViewer);
         }

     

  • createSourcePage()

    1. void createSourcePage() {
            try {
               textEditor = new TextEditor();
               int index = addPage(textEditor, getEditorInput());
               setPageText(index, "Source");
            }
            catch (PartInitException e) {
               FavoritesLog.logError("Error creating nested text editor",e);
            }
         }

 

  • updateTitle()

    • IEditorInput을 확실히 모르니 이해가 안된다...

      • 그림 8-3을 보고 이해하면, 'test.properties' 라는 텍스트가 나타니는 것으로 보아서 Data 입력 파일, 즉 IEditorInput에서 사용할 부분 가리키는 것 같다.
    1.   /**
          * Update the editor's title based upon the content being edited.
          */
         void updateTitle() {
            IEditorInput input = getEditorInput();
            setPartName(input.getName());
            setTitleToolTip(input.getToolTipText());
         }
    • setPartName()

      1. /**
             * Sets the name of this part. The name will be shown in the tab area for
             * the part. Clients should call this method instead of overriding getPartName.
             * Setting this to the empty string will cause a default part name to be used.
             *
             * <p>
             * setPartName and setContentDescription are intended to replace setTitle.
             * This may change a value that was previously set using setTitle. 
             * </p>
             *
             * @param partName the part name, as it should be displayed in tabs.
             *
             * @since 3.0
             */
            protected void setPartName(String partName) {

       

  • setFocus()
  • gotoMarker()
  • isSaveAsAllowed()

 

 

Editor Model(Data) #

 

  • 이제 데이터의 내용을 Properties Page의 Tree에 넣어보자.

    • 먼저 Model을 만들고,
    • 이것을 절적히 출력하기 위해서 ContentProvider와 LabelProvier를 구현해야한다.

ContentProvider는 Data를 표현하고, LabelProvider는 이것을 어떻게 보여질지에 대해서 구현하는 클래스로 이해하면 되지 않을까? 

 

A : 비슷하네..

Page.341

  • content provider extracts property elements to be displayed
  • LabelProvider로 부터 얻은 행 요소 객체를 각 셀에 출력할 이미지와 텍스트로 변환한다.

 

  • 추가 구현 사항

    • 텍스트 파서를 좀 더 잘 만들어보자.

      경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
      어린이용이며, 설치가 필요없는 브라우저 게임입니다.
      https://s1004games.com

      • 독립된 클래스로 만들고,
      • 여러 줄로 구성된 값도 해석하는 기능을 추가해보자.

 

  • PropertyElement

    • PropertyElement의 abstract 클래스.

 

  • PropertyEntry

    1. public class PropertyEntry extends PropertyElement
    • Property 파일의 데이터를 key, value 쌍으로 표현하기 위한 클래스.

 

  • PropertyCategory

    1. public class PropertyCategory extends PropertyElement
    • Property 항목의 그룹을 나타내기 위한 클래스.
    • 그룹명은 '#xxx' 형식으로 얻는다.

 

  • PropertyFile

    1. /**
       * The editor model for a property file.
       */
      public class PropertyFile extends PropertyElement
    • PropertyEntry와 PropertyCategory를 가지고 클래스.
    • PropertyFileListener 인터페이스를 사용

      • 등록된 Listener(PropertiesEditor 등)에 모델(Data)이 변경되었음을 알리는 이벤트를 전달한다.
      1.    private List<PropertyFileListener> listeners;
      2.    ...
      3.    void addPropertyFileListener(PropertyFileListener listener) {
              if (!listeners.contains(listener))
                 listeners.add(listener);
           }

 

 

ContentProvider #

 

  • ContentProvider는 Tree에 나타날 행들을 Parent/Child 관계에 따라 제공하지만, 셀 단위의 내용을 제공하지는 않는다.

    • 셀 단위의 내용은 LabelProvider에서 제공한다.

 

  1. /**
     * A content provider mediates between the viewer's model and the
     * viewer itself. In our case, this content provider extracts property
     * elements to be displayed one per row in the editor.
     */
    public class PropertiesEditorContentProvider
       implements ITreeContentProvider

 

 

 

LabelProvider #

 

  • ContentProvider로 부터 얻은 행 요소 객체를 각 셀에 출력할 이미지와 텍스트로 변환한다.

    1. /**
       * A label provider maps an element of the viewer's model to an
       * optional image and optional text string used to display the element * in the viewer's control. In our case, this label provider extracts
       * text from the {@link PropertyElement} to display in the editor.
       */
      public class PropertiesEditorLabelProvider extends LabelProvider
         implements ITableLabelProvider
      {

     

 

 

ContentProvider와 LabelProvider를 연결 #

 

  • 위에서 만든 PropertyFile, ContentProvider, LabelProvider를 사용해서

    • TextEditor로부터 데이터를 읽어서 TreeView에 뿌려보자.

 

  • 아래 createPages()에서 호출하고 있다.

    1.    protected void createPages() {
            createPropertiesPage();
            createSourcePage();
            updateTitle();
            initTreeContent();
            ...
         }

     

  • initTreeContent()

    1.   /**
          * Initialize the model behind the editor.
          */
         void initTreeContent() {
            treeContentProvider = new PropertiesEditorContentProvider();
            treeViewer.setContentProvider(treeContentProvider);
            treeLabelProvider = new PropertiesEditorLabelProvider();
            treeViewer.setLabelProvider(treeLabelProvider);
           
            // Reset the input from the text editor content
            // after the editor initialization has completed.
            treeViewer.setInput(new PropertyFile(""));
            treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
               public void run() {
                  updateTreeFromTextEditor();
               }
            });
            treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
         }

 

  • updateTreeFromTextEditor()

    • TextEditor가 input 데이터가 된다.
    1.    /**
          * Called when the properties page should be updated
          * based upon new input from the source page
          */
         void updateTreeFromTextEditor() {
            PropertyFile propertyFile = (PropertyFile) treeViewer.getInput();
            propertyFile.removePropertyFileListener(propertyFileListener);
            propertyFile = new PropertyFile(
               textEditor
                  .getDocumentProvider()
                  .getDocument(textEditor.getEditorInput())
                  .get());
            treeViewer.setInput(propertyFile);
            propertyFile.addPropertyFileListener(propertyFileListener);
         }

 

  • 위의 코드에서 왜 propertyFile.removePropertyFileListener(), propertyFile.addPropertyFileListener()를 호출하는지 궁금하지 않는가?

    • 뒤에서 설명.

 

 

 

Edit #

 

  • Properties 페이지에서 내용을 수정할 수 있도록 해보자.

    • 참고 : Text Editor에서 Content를 다루는 예제는 14.2.4절 '마커 해결-빠른 수정' (p520을 참고)

 

  1.    protected void createPages() {
          ...
          initTreeContent();
          initTreeEditors();
          ...
       }

 

Cell Editor #

 

  • Cell Editor는 특정 객체에서 어떤 부분을 편집.

    • 수정

      • getCellEditor() 셀 Editor 정의
      • canEdit()
      • getValue()
      • setValue()
    • Cell Change 리스너.

      • ICellEditorListener.
    • Cell Validator

      • ICellEditorValidator.
    • Cell Editor 활성화

      • addEditorActivationListener()

 

  1.   /**
        * Initialize the cell editors in the tree
        */
       private void initTreeEditors() {
          TreeViewerColumn column1 = new TreeViewerColumn(treeViewer, keyColumn);
          TreeViewerColumn column2 = new TreeViewerColumn(treeViewer, valueColumn);
         
          column1.setLabelProvider(new ColumnLabelProvider() {
             public String getText(Object element) {
                return treeLabelProvider.getColumnText(element, 0);
             }
          });
          ...
         
          column1.setEditingSupport(new EditingSupport(treeViewer) {
             TextCellEditor editor = null;

             protected boolean canEdit(Object element) {
                return true;
             }

             protected CellEditor getCellEditor(Object element) {
                if (editor == null) {
                   Composite tree = (Composite) treeViewer.getControl();
                   editor = new TextCellEditor(tree);
  2.                /* Cell Validator */
                   editor.setValidator(new ICellEditorValidator() {
                      public String isValid(Object value) {
                         if (((String) value).trim().length() == 0)
                            return "Key must not be empty string";
                         return null;
                      }
                   });
  3.                /* 사용자가 잘못된 값을 입력했을 때 어떻게 알려줄지를 구현한다. */
                   editor.addListener(new ICellEditorListener() {
                      public void applyEditorValue() {
                         setErrorMessage(null);
                      }
                      public void cancelEditor() {
                         setErrorMessage(null);
                      }
                      public void editorValueChanged(
                            boolean oldValidState,
                            boolean newValidState) {
                         setErrorMessage(editor.getErrorMessage());
                      }
                      private void setErrorMessage(String errorMessage) {
                         getEditorSite().getActionBars()
                               .getStatusLineManager()
                               .setErrorMessage(errorMessage);
                      }
                   });
                }
                return editor;
             }

             protected Object getValue(Object element) {
                return treeLabelProvider.getColumnText(element, 0);
             }

             protected void setValue(Object element, Object value) {
                if (value == null)
                   return;
                String text = ((String) value).trim();
                if (element instanceof PropertyCategory)
                   ((PropertyCategory) element).setName(text);
                if (element instanceof PropertyEntry)
                   ((PropertyEntry) element).setKey(text);
             }
          }); 
  4.      
  5.       /* Alt 눌렀을 때, Cell을 Editor할 수 있다. */
  6.       treeViewer.getColumnViewerEditor().addEditorActivationListener(
                new AltClickCellEditListener());
  7. }

 

 

 

 

Editor life cycle #

 

  • 일반적으로 Editor는 'Open-Modify-Save-Close'와 같은 life cycle을 가진다.

 

  • Editor Open시에 init(IeditorSite, IEditorInput) 메소드를 호출해서 편집기의 기본 Content를 설정한다.

 

  • Content가 수정되면, Editor는 firePropertyChange(int) 메소드를 호출해서 수정되었음을 다른 구성요소에 알려야 한다.

    • dirty 값이 설정되어있을 때, 상황에 따라 여러 동작을 한다.

      • Editor title bar 수정
      • title bar 앞에 '*' 붙이기
      • 'Save' 메뉴 활성화

 

  • Editor Close시에 dirty 값이 true이면 Content를 저장한다.

 

 

수정사항이 있는 Editor #

 

  • 수정이 있을 때,

    • 마지막 저장후에 Content가 수정되었는지 확인할 수 있어야한다.
    1.   /**
          * Called when a cell editor has modified the editor model.
          */
         public void treeModified() {
            boolean wasDirty = isDirty();
            isPageModified = true;
            if (!wasDirty)
               firePropertyChange(IEditorPart.PROP_DIRTY);
         }

     

 

  • Source 페이지(Text Editor)가 수정되었을 때 Property 페이지에서 인지하기 위해서.

    1. /**
          * Handles a property change notification from a nested editor. In
          * our case, the <code>isPageModified</code> field is adjusted as
          * appropriate and superclass is called to notify listeners of the
          * change.
          */
         protected void handlePropertyChange(int propertyId) {
            if (propertyId == IEditorPart.PROP_DIRTY)
               isPageModified = isDirty();
            super.handlePropertyChange(propertyId);
         }

 

  • MultiPageEditorPart의 경우.

    • addPage() 메소드에서 Editor가 수정될 때마다 다른 Part 들에게 알린다.
    1.     /**
           * Creates and adds a new page containing the given editor to this
           * multi-page editor. The page is added at the given index. This also hooks
           * a property change listener on the nested editor.
           *
           *  ...
           * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
           *      property change events from the nested editor
           */
          public void addPage(int index, IEditorPart editor, IEditorInput input)
                  throws PartInitException {
              IEditorSite site = createSite(editor);
              // call init first so that if an exception is thrown, we have created no
              // new widgets
              editor.init(site, input);
              Composite parent2 = new Composite(getContainer(),
                      getOrientation(editor));
              parent2.setLayout(new FillLayout());
              editor.createPartControl(parent2);
              editor.addPropertyListener(new IPropertyListener() {
                  public void propertyChanged(Object source, int propertyId) {
                      MultiPageEditorPart.this.handlePropertyChange(propertyId);
                  }
              });
              // create item for page only after createPartControl has succeeded
              Item item = createItem(index, parent2);
              // remember the editor, as both data on the item, and in the list of
              // editors (see field comment)
              item.setData(editor);
              nestedEditors.add(editor);
          }

 

 

페이지 전환 #

 

  • Property와 Source 페이지 사이에서 전환할 때는 Properties 페이지에서 수정된 내용을 자동으로 Source 페이지로 반영시켜야 하며 반대로도 동작해야 한다.

    • 이 구현을 위해서 pageChange(int) 메소드를 override 해야한다.
    1. /**
          * Notifies this multi-page editor that the page with the given id has been
          * activated. This method is called when the user selects a different tab.
          */
         protected void pageChange(int newPageIndex) {
            switch (newPageIndex) {
               case 0 :
                  if (isDirty())
                     updateTreeFromTextEditor();
                  setTreeUndoRedo();
                  break;
               case 1 :
                  if (isPageModified)
                     updateTextEditorFromTree();
                  setTextEditorUndoRedo();
                  break;
            }
            isPageModified = false;
           
            super.pageChange(newPageIndex);
         }

 

 

Save Content #

 

  • 현재 구현은 Text Editor에서 Content를 파일에 저장하고 있기 때문에, Properties 페이지의 변경 사항이 사용자가 Source 페이지로 전환할 때까지 저장되지 않는다.

    • 그래서 저장하기 전에 Text Editor의 내용을 갱신하도록 수정하자.
    • 이 때, 이 작업을 IProgressMonitor를 사용하여 구현하자.

 

  1. /**
        * Saves the contents of this part. If the save is successful, the
        * part should fire a property changed event reflecting the new
        * dirty state (<code>PROP_DIRTY</code> property).
        */
       public void doSave(IProgressMonitor monitor) {
          if (getActivePage() == 0 && isPageModified)
             updateTextEditorFromTree();
          isPageModified = false;
          textEditor.doSave(monitor);
       }

 

 

 

Editor Action #

 

 

  • Context Menu

    • 액션 생성, Context Menu 작성, Context Menu 동적 구성
  • Editor Contributor

    • Global Action
    • top-level Menu
    • toolbar button
    • keyboard action
  • Undo, Redo

 

Editor 연결 #

 

 

RFRS guideline. #

 

 

 

 

 

 

 

 

  1.    /**
         * Returns the input for this editor.  If this value changes the part must
         * fire a property listener event with <code>PROP_INPUT</code>.
         *
         * @return the editor input
         */
        public IEditorInput getEditorInput();

  2.     /**
         * Returns the site for this editor.
         * This method is equivalent to <code>(IEditorSite) getSite()</code>.
         * <p> 
         * The site can be <code>null</code> while the editor is being initialized.
         * After the initialization is complete, this value must be non-<code>null</code>
         * for the remainder of the editor's life cycle.
         * </p>
         *
         * @return the editor site; this value may be <code>null</code> if the editor
         *         has not yet been initialized
         */
        public IEditorSite getEditorSite();

        /**
         * Initializes this editor with the given editor site and input.
         * <p>
         * This method is automatically called shortly after the part is instantiated.
         * It marks the start of the part's lifecycle. The
         * {@link IWorkbenchPart#dispose IWorkbenchPart.dispose} method will be called
         * automically at the end of the lifecycle. Clients must not call this method.
         * </p><p>
         * Implementors of this method must examine the editor input object type to
         * determine if it is understood.  If not, the implementor must throw
         * a <code>PartInitException</code>
         * </p>
         * @param site the editor site
         * @param input the editor input
         * @exception PartInitException if this editor was not initialized successfully
         */
        public void init(IEditorSite site, IEditorInput input)
                throws PartInitException;
  3.  

 

 

 

 

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
35 [EMF] EMF Tutorial EMF 튜터리얼 file 졸리운_곰 2023.08.23 21
34 Eclipse RAP Tutorial for Beginners - Workbench Application (OLD) file 졸리운_곰 2021.01.30 66
33 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.27 42
32 [Eclipse] GEF entry series (10, an implementation of the form) file 졸리운_곰 2019.11.25 45
31 GEF Programmer Guide 번역 졸리운_곰 2019.11.25 53
30 Learn Eclipse GMF in 15 minutes file 졸리운_곰 2019.11.20 41
» RCP 에디터 정리 졸리운_곰 2019.11.20 122
28 다른 그림과 관련하여 GEF 편집기 레이아웃에서 그림의 위치 제한 조건을 동적으로 계산 Dynamically calculating the position constraints for a figure in a GEF editor layout in relation to another figure file 졸리운_곰 2019.11.20 90
27 RCP 등에서 .mf 파일로 다른 프로젝트 익스포트 포함시 라이브러리(메소드)를 찾지 못할 때, Eclipse RCP - cant resolve importing libraries in final build file 졸리운_곰 2019.10.15 181
26 ESE2006-EclipseModelingSymposium15_GMF.pdf file 졸리운_곰 2019.09.21 64
25 GMF_Creation_Review.pdf file 졸리운_곰 2019.09.21 74
24 Eclipse EMF and GMF Tutorial file 졸리운_곰 2019.09.21 46
23 GMF Tutorial/ko file 졸리운_곰 2019.09.20 162
22 Model Driven Architecture approach to domain of graphical editors file 졸리운_곰 2019.09.20 44
21 Single_Sourcing_RAP_RCP_en.pdf file 졸리운_곰 2019.05.15 27
20 Rich client platform 설명 및 배우기 참고 졸리운_곰 2019.05.15 89
19 Rich Ajax Platform, Part 1: 소개 file 졸리운_곰 2019.05.15 127
18 또 하나의 크로스 플랫폼: Eclipse RAP file 졸리운_곰 2019.05.15 143
17 Eclipse 4 RCP 튜토리얼(완료) file 졸리운_곰 2019.05.14 682
16 Updating UI in Eclipse RCP 졸리운_곰 2015.11.07 183
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED