Eclipse Editor: Partially editable/read-only editor Part 1

Restricting any keyboard input  in read-only section

Eclipse plug-in development framework provides a robust editor feature. Given default implementation take care of each and every single requirements of a source editor like color coding, keyword highlighting, content assist , partitions and many more. 9Go through the another series of editor development on my blog for more details about building editor.)

Eclipse Editor: Partially editable/read-only editor Series

Many time we have very strange requirements which standard eclipse framework doesn’t support or there is no straight forward way to achieve it. Many times source files are auto generated by some designing tools and such file can only be editable in the designing tool if it is in designer tool understandable format. For example Netbeans provides design tool for swing based application and generate source files for you. Netbeans doesn’t allow to update auto-generated code when you open the source file source editor. This was IDE prevents unwanted modification to keep the file compatible with  designer tool. User is only allowed to edit what they supposed to edit.

In this series I am going to explain how we can achieve partially editable source editor using eclipse plug-in development framework.

Here I have taken hypothetical requirement that given xml file whatever xml section falls in between comment line  <!--auto-generated-code-start--> and <!--auto-generated-code-end—> is read only section. When user opens such file in our custom xml editor, xml section in between these two markups is read-only.

This Lets start with creating tutorial will focus on restricting any keyboard input in read-only section.

Lets start with creating eclipse plug-in project with sample XML editor project.  Here I am going to going to update default XML editor implementation to achieve our goal.

2012-05-12_1240

We should have utility class which can find out exact position of read-only section with help of <!--auto-generated-code-start--> and <!--auto-generated-code-end—> markups. Here I have written simple utility class with iterate over the document lines and compare each line text with start and end marks. Once it identifies the read-only section then it return position with help of offset and length,

Here offset is starting point of first character of read-only block from starting of file that is index 1 and length is length of read-only section from starting offset.

Here is the sample implementation of utility class.

package sample.xml.editor.editors;
 
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
 
 
public class CustomEditorSupport {
    
    private XMLEditor editor = null;
    
    public static final String START_MARK = "<!--auto-generate-code-start-->";
    public static final String END_MARK ="<!--auto-generate-code-end-->";
 
    public CustomEditorSupport(XMLEditor editor) {
        this.editor = editor;
    }
    
