Thinker

  - long way to go...

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  24 随笔 :: 0 文章 :: 143 评论 :: 0 Trackbacks
    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, truetruefalse);
 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, truetruetrue);
 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, nullnull);
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, nullnull);
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(420250);
119     shell.setText("SWT Application");
120 
121     MyStyledText styledText = new MyStyledText(shell, SWT.BORDER);
122     GridData gd_styledText = new GridData(SWT.FILL, SWT.CENTER, falsefalse);
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操作的实现。
posted on 2007-06-07 10:25 Long 阅读(4912) 评论(7)  编辑  收藏 所属分类: Java

评论

# re: 为SWT的StyledText添加Undo、Redo操作 2007-06-07 10:39 Long
如果想写纯SWT应用程序,可以自己实现一个OperationHistory,实际上就是维护Undo/Redo两个操作列表/栈。  回复  更多评论
  

# re: 为SWT的StyledText添加Undo、Redo操作 2007-06-07 12:17 BeanSoft
貌似可以这里直接用 Swing 的 UNDO/REDO 机制.  回复  更多评论
  

# re: 为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持 2007-06-11 12:00 Long
上次看到网友说贴代码时不要显示列号。
呵呵,显示列号没关系,只要用UltraEdit的列模式很容易就删除掉了。  回复  更多评论
  

# re: 为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持 2007-09-07 09:40 wang
请教下,对小键盘回车 绑定快捷键 ,怎样写代码呢,忘告知,谢谢  回复  更多评论
  

# 为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持 2008-07-15 11:38 zheng1285
如果还要实现 从网页上复制过来的粘贴到StyledText中能不能带它的属性呢?
比如它有链接、加粗等等。  回复  更多评论
  

# 为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持 2008-07-15 11:39 zheng1285
<B>如果还要实现 从网页上复制过来的粘贴到StyledText中能不能带它的属性呢?
比如它有链接、加粗等等。<B>  回复  更多评论
  

# re: 为SWT的StyledText添加Undo/Redo操作以及对快捷键动作绑定的支持[未登录] 2014-09-11 14:10 1
jianren  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: