diff options
author | Dylam De La Torre <DyXel04@gmail.com> | 2021-11-23 05:21:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-23 04:21:56 +0000 |
commit | a51a54bc5979a2491f152abc47ad54e6b63f27c8 (patch) | |
tree | ce67092b2f6df366033a65a6111e4650866766b2 /windows | |
parent | d88079000a79e6bcbe51c5a2868d57b303b5fcb6 (diff) | |
download | abaddon-portaudio-a51a54bc5979a2491f152abc47ad54e6b63f27c8.tar.gz abaddon-portaudio-a51a54bc5979a2491f152abc47ad54e6b63f27c8.zip |
Restructure source and resource files (#46)
importantly, res is now res/res and css is now res/css
Diffstat (limited to 'windows')
30 files changed, 0 insertions, 4116 deletions
diff --git a/windows/guildsettings/auditlogpane.cpp b/windows/guildsettings/auditlogpane.cpp deleted file mode 100644 index 08f99da..0000000 --- a/windows/guildsettings/auditlogpane.cpp +++ /dev/null @@ -1,636 +0,0 @@ -#include "auditlogpane.hpp" -#include "abaddon.hpp" - -using namespace std::string_literals; - -GuildSettingsAuditLogPane::GuildSettingsAuditLogPane(Snowflake id) - : GuildID(id) { - signal_map().connect(sigc::mem_fun(*this, &GuildSettingsAuditLogPane::OnMap)); - set_name("guild-audit-log-pane"); - set_hexpand(true); - set_vexpand(true); - - m_list.set_selection_mode(Gtk::SELECTION_NONE); - m_list.show(); - add(m_list); -} - -void GuildSettingsAuditLogPane::OnMap() { - if (m_requested) return; - m_requested = true; - - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - - if (discord.HasGuildPermission(self_id, GuildID, Permission::VIEW_AUDIT_LOG)) - discord.FetchAuditLog(GuildID, sigc::mem_fun(*this, &GuildSettingsAuditLogPane::OnAuditLogFetch)); -} - -void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) { - auto &discord = Abaddon::Get().GetDiscordClient(); - auto guild = *discord.GetGuild(GuildID); - for (const auto &entry : data.Entries) { - if (entry.TargetID == "") continue; - - auto expander = Gtk::manage(new Gtk::Expander); - auto label = Gtk::manage(new Gtk::Label); - label->set_ellipsize(Pango::ELLIPSIZE_END); - - Glib::ustring user_markup = "<b>Unknown User</b>"; - if (entry.UserID.has_value()) { - if (auto user = discord.GetUser(*entry.UserID); user.has_value()) - user_markup = discord.GetUser(*entry.UserID)->GetEscapedBoldString<false>(); - } - - // spaghetti moment - Glib::ustring markup; - std::vector<Glib::ustring> extra_markup; - switch (entry.Type) { - case AuditLogActionType::GUILD_UPDATE: { - markup = - user_markup + - " made changes to <b>" + - Glib::Markup::escape_text(guild.Name) + - "</b>"; - - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "icon_hash") { - extra_markup.push_back("Set the server icon"); - } else if (change.Key == "name") { - auto new_name = change.NewValue; - if (new_name.has_value()) - extra_markup.push_back("Set the server name to <b>" + - Glib::Markup::escape_text(new_name->get<std::string>()) + - "</b>"); - else - extra_markup.push_back("Set the server name"); - } - } - } break; - case AuditLogActionType::CHANNEL_CREATE: { - const auto type = *entry.GetNewFromKey<ChannelType>("type"); - markup = user_markup + - " created a " + (type == ChannelType::GUILD_VOICE ? "voice" : "text") + - " channel <b>#" + - Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) + - "</b>"; - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "name" && change.NewValue.has_value()) - extra_markup.push_back("Set the name to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - else if (change.Key == "nsfw" && change.NewValue.has_value()) - extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") + - " the channel as NSFW"s); - } - - } break; - case AuditLogActionType::CHANNEL_UPDATE: { - const auto target_channel = discord.GetChannel(entry.TargetID); - if (target_channel.has_value()) { - markup = user_markup + - " made changes to <b>#" + - Glib::Markup::escape_text(*target_channel->Name) + - "</b>"; - } else { - markup = user_markup + - " made changes to <b><#" + - entry.TargetID + - "></b>"; - } - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "name" && change.NewValue.has_value()) { - if (change.OldValue.has_value()) - extra_markup.push_back("Changed the name from <b>" + - Glib::Markup::escape_text(change.OldValue->get<std::string>()) + - "</b> to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - else - extra_markup.push_back("Changed the name to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } else if (change.Key == "topic") { - if (change.NewValue.has_value()) - extra_markup.push_back("Changed the topic to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - else - extra_markup.push_back("Cleared the topic"); - } else if (change.Key == "nsfw" && change.NewValue.has_value()) { - extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") + " the channel as NSFW"s); - } else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) { - const int secs = change.NewValue->get<int>(); - if (secs == 0) - extra_markup.push_back("Disabled slowmode"); - else - extra_markup.push_back("Set slowmode to <b>" + - std::to_string(secs) + " seconds</b>"); - } - } - } break; - case AuditLogActionType::CHANNEL_DELETE: { - markup = user_markup + - " removed <b>#" + - Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + - "</b>"; - } break; - case AuditLogActionType::CHANNEL_OVERWRITE_CREATE: { - const auto channel = discord.GetChannel(entry.TargetID); - if (channel.has_value()) { - markup = user_markup + - " created channel overrides for <b>#" + - Glib::Markup::escape_text(*channel->Name) + "</b>"; - } else { - markup = user_markup + - " created channel overrides for <b><#" + - entry.TargetID + "></b>"; - } - } break; - case AuditLogActionType::CHANNEL_OVERWRITE_UPDATE: { - const auto channel = discord.GetChannel(entry.TargetID); - if (channel.has_value()) { - markup = user_markup + - " updated channel overrides for <b>#" + - Glib::Markup::escape_text(*channel->Name) + "</b>"; - } else { - markup = user_markup + - " updated channel overrides for <b><#" + - entry.TargetID + "></b>"; - } - } break; - case AuditLogActionType::CHANNEL_OVERWRITE_DELETE: { - const auto channel = discord.GetChannel(entry.TargetID); - if (channel.has_value()) { - markup = user_markup + - " removed channel overrides for <b>#" + - Glib::Markup::escape_text(*channel->Name) + "</b>"; - } else { - markup = user_markup + - " removed channel overrides for <b><#" + - entry.TargetID + "></b>"; - } - } break; - case AuditLogActionType::MEMBER_KICK: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " kicked <b>" + - target_user->GetEscapedString() + - "</b>"; - } break; - case AuditLogActionType::MEMBER_PRUNE: { - markup = user_markup + - " pruned <b>" + - *entry.Options->MembersRemoved + - "</b> members"; - extra_markup.push_back("For <b>" + - *entry.Options->DeleteMemberDays + - " days</b> of inactivity"); - } break; - case AuditLogActionType::MEMBER_BAN_ADD: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " banned <b>" + - target_user->GetEscapedString() + - "</b>"; - } break; - case AuditLogActionType::MEMBER_BAN_REMOVE: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " removed the ban for <b>" + - target_user->GetEscapedString() + - "</b>"; - } break; - case AuditLogActionType::MEMBER_UPDATE: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " updated <b>" + - target_user->GetEscapedString() + - "</b>"; - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "deaf" && change.NewValue.has_value()) - extra_markup.push_back( - (change.NewValue->get<bool>() ? "<b>Deafened</b>"s : "<b>Undeafened</b>"s) + - " them"); - else if (change.Key == "mute" && change.NewValue.has_value()) - extra_markup.push_back( - (change.NewValue->get<bool>() ? "<b>Muted</b>"s : "<b>Unmuted</b>"s) + - " them"); - else if (change.Key == "nick" && change.NewValue.has_value()) - extra_markup.push_back("Set their nickname to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } - } break; - case AuditLogActionType::MEMBER_ROLE_UPDATE: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " updated roles for <b>" + - target_user->GetEscapedString() + "</b>"; - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "$remove" && change.NewValue.has_value()) { - extra_markup.push_back("<b>Removed</b> a role <b>" + - Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get<std::string>()) + - "</b>"); - } else if (change.Key == "$add" && change.NewValue.has_value()) { - extra_markup.push_back("<b>Added</b> a role <b>" + - Glib::Markup::escape_text(change.NewValue.value()[0].at("name").get<std::string>()) + - "</b>"); - } - } - } break; - case AuditLogActionType::MEMBER_MOVE: { - const auto channel = discord.GetChannel(*entry.Options->ChannelID); - markup = user_markup + - " moved <b>" + - *entry.Options->Count + - " user" + - (*entry.Options->Count == "1" ? ""s : "s"s) + - "</b> to <b>" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } break; - case AuditLogActionType::MEMBER_DISCONNECT: { - markup = user_markup + - " disconnected <b>" + - *entry.Options->Count + - "</b> users from voice"; - } break; - case AuditLogActionType::BOT_ADD: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " added <b>" + - target_user->GetEscapedString() + - "</b> to the server"; - } break; - case AuditLogActionType::ROLE_CREATE: { - markup = user_markup + - " created the role <b>" + - *entry.GetNewFromKey<std::string>("name") + - "</b>"; - } break; - case AuditLogActionType::ROLE_UPDATE: { - const auto role = discord.GetRole(entry.TargetID); - markup = user_markup + - " updated the role <b>" + - (role.has_value() ? Glib::Markup::escape_text(role->Name) : Glib::ustring(entry.TargetID)) + - "</b>"; - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "name" && change.NewValue.has_value()) { - extra_markup.push_back("Changed the name to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } else if (change.Key == "color" && change.NewValue.has_value()) { - const auto col = change.NewValue->get<int>(); - if (col == 0) - extra_markup.push_back("Removed the color"); - else - extra_markup.push_back("Set the color to <b>" + - IntToCSSColor(col) + - "</b>"); - } else if (change.Key == "permissions") { - extra_markup.push_back("Updated the permissions"); - } else if (change.Key == "mentionable" && change.NewValue.has_value()) { - extra_markup.push_back(change.NewValue->get<bool>() ? "Mentionable" : "Not mentionable"); - } else if (change.Key == "hoist" && change.NewValue.has_value()) { - extra_markup.push_back(change.NewValue->get<bool>() ? "Not hoisted" : "Hoisted"); - } - } - } break; - case AuditLogActionType::ROLE_DELETE: { - markup = user_markup + - " deleted the role <b>" + - *entry.GetOldFromKey<std::string>("name") + - "</b>"; - } break; - case AuditLogActionType::INVITE_CREATE: { - const auto code = *entry.GetNewFromKey<std::string>("code"); - markup = user_markup + - " created an invite <b>" + code + "</b>"; - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "channel_id" && change.NewValue.has_value()) { - const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>()); - if (!channel.has_value()) continue; - extra_markup.push_back("For channel <b>#" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"); - } else if (change.Key == "max_uses" && change.NewValue.has_value()) { - const auto uses = change.NewValue->get<int>(); - if (uses == 0) - extra_markup.push_back("Which has <b>unlimited</b> uses"); - else - extra_markup.push_back("Which has <b>" + std::to_string(uses) + "</b> uses"); - } else if (change.Key == "temporary" && change.NewValue.has_value()) { - extra_markup.push_back("With temporary <b>"s + - (change.NewValue->get<bool>() ? "on" : "off") + - "</b>"); - } // no max_age cuz fuck time - } - } break; - case AuditLogActionType::INVITE_DELETE: { - markup = user_markup + - " deleted an invite <b>" + - *entry.GetOldFromKey<std::string>("code") + - "</b>"; - } break; - case AuditLogActionType::WEBHOOK_CREATE: { - markup = user_markup + - " created the webhook <b>" + - Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) + - "</b>"; - for (const auto &change : *entry.Changes) { - if (change.Key == "channel_id" && change.NewValue.has_value()) { - const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>()); - if (channel.has_value()) { - extra_markup.push_back("With channel <b>#" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"); - } - } - } - } break; - case AuditLogActionType::WEBHOOK_UPDATE: { - const WebhookData *webhookptr = nullptr; - for (const auto &webhook : data.Webhooks) { - if (webhook.ID == entry.TargetID) - webhookptr = &webhook; - } - if (webhookptr != nullptr) { - markup = user_markup + - " updated the webhook <b>" + - Glib::Markup::escape_text(webhookptr->Name) + - "</b>"; - } else { - markup = user_markup + - " updated a webhook"; - } - if (entry.Changes.has_value()) - for (const auto &change : *entry.Changes) { - if (change.Key == "name" && change.NewValue.has_value()) { - extra_markup.push_back("Changed the name to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } else if (change.Key == "avatar_hash") { - extra_markup.push_back("Changed the avatar"); - } else if (change.Key == "channel_id" && change.NewValue.has_value()) { - const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>()); - if (channel.has_value()) { - extra_markup.push_back("Changed the channel to <b>#" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"); - } else { - extra_markup.push_back("Changed the channel"); - } - } - } - } break; - case AuditLogActionType::WEBHOOK_DELETE: { - markup = user_markup + - " deleted the webhook <b>" + - Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + - "</b>"; - } break; - case AuditLogActionType::EMOJI_CREATE: { - markup = user_markup + - " created the emoji <b>" + - Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) + - "</b>"; - } break; - case AuditLogActionType::EMOJI_UPDATE: { - markup = user_markup + - " updated the emoji <b>" + - Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + - "</b>"; - extra_markup.push_back("Changed the name from <b>" + - Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + - "</b> to <b>" + - Glib::Markup::escape_text(*entry.GetNewFromKey<std::string>("name")) + - "</b>"); - } break; - case AuditLogActionType::EMOJI_DELETE: { - markup = user_markup + - " deleted the emoji <b>" + - Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + - "</b>"; - } break; - case AuditLogActionType::MESSAGE_DELETE: { - const auto channel = discord.GetChannel(*entry.Options->ChannelID); - const auto count = *entry.Options->Count; - if (channel.has_value()) { - markup = user_markup + - " deleted <b>" + count + "</b> messages in <b>#" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } else { - markup = user_markup + - " deleted <b>" + count + "</b> messages"; - } - } break; - case AuditLogActionType::MESSAGE_BULK_DELETE: { - const auto channel = discord.GetChannel(entry.TargetID); - if (channel.has_value()) { - markup = user_markup + - " deleted <b>" + - *entry.Options->Count + - "</b> messages in <b>#" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } else { - markup = user_markup + - " deleted <b>" + - *entry.Options->Count + - "</b> messages"; - } - } break; - case AuditLogActionType::MESSAGE_PIN: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " pinned a message by <b>" + - target_user->GetEscapedString() + - "</b>"; - } break; - case AuditLogActionType::MESSAGE_UNPIN: { - const auto target_user = discord.GetUser(entry.TargetID); - markup = user_markup + - " unpinned a message by <b>" + - target_user->GetEscapedString() + - "</b>"; - } break; - case AuditLogActionType::STAGE_INSTANCE_CREATE: { - const auto channel = discord.GetChannel(*entry.Options->ChannelID); - if (channel.has_value()) { - markup = user_markup + - " started the stage for <b>" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } else { - markup = user_markup + - " started the stage for <b>" + - std::to_string(*entry.Options->ChannelID) + - "</b>"; - } - - if (entry.Changes.has_value()) { - for (const auto &change : *entry.Changes) { - if (change.Key == "topic" && change.NewValue.has_value()) { - extra_markup.push_back( - "Set the topic to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } else if (change.Key == "privacy_level" && change.NewValue.has_value()) { - Glib::ustring str = Glib::Markup::escape_text(GetStagePrivacyDisplayString(change.NewValue->get<StagePrivacy>())); - extra_markup.push_back( - "Set the privacy level to <b>" + - str + - "</b>"); - } - } - } - } break; - case AuditLogActionType::STAGE_INSTANCE_UPDATE: { - const auto channel = discord.GetChannel(*entry.Options->ChannelID); - if (channel.has_value()) { - markup = user_markup + - " updated the stage for <b>" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } else { - markup = user_markup + - " updated the stage for <b>" + - std::to_string(*entry.Options->ChannelID) + - "</b>"; - } - - if (entry.Changes.has_value()) { - for (const auto &change : *entry.Changes) { - if (change.Key == "topic" && change.NewValue.has_value()) { - extra_markup.push_back( - "Set the topic to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - } else if (change.Key == "privacy_level" && change.NewValue.has_value()) { - Glib::ustring str = Glib::Markup::escape_text(GetStagePrivacyDisplayString(change.NewValue->get<StagePrivacy>())); - extra_markup.push_back( - "Set the privacy level to <b>" + - str + - "</b>"); - } - } - } - } break; - case AuditLogActionType::STAGE_INSTANCE_DELETE: { - const auto channel = discord.GetChannel(*entry.Options->ChannelID); - if (channel.has_value()) { - markup = user_markup + - " ended the stage for <b>" + - Glib::Markup::escape_text(*channel->Name) + - "</b>"; - } else { - markup = user_markup + - " ended the stage for <b>" + - std::to_string(*entry.Options->ChannelID) + - "</b>"; - } - } break; - case AuditLogActionType::THREAD_CREATE: { - const auto channel = discord.GetChannel(entry.TargetID); - markup = user_markup + - " created a thread <b>" + - (channel.has_value() - ? Glib::Markup::escape_text(*channel->Name) - : Glib::ustring(*entry.GetNewFromKey<std::string>("name"))) + - "</b>"; - if (entry.Changes.has_value()) { - for (const auto &change : *entry.Changes) { - if (change.Key == "name") - extra_markup.push_back("Set the name to <b>" + Glib::Markup::escape_text(change.NewValue->get<std::string>()) + "</b>"); - else if (change.Key == "archived") - extra_markup.push_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread"); - else if (change.Key == "auto_archive_duration") - extra_markup.push_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s); - else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) { - const int secs = change.NewValue->get<int>(); - if (secs == 0) - extra_markup.push_back("Disabled slowmode"); - else - extra_markup.push_back("Set slowmode to <b>" + - std::to_string(secs) + " seconds</b>"); - } else if (change.Key == "locked") - extra_markup.push_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators"); - } - } - } break; - case AuditLogActionType::THREAD_UPDATE: { - const auto channel = discord.GetChannel(entry.TargetID); - markup = user_markup + - " made changes to the thread <b>" + - (channel.has_value() - ? Glib::Markup::escape_text(*channel->Name) - : Glib::ustring(entry.TargetID)) + - "</b>"; - for (const auto &change : *entry.Changes) { - if (change.Key == "name") - extra_markup.push_back( - "Changed the name from <b>" + - Glib::Markup::escape_text(change.OldValue->get<std::string>()) + - "</b> to <b>" + - Glib::Markup::escape_text(change.NewValue->get<std::string>()) + - "</b>"); - else if (change.Key == "auto_archive_duration") - extra_markup.push_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s); - else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) { - const int secs = change.NewValue->get<int>(); - if (secs == 0) - extra_markup.push_back("Disabled slowmode"); - else - extra_markup.push_back("Set slowmode to <b>" + - std::to_string(secs) + - " seconds</b>"); - } else if (change.Key == "locked") - extra_markup.push_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators"); - else if (change.Key == "archived") - extra_markup.push_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread"); - } - } break; - case AuditLogActionType::THREAD_DELETE: { - markup = user_markup + - " deleted the thread <b>" + Glib::Markup::escape_text(*entry.GetOldFromKey<std::string>("name")) + "</b>"; - } break; - default: - markup = "<i>Unknown action</i>"; - break; - } - - label->set_markup(markup); - expander->set_label_widget(*label); - - if (entry.Reason.has_value()) { - extra_markup.push_back("With reason <b>" + - Glib::Markup::escape_text(*entry.Reason) + - "</b>"); - } - - expander->set_expanded(true); - - auto contents = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); - for (const auto &extra : extra_markup) { - auto extra_label = Gtk::manage(new Gtk::Label); - extra_label->set_markup(extra); - extra_label->set_halign(Gtk::ALIGN_START); - extra_label->set_margin_start(25); - extra_label->set_ellipsize(Pango::ELLIPSIZE_END); - contents->add(*extra_label); - } - expander->add(*contents); - expander->set_margin_bottom(5); - expander->show_all(); - m_list.add(*expander); - } -} diff --git a/windows/guildsettings/auditlogpane.hpp b/windows/guildsettings/auditlogpane.hpp deleted file mode 100644 index ac12321..0000000 --- a/windows/guildsettings/auditlogpane.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class GuildSettingsAuditLogPane : public Gtk::ScrolledWindow { -public: - GuildSettingsAuditLogPane(Snowflake id); - -private: - void OnMap(); - - bool m_requested = false; - - Gtk::ListBox m_list; - - void OnAuditLogFetch(const AuditLogData &data); - - Snowflake GuildID; -}; diff --git a/windows/guildsettings/banspane.cpp b/windows/guildsettings/banspane.cpp deleted file mode 100644 index 97a70c4..0000000 --- a/windows/guildsettings/banspane.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "banspane.hpp" -#include "abaddon.hpp" - -// gtk_list_store_set_value: assertion 'column >= 0 && column < priv->n_columns' failed -// dont care to figure out why this happens cuz it doesnt seem to break anything - -GuildSettingsBansPane::GuildSettingsBansPane(Snowflake id) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , GuildID(id) - , m_model(Gtk::ListStore::create(m_columns)) - , m_menu_unban("Unban") - , m_menu_copy_id("Copy ID") { - signal_map().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnMap)); - set_name("guild-bans-pane"); - set_hexpand(true); - set_vexpand(true); - - auto &discord = Abaddon::Get().GetDiscordClient(); - - discord.signal_guild_ban_add().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnBanAdd)); - discord.signal_guild_ban_remove().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnBanRemove)); - - const auto self_id = discord.GetUserData().ID; - const auto can_ban = discord.HasGuildPermission(self_id, GuildID, Permission::BAN_MEMBERS); - - if (!can_ban) { - for (const auto &ban : discord.GetBansInGuild(id)) - OnGuildBanFetch(ban); - - m_no_perms_note = Gtk::manage(new Gtk::Label("You do not have permission to see bans. However, bans made while you are connected will appear here")); - m_no_perms_note->set_single_line_mode(true); - m_no_perms_note->set_ellipsize(Pango::ELLIPSIZE_END); - m_no_perms_note->set_halign(Gtk::ALIGN_START); - add(*m_no_perms_note); - } - - m_menu_unban.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnMenuUnban)); - m_menu_copy_id.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnMenuCopyID)); - m_menu_unban.show(); - m_menu_copy_id.show(); - m_menu.append(m_menu_unban); - m_menu.append(m_menu_copy_id); - - m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &GuildSettingsBansPane::OnTreeButtonPress), false); - m_view.show(); - - m_scroll.set_propagate_natural_height(true); - m_scroll.add(m_view); - add(m_scroll); - show_all_children(); - - m_view.set_enable_search(false); - m_view.set_model(m_model); - m_view.append_column("User", m_columns.m_col_user); - m_view.append_column("Reason", m_columns.m_col_reason); -} - -void GuildSettingsBansPane::OnMap() { - if (m_requested) return; - m_requested = true; - - auto &discord = Abaddon::Get().GetDiscordClient(); - - const auto self_id = discord.GetUserData().ID; - const auto can_ban = discord.HasGuildPermission(self_id, GuildID, Permission::BAN_MEMBERS); - - if (can_ban) - discord.FetchGuildBans(GuildID, sigc::mem_fun(*this, &GuildSettingsBansPane::OnGuildBansFetch)); -} - -void GuildSettingsBansPane::OnGuildBanFetch(const BanData &ban) { - const auto user = Abaddon::Get().GetDiscordClient().GetUser(ban.User.ID); - auto row = *m_model->append(); - row[m_columns.m_col_id] = ban.User.ID; - if (user.has_value()) - row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator; - else - row[m_columns.m_col_user] = "<@" + std::to_string(ban.User.ID) + ">"; - - row[m_columns.m_col_reason] = ban.Reason; -} - -void GuildSettingsBansPane::OnGuildBansFetch(const std::vector<BanData> &bans) { - for (const auto &ban : bans) { - const auto user = Abaddon::Get().GetDiscordClient().GetUser(ban.User.ID); - auto row = *m_model->append(); - row[m_columns.m_col_id] = user->ID; - row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator; - row[m_columns.m_col_reason] = ban.Reason; - } -} - -void GuildSettingsBansPane::OnMenuUnban() { - auto selected_row = *m_view.get_selection()->get_selected(); - if (selected_row) { - Snowflake id = selected_row[m_columns.m_col_id]; - auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to unban user", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - Abaddon::Get().GetDiscordClient().UnbanUser(GuildID, id, sigc::track_obj(cb, *this)); - } -} - -void GuildSettingsBansPane::OnMenuCopyID() { - auto selected_row = *m_view.get_selection()->get_selected(); - if (selected_row) - Gtk::Clipboard::get()->set_text(std::to_string(static_cast<Snowflake>(selected_row[m_columns.m_col_id]))); -} - -bool GuildSettingsBansPane::OnTreeButtonPress(GdkEventButton *event) { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const auto can_ban = discord.HasGuildPermission(self_id, GuildID, Permission::BAN_MEMBERS); - m_menu_unban.set_sensitive(can_ban); - auto selection = m_view.get_selection(); - Gtk::TreeModel::Path path; - if (m_view.get_path_at_pos(event->x, event->y, path)) { - m_view.get_selection()->select(path); - m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); - } - - return true; - } - - return false; -} - -void GuildSettingsBansPane::OnBanRemove(Snowflake guild_id, Snowflake user_id) { - if (guild_id != GuildID) return; - for (auto &child : m_model->children()) { - if (static_cast<Snowflake>(child[m_columns.m_col_id]) == user_id) { - m_model->erase(child); - break; - } - } -} - -void GuildSettingsBansPane::OnBanAdd(Snowflake guild_id, Snowflake user_id) { - if (guild_id != GuildID) return; - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.HasGuildPermission(discord.GetUserData().ID, guild_id, Permission::BAN_MEMBERS)) { - discord.FetchGuildBan(guild_id, user_id, sigc::mem_fun(*this, &GuildSettingsBansPane::OnGuildBanFetch)); - } else { - auto user = *discord.GetUser(user_id); - auto row = *m_model->append(); - row[m_columns.m_col_id] = user_id; - row[m_columns.m_col_user] = user.Username + "#" + user.Discriminator; - row[m_columns.m_col_reason] = ""; - } -} - -GuildSettingsBansPane::ModelColumns::ModelColumns() { - add(m_col_id); - add(m_col_user); - add(m_col_reason); -} diff --git a/windows/guildsettings/banspane.hpp b/windows/guildsettings/banspane.hpp deleted file mode 100644 index b2420a9..0000000 --- a/windows/guildsettings/banspane.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/snowflake.hpp" -#include "discord/ban.hpp" - -class GuildSettingsBansPane : public Gtk::Box { -public: - GuildSettingsBansPane(Snowflake id); - -private: - void OnMap(); - - bool m_requested = false; - - void OnGuildBanFetch(const BanData &ban); - void OnGuildBansFetch(const std::vector<BanData> &bans); - void OnMenuUnban(); - void OnMenuCopyID(); - bool OnTreeButtonPress(GdkEventButton *event); - void OnBanRemove(Snowflake guild_id, Snowflake user_id); - void OnBanAdd(Snowflake guild_id, Snowflake user_id); - - Gtk::Label *m_no_perms_note = nullptr; - - Gtk::ScrolledWindow m_scroll; - Gtk::TreeView m_view; - - Snowflake GuildID; - - class ModelColumns : public Gtk::TreeModel::ColumnRecord { - public: - ModelColumns(); - - Gtk::TreeModelColumn<Glib::ustring> m_col_user; - Gtk::TreeModelColumn<Glib::ustring> m_col_reason; - Gtk::TreeModelColumn<Snowflake> m_col_id; - }; - - ModelColumns m_columns; - Glib::RefPtr<Gtk::ListStore> m_model; - - Gtk::Menu m_menu; - Gtk::MenuItem m_menu_unban; - Gtk::MenuItem m_menu_copy_id; -}; diff --git a/windows/guildsettings/emojispane.cpp b/windows/guildsettings/emojispane.cpp deleted file mode 100644 index 1f4bfa9..0000000 --- a/windows/guildsettings/emojispane.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "emojispane.hpp" -#include "abaddon.hpp" -#include "components/cellrendererpixbufanimation.hpp" - -GuildSettingsEmojisPane::GuildSettingsEmojisPane(Snowflake guild_id) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , GuildID(guild_id) - , m_model(Gtk::ListStore::create(m_columns)) - , m_filter(Gtk::TreeModelFilter::create(m_model)) - , m_menu_delete("Delete") - , m_menu_copy_id("Copy ID") - , m_menu_copy_emoji_url("Copy Emoji URL") - , m_menu_show_emoji("Open in Browser") { - signal_map().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnMap)); - set_name("guild-emojis-pane"); - - m_view_scroll.set_hexpand(true); - m_view_scroll.set_vexpand(true); - - m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnTreeButtonPress), false); - - m_menu_copy_id.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnMenuCopyID)); - m_menu_delete.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnMenuDelete)); - m_menu_copy_emoji_url.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnMenuCopyEmojiURL)); - m_menu_show_emoji.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnMenuShowEmoji)); - - m_menu.append(m_menu_delete); - m_menu.append(m_menu_copy_id); - m_menu.append(m_menu_copy_emoji_url); - m_menu.append(m_menu_show_emoji); - m_menu.show_all(); - - auto &discord = Abaddon::Get().GetDiscordClient(); - - discord.signal_guild_emojis_update().connect(sigc::hide<0>(sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnFetchEmojis))); - - const auto self_id = discord.GetUserData().ID; - const bool can_manage = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_EMOJIS); - m_menu_delete.set_sensitive(can_manage); - - m_search.set_placeholder_text("Filter"); - m_search.signal_changed().connect([this]() { - m_filter->refilter(); - }); - - m_view_scroll.add(m_view); - add(m_search); - add(m_view_scroll); - m_search.show(); - m_view.show(); - m_view_scroll.show(); - - m_filter->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { - const auto text = m_search.get_text(); - if (text == "") return true; - return StringContainsCaseless((*iter)[m_columns.m_col_name], text); - }); - m_view.set_enable_search(false); - m_view.set_model(m_filter); - - auto *column = Gtk::manage(new Gtk::TreeView::Column("Emoji")); - auto *renderer = Gtk::manage(new CellRendererPixbufAnimation); - column->pack_start(*renderer); - column->add_attribute(renderer->property_pixbuf(), m_columns.m_col_pixbuf); - column->add_attribute(renderer->property_pixbuf_animation(), m_columns.m_col_pixbuf_animation); - m_view.append_column(*column); - - if (can_manage) { - auto *column = Gtk::manage(new Gtk::TreeView::Column("Name")); - auto *renderer = Gtk::manage(new Gtk::CellRendererText); - column->pack_start(*renderer); - column->add_attribute(renderer->property_text(), m_columns.m_col_name); - renderer->property_editable() = true; - renderer->signal_edited().connect([this, renderer, column](const Glib::ustring &path, const Glib::ustring &text) { - std::string new_str; - int size = 0; - for (const auto ch : text) { - if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_') - new_str += ch; - else if (ch == ' ') - new_str += '_'; - if (++size == 32) break; - } - if (auto row = *m_model->get_iter(path)) { - row[m_columns.m_col_name] = new_str; - OnEditName(row[m_columns.m_col_id], new_str); - } - }); - m_view.append_column(*column); - } else - m_view.append_column("Name", m_columns.m_col_name); - if (can_manage) - m_view.append_column("Creator", m_columns.m_col_creator); - m_view.append_column("Is Animated?", m_columns.m_col_animated); - - for (const auto column : m_view.get_columns()) - column->set_resizable(true); -} - -void GuildSettingsEmojisPane::OnMap() { - m_view.grab_focus(); - - if (m_requested) return; - m_requested = true; - - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const bool can_manage = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_EMOJIS); - m_menu_delete.set_sensitive(can_manage); - - discord.FetchGuildEmojis(GuildID, sigc::mem_fun(*this, &GuildSettingsEmojisPane::OnFetchEmojis)); -} - -void GuildSettingsEmojisPane::AddEmojiRow(const EmojiData &emoji) { - auto &img = Abaddon::Get().GetImageManager(); - - auto row = *m_model->append(); - - row[m_columns.m_col_id] = emoji.ID; - row[m_columns.m_col_pixbuf] = img.GetPlaceholder(32); - row[m_columns.m_col_name] = emoji.Name; - if (emoji.Creator.has_value()) - row[m_columns.m_col_creator] = emoji.Creator->Username + "#" + emoji.Creator->Discriminator; - if (emoji.IsAnimated.has_value()) - row[m_columns.m_col_animated] = *emoji.IsAnimated ? "Yes" : "No"; - else - row[m_columns.m_col_animated] = "No"; - if (emoji.IsAvailable.has_value()) - row[m_columns.m_col_available] = *emoji.IsAvailable ? "Yes" : "No"; - else - row[m_columns.m_col_available] = "Yes"; - - static bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - if (show_animations && emoji.IsAnimated.has_value() && *emoji.IsAnimated) { - const auto cb = [this, id = emoji.ID](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - for (auto &row : m_model->children()) { - if (static_cast<Snowflake>(row[m_columns.m_col_id]) == id) { - row[m_columns.m_col_pixbuf_animation] = pb; - return; - } - } - }; - img.LoadAnimationFromURL(emoji.GetURL("gif"), 32, 32, sigc::track_obj(cb, *this)); - } else { - const auto cb = [this, id = emoji.ID](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - for (auto &row : m_model->children()) { - if (static_cast<Snowflake>(row[m_columns.m_col_id]) == id) { - row[m_columns.m_col_pixbuf] = pb->scale_simple(32, 32, Gdk::INTERP_BILINEAR); - return; - } - } - }; - img.LoadFromURL(emoji.GetURL(), sigc::track_obj(cb, *this)); - } -} - -void GuildSettingsEmojisPane::OnFetchEmojis(std::vector<EmojiData> emojis) { - m_model->clear(); - - // put animated emojis at the end then sort alphabetically - std::sort(emojis.begin(), emojis.end(), [&](const EmojiData &a, const EmojiData &b) { - const bool a_is_animated = a.IsAnimated.has_value() && *a.IsAnimated; - const bool b_is_animated = b.IsAnimated.has_value() && *b.IsAnimated; - if (a_is_animated == b_is_animated) - return a.Name < b.Name; - else if (a_is_animated && !b_is_animated) - return false; - else if (!a_is_animated && b_is_animated) - return true; - return false; // this wont happen please be quiet compiler - }); - - for (const auto &emoji : emojis) - AddEmojiRow(emoji); -} - -void GuildSettingsEmojisPane::OnEditName(Snowflake id, const std::string &name) { - const auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to set emoji name", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - Abaddon::Get().GetDiscordClient().ModifyEmojiName(GuildID, id, name, cb); -} - -void GuildSettingsEmojisPane::OnMenuCopyID() { - if (auto selected_row = *m_view.get_selection()->get_selected()) { - const auto id = static_cast<Snowflake>(selected_row[m_columns.m_col_id]); - Gtk::Clipboard::get()->set_text(std::to_string(id)); - } -} - -void GuildSettingsEmojisPane::OnMenuDelete() { - if (auto selected_row = *m_view.get_selection()->get_selected()) { - const auto name = static_cast<Glib::ustring>(selected_row[m_columns.m_col_name]); - const auto id = static_cast<Snowflake>(selected_row[m_columns.m_col_id]); - if (auto *window = dynamic_cast<Gtk::Window *>(get_toplevel())) - if (Abaddon::Get().ShowConfirm("Are you sure you want to delete " + name + "?", window)) { - const auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to delete emoji", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - Abaddon::Get().GetDiscordClient().DeleteEmoji(GuildID, id, cb); - } - } -} - -void GuildSettingsEmojisPane::OnMenuCopyEmojiURL() { - if (auto selected_row = *m_view.get_selection()->get_selected()) { - const auto id = static_cast<Snowflake>(selected_row[m_columns.m_col_id]); - const bool is_animated = static_cast<Glib::ustring>(selected_row[m_columns.m_col_animated]) == "Yes"; - Gtk::Clipboard::get()->set_text(EmojiData::URLFromID(id, is_animated ? "gif" : "png", "256")); - } -} - -void GuildSettingsEmojisPane::OnMenuShowEmoji() { - if (auto selected_row = *m_view.get_selection()->get_selected()) { - const auto id = static_cast<Snowflake>(selected_row[m_columns.m_col_id]); - const bool is_animated = static_cast<Glib::ustring>(selected_row[m_columns.m_col_animated]) == "Yes"; - LaunchBrowser(EmojiData::URLFromID(id, is_animated ? "gif" : "png", "256")); - } -} - -bool GuildSettingsEmojisPane::OnTreeButtonPress(GdkEventButton *event) { - if (event->button == GDK_BUTTON_SECONDARY) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const bool can_manage = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_EMOJIS); - m_menu_delete.set_sensitive(can_manage); - - auto selection = m_view.get_selection(); - Gtk::TreeModel::Path path; - if (m_view.get_path_at_pos(event->x, event->y, path)) { - m_view.get_selection()->select(path); - m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); - } - - return true; - } - - return false; -} - -GuildSettingsEmojisPane::ModelColumns::ModelColumns() { - add(m_col_id); - add(m_col_pixbuf); - add(m_col_pixbuf_animation); - add(m_col_name); - add(m_col_creator); - add(m_col_animated); - add(m_col_available); -} diff --git a/windows/guildsettings/emojispane.hpp b/windows/guildsettings/emojispane.hpp deleted file mode 100644 index 1c0edd1..0000000 --- a/windows/guildsettings/emojispane.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/emoji.hpp" - -class GuildSettingsEmojisPane : public Gtk::Box { -public: - GuildSettingsEmojisPane(Snowflake guild_id); - -private: - void OnMap(); - - bool m_requested = false; - - void AddEmojiRow(const EmojiData &emoji); - - void OnFetchEmojis(std::vector<EmojiData> emojis); - - void OnEditName(Snowflake id, const std::string &name); - void OnMenuCopyID(); - void OnMenuDelete(); - void OnMenuCopyEmojiURL(); - void OnMenuShowEmoji(); - bool OnTreeButtonPress(GdkEventButton *event); - - Snowflake GuildID; - - Gtk::Entry m_search; - Gtk::ScrolledWindow m_view_scroll; - Gtk::TreeView m_view; - - class ModelColumns : public Gtk::TreeModel::ColumnRecord { - public: - ModelColumns(); - - Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_col_pixbuf; - Gtk::TreeModelColumn<Glib::RefPtr<Gdk::PixbufAnimation>> m_col_pixbuf_animation; - Gtk::TreeModelColumn<Glib::ustring> m_col_name; - Gtk::TreeModelColumn<Glib::ustring> m_col_creator; - Gtk::TreeModelColumn<Glib::ustring> m_col_animated; - Gtk::TreeModelColumn<Glib::ustring> m_col_available; - Gtk::TreeModelColumn<Snowflake> m_col_id; - }; - - ModelColumns m_columns; - Glib::RefPtr<Gtk::ListStore> m_model; - Glib::RefPtr<Gtk::TreeModelFilter> m_filter; - - Gtk::Menu m_menu; - Gtk::MenuItem m_menu_delete; - Gtk::MenuItem m_menu_copy_id; - Gtk::MenuItem m_menu_copy_emoji_url; - Gtk::MenuItem m_menu_show_emoji; -}; diff --git a/windows/guildsettings/infopane.cpp b/windows/guildsettings/infopane.cpp deleted file mode 100644 index b4f75f3..0000000 --- a/windows/guildsettings/infopane.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include "infopane.hpp" -#include "abaddon.hpp" -#include <filesystem> - -GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id) - : m_guild_icon_label("Guild icon") - , m_guild_name_label("Guild name") - , GuildID(id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto guild = *discord.GetGuild(id); - const auto self_id = discord.GetUserData().ID; - const auto can_modify = discord.HasGuildPermission(self_id, id, Permission::MANAGE_GUILD); - - set_name("guild-info-pane"); - - m_guild_name.set_sensitive(can_modify); - m_guild_name.set_text(guild.Name); - m_guild_name.signal_focus_out_event().connect([this](GdkEventFocus *e) -> bool { - UpdateGuildName(); - return false; - }); - m_guild_name.signal_key_press_event().connect([this](GdkEventKey *e) -> bool { - if (e->keyval == GDK_KEY_Return) - UpdateGuildName(); - return false; - // clang-format off - }, false); - // clang-format on - m_guild_name.set_tooltip_text("Press enter or lose focus to submit"); - m_guild_name.show(); - m_guild_name_label.show(); - - auto guild_update_cb = [this](Snowflake id) { - if (id != GuildID) return; - const auto guild = *Abaddon::Get().GetDiscordClient().GetGuild(id); - FetchGuildIcon(guild); - }; - discord.signal_guild_update().connect(sigc::track_obj(guild_update_cb, *this)); - FetchGuildIcon(guild); - - AddPointerCursor(m_guild_icon_ev); - - m_guild_icon.set_margin_bottom(10); - if (can_modify) { - m_guild_icon_ev.set_tooltip_text("Click to choose a file, right click to paste"); - - m_guild_icon_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS) { - if (event->button == GDK_BUTTON_PRIMARY) - UpdateGuildIconPicker(); - else if (event->button == GDK_BUTTON_SECONDARY) - UpdateGuildIconClipboard(); - } - - return false; - }); - } else if (guild.HasIcon()) { - std::string guild_icon_url; - if (guild.HasAnimatedIcon()) - guild_icon_url = guild.GetIconURL("gif", "512"); - else - guild_icon_url = guild.GetIconURL("png", "512"); - m_guild_icon_ev.signal_button_press_event().connect([this, guild_icon_url](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS) - if (event->button == GDK_BUTTON_PRIMARY) - LaunchBrowser(guild_icon_url); - - return false; - }); - } - - m_guild_icon.show(); - m_guild_icon_ev.show(); - - m_guild_icon_ev.add(m_guild_icon); - attach(m_guild_icon_ev, 0, 0, 1, 1); - attach(m_guild_name_label, 0, 1, 1, 1); - attach_next_to(m_guild_name, m_guild_name_label, Gtk::POS_RIGHT, 1, 1); -} - -void GuildSettingsInfoPane::FetchGuildIcon(const GuildData &guild) { - m_guild_icon.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32); - if (guild.HasIcon()) { - if (Abaddon::Get().GetSettings().GetShowAnimations() && guild.HasAnimatedIcon()) { - auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pixbuf) { - m_guild_icon.property_pixbuf_animation() = pixbuf; - }; - Abaddon::Get().GetImageManager().LoadAnimationFromURL(guild.GetIconURL("gif", "64"), 64, 64, sigc::track_obj(cb, *this)); - } - - auto cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) { - m_guild_icon.property_pixbuf() = pixbuf->scale_simple(64, 64, Gdk::INTERP_BILINEAR); - }; - Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL("png", "64"), sigc::track_obj(cb, *this)); - } -} - -void GuildSettingsInfoPane::UpdateGuildName() { - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.GetGuild(GuildID)->Name == m_guild_name.get_text()) return; - - auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - m_guild_name.set_text(Abaddon::Get().GetDiscordClient().GetGuild(GuildID)->Name); - Gtk::MessageDialog dlg("Failed to set guild name", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - discord.SetGuildName(GuildID, m_guild_name.get_text(), sigc::track_obj(cb, *this)); -} - -void GuildSettingsInfoPane::UpdateGuildIconFromData(const std::vector<uint8_t> &data, const std::string &mime) { - auto encoded = "data:" + mime + ";base64," + Glib::Base64::encode(std::string(data.begin(), data.end())); - auto &discord = Abaddon::Get().GetDiscordClient(); - - auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to set guild icon", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - discord.SetGuildIcon(GuildID, encoded, sigc::track_obj(cb, *this)); -} - -void GuildSettingsInfoPane::UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf) { - int w = pixbuf->get_width(); - int h = pixbuf->get_height(); - if (w > 1024 || h > 1024) { - GetImageDimensions(w, h, w, h, 1024, 1024); - pixbuf = pixbuf->scale_simple(w, h, Gdk::INTERP_BILINEAR); - } - gchar *buffer; - gsize buffer_size; - pixbuf->save_to_buffer(buffer, buffer_size, "png"); - std::vector<uint8_t> data(buffer_size); - std::memcpy(data.data(), buffer, buffer_size); - UpdateGuildIconFromData(data, "image/png"); -} - -void GuildSettingsInfoPane::UpdateGuildIconPicker() { - // this picker fucking sucks - Gtk::FileChooserDialog dlg("Choose new guild icon", Gtk::FILE_CHOOSER_ACTION_OPEN); - dlg.get_style_context()->remove_provider(Abaddon::Get().GetStyleProvider()); - dlg.set_modal(true); - dlg.signal_response().connect([this, &dlg](int response) { - if (response == Gtk::RESPONSE_OK) { - auto data = ReadWholeFile(dlg.get_filename()); - if (GetExtension(dlg.get_filename()) == ".gif") - UpdateGuildIconFromData(data, "image/gif"); - else - try { - auto loader = Gdk::PixbufLoader::create(); - loader->signal_size_prepared().connect([&loader](int inw, int inh) { - int w, h; - GetImageDimensions(inw, inh, w, h, 1024, 1024); - loader->set_size(w, h); - }); - loader->write(data.data(), data.size()); - loader->close(); - UpdateGuildIconFromPixbuf(loader->get_pixbuf()); - } catch (const std::exception &) {}; - } - }); - - dlg.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - - auto filter_images = Gtk::FileFilter::create(); - if (Abaddon::Get().GetDiscordClient().GetGuild(GuildID)->HasFeature("ANIMATED_ICON")) { - filter_images->set_name("Supported images (*.jpg, *.jpeg, *.png, *.gif)"); - filter_images->add_pattern("*.gif"); - } else { - filter_images->set_name("Supported images (*.jpg, *.jpeg, *.png)"); - } - filter_images->add_pattern("*.jpg"); - filter_images->add_pattern("*.jpeg"); - filter_images->add_pattern("*.png"); - dlg.add_filter(filter_images); - - auto filter_all = Gtk::FileFilter::create(); - filter_all->set_name("All files (*.*)"); - filter_all->add_pattern("*.*"); - dlg.add_filter(filter_all); - - dlg.run(); -} - -void GuildSettingsInfoPane::UpdateGuildIconClipboard() { - std::vector<uint8_t> icon_data; - - auto cb = Gtk::Clipboard::get(); - // query for file path then for actual image - if (cb->wait_is_text_available()) { - auto path = cb->wait_for_text(); - if (!std::filesystem::exists(path.c_str())) return; - auto data = ReadWholeFile(path); - try { - auto loader = Gdk::PixbufLoader::create(); - loader->signal_size_prepared().connect([&loader](int inw, int inh) { - int w, h; - GetImageDimensions(inw, inh, w, h, 1024, 1024); - loader->set_size(w, h); - }); - loader->write(data.data(), data.size()); - loader->close(); - auto pb = loader->get_pixbuf(); - UpdateGuildIconFromPixbuf(pb); - - return; - } catch (const std::exception &) {}; - } - - if (cb->wait_is_image_available()) { - auto pb = cb->wait_for_image(); - UpdateGuildIconFromPixbuf(pb); - return; - } -} diff --git a/windows/guildsettings/infopane.hpp b/windows/guildsettings/infopane.hpp deleted file mode 100644 index 8a7e6a2..0000000 --- a/windows/guildsettings/infopane.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/guild.hpp" - -class GuildSettingsInfoPane : public Gtk::Grid { -public: - GuildSettingsInfoPane(Snowflake id); - -private: - void FetchGuildIcon(const GuildData &guild); - - void UpdateGuildName(); - void UpdateGuildIconFromData(const std::vector<uint8_t> &data, const std::string &mime); - void UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf); - void UpdateGuildIconPicker(); - void UpdateGuildIconClipboard(); - - Gtk::Label m_guild_icon_label; - Gtk::EventBox m_guild_icon_ev; // necessary to make custom cursor behave properly - Gtk::Image m_guild_icon; - - Gtk::Label m_guild_name_label; - Gtk::Entry m_guild_name; - - Snowflake GuildID; -}; diff --git a/windows/guildsettings/invitespane.cpp b/windows/guildsettings/invitespane.cpp deleted file mode 100644 index bec4784..0000000 --- a/windows/guildsettings/invitespane.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "invitespane.hpp" -#include "abaddon.hpp" - -GuildSettingsInvitesPane::GuildSettingsInvitesPane(Snowflake id) - : GuildID(id) - , m_model(Gtk::ListStore::create(m_columns)) - , m_menu_delete("Delete") { - signal_map().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnMap)); - set_name("guild-invites-pane"); - set_hexpand(true); - set_vexpand(true); - - m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnTreeButtonPress), false); - - m_menu_delete.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnMenuDelete)); - m_menu.append(m_menu_delete); - m_menu.show_all(); - - auto &discord = Abaddon::Get().GetDiscordClient(); - - discord.signal_invite_create().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInviteCreate)); - discord.signal_invite_delete().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInviteDelete)); - - m_view.show(); - add(m_view); - - m_view.set_enable_search(false); - m_view.set_model(m_model); - m_view.append_column("Code", m_columns.m_col_code); - m_view.append_column("Expires", m_columns.m_col_expires); - m_view.append_column("Created by", m_columns.m_col_inviter); - m_view.append_column("Uses", m_columns.m_col_uses); - m_view.append_column("Max uses", m_columns.m_col_max_uses); - m_view.append_column("Grants temporary membership", m_columns.m_col_temporary); - - for (const auto column : m_view.get_columns()) - column->set_resizable(true); -} - -void GuildSettingsInvitesPane::OnMap() { - if (m_requested) return; - m_requested = true; - - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - - if (discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD)) - discord.FetchGuildInvites(GuildID, sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInvitesFetch)); -} - -void GuildSettingsInvitesPane::AppendInvite(const InviteData &invite) { - auto row = *m_model->append(); - row[m_columns.m_col_code] = invite.Code; - if (invite.Inviter.has_value()) - row[m_columns.m_col_inviter] = invite.Inviter->Username + "#" + invite.Inviter->Discriminator; - - if (invite.MaxAge.has_value()) { - if (*invite.MaxAge == 0) - row[m_columns.m_col_expires] = "Never"; - else - row[m_columns.m_col_expires] = FormatISO8601(*invite.CreatedAt, *invite.MaxAge); - } - - row[m_columns.m_col_uses] = *invite.Uses; - if (*invite.MaxUses == 0) - row[m_columns.m_col_max_uses] = "Unlimited"; - else - row[m_columns.m_col_max_uses] = std::to_string(*invite.MaxUses); - - row[m_columns.m_col_temporary] = *invite.IsTemporary ? "Yes" : "No"; -} - -void GuildSettingsInvitesPane::OnInviteFetch(const std::optional<InviteData> &invite) { - if (!invite.has_value()) return; - AppendInvite(*invite); -} - -void GuildSettingsInvitesPane::OnInvitesFetch(const std::vector<InviteData> &invites) { - for (const auto &invite : invites) - AppendInvite(invite); -} - -void GuildSettingsInvitesPane::OnInviteCreate(const InviteData &invite) { - if (invite.Guild->ID == GuildID) - OnInviteFetch(std::make_optional(invite)); -} - -void GuildSettingsInvitesPane::OnInviteDelete(const InviteDeleteObject &data) { - if (*data.GuildID == GuildID) - for (auto &row : m_model->children()) - if (row[m_columns.m_col_code] == data.Code) - m_model->erase(row); -} - -void GuildSettingsInvitesPane::OnMenuDelete() { - auto selected_row = *m_view.get_selection()->get_selected(); - if (selected_row) { - auto code = static_cast<Glib::ustring>(selected_row[m_columns.m_col_code]); - auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to delete invite", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - Abaddon::Get().GetDiscordClient().DeleteInvite(code, sigc::track_obj(cb, *this)); - } -} - -bool GuildSettingsInvitesPane::OnTreeButtonPress(GdkEventButton *event) { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const auto can_manage = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD); - m_menu_delete.set_sensitive(can_manage); - auto selection = m_view.get_selection(); - Gtk::TreeModel::Path path; - if (m_view.get_path_at_pos(event->x, event->y, path)) { - m_view.get_selection()->select(path); - m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event)); - } - - return true; - } - - return false; -} - -GuildSettingsInvitesPane::ModelColumns::ModelColumns() { - add(m_col_code); - add(m_col_expires); - add(m_col_inviter); - add(m_col_temporary); - add(m_col_uses); - add(m_col_max_uses); -} diff --git a/windows/guildsettings/invitespane.hpp b/windows/guildsettings/invitespane.hpp deleted file mode 100644 index 5268d68..0000000 --- a/windows/guildsettings/invitespane.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class GuildSettingsInvitesPane : public Gtk::ScrolledWindow { -public: - GuildSettingsInvitesPane(Snowflake id); - -private: - void OnMap(); - - bool m_requested = false; - - void AppendInvite(const InviteData &invite); - void OnInviteFetch(const std::optional<InviteData> &invite); - void OnInvitesFetch(const std::vector<InviteData> &invites); - void OnInviteCreate(const InviteData &invite); - void OnInviteDelete(const InviteDeleteObject &data); - void OnMenuDelete(); - bool OnTreeButtonPress(GdkEventButton *event); - - Gtk::TreeView m_view; - - Snowflake GuildID; - - class ModelColumns : public Gtk::TreeModel::ColumnRecord { - public: - ModelColumns(); - - Gtk::TreeModelColumn<Glib::ustring> m_col_code; - Gtk::TreeModelColumn<Glib::ustring> m_col_expires; - Gtk::TreeModelColumn<Glib::ustring> m_col_inviter; - Gtk::TreeModelColumn<Glib::ustring> m_col_temporary; - Gtk::TreeModelColumn<int> m_col_uses; - Gtk::TreeModelColumn<Glib::ustring> m_col_max_uses; - }; - - ModelColumns m_columns; - Glib::RefPtr<Gtk::ListStore> m_model; - - Gtk::Menu m_menu; - Gtk::MenuItem m_menu_delete; -}; diff --git a/windows/guildsettings/memberspane.cpp b/windows/guildsettings/memberspane.cpp deleted file mode 100644 index 36c5c0b..0000000 --- a/windows/guildsettings/memberspane.cpp +++ /dev/null @@ -1,410 +0,0 @@ -#include "memberspane.hpp" -#include "abaddon.hpp" - -GuildSettingsMembersPane::GuildSettingsMembersPane(Snowflake id) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , GuildID(id) - , m_layout(Gtk::ORIENTATION_HORIZONTAL) - , m_member_list(id) - , m_member_info(id) { - set_name("guild-members-pane"); - set_hexpand(true); - set_vexpand(true); - - m_member_list.signal_member_select().connect(sigc::mem_fun(m_member_info, &GuildSettingsMembersPaneInfo::SetUser)); - - m_note.set_label("Some members may not be shown if the client is not aware of them"); - m_note.set_single_line_mode(true); - m_note.set_ellipsize(Pango::ELLIPSIZE_END); - - m_layout.set_homogeneous(true); - m_layout.add(m_member_list); - m_layout.add(m_member_info); - add(m_note); - add(m_layout); - - m_member_list.show(); - m_member_info.show(); - m_note.show(); - m_layout.show(); -} - -GuildSettingsMembersPaneMembers::GuildSettingsMembersPaneMembers(Snowflake id) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , GuildID(id) { - m_list_scroll.get_style_context()->add_class("guild-members-pane-list"); - - m_list_scroll.set_hexpand(true); - m_list_scroll.set_vexpand(true); - m_list_scroll.set_propagate_natural_height(true); - - auto &discord = Abaddon::Get().GetDiscordClient(); - auto members = discord.GetUsersInGuild(id); - const auto guild = *discord.GetGuild(GuildID); - for (const auto member_id : members) { - auto member = discord.GetMember(member_id, GuildID); - if (!member.has_value()) continue; // fixme this should not be necessary - member->User = discord.GetUser(member_id); - if (member->User->IsDeleted()) continue; - auto *row = Gtk::manage(new GuildSettingsMembersListItem(guild, *member)); - row->show(); - m_list.add(*row); - } - - m_list.set_selection_mode(Gtk::SELECTION_SINGLE); - m_list.signal_row_selected().connect([this](Gtk::ListBoxRow *selected_) { - if (auto *selected = dynamic_cast<GuildSettingsMembersListItem *>(selected_)) - m_signal_member_select.emit(selected->UserID); - }); - - m_search.set_placeholder_text("Filter"); - m_search.signal_changed().connect([this] { - m_list.invalidate_filter(); - }); - - m_list.set_filter_func([this](Gtk::ListBoxRow *row_) -> bool { - const auto search_term = m_search.get_text(); - if (search_term.size() == 0) return true; - if (auto *row = dynamic_cast<GuildSettingsMembersListItem *>(row_)) - return StringContainsCaseless(row->DisplayTerm, m_search.get_text()); - return true; - }); - - m_list_scroll.add(m_list); - add(m_search); - add(m_list_scroll); - - m_search.show(); - m_list.show(); - m_list_scroll.show(); -} - -GuildSettingsMembersPaneMembers::type_signal_member_select GuildSettingsMembersPaneMembers::signal_member_select() { - return m_signal_member_select; -} - -GuildSettingsMembersListItem::GuildSettingsMembersListItem(const GuildData &guild, const GuildMember &member) - : UserID(member.User->ID) - , GuildID(guild.ID) - , m_avatar(32, 32) { - m_avatar.SetAnimated(true); - - m_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { - Abaddon::Get().ShowUserMenu(reinterpret_cast<GdkEvent *>(event), UserID, GuildID); - return true; - } - return false; - }); - - auto &discord = Abaddon::Get().GetDiscordClient(); - - if (member.User->HasAnimatedAvatar() && Abaddon::Get().GetSettings().GetShowAnimations()) - m_avatar.SetURL(member.User->GetAvatarURL("gif", "32")); - else - m_avatar.SetURL(member.User->GetAvatarURL("png", "32")); - - DisplayTerm = member.User->Username + "#" + member.User->Discriminator; - - const auto member_update_cb = [this](Snowflake guild_id, Snowflake user_id) { - if (user_id == UserID) - UpdateColor(); - }; - discord.signal_guild_member_update().connect(sigc::track_obj(member_update_cb, *this)); - UpdateColor(); - - static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown(); - if (crown && guild.OwnerID == member.User->ID) { - try { - const static auto crown_path = Abaddon::GetResPath("/crown.png"); - auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12); - m_crown = Gtk::manage(new Gtk::Image(pixbuf)); - m_crown->set_valign(Gtk::ALIGN_CENTER); - m_crown->set_margin_start(10); - m_crown->show(); - } catch (...) {} - } - - m_avatar.set_margin_end(5); - m_avatar.set_halign(Gtk::ALIGN_START); - m_avatar.set_valign(Gtk::ALIGN_CENTER); - m_name.set_halign(Gtk::ALIGN_START); - m_name.set_valign(Gtk::ALIGN_CENTER); - - m_main.set_hexpand(true); - - m_main.add(m_avatar); - m_main.add(m_name); - if (m_crown != nullptr) - m_main.add(*m_crown); - - m_ev.add(m_main); - add(m_ev); - - m_avatar.show(); - m_name.show(); - m_main.show(); - m_ev.show(); -} - -void GuildSettingsMembersListItem::UpdateColor() { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto user = *discord.GetUser(UserID); - if (auto color_id = discord.GetMemberHoistedRole(GuildID, UserID, true); color_id.IsValid()) { - auto role = *discord.GetRole(color_id); - m_name.set_markup("<span color='#" + IntToCSSColor(role.Color) + "'>" + user.GetEscapedBoldString<false>() + "</span>"); - } else - m_name.set_markup(user.GetEscapedBoldString<false>()); -} - -GuildSettingsMembersPaneInfo::GuildSettingsMembersPaneInfo(Snowflake guild_id) - : GuildID(guild_id) - , m_roles(guild_id) - , m_box(Gtk::ORIENTATION_VERTICAL) { - get_style_context()->add_class("guild-members-pane-info"); - - const auto label = [](Gtk::Label &lbl) { - lbl.set_single_line_mode(true); - lbl.set_halign(Gtk::ALIGN_START); - lbl.set_valign(Gtk::ALIGN_START); - lbl.set_ellipsize(Pango::ELLIPSIZE_END); - lbl.set_margin_bottom(5); - lbl.show(); - }; - - m_bot.set_text("User is a bot"); - - label(m_bot); - label(m_id); - label(m_created); - label(m_joined); - label(m_nickname); - label(m_boosting); - - m_box.set_halign(Gtk::ALIGN_FILL); - m_box.set_valign(Gtk::ALIGN_START); - m_box.set_hexpand(true); - m_box.set_vexpand(true); - m_box.add(m_bot); - m_box.add(m_id); - m_box.add(m_created); - m_box.add(m_joined); - m_box.add(m_nickname); - m_box.add(m_boosting); - m_box.add(m_roles); - - m_bot.hide(); - m_box.show(); - - add(m_box); -} - -void GuildSettingsMembersPaneInfo::SetUser(Snowflake user_id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto guild = *discord.GetGuild(GuildID); - auto member = *discord.GetMember(user_id, GuildID); - member.User = discord.GetUser(user_id); - - m_bot.set_visible(member.User->IsBot.has_value() && *member.User->IsBot); - - m_id.set_text("User ID: " + std::to_string(user_id)); - m_created.set_text("Account created: " + user_id.GetLocalTimestamp()); - if (member.JoinedAt != "") - m_joined.set_text("Joined server: " + FormatISO8601(member.JoinedAt)); - else - m_joined.set_text("Joined server: Unknown"); - m_nickname.set_text("Nickname: " + member.Nickname); - m_nickname.set_visible(member.Nickname != ""); - if (member.PremiumSince.has_value()) { - m_boosting.set_text("Boosting since " + FormatISO8601(*member.PremiumSince)); - m_boosting.show(); - } else - m_boosting.hide(); - - m_roles.show(); - m_roles.SetRoles(user_id, member.Roles, guild.OwnerID == discord.GetUserData().ID); -} - -GuildSettingsMembersPaneRoles::GuildSettingsMembersPaneRoles(Snowflake guild_id) - : GuildID(guild_id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const bool can_modify = discord.HasGuildPermission(self_id, guild_id, Permission::MANAGE_ROLES); - const auto highest = discord.GetMemberHighestRole(GuildID, self_id); - if (highest.has_value()) - m_hoisted_position = highest->Position; - - discord.signal_role_create().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleCreate)); - discord.signal_role_update().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleUpdate)); - discord.signal_role_delete().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleDelete)); - - const auto guild = *discord.GetGuild(guild_id); - const auto roles = guild.FetchRoles(); - for (const auto &role : roles) { - CreateRow(can_modify, role, guild.OwnerID == self_id); - } - - m_list.set_sort_func([this](Gtk::ListBoxRow *a, Gtk::ListBoxRow *b) -> int { - auto *rowa = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(a); - auto *rowb = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(b); - return rowb->Position - rowa->Position; - }); - - set_propagate_natural_height(true); - set_propagate_natural_width(true); - set_hexpand(true); - set_vexpand(true); - set_halign(Gtk::ALIGN_FILL); - set_valign(Gtk::ALIGN_START); - - m_list.show(); - - add(m_list); -} - -void GuildSettingsMembersPaneRoles::SetRoles(Snowflake user_id, const std::vector<Snowflake> &roles, bool is_owner) { - UserID = user_id; - - for (auto it = m_update_connection.begin(); it != m_update_connection.end();) { - it->disconnect(); - it = m_update_connection.erase(it); - } - - m_set_role_ids = { roles.begin(), roles.end() }; - for (const auto &[role_id, row] : m_rows) { - auto role = *Abaddon::Get().GetDiscordClient().GetRole(role_id); - if (role.ID == GuildID) { - row->SetChecked(true); - row->SetToggleable(false); - } else { - row->SetToggleable(role.Position < m_hoisted_position || is_owner); - row->SetChecked(m_set_role_ids.find(role_id) != m_set_role_ids.end()); - } - } -} - -void GuildSettingsMembersPaneRoles::CreateRow(bool has_manage_roles, const RoleData &role, bool is_owner) { - auto *row = Gtk::manage(new GuildSettingsMembersPaneRolesItem(has_manage_roles, role)); - if (role.ID == GuildID) { - row->SetChecked(true); - row->SetToggleable(false); - } else - row->SetToggleable(role.Position < m_hoisted_position || is_owner); - row->signal_role_click().connect(sigc::mem_fun(*this, &GuildSettingsMembersPaneRoles::OnRoleToggle)); - row->show(); - m_rows[role.ID] = row; - m_list.add(*row); -} - -void GuildSettingsMembersPaneRoles::OnRoleToggle(Snowflake role_id, bool new_set) { - auto row = m_rows.at(role_id); - row->SetToggleable(false); - auto &discord = Abaddon::Get().GetDiscordClient(); - auto cb = [this, new_set, role_id, row](bool success) { - if (!success) { // undo - if (new_set) - m_set_role_ids.erase(role_id); - else - m_set_role_ids.insert(role_id); - } else - row->SetChecked(new_set); - - row->SetToggleable(true); - }; - - if (new_set) - m_set_role_ids.insert(role_id); - else - m_set_role_ids.erase(role_id); - - // hack to prevent cb from being called if SetRoles is called before callback completion - sigc::signal<void, bool> tmp; - m_update_connection.push_back(tmp.connect(std::move(cb))); - const auto tmp_cb = [this, tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); }; - discord.SetMemberRoles(GuildID, UserID, m_set_role_ids.begin(), m_set_role_ids.end(), sigc::track_obj(tmp_cb, *this)); -} - -void GuildSettingsMembersPaneRoles::OnRoleCreate(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID) return; - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto self_id = discord.GetUserData().ID; - const bool can_modify = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_ROLES); - const auto role = *discord.GetRole(role_id); - CreateRow(can_modify, role, discord.GetGuild(guild_id)->OwnerID == self_id); -} - -void GuildSettingsMembersPaneRoles::OnRoleUpdate(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID) return; - auto role = *Abaddon::Get().GetDiscordClient().GetRole(role_id); - m_rows.at(role_id)->UpdateRoleData(role); - m_list.invalidate_sort(); -} - -void GuildSettingsMembersPaneRoles::OnRoleDelete(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID) return; - delete m_rows.at(role_id); -} - -GuildSettingsMembersPaneRolesItem::GuildSettingsMembersPaneRolesItem(bool sensitive, const RoleData &role) - : RoleID(role.ID) { - UpdateRoleData(role); - - m_main.set_hexpand(true); - m_main.set_vexpand(true); - - const auto cb = [this](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { - m_signal_role_click.emit(RoleID, !m_check.get_active()); - return true; - } - return false; - }; - m_check.signal_button_press_event().connect(cb, false); - - m_desired_sensitivity = sensitive; - ComputeSensitivity(); - - m_check.set_margin_start(5); - m_label.set_margin_start(5); - - m_main.add(m_check); - m_main.add(m_label); - add(m_main); - m_check.show(); - m_label.show(); - m_main.show(); -} - -void GuildSettingsMembersPaneRolesItem::SetChecked(bool checked) { - m_check.set_active(checked); -} - -void GuildSettingsMembersPaneRolesItem::SetToggleable(bool toggleable) { - m_desired_sensitivity = toggleable; - ComputeSensitivity(); -} - -void GuildSettingsMembersPaneRolesItem::UpdateRoleData(const RoleData &role) { - m_role = role; - Position = role.Position; - UpdateLabel(); -} - -void GuildSettingsMembersPaneRolesItem::UpdateLabel() { - if (m_role.Color) - m_label.set_markup("<span color='#" + IntToCSSColor(m_role.Color) + "'>" + Glib::Markup::escape_text(m_role.Name) + "</span>"); - else - m_label.set_text(m_role.Name); -} - -void GuildSettingsMembersPaneRolesItem::ComputeSensitivity() { - if (m_role.IsManaged) { - m_check.set_sensitive(false); - return; - } - m_check.set_sensitive(m_desired_sensitivity); -} - -GuildSettingsMembersPaneRolesItem::type_signal_role_click GuildSettingsMembersPaneRolesItem::signal_role_click() { - return m_signal_role_click; -} diff --git a/windows/guildsettings/memberspane.hpp b/windows/guildsettings/memberspane.hpp deleted file mode 100644 index 01398da..0000000 --- a/windows/guildsettings/memberspane.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once -#include <unordered_set> -#include <gtkmm.h> -#include "discord/member.hpp" -#include "discord/guild.hpp" -#include "components/lazyimage.hpp" - -class GuildSettingsMembersPaneRolesItem : public Gtk::ListBoxRow { -public: - GuildSettingsMembersPaneRolesItem(bool sensitive, const RoleData &role); - void SetChecked(bool checked); - void SetToggleable(bool toggleable); - void UpdateRoleData(const RoleData &role); - - Snowflake RoleID; - int Position; - -private: - void UpdateLabel(); - void ComputeSensitivity(); - bool m_desired_sensitivity = true; - - RoleData m_role; - - Gtk::Box m_main; - Gtk::CheckButton m_check; - Gtk::Label m_label; - - // own thing so we can stop it from actually changing - typedef sigc::signal<void, Snowflake, bool> type_signal_role_click; - - type_signal_role_click m_signal_role_click; - -public: - type_signal_role_click signal_role_click(); -}; - -class GuildSettingsMembersPaneRoles : public Gtk::ScrolledWindow { -public: - GuildSettingsMembersPaneRoles(Snowflake guild_id); - - void SetRoles(Snowflake user_id, const std::vector<Snowflake> &roles, bool is_owner); - -private: - void CreateRow(bool has_manage_roles, const RoleData &role, bool is_owner); - - void OnRoleToggle(Snowflake role_id, bool new_set); - - void OnRoleCreate(Snowflake guild_id, Snowflake role_id); - void OnRoleUpdate(Snowflake guild_id, Snowflake role_id); - void OnRoleDelete(Snowflake guild_id, Snowflake role_id); - - int m_hoisted_position = 0; - - std::vector<sigc::connection> m_update_connection; - - std::unordered_set<Snowflake> m_set_role_ids; - - Snowflake GuildID; - Snowflake UserID; - - Gtk::ListBox m_list; - - std::unordered_map<Snowflake, GuildSettingsMembersPaneRolesItem *> m_rows; -}; - -class GuildSettingsMembersPaneInfo : public Gtk::ScrolledWindow { -public: - GuildSettingsMembersPaneInfo(Snowflake guild_id); - - void SetUser(Snowflake user_id); - -private: - Snowflake GuildID; - Snowflake UserID; - - Gtk::Label m_bot; - Gtk::Label m_id; - Gtk::Label m_created; - Gtk::Label m_joined; - Gtk::Label m_nickname; - Gtk::Label m_boosting; - GuildSettingsMembersPaneRoles m_roles; - Gtk::Box m_box; -}; - -class GuildSettingsMembersPaneMembers : public Gtk::Box { -public: - GuildSettingsMembersPaneMembers(Snowflake id); - -private: - Snowflake GuildID; - - Gtk::Entry m_search; - Gtk::ScrolledWindow m_list_scroll; - Gtk::ListBox m_list; - - typedef sigc::signal<void, Snowflake> type_signal_member_select; - type_signal_member_select m_signal_member_select; - -public: - type_signal_member_select signal_member_select(); -}; - -class GuildSettingsMembersListItem : public Gtk::ListBoxRow { -public: - GuildSettingsMembersListItem(const GuildData &guild, const GuildMember &member); - - Glib::ustring DisplayTerm; - - Snowflake UserID; - Snowflake GuildID; - -private: - void UpdateColor(); - - Gtk::EventBox m_ev; - LazyImage m_avatar; - Gtk::Label m_name; - Gtk::Box m_main; - Gtk::Image *m_crown = nullptr; -}; - -class GuildSettingsMembersPane : public Gtk::Box { -public: - GuildSettingsMembersPane(Snowflake id); - -private: - Snowflake GuildID; - - Gtk::Box m_layout; - Gtk::Label m_note; - GuildSettingsMembersPaneMembers m_member_list; - GuildSettingsMembersPaneInfo m_member_info; -}; diff --git a/windows/guildsettings/rolespane.cpp b/windows/guildsettings/rolespane.cpp deleted file mode 100644 index 8d355ee..0000000 --- a/windows/guildsettings/rolespane.cpp +++ /dev/null @@ -1,419 +0,0 @@ -#include "rolespane.hpp" -#include "abaddon.hpp" - -GuildSettingsRolesPane::GuildSettingsRolesPane(Snowflake id) - : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) - , GuildID(id) - , m_roles_list(id) - , m_roles_perms(id) { - set_name("guild-roles-pane"); - set_hexpand(true); - set_vexpand(true); - - m_roles_list.signal_role_select().connect(sigc::mem_fun(*this, &GuildSettingsRolesPane::OnRoleSelect)); - - m_roles_perms.set_sensitive(false); - - m_layout.set_homogeneous(true); - m_layout.add(m_roles_list); - m_layout.add(m_roles_perms); - add(m_layout); - - m_roles_list.show(); - m_roles_perms.show(); - m_layout.show(); -} - -void GuildSettingsRolesPane::OnRoleSelect(Snowflake role_id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto role = *discord.GetRole(role_id); - m_roles_perms.SetRole(role); - m_roles_perms.set_sensitive(discord.CanModifyRole(GuildID, role_id)); -} - -static std::vector<Gtk::TargetEntry> g_target_entries = { - Gtk::TargetEntry("GTK_LIST_ROLES_ROW", Gtk::TARGET_SAME_APP, 0) -}; - -GuildSettingsRolesPaneRoles::GuildSettingsRolesPaneRoles(Snowflake guild_id) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , GuildID(guild_id) { - m_list.get_style_context()->add_class("guild-roles-pane-list"); - - m_list_scroll.set_hexpand(true); - m_list_scroll.set_vexpand(true); - m_list_scroll.set_propagate_natural_height(true); - m_list_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - - m_list.set_selection_mode(Gtk::SELECTION_SINGLE); - m_list.signal_row_selected().connect([this](Gtk::ListBoxRow *selected_) { - if (auto *selected = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(selected_)) - m_signal_role_select.emit(selected->RoleID); - }); - - m_list.set_focus_vadjustment(m_list_scroll.get_vadjustment()); - m_list.signal_on_drop().connect([this](Gtk::ListBoxRow *row_, int new_index) -> bool { - if (auto *row = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(row_)) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto num_rows = m_list.get_children().size(); - const auto new_pos = num_rows - new_index - 1; - if (row->RoleID == GuildID) return true; // moving role @everyone - if (static_cast<size_t>(new_index) == num_rows) return true; // trying to move row below @everyone - // make sure it wont modify a neighbor role u dont have perms to modify - if (!discord.CanModifyRole(GuildID, row->RoleID)) return false; - const auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to set role position", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); - dlg.run(); - } - }; - discord.ModifyRolePosition(GuildID, row->RoleID, new_pos, sigc::track_obj(cb, *this)); - return true; - } - return false; - }); - - auto &discord = Abaddon::Get().GetDiscordClient(); - discord.signal_role_create().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneRoles::OnRoleCreate)); - discord.signal_role_delete().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneRoles::OnRoleDelete)); - - const auto guild = *discord.GetGuild(GuildID); - const auto roles = guild.FetchRoles(); - const bool can_modify = discord.HasGuildPermission(discord.GetUserData().ID, GuildID, Permission::MANAGE_ROLES); - for (const auto &role : roles) { - auto *row = Gtk::manage(new GuildSettingsRolesPaneRolesListItem(guild, role)); - row->drag_source_set(g_target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); - row->set_margin_start(5); - row->set_halign(Gtk::ALIGN_FILL); - row->show(); - m_rows[role.ID] = row; - if (can_modify) - m_list.add_draggable(row); - else - m_list.add(*row); - } - - m_list.set_sort_func([this](Gtk::ListBoxRow *rowa_, Gtk::ListBoxRow *rowb_) -> int { - auto *rowa = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(rowa_); - auto *rowb = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(rowb_); - return rowb->Position - rowa->Position; - }); - m_list.invalidate_sort(); - - m_list.set_filter_func([this](Gtk::ListBoxRow *row_) -> bool { - const auto search_term = m_search.get_text(); - if (search_term.size() == 0) return true; - if (auto *row = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(row_)) - return StringContainsCaseless(row->DisplayTerm, m_search.get_text()); - return true; - }); - - m_search.set_placeholder_text("Filter"); - m_search.signal_changed().connect([this] { - m_list.invalidate_filter(); - }); - - m_list_scroll.add(m_list); - add(m_search); - add(m_list_scroll); - - m_search.show(); - m_list.show(); - m_list_scroll.show(); -} - -void GuildSettingsRolesPaneRoles::OnRoleCreate(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID) return; - auto &discord = Abaddon::Get().GetDiscordClient(); - const bool can_modify = discord.HasGuildPermission(discord.GetUserData().ID, guild_id, Permission::MANAGE_ROLES); - const auto guild = *discord.GetGuild(guild_id); - const auto role = *discord.GetRole(role_id); - auto *row = Gtk::manage(new GuildSettingsRolesPaneRolesListItem(guild, role)); - row->show(); - m_rows[role_id] = row; - if (can_modify) - m_list.add_draggable(row); - else - m_list.add(*row); -} - -void GuildSettingsRolesPaneRoles::OnRoleDelete(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID) return; - auto it = m_rows.find(role_id); - delete it->second; - m_rows.erase(it); -} - -GuildSettingsRolesPaneRoles::type_signal_role_select GuildSettingsRolesPaneRoles::signal_role_select() { - return m_signal_role_select; -} - -GuildSettingsRolesPaneRolesListItem::GuildSettingsRolesPaneRolesListItem(const GuildData &guild, const RoleData &role) - : GuildID(guild.ID) - , RoleID(role.ID) - , Position(role.Position) { - auto &discord = Abaddon::Get().GetDiscordClient(); - - set_hexpand(true); - - UpdateItem(role); - - discord.signal_role_update().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneRolesListItem::OnRoleUpdate)); - - m_name.set_ellipsize(Pango::ELLIPSIZE_END); - - m_ev.set_halign(Gtk::ALIGN_START); - m_ev.add(m_name); - add(m_ev); - - m_name.show(); - m_ev.show(); -} - -void GuildSettingsRolesPaneRolesListItem::UpdateItem(const RoleData &role) { - DisplayTerm = role.Name; - - if (role.Color != 0) - m_name.set_markup("<span color='#" + IntToCSSColor(role.Color) + "'>" + - Glib::Markup::escape_text(role.Name) + - "</span>"); - else - m_name.set_text(role.Name); -} - -void GuildSettingsRolesPaneRolesListItem::OnRoleUpdate(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID || role_id != RoleID) return; - const auto role = Abaddon::Get().GetDiscordClient().GetRole(RoleID); - if (!role.has_value()) return; - Position = role->Position; - UpdateItem(*role); - changed(); -} - -GuildSettingsRolesPaneInfo::GuildSettingsRolesPaneInfo(Snowflake guild_id) - : GuildID(guild_id) - , m_layout(Gtk::ORIENTATION_VERTICAL) - , m_meta(Gtk::ORIENTATION_HORIZONTAL) { - set_propagate_natural_height(true); - set_propagate_natural_width(true); - - auto &discord = Abaddon::Get().GetDiscordClient(); - discord.signal_role_update().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneInfo::OnRoleUpdate)); - - const auto cb = [this](GdkEventKey *e) -> bool { - if (e->keyval == GDK_KEY_Return) - UpdateRoleName(); - return false; - }; - m_role_name.signal_key_press_event().connect(cb, false); - - m_role_name.set_tooltip_text("Press enter to submit"); - - m_role_name.set_max_length(100); - - m_role_name.set_margin_top(5); - m_role_name.set_margin_bottom(5); - m_role_name.set_margin_start(5); - m_role_name.set_margin_end(5); - - m_color_button.set_margin_top(5); - m_color_button.set_margin_bottom(5); - m_color_button.set_margin_start(5); - m_color_button.set_margin_end(5); - - m_color_button.signal_color_set().connect([this, &discord]() { - const auto color = m_color_button.get_rgba(); - const auto cb = [this, &discord](DiscordError code) { - if (code != DiscordError::NONE) { - m_color_button.set_rgba(IntToRGBA(discord.GetRole(RoleID)->Color)); - Gtk::MessageDialog dlg("Failed to set role color", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); - dlg.run(); - } - }; - discord.ModifyRoleColor(GuildID, RoleID, color, cb); - }); - - int left_ypos = 0; - int right_ypos = 0; - - const int LEFT = 0; - const int RIGHT = 1; - - auto add_perms = [&](const std::string &label, int side, const std::initializer_list<Permission> &perms) { - int &pos = side == LEFT ? left_ypos : right_ypos; - auto *header = Gtk::manage(new Gtk::Label(label)); - header->show(); - m_grid.attach(*header, side, pos++, 1, 1); - for (const auto perm : perms) { - auto *btn = Gtk::manage(new GuildSettingsRolesPanePermItem(perm)); - btn->signal_permission_click().connect(sigc::mem_fun(*this, &GuildSettingsRolesPaneInfo::OnPermissionToggle)); - m_perm_items[perm] = btn; - btn->show(); - m_grid.attach(*btn, side, pos++, 1, 1); - } - pos++; - }; - - // fuck you clang-format you suck - // clang-format off - add_perms("General", LEFT, { - Permission::VIEW_CHANNEL, - Permission::MANAGE_CHANNELS, - Permission::MANAGE_ROLES, - Permission::MANAGE_EMOJIS, - Permission::VIEW_AUDIT_LOG, - Permission::VIEW_GUILD_INSIGHTS, - Permission::MANAGE_WEBHOOKS, - Permission::MANAGE_GUILD }); - - add_perms("Membership", LEFT, { - Permission::CREATE_INSTANT_INVITE, - Permission::CHANGE_NICKNAME, - Permission::MANAGE_NICKNAMES, - Permission::KICK_MEMBERS, - Permission::BAN_MEMBERS }); - - add_perms("Text Channels", RIGHT, { - Permission::SEND_MESSAGES, - Permission::USE_PUBLIC_THREADS, - Permission::USE_PRIVATE_THREADS, - Permission::EMBED_LINKS, - Permission::ATTACH_FILES, - Permission::ADD_REACTIONS, - Permission::USE_EXTERNAL_EMOJIS, - Permission::MENTION_EVERYONE, - Permission::MANAGE_MESSAGES, - Permission::MANAGE_THREADS, - Permission::READ_MESSAGE_HISTORY, - Permission::SEND_TTS_MESSAGES, - Permission::USE_SLASH_COMMANDS }); - - add_perms("Voice Channels", RIGHT, { - Permission::CONNECT, - Permission::SPEAK, - Permission::STREAM, - Permission::USE_VAD, - Permission::PRIORITY_SPEAKER, - Permission::MUTE_MEMBERS, - Permission::DEAFEN_MEMBERS, - Permission::MOVE_MEMBERS }); - - add_perms("Advanced", LEFT, { Permission::ADMINISTRATOR }); - - // clang-format on - - m_meta.add(m_role_name); - m_meta.add(m_color_button); - m_layout.add(m_meta); - m_layout.add(m_grid); - add(m_layout); - m_meta.show(); - m_color_button.show(); - m_role_name.show(); - m_layout.show(); - m_grid.show(); -} - -void GuildSettingsRolesPaneInfo::SetRole(const RoleData &role) { - for (auto it = m_update_connections.begin(); it != m_update_connections.end();) { - it->disconnect(); - it = m_update_connections.erase(it); - } - - if (role.Color != 0) { - m_color_button.set_rgba(IntToRGBA(role.Color)); - } else { - static Gdk::RGBA trans; - trans.set_alpha(0.0); - m_color_button.set_rgba(trans); - } - - m_role_name.set_text(role.Name); - - RoleID = role.ID; - m_perms = role.Permissions; - for (const auto [perm, btn] : m_perm_items) { - btn->set_sensitive(true); - btn->set_active((role.Permissions & perm) == perm); - } -} - -void GuildSettingsRolesPaneInfo::OnRoleUpdate(Snowflake guild_id, Snowflake role_id) { - if (guild_id != GuildID || role_id != RoleID) return; - const auto role = *Abaddon::Get().GetDiscordClient().GetRole(RoleID); - m_role_name.set_text(role.Name); - - if (role.Color != 0) { - m_color_button.set_rgba(IntToRGBA(role.Color)); - } else { - static Gdk::RGBA trans; - trans.set_alpha(0.0); - m_color_button.set_rgba(trans); - } - - m_perms = role.Permissions; - for (const auto [perm, btn] : m_perm_items) - btn->set_active((role.Permissions & perm) == perm); -} - -void GuildSettingsRolesPaneInfo::OnPermissionToggle(Permission perm, bool new_set) { - auto btn = m_perm_items.at(perm); - btn->set_sensitive(false); - auto &discord = Abaddon::Get().GetDiscordClient(); - auto cb = [this, new_set, perm, btn](bool success) { - if (!success) { // undo - if (new_set) - m_perms &= ~perm; - else - m_perms |= perm; - } else - btn->set_active(new_set); - btn->set_sensitive(true); - }; - - if (new_set) - m_perms |= perm; - else - m_perms &= ~perm; - - sigc::signal<void, bool> tmp; - m_update_connections.push_back(tmp.connect(std::move(cb))); - const auto tmp_cb = [this, tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); }; - discord.ModifyRolePermissions(GuildID, RoleID, m_perms, sigc::track_obj(tmp_cb, *this)); -} - -void GuildSettingsRolesPaneInfo::UpdateRoleName() { - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.GetRole(RoleID)->Name == m_role_name.get_text()) return; - - const auto cb = [this, &discord](DiscordError code) { - if (code != DiscordError::NONE) { - m_role_name.set_text(discord.GetRole(RoleID)->Name); - Gtk::MessageDialog dlg("Failed to set role name", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER_ON_PARENT); - dlg.run(); - } - }; - discord.ModifyRoleName(GuildID, RoleID, m_role_name.get_text(), cb); -} - -GuildSettingsRolesPanePermItem::GuildSettingsRolesPanePermItem(Permission perm) - : Gtk::CheckButton(GetPermissionString(perm)) - , m_permission(perm) { - set_tooltip_text(GetPermissionDescription(m_permission)); - - const auto cb = [this](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { - m_signal_permission.emit(m_permission, !get_active()); - return true; - } - return false; - }; - signal_button_press_event().connect(cb, false); -} - -GuildSettingsRolesPanePermItem::type_signal_permission_click GuildSettingsRolesPanePermItem::signal_permission_click() { - return m_signal_permission; -} diff --git a/windows/guildsettings/rolespane.hpp b/windows/guildsettings/rolespane.hpp deleted file mode 100644 index 2999f32..0000000 --- a/windows/guildsettings/rolespane.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include <unordered_map> -#include "discord/guild.hpp" -#include "components/draglistbox.hpp" - -class GuildSettingsRolesPaneRolesListItem : public Gtk::ListBoxRow { -public: - GuildSettingsRolesPaneRolesListItem(const GuildData &guild, const RoleData &role); - - Glib::ustring DisplayTerm; - - Snowflake GuildID; - Snowflake RoleID; - int Position; - -private: - void UpdateItem(const RoleData &role); - void OnRoleUpdate(Snowflake guild_id, Snowflake role_id); - - Gtk::EventBox m_ev; - Gtk::Label m_name; -}; - -class GuildSettingsRolesPaneRoles : public Gtk::Box { -public: - GuildSettingsRolesPaneRoles(Snowflake guild_id); - -private: - void OnRoleCreate(Snowflake guild_id, Snowflake role_id); - void OnRoleDelete(Snowflake guild_id, Snowflake role_id); - - Snowflake GuildID; - - Gtk::Entry m_search; - Gtk::ScrolledWindow m_list_scroll; - DragListBox m_list; - - typedef sigc::signal<void, Snowflake /* role_id */> type_signal_role_select; - type_signal_role_select m_signal_role_select; - -public: - std::unordered_map<Snowflake, GuildSettingsRolesPaneRolesListItem *> m_rows; - type_signal_role_select signal_role_select(); -}; - -class GuildSettingsRolesPanePermItem : public Gtk::CheckButton { -public: - GuildSettingsRolesPanePermItem(Permission perm); - -private: - Permission m_permission; - - typedef sigc::signal<void, Permission, bool> type_signal_permission_click; - - type_signal_permission_click m_signal_permission; - -public: - type_signal_permission_click signal_permission_click(); -}; - -class GuildSettingsRolesPaneInfo : public Gtk::ScrolledWindow { -public: - GuildSettingsRolesPaneInfo(Snowflake guild_id); - - void SetRole(const RoleData &role); - -private: - void OnRoleUpdate(Snowflake guild_id, Snowflake role_id); - void OnPermissionToggle(Permission perm, bool new_set); - - void UpdateRoleName(); - - Snowflake GuildID; - Snowflake RoleID; - - Permission m_perms; - - std::vector<sigc::connection> m_update_connections; - - Gtk::Box m_layout; - Gtk::Box m_meta; - Gtk::Entry m_role_name; - Gtk::ColorButton m_color_button; - Gtk::Grid m_grid; - - std::unordered_map<Permission, GuildSettingsRolesPanePermItem *> m_perm_items; -}; - -class GuildSettingsRolesPane : public Gtk::Box { -public: - GuildSettingsRolesPane(Snowflake id); - -private: - void OnRoleSelect(Snowflake role_id); - - Snowflake GuildID; - - Gtk::Box m_layout; - GuildSettingsRolesPaneRoles m_roles_list; - GuildSettingsRolesPaneInfo m_roles_perms; -}; diff --git a/windows/guildsettingswindow.cpp b/windows/guildsettingswindow.cpp deleted file mode 100644 index 1e3395d..0000000 --- a/windows/guildsettingswindow.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "guildsettingswindow.hpp" -#include "abaddon.hpp" - -GuildSettingsWindow::GuildSettingsWindow(Snowflake id) - : m_main(Gtk::ORIENTATION_VERTICAL) - , m_pane_info(id) - , m_pane_members(id) - , m_pane_roles(id) - , m_pane_bans(id) - , m_pane_invites(id) - , m_pane_emojis(id) - , m_pane_audit_log(id) - , GuildID(id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto guild = *discord.GetGuild(id); - - auto guild_update_cb = [this](Snowflake id) { - if (id != GuildID) return; - const auto guild = *Abaddon::Get().GetDiscordClient().GetGuild(id); - set_title(guild.Name); - if (guild.HasIcon()) - Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL(), sigc::mem_fun(*this, &GuildSettingsWindow::set_icon)); - }; - discord.signal_guild_update().connect(sigc::track_obj(guild_update_cb, *this)); - - set_name("guild-settings"); - set_default_size(800, 600); - set_title(guild.Name); - set_position(Gtk::WIN_POS_CENTER); - get_style_context()->add_class("app-window"); - get_style_context()->add_class("app-popup"); - get_style_context()->add_class("guild-settings-window"); - - if (guild.HasIcon()) { - Abaddon::Get().GetImageManager().LoadFromURL(guild.GetIconURL(), sigc::mem_fun(*this, &GuildSettingsWindow::set_icon)); - } - - m_switcher.set_stack(m_stack); - m_switcher.set_halign(Gtk::ALIGN_CENTER); - m_switcher.set_hexpand(true); - m_switcher.set_margin_top(10); - m_switcher.show(); - - m_pane_info.show(); - m_pane_members.show(); - m_pane_roles.show(); - m_pane_bans.show(); - m_pane_invites.show(); - m_pane_emojis.show(); - m_pane_audit_log.show(); - - m_stack.set_transition_duration(100); - m_stack.set_transition_type(Gtk::STACK_TRANSITION_TYPE_CROSSFADE); - m_stack.set_margin_top(10); - m_stack.set_margin_bottom(10); - m_stack.set_margin_left(10); - m_stack.set_margin_right(10); - - const auto self_id = discord.GetUserData().ID; - - m_stack.add(m_pane_info, "info", "Info"); - m_stack.add(m_pane_members, "members", "Members"); - m_stack.add(m_pane_roles, "roles", "Roles"); - m_stack.add(m_pane_bans, "bans", "Bans"); - if (discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD)) - m_stack.add(m_pane_invites, "invites", "Invites"); - m_stack.add(m_pane_emojis, "emojis", "Emojis"); - if (discord.HasGuildPermission(self_id, GuildID, Permission::VIEW_AUDIT_LOG)) - m_stack.add(m_pane_audit_log, "audit-log", "Audit Log"); - m_stack.show(); - - m_main.add(m_switcher); - m_main.add(m_stack); - m_main.show(); - add(m_main); -} diff --git a/windows/guildsettingswindow.hpp b/windows/guildsettingswindow.hpp deleted file mode 100644 index b591640..0000000 --- a/windows/guildsettingswindow.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/snowflake.hpp" -#include "guildsettings/infopane.hpp" -#include "guildsettings/banspane.hpp" -#include "guildsettings/invitespane.hpp" -#include "guildsettings/auditlogpane.hpp" -#include "guildsettings/memberspane.hpp" -#include "guildsettings/rolespane.hpp" -#include "guildsettings/emojispane.hpp" - -class GuildSettingsWindow : public Gtk::Window { -public: - GuildSettingsWindow(Snowflake id); - -private: - Gtk::Box m_main; - Gtk::Stack m_stack; - Gtk::StackSwitcher m_switcher; - - GuildSettingsInfoPane m_pane_info; - GuildSettingsMembersPane m_pane_members; - GuildSettingsRolesPane m_pane_roles; - GuildSettingsBansPane m_pane_bans; - GuildSettingsInvitesPane m_pane_invites; - GuildSettingsEmojisPane m_pane_emojis; - GuildSettingsAuditLogPane m_pane_audit_log; - - Snowflake GuildID; -}; diff --git a/windows/mainwindow.cpp b/windows/mainwindow.cpp deleted file mode 100644 index 659107a..0000000 --- a/windows/mainwindow.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "mainwindow.hpp" -#include "abaddon.hpp" - -MainWindow::MainWindow() - : m_main_box(Gtk::ORIENTATION_VERTICAL) - , m_content_box(Gtk::ORIENTATION_HORIZONTAL) - , m_chan_content_paned(Gtk::ORIENTATION_HORIZONTAL) - , m_content_members_paned(Gtk::ORIENTATION_HORIZONTAL) { - set_default_size(1200, 800); - get_style_context()->add_class("app-window"); - - m_menu_discord.set_label("Discord"); - m_menu_discord.set_submenu(m_menu_discord_sub); - m_menu_discord_connect.set_label("Connect"); - m_menu_discord_connect.set_sensitive(false); - m_menu_discord_disconnect.set_label("Disconnect"); - m_menu_discord_disconnect.set_sensitive(false); - m_menu_discord_set_token.set_label("Set Token"); - m_menu_discord_join_guild.set_label("Accept Invite"); - m_menu_discord_join_guild.set_sensitive(false); - m_menu_discord_set_status.set_label("Set Status"); - m_menu_discord_set_status.set_sensitive(false); - m_menu_discord_add_recipient.set_label("Add user to DM"); - m_menu_discord_sub.append(m_menu_discord_connect); - m_menu_discord_sub.append(m_menu_discord_disconnect); - m_menu_discord_sub.append(m_menu_discord_set_token); - m_menu_discord_sub.append(m_menu_discord_join_guild); - m_menu_discord_sub.append(m_menu_discord_set_status); - m_menu_discord_sub.append(m_menu_discord_add_recipient); - m_menu_discord_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnDiscordSubmenuPopup)); // this gets called twice for some reason - m_menu_discord.set_submenu(m_menu_discord_sub); - - m_menu_file.set_label("File"); - m_menu_file.set_submenu(m_menu_file_sub); - m_menu_file_reload_css.set_label("Reload CSS"); - m_menu_file_clear_cache.set_label("Clear file cache"); - m_menu_file_sub.append(m_menu_file_reload_css); - m_menu_file_sub.append(m_menu_file_clear_cache); - - m_menu_view.set_label("View"); - m_menu_view.set_submenu(m_menu_view_sub); - m_menu_view_friends.set_label("Friends"); - m_menu_view_pins.set_label("Pins"); - m_menu_view_threads.set_label("Threads"); - m_menu_view_sub.append(m_menu_view_friends); - m_menu_view_sub.append(m_menu_view_pins); - m_menu_view_sub.append(m_menu_view_threads); - m_menu_view_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnViewSubmenuPopup)); - - m_menu_bar.append(m_menu_file); - m_menu_bar.append(m_menu_discord); - m_menu_bar.append(m_menu_view); - m_menu_bar.show_all(); - - m_menu_discord_connect.signal_activate().connect([this] { - m_signal_action_connect.emit(); - }); - - m_menu_discord_disconnect.signal_activate().connect([this] { - m_signal_action_disconnect.emit(); - }); - - m_menu_discord_set_token.signal_activate().connect([this] { - m_signal_action_set_token.emit(); - }); - - m_menu_discord_join_guild.signal_activate().connect([this] { - m_signal_action_join_guild.emit(); - }); - - m_menu_file_reload_css.signal_activate().connect([this] { - m_signal_action_reload_css.emit(); - }); - - m_menu_discord_set_status.signal_activate().connect([this] { - m_signal_action_set_status.emit(); - }); - - m_menu_file_clear_cache.signal_activate().connect([this] { - Abaddon::Get().GetImageManager().ClearCache(); - }); - - m_menu_discord_add_recipient.signal_activate().connect([this] { - m_signal_action_add_recipient.emit(GetChatActiveChannel()); - }); - - m_menu_view_friends.signal_activate().connect([this] { - UpdateChatActiveChannel(Snowflake::Invalid); - m_members.UpdateMemberList(); - m_content_stack.set_visible_child("friends"); - }); - - m_menu_view_pins.signal_activate().connect([this] { - m_signal_action_view_pins.emit(GetChatActiveChannel()); - }); - - m_menu_view_threads.signal_activate().connect([this] { - m_signal_action_view_threads.emit(GetChatActiveChannel()); - }); - - m_content_box.set_hexpand(true); - m_content_box.set_vexpand(true); - m_content_box.show(); - - m_main_box.add(m_menu_bar); - m_main_box.add(m_content_box); - m_main_box.show(); - - auto *member_list = m_members.GetRoot(); - auto *chat = m_chat.GetRoot(); - - chat->set_vexpand(true); - chat->set_hexpand(true); - chat->show(); - - m_channel_list.set_vexpand(true); - m_channel_list.set_size_request(-1, -1); - m_channel_list.show(); - - member_list->set_vexpand(true); - member_list->show(); - - m_friends.set_vexpand(true); - m_friends.set_hexpand(true); - m_friends.show(); - - m_content_stack.add(*chat, "chat"); - m_content_stack.add(m_friends, "friends"); - m_content_stack.set_vexpand(true); - m_content_stack.set_hexpand(true); - m_content_stack.set_visible_child("chat"); - m_content_stack.show(); - - m_chan_content_paned.pack1(m_channel_list); - m_chan_content_paned.pack2(m_content_members_paned); - m_chan_content_paned.child_property_shrink(m_channel_list) = false; - m_chan_content_paned.child_property_resize(m_channel_list) = false; - m_chan_content_paned.set_position(200); - m_chan_content_paned.show(); - m_content_box.add(m_chan_content_paned); - - m_content_members_paned.pack1(m_content_stack); - m_content_members_paned.pack2(*member_list); - m_content_members_paned.child_property_shrink(*member_list) = false; - m_content_members_paned.child_property_resize(*member_list) = false; - int w, h; - get_default_size(w, h); // :s - m_content_members_paned.set_position(w - m_chan_content_paned.get_position() - 150); - m_content_members_paned.show(); - - add(m_main_box); -} - -void MainWindow::UpdateComponents() { - bool discord_active = Abaddon::Get().IsDiscordActive(); - - if (!discord_active) { - m_chat.Clear(); - m_members.Clear(); - } else { - m_members.UpdateMemberList(); - } - UpdateChannelListing(); -} - -void MainWindow::UpdateMembers() { - m_members.UpdateMemberList(); -} - -void MainWindow::UpdateChannelListing() { - m_channel_list.UpdateListing(); -} - -void MainWindow::UpdateChatWindowContents() { - auto &discord = Abaddon::Get().GetDiscordClient(); - auto msgs = discord.GetMessagesForChannel(m_chat.GetActiveChannel(), 50); - m_chat.SetMessages(msgs); - m_members.UpdateMemberList(); -} - -void MainWindow::UpdateChatActiveChannel(Snowflake id) { - m_chat.SetActiveChannel(id); - m_members.SetActiveChannel(id); - m_channel_list.SetActiveChannel(id); - m_content_stack.set_visible_child("chat"); -} - -Snowflake MainWindow::GetChatActiveChannel() const { - return m_chat.GetActiveChannel(); -} - -void MainWindow::UpdateChatNewMessage(const Message &data) { - if (data.ChannelID == GetChatActiveChannel()) { - m_chat.AddNewMessage(data); - } -} - -void MainWindow::UpdateChatMessageDeleted(Snowflake id, Snowflake channel_id) { - if (channel_id == GetChatActiveChannel()) - m_chat.DeleteMessage(id); -} - -void MainWindow::UpdateChatMessageUpdated(Snowflake id, Snowflake channel_id) { - if (channel_id == GetChatActiveChannel()) - m_chat.UpdateMessage(id); -} - -void MainWindow::UpdateChatPrependHistory(const std::vector<Message> &msgs) { - m_chat.AddNewHistory(msgs); // given vector should be sorted ascending -} - -void MainWindow::InsertChatInput(std::string text) { - m_chat.InsertChatInput(text); -} - -Snowflake MainWindow::GetChatOldestListedMessage() { - return m_chat.GetOldestListedMessage(); -} - -void MainWindow::UpdateChatReactionAdd(Snowflake id, const Glib::ustring ¶m) { - m_chat.UpdateReactions(id); -} - -void MainWindow::UpdateChatReactionRemove(Snowflake id, const Glib::ustring ¶m) { - m_chat.UpdateReactions(id); -} - -void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) { - auto &discord = Abaddon::Get().GetDiscordClient(); - auto channel_id = GetChatActiveChannel(); - m_menu_discord_add_recipient.set_visible(false); - if (channel_id.IsValid()) { - auto channel = discord.GetChannel(channel_id); - if (channel.has_value() && channel->GetDMRecipients().size() + 1 < 10) - m_menu_discord_add_recipient.set_visible(channel->Type == ChannelType::GROUP_DM); - } - - const bool discord_active = Abaddon::Get().GetDiscordClient().IsStarted(); - - std::string token = Abaddon::Get().GetDiscordToken(); - m_menu_discord_connect.set_sensitive(token.size() > 0 && !discord_active); - m_menu_discord_disconnect.set_sensitive(discord_active); - m_menu_discord_join_guild.set_sensitive(discord_active); - m_menu_discord_set_token.set_sensitive(!discord_active); - m_menu_discord_set_status.set_sensitive(discord_active); -} - -void MainWindow::OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) { - m_menu_view_friends.set_sensitive(Abaddon::Get().GetDiscordClient().IsStarted()); - auto channel_id = GetChatActiveChannel(); - m_menu_view_pins.set_sensitive(false); - m_menu_view_threads.set_sensitive(false); - if (channel_id.IsValid()) { - auto channel = Abaddon::Get().GetDiscordClient().GetChannel(channel_id); - if (channel.has_value()) { - m_menu_view_threads.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->IsThread()); - m_menu_view_pins.set_sensitive(channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM || channel->IsThread()); - } - } -} - -ChannelList *MainWindow::GetChannelList() { - return &m_channel_list; -} - -ChatWindow *MainWindow::GetChatWindow() { - return &m_chat; -} - -MemberList *MainWindow::GetMemberList() { - return &m_members; -} - -MainWindow::type_signal_action_connect MainWindow::signal_action_connect() { - return m_signal_action_connect; -} - -MainWindow::type_signal_action_disconnect MainWindow::signal_action_disconnect() { - return m_signal_action_disconnect; -} - -MainWindow::type_signal_action_set_token MainWindow::signal_action_set_token() { - return m_signal_action_set_token; -} - -MainWindow::type_signal_action_reload_css MainWindow::signal_action_reload_css() { - return m_signal_action_reload_css; -} - -MainWindow::type_signal_action_join_guild MainWindow::signal_action_join_guild() { - return m_signal_action_join_guild; -} - -MainWindow::type_signal_action_set_status MainWindow::signal_action_set_status() { - return m_signal_action_set_status; -} - -MainWindow::type_signal_action_add_recipient MainWindow::signal_action_add_recipient() { - return m_signal_action_add_recipient; -} - -MainWindow::type_signal_action_view_pins MainWindow::signal_action_view_pins() { - return m_signal_action_view_pins; -} - -MainWindow::type_signal_action_view_threads MainWindow::signal_action_view_threads() { - return m_signal_action_view_threads; -} diff --git a/windows/mainwindow.hpp b/windows/mainwindow.hpp deleted file mode 100644 index df1c968..0000000 --- a/windows/mainwindow.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once -#include "components/channels.hpp" -#include "components/chatwindow.hpp" -#include "components/memberlist.hpp" -#include "components/friendslist.hpp" -#include <gtkmm.h> - -class MainWindow : public Gtk::Window { -public: - MainWindow(); - - void UpdateComponents(); - void UpdateMembers(); - void UpdateChannelListing(); - void UpdateChatWindowContents(); - void UpdateChatActiveChannel(Snowflake id); - Snowflake GetChatActiveChannel() const; - void UpdateChatNewMessage(const Message &data); - void UpdateChatMessageDeleted(Snowflake id, Snowflake channel_id); - void UpdateChatMessageUpdated(Snowflake id, Snowflake channel_id); - void UpdateChatPrependHistory(const std::vector<Message> &msgs); - void InsertChatInput(std::string text); - Snowflake GetChatOldestListedMessage(); - void UpdateChatReactionAdd(Snowflake id, const Glib::ustring ¶m); - void UpdateChatReactionRemove(Snowflake id, const Glib::ustring ¶m); - - ChannelList *GetChannelList(); - ChatWindow *GetChatWindow(); - MemberList *GetMemberList(); - -public: - typedef sigc::signal<void> type_signal_action_connect; - typedef sigc::signal<void> type_signal_action_disconnect; - typedef sigc::signal<void> type_signal_action_set_token; - typedef sigc::signal<void> type_signal_action_reload_css; - typedef sigc::signal<void> type_signal_action_join_guild; - typedef sigc::signal<void> type_signal_action_set_status; - // this should probably be removed - typedef sigc::signal<void, Snowflake> type_signal_action_add_recipient; // channel id - typedef sigc::signal<void, Snowflake> type_signal_action_view_pins; // channel id - typedef sigc::signal<void, Snowflake> type_signal_action_view_threads; // channel id - - type_signal_action_connect signal_action_connect(); - type_signal_action_disconnect signal_action_disconnect(); - type_signal_action_set_token signal_action_set_token(); - type_signal_action_reload_css signal_action_reload_css(); - type_signal_action_join_guild signal_action_join_guild(); - type_signal_action_set_status signal_action_set_status(); - type_signal_action_add_recipient signal_action_add_recipient(); - type_signal_action_view_pins signal_action_view_pins(); - type_signal_action_view_threads signal_action_view_threads(); - -protected: - type_signal_action_connect m_signal_action_connect; - type_signal_action_disconnect m_signal_action_disconnect; - type_signal_action_set_token m_signal_action_set_token; - type_signal_action_reload_css m_signal_action_reload_css; - type_signal_action_join_guild m_signal_action_join_guild; - type_signal_action_set_status m_signal_action_set_status; - type_signal_action_add_recipient m_signal_action_add_recipient; - type_signal_action_view_pins m_signal_action_view_pins; - type_signal_action_view_threads m_signal_action_view_threads; - -protected: - Gtk::Box m_main_box; - Gtk::Box m_content_box; - Gtk::Paned m_chan_content_paned; - Gtk::Paned m_content_members_paned; - - ChannelList m_channel_list; - ChatWindow m_chat; - MemberList m_members; - FriendsList m_friends; - - Gtk::Stack m_content_stack; - - Gtk::MenuBar m_menu_bar; - Gtk::MenuItem m_menu_discord; - Gtk::Menu m_menu_discord_sub; - Gtk::MenuItem m_menu_discord_connect; - Gtk::MenuItem m_menu_discord_disconnect; - Gtk::MenuItem m_menu_discord_set_token; - Gtk::MenuItem m_menu_discord_join_guild; - Gtk::MenuItem m_menu_discord_set_status; - Gtk::MenuItem m_menu_discord_add_recipient; // move me somewhere else some day - void OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y); - - Gtk::MenuItem m_menu_file; - Gtk::Menu m_menu_file_sub; - Gtk::MenuItem m_menu_file_reload_css; - Gtk::MenuItem m_menu_file_clear_cache; - - Gtk::MenuItem m_menu_view; - Gtk::Menu m_menu_view_sub; - Gtk::MenuItem m_menu_view_friends; - Gtk::MenuItem m_menu_view_pins; - Gtk::MenuItem m_menu_view_threads; - void OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y); -}; diff --git a/windows/pinnedwindow.cpp b/windows/pinnedwindow.cpp deleted file mode 100644 index a5484e3..0000000 --- a/windows/pinnedwindow.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "pinnedwindow.hpp" -#include "abaddon.hpp" - -PinnedWindow::PinnedWindow(const ChannelData &data) - : ChannelID(data.ID) { - if (data.GuildID.has_value()) - GuildID = *data.GuildID; - - set_name("pinned-messages"); - set_default_size(450, 375); - if (data.Name.has_value()) - set_title("#" + *data.Name + " - Pinned Messages"); - else - set_title("Pinned Messages"); - set_position(Gtk::WIN_POS_CENTER); - get_style_context()->add_class("app-window"); - get_style_context()->add_class("app-popup"); - get_style_context()->add_class("pinned-messages-window"); - - add(m_chat); - m_chat.show(); - - m_chat.SetSeparateAll(true); - m_chat.SetActiveChannel(ChannelID); - m_chat.SetUsePinnedMenu(); - - Abaddon::Get().GetDiscordClient().signal_message_pinned().connect(sigc::mem_fun(*this, &PinnedWindow::OnMessagePinned)); - Abaddon::Get().GetDiscordClient().signal_message_unpinned().connect(sigc::mem_fun(*this, &PinnedWindow::OnMessageUnpinned)); - FetchPinned(); -} - -void PinnedWindow::OnMessagePinned(const Message &msg) { - FetchPinned(); -} - -void PinnedWindow::OnMessageUnpinned(const Message &msg) { - m_chat.ActuallyRemoveMessage(msg.ID); -} - -void PinnedWindow::FetchPinned() { - Abaddon::Get().GetDiscordClient().FetchPinned(ChannelID, sigc::mem_fun(*this, &PinnedWindow::OnFetchedPinned)); -} - -void PinnedWindow::OnFetchedPinned(const std::vector<Message> &msgs, DiscordError code) { - if (code != DiscordError::NONE) return; - m_chat.SetMessages(msgs.begin(), msgs.end()); -} diff --git a/windows/pinnedwindow.hpp b/windows/pinnedwindow.hpp deleted file mode 100644 index cf2ec3c..0000000 --- a/windows/pinnedwindow.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/errors.hpp" -#include "discord/channel.hpp" -#include "discord/message.hpp" -#include "components/chatlist.hpp" - -class PinnedWindow : public Gtk::Window { -public: - PinnedWindow(const ChannelData &data); - - Snowflake GuildID; - Snowflake ChannelID; - -private: - void OnMessagePinned(const Message &msg); - void OnMessageUnpinned(const Message &msg); - void FetchPinned(); - void OnFetchedPinned(const std::vector<Message> &msgs, DiscordError code); - - ChatList m_chat; -}; diff --git a/windows/profile/mutualfriendspane.cpp b/windows/profile/mutualfriendspane.cpp deleted file mode 100644 index 339fd71..0000000 --- a/windows/profile/mutualfriendspane.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "mutualfriendspane.hpp" -#include "abaddon.hpp" - -MutualFriendItem::MutualFriendItem(const UserData &user) - : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) { - get_style_context()->add_class("mutual-friend-item"); - m_name.get_style_context()->add_class("mutual-friend-item-name"); - m_avatar.get_style_context()->add_class("mutual-friend-item-avatar"); - - m_avatar.set_margin_end(10); - - const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - auto &img = Abaddon::Get().GetImageManager(); - m_avatar.property_pixbuf() = img.GetPlaceholder(24); - if (user.HasAnimatedAvatar() && show_animations) { - auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - m_avatar.property_pixbuf_animation() = pb; - }; - img.LoadAnimationFromURL(user.GetAvatarURL("gif", "32"), 24, 24, sigc::track_obj(cb, *this)); - } else { - auto cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - m_avatar.property_pixbuf() = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(user.GetAvatarURL("png", "32"), sigc::track_obj(cb, *this)); - } - - m_name.set_markup(user.GetEscapedBoldString<false>()); - - m_name.set_valign(Gtk::ALIGN_CENTER); - add(m_avatar); - add(m_name); - show_all_children(); -} - -MutualFriendsPane::MutualFriendsPane(Snowflake id) - : UserID(id) { - signal_map().connect(sigc::mem_fun(*this, &MutualFriendsPane::OnMap)); - add(m_list); - show_all_children(); -} - -void MutualFriendsPane::OnFetchRelationships(const std::vector<UserData> &users) { - for (auto child : m_list.get_children()) - delete child; - - for (const auto &user : users) { - auto *item = Gtk::manage(new MutualFriendItem(user)); - item->show(); - m_list.add(*item); - } -} - -void MutualFriendsPane::OnMap() { - if (m_requested) return; - m_requested = true; - - Abaddon::Get().GetDiscordClient().FetchUserRelationships(UserID, sigc::mem_fun(*this, &MutualFriendsPane::OnFetchRelationships)); -} diff --git a/windows/profile/mutualfriendspane.hpp b/windows/profile/mutualfriendspane.hpp deleted file mode 100644 index ef41aa6..0000000 --- a/windows/profile/mutualfriendspane.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class MutualFriendItem : public Gtk::Box { -public: - MutualFriendItem(const UserData &user); - -private: - Gtk::Image m_avatar; - Gtk::Label m_name; -}; - -class MutualFriendsPane : public Gtk::ScrolledWindow { -public: - MutualFriendsPane(Snowflake id); - - Snowflake UserID; - -private: - void OnMap(); - - bool m_requested = false; - - void OnFetchRelationships(const std::vector<UserData> &users); - - Gtk::ListBox m_list; -}; diff --git a/windows/profile/mutualguildspane.cpp b/windows/profile/mutualguildspane.cpp deleted file mode 100644 index 6bfdc7b..0000000 --- a/windows/profile/mutualguildspane.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "mutualguildspane.hpp" -#include "abaddon.hpp" - -MutualGuildItem::MutualGuildItem(const MutualGuildData &guild) - : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) - , m_box(Gtk::ORIENTATION_VERTICAL) { - get_style_context()->add_class("mutual-guild-item"); - m_name.get_style_context()->add_class("mutual-guild-item-name"); - m_icon.get_style_context()->add_class("mutual-guild-item-icon"); - - m_icon.set_margin_end(10); - - // discord will return info (id + nick) for "deleted" guilds from this endpoint. strange ! - const auto data = Abaddon::Get().GetDiscordClient().GetGuild(guild.ID); - if (data.has_value()) { - const auto show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - auto &img = Abaddon::Get().GetImageManager(); - m_icon.property_pixbuf() = img.GetPlaceholder(24); - if (data->HasIcon()) { - if (data->HasAnimatedIcon() && show_animations) { - auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - m_icon.property_pixbuf_animation() = pb; - }; - img.LoadAnimationFromURL(data->GetIconURL("gif", "32"), 24, 24, sigc::track_obj(cb, *this)); - } else { - auto cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - m_icon.property_pixbuf() = pb->scale_simple(24, 24, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(data->GetIconURL("png", "32"), sigc::track_obj(cb, *this)); - } - } - - m_name.set_markup("<b>" + Glib::Markup::escape_text(data->Name) + "</b>"); - } else { - m_icon.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(24); - m_name.set_markup("<b>Unknown server</b>"); - } - - if (guild.Nick.has_value()) { - m_nick = Gtk::manage(new Gtk::Label(*guild.Nick)); - m_nick->get_style_context()->add_class("mutual-guild-item-nick"); - m_nick->set_margin_start(5); - m_nick->set_halign(Gtk::ALIGN_START); - m_nick->set_single_line_mode(true); - m_nick->set_ellipsize(Pango::ELLIPSIZE_END); - } - - m_box.set_valign(Gtk::ALIGN_CENTER); - - m_box.add(m_name); - if (m_nick != nullptr) - m_box.add(*m_nick); - add(m_icon); - add(m_box); - show_all_children(); -} - -UserMutualGuildsPane::UserMutualGuildsPane(Snowflake id) - : UserID(id) { - add(m_list); - show_all_children(); -} - -void UserMutualGuildsPane::SetMutualGuilds(const std::vector<MutualGuildData> &guilds) { - for (auto child : m_list.get_children()) - delete child; - - for (const auto &guild : guilds) { - auto *item = Gtk::manage(new MutualGuildItem(guild)); - item->show(); - m_list.add(*item); - } -} diff --git a/windows/profile/mutualguildspane.hpp b/windows/profile/mutualguildspane.hpp deleted file mode 100644 index 9bdd97e..0000000 --- a/windows/profile/mutualguildspane.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class MutualGuildItem : public Gtk::Box { -public: - MutualGuildItem(const MutualGuildData &guild); - -private: - Gtk::Image m_icon; - Gtk::Box m_box; - Gtk::Label m_name; - Gtk::Label *m_nick = nullptr; -}; - -class UserMutualGuildsPane : public Gtk::ScrolledWindow { -public: - UserMutualGuildsPane(Snowflake id); - - void SetMutualGuilds(const std::vector<MutualGuildData> &guilds); - - Snowflake UserID; - -private: - Gtk::ListBox m_list; -}; diff --git a/windows/profile/userinfopane.cpp b/windows/profile/userinfopane.cpp deleted file mode 100644 index a95a14c..0000000 --- a/windows/profile/userinfopane.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "userinfopane.hpp" -#include <unordered_set> -#include "abaddon.hpp" - -ConnectionItem::ConnectionItem(const ConnectionData &conn) - : m_box(Gtk::ORIENTATION_HORIZONTAL) - , m_name(conn.Name) { - Glib::RefPtr<Gdk::Pixbuf> pixbuf; - try { - pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + conn.Type + ".png"), 32, 32); - } catch (const Glib::Exception &e) {} - std::string url; - if (conn.Type == "github") - url = "https://github.com/" + conn.Name; - else if (conn.Type == "steam") - url = "https://steamcommunity.com/profiles/" + conn.ID; - else if (conn.Type == "twitch") - url = "https://twitch.tv/" + conn.Name; - else if (conn.Type == "twitter") - url = "https://twitter.com/i/user/" + conn.ID; - else if (conn.Type == "spotify") - url = "https://open.spotify.com/user/" + conn.ID; - else if (conn.Type == "reddit") - url = "https://reddit.com/u/" + conn.Name; - else if (conn.Type == "youtube") - url = "https://www.youtube.com/channel/" + conn.ID; - else if (conn.Type == "facebook") - url = "https://www.facebook.com/" + conn.ID; - if (pixbuf) { - m_image = Gtk::manage(new Gtk::Image(pixbuf)); - m_image->get_style_context()->add_class("profile-connection-image"); - m_box.add(*m_image); - } - m_box.set_halign(Gtk::ALIGN_START); - m_box.set_size_request(200, -1); - m_box.get_style_context()->add_class("profile-connection"); - m_name.get_style_context()->add_class("profile-connection-label"); - m_name.set_valign(Gtk::ALIGN_CENTER); - m_name.set_single_line_mode(true); - m_name.set_ellipsize(Pango::ELLIPSIZE_END); - m_box.add(m_name); - if (url != "") { - auto cb = [this, url](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { - LaunchBrowser(url); - return true; - } - return false; - }; - signal_button_press_event().connect(sigc::track_obj(cb, *this)); - AddPointerCursor(*this); - } - m_overlay.add(m_box); - if (conn.IsVerified) { - try { - const static auto checkmarks_path = Abaddon::GetResPath("/checkmark.png"); - static auto pb = Gdk::Pixbuf::create_from_file(checkmarks_path, 24, 24); - m_check = Gtk::manage(new Gtk::Image(pb)); - m_check->get_style_context()->add_class("profile-connection-check"); - m_check->set_margin_end(25); - m_check->set_valign(Gtk::ALIGN_CENTER); - m_check->set_halign(Gtk::ALIGN_END); - m_check->show(); - m_overlay.add_overlay(*m_check); - } catch (const Glib::Exception &e) {} - } - m_overlay.set_hexpand(false); - m_overlay.set_halign(Gtk::ALIGN_START); - add(m_overlay); - show_all_children(); -} - -ConnectionsContainer::ConnectionsContainer() { - get_style_context()->add_class("profile-connections"); - set_column_homogeneous(true); - set_row_spacing(10); - set_column_spacing(10); - show_all_children(); -} - -void ConnectionsContainer::SetConnections(const std::vector<ConnectionData> &connections) { - for (auto child : get_children()) - delete child; - - static const std::unordered_set<std::string> supported_services = { - "battlenet", - "github", - "leagueoflegends", - "reddit", - "skype", - "spotify", - "steam", - "twitch", - "twitter", - "xbox", - "youtube", - "facebook" - }; - - for (size_t i = 0; i < connections.size(); i++) { - const auto &conn = connections[i]; - if (supported_services.find(conn.Type) == supported_services.end()) continue; - auto widget = Gtk::manage(new ConnectionItem(conn)); - widget->show(); - attach(*widget, i % 2, i / 2, 1, 1); - } - - set_halign(Gtk::ALIGN_FILL); - set_hexpand(true); -} - -NotesContainer::NotesContainer() - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) { - get_style_context()->add_class("profile-notes"); - m_label.get_style_context()->add_class("profile-notes-label"); - m_note.get_style_context()->add_class("profile-notes-text"); - - m_label.set_markup("<b>NOTE</b>"); - m_label.set_halign(Gtk::ALIGN_START); - - m_note.set_wrap_mode(Gtk::WRAP_WORD_CHAR); - m_note.signal_key_press_event().connect(sigc::mem_fun(*this, &NotesContainer::OnNoteKeyPress), false); - - add(m_label); - add(m_note); - show_all_children(); -} - -void NotesContainer::SetNote(const std::string ¬e) { - m_note.get_buffer()->set_text(note); -} - -void NotesContainer::UpdateNote() { - auto text = m_note.get_buffer()->get_text(); - if (text.size() > 256) - text = text.substr(0, 256); - m_signal_update_note.emit(text); -} - -bool NotesContainer::OnNoteKeyPress(GdkEventKey *event) { - if (event->type != GDK_KEY_PRESS) return false; - const auto text = m_note.get_buffer()->get_text(); - if (event->keyval == GDK_KEY_Return) { - if (event->state & GDK_SHIFT_MASK) { - int newlines = 0; - for (const auto c : text) - if (c == '\n') newlines++; - return newlines >= 5; - } else { - UpdateNote(); - return true; - } - } - - return false; -} - -NotesContainer::type_signal_update_note NotesContainer::signal_update_note() { - return m_signal_update_note; -} - -BioContainer::BioContainer() - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) { - m_label.set_markup("<b>ABOUT ME</b>"); - m_label.set_halign(Gtk::ALIGN_START); - m_bio.set_halign(Gtk::ALIGN_START); - m_bio.set_line_wrap(true); - m_bio.set_line_wrap_mode(Pango::WRAP_WORD_CHAR); - - m_label.show(); - m_bio.show(); - - add(m_label); - add(m_bio); -} - -void BioContainer::SetBio(const std::string &bio) { - m_bio.set_text(bio); -} - -ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID) - : Gtk::Box(Gtk::ORIENTATION_VERTICAL) - , UserID(ID) { - get_style_context()->add_class("profile-info-pane"); - m_created.get_style_context()->add_class("profile-info-created"); - - m_note.signal_update_note().connect([this](const Glib::ustring ¬e) { - auto cb = [this](DiscordError code) { - if (code != DiscordError::NONE) { - Gtk::MessageDialog dlg("Failed to set note", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dlg.set_position(Gtk::WIN_POS_CENTER); - dlg.run(); - } - }; - Abaddon::Get().GetDiscordClient().SetUserNote(UserID, note, sigc::track_obj(cb, *this)); - }); - - auto &discord = Abaddon::Get().GetDiscordClient(); - auto note_update_cb = [this](Snowflake id, std::string note) { - if (id == UserID) - m_note.SetNote(note); - }; - discord.signal_note_update().connect(sigc::track_obj(note_update_cb, m_note)); - - auto fetch_note_cb = [this](const std::string ¬e) { - m_note.SetNote(note); - }; - discord.FetchUserNote(UserID, sigc::track_obj(fetch_note_cb, *this)); - - m_created.set_halign(Gtk::ALIGN_START); - m_created.set_margin_top(5); - m_created.set_text("Account created: " + ID.GetLocalTimestamp()); - - m_conns.set_halign(Gtk::ALIGN_START); - m_conns.set_hexpand(true); - - m_created.show(); - m_note.show(); - m_conns.show(); - - add(m_created); - add(m_bio); - add(m_note); - add(m_conns); -} - -void ProfileUserInfoPane::SetProfile(const UserProfileData &data) { - if (data.User.Bio.has_value() && *data.User.Bio != "") { - m_bio.SetBio(*data.User.Bio); - m_bio.show(); - } else { - m_bio.hide(); - } - - m_conns.SetConnections(data.ConnectedAccounts); -} diff --git a/windows/profile/userinfopane.hpp b/windows/profile/userinfopane.hpp deleted file mode 100644 index 90a4d55..0000000 --- a/windows/profile/userinfopane.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class ConnectionItem : public Gtk::EventBox { -public: - ConnectionItem(const ConnectionData &connection); - -private: - Gtk::Overlay m_overlay; - Gtk::Box m_box; - Gtk::Label m_name; - Gtk::Image *m_image = nullptr; - Gtk::Image *m_check = nullptr; -}; - -class ConnectionsContainer : public Gtk::Grid { -public: - ConnectionsContainer(); - void SetConnections(const std::vector<ConnectionData> &connections); -}; - -class NotesContainer : public Gtk::Box { -public: - NotesContainer(); - void SetNote(const std::string ¬e); - -private: - void UpdateNote(); - bool OnNoteKeyPress(GdkEventKey *event); - - Gtk::Label m_label; - Gtk::TextView m_note; - - typedef sigc::signal<void, Glib::ustring> type_signal_update_note; - type_signal_update_note m_signal_update_note; - -public: - type_signal_update_note signal_update_note(); -}; - -class BioContainer : public Gtk::Box { -public: - BioContainer(); - void SetBio(const std::string &bio); - -private: - Gtk::Label m_label; - Gtk::Label m_bio; -}; - -class ProfileUserInfoPane : public Gtk::Box { -public: - ProfileUserInfoPane(Snowflake ID); - void SetProfile(const UserProfileData &data); - - Snowflake UserID; - -private: - Gtk::Label m_created; - - BioContainer m_bio; - NotesContainer m_note; - ConnectionsContainer m_conns; -}; diff --git a/windows/profilewindow.cpp b/windows/profilewindow.cpp deleted file mode 100644 index 4d41f89..0000000 --- a/windows/profilewindow.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "profilewindow.hpp" -#include "abaddon.hpp" - -ProfileWindow::ProfileWindow(Snowflake user_id) - : ID(user_id) - , m_main(Gtk::ORIENTATION_VERTICAL) - , m_upper(Gtk::ORIENTATION_HORIZONTAL) - , m_badges(Gtk::ORIENTATION_HORIZONTAL) - , m_pane_info(user_id) - , m_pane_guilds(user_id) - , m_pane_friends(user_id) { - auto &discord = Abaddon::Get().GetDiscordClient(); - auto user = *discord.GetUser(ID); - - discord.FetchUserProfile(user_id, sigc::mem_fun(*this, &ProfileWindow::OnFetchProfile)); - - set_name("user-profile"); - set_default_size(450, 375); - set_title(user.Username + "#" + user.Discriminator); - set_position(Gtk::WIN_POS_CENTER); - get_style_context()->add_class("app-window"); - get_style_context()->add_class("app-popup"); - get_style_context()->add_class("user-profile-window"); - m_main.get_style_context()->add_class("profile-main-container"); - m_avatar.get_style_context()->add_class("profile-avatar"); - m_username.get_style_context()->add_class("profile-username"); - m_switcher.get_style_context()->add_class("profile-switcher"); - m_stack.get_style_context()->add_class("profile-stack"); - m_badges.get_style_context()->add_class("profile-badges"); - - m_scroll.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - m_scroll.set_vexpand(true); - m_scroll.set_propagate_natural_height(true); - - if (user.HasAvatar()) - AddPointerCursor(m_avatar_ev); - m_avatar_ev.signal_button_press_event().connect([this, user](GdkEventButton *event) -> bool { - if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { - if (user.HasAnimatedAvatar()) - LaunchBrowser(user.GetAvatarURL("gif", "512")); - else - LaunchBrowser(user.GetAvatarURL("png", "512")); - } - return false; - }); - - static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); - auto &img = Abaddon::Get().GetImageManager(); - m_avatar.property_pixbuf() = img.GetPlaceholder(64); - auto icon_cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - set_icon(pb); - }; - img.LoadFromURL(user.GetAvatarURL("png", "64"), sigc::track_obj(icon_cb, *this)); - - if (show_animations && user.HasAnimatedAvatar()) { - auto cb = [this](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - m_avatar.property_pixbuf_animation() = pb; - }; - img.LoadAnimationFromURL(user.GetAvatarURL("gif", "64"), 64, 64, sigc::track_obj(cb, *this)); - } else { - auto cb = [this](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - m_avatar.property_pixbuf() = pb->scale_simple(64, 64, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(user.GetAvatarURL("png", "64"), sigc::track_obj(cb, *this)); - } - - m_username.set_markup(user.GetEscapedString()); - - m_switcher.set_stack(m_stack); - m_switcher.set_halign(Gtk::ALIGN_START); - m_switcher.set_hexpand(true); - - m_stack.add(m_pane_info, "info", "User Info"); - m_stack.add(m_pane_guilds, "guilds", "Mutual Servers"); - m_stack.add(m_pane_friends, "friends", "Mutual Friends"); - - m_badges.set_valign(Gtk::ALIGN_CENTER); - m_badges_scroll.set_hexpand(true); - m_badges_scroll.set_propagate_natural_width(true); - m_badges_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER); - - m_upper.set_halign(Gtk::ALIGN_START); - m_avatar.set_halign(Gtk::ALIGN_START); - m_username.set_halign(Gtk::ALIGN_START); - m_avatar_ev.add(m_avatar); - m_upper.add(m_avatar_ev); - m_upper.add(m_username); - m_badges_scroll.add(m_badges); - m_upper.add(m_badges_scroll); - m_main.add(m_upper); - m_main.add(m_switcher); - m_scroll.add(m_stack); - m_main.add(m_scroll); - add(m_main); - show_all_children(); -} - -void ProfileWindow::OnFetchProfile(const UserProfileData &data) { - m_pane_info.SetProfile(data); - m_pane_guilds.SetMutualGuilds(data.MutualGuilds); - - for (auto child : m_badges.get_children()) - delete child; - - if (!data.User.PublicFlags.has_value()) return; - const auto x = *data.User.PublicFlags; - for (uint64_t i = 1; i <= UserData::MaxFlag; i <<= 1) { - if (!(x & i)) continue; - const std::string name = UserData::GetFlagName(i); - if (name == "unknown") continue; - Glib::RefPtr<Gdk::Pixbuf> pixbuf; - try { - if (name == "verifiedbot") - pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/checkmark.png"), 24, 24); - else - pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + name + ".png"), 24, 24); - } catch (const Glib::Exception &e) { - pixbuf = Abaddon::Get().GetImageManager().GetPlaceholder(24); - } - if (!pixbuf) continue; - auto *image = Gtk::manage(new Gtk::Image(pixbuf)); - image->get_style_context()->add_class("profile-badge"); - image->set_tooltip_text(UserData::GetFlagReadableName(i)); - image->show(); - m_badges.add(*image); - } -} diff --git a/windows/profilewindow.hpp b/windows/profilewindow.hpp deleted file mode 100644 index 3d8199b..0000000 --- a/windows/profilewindow.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/snowflake.hpp" -#include "profile/userinfopane.hpp" -#include "profile/mutualguildspane.hpp" -#include "profile/mutualfriendspane.hpp" - -class ProfileWindow : public Gtk::Window { -public: - ProfileWindow(Snowflake user_id); - - Snowflake ID; - -private: - void OnFetchProfile(const UserProfileData &data); - - Gtk::Box m_main; - Gtk::Box m_upper; - Gtk::Box m_badges; - Gtk::ScrolledWindow m_badges_scroll; - Gtk::EventBox m_avatar_ev; - Gtk::Image m_avatar; - Gtk::Label m_username; - Gtk::ScrolledWindow m_scroll; - Gtk::Stack m_stack; - Gtk::StackSwitcher m_switcher; - - ProfileUserInfoPane m_pane_info; - UserMutualGuildsPane m_pane_guilds; - MutualFriendsPane m_pane_friends; -}; diff --git a/windows/threadswindow.cpp b/windows/threadswindow.cpp deleted file mode 100644 index 9071d81..0000000 --- a/windows/threadswindow.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "threadswindow.hpp" -#include "abaddon.hpp" - -ThreadsWindow::ThreadsWindow(const ChannelData &channel) - : m_channel_id(channel.ID) - , m_filter_public(m_group, "Public") - , m_filter_private(m_group, "Private") - , m_box(Gtk::ORIENTATION_VERTICAL) - , m_active(channel, sigc::mem_fun(*this, &ThreadsWindow::ListFilterFunc)) - , m_archived(channel, sigc::mem_fun(*this, &ThreadsWindow::ListFilterFunc)) { - set_name("threads-window"); - set_default_size(450, 375); - set_title("#" + *channel.Name + " - Threads"); - set_position(Gtk::WIN_POS_CENTER); - get_style_context()->add_class("app-window"); - get_style_context()->add_class("app-popup"); - get_style_context()->add_class("threads-window"); - - const auto cb = [this](Snowflake id) { - Abaddon::Get().ActionChannelOpened(id); - hide(); - }; - m_active.signal_thread_open().connect(cb); - m_archived.signal_thread_open().connect(cb); - - m_switcher.set_halign(Gtk::ALIGN_CENTER); - m_switcher.set_stack(m_stack); - - m_stack.add(m_active, "active", "Active Threads"); - m_stack.add(m_archived, "archived", "Archived Threads"); - - m_filter_buttons.set_homogeneous(true); - m_filter_buttons.set_halign(Gtk::ALIGN_CENTER); - m_filter_buttons.add(m_filter_public); - m_filter_buttons.add(m_filter_private); - - // a little strange - const auto btncb = [this](Gtk::RadioButton *btn) { - if (btn->get_active()) { - if (btn == &m_filter_public) - m_filter_mode = FILTER_PUBLIC; - else if (btn == &m_filter_private) - m_filter_mode = FILTER_PRIVATE; - - m_active.InvalidateFilter(); - m_archived.InvalidateFilter(); - } - }; - m_filter_public.signal_toggled().connect(sigc::bind(btncb, &m_filter_public)); - m_filter_private.signal_toggled().connect(sigc::bind(btncb, &m_filter_private)); - - m_active.show(); - m_archived.show(); - m_switcher.show(); - m_filter_buttons.show_all(); - m_stack.show(); - m_box.show(); - - m_box.add(m_switcher); - m_box.add(m_filter_buttons); - m_box.add(m_stack); - add(m_box); -} - -bool ThreadsWindow::ListFilterFunc(Gtk::ListBoxRow *row_) { - if (auto *row = dynamic_cast<ThreadListRow *>(row_)) - return (m_filter_mode == FILTER_PUBLIC && (row->Type == ChannelType::GUILD_PUBLIC_THREAD || row->Type == ChannelType::GUILD_NEWS_THREAD)) || - (m_filter_mode == FILTER_PRIVATE && row->Type == ChannelType::GUILD_PRIVATE_THREAD); - return false; -} - -ThreadListRow::ThreadListRow(const ChannelData &channel) - : ID(channel.ID) - , Type(channel.Type) - , m_label(*channel.Name, Gtk::ALIGN_START) { - m_label.show(); - add(m_label); -} - -ActiveThreadsList::ActiveThreadsList(const ChannelData &channel, const Gtk::ListBox::SlotFilter &filter) { - set_vexpand(true); - - m_list.set_filter_func(filter); - m_list.set_selection_mode(Gtk::SELECTION_SINGLE); - m_list.set_hexpand(true); - m_list.show(); - - add(m_list); - - m_list.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { - if (ev->button == GDK_BUTTON_PRIMARY && ev->type == GDK_2BUTTON_PRESS) { - if (auto row = dynamic_cast<ThreadListRow *>(m_list.get_selected_row())) - m_signal_thread_open.emit(row->ID); - } - return false; - }); - - const auto threads = Abaddon::Get().GetDiscordClient().GetActiveThreads(channel.ID); - for (const auto &thread : threads) { - auto row = Gtk::manage(new ThreadListRow(thread)); - row->show(); - m_list.add(*row); - } -} - -void ActiveThreadsList::InvalidateFilter() { - m_list.invalidate_filter(); -} - -ActiveThreadsList::type_signal_thread_open ActiveThreadsList::signal_thread_open() { - return m_signal_thread_open; -} - -ArchivedThreadsList::ArchivedThreadsList(const ChannelData &channel, const Gtk::ListBox::SlotFilter &filter) { - set_vexpand(true); - - m_list.set_filter_func(filter); - m_list.set_selection_mode(Gtk::SELECTION_SINGLE); - m_list.set_hexpand(true); - m_list.show(); - - add(m_list); - - m_list.signal_button_press_event().connect([this](GdkEventButton *ev) -> bool { - if (ev->button == GDK_BUTTON_PRIMARY && ev->type == GDK_2BUTTON_PRESS) { - if (auto row = dynamic_cast<ThreadListRow *>(m_list.get_selected_row())) - m_signal_thread_open.emit(row->ID); - } - return false; - }); - - Abaddon::Get().GetDiscordClient().GetArchivedPublicThreads(channel.ID, sigc::mem_fun(*this, &ArchivedThreadsList::OnPublicFetched)); -} - -void ArchivedThreadsList::InvalidateFilter() { - m_list.invalidate_filter(); -} - -void ArchivedThreadsList::OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data) { - for (const auto &thread : data.Threads) { - auto row = Gtk::manage(new ThreadListRow(thread)); - row->show(); - m_list.add(*row); - } -} - -ArchivedThreadsList::type_signal_thread_open ArchivedThreadsList::signal_thread_open() { - return m_signal_thread_open; -} diff --git a/windows/threadswindow.hpp b/windows/threadswindow.hpp deleted file mode 100644 index 0e42414..0000000 --- a/windows/threadswindow.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include <gtkmm.h> -#include "discord/objects.hpp" - -class ActiveThreadsList : public Gtk::ScrolledWindow { -public: - ActiveThreadsList(const ChannelData &channel, const Gtk::ListBox::SlotFilter &filter); - - void InvalidateFilter(); - -private: - Gtk::ListBox m_list; - - using type_signal_thread_open = sigc::signal<void, Snowflake>; - type_signal_thread_open m_signal_thread_open; - -public: - type_signal_thread_open signal_thread_open(); -}; - -class ArchivedThreadsList : public Gtk::ScrolledWindow { -public: - ArchivedThreadsList(const ChannelData &channel, const Gtk::ListBox::SlotFilter &filter); - - void InvalidateFilter(); - -private: - Gtk::ListBox m_list; - - void OnPublicFetched(DiscordError code, const ArchivedThreadsResponseData &data); - - using type_signal_thread_open = sigc::signal<void, Snowflake>; - type_signal_thread_open m_signal_thread_open; - -public: - type_signal_thread_open signal_thread_open(); -}; - -// view all threads in a channel -class ThreadsWindow : public Gtk::Window { -public: - ThreadsWindow(const ChannelData &channel); - -private: - // this filtering is rather cringe but idk what a better alternative would be - bool ListFilterFunc(Gtk::ListBoxRow *row_); - - enum FilterMode { - FILTER_PUBLIC = 0, - FILTER_PRIVATE = 1, - }; - bool m_filter_mode = FILTER_PUBLIC; - - Snowflake m_channel_id; - - Gtk::StackSwitcher m_switcher; - Gtk::Stack m_stack; - - Gtk::RadioButtonGroup m_group; - Gtk::ButtonBox m_filter_buttons; - Gtk::RadioButton m_filter_public; - Gtk::RadioButton m_filter_private; - - Gtk::Box m_box; - - ActiveThreadsList m_active; - ArchivedThreadsList m_archived; -}; - -class ThreadListRow : public Gtk::ListBoxRow { -public: - ThreadListRow(const ChannelData &channel); - - Snowflake ID; - ChannelType Type; - -private: - Gtk::Label m_label; -}; |