summaryrefslogtreecommitdiff
path: root/src/gui_gtk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui_gtk.cc')
-rw-r--r--src/gui_gtk.cc164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc
index afccd85..6afec5d 100644
--- a/src/gui_gtk.cc
+++ b/src/gui_gtk.cc
@@ -17,6 +17,7 @@
#include "gui_main.hh"
#include "gui_menu.hh"
#include "gui_statusbar.hh"
+#include "gui_textwnd.hh"
#include "looper.hh"
#include "observers.hh"
@@ -1185,6 +1186,162 @@ private:
bool applied_;
};
+class GtkGuiTextWindow : public virtual GuiTextWindow, public GtkGuiWindow {
+public:
+ GtkGuiTextWindow(std::string const& title, uint32_t width, uint32_t height,
+ AttributedText const* text)
+ : title_(title), width_(width), height_(height), text_(text), wnd_(nullptr),
+ view_(nullptr), end_mark_(nullptr) {
+ }
+
+ ~GtkGuiTextWindow() override {
+ disconnect_buffer();
+ if (wnd_) {
+ gtk_widget_destroy(wnd_);
+ }
+ }
+
+ void set_title(std::string const& title) override {
+ title_ = title;
+ GtkGuiWindow::set_title(title);
+ }
+
+ void set_text(AttributedText const* text) override {
+ if (!text) {
+ assert(false);
+ if (text_own_) text_own_->reset();
+ return;
+ }
+ disconnect_buffer();
+ text_ = text;
+ connect_buffer();
+ text_own_.reset();
+ }
+
+ void set_text(std::unique_ptr<AttributedText>&& text) override {
+ if (!text) {
+ assert(false);
+ if (text_own_) text_own_->reset();
+ return;
+ }
+ disconnect_buffer();
+ text_ = text.get();
+ connect_buffer();
+ text_own_.swap(text);
+ }
+
+ AttributedText const* text() const override {
+ return text_;
+ }
+
+ void* impl() const override {
+ return wnd_;
+ }
+
+ void add_listener(GuiTextWindow::Listener* listener) override {
+ observers_.insert(listener);
+ }
+
+ void remove_listener(GuiTextWindow::Listener* listener) override {
+ observers_.erase(listener);
+ }
+
+ void show(GuiWindow* UNUSED(parent)) override {
+ if (wnd_) {
+ assert(false);
+ focus();
+ return;
+ }
+ wnd_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(G_OBJECT(wnd_), "delete-event",
+ G_CALLBACK(delete_event), this);
+ gtk_window_set_default_size(GTK_WINDOW(wnd_), width_, height_);
+ gtk_window_set_title(GTK_WINDOW(wnd_), title_.c_str());
+ view_ = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(view_), false);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view_), false);
+ connect_buffer();
+ auto scroll = gtk_scrolled_window_new(nullptr, nullptr);
+ gtk_container_add(GTK_CONTAINER(scroll), view_);
+ gtk_container_add(GTK_CONTAINER(wnd_), scroll);
+ gtk_widget_show_all(wnd_);
+ }
+
+ void focus() override {
+ if (!wnd_) {
+ assert(false);
+ return;
+ }
+ gtk_window_present(GTK_WINDOW(wnd_));
+ }
+
+protected:
+ bool notify_about_to_close() {
+ auto it = observers_.notify();
+ while (it.has_next()) {
+ if (!it.next()->about_to_close(this)) return false;
+ }
+ return true;
+ }
+
+ void disconnect_buffer() {
+ if (!view_ || !text_) return;
+ auto buf = static_cast<GtkAttributedText const*>(text_)->buffer();
+ g_signal_handler_disconnect(G_OBJECT(buf), changed_handler_);
+ gtk_text_buffer_delete_mark(buf, end_mark_);
+ end_mark_ = nullptr;
+ }
+
+ void scroll_to_bottom() {
+ gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(view_), end_mark_,
+ 0.0, true, 0.0, 1.0);
+ }
+
+ void connect_buffer() {
+ if (!view_ || !text_) return;
+ auto buf = static_cast<GtkAttributedText const*>(text_)->buffer();
+ changed_handler_ = g_signal_connect(G_OBJECT(buf), "changed",
+ G_CALLBACK(buf_changed), this);
+ GtkTextIter end;
+ gtk_text_buffer_get_end_iter(buf, &end);
+ end_mark_ = gtk_text_buffer_create_mark(buf, nullptr, &end, false);
+ gtk_text_view_set_buffer(GTK_TEXT_VIEW(view_), buf);
+ }
+
+ static void buf_changed(GtkTextBuffer* UNUSED(buffer), gpointer user_data) {
+ auto me = reinterpret_cast<GtkGuiTextWindow*>(user_data);
+ me->scroll_to_bottom();
+ }
+
+ static gboolean delete_event(GtkWidget* widget, GdkEvent* UNUSED(event),
+ gpointer user_data) {
+ auto me = reinterpret_cast<GtkGuiTextWindow*>(user_data);
+ assert(me->wnd_ == widget);
+ me->disconnect_buffer();
+ me->wnd_ = nullptr;
+ auto view = me->view_;
+ me->view_ = nullptr;
+ if (me->notify_about_to_close()) {
+ gtk_widget_destroy(widget);
+ return false;
+ }
+ me->wnd_ = widget;
+ me->view_ = view;
+ me->connect_buffer();
+ return true;
+ }
+
+ std::string title_;
+ uint32_t width_;
+ uint32_t height_;
+ AttributedText const* text_;
+ std::unique_ptr<AttributedText> text_own_;
+ Observers<GuiTextWindow::Listener*> observers_;
+ GtkWidget* wnd_;
+ GtkWidget* view_;
+ GtkTextMark* end_mark_;
+ gulong changed_handler_;
+};
struct _MainApp
{
@@ -1667,3 +1824,10 @@ Looper* GuiMain::createLooper() {
AttributedText* AttributedText::create() {
return new GtkAttributedText();
}
+
+// static
+GuiTextWindow* GuiTextWindow::create(std::string const& title,
+ uint32_t width, uint32_t height,
+ AttributedText const* text) {
+ return new GtkGuiTextWindow(title, width, height, text);
+}