summaryrefslogtreecommitdiff
path: root/src/gui_gtk.ccbak
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui_gtk.ccbak')
-rw-r--r--src/gui_gtk.ccbak490
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);
+}