diff --git a/CMakeLists.txt b/CMakeLists.txt index a0b6aaf..f7fd238 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ pkg_check_modules(CAIROMM cairomm-1.0>=1.0) SET(SOURCES src/config.cpp src/config.h + src/colour.cpp + src/colour.h src/gui.cpp src/gui.h src/inifile.cpp diff --git a/src/colour.cpp b/src/colour.cpp new file mode 100644 index 0000000..b6d794c --- /dev/null +++ b/src/colour.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "colour.h" + +Colour::Colour() +{ +} + +Colour::Colour(std::string Code) +{ + if (Code.length() == 7) { + char p[3]; + p[0] = Code[1]; + p[1] = Code[2]; + p[2] = '\0'; + r = (float)strtol(p, NULL, 16) / 0xFF; + p[0] = Code[3]; + p[1] = Code[4]; + g = (float)strtol(p, NULL, 16) / 0xFF; + p[0] = Code[5]; + p[1] = Code[6]; + b = (float)strtol(p, NULL, 16) / 0xFF; + } +} + +std::string Colour::ToCode() const +{ + char code[8]; + snprintf(code, sizeof code, "#%02X%02X%02X", (int)(r * 0xFF), (int)(g * 0xFF), (int)(b * 0xFF)); + return std::string(code); +} diff --git a/src/colour.h b/src/colour.h new file mode 100644 index 0000000..a3d34c5 --- /dev/null +++ b/src/colour.h @@ -0,0 +1,15 @@ +#ifndef COLOUR_H +#define COLOUR_H + +struct Colour +{ + float r; + float g; + float b; + + Colour(); + Colour(std::string Code); + std::string ToCode() const; +}; + +#endif // COLOUR_H diff --git a/src/config.cpp b/src/config.cpp index 0864b70..d911afa 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -16,14 +16,26 @@ void Config::Read() ini.LoadFromDisk(m_filename); - IniGroup *group = ini.GetGroup("gui"); - CellPadding = group->GetValue("cellpadding", 3); - FontSize = group->GetValue("fontsize", 10); - FontFace = group->GetValue("fontface", std::string("Sans")); + IniGroup *group; + group = ini.GetGroup("gui"); + CellPadding = group->GetValue("cellpadding", 3); + FontSize = group->GetValue("fontsize", 10); + FontFace = group->GetValue("fontface", "Sans"); ExpandClients = group->GetValue("expandclients", true); ExpandGroups = group->GetValue("expandgroups", false); + group = ini.GetGroup("colours"); + Background = Colour(group->GetValue("background", "#404040")); + GridLinesClient = Colour(group->GetValue("gridlinesclient", "#000000")); + GridLinesGroup = Colour(group->GetValue("gridlinesgroup", "#1A1A1A")); + GridLinesPort = Colour(group->GetValue("gridlinesport", "#333333")); + Text = Colour(group->GetValue("text", "#FFFFFF")); + TabClient = Colour(group->GetValue("tabclient", "#1A1B1B")); + TabGroup = Colour(group->GetValue("tabgroup", "#295630")); + TabPortAudio = Colour(group->GetValue("tabportaudio", "#294056")); + TabPortMidi = Colour(group->GetValue("tabportmidi", "#4F3030")); + pm.AliasClear(); group = ini.GetGroup("aliases"); @@ -36,7 +48,9 @@ void Config::Read() void Config::Write() { - IniGroup *group = ini.GetGroup("gui"); + IniGroup *group; + + group = ini.GetGroup("gui"); group->SetValue("cellpadding", CellPadding); group->SetValue("fontsize", FontSize); group->SetValue("fontface", FontFace); @@ -44,6 +58,17 @@ void Config::Write() group->SetValue("expandclients", ExpandClients); group->SetValue("expandgroups", ExpandGroups); + group = ini.GetGroup("colours"); + group->SetValue("background", Background.ToCode()); + group->SetValue("gridlinesclient", GridLinesClient.ToCode()); + group->SetValue("gridlinesgroup", GridLinesGroup.ToCode()); + group->SetValue("gridlinesport", GridLinesPort.ToCode()); + group->SetValue("text", Text.ToCode()); + group->SetValue("tabclient", TabClient.ToCode()); + group->SetValue("tabgroup", TabGroup.ToCode()); + group->SetValue("tabportaudio", TabPortAudio.ToCode()); + group->SetValue("tabportmidi", TabPortMidi.ToCode()); + ini.RemoveGroup("aliases"); group = ini.GetGroup("aliases"); diff --git a/src/config.h b/src/config.h index 5d6891d..a10487c 100644 --- a/src/config.h +++ b/src/config.h @@ -1,4 +1,5 @@ #include "inifile.h" +#include "colour.h" class Config { @@ -16,6 +17,16 @@ public: bool ExpandClients; bool ExpandGroups; + + Colour Background; + Colour GridLinesClient; + Colour GridLinesGroup; + Colour GridLinesPort; + Colour Text; + Colour TabClient; + Colour TabGroup; + Colour TabPortAudio; + Colour TabPortMidi; }; extern Config cfg; diff --git a/src/gui.cpp b/src/gui.cpp index e83f8dc..fc6591e 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -10,12 +10,17 @@ Glib::RefPtr g_builder; Gui::Gui(BaseObjectType *cobject, const Glib::RefPtr &builder) : Gtk::Window(cobject), - m_scrolledwindow(NULL) + m_scrolledwindow_audio(NULL), + m_scrolledwindow_midi(NULL) { - builder->get_widget("scrolledwindow1", m_scrolledwindow); + builder->get_widget("scrolledwindow_audio", m_scrolledwindow_audio); + builder->get_widget("scrolledwindow_midi", m_scrolledwindow_midi); - if (m_scrolledwindow != NULL) { - m_scrolledwindow->add(m_matrix); + if (m_scrolledwindow_audio != NULL) { + m_scrolledwindow_audio->add(m_matrix); + } + if (m_scrolledwindow_midi != NULL) { + m_scrolledwindow_midi->add(m_matrix); } m_matrix.parent = this; @@ -23,6 +28,9 @@ Gui::Gui(BaseObjectType *cobject, const Glib::RefPtr &builder) Refresh(); Glib::signal_idle().connect(sigc::mem_fun(*this, &Gui::on_idle)); + + Preferences *p = Preferences::Open(); + p->show(); } Gui::~Gui() @@ -57,3 +65,14 @@ bool Gui::on_idle() return true; } +Preferences::Preferences(BaseObjectType *cobject, const Glib::RefPtr &builder) + : Gtk::Dialog(cobject) +{ +} + +Preferences *Preferences::Open() +{ + Preferences *window = NULL; + g_builder->get_widget_derived("dialog_preferences", window); + return window; +} diff --git a/src/gui.h b/src/gui.h index e68c924..f672c3e 100644 --- a/src/gui.h +++ b/src/gui.h @@ -9,7 +9,8 @@ extern Glib::RefPtr g_builder; class Gui : public Gtk::Window { private: - Gtk::ScrolledWindow *m_scrolledwindow; + Gtk::ScrolledWindow *m_scrolledwindow_audio; + Gtk::ScrolledWindow *m_scrolledwindow_midi; Matrix m_matrix; public: @@ -24,4 +25,11 @@ protected: bool on_idle(); }; +class Preferences : public Gtk::Dialog +{ +public: + Preferences(BaseObjectType *cobject, const Glib::RefPtr &builder); + static Preferences *Open(); +}; + #endif // GUI_H diff --git a/src/inifile.h b/src/inifile.h index aa5907a..8a1aaec 100644 --- a/src/inifile.h +++ b/src/inifile.h @@ -58,6 +58,12 @@ struct IniGroup item->value = value; } + std::string GetValue(std::string name, const char *default_value) + { + IniItem *item = GetItem(name, default_value); + return item->value; + } + template T GetValue(std::string name, T default_value) { diff --git a/src/jsweeper.glade b/src/jsweeper.glade index fecf362..c13be3e 100644 --- a/src/jsweeper.glade +++ b/src/jsweeper.glade @@ -1,6 +1,6 @@ - + JACK Sweeper @@ -157,7 +157,7 @@ True True - + True True GTK_POLICY_AUTOMATIC @@ -182,7 +182,19 @@ - + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + + + + 1 + diff --git a/src/matrix.cpp b/src/matrix.cpp index ff54c87..4d7663b 100644 --- a/src/matrix.cpp +++ b/src/matrix.cpp @@ -9,8 +9,7 @@ const Rect Rect::None = Rect(0, 0, -1, -1); Matrix::Matrix() : - m_client_width(0), m_client_height(0), m_port_width(0), m_port_height(0), m_separation(0), m_padding(cfg.CellPadding), - m_font_size(cfg.FontSize), m_typeface(cfg.FontFace) + m_client_width(0), m_client_height(0), m_port_width(0), m_port_height(0), m_separation(0) { set_size_request(0, 0); show(); @@ -40,8 +39,8 @@ void Matrix::Refresh() int rows = 0; Cairo::RefPtr cr = window->create_cairo_context(); - cr->select_font_face(m_typeface, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); - cr->set_font_size(m_font_size); + cr->select_font_face(cfg.FontFace, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); + cr->set_font_size(cfg.FontSize); PortList::iterator plit; for (plit = pm.m_ports.begin(); plit != pm.m_ports.end(); ++plit) { @@ -55,7 +54,7 @@ void Matrix::Refresh() cr->get_text_extents(c->m_name, c->extents); - int width = c->extents.width + m_padding * 2; + int width = c->extents.width + cfg.CellPadding * 2; if (c->m_is_input) { if (width > m_client_height) m_client_height = width; } else { @@ -87,7 +86,7 @@ void Matrix::Refresh() if (pg->extents.height > m_separation) m_separation = pg->extents.height; if (!pg->m_expanded && pg->m_name != "") { - int width = pg->extents.width + m_padding * 2; + int width = pg->extents.width + cfg.CellPadding * 2; if (c->m_is_input) { if (width > m_port_height) m_port_height = width; cols++; @@ -104,7 +103,7 @@ void Matrix::Refresh() cr->get_text_extents(p->m_name, p->extents); - int width = pg->extents.width + p->extents.width + m_padding * (pg->extents.width > 0 ? 4 : 2); + int width = pg->extents.width + p->extents.width + cfg.CellPadding * (pg->extents.width > 0 ? 4 : 2); if (p->m_flags & JackPortIsInput) { if (width > m_port_height) m_port_height = width; cols++; @@ -133,13 +132,13 @@ void Matrix::Refresh() if (p->extents.height > m_separation) m_separation = p->extents.height; } */ - m_separation += m_padding * 2; + m_separation += cfg.CellPadding * 2; // if (m_port_width > m_separation * 5) m_port_width = m_separation * 5; // if (m_port_height > m_separation * 5) m_port_height = m_separation * 5; - int y = m_client_height + m_port_height + m_padding; - int x = m_client_width + m_port_width + m_padding; + int y = m_client_height + m_port_height + cfg.CellPadding; + int x = m_client_width + m_port_width + cfg.CellPadding; across.clear(); down.clear(); @@ -152,7 +151,7 @@ void Matrix::Refresh() if (c->m_is_input) { r.left = x; - r.top = m_padding; + r.top = cfg.CellPadding; r.width = -1; r.height = m_client_height; c->rect = r; @@ -171,9 +170,9 @@ void Matrix::Refresh() if (pg->m_name != "") { r.left = x; - r.top = m_padding + m_client_height; + r.top = cfg.CellPadding + m_client_height; r.width = pg->m_expanded ? m_separation * pg->m_ports.size() : m_separation; - r.height = pg->m_expanded ? pg->extents.width + m_padding * 2 : m_port_height; + r.height = pg->m_expanded ? pg->extents.width + cfg.CellPadding * 2 : m_port_height; pg->rect = r; if (!pg->m_expanded) { @@ -189,7 +188,7 @@ void Matrix::Refresh() } else { pg->rect = Rect::None; - r.top = m_padding + m_client_height; + r.top = cfg.CellPadding + m_client_height; r.width = m_separation; r.height = m_port_height; } @@ -211,7 +210,7 @@ void Matrix::Refresh() } } } else { - r.left = m_padding; + r.left = cfg.CellPadding; r.top = y; r.width = m_client_width; r.height = -1; @@ -230,9 +229,9 @@ void Matrix::Refresh() PortGroup *pg = *pglit; if (pg->m_name != "") { - r.left = m_padding + m_client_width; + r.left = cfg.CellPadding + m_client_width; r.top = y; - r.width = pg->m_expanded ? pg->extents.width + m_padding * 2 : m_port_width; + r.width = pg->m_expanded ? pg->extents.width + cfg.CellPadding * 2 : m_port_width; r.height = pg->m_expanded ? m_separation * pg->m_ports.size() : m_separation; pg->rect = r; @@ -249,7 +248,7 @@ void Matrix::Refresh() } else { pg->rect = Rect::None; - r.left = m_padding + m_client_width; + r.left = cfg.CellPadding + m_client_width; r.width = m_port_width; r.height = m_separation; } @@ -273,8 +272,8 @@ void Matrix::Refresh() } } - int w = m_padding * 2 + m_client_width + m_port_width; - int h = m_padding * 2 + m_client_height + m_port_height; + int w = cfg.CellPadding * 2 + m_client_width + m_port_width; + int h = cfg.CellPadding * 2 + m_client_height + m_port_height; w += cols * m_separation; h += rows * m_separation; @@ -286,9 +285,14 @@ void Matrix::Refresh() // parent->set_size_request(w + 20, h + 20); } -void Matrix::Box(Cairo::RefPtr cr, float r, float g, float b, Rect &rect) +static void SetColour(Cairo::RefPtr cr, Colour &c) { - cr->set_source_rgb(r, g, b); + cr->set_source_rgb(c.r, c.g, c.b); +} + +void Matrix::Box(Cairo::RefPtr cr, Colour &c, Rect &rect) +{ + SetColour(cr, c); cr->rectangle(rect.left, rect.top, rect.width, rect.height); cr->fill(); @@ -296,7 +300,7 @@ void Matrix::Box(Cairo::RefPtr cr, float r, float g, float b, Re cr->rectangle(rect.left, rect.top, rect.width, rect.height); cr->stroke(); - cr->set_source_rgb(1, 1, 1); + SetColour(cr, cfg.Text); } bool Matrix::on_expose_event(GdkEventExpose *event) @@ -319,13 +323,13 @@ bool Matrix::on_expose_event(GdkEventExpose *event) cr->clip(); // Clear background - cr->set_source_rgb(0.25, 0.25, 0.25); + SetColour(cr, cfg.Background); cr->rectangle(0, 0, alloc_width, alloc_height); cr->fill(); cr->set_source_rgb(1, 1, 1); - cr->select_font_face(m_typeface, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); - cr->set_font_size(m_font_size); + cr->select_font_face(cfg.FontFace, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); + cr->set_font_size(cfg.FontSize); Rect tr; Rect &r = tr; @@ -336,18 +340,18 @@ bool Matrix::on_expose_event(GdkEventExpose *event) r = c->rect; if (c->m_is_input) { - Box(cr, BACK_COLOUR, r); + Box(cr, cfg.TabClient, r); cr->save(); - cr->move_to(r.left + m_padding - c->extents.y_bearing, r.top + r.height - m_padding - c->extents.x_bearing); + cr->move_to(r.left + cfg.CellPadding - c->extents.y_bearing, r.top + r.height - cfg.CellPadding - c->extents.x_bearing); cr->rotate(2 * M_PI * 0.75); cr->show_text(c->m_name); cr->restore(); if (!c->m_expanded) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(r.left + r.width, m_height - m_padding); + cr->line_to(r.left + r.width, m_height - cfg.CellPadding); cr->stroke(); continue; } @@ -361,22 +365,22 @@ bool Matrix::on_expose_event(GdkEventExpose *event) r = pg->rect; if (pg->m_name != "") { - Box(cr, GROUP_COLOUR, r); + Box(cr, cfg.TabGroup, r); cr->save(); - cr->move_to(r.left + m_padding - pg->extents.y_bearing, r.top + r.height - m_padding - pg->extents.x_bearing); + cr->move_to(r.left + cfg.CellPadding - pg->extents.y_bearing, r.top + r.height - cfg.CellPadding - pg->extents.x_bearing); cr->rotate(2 * M_PI * 0.75); cr->show_text(pg->m_name); cr->restore(); if (!pg->m_expanded) { if (pglit == pglast) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); } else { - cr->set_source_rgb(GRID_GROUP); + SetColour(cr, cfg.GridLinesGroup); } cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(r.left + r.width, m_height - m_padding); + cr->line_to(r.left + r.width, m_height - cfg.CellPadding); cr->stroke(); continue; } @@ -390,40 +394,36 @@ bool Matrix::on_expose_event(GdkEventExpose *event) Port *p = *plit; r = p->rect; - if (p->m_is_midi) { - Box(cr, MIDI_COLOUR, r); - } else { - Box(cr, AUDIO_COLOUR, r); - } + Box(cr, p->m_is_midi ? cfg.TabPortMidi : cfg.TabPortAudio, r); cr->save(); - cr->move_to(r.left + m_padding - p->extents.y_bearing, r.top + r.height - m_padding - p->extents.x_bearing); + cr->move_to(r.left + cfg.CellPadding - p->extents.y_bearing, r.top + r.height - cfg.CellPadding - p->extents.x_bearing); cr->rotate(2 * M_PI * 0.75); cr->show_text(p->m_name); cr->restore(); if (pglit == pglast && plit == plast) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); } else if (plit == plast) { - cr->set_source_rgb(GRID_GROUP); + SetColour(cr, cfg.GridLinesGroup); } else { - cr->set_source_rgb(GRID_PORT); + SetColour(cr, cfg.GridLinesPort); } cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(r.left + r.width, m_height - m_padding); + cr->line_to(r.left + r.width, m_height - cfg.CellPadding); cr->stroke(); } } } else { - Box(cr, BACK_COLOUR, r); + Box(cr, cfg.TabClient, r); - cr->move_to(r.left + m_padding - c->extents.x_bearing, r.top + m_padding - c->extents.y_bearing); + cr->move_to(r.left + cfg.CellPadding - c->extents.x_bearing, r.top + cfg.CellPadding - c->extents.y_bearing); cr->show_text(c->m_name); if (!c->m_expanded) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(m_width - m_padding, r.top + r.height); + cr->line_to(m_width - cfg.CellPadding, r.top + r.height); cr->stroke(); continue; } @@ -437,18 +437,18 @@ bool Matrix::on_expose_event(GdkEventExpose *event) r = pg->rect; if (pg->m_name != "") { - Box(cr, GROUP_COLOUR, r); - cr->move_to(r.left + m_padding - pg->extents.x_bearing, r.top + m_padding - pg->extents.y_bearing); + Box(cr, cfg.TabGroup, r); + cr->move_to(r.left + cfg.CellPadding - pg->extents.x_bearing, r.top + cfg.CellPadding - pg->extents.y_bearing); cr->show_text(pg->m_name); if (!pg->m_expanded) { if (pglit == pglast) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); } else { - cr->set_source_rgb(GRID_GROUP); + SetColour(cr, cfg.GridLinesGroup); } cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(m_width - m_padding, r.top + r.height); + cr->line_to(m_width - cfg.CellPadding, r.top + r.height); cr->stroke(); continue; } @@ -462,37 +462,33 @@ bool Matrix::on_expose_event(GdkEventExpose *event) Port *p = *plit; r = p->rect; - if (p->m_is_midi) { - Box(cr, MIDI_COLOUR, r); - } else { - Box(cr, AUDIO_COLOUR, r); - } + Box(cr, p->m_is_midi ? cfg.TabPortMidi : cfg.TabPortAudio, r); - cr->move_to(r.left + m_padding - p->extents.x_bearing, r.top + m_padding - p->extents.y_bearing); + cr->move_to(r.left + cfg.CellPadding - p->extents.x_bearing, r.top + cfg.CellPadding - p->extents.y_bearing); cr->show_text(p->m_name); if (pglit == pglast && plit == plast) { - cr->set_source_rgb(GRID_CLIENT); + SetColour(cr, cfg.GridLinesClient); } else if (plit == plast) { - cr->set_source_rgb(GRID_GROUP); + SetColour(cr, cfg.GridLinesGroup); } else { - cr->set_source_rgb(GRID_PORT); + SetColour(cr, cfg.GridLinesPort); } cr->move_to(r.left + r.width, r.top + r.height); - cr->line_to(m_width - m_padding, r.top + r.height); + cr->line_to(m_width - cfg.CellPadding, r.top + r.height); cr->stroke(); } } } } - int x = m_client_width + m_port_width + m_padding; + int x = m_client_width + m_port_width + cfg.CellPadding; BaseList::iterator bit1; for (bit1 = across.begin(); bit1 != across.end(); ++bit1) { BaseList::iterator bit2; Base *b1 = *bit1; - int y = m_client_height + m_port_height + m_padding; + int y = m_client_height + m_port_height + cfg.CellPadding; for (bit2 = down.begin(); bit2 != down.end(); ++bit2) { Base *b2 = *bit2; @@ -502,7 +498,7 @@ bool Matrix::on_expose_event(GdkEventExpose *event) // if (p1->m_is_midi) { // cr->set_source_rgb(MIDI_COLOUR); // } else { - cr->set_source_rgb(AUDIO_COLOUR); + SetColour(cr, cfg.TabPortAudio); // } cr->rectangle(x + 1, y + 1, m_separation - 1, m_separation - 1); cr->fill(); @@ -523,7 +519,7 @@ bool Matrix::on_expose_event(GdkEventExpose *event) Port *p2 = *plit2; if (p2->pos == 0) continue; // cr->imove_to(p2->pos, p1->pos); -// cr->arc(p1->pos, p2->pos, (m_separation - m_padding * 2) / 2.0, 0, 2 * M_PI); +// cr->arc(p1->pos, p2->pos, (m_separation - cfg.CellPadding * 2) / 2.0, 0, 2 * M_PI); if (p1->m_is_midi) { cr->set_source_rgb(MIDI_COLOUR); } else { diff --git a/src/matrix.h b/src/matrix.h index ebb69e8..e635979 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -4,15 +4,7 @@ #include #include #include "rect.h" - -#define BACK_COLOUR 0.1020, 0.1059, 0.1059 -#define MIDI_COLOUR 0.3098, 0.1882, 0.1882 -#define AUDIO_COLOUR 0.1608, 0.2510, 0.3373 -#define GROUP_COLOUR 0.1608, 0.3373, 0.1647 - -#define GRID_CLIENT 0.00, 0.00, 0.00 -#define GRID_GROUP 0.10, 0.10, 0.10 -#define GRID_PORT 0.20, 0.20, 0.20 +#include "colour.h" typedef std::list BaseList; @@ -24,13 +16,9 @@ private: int m_port_width; int m_port_height; int m_separation; - int m_padding; int m_width; int m_height; - int m_font_size; - std::string m_typeface; - BaseList across; BaseList down; @@ -43,7 +31,7 @@ public: Gtk::Window *parent; protected: - void Box(Cairo::RefPtr cr, float r, float g, float b, Rect &rect); + void Box(Cairo::RefPtr cr, Colour &c, Rect &rect); virtual bool on_expose_event(GdkEventExpose *event); virtual bool on_button_press_event(GdkEventButton *event); };