From 9971378f3c00212b35cb2cc0a04be576c357515d Mon Sep 17 00:00:00 2001 From: Syping Date: Sun, 12 Apr 2026 16:40:19 +0200 Subject: [PATCH] gui: add configuration system --- CMakeLists.txt | 4 + src/core/curl_exception.h | 2 +- src/gui/translator_dialog.cpp | 175 ++++++++++++++++++++++++++++++++ src/gui/translator_dialog.h | 41 ++++++++ src/gui/user_config.cpp | 183 ++++++++++++++++++++++++++++++++++ src/gui/user_config.h | 51 ++++++++++ src/gui/user_interface.cpp | 120 +++++++++++++++++----- src/gui/user_interface.h | 19 +++- 8 files changed, 564 insertions(+), 31 deletions(-) create mode 100644 src/gui/translator_dialog.cpp create mode 100644 src/gui/translator_dialog.h create mode 100644 src/gui/user_config.cpp create mode 100644 src/gui/user_config.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5758805..d11af48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,10 +94,14 @@ if (WITH_GUI) find_package(PkgConfig REQUIRED) pkg_check_modules(GTKMM REQUIRED gtkmm-4.0) list(APPEND DTRANSLATEBOT_HEADERS + src/gui/translator_dialog.h + src/gui/user_config.h src/gui/user_interface.h ) list(APPEND DTRANSLATEBOT_SOURCES src/gui/main.cpp + src/gui/translator_dialog.cpp + src/gui/user_config.cpp src/gui/user_interface.cpp ) else() diff --git a/src/core/curl_exception.h b/src/core/curl_exception.h index ddc1187..2ffd0bf 100644 --- a/src/core/curl_exception.h +++ b/src/core/curl_exception.h @@ -37,6 +37,6 @@ namespace bot { CURLcode m_error; }; } -}; +} #endif // CURL_EXCEPTION_H diff --git a/src/gui/translator_dialog.cpp b/src/gui/translator_dialog.cpp new file mode 100644 index 0000000..d88fb83 --- /dev/null +++ b/src/gui/translator_dialog.cpp @@ -0,0 +1,175 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include "translator_dialog.h" +using namespace bot::gui; + +translator_dialog::translator_dialog(Gtk::Window &parent, const std::string &translator, user_config &user_config) : + Gtk::Dialog("dtranslatebot - Translator", parent, true), m_user_config(user_config) { + m_json = m_user_config.get_translator_settings(translator); + + set_default_size(300, 0); + set_resizable(false); + + auto vertical_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->set_margin(6); + + auto hostname_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*hostname_box); + + auto hostname_label = Gtk::make_managed("Hostname"); + hostname_box->append(*hostname_label); + + auto hostname_entry = Gtk::make_managed(); + hostname_entry->set_hexpand(true); + auto json_hostname = m_json.find("hostname"); + if (json_hostname != m_json.end()) + hostname_entry->set_text(static_cast(*json_hostname)); + hostname_entry->signal_changed().connect([=]() { + m_json["hostname"] = hostname_entry->get_text(); + }); + hostname_box->append(*hostname_entry); + + if (translator != "deepl") { + auto url_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*url_box); + + auto url_label = Gtk::make_managed("URL"); + url_box->append(*url_label); + + auto url_entry = Gtk::make_managed(); + url_entry->set_hexpand(true); + auto json_url = m_json.find("url"); + if (json_url != m_json.end()) + url_entry->set_text(static_cast(*json_url)); + url_entry->signal_changed().connect([=]() { + m_json["url"] = url_entry->get_text(); + }); + url_box->append(*url_entry); + + auto port_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*port_box); + + auto port_label = Gtk::make_managed("Port"); + port_box->append(*port_label); + + auto port_entry = Gtk::make_managed(); + port_entry->set_hexpand(true); + auto json_port = m_json.find("port"); + if (json_port != m_json.end()) + port_entry->set_text(std::to_string(static_cast(*json_port))); + else + port_entry->set_text("443"); + port_entry->signal_changed().connect([=]() { + try { + m_json["port"] = std::stoi(port_entry->get_text()); + } + catch (const std::exception &exception) { + // TODO: Enforce Entry being Number only + } + }); + port_box->append(*port_entry); + + auto tls_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*tls_box); + + auto tls_label = Gtk::make_managed("TLS"); + tls_box->append(*tls_label); + + auto tls_checkbutton = Gtk::make_managed("Enabled"); + auto json_tls = m_json.find("tls"); + if (json_tls != m_json.end()) + tls_checkbutton->set_active(*json_tls); + else + tls_checkbutton->set_active(true); + tls_checkbutton->signal_toggled().connect([=]() { + m_json["tls"] = tls_checkbutton->get_active(); + if (tls_checkbutton->get_active() && port_entry->get_text() == "80") + port_entry->set_text("443"); + else if (!tls_checkbutton->get_active() && port_entry->get_text() == "443") + port_entry->set_text("80"); + }); + tls_box->append(*tls_checkbutton); + } + + if (translator == "mozhi") { + auto engine_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*engine_box); + + auto engine_label = Gtk::make_managed("Engine"); + engine_box->append(*engine_label); + + auto engine_entry = Gtk::make_managed(); + engine_entry->set_hexpand(true); + auto json_engine = m_json.find("engine"); + if (json_engine != m_json.end()) + engine_entry->set_text(static_cast(*json_engine)); + engine_entry->signal_changed().connect([=]() { + m_json["engine"] = engine_entry->get_text(); + }); + engine_box->append(*engine_entry); + } + + if (translator != "lingvatranslate" && translator != "mozhi") { + auto apikey_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + vertical_box->append(*apikey_box); + + auto apikey_label = Gtk::make_managed("API Key"); + apikey_box->append(*apikey_label); + + auto apikey_entry = Gtk::make_managed(); + apikey_entry->set_hexpand(true); + auto json_apiKey = m_json.find("apiKey"); + if (json_apiKey != m_json.end()) + apikey_entry->set_text(static_cast(*json_apiKey)); + apikey_entry->signal_changed().connect([=]() { + m_json["apiKey"] = apikey_entry->get_text(); + }); + apikey_box->append(*apikey_entry); + } + + auto button_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 6); + button_box->set_halign(Gtk::Align::CENTER); + vertical_box->append(*button_box); + + auto save_button = Gtk::make_managed("Save"); + save_button->set_size_request(80, -1); + save_button->signal_clicked().connect(sigc::mem_fun(*this, &translator_dialog::on_save_button_clicked)); + button_box->append(*save_button); + + auto close_button = Gtk::make_managed("Close"); + close_button->set_size_request(80, -1); + close_button->signal_clicked().connect(sigc::mem_fun(*this, &Gtk::Dialog::close)); + button_box->append(*close_button); + + set_child(*vertical_box); +} + +void translator_dialog::configure(Gtk::Window &parent, const std::string &translator, user_config &user_config) { + auto dialog = Gtk::make_managed(parent, translator, user_config); + dialog->set_visible(); +} + +void translator_dialog::on_save_button_clicked() { + m_user_config.set_translator_settings(m_json); + m_user_config.save(); + close(); +} diff --git a/src/gui/translator_dialog.h b/src/gui/translator_dialog.h new file mode 100644 index 0000000..eb69fc2 --- /dev/null +++ b/src/gui/translator_dialog.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef TRANSLATOR_DIALOG_H +#define TRANSLATOR_DIALOG_H + +#include +#include +#include "user_config.h" + +namespace bot { + namespace gui { + class translator_dialog : public Gtk::Dialog { + public: + explicit translator_dialog(Gtk::Window &parent, const std::string &translator, user_config &user_config); + static void configure(Gtk::Window &parent, const std::string &translator, user_config &user_config); + void on_save_button_clicked(); + + private: + dpp::json m_json; + user_config& m_user_config; + }; + } +} + +#endif // TRANSLATOR_DIALOG_H diff --git a/src/gui/user_config.cpp b/src/gui/user_config.cpp new file mode 100644 index 0000000..80c8f26 --- /dev/null +++ b/src/gui/user_config.cpp @@ -0,0 +1,183 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include "user_config.h" +using namespace bot::gui; + +user_config::user_config() { + m_key_file = Glib::KeyFile::create(); + try { + m_key_file->load_from_file(get_app_config_file()); + m_is_loaded = true; + } + catch (const Glib::FileError &exception) { + m_is_loaded = false; + } + catch (const Glib::KeyFileError &exception) { + m_is_loaded = false; + } + m_is_saved = true; +} + +user_config::~user_config() { + if (!m_is_saved) + save(); +} + +const std::string user_config::get_app_config_directory() { + return Glib::build_filename(Glib::get_user_config_dir(), "dtranslatebot"); +} + +const std::string user_config::get_app_config_file() { + return Glib::build_filename(get_app_config_directory(), "dtranslatebot.conf"); +} + +const std::string user_config::get_token() const { + if (!m_key_file->has_group("Discord")) + return {}; + if (!m_key_file->has_key("Discord", "Token")) + return {}; + return m_key_file->get_string("Discord", "Token"); +} + +const std::string user_config::get_translator() const { + if (!m_key_file->has_group("Translator")) + return "stub"; + if (!m_key_file->has_key("Translator", "Type")) + return "stub"; + return m_key_file->get_string("Translator", "Type"); +} + +void user_config::get_translator_settings(const std::string &group, dpp::json &json) const { + if (m_key_file->has_key(group, "Hostname")) + json["hostname"] = m_key_file->get_string(group, "Hostname"); + if (m_key_file->has_key(group, "TLS")) + json["tls"] = m_key_file->get_boolean(group, "TLS"); + if (m_key_file->has_key(group, "Port")) + json["port"] = m_key_file->get_integer(group, "Port"); + if (m_key_file->has_key(group, "URL")) + json["url"] = m_key_file->get_string(group, "URL"); + if (m_key_file->has_key(group, "ApiKey")) + json["apiKey"] = m_key_file->get_string(group, "ApiKey"); +} + +const dpp::json user_config::get_translator_settings(const std::string &translator) const { + dpp::json json = { + {"type", translator} + }; + if (translator == "deepl") { + if (!m_key_file->has_group("DeepL")) + return json; + if (m_key_file->has_key("DeepL", "Hostname")) + json["hostname"] = m_key_file->get_string("DeepL", "Hostname"); + if (m_key_file->has_key("DeepL", "ApiKey")) + json["apiKey"] = m_key_file->get_string("DeepL", "ApiKey"); + } + else if (translator == "mozhi") { + if (!m_key_file->has_group("Mozhi")) + return json; + get_translator_settings("Mozhi", json); + if (m_key_file->has_key("Mozhi", "Engine")) + json["engine"] = m_key_file->get_string("Mozhi", "Engine"); + } + else if (translator == "libretranslate") { + if (!m_key_file->has_group("LibreTranslate")) + return json; + get_translator_settings("LibreTranslate", json); + } + else if (translator == "lingvatranslate") { + if (!m_key_file->has_group("LingvaTranslate")) + return json; + get_translator_settings("LingvaTranslate", json); + } + return json; +} + +bool user_config::save() { + auto app_config_directory = Gio::File::create_for_path(get_app_config_directory()); + if (!app_config_directory->query_exists() && !app_config_directory->make_directory_with_parents()) + return false; + if (!m_key_file->save_to_file(get_app_config_file())) + return false; + m_is_saved = true; + return true; +} + +void user_config::set_token(const std::string &token) { + m_key_file->set_string("Discord", "Token", token); + m_is_loaded = false; + m_is_saved = false; +} + +void user_config::set_translator(const std::string &translator) { + m_key_file->set_string("Translator", "Type", translator); + m_is_loaded = false; + m_is_saved = false; +} + +void user_config::set_translator_settings(const dpp::json &json) { + if (!json.is_object()) + return; + auto json_type = json.find("type"); + if (json_type == json.end()) + return; + const std::string translator = *json_type; + if (translator == "deepl") { + auto json_deepl_hostname = json.find("hostname"); + if (json_deepl_hostname != json.end()) + m_key_file->set_string("DeepL", "Hostname", static_cast(*json_deepl_hostname)); + auto json_deepl_apiKey = json.find("apiKey"); + if (json_deepl_apiKey != json.end()) + m_key_file->set_string("DeepL", "ApiKey", static_cast(*json_deepl_apiKey)); + } + else if (translator == "mozhi") { + set_translator_settings("Mozhi", json); + auto json_mozhi_engine = json.find("engine"); + if (json_mozhi_engine != json.end()) + m_key_file->set_string("Mozhi", "Engine", static_cast(*json_mozhi_engine)); + } + else if (translator == "libretranslate") { + set_translator_settings("LibreTranslate", json); + } + else if (translator == "lingvatranslate") { + set_translator_settings("LingvaTranslate", json); + } + m_is_loaded = false; + m_is_saved = false; +} + +void user_config::set_translator_settings(const std::string &group, const dpp::json &json) { + auto json_hostname = json.find("hostname"); + if (json_hostname != json.end()) + m_key_file->set_string(group, "Hostname", static_cast(*json_hostname)); + auto json_tls = json.find("tls"); + if (json_tls != json.end()) + m_key_file->set_boolean(group, "TLS", *json_tls); + auto json_port = json.find("port"); + if (json_port != json.end()) + m_key_file->set_integer(group, "Port", *json_port); + auto json_url = json.find("url"); + if (json_url != json.end()) + m_key_file->set_string(group, "URL", static_cast(*json_hostname)); + auto json_apiKey = json.find("apiKey"); + if (json_url != json.end()) + m_key_file->set_string(group, "ApiKey", static_cast(*json_apiKey)); +} diff --git a/src/gui/user_config.h b/src/gui/user_config.h new file mode 100644 index 0000000..84f9c40 --- /dev/null +++ b/src/gui/user_config.h @@ -0,0 +1,51 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef USER_CONFIG_H +#define USER_CONFIG_H + +#include +#include + +namespace bot { + namespace gui { + class user_config { + public: + explicit user_config(); + ~user_config(); + static const std::string get_app_config_directory(); + static const std::string get_app_config_file(); + const std::string get_token() const; + const std::string get_translator() const; + void get_translator_settings(const std::string &group, dpp::json &json) const; + const dpp::json get_translator_settings(const std::string &translator) const; + bool save(); + void set_token(const std::string &token); + void set_translator(const std::string &translator); + void set_translator_settings(const dpp::json &json); + void set_translator_settings(const std::string &group, const dpp::json &json); + + private: + Glib::RefPtr m_key_file; + bool m_is_loaded; + bool m_is_saved; + }; + } +} + +#endif // USER_CONFIG_H diff --git a/src/gui/user_interface.cpp b/src/gui/user_interface.cpp index 644502e..7853e5b 100644 --- a/src/gui/user_interface.cpp +++ b/src/gui/user_interface.cpp @@ -19,47 +19,75 @@ #include #include #include -#include +#include #include #include +#include +#include #include #include #include "../core/regex.h" +#include "translator_dialog.h" #include "user_interface.h" using namespace bot::gui; +const char* translators[] = {"stub", "libretranslate", "lingvatranslate", "mozhi", "deepl"}; + user_interface::user_interface() { set_title("dtranslatebot"); set_default_size(500, 0); set_resizable(false); - auto vertical_box = Gtk::make_managed(Gtk::Orientation::VERTICAL); + auto vertical_box = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); vertical_box->set_margin(6); - vertical_box->set_spacing(6); - auto token_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); - token_box->set_spacing(6); + auto token_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 6); vertical_box->append(*token_box); auto token_label = Gtk::make_managed("Discord Bot Token:"); token_box->append(*token_label); m_token_entry = Gtk::make_managed(); - m_token_entry->set_show_peek_icon(true); m_token_entry->set_hexpand(true); + m_token_entry->set_show_peek_icon(true); + m_token_entry->signal_changed().connect(sigc::mem_fun(*this, &user_interface::on_token_entry_changed)); token_box->append(*m_token_entry); - auto log_textview = Gtk::make_managed(); - log_textview->set_size_request(-1, 300); - log_textview->set_editable(false); - log_textview->set_monospace(true); - log_textview->set_wrap_mode(Gtk::WrapMode::WORD_CHAR); - m_log = log_textview->get_buffer(); - vertical_box->append(*log_textview); + auto translator_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 6); + vertical_box->append(*translator_box); - auto button_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); - button_box->set_spacing(6); + auto translator_label = Gtk::make_managed("Translator:"); + translator_box->append(*translator_label); + + auto translator_list = Gtk::StringList::create({"Stub", "LibreTranslate", "Lingva Translate", "Mozhi", "DeepL"}); + m_translator_dropdown = Gtk::make_managed(translator_list); + m_translator_dropdown->property_selected().signal_changed().connect(sigc::mem_fun(*this, &user_interface::on_translator_dropdown_changed)); + translator_box->append(*m_translator_dropdown); + + auto translator_spacer = Gtk::make_managed(); + translator_spacer->set_hexpand(true); + translator_box->append(*translator_spacer); + + m_translator_configure_button = Gtk::make_managed("Configure..."); + m_translator_configure_button->set_size_request(80, -1); + m_translator_configure_button->set_sensitive(false); + m_translator_configure_button->signal_clicked().connect(sigc::mem_fun(*this, &user_interface::on_translator_configure_pressed)); + translator_box->append(*m_translator_configure_button); + + auto log_scrolledwindow = Gtk::make_managed(); + log_scrolledwindow->set_size_request(-1, 300); + log_scrolledwindow->set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC); + vertical_box->append(*log_scrolledwindow); + + m_log_textview = Gtk::make_managed(); + m_log_textview->set_editable(false); + m_log_textview->set_monospace(true); + m_log_textview->set_wrap_mode(Gtk::WrapMode::WORD_CHAR); + m_log = m_log_textview->get_buffer(); + log_scrolledwindow->set_child(*m_log_textview); + + auto button_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 6); button_box->set_halign(Gtk::Align::CENTER); vertical_box->append(*button_box); @@ -75,20 +103,22 @@ user_interface::user_interface() m_stop_button->signal_clicked().connect(sigc::mem_fun(*this, &user_interface::terminate)); button_box->append(*m_stop_button); - m_token_entry->signal_changed().connect([=]{ - bool token_valid = bot::regex::verify_discord_bot_token(m_token_entry->get_text()); - m_start_button->set_sensitive(!m_bot.is_running() ? token_valid : false); - }); + const std::string token = m_user_config.get_token(); + m_token_entry->set_text(token); - set_child(*vertical_box); + const std::string translator = m_user_config.get_translator(); + for (guint i = 0; i < sizeof(translators); i++) { + if (translators[i] != translator) + continue; + m_translator_dropdown->set_selected(i); + break; + } m_log_callback = std::bind(&user_interface::log_append, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); m_bot.log_callback_add(m_log_callback); - m_log_dispatcher.connect([=](){ - const std::lock_guard guard(m_log_buffer_mutex); - m_log->insert(m_log->end(), m_log->begin() != m_log->end() ? m_log_buffer : m_log_buffer.substr(1)); - m_log_buffer.clear(); - }); + m_log_dispatcher.connect(sigc::mem_fun(*this, &user_interface::on_log_dispatched)); + + set_child(*vertical_box); } void user_interface::log_append(const std::string &message, const std::string &type, bool is_error) { @@ -97,13 +127,26 @@ void user_interface::log_append(const std::string &message, const std::string &t m_log_dispatcher.emit(); } +void user_interface::log_scroll_down() { + m_log->place_cursor(m_log->end()); + m_log_textview->scroll_to(m_log->get_insert(), 0, 1, 1); +} + void user_interface::run() { + const std::string token = m_token_entry->get_text(); + const std::string translator = translators[m_translator_dropdown->get_selected()]; + m_user_config.set_token(token); + m_user_config.set_translator(translator); + m_user_config.save(); auto settings = std::make_shared(); - if (settings->process({{"token", m_token_entry->get_text()}, {"translator", {{"type", "stub"}}}}, m_log_callback)) { + if (settings->process({{"token", token}, {"translator", m_user_config.get_translator_settings(translator)}}, m_log_callback)) { try { m_bot.run(settings, true, false); m_start_button->set_sensitive(false); m_stop_button->set_sensitive(true); + m_token_entry->set_sensitive(false); + m_translator_configure_button->set_sensitive(false); + m_translator_dropdown->set_sensitive(false); } catch (const std::exception &exception) { auto alert_dialog = Gtk::AlertDialog::create(exception.what()); @@ -124,4 +167,29 @@ void user_interface::terminate() { bool token_valid = bot::regex::verify_discord_bot_token(m_token_entry->get_text()); m_start_button->set_sensitive(token_valid); m_stop_button->set_sensitive(false); + m_token_entry->set_sensitive(true); + bool translator_configureable = m_translator_dropdown->get_selected() > 0; + m_translator_configure_button->set_sensitive(translator_configureable); + m_translator_dropdown->set_sensitive(true); +} + +void user_interface::on_log_dispatched() { + const std::lock_guard guard(m_log_buffer_mutex); + m_log->insert(m_log->end(), m_log->begin() != m_log->end() ? m_log_buffer : m_log_buffer.substr(1)); + m_log_buffer.clear(); + Glib::signal_idle().connect_once(sigc::mem_fun(*this, &user_interface::log_scroll_down)); +} + +void user_interface::on_token_entry_changed() { + bool token_valid = bot::regex::verify_discord_bot_token(m_token_entry->get_text()); + m_start_button->set_sensitive(!m_bot.is_running() ? token_valid : false); +} + +void user_interface::on_translator_configure_pressed() { + translator_dialog::configure(*this, translators[m_translator_dropdown->get_selected()], m_user_config); +} + +void user_interface::on_translator_dropdown_changed() { + bool translator_configureable = m_translator_dropdown->get_selected() > 0; + m_translator_configure_button->set_sensitive(translator_configureable); } diff --git a/src/gui/user_interface.h b/src/gui/user_interface.h index 4858997..b1d09e3 100644 --- a/src/gui/user_interface.h +++ b/src/gui/user_interface.h @@ -18,11 +18,13 @@ #include #include +#include #include -#include +#include #include #include #include "../core/discord_bot.h" +#include "user_config.h" namespace bot { namespace gui { @@ -30,19 +32,28 @@ namespace bot { public: explicit user_interface(); void log_append(const std::string &message, const std::string &type = "Log", bool is_error = false); + void log_scroll_down(); void run(); void terminate(); + void on_log_dispatched(); + void on_token_entry_changed(); + void on_translator_configure_pressed(); + void on_translator_dropdown_changed(); private: bot::discord_bot m_bot; Glib::RefPtr m_log; - Gtk::Button* m_start_button; - Gtk::Button* m_stop_button; - Gtk::PasswordEntry* m_token_entry; std::string m_log_buffer; Glib::Dispatcher m_log_dispatcher; std::mutex m_log_buffer_mutex; bot::log::log_message_callback m_log_callback; + Gtk::TextView* m_log_textview; + Gtk::Button* m_start_button; + Gtk::Button* m_stop_button; + Gtk::PasswordEntry* m_token_entry; + Gtk::Button* m_translator_configure_button; + Gtk::DropDown* m_translator_dropdown; + user_config m_user_config; }; } }