class EditorWindow : public Fl_Double_Window { public: EditorWindow(int w, int h, const char* t); ~EditorWindow(); Fl_Window *replace_dlg; Fl_Input *replace_find; Fl_Input *replace_with; Fl_Button *replace_all; Fl_Return_Button *replace_next; Fl_Button *replace_cancel; Fl_Text_Editor *editor; char search[256]; };
int changed = 0; char filename[1024] = ""; char search[256] = ""; Fl_Text_Buffer *textbuf;The textbuf variable is the text editor buffer for our window described previously. We'll cover the other variables as we build the application.
Fl_Menu_Item menuitems[] = { { "&File", 0, 0, 0, FL_SUBMENU }, { "&New File", 0, (Fl_Callback *)new_cb }, { "&Open File...", FL_CTRL + 'o', (Fl_Callback *)open_cb }, { "&Insert File...", FL_CTRL + 'i', (Fl_Callback *)insert_cb, 0, FL_MENU_DIVIDER }, { "&Save File", FL_CTRL + 's', (Fl_Callback *)save_cb }, { "Save File &As...", FL_CTRL + FL_SHIFT + 's', (Fl_Callback *)saveas_cb, 0, FL_MENU_DIVIDER }, { "New &View", FL_ALT + 'v', (Fl_Callback *)view_cb, 0 }, { "&Close View", FL_CTRL + 'w', (Fl_Callback *)close_cb, 0, FL_MENU_DIVIDER }, { "E&xit", FL_CTRL + 'q', (Fl_Callback *)quit_cb, 0 }, { 0 }, { "&Edit", 0, 0, 0, FL_SUBMENU }, { "&Undo", FL_CTRL + 'z', (Fl_Callback *)undo_cb, 0, FL_MENU_DIVIDER }, { "Cu&t", FL_CTRL + 'x', (Fl_Callback *)cut_cb }, { "&Copy", FL_CTRL + 'c', (Fl_Callback *)copy_cb }, { "&Paste", FL_CTRL + 'v', (Fl_Callback *)paste_cb }, { "&Delete", 0, (Fl_Callback *)delete_cb }, { 0 }, { "&Search", 0, 0, 0, FL_SUBMENU }, { "&Find...", FL_CTRL + 'f', (Fl_Callback *)find_cb }, { "F&ind Again", FL_CTRL + 'g', find2_cb }, { "&Replace...", FL_CTRL + 'r', replace_cb }, { "Re&place Again", FL_CTRL + 't', replace2_cb }, { 0 }, { 0 } };Once we have the menus defined we can create the Fl_Menu_Bar widget and assign the menus to it with:
Fl_Menu_Bar *m = new Fl_Menu_Bar(0, 0, 512, 30); m->copy(menuitems);We'll define the callback functions later.
w->editor = new Fl_Text_Editor(0, 30, 512, 354); w->editor->buffer(textbuf);So that we can keep track of changes to the file, we also want to add a "modify" callback:
textbuf->add_modify_callback(changed_cb, w); textbuf->call_modify_callbacks();Finally, we want to use a mono-spaced font like FL_COURIER:
w->editor->textfont(FL_COURIER);
Fl_Window *replace_dlg = new Fl_Window(300, 105, "Replace"); Fl_Input *replace_find = new Fl_Input(70, 10, 200, 25, "Find:"); Fl_Input *replace_with = new Fl_Input(70, 40, 200, 25, "Replace:"); Fl_Button *replace_all = new Fl_Button(10, 70, 90, 25, "Replace All"); Fl_Button *replace_next = new Fl_Button(105, 70, 120, 25, "Replace Next"); Fl_Button *replace_cancel = new Fl_Button(230, 70, 60, 25, "Cancel");
void changed_cb(void) { set_changed(1); }The set_changed() function is one that we will write to set the changed status on the current file. We're doing it this way because some of the other callbacks will set the changed status to 0, and also because we want to show the changed status in the window's title bar.
void copy_cb(void) { input->copy(); }
void cut_cb(void) { input->copy(); input->cut(); }
void delete_cb(void) { input->cut(); }
void find_cb(void) { const char *val; val = fl_input("Search String:", search); if (val != NULL) { // User entered a string - go find it! strcpy(search, val); find2_cb(); } }
void find2_cb(void) { const char *val, *found; int pos; if (search[0] == '\0') { // Search string is blank; get a new one... find_cb(); return; } val = input->value() + input->mark(); found = strstr(val, search); if (found != NULL) { // Found a match; update the position and mark... pos = input->mark() + found - val; input->position(pos, pos + strlen(search)); } else fl_alert("No occurrences of \'%s\' found!", search); }If the search string cannot be found we use the fl_alert() convenience function to display a message to that effect.
void new_cb(void) { if (changed) if (!check_save()) return; filename[0] = '\0'; input->value(""); set_changed(0); }
void open_cb(void) { char *newfile; if (changed) if (!check_save()) return; newfile = fl_file_chooser("Open File?", "*", filename); if (newfile != NULL) load_file(newfile); }We call the load_file() function to actually load the file.
void paste_cb(void) { Fl::paste(*input); }
void quit_cb(void) { if (changed) if (!check_save()) return; window->hide(); }
void replace_cb(Fl_Widget*, void* v) { EditorWindow* e = (EditorWindow*)v; e->replace_dlg->show(); }
void replace2_cb(Fl_Widget*, void* v) { EditorWindow* e = (EditorWindow*)v; const char *find = e->replace_find->value(); const char *replace = e->replace_with->value(); if (find[0] == '\0') { // Search string is blank; get a new one... e->replace_dlg->show(); return; } e->replace_dlg->hide(); int pos = e->editor->insert_position(); int found = textbuf->search_forward(pos, find, &pos); if (found) { // Found a match; update the position and replace text... textbuf->select(pos, pos+strlen(find)); textbuf->remove_selection(); textbuf->insert(pos, replace); textbuf->select(pos, pos+strlen(replace)); e->editor->insert_position(pos+strlen(replace)); e->editor->show_insert_position(); } else fl_alert("No occurrences of \'%s\' found!", find); }
void replall_cb(Fl_Widget*, void* v) { EditorWindow* e = (EditorWindow*)v; const char *find = e->replace_find->value(); const char *replace = e->replace_with->value(); find = e->replace_find->value(); if (find[0] == '\0') { // Search string is blank; get a new one... e->replace_dlg->show(); return; } e->replace_dlg->hide(); e->editor->insert_position(0); int times = 0; // Loop through the whole string for (int found = 1; found;) { int pos = e->editor->insert_position(); found = textbuf->search_forward(pos, find, &pos); if (found) { // Found a match; update the position and replace text... textbuf->select(pos, pos+strlen(find)); textbuf->remove_selection(); textbuf->insert(pos, replace); e->editor->insert_position(pos+strlen(replace)); e->editor->show_insert_position(); times++; } } if (times) fl_message("Replaced %d occurrences.", times); else fl_alert("No occurrences of \'%s\' found!", find); }
void replcan_cb(Fl_Widget*, void* v) { EditorWindow* e = (EditorWindow*)v; e->replace_dlg->hide(); }
void save_cb(void) { if (filename[0] == '\0') { // No filename - get one! saveas_cb(); return; } else save_file(filename); }The save_file() function saves the current file to the specified filename.
void saveas_cb(void) { char *newfile; newfile = fl_file_chooser("Save File As?", "*", filename); if (newfile != NULL) save_file(newfile); }The save_file() function saves the current file to the specified filename.
void undo_cb(void) { input->undo(); }
int check_save(void) { if (!changed) return 1; int r = fl_choice("The current file has not been saved.\n" "Would you like to save it now?", "Cancel", "Save", "Discard"); if (r == 1) { save_cb(); // Save the file... return !changed; } return (r == 2) ? 1 : 0; }
int loading = 0; void load_file(char *newfile, int ipos) { loading = 1; int insert = (ipos != -1); changed = insert; if (!insert) strcpy(filename, ""); int r; if (!insert) r = textbuf->loadfile(newfile); else r = textbuf->insertfile(newfile, ipos); if (r) fl_alert("Error reading from file \'%s\':\n%s.", newfile, strerror(errno)); else if (!insert) strcpy(filename, newfile); loading = 0; textbuf->call_modify_callbacks(); }When loading the file we use the loadfile() method to "replace" the text in the buffer.
void save_file(char *newfile) { if (textbuf->savefile(newfile)) fl_alert("Error writing to file \'%s\':\n%s.", newfile, strerror(errno)); else strcpy(filename, newfile); changed = 0; textbuf->call_modify_callbacks(); }
void set_changed(int c) { if (c != changed) { char title[1024]; char *slash; changed = c; if (filename[0] == '\0') strcpy(title, "Untitled"); else { slash = strrchr(filename, '/'); if (slash == NULL) slash = strrchr(filename, '\\'); if (slash != NULL) strcpy(title, slash + 1); else strcpy(title, filename); } if (changed) strcat(title, " (modified)"); window->label(title); } }
CC -o editor editor.cxx -lfltk -lXext -lX11 -lmAs noted in Chapter 1, you may need to include compiler and linker options to tell them where to find the FLTK library. Also, the CC command may also be called gcc or c++ on your system.
Congratulations, you've just built your own text editor!