diff options
author | Lucas Fryzek <lucas.fryzek@fryzekconcepts.com> | 2024-10-06 17:34:29 +0100 |
---|---|---|
committer | Lucas Fryzek <lucas.fryzek@fryzekconcepts.com> | 2024-10-06 17:34:29 +0100 |
commit | db10dc4fdd656b25442c63eb02ea30931eee300e (patch) | |
tree | 9b2a4e835d6d52b48c0e7fb9bcb83884999ee1da | |
parent | 0dc8409b168b293e9566dc5a35bded510fbd043d (diff) |
window: Add interface for connecting and disconnecting
-rw-r--r-- | src/chat.py | 3 | ||||
-rw-r--r-- | src/config.py | 18 | ||||
-rw-r--r-- | src/gtk/preferences.ui | 12 | ||||
-rw-r--r-- | src/gtk/welcome.ui | 38 | ||||
-rw-r--r-- | src/main.py | 51 | ||||
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/preferences.py | 3 | ||||
-rw-r--r-- | src/relay/network.py | 36 | ||||
-rw-r--r-- | src/weegtk.gresource.xml | 1 | ||||
-rw-r--r-- | src/welcome.py | 38 | ||||
-rw-r--r-- | src/window.py | 12 | ||||
-rw-r--r-- | src/window.ui | 6 |
12 files changed, 187 insertions, 32 deletions
diff --git a/src/chat.py b/src/chat.py index 603c2be..b82b4a3 100644 --- a/src/chat.py +++ b/src/chat.py @@ -1,4 +1,4 @@ -# window.py +# chat.py # # Copyright 2024 Lucas Fryzek # @@ -179,3 +179,4 @@ class WeegtkChat(Adw.Bin): dialog = Gtk.FileDialog() dialog.set_title("Select file to upload") dialog.open(self.get_root(), None, self.open_file_dialog, self) + diff --git a/src/config.py b/src/config.py index ac52063..dc7d507 100644 --- a/src/config.py +++ b/src/config.py @@ -34,13 +34,13 @@ CONFIG_DEFAULT_RELAY_LINES = 50 CONFIG_DEFAULT_SECTIONS = ('relay', 'look', 'color', 'ssh', 'upload') CONFIG_DEFAULT_OPTIONS = (('relay.hostname', ''), - ('relay.port', ''), - ('relay.ssl', 'off'), + ('relay.port', 0), + ('relay.ssl', False), ('relay.password', ''), - ('relay.autoconnect', 'off'), + ('relay.autoconnect', False), ('relay.lines', str(CONFIG_DEFAULT_RELAY_LINES)), ('ssh.host', ''), - ('ssh.port', ''), + ('ssh.port', 0), ('ssh.username', ''), ('ssh.key', ''), ('upload.url', ''), @@ -139,3 +139,13 @@ def color_options(): """Return color options.""" global config_options return config_options["color"] + +def str_to_bool(value): + truthy = ["true", "1", "on", "y", "yes", "t", "i"] + #falsy = ["false", "0", "off", "n", "no", "f", "o"] + + lower_val = value.lower() + if lower_val in truthy: + return True + else: + return False diff --git a/src/gtk/preferences.ui b/src/gtk/preferences.ui index 9e4d9fc..77f3c14 100644 --- a/src/gtk/preferences.ui +++ b/src/gtk/preferences.ui @@ -13,7 +13,12 @@ <property name="use-underline">True</property> <child> <object class="AdwPreferencesGroup"> - <property name="title" translatable="yes">Connection</property> + <property name="title" translatable="yes">Relay Connection</property> + <child> + <object class="AdwSwitchRow" id="autoconnect"> + <property name="title" translatable="yes">Autoconnect</property> + </object> + </child> <child> <object class="AdwEntryRow" id="hostname"> <property name="title" translatable="yes">Hostname</property> @@ -31,6 +36,11 @@ <property name="title" translatable="yes">Password</property> </object> </child> + </object> + </child> + <child> + <object class="AdwPreferencesGroup"> + <property name="title" translatable="yes">SSH Connection</property> <child> <object class="AdwEntryRow" id="ssh_host"> <property name="title" translatable="yes">SSH host</property> diff --git a/src/gtk/welcome.ui b/src/gtk/welcome.ui new file mode 100644 index 0000000..9b6ad76 --- /dev/null +++ b/src/gtk/welcome.ui @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk" version="4.0"/> + <requires lib="libadwaita" version="1.0"/> + <template class="WeegtkWelcome" parent="AdwBin"> + <property name="child"> + <object class="AdwToolbarView"> + <child type="top"> + <object class="AdwHeaderBar"> + <property name="show-title">False</property> + </object> + </child> + <property name="content"> + <object class="AdwStatusPage"> + <property name="icon-name">com.fryzekconcepts.weegtk-symbolic</property> + <property name="title" translatable="yes">Welcome to Weegtk</property> + <property name="description" translatable="yes">Change connection settings in preferences</property> + <property name="child"> + <object class="GtkButton"> + <signal name="clicked" handler="connect_network"/> + <style> + <class name="suggested-action"/> + </style> + <property name="child"> + <object class="AdwButtonContent"> + <property name="icon-name">network-wired-symbolic</property> + <property name="label" translatable="yes">_Connect</property> + <property name="use-underline">True</property> + </object> + </property> + </object> + </property> + </object> + </property> + </object> + </property> + </template> +</interface> diff --git a/src/main.py b/src/main.py index 5a1427a..53206be 100644 --- a/src/main.py +++ b/src/main.py @@ -27,11 +27,12 @@ gi.require_version('Adw', '1') from gi.repository import Gtk, Gio, Adw from .window import WeegtkWindow -from .network import Network +from weegtk import network from weegtk import protocol from weegtk import config from .chat import WeegtkChat from .preferences import WeegtkPreferences +from .welcome import WeegtkWelcome class WeegtkApplication(Adw.Application): """The main application singleton class.""" @@ -42,13 +43,20 @@ class WeegtkApplication(Adw.Application): self.create_action('quit', lambda *_: self.quit(), ['<primary>q']) self.create_action('about', self.on_about_action) self.create_action('preferences', self.on_preferences_action) + self.create_action('disconnect', self.disconnect) - self.network = Network() + self.network = network.Network() self.network.connect("status_changed", self._network_status_changed) self.network.connect("message_from_weechat", self._network_weechat_msg) + self.buffers = [] + + conf = config.read() + if config.str_to_bool(conf["relay"]["autoconnect"]): + self.connect_to_weechat() + + def connect_to_weechat(self, *args): conf = config.read() - #self.network.connect_weechat("localhost", "9001", False, relay_pw, None, "") self.network.connect_weechat_ssh(conf["ssh"]["host"], conf["ssh"]["port"], conf["ssh"]["username"], @@ -56,21 +64,25 @@ class WeegtkApplication(Adw.Application): conf["relay"]["hostname"], conf["relay"]["port"], conf["relay"]["password"]) - + def clear_buffers(self): + for buffer in self.buffers: + self.props.active_window.stack.remove(buffer) self.buffers = [] + def disconnect(self, *args): + self.network.disconnect_weechat() + + def handle_disconnect(self): + self.props.active_window.set_page(self.welcome_page) + self.clear_buffers() + def _network_status_changed(self, source_object, status, extra): """Called when the network status has changed.""" - # TODO handle this - #if self.config.getboolean('look', 'statusbar'): - # self.statusBar().showMessage(status) - #self.network.debug_print(0, '', status, forcecolor='#0000AA') - #self.network_status_set(status) - print(f"Status is {status}") + if status == network.STATUS_DISCONNECTED: + self.handle_disconnect() def _network_weechat_msg(self, source_object, message): """Called when a message is received from WeeChat.""" - #print(f"Message {message}") try: proto = protocol.Protocol() message = proto.decode(message.get_data()) @@ -99,6 +111,10 @@ class WeegtkApplication(Adw.Application): win = self.props.active_window if not win: win = WeegtkWindow(application=self) + welcome = WeegtkWelcome() + welcome.connect("connect-network", self.connect_to_weechat) + self.welcome_page = win.stack.add_named(welcome, "welcome") + win.set_page(self.welcome_page) win.present() def on_about_action(self, *args): @@ -148,11 +164,8 @@ class WeegtkApplication(Adw.Application): for obj in message.objects: if obj.objtype != 'hda' or obj.value['path'][-1] != 'buffer': continue - #self.list_buffers.clear() - #while self.stacked_buffers.count() > 0: - # buf = self.stacked_buffers.widget(0) - # self.stacked_buffers.removeWidget(buf) - self.buffers = [] + + self.clear_buffers() for item in obj.value['items']: buf = self.create_buffer(item) self.insert_buffer(len(self.buffers), buf) @@ -323,8 +336,9 @@ class WeegtkApplication(Adw.Application): # Only make chats visible on main screen if True or buf.is_chat(): - test_label = Gtk.Label(label="Hello") - self.props.active_window.stack.add_titled(buf, name=buf_name, title=buf_name) + page = self.props.active_window.stack.add_titled(buf, name=buf_name, title=buf_name) + if buf.data["full_name"] == "core.weechat": + self.props.active_window.set_page(page) def buffer_input(self, source_object, full_name, text): if self.network.is_connected(): @@ -335,3 +349,4 @@ def main(version): """The application's entry point.""" app = WeegtkApplication() return app.run(sys.argv) + diff --git a/src/meson.build b/src/meson.build index 649a19e..081477c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -34,6 +34,7 @@ weegtk_sources = [ 'chat.py', 'message.py', 'preferences.py', + 'welcome.py', 'relay/network.py', 'relay/protocol.py', 'relay/color.py', diff --git a/src/preferences.py b/src/preferences.py index 1a87e29..8e6d150 100644 --- a/src/preferences.py +++ b/src/preferences.py @@ -28,6 +28,7 @@ import os class WeegtkPreferences(Adw.PreferencesDialog): __gtype_name__ = 'WeegtkPreferences' + autoconnect = Gtk.Template.Child() hostname = Gtk.Template.Child() port = Gtk.Template.Child() password = Gtk.Template.Child() @@ -46,6 +47,7 @@ class WeegtkPreferences(Adw.PreferencesDialog): self.config = config.read() + self.autoconnect.set_active(config.str_to_bool(self.config["relay"]["autoconnect"])) self.hostname.set_text(self.config["relay"]["hostname"]) self.port.set_text(self.config["relay"]["port"]) self.password.set_text(self.config["relay"]["password"]) @@ -60,6 +62,7 @@ class WeegtkPreferences(Adw.PreferencesDialog): self.install_action("ssh_key.open", None, self.ssh_key_open) def read_preferences(self): + self.config["relay"]["autoconnect"] = str(self.autoconnect.get_active()) self.config["relay"]["hostname"] = self.hostname.get_text() self.config["relay"]["port"] = self.port.get_text() self.config["relay"]["password"] = self.password.get_text() diff --git a/src/relay/network.py b/src/relay/network.py index 4308ecf..b502ddd 100644 --- a/src/relay/network.py +++ b/src/relay/network.py @@ -212,7 +212,7 @@ class Network(GObject.GObject): """Slot: data available on socket.""" try: gbytes = self.input.read_bytes_finish(res) - except GLib.Error as err: + except GLib.GError as err: self.handle_network_error(err) return @@ -281,6 +281,9 @@ class Network(GObject.GObject): except ValueError: self._lines = config.CONFIG_DEFAULT_RELAY_LINES + if self.cancel_network_reads.is_cancelled(): + self.cancel_network_reads.reset() + # TODO handle SSL self._socketclient.connect_async( Gio.NetworkAddress.new(self._hostname, self._port), @@ -306,15 +309,33 @@ class Network(GObject.GObject): def disconnect_weechat(self): """Disconnect from WeeChat.""" - if self._socket.state() == QtNetwork.QAbstractSocket.UnconnectedState: - self.set_status(STATUS_DISCONNECTED) + if not self._socket.is_connected(): return - if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState: - self.send_to_weechat('quit\n') - self._socket.waitForBytesWritten(1000) else: + self.send_to_weechat('quit\n') + self._socket.set_graceful_disconnect(True) + self._socket.close() + self._socket = None + self.cancel_network_reads.cancel() + self.ssh_tunnel.stop() + self.ssh_tunnel = None self.set_status(STATUS_DISCONNECTED) - self._socket.abort() + + def handle_network_error(self, err): + if err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.CANCELLED): + print("Connection has been canceled by user.") + return + elif err.matches(Gio.tls_error_quark(), Gio.TlsError.EOF): + print("Server has closed the connection.") + return + elif err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.BROKEN_PIPE): + print("Broken pipe, connection lost.") + return + elif err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.TIMED_OUT): + print("Connection timed out.") + return + else: + raise def send_to_weechat(self, message): """Send a message to WeeChat.""" @@ -402,3 +423,4 @@ class Network(GObject.GObject): self.debug_dialog.chat.scroll_bottom() + diff --git a/src/weegtk.gresource.xml b/src/weegtk.gresource.xml index c54ffe8..9b7746a 100644 --- a/src/weegtk.gresource.xml +++ b/src/weegtk.gresource.xml @@ -6,6 +6,7 @@ <file preprocess="xml-stripblanks">gtk/chat.ui</file> <file preprocess="xml-stripblanks">gtk/message.ui</file> <file preprocess="xml-stripblanks">gtk/preferences.ui</file> + <file preprocess="xml-stripblanks">gtk/welcome.ui</file> <file compressed="true">style.css</file> </gresource> </gresources> diff --git a/src/welcome.py b/src/welcome.py new file mode 100644 index 0000000..96bc0e6 --- /dev/null +++ b/src/welcome.py @@ -0,0 +1,38 @@ +# welcome.py +# +# Copyright 2024 Lucas Fryzek +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# SPDX-License-Identifier: GPL-3.0-or-later +from gi.repository import Adw, Gtk, GObject + +@Gtk.Template(resource_path='/com/fryzekconcepts/weegtk/gtk/welcome.ui') +class WeegtkWelcome(Adw.Bin): + __gtype_name__ = 'WeegtkWelcome' + + __gsignals__ = { + "connect-network" : ( + GObject.SignalFlags.RUN_FIRST, + None, + () + ) + } + + def __init__(self, data=None, **kwargs): + super().__init__(**kwargs) + + @Gtk.Template.Callback() + def connect_network(self, *args): + self.emit("connect-network") diff --git a/src/window.py b/src/window.py index df63e36..8a4eab7 100644 --- a/src/window.py +++ b/src/window.py @@ -28,6 +28,7 @@ class WeegtkWindow(Adw.ApplicationWindow): content_page = Gtk.Template.Child() color_scheme_button = Gtk.Template.Child() split_view = Gtk.Template.Child() + primary_menu = Gtk.Template.Child() def __init__(self, **kwargs): super().__init__(**kwargs) @@ -41,6 +42,9 @@ class WeegtkWindow(Adw.ApplicationWindow): self.notify_system_supports_color_schemes_cb() self.notify_visible_child_cb() + def set_connect_label(self): + pass + def notify_system_supports_color_schemes_cb(self): manager = Adw.StyleManager.get_default() supports = manager.get_system_supports_color_schemes() @@ -57,9 +61,14 @@ class WeegtkWindow(Adw.ApplicationWindow): if child is None: return page = self.stack.get_page(child) - self.content_page.set_title(page.get_title()) + page_title = "Weegtk" if page.get_title() is None else page.get_title() + self.content_page.set_title(page_title) self.split_view.set_show_content(True) + def set_page(self, page): + page_name = page.get_name() + self.stack.set_visible_child_name(page_name) + @Gtk.Template.Callback() def get_color_scheme_icon_name(self, user_data, dark): return "light-mode-symbolic" if dark else "dark-mode-symbolic" @@ -68,3 +77,4 @@ class WeegtkWindow(Adw.ApplicationWindow): def color_scheme_button_clicked_cb(self, *args): raise NotImplementedError + diff --git a/src/window.ui b/src/window.ui index efc7d87..54646e9 100644 --- a/src/window.ui +++ b/src/window.ui @@ -5,6 +5,12 @@ <menu id="primary_menu"> <section> <item> + <attribute name="label" translatable="yes">_Disconnect</attribute> + <attribute name="action">app.disconnect</attribute> + </item> + </section> + <section> + <item> <attribute name="label" translatable="yes">_Preferences</attribute> <attribute name="action">app.preferences</attribute> </item> |