为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持

为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持
    StyledText是SWT包中的一个基础组件,就像它的名字定义的那样,通过它可以显示各式各样的字体。但是StyledText没有提供Undo/Redo的功能,很是让人不爽,下面给一个简单的例子,演示在StyledText增加Undo/Redo操作,同时在这个自定义的StyledText中增加了删除选中文本、清除全部文本的动作支持。
    思路很简单,就是监听文本内容更改事件,然后根据更改的内容生成Undo操作存入Undo操作栈中,在Undo操作栈中维护了两个列表UndoList和RedoList,当发出Undo操作命令的时候,从Undo操作列表中取出一个Undo操作,执行其undo()方法,然后将其放入Redo操作列表中。如果发出Redo操作命令,就从Redo操作列表中出一个Redo操作,执行其redo()方法,然后将其放入Undo操作列表中。
    本篇文章只是给出了一个简单的演示示例,需要改进的地方是监听文本更改时,应该采用一定的规则策略来生成Undo操作,否则每输入或者删除一个字符都会创建一个Undo操作,这样就会产生非常多的Undo操作,并且也不符合日常使用习惯。日后有时间的话我会整理一下改进的版本发布出来。如果你有兴趣也可以研究一下。
    另外由于StyledText的invokeAction()方法中Action的动作不够充足,致使无法用setKeyBinding()方法方便的将快捷键与执行动作绑定,通过重写StyledText中的invokeAction()方法可以定制某些自己的动作,完善StyledText的功能。

