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

Blocking Editor Actions in read-only code section:

In addition to previous posts about partially editable/read-only editor, I am going to explain blocking editor actions like Ctrl-D in read-only code sections. Till now I have taken care of keyboard events and background color for read only code section. This post, we will see restricting editor actions like Ctrl-D, move line up/down etc.
I am going to extend same example of XML editor . In this example, XMLEditor extends TextEditors and hence AbstractTextEditor. AbstractTextEditor is the one who introduces all basic text editor actions to text editor. Few of these actions are
  • Delete Line
  • Cut line
  • move line up/down
  • copy line up/down
  • Shift Left/Right
Every Action implements IAction directly or indirectly, IAction provides flexibility of enabling/disabling or handling by providing following set of methods
   1: /**
   2:      * Returns whether this action is enabled.
   3:      * <p>
   4:      * This method is associated with the <code>ENABLED</code> property;
   5:      * property change events are reported when its value changes.
   6:      * </p>
   7:      *
   8:      * @return <code>true</code> if enabled, and
   9:      *   <code>false</code> if disabled
  10:      * @see #ENABLED
  11:      */
  12:     public boolean isEnabled();
  13:  
  14:     /**
  15:      * Returns whether this action is handled. In the default case, this is
  16:      * always <code>true</code>. However, if the action delegates some of its
  17:      * behaviour to some other object, then this method should answer whether
  18:      * such an object is currently available.
  19:      * 
  20:      * @return <code>true</code> if all of the action's behaviour is
  21:      *         available; <code>false</code> otherwise.
  22:      * @since 3.1
  23:      */
  24:     public boolean isHandled();

But we can not override these methods in all required text editor actions as these actions are introduced in high in hierarchy i.e. AbstractTextEditor.

You can get any of the text editor action handler using its id, once you have reference of action handler then you can invoke isEnable() to enable/disable the action.

Next question, when should be disable them ? If we disable the actions, those will not even be available for editable code section. We wanted to disable text editor actions only in read-only code section , but allow those in editable code section. We have to do this dynamically based on the current editor code section.

There is a tricky way to enable/disable these text editor actions based on code editor section, using caret listener. Caret listener provide you caret’s exact location in the editor, which helps you to decide whether you are in editable code section or read-only code section. Once you know that the current caret position is in read-only code section then all text editor modifying action should be disabled and when the current caret position is in editable code section then enable them all.

