OSDN Git Service

印刷時の画面に収まる行数の計算方法を元に戻した
[fooeditengine/FooEditEngine.git] / Common / UndoManager.cs
1 /*\r
2  * Copyright (C) 2013 FooProject\r
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by\r
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.\r
5 \r
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of \r
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
8 \r
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.\r
10  */\r
11 using System;\r
12 using System.Collections.Generic;\r
13 using System.Linq;\r
14 \r
15 namespace FooEditEngine\r
16 {\r
17     interface ICommand\r
18     {\r
19         /// <summary>\r
20         /// アンドゥする\r
21         /// </summary>\r
22         void undo();\r
23         /// <summary>\r
24         /// リドゥする\r
25         /// </summary>\r
26         void redo();\r
27         /// <summary>\r
28         /// マージする\r
29         /// </summary>\r
30         /// <param name="a"></param>\r
31         /// <returns>マージできた場合は真、そうでない場合は偽を返す</returns>\r
32         bool marge(ICommand a);\r
33         /// <summary>\r
34         /// コマンドを結合した結果が空なら真。そうでないなら偽を返す\r
35         /// </summary>\r
36         /// <returns></returns>\r
37         bool isempty();\r
38 \r
39     }\r
40 \r
41     sealed class BeginActionCommand : ICommand\r
42     {\r
43         #region ICommand メンバー\r
44 \r
45         public void undo()\r
46         {\r
47         }\r
48 \r
49         public void redo()\r
50         {\r
51         }\r
52 \r
53         public bool marge(ICommand a)\r
54         {\r
55             return false;\r
56         }\r
57 \r
58         public bool isempty()\r
59         {\r
60             return false;\r
61         }\r
62         #endregion\r
63     }\r
64 \r
65     sealed class EndActionCommand : ICommand\r
66     {\r
67         #region ICommand メンバー\r
68 \r
69         public void undo()\r
70         {\r
71         }\r
72 \r
73         public void redo()\r
74         {\r
75         }\r
76 \r
77         public bool marge(ICommand a)\r
78         {\r
79             return false;\r
80         }\r
81 \r
82         public bool isempty()\r
83         {\r
84             return false;\r
85         }\r
86         #endregion\r
87     }\r
88 \r
89     /// <summary>\r
90     /// アンドゥバッファーを管理するクラス\r
91     /// </summary>\r
92     public sealed class UndoManager\r
93     {\r
94         private bool locked = false;\r
95         private Stack<ICommand> UndoStack = new Stack<ICommand>();\r
96         private Stack<ICommand> RedoStack = new Stack<ICommand>();\r
97         private int groupLevel = 0;\r
98 \r
99         /// <summary>\r
100         /// コンストラクター\r
101         /// </summary>\r
102         internal UndoManager()\r
103         {\r
104             this.Grouping = false;\r
105         }\r
106 \r
107         /// <summary>\r
108         /// 操作を履歴として残します\r
109         /// </summary>\r
110         /// <param name="cmd">ICommandインターフェイス</param>\r
111         internal void push(ICommand cmd)\r
112         {\r
113             if (this.locked == true)\r
114                 return;\r
115             ICommand last = null;\r
116             if (this.AutoMerge && UndoStack.Count() > 0)\r
117                 last = UndoStack.First();\r
118             if(last == null || last.marge(cmd) == false)\r
119                 UndoStack.Push(cmd);\r
120             if (last != null && last.isempty())\r
121                 UndoStack.Pop();\r
122             if (this.RedoStack.Count > 0)\r
123                 RedoStack.Clear();\r
124         }\r
125 \r
126         /// <summary>\r
127         /// 履歴として残される操作が一連のグループとして追加されるなら真を返し、そうでなければ偽を返す\r
128         /// </summary>\r
129         internal bool Grouping\r
130         {\r
131             get;\r
132             set;\r
133         }\r
134 \r
135 \r
136         /// <summary>\r
137         /// アクションを結合するなら真。そうでないなら偽\r
138         /// </summary>\r
139         internal bool AutoMerge\r
140         {\r
141             get;\r
142             set;\r
143         }\r
144 \r
145         /// <summary>\r
146         /// 一連のアンドゥアクションの開始を宣言します\r
147         /// </summary>\r
148         public void BeginUndoGroup()\r
149         {\r
150             if (this.Grouping)\r
151             {\r
152                 this.groupLevel++;\r
153             }\r
154             else\r
155             {\r
156                 this.push(new BeginActionCommand());\r
157                 this.Grouping = true;\r
158                 this.AutoMerge = true;\r
159             }\r
160         }\r
161 \r
162         /// <summary>\r
163         /// 一連のアンドゥアクションの終了を宣言します\r
164         /// </summary>\r
165         public void EndUndoGroup()\r
166         {\r
167             if (this.Grouping == false)\r
168                 throw new InvalidOperationException("BeginUndoGroup()を呼び出してください");\r
169             if (this.groupLevel > 0)\r
170             {\r
171                 this.groupLevel--;\r
172             }\r
173             else\r
174             {\r
175                 ICommand last = UndoStack.First();\r
176                 if (last != null && last is BeginActionCommand)\r
177                     this.UndoStack.Pop();\r
178                 else\r
179                     this.push(new EndActionCommand());\r
180                 this.Grouping = false;\r
181                 this.AutoMerge = false;\r
182             }\r
183         }\r
184 \r
185         /// <summary>\r
186         /// 元に戻します\r
187         /// </summary>\r
188         public void undo()\r
189         {\r
190             if (this.UndoStack.Count == 0 || this.locked == true)\r
191                 return; \r
192   \r
193             ICommand cmd;\r
194             bool isGrouped = false;\r
195 \r
196             do\r
197             {\r
198                 cmd = this.UndoStack.Pop();\r
199                 this.RedoStack.Push(cmd);\r
200                 this.BeginLock();\r
201                 cmd.undo();\r
202                 this.EndLock();\r
203                 //アンドゥスタック上ではEndActionCommand,...,BeginActionCommandの順番になる\r
204                 if (cmd is EndActionCommand)\r
205                     isGrouped = true;\r
206                 else if (cmd is BeginActionCommand)\r
207                     isGrouped = false;\r
208             } while (isGrouped);\r
209 \r
210         }\r
211 \r
212         /// <summary>\r
213         /// 元に戻した動作をやり直します\r
214         /// </summary>\r
215         public void redo()\r
216         {\r
217             if (this.RedoStack.Count == 0 || this.locked == true)\r
218                 return;\r
219             ICommand cmd;\r
220             bool isGrouped = false;\r
221 \r
222             do\r
223             {\r
224                 cmd = this.RedoStack.Pop();\r
225                 this.UndoStack.Push(cmd);\r
226                 this.BeginLock();\r
227                 cmd.redo();\r
228                 this.EndLock();\r
229                 //リドゥスタック上ではBeginActionCommand,...,EndActionCommandの順番になる\r
230                 if (cmd is BeginActionCommand)\r
231                     isGrouped = true;\r
232                 else if (cmd is EndActionCommand)\r
233                     isGrouped = false;\r
234             } while (isGrouped);\r
235         }\r
236 \r
237         /// <summary>\r
238         /// 操作履歴をすべて削除します\r
239         /// </summary>\r
240         public void clear()\r
241         {\r
242             if (this.locked == true)\r
243                 return;\r
244             this.UndoStack.Clear();\r
245             this.RedoStack.Clear();\r
246         }\r
247         /// <summary>\r
248         /// 以後の操作をアンドゥ不能にする\r
249         /// </summary>\r
250         public void BeginLock()\r
251         {\r
252             this.locked = true;\r
253         }\r
254 \r
255         /// <summary>\r
256         /// 以後の操作をアンドゥ可能にする\r
257         /// </summary>\r
258         public void EndLock()\r
259         {\r
260             this.locked = false;\r
261         }\r
262     }\r
263 }\r