Its ok... I'm just taking it as a pet project practice c++11 code...
This is actually very tricky code, so i thought somebody might find it useful...
Actually, I found an even better solution that will delete the data pointers when the object is destroyed as well...
#ifndef FL_CALLBACK_H
#define FL_CALLBACK_H
//=======================================================================================
// INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)
//=======================================================================================
// Requires: C++11
#include <vector>
using std::vector;
// generic callback data
template<typename classname>
struct d${
classname* inst;
void (classname::*func)(Fl_Widget*, void*);
void* data;
Fl_Widget* widget;
};
// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
d$<classname>* cb_data = (d$<classname>*)data;
cb_data->widget = widget;
// given a class instances, call a non-static member function of the class
(cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}
// CALLBACK REGISTERY - Keep track of callback data using RAII
template<typename classname>
class cb_registry {
public:
using datatype = d$<classname>;
cb_registry(classname* inst) : inst{inst} {}
~cb_registry() {
for (datatype* elem : keeper) {
delete elem;
}
}
Fl_Callback* func() {
return (Fl_Callback*) x$<classname>;
}
void* data(
void (classname::*fptr)(Fl_Widget*, void*),
void* data)
{
datatype* dataptr = new datatype {inst, fptr, data, 0};
keeper.push_back(dataptr);
return (void*) dataptr;
}
private:
classname* inst;
vector<datatype*> keeper;
};
// EXAMPLE USAGE:
// class EditorWindow() : Fl_Window{600, 400, "Editor Window) {
// public:
// cb_registry<EditorWindow> cb;
//
// EditorWindow()
// : Fl_Window{600, 400, "Editor Window),
// cb{this}
// {
// create_menu();
// }
// ~EditorWindow() {}
// void create_menu();
//
// void cb_new (Fl_Widget*, void*);
// void cb_open (Fl_Widget*, void*);
//
// Fl_Menu_Item* menuitems;
// Fl_Menu_Bar* menubar;
//};
//
//}
//
//void EditorWindow::create_menu() {
//
// void EditorWindow::create_menu() {
// menuitems = new Fl_Menu_Item[20] {
// { "&File", 0, 0, 0, FL_SUBMENU },
// { "&New File", 0, cb->func(), cb->data(cb_new, nullptr), 0 },
// { "&Open File...", FL_COMMAND + 'o', cb->func(), cb->data(cb_open, nullptr), 0 },
// { 0 },
//
// { 0 }
// };
//
// menubar = new Fl_Menu_Bar (0, 0, 640, 30);
// menubar->copy (menuitems);
//}
#endif
//============================================================
// HERE"S the rewrite of text editor so far using this class
//#ifndef FL_CALLBACK_H
#define FL_CALLBACK_H
//=======================================================================================
// INVOKE MEMBER OF A CLASS FROM STATIC CALLBACK (without using static keyword)
//=======================================================================================
// Requires: C++11
#include <vector>
using std::vector;
// generic callback data
template<typename classname>
struct d${
classname* inst;
void (classname::*func)(Fl_Widget*, void*);
void* data;
Fl_Widget* widget;
};
// generic callback function
template<typename classname>
void x$(Fl_Widget* widget, void* data) {
d$<classname>* cb_data = (d$<classname>*)data;
cb_data->widget = widget;
// given a class instances, call a non-static member function of the class
(cb_data->inst->*(cb_data->func))(cb_data->widget, cb_data->data);
}
// CALLBACK REGISTERY - Keep track of callback data using RAII
template<typename classname>
class cb_registry {
public:
using datatype = d$<classname>;
cb_registry(classname* inst) : inst{inst} {}
~cb_registry() {
for (datatype* elem : keeper) {
delete elem;
}
}
Fl_Callback* func() {
return (Fl_Callback*) x$<classname>;
}
void* data(
void (classname::*fptr)(Fl_Widget*, void*),
void* data)
{
datatype* dataptr = new datatype {inst, fptr, data, 0};
keeper.push_back(dataptr);
return (void*) dataptr;
}
private:
classname* inst;
vector<datatype*> keeper;
};
#endif
============================================================
#include <FL/Fl.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Menu_Bar.H>
#include <Fl/Fl_Window.H>
#include <Fl/Fl_Double_Window.H>
#include <Fl/Fl_Text_Editor.H>
#include <iostream>
#include <string>
using std::cout;
using std::string;
#include "fl_callback.h"
class ReplaceWindow : public Fl_Window {;
public:
Fl_Input* replace_find;
Fl_Input* replace_with;
Fl_Button* replace_all;
Fl_Button* replace_next;
Fl_Button* replace_cancel;
ReplaceWindow() : Fl_Window{300, 105, "Replace"} {
replace_find = new Fl_Input (70, 10, 200, 25, "Find:");
replace_with = new Fl_Input (70, 40, 200, 25, "Replace:");
replace_all = new Fl_Button (10, 70, 90, 25, "Replace All");
replace_next = new Fl_Button (105, 70, 120, 25, "Replace Next");
replace_cancel = new Fl_Button (230, 70, 60, 25, "Cancel");
}
~ReplaceWindow() {
delete replace_find;
delete replace_with;
delete replace_all;
delete replace_next;
delete replace_cancel;
}
};
class EditorWindow : public Fl_Double_Window
{
public:
FL_CALLBACK(EditorWindow);
EditorWindow(int w, int h, const char* t);
~EditorWindow() {}
void create_menu();
cb_registry<EditorWindow> cb;
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;
Fl_Text_Buffer* textbuf;
Fl_Menu_Item* menuitems;
Fl_Menu_Bar* menubar;
char search[256];
int changed = 0;
string filename {""};
string title {""};
int loading {0};
void set_title ();
void cb_new (Fl_Widget* widget, void* data) {cout << "new\n";}
void cb_open (Fl_Widget* widget, void* data) {cout << "open\n";}
void cb_insert (Fl_Widget* widget, void* data) {cout << "insert\n";}
void cb_save (Fl_Widget* widget, void* data) {cout << "save\n";}
void cb_saveas (Fl_Widget* widget, void* data) {cout << "saveas\n";}
void cb_view (Fl_Widget* widget, void* data) {cout << "view\n";}
void cb_close (Fl_Widget* widget, void* data) {cout << "close\n";}
void cb_quit (Fl_Widget* widget, void* data) {cout << "quit\n";}
void cb_undo (Fl_Widget* widget, void* data) {cout << "undo\n";}
void cb_cut (Fl_Widget* widget, void* data);
void cb_copy (Fl_Widget* widget, void* data);
void cb_paste (Fl_Widget* widget, void* data);
void cb_delete (Fl_Widget* widget, void* data);
void cb_find (Fl_Widget* widget, void* data);
void cb_find2 (Fl_Widget* widget, void* data);
void cb_replace (Fl_Widget* widget, void* data);
void cb_replace2 (Fl_Widget* widget, void* data);
void changed_cb(int nInserted, int nDeleted);
static void changed_cb_static(int, int nInserted, int nDeleted, int, const char*, void* v);
};
#include <Fl/Fl_Menu_Item.H>
#include <Fl/Fl_Menu_Bar.H>
#include <Fl/Fl_Input.H>
#include <FL/fl_ask.H> // fl_input
#include "win_first.h"
EditorWindow::EditorWindow(int w, int h, const char* t) :
Fl_Double_Window{w, h, t},
cb{this}
{
create_menu();
textbuf = new Fl_Text_Buffer();
editor = new Fl_Text_Editor (0, 30, w, h-30);
editor->buffer(textbuf);
editor->textfont(FL_COURIER);
textbuf->add_modify_callback(changed_cb_static, (void*)this);
textbuf->call_modify_callbacks ();
set_title();
}
void EditorWindow::create_menu()
{
using d = d$<EditorWindow>; // Data for Callback Function
menuitems = new Fl_Menu_Item[100]
{
{ "&File", 0, 0, 0, FL_SUBMENU },
{ "&New File", 0, cb.func(), cb.data(cb_new, nullptr), 0},
{ "&Open File...", FL_COMMAND + 'o', cb.func(), cb.data(cb_open, nullptr), 0},
{ "&Insert File...", FL_COMMAND + 'i', cb.func(), cb.data(cb_insert, nullptr), FL_MENU_DIVIDER},
{ "&Save File", FL_COMMAND + 's', cb.func(), cb.data(cb_save, nullptr), 0},
{ "Save File &As...", FL_COMMAND + FL_SHIFT + 's', cb.func(), cb.data(cb_saveas, nullptr), FL_MENU_DIVIDER},
{ "New &View", FL_ALT + 'v', cb.func(), cb.data(cb_view, nullptr), 0},
{ "&Close View", FL_COMMAND + 'w', cb.func(), cb.data(cb_close, nullptr), FL_MENU_DIVIDER},
{ "E&xit", FL_COMMAND + 'q', cb.func(), cb.data(cb_quit, nullptr), 0},
{ 0 },
{ "&Edit", 0, 0, 0, FL_SUBMENU },
{ "&Undo", FL_COMMAND + 'z', cb.func(), cb.data(cb_undo, nullptr), FL_MENU_DIVIDER},
{ "Cu&t", FL_COMMAND + 'x', cb.func(), cb.data(cb_cut, nullptr), 0},
{ "&Copy", FL_COMMAND + 'c', cb.func(), cb.data(cb_copy, nullptr), 0},
{ "&Paste", FL_COMMAND + 'v', cb.func(), cb.data(cb_paste, nullptr), 0},
{ "&Delete", 0, cb.func(), cb.data(cb_delete, nullptr), 0},
{ 0 },
{ "&Search", 0, 0, 0, FL_SUBMENU, 0, 0, 0},
{ "&Find...", FL_COMMAND + 'f', cb.func(), cb.data(cb_find, nullptr), 0},
{ "F&ind Again", FL_COMMAND + 'g', cb.func(), cb.data(cb_find2, nullptr), 0},
{ "&Replace...", FL_COMMAND + 'r', cb.func(), cb.data(cb_replace, nullptr), 0},
{ "Re&place Again", FL_COMMAND + 't', cb.func(), cb.data(cb_replace2, nullptr), 0},
{ 0 },
{ 0 }
};
menubar = new Fl_Menu_Bar (0, 0, 640, 30);
menubar->copy (menuitems);
}
void EditorWindow::set_title()
{
if (!filename[0])
title = "Untitled";
else {
title = filename;
const size_t last_slash_idx = title.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
title.erase(0, last_slash_idx + 1);
}
}
if (changed) {
title += " (modified)";
}
label (title.c_str());
}
void EditorWindow::cb_copy(Fl_Widget* widget, void* data)
{
Fl_Text_Editor::kf_copy (0, editor);
}
void EditorWindow::cb_cut(Fl_Widget*, void* data)
{
Fl_Text_Editor::kf_cut (0, editor);
}
void EditorWindow::cb_delete(Fl_Widget*, void* data)
{
textbuf->remove_selection ();
}
void EditorWindow::cb_find(Fl_Widget* w, void* v)
{
const char *val;
val = fl_input ("Search String:", search);
if (val != NULL)
{
// User entered a string - go find it!
strcpy(search, val);
cb_find2(w, v);
}
}
void EditorWindow::cb_find2(Fl_Widget* w, void* v)
{
if (search[0] == '\0')
{
// Search string is blank; get a new one...
cb_find(w, v);
return;
}
int pos = editor->insert_position();
int found = textbuf->search_forward (pos, search, &pos);
if (found)
{
// Found a match; select and update the position...
textbuf->select (pos, pos+strlen(search));
editor->insert_position(pos+strlen(search));
editor->show_insert_position();
}
else fl_alert ("No occurrences of \'%s\' found!", search);
}
void EditorWindow::cb_paste(Fl_Widget* w, void* v) {
Fl_Text_Editor::kf_paste (0, editor);
}
void EditorWindow::changed_cb_static(int, int nInserted, int nDeleted, int, const char*, void* v) {
EditorWindow *w = (EditorWindow *)v;
w->changed_cb(nInserted, nDeleted);
}
void EditorWindow::changed_cb(int nInserted, int nDeleted)
{
if ((nInserted || nDeleted) && !loading)
changed = 1;
set_title();
if (loading)
editor->show_insert_position();
}
void EditorWindow::cb_replace (Fl_Widget* widget, void* data) {
cout << "replace\n";
ReplaceWindow* w = new ReplaceWindow();
w->show();
while (w->shown()) Fl::wait();
cout << "replace done\n";
}
void EditorWindow::cb_replace2 (Fl_Widget* widget, void* data) {
cout << "replace2\n";
}