下面是Undo操作管理器UndoManager的代码:
  1  import  org.eclipse.core.commands.ExecutionException;
  2  import  org.eclipse.core.commands.operations.AbstractOperation;
  3  import  org.eclipse.core.commands.operations.IOperationHistory;
  4  import  org.eclipse.core.commands.operations.IUndoContext;
  5  import  org.eclipse.core.commands.operations.ObjectUndoContext;
  6  import  org.eclipse.core.commands.operations.OperationHistoryFactory;
  7  import  org.eclipse.core.runtime.IAdaptable;
  8  import  org.eclipse.core.runtime.IProgressMonitor;
  9  import  org.eclipse.core.runtime.IStatus;
 10  import  org.eclipse.core.runtime.Status;
 11  import  org.eclipse.swt.custom.ExtendedModifyEvent;
 12  import  org.eclipse.swt.custom.ExtendedModifyListener;
 13  import  org.eclipse.swt.custom.StyledText;
 14 
 15  /**
 16   * 管理Undo操作,用于监听文本域的文本改变事件,生成Undo操作并记录。<br>
 17   * 
 18   *  @author  qujinlong
 19    */
 20  public   class  UndoManager
 21  {
 22     /*
 23     * 用于存储历史Undo操作,每改变一次文本内容,就将构造一个Undo操作存入OperationHistory中。
 24      */
 25     private   final  IOperationHistory opHistory;
 26 
 27     /*
 28     * Undo操作上下文,一般用于在OperationHistory中查找当前文本框的Undo操作。
 29      */
 30     private  IUndoContext undoContext  =   null ;
 31 
 32     /*
 33     * 所要监听的需要实现Undo操作的文本框。
 34      */
 35     private  StyledText styledText  =   null ;
 36 
 37     private   int  undoLevel  =   0 ;
 38 
 39     public  UndoManager( int  undoLevel)
 40    {
 41      opHistory  =  OperationHistoryFactory.getOperationHistory();
 42 
 43      setMaxUndoLevel(undoLevel);
 44    }
 45 
 46     public   void  setMaxUndoLevel( int  undoLevel)
 47    {
 48       this .undoLevel  =  Math.max( 0 , undoLevel);
 49 
 50       if  (isConnected())
 51        opHistory.setLimit(undoContext,  this .undoLevel);
 52    }
 53 
 54     public   boolean  isConnected()
 55    {
 56       return  styledText  !=   null ;
 57    }
 58 
 59     /*
 60     * 将Undo管理器与指定的StyledText文本框相关联。
 61      */
 62     public   void  connect(StyledText styledText)
 63    {
 64       if  ( !  isConnected()  &&  styledText  !=   null )
 65      {
 66         this .styledText  =  styledText;
 67 
 68         if  (undoContext  ==   null )
 69          undoContext  =   new  ObjectUndoContext( this );
 70 
 71        opHistory.setLimit(undoContext, undoLevel);
 72        opHistory.dispose(undoContext,  true true false );
 73 
 74        addListeners();
 75      }
 76    }
 77 
 78     public   void  disconnect()
 79    {
 80       if  (isConnected())
 81      {
 82        removeListeners();
 83 
 84        styledText  =   null ;
 85 
 86        opHistory.dispose(undoContext,  true true true );
 87 
 88        undoContext  =   null ;
 89      }
 90    }
 91 
 92     private  ExtendedModifyListener extendedModifyListener  =   null ;
 93 
 94     private   boolean  isUndoing  =   false ;
 95 
 96     /*
 97     * 向Styled中注册监听文本改变的监听器。
 98     * 
 99     * 如果文本改变,就构造一个Undo操作压入Undo操作栈中。
100      */
101     private   void  addListeners()
102    {
103       if  (styledText  !=   null )
104      {
105        extendedModifyListener  =   new  ExtendedModifyListener() {
106           public   void  modifyText(ExtendedModifyEvent event)
107          {
108             if  (isUndoing)
109               return ;
110 
111            String newText  =  styledText.getText().substring(event.start,
112                event.start  +  event.length);
113 
114            UndoableOperation operation  =   new  UndoableOperation(undoContext);
115 
116            operation.set(event.start, newText, event.replacedText);
117 
118            opHistory.add(operation);
119          }
120        };
121 
122        styledText.addExtendedModifyListener(extendedModifyListener);
123      }
124    }
125 
126     private   void  removeListeners()
127    {
128       if  (styledText  !=   null )
129      {
130         if  (extendedModifyListener  !=   null )
131        {
132          styledText.removeExtendedModifyListener(extendedModifyListener);
133 
134          extendedModifyListener  =   null ;
135        }
136      }
137    }
138 
139     public   void  redo()
140    {
141       if  (isConnected())
142      {
143         try
144        {
145          opHistory.redo(undoContext,  null null );
146        }
147         catch  (ExecutionException ex)
148        {
149        }
150      }
151    }
152 
153     public   void  undo()
154    {
155       if  (isConnected())
156      {
157         try
158        {
159          opHistory.undo(undoContext,  null null );
160        }
161         catch  (ExecutionException ex)
162        {
163        }
164      }
165    }
166 
167     /*
168     * Undo操作用于记录StyledText的文本被改变时的相关数据。
169     * 
170     * 比如文本框中本来的文本为111222333,如果此时选中222替换为444(用复制粘帖的方法),
171     * 
172     * 则Undo操作中记录的相关数据为: startIndex = 3; newText = 444; replacedText = 222;
173      */
174     private   class  UndoableOperation  extends  AbstractOperation
175    {
176       //  记录Undo操作时,被替换文本的开始索引
177       protected   int  startIndex  =   -   1 ;
178 
179       //  新输入的文本
180       protected  String newText  =   null ;
181 
182       //  被替换掉的文本
183       protected  String replacedText  =   null ;
184 
185       public  UndoableOperation(IUndoContext context)
186      {
187         super ( " Undo-Redo " );
188 
189        addContext(context);
190      }
191 
192       /*
193       * 设置Undo操作中要存储的相关数据。
194        */
195       public   void  set( int  startIndex, String newText, String replacedText)
196      {
197         this .startIndex  =  startIndex;
198 
199         this .newText  =  newText;
200         this .replacedText  =  replacedText;
201      }
202 
203       /*
204       * (non-Javadoc)
205       * 
206       * @see org.eclipse.core.commands.operations.AbstractOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
207       *      org.eclipse.core.runtime.IAdaptable)
208        */
209       public  IStatus undo(IProgressMonitor monitor, IAdaptable info)
210           throws  ExecutionException
211      {
212        isUndoing  =   true ;
213        styledText.replaceTextRange(startIndex, newText.length(), replacedText);
214        isUndoing  =   false ;
215 
216         return  Status.OK_STATUS;
217      }
218 
219       /*
220       * (non-Javadoc)
221       * 
222       * @see org.eclipse.core.commands.operations.AbstractOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
223       *      org.eclipse.core.runtime.IAdaptable)
224        */
225       public  IStatus redo(IProgressMonitor monitor, IAdaptable info)
226           throws  ExecutionException
227      {
228        isUndoing  =   true ;
229        styledText.replaceTextRange(startIndex, replacedText.length(), newText);
230        isUndoing  =   false ;
231 
232         return  Status.OK_STATUS;
233      }
234 
235       /*
236       * (non-Javadoc)
237       * 
238       * @see org.eclipse.core.commands.operations.AbstractOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
239       *      org.eclipse.core.runtime.IAdaptable)
240        */
241       public  IStatus execute(IProgressMonitor monitor, IAdaptable info)
242           throws  ExecutionException
243      {
244         return  Status.OK_STATUS;
245      }
246    }
247  }


