diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui_gtk.cc | 190 | ||||
| -rw-r--r-- | src/gui_menu.hh | 31 | ||||
| -rw-r--r-- | src/gui_qt.cc | 120 | ||||
| -rw-r--r-- | src/monitor-gui.cc | 33 |
4 files changed, 321 insertions, 53 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(); } diff --git a/src/gui_menu.hh b/src/gui_menu.hh index e2af274..a6c6b54 100644 --- a/src/gui_menu.hh +++ b/src/gui_menu.hh @@ -17,11 +17,40 @@ public: Listener() {} }; + static const uint32_t CTRL; + static const uint32_t ALT; + static const uint32_t SHIFT; + static const uint32_t META; + + static const char ESC; + static const char UP; + static const char LEFT; + static const char RIGHT; + static const char DOWN; + static const char RETURN; + + struct Shortcut { + uint32_t const mask; + char const key; + bool const function; + + Shortcut() + : mask(0), key(0), function(false) { + } + Shortcut(uint32_t mask, char key) + : mask(mask), key(key), function(false) { + } + Shortcut(uint32_t mask, int function) + : mask(mask), key(function), function(true) { + } + }; + virtual ~GuiMenu() {} static GuiMenu* create(); - virtual void add_item(std::string const& id, std::string const& label) = 0; + virtual void add_item(std::string const& id, std::string const& label, + Shortcut const& shortcut = Shortcut()) = 0; // The returned menu lives as long as the root-menu, do not free // the pointer yourself virtual GuiMenu* add_menu(std::string const& label) = 0; diff --git a/src/gui_qt.cc b/src/gui_qt.cc index d4bc3d8..8607e16 100644 --- a/src/gui_qt.cc +++ b/src/gui_qt.cc @@ -173,12 +173,13 @@ public: : parent_(nullptr) { } - void add_item(std::string const& id, std::string const& label) override { - item_.emplace_back(id, label); + void add_item(std::string const& id, std::string const& label, + Shortcut const& shortcut) override { + item_.emplace_back(id, label, shortcut); } void add_separator() override { - item_.emplace_back("", ""); + item_.emplace_back("", "", Shortcut()); } GuiMenu* add_menu(std::string const& label) override; @@ -202,16 +203,68 @@ public: } protected: + struct Entry { + std::string const id; + std::string const label; + Shortcut const shortcut; + + Entry(std::string const& id, std::string const& label, + Shortcut const& shortcut) + : id(id), label(label), shortcut(shortcut) { + } + }; + bool find(const std::string& id) const { - for (auto const& pair : item_) { - if (pair.first == id) return true; + for (auto const& entry : item_) { + if (entry.id == id) return true; } return false; } + static QKeySequence make_key_sequence(Shortcut const& shortcut) { + assert(shortcut.key); + int mod = 0; + if (shortcut.mask & CTRL) mod |= Qt::CTRL; + if (shortcut.mask & ALT) mod |= Qt::ALT; + if (shortcut.mask & SHIFT) mod |= Qt::SHIFT; + if (shortcut.mask & META) mod |= Qt::META; + int key = 0; + if (shortcut.function) { + key = Qt::Key_F1 + (shortcut.key - 1); + if (key > Qt::Key_F35) { + return QKeySequence(); + } + } else { + if (shortcut.key == ESC) { + key = Qt::Key_Escape; + } else if (shortcut.key == RETURN) { + key = Qt::Key_Return; + } else if (shortcut.key == UP) { + key = Qt::Key_Up; + } else if (shortcut.key == LEFT) { + key = Qt::Key_Left; + } else if (shortcut.key == RIGHT) { + key = Qt::Key_Right; + } else if (shortcut.key == DOWN) { + key = Qt::Key_Down; + } else if (shortcut.key >= 0x20 && shortcut.key <= 0x60) { + key = shortcut.key; + } else if (shortcut.key > 0x60 && shortcut.key < 0x7b) { + key = shortcut.key - 32; + } else if (shortcut.key == '\t') { + key = Qt::Key_Tab; + } else if (shortcut.key == 0x08) { + key = Qt::Key_Backspace; + } else { + return QKeySequence(); + } + } + return QKeySequence(mod + key); + } + QtCommonMenu* const parent_; std::string const label_; - std::vector<std::pair<std::string, std::string>> item_; + std::vector<Entry> item_; std::unordered_set<std::string> disabled_; std::vector<std::unique_ptr<QtChildGuiMenu>> children_; }; @@ -220,7 +273,8 @@ class QtGuiMenu : public QtCommonMenu { public: QtGuiMenu(); - 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& shotcut) override; void add_separator() override; GuiMenu* add_menu(std::string const& label) override; bool enable_item(const std::string& id, bool enable) override; @@ -599,12 +653,16 @@ public: : QtCommonMenu(parent, label), menu_(nullptr) { } - 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 { if (!menu_) { - QtCommonMenu::add_item(id, label); + QtCommonMenu::add_item(id, label, shortcut); return; } auto action = menu_->addAction(QString::fromStdString(label)); + if (shortcut.key != 0) { + action->setShortcut(make_key_sequence(shortcut)); + } parent_->add_action(id, action); } @@ -637,13 +695,16 @@ public: assert(menu); menu_ = menu; - for (auto const& pair : item_) { - if (pair.first.empty() && pair.second.empty()) { + for (auto const& entry : item_) { + if (entry.id.empty() && entry.label.empty()) { menu_->addSeparator(); continue; } - auto action = menu_->addAction(QString::fromStdString(pair.second)); - parent_->add_action(pair.first, action); + auto action = menu_->addAction(QString::fromStdString(entry.label)); + if (entry.shortcut.key) { + action->setShortcut(make_key_sequence(entry.shortcut)); + } + parent_->add_action(entry.id, action); } item_.clear(); @@ -666,12 +727,17 @@ QtGuiMenu::QtGuiMenu() : QtCommonMenu(), menubar_(nullptr) { } -void QtGuiMenu::add_item(std::string const& id, std::string const& label) { +void QtGuiMenu::add_item(std::string const& id, std::string const& label, + Shortcut const& shortcut) { if (!menubar_) { - QtCommonMenu::add_item(id, label); + QtCommonMenu::add_item(id, label, shortcut); return; } - add_action(id, menubar_->addAction(QString::fromStdString(label))); + auto action = menubar_->addAction(QString::fromStdString(label)); + if (shortcut.key != 0) { + action->setShortcut(make_key_sequence(shortcut)); + } + add_action(id, action); } void QtGuiMenu::add_separator() { @@ -725,9 +791,11 @@ void QtGuiMenu::setup(QMenuBar* menubar) { assert(menubar); assert(!menubar_); menubar_ = menubar; - for (auto const& pair : item_) { - auto action = menubar_->addAction(QString::fromStdString(pair.second)); - add_action(pair.first, action); + for (auto const& entry : item_) { + auto action = menubar_->addAction(QString::fromStdString(entry.label)); + if (entry.shortcut.key) { + } + add_action(entry.id, action); } item_.clear(); @@ -1779,6 +1847,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 QtGuiStatusBar(); } diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc index 2b8cf07..0670b80 100644 --- a/src/monitor-gui.cc +++ b/src/monitor-gui.cc @@ -693,12 +693,17 @@ public: crt_filter_.back().masks.emplace_back("*.*"); auto file = menu_->add_menu("File"); - file->add_item(ACTION_NEW, "New"); - file->add_item(ACTION_OPEN, "Open..."); - file->add_item(ACTION_SAVE, "Save"); - file->add_item(ACTION_SAVE_AS, "Save As..."); + file->add_item(ACTION_NEW, "New", + GuiMenu::Shortcut(GuiMenu::CTRL, 'N')); + file->add_item(ACTION_OPEN, "Open...", + GuiMenu::Shortcut(GuiMenu::CTRL, 'O')); + file->add_item(ACTION_SAVE, "Save", + GuiMenu::Shortcut(GuiMenu::CTRL, 'S')); + file->add_item(ACTION_SAVE_AS, "Save As...", + GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::SHIFT, 'S')); file->add_separator(); - file->add_item(ACTION_EXIT, "Exit"); + file->add_item(ACTION_EXIT, "Quit", + GuiMenu::Shortcut(GuiMenu::CTRL, 'Q')); menu_->enable_item(ACTION_SAVE, false); @@ -716,14 +721,19 @@ public: auto proxy = menu_->add_menu("Proxy"); proxy->add_item(ACTION_SETUP, "Setup..."); - proxy->add_item(ACTION_CONNECT, "Connect..."); - proxy->add_item(ACTION_DISCONNECT, "Disconnect"); + proxy->add_item(ACTION_CONNECT, "Connect...", + GuiMenu::Shortcut(GuiMenu::CTRL, 'C')); + proxy->add_item(ACTION_DISCONNECT, "Disconnect", + GuiMenu::Shortcut(GuiMenu::CTRL, 'D')); auto edit = menu_->add_menu("Edit"); - edit->add_item(ACTION_COPY_TEXT, "Copy"); - edit->add_item(ACTION_COPY_RAW, "Copy binary"); + edit->add_item(ACTION_COPY_TEXT, "Copy", + GuiMenu::Shortcut(GuiMenu::CTRL, 'C')); + edit->add_item(ACTION_COPY_RAW, "Copy binary", + GuiMenu::Shortcut(GuiMenu::CTRL | GuiMenu::SHIFT, 'C')); edit->add_separator(); - edit->add_item(ACTION_JUMP, "Jump to related"); + edit->add_item(ACTION_JUMP, "Jump to related", + GuiMenu::Shortcut(GuiMenu::CTRL, ' ')); edit->add_separator(); edit->add_item(ACTION_CLEAR, "Clear"); #if HAVE_SSL @@ -731,7 +741,8 @@ public: tools->add_item(ACTION_GENERATE_CA, "Generate CA..."); #endif // HAVE_SSL auto help = menu_->add_menu("Help"); - help->add_item(ACTION_ABOUT, "About..."); + help->add_item(ACTION_ABOUT, "About...", + GuiMenu::Shortcut(0, 1)); help->add_item(ACTION_PROXY_LOG, "Proxy log..."); main_->set_menu(menu_.get()); main_->set_statusbar(statusbar_.get()); |
