FLTK 是一个GUI库,它提供了跨平台支持。下载源代码在 deepin 系统中,make 即可生成。生成时,根据提示需先安装依赖库。
编译 FLTK 需要的库:
sudo apt-get install libx11-dev
sudo apt-get install libxrender-dev
sudo apt-get install libxft-dev
sudo apt-get install libgl1-mesa-dev
sudo apt-get install libglu1-mesa-dev
sudo apt install mesa-utils
sudo apt install libglew-dev
FLTK Editor 效果图:
编译运行:
- 在fltk目录中, 新建 Editor.cxx 文件,终端编译:
./fltk-config --compile Editor.cxx`
- 终端运行:
./Editor
Editor.cxx 源码:
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/fl_ask.H>
#include <FL/filename.H>
#include <FL/fl_string_functions.h>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <FL/platform.H>
#include <FL/Fl_Flex.H>
#include <FL/Fl_Tile.H>
#include <errno.h>
Fl_Double_Window *window = NULL;
Fl_Menu_Bar *menubar = NULL;
bool text_changed = false;
char app_filename[FL_PATH_MAX + 1] = "";
char last_find_text[1024] = "";
char last_replace_text[1024] = "";
Fl_Text_Editor *text_editor = NULL;
Fl_Text_Editor *split_editor = NULL;
Fl_Text_Buffer *text_buffer = NULL;
Fl_Tile *app_tile = NULL;
void SetChanged(bool);
void TextChanged(int, int inserted, int deleted, int, const char *, void *) {
SetChanged(true);
}
void MenuNewCallback(Fl_Widget *, void *) {
text_buffer->text("");
SetChanged(false);
}
void Build_Editor() {
window->begin();
text_buffer = new Fl_Text_Buffer();
text_buffer->add_modify_callback(TextChanged, NULL);
text_editor = new Fl_Text_Editor(0, 25, window->w(), window->h() - 25);
text_editor->buffer(text_buffer);
text_editor->textfont(FL_COURIER);
window->resizable(text_editor);
window->end();
}
void Build_Window() {
window = new Fl_Double_Window(900, 680, "FLTK Editor");
}
void UpdateTitle() {
const char *filename = NULL;
if (app_filename[0])
filename = fl_filename_name(app_filename);
if (filename) {
char buf[FL_PATH_MAX + 3];
if (text_changed) {
snprintf(buf, FL_PATH_MAX + 2, "%s *", filename);
} else {
snprintf(buf, FL_PATH_MAX + 2, "%s", filename);
}
window->copy_label(buf);
} else {
window->label("FLTK Editor");
}
}
void SetChanged(bool v) {
if (v != text_changed) {
text_changed = v;
UpdateTitle();
}
}
void SetFilename(const char *filename) {
if (filename) {
fl_strlcpy(app_filename, filename, FL_PATH_MAX + 1);
} else {
app_filename[0] = 0;
}
UpdateTitle();
}
void MenuQuitCallback(Fl_Widget *w, void *v) {
if (text_changed) {
int c = fl_choice("Save changes?", "Quit", "Cancel", NULL);
if (c == 1) {
return;
}
}
Fl::hide_all_windows();
}
void Build_Menu() {
window->begin();
menubar = new Fl_Menu_Bar(0, 0, window->w(), 25);
menubar->add("File/Quit", FL_COMMAND + 'q', MenuQuitCallback);
int index = menubar->find_index(MenuQuitCallback);
menubar->insert(index, "New", FL_COMMAND + 'n', MenuNewCallback);
window->callback(MenuQuitCallback);
window->end();
}
void MenuSaveAsCallback(Fl_Widget *, void *) {
Fl_Native_File_Chooser chooser;
chooser.title("Save As");
chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
if (app_filename[0]) {
char temp[FL_PATH_MAX + 1];
fl_strlcpy(temp, app_filename, FL_PATH_MAX + 1);
const char *name = fl_filename_name(temp);
if (name) {
chooser.preset_file(name);
temp[name - temp] = 0;
chooser.directory(temp);
}
}
if (chooser.show() == 0) {
text_buffer->savefile(chooser.filename());
SetFilename(chooser.filename());
SetChanged(false);
}
}
void MenuSaveCallback(Fl_Widget *, void *) {
if (app_filename[0]) {
text_buffer->savefile(app_filename);
SetChanged(false);
} else {
MenuSaveAsCallback(NULL, NULL);
}
}
void LoadFlie(const char *filename) {
if (filename && filename[0]) {
text_buffer->loadfile(filename);
SetFilename(filename);
SetChanged(false);
}
}
// TODO: Add Open File (Fl_Native_File_Chooser
void MenuOpenCallback(Fl_Widget *, void *) {
if (text_changed) {
int c = fl_choice("Save changes?", "Cancel", "Save", "Don't Save");
if (c == 2)
return;
if (c == 1)
MenuSaveCallback(NULL, NULL);
}
Fl_Native_File_Chooser chooser;
chooser.title("Open File");
chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
if (!app_filename[0]) {
char temp[FL_PATH_MAX + 1];
fl_strlcpy(temp, app_filename, FL_PATH_MAX + 1);
const char *name = fl_filename_name(temp);
if (name) {
chooser.preset_file(name);
temp[name - temp] = 0;
chooser.directory(temp);
}
}
if (chooser.show() == 0) {
LoadFlie(chooser.filename());
}
}
void AddFileMenu() {
int i = menubar->find_index(MenuQuitCallback);
menubar->insert(i, "Open", FL_COMMAND + 'o', MenuOpenCallback, NULL, FL_MENU_DIVIDER);
menubar->insert(i + 1, "Save", FL_COMMAND + 's', MenuSaveCallback);
menubar->insert(i + 2, "Save As", FL_COMMAND + 'a', MenuSaveAsCallback, NULL, FL_MENU_DIVIDER);
}
int ArgsHandler(int argc, char **argv, int &i) {
if (argv && argv[i] && argv[i][0] != '-') {
LoadFlie(argv[i]);
i++;
return 1;
}
return 0;
}
int HandleCommandLine(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
printf("Usage: %s [filename]\n", argv[0]);
}
}
int i = 0;
Fl::args_to_utf8(argc, argv);
Fl::args(argc, argv, i, ArgsHandler);
fl_open_callback(LoadFlie);
window->show(argc, argv);
return Fl::run();
}
void MenuCutCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_cut(0, (Fl_Text_Editor *)w);
}
}
void MenuCopyCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_copy(0, (Fl_Text_Editor *)w);
}
}
void MenuPasteCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_paste(0, (Fl_Text_Editor *)w);
}
}
void FindNext(const char *needle) {
Fl_Text_Editor *editor = text_editor;
Fl_Widget *w = Fl::focus();
if (w && w == split_editor) {
editor = split_editor;
}
int pos = editor->insert_position();
int found = text_buffer->search_forward(pos, needle, &pos);
if (found) {
text_buffer->select(pos, pos + (int)strlen(needle));
editor->insert_position(pos + (int)strlen(needle));
editor->show_insert_position();
} else {
fl_alert("Find: '%s' not found", needle);
}
}
void MenuFindCallback(Fl_Widget *, void *) {
const char *find_text = fl_input("Find in text: ", last_find_text);
if (find_text && find_text[0]) {
fl_strlcpy(last_find_text, find_text, sizeof(last_find_text));
FindNext(find_text);
}
}
void MenuF1indNextCallback(Fl_Widget *, void *) {
if (last_find_text[0]) {
FindNext(last_find_text);
} else {
MenuFindCallback(NULL, NULL);
}
}
void ReplaceSelection(const char *new_text) {
Fl_Text_Editor *editor = text_editor;
Fl_Widget *w = Fl::focus();
if (w && w == split_editor) {
editor = split_editor;
}
int start, end;
if (text_buffer->selection_position(&start, &end)) {
text_buffer->remove_selection();
text_buffer->insert(start, new_text);
text_buffer->select(start, start + (int)strlen(new_text));
editor->insert_position(start + (int)strlen(new_text));
editor->show_insert_position();
}
}
class ReplaceDialog : public Fl_Double_Window {
Fl_Input *find_text_input, *replace_text_input;
Fl_Button *find_next_button, *replace_and_find_button, *close_button;
public:
ReplaceDialog(const char *label);
void show() FL_OVERRIDE;
private:
static void find_next_callback(Fl_Widget *, void *);
static void replace_and_find_callback(Fl_Widget *, void *);
static void close_callback(Fl_Widget *, void *);
};
ReplaceDialog *replace_dialog = NULL;
ReplaceDialog::ReplaceDialog(const char *label)
: Fl_Double_Window(430, 100, label) {
find_text_input = new Fl_Input(100, 10, 320, 25, "Find: ");
replace_text_input = new Fl_Input(100, 50, 320, 25, "Replace: ");
Fl_Flex *button_field = new Fl_Flex(100, 70, w() - 100, 40);
button_field->type(Fl_Flex::HORIZONTAL);
button_field->margin(0, 5, 10, 10);
button_field->gap(10);
find_next_button = new Fl_Button(0, 0, 0, 0, "Next");
find_next_button->callback(replace_and_find_callback, this);
close_button = new Fl_Button(0, 0, 0, 0, "Close");
close_button->callback(close_callback, this);
button_field->end();
set_non_modal();
}
void ReplaceDialog::show() {
find_text_input->value(last_find_text);
replace_text_input->value(last_replace_text);
Fl_Double_Window::show();
}
void ReplaceDialog::find_next_callback(Fl_Widget *, void *data) {
FindNext(static_cast<ReplaceDialog *>(data)->find_text_input->value());
}
void ReplaceDialog::replace_and_find_callback(Fl_Widget *, void *data) {
ReplaceDialog *dlg = static_cast<ReplaceDialog *>(data);
ReplaceSelection(dlg->replace_text_input->value());
find_next_callback(NULL, data);
}
void ReplaceDialog::close_callback(Fl_Widget *, void *data) {
ReplaceDialog *dlg = static_cast<ReplaceDialog *>(data);
dlg->hide();
}
void MenuReplaceCallback(Fl_Widget *, void *) {
if (!replace_dialog) {
replace_dialog = new ReplaceDialog("Find and Replace");
}
replace_dialog->show();
}
void AddEditMenu() {
menubar->add("Edit/Cut", FL_COMMAND + 'x', MenuCutCallback);
menubar->add("Edit/Copy", FL_COMMAND + 'c', MenuCopyCallback);
menubar->add("Edit/Paste", FL_COMMAND + 'v', MenuPasteCallback, NULL, FL_MENU_DIVIDER);
menubar->add("Edit/Find", FL_COMMAND + 'f', MenuFindCallback);
menubar->add("Edit/Find Next", FL_COMMAND + 'n', MenuF1indNextCallback);
menubar->add("Edit/Replace", FL_COMMAND + 'r', MenuReplaceCallback);
}
// Window menu
void MenuLineNumberCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
text_editor->linenumber_width(40);
if (split_editor)
split_editor->linenumber_width(40);
} else {
text_editor->linenumber_width(0);
if (split_editor)
split_editor->linenumber_width(0);
}
text_editor->redraw();
if (split_editor)
split_editor->redraw();
}
void MenuWordWrapCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
text_editor->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
if (split_editor)
split_editor->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
} else {
text_editor->wrap_mode(Fl_Text_Display::WRAP_NONE, 0);
if (split_editor)
split_editor->wrap_mode(Fl_Text_Display::WRAP_NONE, 0);
}
text_editor->redraw();
if (split_editor)
split_editor->redraw();
}
void BuildSplitEditor() {
window->begin();
app_tile = new Fl_Tile(text_editor->x(), text_editor->y(), text_editor->w(), text_editor->h());
window->remove(text_editor);
app_tile->add(text_editor);
split_editor = new Fl_Text_Editor(app_tile->x(), app_tile->y() + app_tile->h(), app_tile->w(), 0);
split_editor->buffer(text_buffer);
split_editor->textfont(FL_COURIER);
split_editor->hide();
app_tile->end();
app_tile->size_range(0, 25, 25);
app_tile->size_range(1, 25, 25);
window->end();
window->resizable(app_tile);
app_tile->resizable(text_editor);
}
void MenuSplitCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
int h_split = app_tile->h() / 2;
text_editor->size(app_tile->w(), h_split);
split_editor->resize(app_tile->x(), app_tile->y() + h_split, app_tile->w(),
app_tile->h() - h_split);
split_editor->show();
} else {
text_editor->size(app_tile->w(), app_tile->h());
split_editor->resize(app_tile->x(), app_tile->y() + app_tile->h(), app_tile->w(), 0);
split_editor->hide();
}
app_tile->resizable(text_editor);
app_tile->init_sizes();
app_tile->redraw();
}
// Syntax highlighting
#include <ctype.h>
#include <stdlib.h>
Fl_Text_Buffer *app_style_buffer = NULL;
#define TS 14
Fl_Text_Display::Style_Table_Entry styletable[] = {
{FL_BLACK, FL_COURIER, TS}, // A - Plain
{FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS}, // B - Line comments
{FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS}, // C - Block comments
{FL_BLUE, FL_COURIER, TS}, // D - Strings
{FL_DARK_RED, FL_COURIER, TS}, // E - Directives
{FL_DARK_RED, FL_COURIER_BOLD, TS}, // F - Types
{FL_BLUE, FL_COURIER_BOLD, TS}, // G - Keywords
};
const char *code_keywords[] = {
"and", "and_eq", "asm", "bitand", "bitor", "break", "case", "catch", "compl",
"continue", "default", "delete", "do", "else", "false", "for", "goto", "if",
"new", "not", "not_eq", "operator", "or", "or_eq", "return", "switch", "template",
"this", "throw", "true", "try", "while", "xor", "xor_eq"};
const char *code_types[] = {
"auto", "bool", "char", "class", "const", "const_cast",
"double", "dynamic_cast", "enum", "explicit", "extern", "float",
"friend", "inline", "int", "long", "mutable", "namespace",
"private", "protected", "public", "register", "short", "signed",
"sizeof", "static", "static_cast", "struct", "template", "typedef",
"typename", "union", "unsigned", "virtual", "void", "volatile"};
extern "C" {
int compare_keywords(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
}
void style_parse(const char *text, char *style, int length) {
char current;
int col;
int last;
char buf[255], *bufptr;
const char *temp;
// Style letters:
//
// A - Plain
// B - Line comments
// C - Block comments
// D - Strings
// E - Directives
// F - Types
// G - Keywords
for (current = *style, col = 0, last = 0; length > 0; length--, text++) {
if (current == 'B' || current == 'F' || current == 'G')
current = 'A';
if (current == 'A') {
// Check for directives, comments, strings, and keywords...
if (col == 0 && *text == '#') {
// Set style to directive
current = 'E';
} else if (strncmp(text, "//", 2) == 0) {
current = 'B';
for (; length > 0 && *text != '\n'; length--, text++)
*style++ = 'B';
if (length == 0)
break;
} else if (strncmp(text, "/*", 2) == 0) {
current = 'C';
} else if (strncmp(text, "\\\"", 2) == 0) {
// Quoted quote...
*style++ = current;
*style++ = current;
text++;
length--;
col += 2;
continue;
} else if (*text == '\"') {
current = 'D';
} else if (!last && (islower((*text) & 255) || *text == '_')) {
// Might be a keyword...
for (temp = text, bufptr = buf;
(islower((*temp) & 255) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1);
*bufptr++ = *temp++) {
// nothing
}
if (!islower((*temp) & 255) && *temp != '_') {
*bufptr = '\0';
bufptr = buf;
if (bsearch(&bufptr, code_types, sizeof(code_types) / sizeof(code_types[0]),
sizeof(code_types[0]), compare_keywords)) {
while (text < temp) {
*style++ = 'F';
text++;
length--;
col++;
}
text--;
length++;
last = 1;
continue;
} else if (bsearch(&bufptr, code_keywords,
sizeof(code_keywords) / sizeof(code_keywords[0]),
sizeof(code_keywords[0]), compare_keywords)) {
while (text < temp) {
*style++ = 'G';
text++;
length--;
col++;
}
text--;
length++;
last = 1;
continue;
}
}
}
} else if (current == 'C' && strncmp(text, "*/", 2) == 0) {
// Close a C comment...
*style++ = current;
*style++ = current;
text++;
length--;
current = 'A';
col += 2;
continue;
} else if (current == 'D') {
// Continuing in string...
if (strncmp(text, "\\\"", 2) == 0) {
// Quoted end quote...
*style++ = current;
*style++ = current;
text++;
length--;
col += 2;
continue;
} else if (*text == '\"') {
// End quote...
*style++ = current;
col++;
current = 'A';
continue;
}
}
// Copy style info...
if (current == 'A' && (*text == '{' || *text == '}'))
*style++ = 'G';
else
*style++ = current;
col++;
last = isalnum((*text) & 255) || *text == '_' || *text == '.';
if (*text == '\n') {
// Reset column and possibly reset the style
col = 0;
if (current == 'B' || current == 'E')
current = 'A';
}
}
}
void StyleInit() {
char *style = new char[text_buffer->length() + 1];
char *text = text_buffer->text();
memset(style, 'A', text_buffer->length());
style[text_buffer->length()] = '\0';
if (!app_style_buffer)
app_style_buffer = new Fl_Text_Buffer(text_buffer->length() + 1);
style_parse(text, style, text_buffer->length());
app_style_buffer->text(style);
delete[] style;
free(text);
}
void StyleUnfinishedCallback(int reason, void *editor) {}
void StyleUpdate(int pos, int nInserted,
int nDeleted, int nRestyled, const char *dletedText, void *cbArg)
{
int start, end;
char last, *style, *text;
if (nInserted == 0 && nDeleted == 0) {
app_style_buffer->unselect();
return;
}
if (nInserted > 0) {
style = new char[nInserted + 1];
memset(style, 'A', nInserted);
style[nInserted] = '\0';
app_style_buffer->replace(pos, pos + nDeleted, style);
delete[] style;
} else {
app_style_buffer->remove(pos, pos + nDeleted);
}
app_style_buffer->select(pos, pos + nInserted - nDeleted);
start = text_buffer->line_start(pos);
end = text_buffer->line_end(pos + nInserted);
text = text_buffer->text_range(start, end);
style = app_style_buffer->text_range(start, end);
if (start == end)
last = 0;
else
last = style[end - start - 1];
style_parse(text, style, end - start);
app_style_buffer->replace(start, end, style);
((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
if (start == end || last != style[end - start - 1]) {
free(text);
free(style);
end = text_buffer->length();
text = text_buffer->text_range(start, end);
style = app_style_buffer->text_range(start, end);
style_parse(text, style, end - start);
app_style_buffer->replace(start, end, style);
((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
}
free(text);
free(style);
}
void MenuSyntaxHighlightCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
StyleInit();
text_editor->highlight_data(app_style_buffer, styletable,
sizeof(styletable) / sizeof(styletable[0]), 'A',
StyleUnfinishedCallback, 0);
text_buffer->add_modify_callback(StyleUpdate, text_editor);
} else {
text_buffer->remove_modify_callback(StyleUpdate, text_editor);
text_editor->highlight_data(NULL, NULL, 0, 'A', NULL, 0);
}
text_editor->redraw();
if (split_editor) {
if (item->value()) {
split_editor->highlight_data(app_style_buffer, styletable,
sizeof(styletable) / sizeof(styletable[0]), 'A',
StyleUnfinishedCallback, 0);
} else {
split_editor->highlight_data(NULL, NULL, 0, 'A', NULL, 0);
}
split_editor->redraw();
}
}
void AddWindowMenu() {
menubar->add("Window/Line Numbers", FL_COMMAND + 'l', MenuLineNumberCallback, NULL,
FL_MENU_TOGGLE);
menubar->add("Window/Word Wrap", FL_COMMAND + 'w', MenuWordWrapCallback, NULL, FL_MENU_TOGGLE);
menubar->add("Window/Split", FL_COMMAND + 's', MenuSplitCallback, NULL, FL_MENU_TOGGLE);
menubar->add("Window/Syntax Highlighting", FL_COMMAND + 'h', MenuSyntaxHighlightCallback, NULL,
FL_MENU_TOGGLE);
}
int main(int argc, char **argv) {
Build_Window();
Build_Menu();
Build_Editor();
BuildSplitEditor();
AddFileMenu();
AddEditMenu();
AddWindowMenu();
return HandleCommandLine(argc, argv);
}
标签:style,FLTK,text,void,editor,Fl,app,Editor
From: https://www.cnblogs.com/china_x01/p/18537924