1 | // |
---|
2 | // "$Id: editor.cxx 8602 2011-04-18 11:29:30Z manolo $" |
---|
3 | // |
---|
4 | // A simple text editor program for the Fast Light Tool Kit (FLTK). |
---|
5 | // |
---|
6 | // This program is described in Chapter 4 of the FLTK Programmer's Guide. |
---|
7 | // |
---|
8 | // Copyright 1998-2010 by Bill Spitzak and others. |
---|
9 | // |
---|
10 | // This library is free software; you can redistribute it and/or |
---|
11 | // modify it under the terms of the GNU Library General Public |
---|
12 | // License as published by the Free Software Foundation; either |
---|
13 | // version 2 of the License, or (at your option) any later version. |
---|
14 | // |
---|
15 | // This library is distributed in the hope that it will be useful, |
---|
16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
18 | // Library General Public License for more details. |
---|
19 | // |
---|
20 | // You should have received a copy of the GNU Library General Public |
---|
21 | // License along with this library; if not, write to the Free Software |
---|
22 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
---|
23 | // USA. |
---|
24 | // |
---|
25 | // Please report all bugs and problems on the following page: |
---|
26 | // |
---|
27 | // http://www.fltk.org/str.php |
---|
28 | // |
---|
29 | |
---|
30 | // |
---|
31 | // Include necessary headers... |
---|
32 | // |
---|
33 | |
---|
34 | #include <stdio.h> |
---|
35 | #include <stdlib.h> |
---|
36 | #include <string.h> |
---|
37 | #include <ctype.h> |
---|
38 | #include <errno.h> |
---|
39 | |
---|
40 | #ifdef __MWERKS__ |
---|
41 | # define FL_DLL |
---|
42 | #endif |
---|
43 | |
---|
44 | #include <FL/Fl.H> |
---|
45 | #include <FL/Fl_Group.H> |
---|
46 | #include <FL/Fl_Double_Window.H> |
---|
47 | #include <FL/fl_ask.H> |
---|
48 | #include <FL/Fl_Native_File_Chooser.H> |
---|
49 | #include <FL/Fl_Menu_Bar.H> |
---|
50 | #include <FL/Fl_Input.H> |
---|
51 | #include <FL/Fl_Button.H> |
---|
52 | #include <FL/Fl_Return_Button.H> |
---|
53 | #include <FL/Fl_Text_Buffer.H> |
---|
54 | #include <FL/Fl_Text_Editor.H> |
---|
55 | #include <FL/filename.H> |
---|
56 | |
---|
57 | int changed = 0; |
---|
58 | char filename[FL_PATH_MAX] = ""; |
---|
59 | char title[FL_PATH_MAX]; |
---|
60 | Fl_Text_Buffer *textbuf = 0; |
---|
61 | |
---|
62 | |
---|
63 | // Syntax highlighting stuff... |
---|
64 | #define TS 14 // default editor textsize |
---|
65 | Fl_Text_Buffer *stylebuf = 0; |
---|
66 | Fl_Text_Display::Style_Table_Entry |
---|
67 | styletable[] = { // Style table |
---|
68 | { FL_BLACK, FL_COURIER, TS }, // A - Plain |
---|
69 | { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS }, // B - Line comments |
---|
70 | { FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS }, // C - Block comments |
---|
71 | { FL_BLUE, FL_COURIER, TS }, // D - Strings |
---|
72 | { FL_DARK_RED, FL_COURIER, TS }, // E - Directives |
---|
73 | { FL_DARK_RED, FL_COURIER_BOLD, TS }, // F - Types |
---|
74 | { FL_BLUE, FL_COURIER_BOLD, TS }, // G - Keywords |
---|
75 | }; |
---|
76 | const char *code_keywords[] = { // List of known C/C++ keywords... |
---|
77 | "and", |
---|
78 | "and_eq", |
---|
79 | "asm", |
---|
80 | "bitand", |
---|
81 | "bitor", |
---|
82 | "break", |
---|
83 | "case", |
---|
84 | "catch", |
---|
85 | "compl", |
---|
86 | "continue", |
---|
87 | "default", |
---|
88 | "delete", |
---|
89 | "do", |
---|
90 | "else", |
---|
91 | "false", |
---|
92 | "for", |
---|
93 | "goto", |
---|
94 | "if", |
---|
95 | "new", |
---|
96 | "not", |
---|
97 | "not_eq", |
---|
98 | "operator", |
---|
99 | "or", |
---|
100 | "or_eq", |
---|
101 | "return", |
---|
102 | "switch", |
---|
103 | "template", |
---|
104 | "this", |
---|
105 | "throw", |
---|
106 | "true", |
---|
107 | "try", |
---|
108 | "while", |
---|
109 | "xor", |
---|
110 | "xor_eq" |
---|
111 | }; |
---|
112 | const char *code_types[] = { // List of known C/C++ types... |
---|
113 | "auto", |
---|
114 | "bool", |
---|
115 | "char", |
---|
116 | "class", |
---|
117 | "const", |
---|
118 | "const_cast", |
---|
119 | "double", |
---|
120 | "dynamic_cast", |
---|
121 | "enum", |
---|
122 | "explicit", |
---|
123 | "extern", |
---|
124 | "float", |
---|
125 | "friend", |
---|
126 | "inline", |
---|
127 | "int", |
---|
128 | "long", |
---|
129 | "mutable", |
---|
130 | "namespace", |
---|
131 | "private", |
---|
132 | "protected", |
---|
133 | "public", |
---|
134 | "register", |
---|
135 | "short", |
---|
136 | "signed", |
---|
137 | "sizeof", |
---|
138 | "static", |
---|
139 | "static_cast", |
---|
140 | "struct", |
---|
141 | "template", |
---|
142 | "typedef", |
---|
143 | "typename", |
---|
144 | "union", |
---|
145 | "unsigned", |
---|
146 | "virtual", |
---|
147 | "void", |
---|
148 | "volatile" |
---|
149 | }; |
---|
150 | |
---|
151 | |
---|
152 | // |
---|
153 | // 'compare_keywords()' - Compare two keywords... |
---|
154 | // |
---|
155 | |
---|
156 | extern "C" { |
---|
157 | int |
---|
158 | compare_keywords(const void *a, |
---|
159 | const void *b) { |
---|
160 | return (strcmp(*((const char **)a), *((const char **)b))); |
---|
161 | } |
---|
162 | } |
---|
163 | |
---|
164 | // |
---|
165 | // 'style_parse()' - Parse text and produce style data. |
---|
166 | // |
---|
167 | |
---|
168 | void |
---|
169 | style_parse(const char *text, |
---|
170 | char *style, |
---|
171 | int length) { |
---|
172 | char current; |
---|
173 | int col; |
---|
174 | int last; |
---|
175 | char buf[255], |
---|
176 | *bufptr; |
---|
177 | const char *temp; |
---|
178 | |
---|
179 | // Style letters: |
---|
180 | // |
---|
181 | // A - Plain |
---|
182 | // B - Line comments |
---|
183 | // C - Block comments |
---|
184 | // D - Strings |
---|
185 | // E - Directives |
---|
186 | // F - Types |
---|
187 | // G - Keywords |
---|
188 | |
---|
189 | for (current = *style, col = 0, last = 0; length > 0; length --, text ++) { |
---|
190 | if (current == 'B' || current == 'F' || current == 'G') current = 'A'; |
---|
191 | if (current == 'A') { |
---|
192 | // Check for directives, comments, strings, and keywords... |
---|
193 | if (col == 0 && *text == '#') { |
---|
194 | // Set style to directive |
---|
195 | current = 'E'; |
---|
196 | } else if (strncmp(text, "//", 2) == 0) { |
---|
197 | current = 'B'; |
---|
198 | for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B'; |
---|
199 | |
---|
200 | if (length == 0) break; |
---|
201 | } else if (strncmp(text, "/*", 2) == 0) { |
---|
202 | current = 'C'; |
---|
203 | } else if (strncmp(text, "\\\"", 2) == 0) { |
---|
204 | // Quoted quote... |
---|
205 | *style++ = current; |
---|
206 | *style++ = current; |
---|
207 | text ++; |
---|
208 | length --; |
---|
209 | col += 2; |
---|
210 | continue; |
---|
211 | } else if (*text == '\"') { |
---|
212 | current = 'D'; |
---|
213 | } else if (!last && (islower((*text)&255) || *text == '_')) { |
---|
214 | // Might be a keyword... |
---|
215 | for (temp = text, bufptr = buf; |
---|
216 | (islower((*temp)&255) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1); |
---|
217 | *bufptr++ = *temp++); |
---|
218 | |
---|
219 | if (!islower((*temp)&255) && *temp != '_') { |
---|
220 | *bufptr = '\0'; |
---|
221 | |
---|
222 | bufptr = buf; |
---|
223 | |
---|
224 | if (bsearch(&bufptr, code_types, |
---|
225 | sizeof(code_types) / sizeof(code_types[0]), |
---|
226 | sizeof(code_types[0]), compare_keywords)) { |
---|
227 | while (text < temp) { |
---|
228 | *style++ = 'F'; |
---|
229 | text ++; |
---|
230 | length --; |
---|
231 | col ++; |
---|
232 | } |
---|
233 | |
---|
234 | text --; |
---|
235 | length ++; |
---|
236 | last = 1; |
---|
237 | continue; |
---|
238 | } else if (bsearch(&bufptr, code_keywords, |
---|
239 | sizeof(code_keywords) / sizeof(code_keywords[0]), |
---|
240 | sizeof(code_keywords[0]), compare_keywords)) { |
---|
241 | while (text < temp) { |
---|
242 | *style++ = 'G'; |
---|
243 | text ++; |
---|
244 | length --; |
---|
245 | col ++; |
---|
246 | } |
---|
247 | |
---|
248 | text --; |
---|
249 | length ++; |
---|
250 | last = 1; |
---|
251 | continue; |
---|
252 | } |
---|
253 | } |
---|
254 | } |
---|
255 | } else if (current == 'C' && strncmp(text, "*/", 2) == 0) { |
---|
256 | // Close a C comment... |
---|
257 | *style++ = current; |
---|
258 | *style++ = current; |
---|
259 | text ++; |
---|
260 | length --; |
---|
261 | current = 'A'; |
---|
262 | col += 2; |
---|
263 | continue; |
---|
264 | } else if (current == 'D') { |
---|
265 | // Continuing in string... |
---|
266 | if (strncmp(text, "\\\"", 2) == 0) { |
---|
267 | // Quoted end quote... |
---|
268 | *style++ = current; |
---|
269 | *style++ = current; |
---|
270 | text ++; |
---|
271 | length --; |
---|
272 | col += 2; |
---|
273 | continue; |
---|
274 | } else if (*text == '\"') { |
---|
275 | // End quote... |
---|
276 | *style++ = current; |
---|
277 | col ++; |
---|
278 | current = 'A'; |
---|
279 | continue; |
---|
280 | } |
---|
281 | } |
---|
282 | |
---|
283 | // Copy style info... |
---|
284 | if (current == 'A' && (*text == '{' || *text == '}')) *style++ = 'G'; |
---|
285 | else *style++ = current; |
---|
286 | col ++; |
---|
287 | |
---|
288 | last = isalnum((*text)&255) || *text == '_' || *text == '.'; |
---|
289 | |
---|
290 | if (*text == '\n') { |
---|
291 | // Reset column and possibly reset the style |
---|
292 | col = 0; |
---|
293 | if (current == 'B' || current == 'E') current = 'A'; |
---|
294 | } |
---|
295 | } |
---|
296 | } |
---|
297 | |
---|
298 | |
---|
299 | // |
---|
300 | // 'style_init()' - Initialize the style buffer... |
---|
301 | // |
---|
302 | |
---|
303 | void |
---|
304 | style_init(void) { |
---|
305 | char *style = new char[textbuf->length() + 1]; |
---|
306 | char *text = textbuf->text(); |
---|
307 | |
---|
308 | memset(style, 'A', textbuf->length()); |
---|
309 | style[textbuf->length()] = '\0'; |
---|
310 | |
---|
311 | if (!stylebuf) stylebuf = new Fl_Text_Buffer(textbuf->length()); |
---|
312 | |
---|
313 | style_parse(text, style, textbuf->length()); |
---|
314 | |
---|
315 | stylebuf->text(style); |
---|
316 | delete[] style; |
---|
317 | free(text); |
---|
318 | } |
---|
319 | |
---|
320 | |
---|
321 | // |
---|
322 | // 'style_unfinished_cb()' - Update unfinished styles. |
---|
323 | // |
---|
324 | |
---|
325 | void |
---|
326 | style_unfinished_cb(int, void*) { |
---|
327 | } |
---|
328 | |
---|
329 | |
---|
330 | // |
---|
331 | // 'style_update()' - Update the style buffer... |
---|
332 | // |
---|
333 | |
---|
334 | void |
---|
335 | style_update(int pos, // I - Position of update |
---|
336 | int nInserted, // I - Number of inserted chars |
---|
337 | int nDeleted, // I - Number of deleted chars |
---|
338 | int /*nRestyled*/, // I - Number of restyled chars |
---|
339 | const char * /*deletedText*/,// I - Text that was deleted |
---|
340 | void *cbArg) { // I - Callback data |
---|
341 | int start, // Start of text |
---|
342 | end; // End of text |
---|
343 | char last, // Last style on line |
---|
344 | *style, // Style data |
---|
345 | *text; // Text data |
---|
346 | |
---|
347 | |
---|
348 | // If this is just a selection change, just unselect the style buffer... |
---|
349 | if (nInserted == 0 && nDeleted == 0) { |
---|
350 | stylebuf->unselect(); |
---|
351 | return; |
---|
352 | } |
---|
353 | |
---|
354 | // Track changes in the text buffer... |
---|
355 | if (nInserted > 0) { |
---|
356 | // Insert characters into the style buffer... |
---|
357 | style = new char[nInserted + 1]; |
---|
358 | memset(style, 'A', nInserted); |
---|
359 | style[nInserted] = '\0'; |
---|
360 | |
---|
361 | stylebuf->replace(pos, pos + nDeleted, style); |
---|
362 | delete[] style; |
---|
363 | } else { |
---|
364 | // Just delete characters in the style buffer... |
---|
365 | stylebuf->remove(pos, pos + nDeleted); |
---|
366 | } |
---|
367 | |
---|
368 | // Select the area that was just updated to avoid unnecessary |
---|
369 | // callbacks... |
---|
370 | stylebuf->select(pos, pos + nInserted - nDeleted); |
---|
371 | |
---|
372 | // Re-parse the changed region; we do this by parsing from the |
---|
373 | // beginning of the previous line of the changed region to the end of |
---|
374 | // the line of the changed region... Then we check the last |
---|
375 | // style character and keep updating if we have a multi-line |
---|
376 | // comment character... |
---|
377 | start = textbuf->line_start(pos); |
---|
378 | // if (start > 0) start = textbuf->line_start(start - 1); |
---|
379 | end = textbuf->line_end(pos + nInserted); |
---|
380 | text = textbuf->text_range(start, end); |
---|
381 | style = stylebuf->text_range(start, end); |
---|
382 | if (start==end) |
---|
383 | last = 0; |
---|
384 | else |
---|
385 | last = style[end - start - 1]; |
---|
386 | |
---|
387 | // printf("start = %d, end = %d, text = \"%s\", style = \"%s\", last='%c'...\n", |
---|
388 | // start, end, text, style, last); |
---|
389 | |
---|
390 | style_parse(text, style, end - start); |
---|
391 | |
---|
392 | // printf("new style = \"%s\", new last='%c'...\n", |
---|
393 | // style, style[end - start - 1]); |
---|
394 | |
---|
395 | stylebuf->replace(start, end, style); |
---|
396 | ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end); |
---|
397 | |
---|
398 | if (start==end || last != style[end - start - 1]) { |
---|
399 | // printf("Recalculate the rest of the buffer style\n"); |
---|
400 | // Either the user deleted some text, or the last character |
---|
401 | // on the line changed styles, so reparse the |
---|
402 | // remainder of the buffer... |
---|
403 | free(text); |
---|
404 | free(style); |
---|
405 | |
---|
406 | end = textbuf->length(); |
---|
407 | text = textbuf->text_range(start, end); |
---|
408 | style = stylebuf->text_range(start, end); |
---|
409 | |
---|
410 | style_parse(text, style, end - start); |
---|
411 | |
---|
412 | stylebuf->replace(start, end, style); |
---|
413 | ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end); |
---|
414 | } |
---|
415 | |
---|
416 | free(text); |
---|
417 | free(style); |
---|
418 | } |
---|
419 | |
---|
420 | |
---|
421 | // Editor window functions and class... |
---|
422 | void save_cb(); |
---|
423 | void saveas_cb(); |
---|
424 | void find2_cb(Fl_Widget*, void*); |
---|
425 | void replall_cb(Fl_Widget*, void*); |
---|
426 | void replace2_cb(Fl_Widget*, void*); |
---|
427 | void replcan_cb(Fl_Widget*, void*); |
---|
428 | |
---|
429 | class EditorWindow : public Fl_Double_Window { |
---|
430 | public: |
---|
431 | EditorWindow(int w, int h, const char* t); |
---|
432 | ~EditorWindow(); |
---|
433 | |
---|
434 | Fl_Window *replace_dlg; |
---|
435 | Fl_Input *replace_find; |
---|
436 | Fl_Input *replace_with; |
---|
437 | Fl_Button *replace_all; |
---|
438 | Fl_Return_Button *replace_next; |
---|
439 | Fl_Button *replace_cancel; |
---|
440 | |
---|
441 | Fl_Text_Editor *editor; |
---|
442 | char search[256]; |
---|
443 | }; |
---|
444 | |
---|
445 | EditorWindow::EditorWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) { |
---|
446 | replace_dlg = new Fl_Window(300, 105, "Replace"); |
---|
447 | replace_find = new Fl_Input(80, 10, 210, 25, "Find:"); |
---|
448 | replace_find->align(FL_ALIGN_LEFT); |
---|
449 | |
---|
450 | replace_with = new Fl_Input(80, 40, 210, 25, "Replace:"); |
---|
451 | replace_with->align(FL_ALIGN_LEFT); |
---|
452 | |
---|
453 | replace_all = new Fl_Button(10, 70, 90, 25, "Replace All"); |
---|
454 | replace_all->callback((Fl_Callback *)replall_cb, this); |
---|
455 | |
---|
456 | replace_next = new Fl_Return_Button(105, 70, 120, 25, "Replace Next"); |
---|
457 | replace_next->callback((Fl_Callback *)replace2_cb, this); |
---|
458 | |
---|
459 | replace_cancel = new Fl_Button(230, 70, 60, 25, "Cancel"); |
---|
460 | replace_cancel->callback((Fl_Callback *)replcan_cb, this); |
---|
461 | replace_dlg->end(); |
---|
462 | replace_dlg->set_non_modal(); |
---|
463 | editor = 0; |
---|
464 | *search = (char)0; |
---|
465 | } |
---|
466 | |
---|
467 | EditorWindow::~EditorWindow() { |
---|
468 | delete replace_dlg; |
---|
469 | } |
---|
470 | |
---|
471 | int check_save(void) { |
---|
472 | if (!changed) return 1; |
---|
473 | |
---|
474 | int r = fl_choice("The current file has not been saved.\n" |
---|
475 | "Would you like to save it now?", |
---|
476 | "Cancel", "Save", "Don't Save"); |
---|
477 | |
---|
478 | if (r == 1) { |
---|
479 | save_cb(); // Save the file... |
---|
480 | return !changed; |
---|
481 | } |
---|
482 | |
---|
483 | return (r == 2) ? 1 : 0; |
---|
484 | } |
---|
485 | |
---|
486 | int loading = 0; |
---|
487 | void load_file(const char *newfile, int ipos) { |
---|
488 | loading = 1; |
---|
489 | int insert = (ipos != -1); |
---|
490 | changed = insert; |
---|
491 | if (!insert) strcpy(filename, ""); |
---|
492 | int r; |
---|
493 | if (!insert) r = textbuf->loadfile(newfile); |
---|
494 | else r = textbuf->insertfile(newfile, ipos); |
---|
495 | changed = changed || textbuf->input_file_was_transcoded; |
---|
496 | if (r) |
---|
497 | fl_alert("Error reading from file \'%s\':\n%s.", newfile, strerror(errno)); |
---|
498 | else |
---|
499 | if (!insert) strcpy(filename, newfile); |
---|
500 | loading = 0; |
---|
501 | textbuf->call_modify_callbacks(); |
---|
502 | } |
---|
503 | |
---|
504 | void save_file(const char *newfile) { |
---|
505 | if (textbuf->savefile(newfile)) |
---|
506 | fl_alert("Error writing to file \'%s\':\n%s.", newfile, strerror(errno)); |
---|
507 | else |
---|
508 | strcpy(filename, newfile); |
---|
509 | changed = 0; |
---|
510 | textbuf->call_modify_callbacks(); |
---|
511 | } |
---|
512 | |
---|
513 | void copy_cb(Fl_Widget*, void* v) { |
---|
514 | EditorWindow* e = (EditorWindow*)v; |
---|
515 | Fl_Text_Editor::kf_copy(0, e->editor); |
---|
516 | } |
---|
517 | |
---|
518 | void cut_cb(Fl_Widget*, void* v) { |
---|
519 | EditorWindow* e = (EditorWindow*)v; |
---|
520 | Fl_Text_Editor::kf_cut(0, e->editor); |
---|
521 | } |
---|
522 | |
---|
523 | void delete_cb(Fl_Widget*, void*) { |
---|
524 | textbuf->remove_selection(); |
---|
525 | } |
---|
526 | |
---|
527 | void find_cb(Fl_Widget* w, void* v) { |
---|
528 | EditorWindow* e = (EditorWindow*)v; |
---|
529 | const char *val; |
---|
530 | |
---|
531 | val = fl_input("Search String:", e->search); |
---|
532 | if (val != NULL) { |
---|
533 | // User entered a string - go find it! |
---|
534 | strcpy(e->search, val); |
---|
535 | find2_cb(w, v); |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | void find2_cb(Fl_Widget* w, void* v) { |
---|
540 | EditorWindow* e = (EditorWindow*)v; |
---|
541 | if (e->search[0] == '\0') { |
---|
542 | // Search string is blank; get a new one... |
---|
543 | find_cb(w, v); |
---|
544 | return; |
---|
545 | } |
---|
546 | |
---|
547 | int pos = e->editor->insert_position(); |
---|
548 | int found = textbuf->search_forward(pos, e->search, &pos); |
---|
549 | if (found) { |
---|
550 | // Found a match; select and update the position... |
---|
551 | textbuf->select(pos, pos+strlen(e->search)); |
---|
552 | e->editor->insert_position(pos+strlen(e->search)); |
---|
553 | e->editor->show_insert_position(); |
---|
554 | } |
---|
555 | else fl_alert("No occurrences of \'%s\' found!", e->search); |
---|
556 | } |
---|
557 | |
---|
558 | void set_title(Fl_Window* w) { |
---|
559 | if (filename[0] == '\0') strcpy(title, "Untitled"); |
---|
560 | else { |
---|
561 | char *slash; |
---|
562 | slash = strrchr(filename, '/'); |
---|
563 | #ifdef WIN32 |
---|
564 | if (slash == NULL) slash = strrchr(filename, '\\'); |
---|
565 | #endif |
---|
566 | if (slash != NULL) strcpy(title, slash + 1); |
---|
567 | else strcpy(title, filename); |
---|
568 | } |
---|
569 | |
---|
570 | if (changed) strcat(title, " (modified)"); |
---|
571 | |
---|
572 | w->label(title); |
---|
573 | } |
---|
574 | |
---|
575 | void changed_cb(int, int nInserted, int nDeleted,int, const char*, void* v) { |
---|
576 | if ((nInserted || nDeleted) && !loading) changed = 1; |
---|
577 | EditorWindow *w = (EditorWindow *)v; |
---|
578 | set_title(w); |
---|
579 | if (loading) w->editor->show_insert_position(); |
---|
580 | } |
---|
581 | |
---|
582 | void new_cb(Fl_Widget*, void*) { |
---|
583 | if (!check_save()) return; |
---|
584 | |
---|
585 | filename[0] = '\0'; |
---|
586 | textbuf->select(0, textbuf->length()); |
---|
587 | textbuf->remove_selection(); |
---|
588 | changed = 0; |
---|
589 | textbuf->call_modify_callbacks(); |
---|
590 | } |
---|
591 | |
---|
592 | void open_cb(Fl_Widget*, void*) { |
---|
593 | if (!check_save()) return; |
---|
594 | Fl_Native_File_Chooser fnfc; |
---|
595 | fnfc.title("Open file"); |
---|
596 | fnfc.type(Fl_Native_File_Chooser::BROWSE_FILE); |
---|
597 | if ( fnfc.show() ) return; |
---|
598 | load_file(fnfc.filename(), -1); |
---|
599 | |
---|
600 | } |
---|
601 | |
---|
602 | void insert_cb(Fl_Widget*, void *v) { |
---|
603 | Fl_Native_File_Chooser fnfc; |
---|
604 | fnfc.title("Insert file"); |
---|
605 | fnfc.type(Fl_Native_File_Chooser::BROWSE_FILE); |
---|
606 | if ( fnfc.show() ) return; |
---|
607 | EditorWindow *w = (EditorWindow *)v; |
---|
608 | load_file(fnfc.filename(), w->editor->insert_position()); |
---|
609 | } |
---|
610 | |
---|
611 | void paste_cb(Fl_Widget*, void* v) { |
---|
612 | EditorWindow* e = (EditorWindow*)v; |
---|
613 | Fl_Text_Editor::kf_paste(0, e->editor); |
---|
614 | } |
---|
615 | |
---|
616 | int num_windows = 0; |
---|
617 | |
---|
618 | void close_cb(Fl_Widget*, void* v) { |
---|
619 | EditorWindow* w = (EditorWindow*)v; |
---|
620 | |
---|
621 | if (num_windows == 1) { |
---|
622 | if (!check_save()) |
---|
623 | return; |
---|
624 | } |
---|
625 | |
---|
626 | w->hide(); |
---|
627 | w->editor->buffer(0); |
---|
628 | textbuf->remove_modify_callback(style_update, w->editor); |
---|
629 | textbuf->remove_modify_callback(changed_cb, w); |
---|
630 | Fl::delete_widget(w); |
---|
631 | |
---|
632 | num_windows--; |
---|
633 | if (!num_windows) exit(0); |
---|
634 | } |
---|
635 | |
---|
636 | void quit_cb(Fl_Widget*, void*) { |
---|
637 | if (changed && !check_save()) |
---|
638 | return; |
---|
639 | |
---|
640 | exit(0); |
---|
641 | } |
---|
642 | |
---|
643 | void replace_cb(Fl_Widget*, void* v) { |
---|
644 | EditorWindow* e = (EditorWindow*)v; |
---|
645 | e->replace_dlg->show(); |
---|
646 | } |
---|
647 | |
---|
648 | void replace2_cb(Fl_Widget*, void* v) { |
---|
649 | EditorWindow* e = (EditorWindow*)v; |
---|
650 | const char *find = e->replace_find->value(); |
---|
651 | const char *replace = e->replace_with->value(); |
---|
652 | |
---|
653 | if (find[0] == '\0') { |
---|
654 | // Search string is blank; get a new one... |
---|
655 | e->replace_dlg->show(); |
---|
656 | return; |
---|
657 | } |
---|
658 | |
---|
659 | e->replace_dlg->hide(); |
---|
660 | |
---|
661 | int pos = e->editor->insert_position(); |
---|
662 | int found = textbuf->search_forward(pos, find, &pos); |
---|
663 | |
---|
664 | if (found) { |
---|
665 | // Found a match; update the position and replace text... |
---|
666 | textbuf->select(pos, pos+strlen(find)); |
---|
667 | textbuf->remove_selection(); |
---|
668 | textbuf->insert(pos, replace); |
---|
669 | textbuf->select(pos, pos+strlen(replace)); |
---|
670 | e->editor->insert_position(pos+strlen(replace)); |
---|
671 | e->editor->show_insert_position(); |
---|
672 | } |
---|
673 | else fl_alert("No occurrences of \'%s\' found!", find); |
---|
674 | } |
---|
675 | |
---|
676 | void replall_cb(Fl_Widget*, void* v) { |
---|
677 | EditorWindow* e = (EditorWindow*)v; |
---|
678 | const char *find = e->replace_find->value(); |
---|
679 | const char *replace = e->replace_with->value(); |
---|
680 | |
---|
681 | find = e->replace_find->value(); |
---|
682 | if (find[0] == '\0') { |
---|
683 | // Search string is blank; get a new one... |
---|
684 | e->replace_dlg->show(); |
---|
685 | return; |
---|
686 | } |
---|
687 | |
---|
688 | e->replace_dlg->hide(); |
---|
689 | |
---|
690 | e->editor->insert_position(0); |
---|
691 | int times = 0; |
---|
692 | |
---|
693 | // Loop through the whole string |
---|
694 | for (int found = 1; found;) { |
---|
695 | int pos = e->editor->insert_position(); |
---|
696 | found = textbuf->search_forward(pos, find, &pos); |
---|
697 | |
---|
698 | if (found) { |
---|
699 | // Found a match; update the position and replace text... |
---|
700 | textbuf->select(pos, pos+strlen(find)); |
---|
701 | textbuf->remove_selection(); |
---|
702 | textbuf->insert(pos, replace); |
---|
703 | e->editor->insert_position(pos+strlen(replace)); |
---|
704 | e->editor->show_insert_position(); |
---|
705 | times++; |
---|
706 | } |
---|
707 | } |
---|
708 | |
---|
709 | if (times) fl_message("Replaced %d occurrences.", times); |
---|
710 | else fl_alert("No occurrences of \'%s\' found!", find); |
---|
711 | } |
---|
712 | |
---|
713 | void replcan_cb(Fl_Widget*, void* v) { |
---|
714 | EditorWindow* e = (EditorWindow*)v; |
---|
715 | e->replace_dlg->hide(); |
---|
716 | } |
---|
717 | |
---|
718 | void save_cb() { |
---|
719 | if (filename[0] == '\0') { |
---|
720 | // No filename - get one! |
---|
721 | saveas_cb(); |
---|
722 | return; |
---|
723 | } |
---|
724 | else save_file(filename); |
---|
725 | } |
---|
726 | |
---|
727 | void saveas_cb() { |
---|
728 | Fl_Native_File_Chooser fnfc; |
---|
729 | fnfc.title("Save File As?"); |
---|
730 | fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); |
---|
731 | if ( fnfc.show() ) return; |
---|
732 | save_file(fnfc.filename()); |
---|
733 | } |
---|
734 | |
---|
735 | Fl_Window* new_view(); |
---|
736 | |
---|
737 | void view_cb(Fl_Widget*, void*) { |
---|
738 | Fl_Window* w = new_view(); |
---|
739 | w->show(); |
---|
740 | } |
---|
741 | |
---|
742 | Fl_Menu_Item menuitems[] = { |
---|
743 | { "&File", 0, 0, 0, FL_SUBMENU }, |
---|
744 | { "&New File", 0, (Fl_Callback *)new_cb }, |
---|
745 | { "&Open File...", FL_COMMAND + 'o', (Fl_Callback *)open_cb }, |
---|
746 | { "&Insert File...", FL_COMMAND + 'i', (Fl_Callback *)insert_cb, 0, FL_MENU_DIVIDER }, |
---|
747 | { "&Save File", FL_COMMAND + 's', (Fl_Callback *)save_cb }, |
---|
748 | { "Save File &As...", FL_COMMAND + FL_SHIFT + 's', (Fl_Callback *)saveas_cb, 0, FL_MENU_DIVIDER }, |
---|
749 | { "New &View", FL_ALT |
---|
750 | #ifdef __APPLE__ |
---|
751 | + FL_COMMAND |
---|
752 | #endif |
---|
753 | + 'v', (Fl_Callback *)view_cb, 0 }, |
---|
754 | { "&Close View", FL_COMMAND + 'w', (Fl_Callback *)close_cb, 0, FL_MENU_DIVIDER }, |
---|
755 | { "E&xit", FL_COMMAND + 'q', (Fl_Callback *)quit_cb, 0 }, |
---|
756 | { 0 }, |
---|
757 | |
---|
758 | { "&Edit", 0, 0, 0, FL_SUBMENU }, |
---|
759 | { "Cu&t", FL_COMMAND + 'x', (Fl_Callback *)cut_cb }, |
---|
760 | { "&Copy", FL_COMMAND + 'c', (Fl_Callback *)copy_cb }, |
---|
761 | { "&Paste", FL_COMMAND + 'v', (Fl_Callback *)paste_cb }, |
---|
762 | { "&Delete", 0, (Fl_Callback *)delete_cb }, |
---|
763 | { 0 }, |
---|
764 | |
---|
765 | { "&Search", 0, 0, 0, FL_SUBMENU }, |
---|
766 | { "&Find...", FL_COMMAND + 'f', (Fl_Callback *)find_cb }, |
---|
767 | { "F&ind Again", FL_COMMAND + 'g', find2_cb }, |
---|
768 | { "&Replace...", FL_COMMAND + 'r', replace_cb }, |
---|
769 | { "Re&place Again", FL_COMMAND + 't', replace2_cb }, |
---|
770 | { 0 }, |
---|
771 | |
---|
772 | { 0 } |
---|
773 | }; |
---|
774 | |
---|
775 | Fl_Window* new_view() { |
---|
776 | EditorWindow* w = new EditorWindow(660, 400, title); |
---|
777 | w->begin(); |
---|
778 | Fl_Menu_Bar* m = new Fl_Menu_Bar(0, 0, 660, 30); |
---|
779 | m->copy(menuitems, w); |
---|
780 | w->editor = new Fl_Text_Editor(0, 30, 660, 370); |
---|
781 | w->editor->textfont(FL_COURIER); |
---|
782 | w->editor->textsize(TS); |
---|
783 | //w->editor->wrap_mode(Fl_Text_Editor::WRAP_AT_BOUNDS, 250); |
---|
784 | w->editor->buffer(textbuf); |
---|
785 | w->editor->highlight_data(stylebuf, styletable, |
---|
786 | sizeof(styletable) / sizeof(styletable[0]), |
---|
787 | 'A', style_unfinished_cb, 0); |
---|
788 | textbuf->text(); |
---|
789 | style_init(); |
---|
790 | w->end(); |
---|
791 | w->resizable(w->editor); |
---|
792 | w->callback((Fl_Callback *)close_cb, w); |
---|
793 | |
---|
794 | textbuf->add_modify_callback(style_update, w->editor); |
---|
795 | textbuf->add_modify_callback(changed_cb, w); |
---|
796 | textbuf->call_modify_callbacks(); |
---|
797 | num_windows++; |
---|
798 | return w; |
---|
799 | } |
---|
800 | |
---|
801 | int main(int argc, char **argv) { |
---|
802 | textbuf = new Fl_Text_Buffer; |
---|
803 | //textbuf->transcoding_warning_action = NULL; |
---|
804 | style_init(); |
---|
805 | |
---|
806 | Fl_Window* window = new_view(); |
---|
807 | |
---|
808 | window->show(1, argv); |
---|
809 | |
---|
810 | if (argc > 1) load_file(argv[1], -1); |
---|
811 | |
---|
812 | return Fl::run(); |
---|
813 | } |
---|
814 | |
---|
815 | // |
---|
816 | // End of "$Id: editor.cxx 8602 2011-04-18 11:29:30Z manolo $". |
---|
817 | // |
---|