下面是扩展的StyledText类:
  1  import  org.eclipse.swt.SWT;
  2  import  org.eclipse.swt.custom.ST;
  3  import  org.eclipse.swt.custom.StyledText;
  4  import  org.eclipse.swt.layout.GridData;
  5  import  org.eclipse.swt.layout.GridLayout;
  6  import  org.eclipse.swt.widgets.Composite;
  7  import  org.eclipse.swt.widgets.Display;
  8  import  org.eclipse.swt.widgets.Shell;
  9 
 10  /**
 11   * 自定义的StyledText,增加了Undo、Redo和清除操作。<br>
 12   * 
 13   *  @author  qujinlong
 14    */
 15  public   class  MyStyledText  extends  StyledText
 16  {
 17     /**
 18     *  @param  parent
 19     *  @param  style
 20      */
 21     public  MyStyledText(Composite parent,  int  style)
 22    {
 23       super (parent, style);
 24    }
 25 
 26     /*
 27     * (non-Javadoc)
 28     * 
 29     * @see org.eclipse.swt.custom.StyledText#invokeAction(int)
 30      */
 31     public   void  invokeAction( int  action)
 32    {
 33       //  增加一个自定义的删除操作,只有当选中文本的时候才将文本删除。
 34       //  否则,ST.DELETE_NEXT会将光标所在位置的后一个字符删除。
 35       if  (action  ==  ActionCode.DELETE  &&  getSelectionCount()  >   0 )
 36        action  =  ST.DELETE_NEXT;
 37 
 38       super .invokeAction(action);
 39 
 40       switch  (action)
 41      {
 42         case  ActionCode.UNDO:
 43          undo();
 44           break ;
 45         case  ActionCode.REDO:
 46          redo();
 47           break ;
 48         case  ActionCode.CLEAR:
 49          clear();
 50           break ;
 51      }
 52    }
 53 
 54     private   void  undo()
 55    {
 56       if  (undoManager  !=   null )
 57        undoManager.undo();
 58    }
 59 
 60     private   void  redo()
 61    {
 62       if  (undoManager  !=   null )
 63        undoManager.redo();
 64    }
 65 
 66     private   void  clear()
 67    {
 68       super .setText( "" );
 69    }
 70 
 71     private  UndoManager undoManager  =   null ;
 72 
 73     /**
 74     *  @return  Returns undoManager.
 75      */
 76     public  UndoManager getUndoManager()
 77    {
 78       return  undoManager;
 79    }
 80 
 81     /**
 82     *  @param  undoManager - The undoManager to set.
 83      */
 84     public   void  setUndoManager(UndoManager undoManager)
 85    {
 86       this .undoManager  =  undoManager;
 87    }
 88 
 89     /*
 90     * (non-Javadoc)
 91     * 
 92     * @see org.eclipse.swt.widgets.Widget#dispose()
 93      */
 94     public   void  dispose()
 95    {
 96       if  (undoManager  !=   null )
 97        undoManager.disconnect();
 98 
 99       super .dispose();
100    }
101 
102     public   static   class  ActionCode
103    {
104       public   static   final   int  UNDO  =  Integer.MAX_VALUE;
105 
106       public   static   final   int  REDO  =  UNDO  -   1 ;
107 
108       public   static   final   int  CLEAR  =  UNDO  -   2 ;
109 
110       public   static   final   int  DELETE  =  UNDO  -   3 ;
111    }
112 
113     public   static   void  main(String[] args)
114    {
115       final  Display display  =  Display.getDefault();
116       final  Shell shell  =   new  Shell();
117      shell.setLayout( new  GridLayout());
118      shell.setSize( 420 250 );
119      shell.setText( " SWT Application " );
120 
121      MyStyledText styledText  =   new  MyStyledText(shell, SWT.BORDER);
122      GridData gd_styledText  =   new  GridData(SWT.FILL, SWT.CENTER,  false false );
123      gd_styledText.heightHint  =   200 ;
124      gd_styledText.widthHint  =   400 ;
125      styledText.setLayoutData(gd_styledText);
126 
127       //  Ctrl+C, Ctrl+X, Ctrl+V 都是StyledText的默认行为。
128 
129       //  styledText.setKeyBinding('C' | SWT.CTRL, ST.COPY);
130       //  styledText.setKeyBinding('V' | SWT.CTRL, ST.PASTE);
131       //  styledText.setKeyBinding('X' | SWT.CTRL, ST.CUT);
132 
133      styledText.setKeyBinding( ' A '   |  SWT.CTRL, ST.SELECT_ALL);
134 
135      styledText.setKeyBinding( ' Z '   |  SWT.CTRL, ActionCode.UNDO);
136      styledText.setKeyBinding( ' Y '   |  SWT.CTRL, ActionCode.REDO);
137      styledText.setKeyBinding( ' F '   |  SWT.CTRL, ActionCode.CLEAR);
138      styledText.setKeyBinding( ' D '   |  SWT.CTRL, ActionCode.DELETE);
139 
140      UndoManager undoManager  =   new  UndoManager( 50 );
141      undoManager.connect(styledText);
142 
143      styledText.setUndoManager(undoManager);
144 
145      shell.open();
146 
147      shell.layout();
148 
149       while  ( !  shell.isDisposed())
150      {
151         if  ( !  display.readAndDispatch())
152          display.sleep();
153      }
154    }
155  }
    本示例着重演示如何实现Undo/Redo操作,也可以参见JFace的TextViewer的Undo/Redo操作的实现。

你可能感兴趣的:(为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持)