diff options
Diffstat (limited to 'src/gui_gtk.ccbak')
| -rw-r--r-- | src/gui_gtk.ccbak | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/src/gui_gtk.ccbak b/src/gui_gtk.ccbak new file mode 100644 index 0000000..fac8886 --- /dev/null +++ b/src/gui_gtk.ccbak @@ -0,0 +1,490 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" + +#include <gtk/gtk.h> +#include <memory> +#include <unordered_map> +#include <vector> + +#include "gui_about.hh" +#include "gui_main.hh" +#include "gui_menu.hh" +#include "observers.hh" + +namespace { + +template<typename T> +class shared_gobject { +public: + shared_gobject() + : ptr_(nullptr) { + } + shared_gobject(std::nullptr_t) + : ptr_(nullptr) { + } + explicit shared_gobject(T* ptr) + : ptr_(ptr) { + } + shared_gobject(shared_gobject const& obj) + : ptr_(obj.ptr_) { + if (ptr_) g_object_ref(ptr_); + } + shared_gobject(shared_gobject&& obj) + : ptr_(obj.ptr_) { + obj.ptr = nullptr; + } + + ~shared_gobject() { + reset(); + } + + shared_gobject& operator=(shared_gobject const& obj) { + reset(obj.ptr_); + if (ptr_) g_object_ref(ptr_); + return *this; + } + shared_gobject& operator=(shared_gobject&& obj) { + ptr_ = obj.ptr_; + obj.ptr_ = nullptr; + return *this; + } + + void swap(shared_gobject& obj) { + auto x = ptr_; + ptr_ = obj.ptr_; + obj.ptr_ = x; + } + + void reset() { + if (ptr_) { + g_object_unref(ptr_); + ptr_ = nullptr; + } + } + + void reset(T* ptr) { + if (ptr_) g_object_unref(ptr_); + ptr_ = ptr; + } + + T* get() const { + return ptr_; + } + + T& operator*() const { + return *ptr_; + } + + T* operator->() const { + return ptr_; + } + + explicit operator bool() const { + return ptr_ != nullptr; + } + +private: + T* ptr_; +}; + +#define MAIN_APP_TYPE main_app_get_type() + +G_DECLARE_FINAL_TYPE(MainApp, main_app, MAIN, APP, GtkApplication) + +#define MAIN_APP_WINDOW_TYPE main_app_window_get_type() +G_DECLARE_FINAL_TYPE(MainAppWindow, main_app_window, MAIN, APP_WINDOW, + GtkApplicationWindow) + +class GtkGuiWindow : public GuiWindow { +public: + GtkWindow* window() const { + return reinterpret_cast<GtkWindow*>(impl()); + } + + void set_title(std::string const& title) override; +}; + +class GtkGuiMain : public virtual GuiMain, public GtkGuiWindow { +public: + GtkGuiMain(std::string const& title, uint32_t width, uint32_t height) + : title_(title), width_(width), height_(height) { + } + + void set_menu(GuiMenu* menu) override { + assert(menu); + menu_ = menu; + } + + GuiMenu* menu() const override { + return menu_; + } + + bool run(int argc, char** argv) override; + + bool exit() override; + + void set_title(std::string const& title) override; + + void show(GuiWindow* window) override; + + uint32_t default_width() const { + return width_; + } + + uint32_t default_height() const { + return height_; + } + + void add_listener(GuiMain::Listener* listener) override { + observers_.insert(listener); + } + + void remove_listener(GuiMain::Listener* listener) override { + observers_.erase(listener); + } + + void* impl() const override; + +private: + bool notify_about_to_exit() { + auto it = observers_.notify(); + while (it.has_next()) { + if (!it.next()->about_to_exit()) return false; + } + return true; + } + + std::string title_; + uint32_t width_; + uint32_t height_; + + shared_gobject<MainApp> app_; + Observers<GuiMain::Listener*> observers_; + GuiMenu* menu_; +}; + +class GtkGuiMenu : public GuiMenu { +public: + GtkGuiMenu() + : parent_(nullptr), menu_(g_menu_new()), map_(nullptr) { + } + + ~GtkGuiMenu() override { + } + + void add_listener(Listener* listener) override { + if (parent_) { + parent_->add_listener(listener); + return; + } + observers_.insert(listener); + } + + void remove_listener(Listener* listener) override { + if (parent_) { + parent_->remove_listener(listener); + return; + } + observers_.erase(listener); + } + + GMenuModel* model() const { + return G_MENU_MODEL(menu_.get()); + } + + void set_map(GActionMap* map); + void activate(GSimpleAction* action); + + void add_item(std::string const& id, std::string const& label) override; + GuiMenu* add_menu(std::string const& label) override; + bool enable_item(std::string const& id, bool enable) override; + +private: + explicit GtkGuiMenu(GtkGuiMenu* parent) + : parent_(parent), menu_(g_menu_new()), map_(nullptr) { + } + + void notify_item_activated(std::string const& id) { + auto it = observers_.notify(); + while (it.has_next()) { + it.next()->item_activated(id); + } + } + + std::string add_action(std::string const& id); + std::string escape(std::string const& id); + std::string unescape(std::string const& action); + + GtkGuiMenu* const parent_; + shared_gobject<GMenu> menu_; + std::unordered_map<std::string, shared_gobject<GSimpleAction>> action_; + GActionMap* map_; + std::vector<std::unique_ptr<GtkGuiMenu>> submenus_; + Observers<Listener*> observers_; +}; + +void close_about_dialog(GtkAboutDialog* about) { + // TODO: Restore dialog state + gtk_widget_hide(GTK_WIDGET(about)); +} + +class GtkGuiAbout : public virtual GuiAbout, public GtkGuiWindow { +public: + GtkGuiAbout(std::string const& title, std::string const& version, + std::vector<std::string> const& authors) + : about_(GTK_ABOUT_DIALOG(gtk_about_dialog_new())) { + gtk_about_dialog_set_program_name(about_, title.c_str()); + gtk_about_dialog_set_version(about_, version.c_str()); + std::vector<std::string> tmp; + tmp.reserve(authors.size() / 2); + for (size_t i = 0; i < authors.size(); i += 2) { + if (authors[i + 1].empty()) { + tmp.push_back(authors[i]); + } else { + tmp.push_back(authors[i] + " <" + authors[i + 1] + ">"); + } + } + std::vector<gchar const*> tmp2; + tmp2.reserve(tmp.size() + 1); + for (auto const& str : tmp) tmp2.push_back(str.c_str()); + tmp2.push_back(nullptr); + gtk_about_dialog_set_authors(about_, tmp2.data()); + + g_signal_connect(about_, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), nullptr); + g_signal_connect(about_, "response", + G_CALLBACK(close_about_dialog), nullptr); + } + + ~GtkGuiAbout() override { + gtk_widget_destroy(GTK_WIDGET(about_)); + } + + void* impl() const override { + return about_; + } + + void set_title(std::string const& title) override { + GtkGuiWindow::set_title(title); + } + + void add_listener(GuiAbout::Listener* listener) override { + observers_.insert(listener); + } + + void remove_listener(GuiAbout::Listener* listener) override { + observers_.erase(listener); + } + +private: + GtkAboutDialog* about_; + Observers<GuiAbout::Listener*> observers_; +}; + +struct _MainApp +{ + GtkApplication parent; + GtkGuiMain* main; +}; + +struct _MainAppWindow +{ + GtkApplicationWindow parent; +}; + +G_DEFINE_TYPE(MainApp, main_app, GTK_TYPE_APPLICATION); + +G_DEFINE_TYPE(MainAppWindow, main_app_window, GTK_TYPE_APPLICATION_WINDOW); + +MainAppWindow* main_app_window_new(MainApp *app) { + auto ret = MAIN_APP_WINDOW(g_object_new(MAIN_APP_WINDOW_TYPE, + "application", app, nullptr)); + gtk_window_set_default_size(GTK_WINDOW(ret), app->main->default_width(), + app->main->default_height()); + return ret; +} + +void main_app_activate(GApplication* g_app) { + auto app = MAIN_APP(g_app); + auto menu = static_cast<GtkGuiMenu*>(app->main->menu()); + if (menu) { + menu->set_map(G_ACTION_MAP(app)); + gtk_application_set_menubar(GTK_APPLICATION(app), menu->model()); + } + auto win = main_app_window_new(app); + gtk_window_present(GTK_WINDOW(win)); +} + +void main_app_window_open(MainAppWindow* win, GFile* file) { +} + +void main_app_open(GApplication* app, GFile** files, gint n_files, + gchar const* hint) { + auto windows = gtk_application_get_windows(GTK_APPLICATION(app)); + auto win = windows ? MAIN_APP_WINDOW(windows->data) + : main_app_window_new(MAIN_APP(app)); + + for (auto i = 0; i < n_files; i++) { + main_app_window_open(win, files[i]); + } + + gtk_window_present(GTK_WINDOW(win)); +} + +void main_app_class_init(MainAppClass* clazz) { + G_APPLICATION_CLASS(clazz)->activate = main_app_activate; + G_APPLICATION_CLASS(clazz)->open = main_app_open; +} + +void main_app_window_init(MainAppWindow* app) { +} + +void main_app_window_class_init(MainAppWindowClass* clazz) { +} + +MainApp* main_app_new(GtkGuiMain* main) { + auto ret = MAIN_APP(g_object_new(MAIN_APP_TYPE, + "application-id", "org.jk.tp", + "flags", G_APPLICATION_HANDLES_OPEN, + nullptr)); + ret->main = main; + return ret; +} + +void GtkGuiWindow::set_title(std::string const& title) { + auto win = window(); + if (!win) return; + gtk_window_set_title(win, title.c_str()); +} + +bool GtkGuiMain::run(int argc, char** argv) { + app_.reset(main_app_new(this)); + return g_application_run(G_APPLICATION(app_.get()), argc, argv); +} + +void* GtkGuiMain::impl() const { + if (!app_) return nullptr; + auto windows = gtk_application_get_windows(GTK_APPLICATION(app_.get())); + if (!windows) return nullptr; + return windows->data; +} + +void GtkGuiMain::set_title(std::string const& title) { + title_ = title; + GtkGuiWindow::set_title(title); +} + +bool GtkGuiMain::exit() { + if (!app_) { + assert(false); + return true; + } + if (!notify_about_to_exit()) return false; + g_application_quit(G_APPLICATION(app_.get())); + return true; +} + +void GtkGuiMain::show(GuiWindow* window) { + auto wnd = reinterpret_cast<GtkWindow*>(window->impl()); + if (!wnd) { + assert(false); + return; + } + gtk_window_set_transient_for(wnd, this->window()); + gtk_window_present(wnd); +} + +void GtkGuiMenu::add_item(std::string const& id, std::string const& label) { + auto action = add_action(id); + g_menu_append(menu_.get(), label.c_str(), action.c_str()); +} + +GuiMenu* GtkGuiMenu::add_menu(std::string const& label) { + auto ret = new GtkGuiMenu(this); + g_menu_append_submenu(menu_.get(), label.c_str(), ret->model()); + submenus_.emplace_back(ret); + return ret; +} + +void GtkGuiMenu::set_map(GActionMap* map) { + assert(!parent_); + assert(!map_); + assert(map); + map_ = map; + for (auto const& pair : action_) { + g_action_map_add_action(map_, G_ACTION(pair.second.get())); + } +} + +std::string GtkGuiMenu::escape(std::string const& action) { + // TODO + return action; +} + +std::string GtkGuiMenu::unescape(std::string const& action) { + return action; +} + +void menu_item_activate(GSimpleAction* action, GVariant* parameter, + gpointer* data) { + auto menu = reinterpret_cast<GtkGuiMenu*>(data); + menu->activate(action); +} + +void GtkGuiMenu::activate(GSimpleAction* action) { + notify_item_activated(unescape(g_action_get_name(G_ACTION(action)))); +} + +std::string GtkGuiMenu::add_action(std::string const& id) { + if (parent_) return parent_->add_action(id); + std::string name = escape(id); + auto action = g_simple_action_new(name.c_str(), nullptr); + action_.emplace(id, action); + g_signal_connect(action, "activate", G_CALLBACK(menu_item_activate), this); + if (map_) g_action_map_add_action(map_, G_ACTION(action)); + return "app." + name; +} + +bool GtkGuiMenu::enable_item(std::string const& id, bool enable) { + if (parent_) return parent_->enable_item(id, enable); + auto pair = action_.find(id); + if (pair == action_.end()) return false; + g_simple_action_set_enabled(pair->second.get(), enable); + return true; +} + +} // namespace + +// static +GuiMain* GuiMain::create(std::string const& title, uint32_t width, + uint32_t height) { + return new GtkGuiMain(title, width, height); +} + +// static +GuiMenu* GuiMenu::create() { + return new GtkGuiMenu(); +} + +// static +GuiAbout* GuiAbout::create(std::string const& title, + std::string const& version, + char const* author_name, + char const* author_email, + ...) { + std::vector<std::string> authors; + authors.push_back(author_name); + authors.push_back(author_email); + va_list args; + va_start(args, author_email); + while (true) { + auto name = va_arg(args, char const*); + if (!name) break; + auto email = va_arg(args, char const*); + authors.push_back(name); + authors.push_back(email); + } + va_end(args); + return new GtkGuiAbout(title, version, authors); +} |
