Fixed help file parsing
[Nebula.git] / TextDocument.cs
1 using System;
2 using System.Collections;
3 using System.IO;
4
5 public struct Cursor {
6         private int _x;
7         private int _y;
8
9         public Cursor(int x, int y) {
10                 _x = x;
11                 _y = y;
12         }
13
14         public int x {
15                 get {
16                         return _x;
17                 }
18                 set {
19                         if (value < 0) _x = 0;
20                         else _x = value;
21                 }
22         }
23
24         public int y {
25                 get {
26                         return _y;
27                 }
28                 set {
29                         if (value < 0) _y = 0;
30                         else _y = value;
31                 }
32         }
33 }
34
35 public class TextArray {
36         private ArrayList lines;
37
38         public TextArray() {
39                 lines = new ArrayList();
40                 lines.Add("");
41         }
42
43         public TextArray(string filename) {
44                 lines = new ArrayList();
45                 StreamReader file;
46                 string line;
47
48                 file = new StreamReader(filename);
49                 while ((line = file.ReadLine()) != null) {
50                         lines.Add(line);
51                 }
52                 file.Close();
53         }
54
55         public ArrayList Lines {
56                 get {
57                         return lines;
58                 }
59         }
60
61         public void Insert(int x, int y, char c) {
62                 while (lines.Count < y+1)
63                         lines.Add("");
64                 string s = (string)lines[y];
65
66                 if (x >= s.Length && (c == ' ' || c == '\t')) return;
67                 if (x > s.Length) {
68                         lines[y] = s.PadRight(x) + c;
69                 } else if (x <= s.Length) {
70                         lines[y] = s.Insert(x, new string(c, 1));
71                 }
72         }
73
74         public void Delete(int x, int y) {
75                 if (y >= lines.Count) return;
76
77                 string s = (string)lines[y];
78                 if (x >= s.Length) {
79                         if (y == lines.Count - 1) return;
80                         if (x > s.Length)
81                                 s = s.PadRight(x);
82                         lines[y] = s + (string)lines[y+1];
83                         lines.RemoveAt(y+1);
84                 } else if (s.Length == 0 && x == 0) {
85                         lines.RemoveAt(y);
86                 } else {
87                         lines[y] = s.Remove(x, 1);
88                 }
89         }
90
91         public void Backspace(int x, int y) {
92                 if (x == 0 && y == 0) return;
93                 if (y >= lines.Count) return;
94
95                 string s = (string)lines[y];
96                 if (x > s.Length) return;
97
98                 if (s.Length == 0 && x == 0) {
99                         lines.RemoveAt(y);
100                 } else if (x == 0 && y > 0) {
101                         lines.RemoveAt(y);
102                         lines[y-1] += s;
103                 } else {
104                         lines[y] = s.Remove(x-1, 1);
105                 }
106         }
107
108         public void Newline(int x, int y) {
109                 if (y >= lines.Count) return;
110                 string s = (string) lines[y];
111                 if (x < s.Length) {
112                         lines[y] = s.Substring(0, x);
113                         lines.Insert(y + 1, s.Substring(x));
114                 } else {
115                         if (y == lines.Count - 1) return;
116                         lines.Insert(y + 1, "");
117                 }
118         }
119
120         public int LineLength(int y) {
121                 if (y >= lines.Count) return 0;
122                 return ((string)lines[y]).Length;
123         }
124
125         public void Save(string filename) {
126                 StreamWriter file = new StreamWriter(filename);
127                 for (int i = 0; i < lines.Count; i++) {
128                         file.Write((string)lines[i]);
129                         file.Write('\n');
130                 }
131                 file.Close();
132         }
133 }
134
135 public class TextDocument : IKeyPress {
136         private TextArray text;
137         public string filename = null;
138         public Cursor cursor;
139         public Cursor textorigin;
140         private int pagewidth, pageheight;
141
142         public TextDocument() {
143                 text = new TextArray();
144         }
145
146         public TextDocument(string filename) {
147                 text = new TextArray(filename);
148                 this.filename = filename;
149         }
150
151         public string GetTextWindow() {
152                 string s = "";
153
154                 for (int i = 0; i <= pageheight + 1 && textorigin.y + i < text.Lines.Count; i++) {
155                         string l = (string)text.Lines[textorigin.y + i];
156                         if (textorigin.x > l.Length) {
157                                 s += "\n";
158                         } else {
159                                 s += l.Substring(textorigin.x) + "\n";
160                         }
161                 }
162                 return s;
163         }
164
165         private void FixTextWindow() {
166                 if (cursor.x < textorigin.x) textorigin.x = cursor.x;
167                 if (cursor.y < textorigin.y) textorigin.y = cursor.y;
168                 if (cursor.x > textorigin.x + pagewidth) textorigin.x = cursor.x - pagewidth;
169                 if (cursor.y > textorigin.y + pageheight) textorigin.y = cursor.y - pageheight;
170         }
171
172         public void SetPageSize(int w, int h) {
173                 pagewidth = w;
174                 pageheight = h;
175                 FixTextWindow();
176         }
177
178         public void MoveCursor(Gdk.Key k) {
179                 switch(k) {
180                 case Gdk.Key.Up:
181                         cursor.y--;
182                         break;
183                 case Gdk.Key.Down:
184                         cursor.y++;
185                         break;
186                 case Gdk.Key.Left:
187                         cursor.x--;
188                         break;
189                 case Gdk.Key.Right:
190                         cursor.x++;
191                         break;
192                 case Gdk.Key.End:
193                         cursor.x = text.LineLength(cursor.y);
194                         break;
195                 case Gdk.Key.Home:
196                         cursor.x = 0;
197                         break;
198                 case Gdk.Key.Page_Up:
199                         cursor.y -= pageheight;
200                         break;
201                 case Gdk.Key.Page_Down:
202                         cursor.y += pageheight;
203                         break;
204                 }
205         }
206
207         public void NextWord() {
208                 if (cursor.x >= text.LineLength(cursor.y)) {
209                         cursor.y++;
210                         cursor.x = 0;
211                 } else {
212                         string s = (String)text.Lines[cursor.y];
213                         int i = s.IndexOf(' ', cursor.x);
214                         if (i == -1) {
215                                 cursor.y++;
216                                 cursor.x = 0;
217                         } else {
218                                 cursor.x = i + 1;
219                         }
220                 }
221         }
222
223         public void PreviousWord() {
224                 if (cursor.y > text.Lines.Count) {
225                         cursor.x = 0;
226                         cursor.y--;
227                 } else if (cursor.y == text.Lines.Count) {
228                         string s = (String)text.Lines[text.Lines.Count - 1];
229                         int i = s.LastIndexOf(' ');
230                         if (i == -1) {
231                                 cursor.x = 0;
232                         } else {
233                                 cursor.x = i + 1;
234                         }
235                         cursor.y--;
236                 } else if (cursor.x == 0 && cursor.y > 0) {
237                         string s = (String)text.Lines[cursor.y - 1];
238                         int i = s.LastIndexOf(' ');
239                         if (i == -1) {
240                                 cursor.x = 0;
241                         } else {
242                                 cursor.x = i + 1;
243                         }
244                         cursor.y--;
245                 } else if (cursor.x == 0 && cursor.y == 0) {
246                         return;
247                 } else {
248                         string s = (String)text.Lines[cursor.y];
249                         if (cursor.x >= s.Length)
250                                 cursor.x = s.Length - 1;
251                         int i = s.LastIndexOf(' ', cursor.x - 2, cursor.x - 2);
252                         if (i == -1) {
253                                 cursor.x = 0;
254                         } else {
255                                 cursor.x = i + 1;
256                         }
257                 }
258         }
259
260         [GLib.ConnectBefore ()]
261         public void KeyPress(object o, Gtk.KeyPressEventArgs args) {
262                 if ((args.Event.State & Gdk.ModifierType.ControlMask) != 0) {
263                         switch(args.Event.Key) {
264                         case Gdk.Key.Right:
265                                 NextWord();
266                                 break;
267                         case Gdk.Key.Left:
268                                 PreviousWord();
269                                 break;
270                         }
271                 } else if ((args.Event.State & Gdk.ModifierType.Mod1Mask) != 0) {
272                 } else {
273                         switch(args.Event.Key) {
274                         case Gdk.Key.Up:
275                         case Gdk.Key.Down:
276                         case Gdk.Key.Left:
277                         case Gdk.Key.Right:
278                         case Gdk.Key.Page_Up:
279                         case Gdk.Key.Page_Down:
280                         case Gdk.Key.Home:
281                         case Gdk.Key.End:
282                                 MoveCursor(args.Event.Key);
283                                 break;
284                         case Gdk.Key.Delete:
285                                 text.Delete(cursor.x, cursor.y);
286                                 break;
287                         case Gdk.Key.BackSpace:
288                                 int l = 0;
289                                 if (cursor.y > 0) l = text.LineLength(cursor.y-1);
290                                 text.Backspace(cursor.x, cursor.y);
291                                 if (cursor.x == 0) {
292                                         cursor.y--;
293                                         if (cursor.y >= text.Lines.Count) return;
294                                         cursor.x = l;
295                                 } else {
296                                         cursor.x--;
297                                 }
298                                 break;
299                         case Gdk.Key.Return:
300                                 text.Newline(cursor.x, cursor.y);
301                                 cursor.x = 0;
302                                 cursor.y++;
303                                 break;
304                         default:
305                                 AddChar((int)args.Event.KeyValue);
306                                 break;
307                         }
308                 }
309                 FixTextWindow();
310         }
311         
312         public void AddChar(int c) {
313                 c = c & 0xFF;
314                 if (c > 128) return;
315
316                 text.Insert(cursor.x, cursor.y, (char)c);
317                 cursor.x++;
318         }
319
320         public void Save() {
321                 if (filename == null)
322                         throw new InvalidOperationException("Cannot Save() without filename");
323                 text.Save(filename);
324         }
325
326         public void Save(string filename) {
327                 this.filename = filename;
328                 Save();
329         }
330 }