summaryrefslogtreecommitdiff
path: root/src/gui_gtk.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-07-30 00:36:18 +0200
committerJoel Klinghed <the_jk@yahoo.com>2017-07-30 00:36:18 +0200
commit124429985be8033a398b891f400f70a058330c87 (patch)
tree6f2e8ba3bd4044a1c8252d17e837fe83c44125d8 /src/gui_gtk.cc
parente7a0bf680f3d805b190b89bfeb1f1f8a4c3ec982 (diff)
Add menu shortcuts
Diffstat (limited to 'src/gui_gtk.cc')
-rw-r--r--src/gui_gtk.cc190
1 files changed, 168 insertions, 22 deletions
diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc
index 7dd2d42..2056034 100644
--- a/src/gui_gtk.cc
+++ b/src/gui_gtk.cc
@@ -822,10 +822,11 @@ public:
return G_MENU_MODEL(menu_.get());
}
- void set_map(GActionMap* map);
+ void set_app(GtkApplication* app);
void activate(GSimpleAction* action);
- void add_item(std::string const& id, std::string const& label) override;
+ void add_item(std::string const& id, std::string const& label,
+ Shortcut const& shortcut) override;
GuiMenu* add_menu(std::string const& label) override;
void add_separator() override;
bool enable_item(std::string const& id, bool enable) override;
@@ -833,7 +834,7 @@ public:
private:
explicit GtkGuiMenu(GtkGuiMenu* parent)
: parent_(parent), menu_(g_menu_new()), section_(g_menu_new()),
- map_(nullptr) {
+ app_(nullptr) {
g_menu_append_section(menu_.get(), nullptr, G_MENU_MODEL(section_.get()));
}
@@ -847,14 +848,16 @@ private:
std::string add_action(std::string const& id);
std::string escape(std::string const& id);
std::string unescape(std::string const& action);
+ void add_shortcut(std::string const& action, Shortcut const& shortcut);
GtkGuiMenu* const parent_;
shared_gobject<GMenu> menu_;
shared_gobject<GMenu> section_;
std::unordered_map<std::string, shared_gobject<GSimpleAction>> action_;
- GActionMap* map_;
+ GtkApplication* app_;
std::vector<std::unique_ptr<GtkGuiMenu>> submenus_;
Observers<Listener*> observers_;
+ std::unordered_map<std::string, std::string> shortcut_;
};
class GtkGuiStatusBar : public GuiStatusBar {
@@ -1852,13 +1855,17 @@ MainAppWindow* main_app_window_new(MainApp *app) {
return ret;
}
-void main_app_activate(GApplication* g_app) {
- auto app = MAIN_APP(g_app);
+void setup_menubar(MainApp* app) {
auto menu = static_cast<GtkGuiMenu*>(app->main_->menu());
if (menu) {
- menu->set_map(G_ACTION_MAP(app));
+ menu->set_app(GTK_APPLICATION(app));
gtk_application_set_menubar(GTK_APPLICATION(app), menu->model());
}
+}
+
+void main_app_activate(GApplication* g_app) {
+ auto app = MAIN_APP(g_app);
+ setup_menubar(app);
auto win = main_app_window_new(app);
gtk_window_set_title(GTK_WINDOW(win), app->main_->title().c_str());
gtk_window_present(GTK_WINDOW(win));
@@ -1876,11 +1883,7 @@ bool main_app_window_open(MainApp* app, MainAppWindow*, GFile* file) {
void main_app_open(GApplication* app, GFile** files, gint n_files,
gchar const*) {
- auto menu = static_cast<GtkGuiMenu*>(MAIN_APP(app)->main_->menu());
- if (menu) {
- menu->set_map(G_ACTION_MAP(app));
- gtk_application_set_menubar(GTK_APPLICATION(app), menu->model());
- }
+ setup_menubar(MAIN_APP(app));
auto windows = gtk_application_get_windows(GTK_APPLICATION(app));
auto win = windows ? MAIN_APP_WINDOW(windows->data)
: main_app_window_new(MAIN_APP(app));
@@ -2010,8 +2013,12 @@ double GtkGuiMain::split() const {
return split_;
}
-void GtkGuiMenu::add_item(std::string const& id, std::string const& label) {
+void GtkGuiMenu::add_item(std::string const& id, std::string const& label,
+ Shortcut const& shortcut) {
auto action = add_action(id);
+ if (shortcut.key) {
+ add_shortcut(action, shortcut);
+ }
g_menu_append(section_.get(), label.c_str(), action.c_str());
}
@@ -2027,23 +2034,148 @@ void GtkGuiMenu::add_separator() {
g_menu_append_section(menu_.get(), nullptr, G_MENU_MODEL(section_.get()));
}
-void GtkGuiMenu::set_map(GActionMap* map) {
+void GtkGuiMenu::set_app(GtkApplication* app) {
assert(!parent_);
- assert(!map_);
- assert(map);
- map_ = map;
+ assert(!app_);
+ assert(app);
+ app_ = app;
for (auto const& pair : action_) {
- g_action_map_add_action(map_, G_ACTION(pair.second.get()));
+ g_action_map_add_action(G_ACTION_MAP(app_), G_ACTION(pair.second.get()));
+ }
+ for (auto const& pair : shortcut_) {
+ gchar const* accels[2];
+ accels[0] = pair.second.c_str();
+ accels[1] = nullptr;
+ gtk_application_set_accels_for_action(app_, pair.first.c_str(), accels);
}
}
+void GtkGuiMenu::add_shortcut(std::string const& action,
+ Shortcut const& shortcut) {
+ if (parent_) return parent_->add_shortcut(action, shortcut);
+ assert(shortcut.key);
+ std::string accel;
+ if (shortcut.mask & CTRL) accel.append("<Ctrl>");
+ if (shortcut.mask & ALT) accel.append("<Alt>");
+ if (shortcut.mask & SHIFT) accel.append("<Shift>");
+ if (shortcut.mask & META) accel.append("<Meta>");
+ guint key;
+ if (shortcut.function) {
+ if (shortcut.key >= 1 && shortcut.key <= 35) {
+ key = GDK_KEY_F1 + (shortcut.key - 1);
+ } else {
+ assert(false);
+ return;
+ }
+ } else {
+ if (shortcut.key == ESC) {
+ key = GDK_KEY_Escape;
+ } else if (shortcut.key == RETURN) {
+ key = GDK_KEY_Return;
+ } else if (shortcut.key == UP) {
+ key = GDK_KEY_Up;
+ } else if (shortcut.key == LEFT) {
+ key = GDK_KEY_Left;
+ } else if (shortcut.key == RIGHT) {
+ key = GDK_KEY_Right;
+ } else if (shortcut.key == DOWN) {
+ key = GDK_KEY_Down;
+ } else if (shortcut.key == '\t') {
+ key = GDK_KEY_Tab;
+ } else if (shortcut.key == 0x08) {
+ key = GDK_KEY_BackSpace;
+ } else if (shortcut.key >= 0x20 && shortcut.key < 0x7f) {
+ key = GDK_KEY_space + (shortcut.key - ' ');
+ } else {
+ assert(false);
+ return;
+ }
+ }
+ auto name = gdk_keyval_name(key);
+ if (!name) {
+ assert(false);
+ return;
+ }
+ accel.append(name);
+ shortcut_.emplace(action, accel);
+ if (app_) {
+ gchar const* accels[2];
+ accels[0] = accel.c_str();
+ accels[1] = nullptr;
+ gtk_application_set_accels_for_action(app_, action.c_str(), accels);
+ }
+}
+
+static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f' };
+
+static int8_t unhex(char c) {
+ if (c >= '0' && c <= '9') return (c - '0');
+ if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
+ return -1;
+}
+
std::string GtkGuiMenu::escape(std::string const& action) {
- // TODO
- return action;
+ size_t last = 0;
+ std::string ret;
+ for (size_t i = 0; i < action.size(); ++i) {
+ auto c = action[i];
+ if ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || c == '-') {
+ continue;
+ }
+ ret.append(action, last, i - last);
+ if (c == '.') {
+ ret.append("..");
+ } else {
+ char tmp[3];
+ tmp[0] = '.';
+ tmp[1] = hex[c >> 4];
+ tmp[2] = hex[c & 0xf];
+ ret.append(tmp, 3);
+ }
+ last = i + 1;
+ }
+ if (last == 0) return action;
+ ret.append(action, last, action.size() - last);
+ return ret;
}
std::string GtkGuiMenu::unescape(std::string const& action) {
- return action;
+ size_t last = 0;
+ std::string ret;
+ for (size_t i = 0; i < action.size(); ++i) {
+ if (action[i] != '.') continue;
+ ++i;
+ if (i == action.size()) {
+ assert(false);
+ return "";
+ }
+ ret.append(action, last, i - last);
+ if (action[i] == '.') {
+ last = i + 1;
+ ret.push_back('.');
+ continue;
+ }
+ auto a = unhex(action[i]);
+ ++i;
+ if (a < 0 || i == action.size()) {
+ assert(false);
+ return "";
+ }
+ auto b = unhex(action[i]);
+ if (b < 0) {
+ assert(false);
+ return "";
+ }
+ ret.push_back((a << 4) | b);
+ last = i + 1;
+ }
+ if (last == 0) return action;
+ ret.append(action, last, action.size() - last);
+ return ret;
}
void menu_item_activate(GSimpleAction* action, GVariant*, gpointer data) {
@@ -2061,7 +2193,7 @@ std::string GtkGuiMenu::add_action(std::string const& id) {
auto action = g_simple_action_new(name.c_str(), nullptr);
action_[id].reset(action);
g_signal_connect(action, "activate", G_CALLBACK(menu_item_activate), this);
- if (map_) g_action_map_add_action(map_, G_ACTION(action));
+ if (app_) g_action_map_add_action(G_ACTION_MAP(app_), G_ACTION(action));
return "app." + name;
}
@@ -2235,6 +2367,20 @@ GuiMenu* GuiMenu::create() {
}
// static
+const uint32_t GuiMenu::CTRL = 1 << 0;
+const uint32_t GuiMenu::ALT = 1 << 1;
+const uint32_t GuiMenu::SHIFT = 1 << 2;
+const uint32_t GuiMenu::META = 1 << 3;
+
+// static
+const char GuiMenu::ESC = 0x1b;
+const char GuiMenu::UP = 1;
+const char GuiMenu::LEFT = 2;
+const char GuiMenu::RIGHT = 3;
+const char GuiMenu::DOWN = 4;
+const char GuiMenu::RETURN = '\n';
+
+// static
GuiStatusBar* GuiStatusBar::create() {
return new GtkGuiStatusBar();
}