diff --git a/include/aoapplication.h b/include/aoapplication.h index f1ba9d7..52b7673 100644 --- a/include/aoapplication.h +++ b/include/aoapplication.h @@ -131,11 +131,14 @@ public: /////////////////////////////////////////// - void set_favorite_list(); + void load_favorite_list(); + void save_favorite_list(); QVector &get_favorite_list() { return favorite_list; } // Adds the server to favorite_servers.ini void add_favorite_server(int p_server); + void remove_favorite_server(int p_server); + void prompt_remove_favorite_server(int p_server); void set_server_list(QVector &servers) { server_list = servers; } QVector &get_server_list() { return server_list; } @@ -331,13 +334,24 @@ public: // Append to the currently open demo file if there is one void append_to_demofile(QString packet_string); - // Returns the contents of serverlist.txt - QVector read_serverlist_txt(); + /** + * @brief Reads favorite_servers.ini and returns a list of servers. + * + * The demo server entry is always present at the top of the list. + * + * If the server list returned was to be empty (exluding the demo server entry), + * will return a list of servers from the legacy serverlist.txt file. + * + * @return A list of servers. + */ + QVector read_favorite_servers(); /** - * @brief Migrates the favorite serverlist format from txt to ini. + * @brief Reads serverlist.txt and returns a list of servers. + * + * @return A list of servers. */ - void migrate_serverlist_txt(QFile &p_serverlist_txt); + QVector read_legacy_favorite_servers(); // Returns the value of p_identifier in the design.ini file in p_design_path QString read_design_ini(QString p_identifier, VPath p_design_path); diff --git a/include/lobby.h b/include/lobby.h index 18d4c4e..d39d7f9 100644 --- a/include/lobby.h +++ b/include/lobby.h @@ -67,6 +67,7 @@ private: AOButton *ui_refresh; AOButton *ui_add_to_fav; + AOButton *ui_remove_from_fav; AOButton *ui_connect; QLabel *ui_version; @@ -99,12 +100,15 @@ private slots: void on_refresh_released(); void on_add_to_fav_pressed(); void on_add_to_fav_released(); + void on_remove_from_fav_pressed(); + void on_remove_from_fav_released(); void on_connect_pressed(); void on_connect_released(); void on_about_clicked(); void on_settings_clicked(); void on_server_list_clicked(QTreeWidgetItem *p_item, int column); void on_server_list_doubleclicked(QTreeWidgetItem *p_item, int column); + void on_server_list_context_menu_requested(const QPoint &point); void on_server_search_edited(QString p_text); }; diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp index 25ec787..d52fc8b 100644 --- a/src/aoapplication.cpp +++ b/src/aoapplication.cpp @@ -51,6 +51,7 @@ void AOApplication::construct_lobby() return; } + load_favorite_list(); w_lobby = new Lobby(this); lobby_constructed = true; @@ -125,9 +126,34 @@ QString AOApplication::get_version_string() void AOApplication::reload_theme() { current_theme = read_theme(); } -void AOApplication::set_favorite_list() +void AOApplication::load_favorite_list() { - favorite_list = read_serverlist_txt(); + favorite_list = read_favorite_servers(); +} + +void AOApplication::save_favorite_list() +{ + QSettings favorite_servers_ini(get_base_path() + "favorite_servers.ini", QSettings::IniFormat); + favorite_servers_ini.setIniCodec("UTF-8"); + + favorite_servers_ini.clear(); + // skip demo server entry, demo server entry is always at index 0 + for(int i = 1; i < favorite_list.size(); ++i) { + auto fav_server = favorite_list.at(i); + favorite_servers_ini.beginGroup(QString::number(i)); + favorite_servers_ini.setValue("name", fav_server.name); + favorite_servers_ini.setValue("address", fav_server.ip); + favorite_servers_ini.setValue("port", fav_server.port); + favorite_servers_ini.setValue("desc", fav_server.desc); + + if (fav_server.socket_type == TCP) { + favorite_servers_ini.setValue("protocol", "tcp"); + } else { + favorite_servers_ini.setValue("protocol", "ws"); + } + favorite_servers_ini.endGroup(); + } + favorite_servers_ini.sync(); } QString AOApplication::get_current_char() @@ -142,25 +168,21 @@ void AOApplication::add_favorite_server(int p_server) { if (p_server < 0 || p_server >= server_list.size()) return; + favorite_list.append(server_list.at(p_server)); + save_favorite_list(); +} - server_type fav_server = server_list.at(p_server); - QSettings l_favorite_ini(get_base_path() + "favorite_servers.ini", QSettings::IniFormat); - QString l_new_group = QString::number(l_favorite_ini.childGroups().size()); - l_favorite_ini.setIniCodec("UTF-8"); +void AOApplication::remove_favorite_server(int p_server) +{ + if (p_server < 0 || p_server >= favorite_list.size()) + return; + favorite_list.removeAt(p_server); + save_favorite_list(); +} - l_favorite_ini.beginGroup(l_new_group); - l_favorite_ini.setValue("name", fav_server.name); - l_favorite_ini.setValue("address", fav_server.ip); - l_favorite_ini.setValue("port", fav_server.port); - l_favorite_ini.setValue("desc", fav_server.desc); +void AOApplication::prompt_remove_favorite_server(int p_server) +{ - if (fav_server.socket_type == TCP) { - l_favorite_ini.setValue("protocol", "tcp"); - } - else { - l_favorite_ini.setValue("protocol", "ws"); - } - l_favorite_ini.sync(); } void AOApplication::server_disconnected() diff --git a/src/lobby.cpp b/src/lobby.cpp index 8fb8e5d..6f3486b 100644 --- a/src/lobby.cpp +++ b/src/lobby.cpp @@ -6,14 +6,14 @@ #include "demoserver.h" #include "networkmanager.h" +#include #include +#include Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() { ao_app = p_ao_app; - - // this->setWindowTitle(tr("Attorney Online %1").arg(ao_app->applicationVersion())); this->setWindowIcon(QIcon(":/logo.png")); this->setWindowFlags( (this->windowFlags() | Qt::CustomizeWindowHint) & ~Qt::WindowMaximizeButtonHint); @@ -28,6 +28,9 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() ui_refresh->setObjectName("ui_refresh"); ui_add_to_fav = new AOButton(this, ao_app); ui_add_to_fav->setObjectName("ui_add_to_fav"); + ui_remove_from_fav = new AOButton(this, ao_app); + ui_remove_from_fav->setObjectName("ui_remove_from_fav"); + ui_remove_from_fav->hide(); ui_connect = new AOButton(this, ao_app); ui_connect->setObjectName("ui_connect"); ui_version = new QLabel(this); @@ -45,6 +48,7 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() ui_server_list->setColumnWidth(0, 0); ui_server_list->setIndentation(0); ui_server_list->setObjectName("ui_server_list"); + ui_server_list->setContextMenuPolicy(Qt::CustomContextMenu); ui_server_search = new QLineEdit(this); ui_server_search->setFrame(false); @@ -79,6 +83,10 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() &Lobby::on_add_to_fav_pressed); connect(ui_add_to_fav, &AOButton::released, this, &Lobby::on_add_to_fav_released); + connect(ui_remove_from_fav, &AOButton::pressed, this, + &Lobby::on_remove_from_fav_pressed); + connect(ui_remove_from_fav, &AOButton::released, this, + &Lobby::on_remove_from_fav_released); connect(ui_connect, &AOButton::pressed, this, &Lobby::on_connect_pressed); connect(ui_connect, &AOButton::released, this, &Lobby::on_connect_released); connect(ui_about, &AOButton::clicked, this, &Lobby::on_about_clicked); @@ -87,6 +95,8 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() &Lobby::on_server_list_clicked); connect(ui_server_list, &QTreeWidget::itemDoubleClicked, this, &Lobby::on_server_list_doubleclicked); + connect(ui_server_list, &QTreeWidget::customContextMenuRequested, this, + &Lobby::on_server_list_context_menu_requested); connect(ui_server_search, &QLineEdit::textChanged, this, &Lobby::on_server_search_edited); connect(ui_cancel, &AOButton::clicked, ao_app, &AOApplication::loading_cancelled); @@ -145,6 +155,9 @@ void Lobby::set_widgets() set_size_and_pos(ui_add_to_fav, "add_to_fav"); ui_add_to_fav->set_image("addtofav"); + set_size_and_pos(ui_remove_from_fav, "remove_from_fav"); + ui_remove_from_fav->set_image("removefromfav"); + set_size_and_pos(ui_connect, "connect"); ui_connect->set_image("connect"); @@ -300,6 +313,8 @@ void Lobby::on_public_servers_clicked() { ui_public_servers->set_image("publicservers_selected"); ui_favorites->set_image("favorites"); + ui_add_to_fav->show(); + ui_remove_from_fav->hide(); reset_selection(); @@ -310,12 +325,14 @@ void Lobby::on_public_servers_clicked() void Lobby::on_favorites_clicked() { - ui_favorites->set_image("favorites_selected"); ui_public_servers->set_image("publicservers"); + ui_favorites->set_image("favorites_selected"); + ui_add_to_fav->hide(); + ui_remove_from_fav->show(); reset_selection(); - ao_app->set_favorite_list(); + ao_app->load_favorite_list(); public_servers_selected = false; @@ -342,7 +359,7 @@ void Lobby::on_refresh_released() ao_app->net_manager->get_server_list(std::bind(&Lobby::list_servers, this)); get_motd(); } else { - ao_app->set_favorite_list(); + ao_app->load_favorite_list(); list_favorites(); } } @@ -363,6 +380,25 @@ void Lobby::on_add_to_fav_released() } } +void Lobby::on_remove_from_fav_pressed() +{ + ui_remove_from_fav->set_image("removefromfav_pressed"); +} + +void Lobby::on_remove_from_fav_released() +{ + ui_remove_from_fav->set_image("removefromfav"); + if (public_servers_selected) { + return; + } + + int selection = get_selected_server(); + if (selection > 0) { + ao_app->remove_favorite_server(selection); + list_favorites(); + } +} + void Lobby::on_connect_pressed() { ui_connect->set_image("connect_pressed"); } void Lobby::on_connect_released() @@ -479,6 +515,31 @@ void Lobby::on_server_list_doubleclicked(QTreeWidgetItem *p_item, int column) //on_connect_released(); } +void Lobby::on_server_list_context_menu_requested(const QPoint &point) +{ + if (public_servers_selected) { + return; + } + + auto *item = ui_server_list->itemAt(point); + if (item == nullptr) { + qInfo() << "no favorite server item; skipping context menu"; + return; + } + const int server_index = item->data(0, Qt::DisplayRole).toInt(); + if (server_index == 0) { + qInfo() << "demo server has no context menu to display"; + return; + } + + auto *menu = new QMenu(this); + menu->addAction(tr("Remove"), ao_app, [this,server_index](){ + ao_app->remove_favorite_server(server_index); + list_favorites(); + }); + menu->popup(ui_server_list->mapToGlobal(point)); +} + void Lobby::on_server_search_edited(QString p_text) { // Iterate through all QTreeWidgetItem items diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index 908bbce..d9c8162 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -243,78 +243,97 @@ bool AOApplication::append_to_file(QString p_text, QString p_file, return false; } -QVector AOApplication::read_serverlist_txt() +QVector AOApplication::read_favorite_servers() { - QVector f_server_list; - - QFile serverlist_txt(get_base_path() + "serverlist.txt"); - QFile serverlist_ini(get_base_path() + "favorite_servers.ini"); - - if (serverlist_txt.exists() && !serverlist_ini.exists()) { - migrate_serverlist_txt(serverlist_txt); - } - - if (serverlist_ini.exists()) { - QSettings l_favorite_ini(get_base_path() + "favorite_servers.ini", QSettings::IniFormat); - l_favorite_ini.setIniCodec("UTF-8"); - for(QString &fav_index: l_favorite_ini.childGroups()) { - server_type f_server; - l_favorite_ini.beginGroup(fav_index); - f_server.ip = l_favorite_ini.value("address", "127.0.0.1").toString(); - f_server.port = l_favorite_ini.value("port", 27016).toInt(); - f_server.name = l_favorite_ini.value("name", "Missing Name").toString(); - f_server.desc = l_favorite_ini.value("desc", "No description").toString(); - f_server.socket_type = to_connection_type.value(l_favorite_ini.value("protocol", "tcp").toString()); - f_server_list.append(f_server); - l_favorite_ini.endGroup(); - } - } + QVector serverlist; + // demo server is always at the top server_type demo_server; demo_server.ip = "127.0.0.1"; demo_server.port = 99999; demo_server.name = tr("Demo playback"); demo_server.desc = tr("Play back demos you have previously recorded"); - f_server_list.append(demo_server); + serverlist.append(demo_server); - return f_server_list; + QString fav_servers_ini_path(get_base_path() + "favorite_servers.ini"); + if (!QFile::exists(fav_servers_ini_path)) { + qWarning() << "failed to locate favorite_servers.ini, falling back to legacy serverlist.txt"; + serverlist += read_legacy_favorite_servers(); + } + else { + QSettings fav_servers_ini(fav_servers_ini_path, QSettings::IniFormat); + fav_servers_ini.setIniCodec("UTF-8"); + + auto grouplist = fav_servers_ini.childGroups(); + { // remove all negative and non-numbers + auto filtered_grouplist = grouplist; + for (const QString &group : qAsConst(grouplist)) { + bool ok = false; + const int l_num = group.toInt(&ok); + if (ok && l_num >= 0) { + continue; + } + filtered_grouplist.append(group); + } + std::sort(filtered_grouplist.begin(), filtered_grouplist.end(), [](const auto &a, const auto &b) -> bool { + return a.toInt() < b.toInt(); + }); + grouplist = std::move(filtered_grouplist); + } + + for(const QString &group: qAsConst(grouplist)) { + server_type f_server; + fav_servers_ini.beginGroup(group); + f_server.ip = fav_servers_ini.value("address", "127.0.0.1").toString(); + f_server.port = fav_servers_ini.value("port", 27016).toInt(); + f_server.name = fav_servers_ini.value("name", "Missing Name").toString(); + f_server.desc = fav_servers_ini.value("desc", "No description").toString(); + f_server.socket_type = to_connection_type.value(fav_servers_ini.value("protocol", "tcp").toString()); + serverlist.append(std::move(f_server)); + fav_servers_ini.endGroup(); + } + } + + return serverlist; } -void AOApplication::migrate_serverlist_txt(QFile &p_serverlist_txt) +QVector AOApplication::read_legacy_favorite_servers() { - // We migrate our legacy serverlist.txt to a QSettings object. - // Then we write it to disk. - QSettings l_settings(get_base_path() + "favorite_servers.ini", QSettings::IniFormat); - l_settings.setIniCodec("UTF-8"); - if (p_serverlist_txt.open(QIODevice::ReadOnly)) { - QTextStream l_favorite_textstream(&p_serverlist_txt); - l_favorite_textstream.setCodec("UTF-8"); - int l_entry_index = 0; + QVector serverlist; - while (!l_favorite_textstream.atEnd()) { - QString l_favorite_line = l_favorite_textstream.readLine(); - QStringList l_line_contents = l_favorite_line.split(":"); + QFile serverlist_txt(get_base_path() + "serverlist.txt"); + if (!serverlist_txt.exists()) { + qWarning() << "serverlist.txt does not exist"; + } else if (!serverlist_txt.open(QIODevice::ReadOnly)) { + qWarning() << "failed to open serverlist.txt"; + } else { + QTextStream stream(&serverlist_txt); + stream.setCodec("UTF-8"); - if (l_line_contents.size() >= 3) { - l_settings.beginGroup(QString::number(l_entry_index)); - l_settings.setValue("name", l_line_contents.at(2)); - l_settings.setValue("address", l_line_contents.at(0)); - l_settings.setValue("port", l_line_contents.at(1)); + while (!stream.atEnd()) + { + QStringList contents = stream.readLine().split(":"); - if (l_line_contents.size() >= 4) { - l_settings.setValue("protocol", l_line_contents.at(3)); - } - else { - l_settings.setValue("protocol","tcp"); - } - l_settings.endGroup(); - l_entry_index++; + int item_count = contents.size(); + if (item_count < 3 || item_count > 4) { + continue; } + + server_type server; + server.ip = contents.at(0); + server.port = contents.at(1).toInt(); + server.name = contents.at(2); + if (item_count == 4) { + server.socket_type = connection_type(contents.at(3).toInt()); + } else { + server.socket_type = TCP; + } + serverlist.append(std::move(server)); } - l_settings.sync(); + serverlist_txt.close(); } - p_serverlist_txt.close(); - p_serverlist_txt.rename(get_base_path() + "serverlist_deprecated.txt"); + + return serverlist; } QString AOApplication::read_design_ini(QString p_identifier,