diff options
| author | Joel Klinghed <the_jk@yahoo.com> | 2017-07-23 03:45:40 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@yahoo.com> | 2017-07-23 03:45:40 +0200 |
| commit | ff8e353290c0b443c0ec68ee3fc4e137a7025f27 (patch) | |
| tree | c19b6c228fe57be695863bef54e9d4f3b4893e95 /src/gui_gtk.cc | |
| parent | a66d21a1fd7b53997c27279f6269e9226f06fb6b (diff) | |
Add proxy log window
Both GTK and QT log windows will scroll down to the last row whenever
a new row is appended.
Diffstat (limited to 'src/gui_gtk.cc')
| -rw-r--r-- | src/gui_gtk.cc | 164 |
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); +} |