    /**
     * Return starting offset and length of read-only section in currently opened XML document.
     * @return
     */
    private Position getReadOnlySection(){
        try {
            IDocument doc = editor.getDocument();
            int lineCount = doc.getNumberOfLines();
            int startOffset = -1, length = 0;
            /*It is assumed that there will be only one read-only block present (for simplicity)*/
            for(int i=0;i<lineCount; i++){
                int lineStartOffset = doc.getLineOffset(i);
                int lineLength = doc.getLineLength(i);
                String lineText = doc.get(lineStartOffset, lineLength);
                if(lineText!=null && lineText.startsWith(START_MARK)){
                    startOffset = lineStartOffset;
                } else if (lineText!= null && lineText.startsWith(END_MARK)){
                    length = (lineStartOffset + lineLength) - startOffset;
                    break;
                }
            }
            if(startOffset != -1 && length >0){
                return new Position(startOffset,length);
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * Return true or false based on the parameter offset position.
     * @param offset
     * @param length
     * @return
     */
    public boolean isInsideReadOnlyBlock(int offset, int length){
        Position readOnlyBLock = getReadOnlySection();
        if(readOnlyBLock != null && readOnlyBLock.overlapsWith(offset, length)){
            return true;
        }
        return false;
    }
}

The main part of this implementation is overriding the EventConsumer and restrict editing in read-only section. This can be achieved by providing providing event consumer on editor source viewer. This custom consumer filters any edit happened in read-only section by stopping respective event processing using Event.doit flag to false;



@Override
public void createPartControl(Composite parent) {
    super.createPartControl(parent);
    /* Install Keyboard event consumer to disable any editing  read only
     * code section */
    getSourceViewer().setEventConsumer(new IEventConsumer() {
        @Override
        public void processEvent(VerifyEvent event) {
            int offset = event.start;
            if(editorSupport.isInsideReadOnlyBlock(offset, 0)){
                // swallow the event.
                event.doit = false;
                return;
            }
        }
    });
}

Here is the complete implementation of XMLEditor.java



package sample.xml.editor.editors;
 
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IEventConsumer;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.texteditor.IDocumentProvider;
 
public class XMLEditor extends TextEditor {
 
    private ColorManager colorManager;
    public CustomEditorSupport editorSupport = null;
 
    public XMLEditor() {
        super();
        editorSupport = new CustomEditorSupport(this);
        colorManager = new ColorManager();
        setSourceViewerConfiguration(new XMLConfiguration(colorManager));
        setDocumentProvider(new XMLDocumentProvider());
    }
    public void dispose() {
        colorManager.dispose();
        super.dispose();
    }
    
    public IDocument getDocument() {
        IDocumentProvider documentProvider = getDocumentProvider();
        if (documentProvider != null) {
            return documentProvider.getDocument(getEditorInput());
        }
        return null;
    }
    
    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        /* Install Keyboard event consumer to disable any editing  read only
         * code section */
        getSourceViewer().setEventConsumer(new IEventConsumer() {
            @Override
            public void processEvent(VerifyEvent event) {
                int offset = event.start;
                if(editorSupport.isInsideReadOnlyBlock(offset, 0)){
                    // swallow the event.
                    event.doit = false;
                    return;
                }
            }
        });
    }
 
}

 


image

Comments

  1. Hi,
    This was an extremely useful post. Thank you so much.
    I would, however, like to raise a few concerns. Hopefully you would be kind enough to respond.

    Using you code as an example, I tried to make the default Java editor having some restricted/non-editable areas and I succeeded except the following issues which I could not overcome.
    As to use in Java code, I chose " //auto-generate-code-start" and "//auto-generate-code-end" as start_tag and end_tag marks.

    (1) If the user presses "Ctrl+D" on any line with in the restricted area, the line gets deleted.
    (2) If the use tries an automatic way to format the code / comments by pressing "Ctrl+F", the format of my start_tag breaks (// auto-generate-code-start) or some other form depending on the formatting preference, which breaks the restricted area.
    (3) It is very strange that the method processEvent(VerifyEvent event) does not catch/handle the "Tab" keys pressed in the restricted area while the cursor is at the start of a line. Hence, moving the line away to the right side.
    (4) If the user presses a Tab key just above the start_tag and presses delete button, the start_tag shifts to that position and the restricted area breaks.

    I really want to customize the default editor having some restricted areas. It would be very nice of you if you could guide about these issues.

    Thanks again.

    ReplyDelete
    Replies
    1. Hi Monsoor,

      Thanks for referring my blog.

      I have already done some research on the issues you are concern about . Very soon i will publish new post for same, if possible i will share that post with you.


      Thanks
      Yogesh Devatraj.

      Delete
    2. Hi Monsoor,

      Thanks for referring my blog.

      I have already done some research on the issues you are concern about . Very soon i will publish new post for same, if possible i will share that post with you.


      Thanks
      Yogesh Devatraj.

      Delete
    3. Hi,

      I am eagerly waiting for your next post then :)
      How can I make sure that it is possible to be shared with me?
      Thank you.

      Delete
  2. Hi Yogesh,
    Could you please publish the promised post soon. I almost daily check your blog in anticipation :). Can you tell when do you plan to publish it?
    Thanks.

    ReplyDelete
  3. Hi Yogesh,

    I wanted this functionality for an editor of my multipage editor, and i am not able to over ride the createPartControl method as it is final in the MultiPageEditorPart.class which my class extends. what should I do now?

    ReplyDelete
    Replies
    1. Sorry I don't have answer right now, Its being long time I have worked on Eclipse plugin development, let me recall and get back to you.

      Delete

Post a Comment

Popular posts from this blog

Composite Design Pattern by example

State Design Pattern by Example

Eclipse command framework core expression: Property tester