Here is complete code for XMLTextEditor


   1: package sample.xml.editor.editors;
   2:  
   3: import javax.lang.model.SourceVersion;
   4:  
   5: import org.eclipse.jface.action.IAction;
   6: import org.eclipse.jface.text.IDocument;
   7: import org.eclipse.jface.text.IEventConsumer;
   8: import org.eclipse.jface.text.ITextSelection;
   9: import org.eclipse.jface.text.source.SourceViewer;
  10: import org.eclipse.swt.custom.CaretEvent;
  11: import org.eclipse.swt.custom.CaretListener;
  12: import org.eclipse.swt.custom.LineBackgroundEvent;
  13: import org.eclipse.swt.custom.LineBackgroundListener;
  14: import org.eclipse.swt.events.VerifyEvent;
  15: import org.eclipse.swt.graphics.RGB;
  16: import org.eclipse.swt.widgets.Composite;
  17: import org.eclipse.ui.editors.text.TextEditor;
  18: import org.eclipse.ui.texteditor.IDocumentProvider;
  19: import org.eclipse.ui.texteditor.ITextEditorActionConstants;
  20:  
  21: public class XMLEditor extends TextEditor {
  22:  
  23:     private ColorManager colorManager;
  24:     public CustomEditorSupport editorSupport = null;
  25:  
  26:     private static final String[] RESTRICTED_ACTION = {
  27:         ITextEditorActionConstants.CUT_LINE,
  28:         ITextEditorActionConstants.CUT_LINE_TO_BEGINNING,
  29:         ITextEditorActionConstants.CUT_LINE_TO_END,
  30:         ITextEditorActionConstants.CUT_EXT,
  31:         ITextEditorActionConstants.DELETE_LINE,
  32:         ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING,
  33:         ITextEditorActionConstants.DELETE_LINE_TO_END,
  34:         ITextEditorActionConstants.CONTENT_ASSIST,
  35:         ITextEditorActionConstants.MOVE_LINE_DOWN,
  36:         ITextEditorActionConstants.MOVE_LINE_UP,
  37:         ITextEditorActionConstants.SHIFT_LEFT,
  38:         ITextEditorActionConstants.SHIFT_RIGHT,
  39:         ITextEditorActionConstants.SHIFT_RIGHT_TAB,
  40: };
  41:     public XMLEditor() {
  42:         super();
  43:         editorSupport = new CustomEditorSupport(this);
  44:         colorManager = new ColorManager();
  45:         setSourceViewerConfiguration(new XMLConfiguration(colorManager));
  46:         setDocumentProvider(new XMLDocumentProvider());
  47:     }
  48:     public void dispose() {
  49:         colorManager.dispose();
  50:         super.dispose();
  51:     }
  52:     
  53:     public IDocument getDocument() {
  54:         IDocumentProvider documentProvider = getDocumentProvider();
  55:         if (documentProvider != null) {
  56:             return documentProvider.getDocument(getEditorInput());
  57:         }
  58:         return null;
  59:     }
  60:     
  61:     @Override
  62:     public void createPartControl(Composite parent) {
  63:         super.createPartControl(parent);
  64:         /* Install Keyboard event consumer to disable any editing  read only
  65:          * code section */
  66:         getSourceViewer().setEventConsumer(new IEventConsumer() {
  67:             @Override
  68:             public void processEvent(VerifyEvent event) {
  69:                 int offset = event.start;
  70:                 if(editorSupport.isInsideReadOnlyBlock(offset, 0)){
  71:                     // swallow the event.
  72:                     event.doit = false;
  73:                     return;
  74:                 }
  75:             }
  76:         });
  77:         
  78:         LineBackgroundListener lineBackgroundListener = new LineBackgroundListener(){
  79:             @Override
  80:             public void lineGetBackground(LineBackgroundEvent event) {
  81:                 int offset = event.lineOffset;
  82:                 if(editorSupport.isInsideReadOnlyBlock(offset, 0)){
  83:                     event.lineBackground = colorManager.getColor(new RGB(245,245,245));
  84:                 }
  85:             }
  86:         };
  87:         //Install line background color listener on source viewer 
  88:         getSourceViewer().getTextWidget().addLineBackgroundListener(lineBackgroundListener);
  89:         
  90:         /*Provide Cursor listen on Source Viewer to handle TExt Editor action*/
  91:         getSourceViewer().getTextWidget().addCaretListener(new CaretListener() {
  92:             
  93:             @Override
  94:             public void caretMoved(CaretEvent event) {
  95:                 ITextSelection sel= (ITextSelection) getSourceViewer().getSelectionProvider().getSelection();
  96:                 if(sel.getLength()>1){
  97:                     /*Convert widget offset to model offset*/
  98:                     int selOffset = ((SourceViewer)getSourceViewer()).modelOffset2WidgetOffset(sel.getOffset());
  99:                     if(editorSupport.isInsideReadOnlyBlock(selOffset, 0)){
 100:                         /*If cureent cursor is in read-only code section*/
 101:                         enableTextEditorAction(false);
 102:                     } else {
 103:                         /*If current cursor is in editable code section*/
 104:                         enableTextEditorAction(true);
 105:                     }
 106:                 } else {
 107:                     /*Convert widget offset to model offset*/
 108:                     int selOffest = ((SourceViewer)getSourceViewer()).modelOffset2WidgetOffset(event.caretOffset);
 109:                     if(editorSupport.isInsideReadOnlyBlock(selOffest, 0)){
 110:                         /*If cureent cursor is in read-only code section*/
 111:                         enableTextEditorAction(false);
 112:                     } else {
 113:                         /*If current cursor is in editable code section*/
 114:                         enableTextEditorAction(true);
 115:                     }
 116:                 }
 117:                 
 118:             }
 119:         });
 120:     }
 121:     
 122:     /**
 123:      * Enable/Disable text editor actions
 124:      * @param flag <code>true/false</code>
 125:      */
 126:     private void enableTextEditorAction(boolean flag){
 127:         for(String actionID : RESTRICTED_ACTION){
 128:                 IAction action = this.getAction(actionID);
 129:                 if(action!=null)
 130:                 action.setEnabled(flag);
 131:         }
 132:     }
 133:  
 134: }

Have close look at enableTextEditorAction() method as well as Caret listener in above code. There is lot of scope to improve and optimize the above piece of code. Its all depend on the requirements.

Comments

  1. Hi Yogesh,
    This post is very useful. I found a case where it will not work as expected.
    1) I start editing in the editable area. Press content assist.
    2) then i go back to the non editable area and start pressing the alphabetical keys. It becomes editable.
    Do you plan to target this in your next blog. Looking forward to it.

    Best regards,
    Madhu

    ReplyDelete
  2. Hi Yogesh,

    Is it possible to disable the drag and drop of the Templates view into the non-editable region of the TextEditor. In the current code, we can drop the code template into the non-editable region.

    Best Regards,
    Madhu

    ReplyDelete
    Replies
    1. Hi Madhu,

      Thanks for referring my blog.

      Yes, it is possible block drag and drop of the text to non-editable code section.

      I have already implemented it. Probably i will continue this series targeting this requirement.

      Thanks
      Yogesh Devatraj

      Delete
    2. Hi Yogesh,
      Thanks for the reply. I'm eagerly waiting for your next post on blocking the drag and drop to non-editable code section.

      Best Regards,
      Madhu

      Delete
  3. Hi Yogesh,
    Could you please tell me as to when you plan to publish blocking of drag n drop into non-editable section. Looking forward to it big time. :)

    Best Regards,
    Madhu

    ReplyDelete
    Replies
    1. Hi Madhu,

      You can find about restricting drag and drop here http://ydtech.blogspot.in/2012/08/eclipse-editor-partially-editableread.html.

      Please let me know whether it serve your purpose. Any suggestions for improved are always welcome.

      Thanks
      Yogesh

      Delete
    2. Thanks a lot Yogesh :) It was helpful :)
      When I right click on the text editor, I see the context menu CUT action. When I select the text in non editable area and do a CUT action via the context menu, the text goes away. Is it possible to disable this CUT action when in read only area.

      Best Regards,
      Madhu

      Delete
  4. I Will update you as soon as possible.

    ReplyDelete
    Replies
    1. HI Yogesh ,
      We can disable the CUT action by getAction(ActionFactory.CUT.getId()).setEnabled(false);

      I was trying code folding in the text editors.
      Is it possible to handle the read only section and non read only section after a section of code is folded.

      Best Regards,
      Madhu

      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