diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-10-24 21:42:19 -0400 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-10-24 21:42:19 -0400 |
commit | 201b114183454f3fb9114fed2caef4ac46587225 (patch) | |
tree | 4cc8a9b1c126b682ffa8aca9d8ecc2a8cf6c1aa7 /src/components/channellist/channellist.cpp | |
parent | 4906775edeb96dc3f9194d43252ab2b47bc11e4e (diff) | |
download | abaddon-portaudio-201b114183454f3fb9114fed2caef4ac46587225.tar.gz abaddon-portaudio-201b114183454f3fb9114fed2caef4ac46587225.zip |
refactor ChannelList -> ChannelListTree
Diffstat (limited to 'src/components/channellist/channellist.cpp')
-rw-r--r-- | src/components/channellist/channellist.cpp | 1321 |
1 files changed, 0 insertions, 1321 deletions
diff --git a/src/components/channellist/channellist.cpp b/src/components/channellist/channellist.cpp deleted file mode 100644 index ca731c9..0000000 --- a/src/components/channellist/channellist.cpp +++ /dev/null @@ -1,1321 +0,0 @@ -#include "channellist.hpp" -#include "imgmanager.hpp" -#include "components/statusindicator.hpp" -#include <algorithm> -#include <map> -#include <unordered_map> - -ChannelList::ChannelList() - : Glib::ObjectBase(typeid(ChannelList)) - , m_model(Gtk::TreeStore::create(m_columns)) - , m_filter_model(Gtk::TreeModelFilter::create(m_model)) - , m_menu_guild_copy_id("_Copy ID", true) - , m_menu_guild_settings("View _Settings", true) - , m_menu_guild_leave("_Leave", true) - , m_menu_guild_mark_as_read("Mark as _Read", true) - , m_menu_category_copy_id("_Copy ID", true) - , m_menu_channel_copy_id("_Copy ID", true) - , m_menu_channel_mark_as_read("Mark as _Read", true) -#ifdef WITH_LIBHANDY - , m_menu_channel_open_tab("Open in New _Tab", true) - , m_menu_dm_open_tab("Open in New _Tab", true) -#endif -#ifdef WITH_VOICE - , m_menu_voice_channel_join("_Join", true) - , m_menu_voice_channel_disconnect("_Disconnect", true) -#endif - , m_menu_dm_copy_id("_Copy ID", true) - , m_menu_dm_close("") // changes depending on if group or not -#ifdef WITH_VOICE - , m_menu_dm_join_voice("Join _Voice", true) - , m_menu_dm_disconnect_voice("_Disconnect Voice", true) -#endif - , m_menu_thread_copy_id("_Copy ID", true) - , m_menu_thread_leave("_Leave", true) - , m_menu_thread_archive("_Archive", true) - , m_menu_thread_unarchive("_Unarchive", true) - , m_menu_thread_mark_as_read("Mark as _Read", true) { - get_style_context()->add_class("channel-list"); - - // Filter iters - const auto cb = [this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) { - auto row = *m_filter_model->get_iter(path); - const auto type = row[m_columns.m_type]; - // text channels should not be allowed to be collapsed - // maybe they should be but it seems a little difficult to handle expansion to permit this - if (type != RenderType::TextChannel && type != RenderType::DM) { - if (row[m_columns.m_expanded]) { - m_view.collapse_row(path); - row[m_columns.m_expanded] = false; - } else { - m_view.expand_row(path, false); - row[m_columns.m_expanded] = true; - } - } - - if (type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread) { - const auto id = static_cast<Snowflake>(row[m_columns.m_id]); - m_signal_action_channel_item_select.emit(id); - Abaddon::Get().GetDiscordClient().MarkChannelAsRead(id, [](...) {}); - } - }; - m_view.signal_row_activated().connect(cb, false); - m_view.signal_row_collapsed().connect(sigc::mem_fun(*this, &ChannelList::OnRowCollapsed), false); - m_view.signal_row_expanded().connect(sigc::mem_fun(*this, &ChannelList::OnRowExpanded), false); - m_view.set_activate_on_single_click(true); - m_view.get_selection()->set_mode(Gtk::SELECTION_SINGLE); - m_view.get_selection()->set_select_function(sigc::mem_fun(*this, &ChannelList::SelectionFunc)); - m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &ChannelList::OnButtonPressEvent), false); - - m_view.set_hexpand(true); - m_view.set_vexpand(true); - - m_view.set_show_expanders(false); - m_view.set_enable_search(false); - m_view.set_headers_visible(false); - m_view.set_model(m_filter_model); - m_model->set_sort_column(m_columns.m_sort, Gtk::SORT_ASCENDING); - - m_model->signal_row_inserted().connect([this](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) { - if (m_updating_listing) return; - if (auto parent = iter->parent(); parent && (*parent)[m_columns.m_expanded]) { - const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(parent)); - m_view.expand_row(filter_path, false); - } - }); - - m_filter_model->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool { - if ((*iter)[m_columns.m_type] == RenderType::Guild) { - return (*iter)[m_columns.m_id] == 754921263616753776ULL; - } - return true; - }); - - m_view.show(); - - add(m_view); - - auto *column = Gtk::manage(new Gtk::TreeView::Column("display")); - auto *renderer = Gtk::manage(new CellRendererChannels); - column->pack_start(*renderer); - column->add_attribute(renderer->property_type(), m_columns.m_type); - column->add_attribute(renderer->property_icon(), m_columns.m_icon); - column->add_attribute(renderer->property_icon_animation(), m_columns.m_icon_anim); - column->add_attribute(renderer->property_name(), m_columns.m_name); - column->add_attribute(renderer->property_id(), m_columns.m_id); - column->add_attribute(renderer->property_expanded(), m_columns.m_expanded); - column->add_attribute(renderer->property_nsfw(), m_columns.m_nsfw); - column->add_attribute(renderer->property_color(), m_columns.m_color); - column->add_attribute(renderer->property_voice_state(), m_columns.m_voice_flags); - m_view.append_column(*column); - - m_menu_guild_copy_id.signal_activate().connect([this] { - Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_guild_settings.signal_activate().connect([this] { - m_signal_action_guild_settings.emit(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_guild_leave.signal_activate().connect([this] { - m_signal_action_guild_leave.emit(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_guild_mark_as_read.signal_activate().connect([this] { - Abaddon::Get().GetDiscordClient().MarkGuildAsRead(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {}); - }); - m_menu_guild_toggle_mute.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsGuildMuted(id)) - discord.UnmuteGuild(id, NOOP_CALLBACK); - else - discord.MuteGuild(id, NOOP_CALLBACK); - }); - m_menu_guild.append(m_menu_guild_mark_as_read); - m_menu_guild.append(m_menu_guild_settings); - m_menu_guild.append(m_menu_guild_leave); - m_menu_guild.append(m_menu_guild_toggle_mute); - m_menu_guild.append(m_menu_guild_copy_id); - m_menu_guild.show_all(); - - m_menu_category_copy_id.signal_activate().connect([this] { - Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_category_toggle_mute.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsChannelMuted(id)) - discord.UnmuteChannel(id, NOOP_CALLBACK); - else - discord.MuteChannel(id, NOOP_CALLBACK); - }); - m_menu_category.append(m_menu_category_toggle_mute); - m_menu_category.append(m_menu_category_copy_id); - m_menu_category.show_all(); - - m_menu_channel_copy_id.signal_activate().connect([this] { - Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_channel_mark_as_read.signal_activate().connect([this] { - Abaddon::Get().GetDiscordClient().MarkChannelAsRead(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {}); - }); - m_menu_channel_toggle_mute.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsChannelMuted(id)) - discord.UnmuteChannel(id, NOOP_CALLBACK); - else - discord.MuteChannel(id, NOOP_CALLBACK); - }); - -#ifdef WITH_LIBHANDY - m_menu_channel_open_tab.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - m_signal_action_open_new_tab.emit(id); - }); - m_menu_channel.append(m_menu_channel_open_tab); -#endif - - m_menu_channel.append(m_menu_channel_mark_as_read); - m_menu_channel.append(m_menu_channel_toggle_mute); - m_menu_channel.append(m_menu_channel_copy_id); - m_menu_channel.show_all(); - -#ifdef WITH_VOICE - m_menu_voice_channel_join.signal_activate().connect([this]() { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - m_signal_action_join_voice_channel.emit(id); - }); - - m_menu_voice_channel_disconnect.signal_activate().connect([this]() { - m_signal_action_disconnect_voice.emit(); - }); - - m_menu_voice_channel.append(m_menu_voice_channel_join); - m_menu_voice_channel.append(m_menu_voice_channel_disconnect); - m_menu_voice_channel.show_all(); -#endif - - m_menu_dm_copy_id.signal_activate().connect([this] { - Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_dm_close.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - const auto channel = discord.GetChannel(id); - if (!channel.has_value()) return; - - if (channel->Type == ChannelType::DM) - discord.CloseDM(id); - else if (Abaddon::Get().ShowConfirm("Are you sure you want to leave this group DM?")) - Abaddon::Get().GetDiscordClient().CloseDM(id); - }); - m_menu_dm_toggle_mute.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsChannelMuted(id)) - discord.UnmuteChannel(id, NOOP_CALLBACK); - else - discord.MuteChannel(id, NOOP_CALLBACK); - }); -#ifdef WITH_LIBHANDY - m_menu_dm_open_tab.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - m_signal_action_open_new_tab.emit(id); - }); - m_menu_dm.append(m_menu_dm_open_tab); -#endif - m_menu_dm.append(m_menu_dm_toggle_mute); - m_menu_dm.append(m_menu_dm_close); -#ifdef WITH_VOICE - m_menu_dm_join_voice.signal_activate().connect([this]() { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - m_signal_action_join_voice_channel.emit(id); - }); - m_menu_dm_disconnect_voice.signal_activate().connect([this]() { - m_signal_action_disconnect_voice.emit(); - }); - m_menu_dm.append(m_menu_dm_join_voice); - m_menu_dm.append(m_menu_dm_disconnect_voice); -#endif - m_menu_dm.append(m_menu_dm_copy_id); - m_menu_dm.show_all(); - - m_menu_thread_copy_id.signal_activate().connect([this] { - Gtk::Clipboard::get()->set_text(std::to_string((*m_model->get_iter(m_path_for_menu))[m_columns.m_id])); - }); - m_menu_thread_leave.signal_activate().connect([this] { - if (Abaddon::Get().ShowConfirm("Are you sure you want to leave this thread?")) - Abaddon::Get().GetDiscordClient().LeaveThread(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), "Context%20Menu", [](...) {}); - }); - m_menu_thread_archive.signal_activate().connect([this] { - Abaddon::Get().GetDiscordClient().ArchiveThread(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {}); - }); - m_menu_thread_unarchive.signal_activate().connect([this] { - Abaddon::Get().GetDiscordClient().UnArchiveThread(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), [](...) {}); - }); - m_menu_thread_mark_as_read.signal_activate().connect([this] { - Abaddon::Get().GetDiscordClient().MarkChannelAsRead(static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]), NOOP_CALLBACK); - }); - m_menu_thread_toggle_mute.signal_activate().connect([this] { - const auto id = static_cast<Snowflake>((*m_model->get_iter(m_path_for_menu))[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsChannelMuted(id)) - discord.UnmuteThread(id, NOOP_CALLBACK); - else - discord.MuteThread(id, NOOP_CALLBACK); - }); - m_menu_thread.append(m_menu_thread_mark_as_read); - m_menu_thread.append(m_menu_thread_toggle_mute); - m_menu_thread.append(m_menu_thread_leave); - m_menu_thread.append(m_menu_thread_archive); - m_menu_thread.append(m_menu_thread_unarchive); - m_menu_thread.append(m_menu_thread_copy_id); - m_menu_thread.show_all(); - - auto &discord = Abaddon::Get().GetDiscordClient(); - discord.signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate)); - discord.signal_guild_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateNewGuild)); - discord.signal_guild_delete().connect(sigc::mem_fun(*this, &ChannelList::UpdateRemoveGuild)); - discord.signal_channel_delete().connect(sigc::mem_fun(*this, &ChannelList::UpdateRemoveChannel)); - discord.signal_channel_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateChannel)); - discord.signal_channel_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateCreateChannel)); - discord.signal_thread_delete().connect(sigc::mem_fun(*this, &ChannelList::OnThreadDelete)); - discord.signal_thread_update().connect(sigc::mem_fun(*this, &ChannelList::OnThreadUpdate)); - discord.signal_thread_list_sync().connect(sigc::mem_fun(*this, &ChannelList::OnThreadListSync)); - discord.signal_added_to_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadJoined)); - discord.signal_removed_from_thread().connect(sigc::mem_fun(*this, &ChannelList::OnThreadRemoved)); - discord.signal_guild_update().connect(sigc::mem_fun(*this, &ChannelList::UpdateGuild)); - discord.signal_message_ack().connect(sigc::mem_fun(*this, &ChannelList::OnMessageAck)); - discord.signal_channel_muted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelMute)); - discord.signal_channel_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnChannelUnmute)); - discord.signal_guild_muted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildMute)); - discord.signal_guild_unmuted().connect(sigc::mem_fun(*this, &ChannelList::OnGuildUnmute)); - -#if WITH_VOICE - discord.signal_voice_user_connect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserConnect)); - discord.signal_voice_user_disconnect().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceUserDisconnect)); - discord.signal_voice_state_set().connect(sigc::mem_fun(*this, &ChannelList::OnVoiceStateSet)); -#endif -} - -void ChannelList::UsePanedHack(Gtk::Paned &paned) { - paned.property_position().signal_changed().connect(sigc::mem_fun(*this, &ChannelList::OnPanedPositionChanged)); -} - -void ChannelList::OnPanedPositionChanged() { - m_view.queue_draw(); -} - -void ChannelList::UpdateListing() { - m_updating_listing = true; - - m_model->clear(); - - auto &discord = Abaddon::Get().GetDiscordClient(); - - /* - guild_folders looks something like this - "guild_folders": [ - { - "color": null, - "guild_ids": [ - "8009060___________" - ], - "id": null, - "name": null - }, - { - "color": null, - "guild_ids": [ - "99615594__________", - "86132141__________", - "35450138__________", - "83714048__________" - ], - "id": 2853066769, - "name": null - } - ] - - so if id != null then its a folder (they can have single entries) - */ - - int sort_value = 0; - - const auto folders = discord.GetUserSettings().GuildFolders; - const auto guild_ids = discord.GetUserSortedGuilds(); - - // user_settings.guild_folders may not contain every guild the user is in - // this seems to be the case if you organize your guilds and join a server without further organization - // so add guilds not present in guild_folders by descending id order first - - std::set<Snowflake> foldered_guilds; - for (const auto &group : folders) { - foldered_guilds.insert(group.GuildIDs.begin(), group.GuildIDs.end()); - } - - for (auto iter = guild_ids.rbegin(); iter != guild_ids.rend(); iter++) { - if (foldered_guilds.find(*iter) == foldered_guilds.end()) { - const auto guild = discord.GetGuild(*iter); - if (!guild.has_value()) continue; - auto tree_iter = AddGuild(*guild, m_model->children()); - if (tree_iter) (*tree_iter)[m_columns.m_sort] = sort_value++; - } - } - - // then whatever is in folders - - for (const auto &group : folders) { - auto iter = AddFolder(group); - if (iter) (*iter)[m_columns.m_sort] = sort_value++; - } - - m_updating_listing = false; - - AddPrivateChannels(); -} - -// TODO update for folders -void ChannelList::UpdateNewGuild(const GuildData &guild) { - AddGuild(guild, m_model->children()); - // update sort order - int sortnum = 0; - for (const auto guild_id : Abaddon::Get().GetDiscordClient().GetUserSortedGuilds()) { - auto iter = GetIteratorForGuildFromID(guild_id); - if (iter) - (*iter)[m_columns.m_sort] = ++sortnum; - } -} - -void ChannelList::UpdateRemoveGuild(Snowflake id) { - auto iter = GetIteratorForGuildFromID(id); - if (!iter) return; - m_model->erase(iter); -} - -void ChannelList::UpdateRemoveChannel(Snowflake id) { - auto iter = GetIteratorForRowFromID(id); - if (!iter) return; - m_model->erase(iter); -} - -void ChannelList::UpdateChannel(Snowflake id) { - auto iter = GetIteratorForRowFromID(id); - auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); - if (!iter || !channel.has_value()) return; - if (channel->Type == ChannelType::GUILD_CATEGORY) return UpdateChannelCategory(*channel); - if (!channel->IsText()) return; - - // refresh stuff that might have changed - const bool is_orphan_TMP = !channel->ParentID.has_value(); - (*iter)[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel->Name); - (*iter)[m_columns.m_nsfw] = channel->NSFW(); - (*iter)[m_columns.m_sort] = *channel->Position + (is_orphan_TMP ? OrphanChannelSortOffset : 0); - - // check if the parent has changed - Gtk::TreeModel::iterator new_parent; - if (channel->ParentID.has_value()) - new_parent = GetIteratorForRowFromID(*channel->ParentID); - else if (channel->GuildID.has_value()) - new_parent = GetIteratorForGuildFromID(*channel->GuildID); - - if (new_parent && iter->parent() != new_parent) - MoveRow(iter, new_parent); -} - -void ChannelList::UpdateCreateChannel(const ChannelData &channel) { - if (channel.Type == ChannelType::GUILD_CATEGORY) return (void)UpdateCreateChannelCategory(channel); - if (channel.Type == ChannelType::DM || channel.Type == ChannelType::GROUP_DM) return UpdateCreateDMChannel(channel); - if (channel.Type != ChannelType::GUILD_TEXT && channel.Type != ChannelType::GUILD_NEWS) return; - - Gtk::TreeRow channel_row; - bool orphan; - if (channel.ParentID.has_value()) { - orphan = false; - auto iter = GetIteratorForRowFromID(*channel.ParentID); - channel_row = *m_model->append(iter->children()); - } else { - orphan = true; - auto iter = GetIteratorForGuildFromID(*channel.GuildID); - channel_row = *m_model->append(iter->children()); - } - channel_row[m_columns.m_type] = RenderType::TextChannel; - channel_row[m_columns.m_id] = channel.ID; - channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name); - channel_row[m_columns.m_nsfw] = channel.NSFW(); - if (orphan) - channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset; - else - channel_row[m_columns.m_sort] = *channel.Position; -} - -void ChannelList::UpdateGuild(Snowflake id) { - auto iter = GetIteratorForGuildFromID(id); - auto &img = Abaddon::Get().GetImageManager(); - const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); - if (!iter || !guild.has_value()) return; - - (*iter)[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild->Name) + "</b>"; - (*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); - if (Abaddon::Get().GetSettings().ShowAnimations && guild->HasAnimatedIcon()) { - const auto cb = [this, id](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - auto iter = GetIteratorForGuildFromID(id); - if (iter) (*iter)[m_columns.m_icon_anim] = pb; - }; - img.LoadAnimationFromURL(guild->GetIconURL("gif", "32"), GuildIconSize, GuildIconSize, sigc::track_obj(cb, *this)); - } else if (guild->HasIcon()) { - const auto cb = [this, id](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - // iter might be invalid - auto iter = GetIteratorForGuildFromID(id); - if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(guild->GetIconURL("png", "32"), sigc::track_obj(cb, *this)); - } -} - -void ChannelList::OnThreadJoined(Snowflake id) { - if (GetIteratorForRowFromID(id)) return; - const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); - if (!channel.has_value()) return; - const auto parent = GetIteratorForRowFromID(*channel->ParentID); - if (parent) - CreateThreadRow(parent->children(), *channel); -} - -void ChannelList::OnThreadRemoved(Snowflake id) { - DeleteThreadRow(id); -} - -void ChannelList::OnThreadDelete(const ThreadDeleteData &data) { - DeleteThreadRow(data.ID); -} - -// todo probably make the row stick around if its selected until the selection changes -void ChannelList::OnThreadUpdate(const ThreadUpdateData &data) { - auto iter = GetIteratorForRowFromID(data.Thread.ID); - if (iter) - (*iter)[m_columns.m_name] = "- " + Glib::Markup::escape_text(*data.Thread.Name); - - if (data.Thread.ThreadMetadata->IsArchived) - DeleteThreadRow(data.Thread.ID); -} - -void ChannelList::OnThreadListSync(const ThreadListSyncData &data) { - // get the threads in the guild - std::vector<Snowflake> threads; - auto guild_iter = GetIteratorForGuildFromID(data.GuildID); - if (!guild_iter) return; - - std::queue<Gtk::TreeModel::iterator> queue; - queue.push(guild_iter); - - while (!queue.empty()) { - auto item = queue.front(); - queue.pop(); - if ((*item)[m_columns.m_type] == RenderType::Thread) - threads.push_back(static_cast<Snowflake>((*item)[m_columns.m_id])); - for (const auto &child : item->children()) - queue.push(child); - } - - // delete all threads not present in the synced data - for (auto thread_id : threads) { - if (std::find_if(data.Threads.begin(), data.Threads.end(), [thread_id](const auto &x) { return x.ID == thread_id; }) == data.Threads.end()) { - auto iter = GetIteratorForRowFromID(thread_id); - m_model->erase(iter); - } - } - - // delete all archived threads - for (auto thread : data.Threads) { - if (thread.ThreadMetadata->IsArchived) { - if (auto iter = GetIteratorForRowFromID(thread.ID)) - m_model->erase(iter); - } - } -} - -#ifdef WITH_VOICE -void ChannelList::OnVoiceUserConnect(Snowflake user_id, Snowflake channel_id) { - auto parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::VoiceChannel); - if (!parent_iter) parent_iter = GetIteratorForRowFromIDOfType(channel_id, RenderType::DM); - if (!parent_iter) return; - const auto user = Abaddon::Get().GetDiscordClient().GetUser(user_id); - if (!user.has_value()) return; - - CreateVoiceParticipantRow(*user, parent_iter->children()); -} - -void ChannelList::OnVoiceUserDisconnect(Snowflake user_id, Snowflake channel_id) { - if (auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant)) { - m_model->erase(iter); - } -} - -void ChannelList::OnVoiceStateSet(Snowflake user_id, Snowflake channel_id, VoiceStateFlags flags) { - if (auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant)) { - (*iter)[m_columns.m_voice_flags] = flags; - } -} -#endif - -void ChannelList::DeleteThreadRow(Snowflake id) { - auto iter = GetIteratorForRowFromID(id); - if (iter) - m_model->erase(iter); -} - -void ChannelList::OnChannelMute(Snowflake id) { - if (auto iter = GetIteratorForRowFromID(id)) - m_model->row_changed(m_model->get_path(iter), iter); -} - -void ChannelList::OnChannelUnmute(Snowflake id) { - if (auto iter = GetIteratorForRowFromID(id)) - m_model->row_changed(m_model->get_path(iter), iter); -} - -void ChannelList::OnGuildMute(Snowflake id) { - if (auto iter = GetIteratorForGuildFromID(id)) - m_model->row_changed(m_model->get_path(iter), iter); -} - -void ChannelList::OnGuildUnmute(Snowflake id) { - if (auto iter = GetIteratorForGuildFromID(id)) - m_model->row_changed(m_model->get_path(iter), iter); -} - -// create a temporary channel row for non-joined threads -// and delete them when the active channel switches off of them if still not joined -void ChannelList::SetActiveChannel(Snowflake id, bool expand_to) { - // mark channel as read when switching off - if (m_active_channel.IsValid()) - Abaddon::Get().GetDiscordClient().MarkChannelAsRead(m_active_channel, [](...) {}); - - m_active_channel = id; - - if (m_temporary_thread_row) { - const auto thread_id = static_cast<Snowflake>((*m_temporary_thread_row)[m_columns.m_id]); - const auto thread = Abaddon::Get().GetDiscordClient().GetChannel(thread_id); - if (thread.has_value() && (!thread->IsJoinedThread() || thread->ThreadMetadata->IsArchived)) - m_model->erase(m_temporary_thread_row); - m_temporary_thread_row = {}; - } - - const auto channel_iter = GetIteratorForRowFromID(id); - if (channel_iter) { - if (expand_to) { - const auto filter_path = m_filter_model->convert_child_path_to_path(m_model->get_path(channel_iter)); - if (filter_path) { - m_view.expand_to_path(filter_path); - } - } - const auto filter_iter = m_filter_model->convert_child_iter_to_iter(channel_iter); - if (filter_iter) { - m_view.get_selection()->select(filter_iter); - } - } else { - m_view.get_selection()->unselect_all(); - const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id); - if (!channel.has_value() || !channel->IsThread()) return; - auto parent_iter = GetIteratorForRowFromID(*channel->ParentID); - if (!parent_iter) return; - m_temporary_thread_row = CreateThreadRow(parent_iter->children(), *channel); - const auto filter_iter = m_filter_model->convert_child_iter_to_iter(m_temporary_thread_row); - if (filter_iter) { - m_view.get_selection()->select(filter_iter); - } - } -} - -void ChannelList::UseExpansionState(const ExpansionStateRoot &root) { -} - -ExpansionStateRoot ChannelList::GetExpansionState() const { - ExpansionStateRoot r; - - return r; -} - -Gtk::TreeModel::iterator ChannelList::AddFolder(const UserSettingsGuildFoldersEntry &folder) { - if (!folder.ID.has_value()) { - // just a guild - if (!folder.GuildIDs.empty()) { - const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(folder.GuildIDs[0]); - if (guild.has_value()) { - return AddGuild(*guild, m_model->children()); - } - } - } else { - auto folder_row = *m_model->append(); - folder_row[m_columns.m_type] = RenderType::Folder; - folder_row[m_columns.m_id] = *folder.ID; - if (folder.Name.has_value()) { - folder_row[m_columns.m_name] = Glib::Markup::escape_text(*folder.Name); - } else { - folder_row[m_columns.m_name] = "Folder"; - } - if (folder.Color.has_value()) { - folder_row[m_columns.m_color] = IntToRGBA(*folder.Color); - } - - int sort_value = 0; - for (const auto &guild_id : folder.GuildIDs) { - const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(guild_id); - if (guild.has_value()) { - auto guild_row = AddGuild(*guild, folder_row->children()); - (*guild_row)[m_columns.m_sort] = sort_value++; - } - } - - return folder_row; - } - - return {}; -} - -Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild, const Gtk::TreeNodeChildren &root) { - auto &discord = Abaddon::Get().GetDiscordClient(); - auto &img = Abaddon::Get().GetImageManager(); - - auto guild_row = *m_model->append(root); - guild_row[m_columns.m_type] = RenderType::Guild; - guild_row[m_columns.m_id] = guild.ID; - guild_row[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild.Name) + "</b>"; - guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); - - if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) { - const auto cb = [this, id = guild.ID](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { - auto iter = GetIteratorForGuildFromID(id); - if (iter) (*iter)[m_columns.m_icon_anim] = pb; - }; - img.LoadAnimationFromURL(guild.GetIconURL("gif", "32"), GuildIconSize, GuildIconSize, sigc::track_obj(cb, *this)); - } else if (guild.HasIcon()) { - const auto cb = [this, id = guild.ID](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - auto iter = GetIteratorForGuildFromID(id); - if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(GuildIconSize, GuildIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(guild.GetIconURL("png", "32"), sigc::track_obj(cb, *this)); - } - - if (!guild.Channels.has_value()) return guild_row; - - // separate out the channels - std::vector<ChannelData> orphan_channels; - std::map<Snowflake, std::vector<ChannelData>> categories; - - for (const auto &channel_ : *guild.Channels) { - const auto channel = discord.GetChannel(channel_.ID); - if (!channel.has_value()) continue; -#ifdef WITH_VOICE - if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS || channel->Type == ChannelType::GUILD_VOICE) { -#else - if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) { -#endif - if (channel->ParentID.has_value()) - categories[*channel->ParentID].push_back(*channel); - else - orphan_channels.push_back(*channel); - } else if (channel->Type == ChannelType::GUILD_CATEGORY) { - categories[channel->ID]; - } - } - - std::map<Snowflake, std::vector<ChannelData>> threads; - for (const auto &tmp : *guild.Threads) { - const auto thread = discord.GetChannel(tmp.ID); - if (thread.has_value()) - threads[*thread->ParentID].push_back(*thread); - } - const auto add_threads = [&](const ChannelData &channel, const Gtk::TreeRow &row) { - row[m_columns.m_expanded] = true; - - const auto it = threads.find(channel.ID); - if (it == threads.end()) return; - - for (const auto &thread : it->second) { - CreateThreadRow(row.children(), thread); - } - }; - -#ifdef WITH_VOICE - auto add_voice_participants = [this, &discord](const ChannelData &channel, const Gtk::TreeNodeChildren &root) { - for (auto user_id : discord.GetUsersInVoiceChannel(channel.ID)) { - if (const auto user = discord.GetUser(user_id); user.has_value()) { - CreateVoiceParticipantRow(*user, root); - } - } - }; -#endif - - for (const auto &channel : orphan_channels) { - auto channel_row = *m_model->append(guild_row.children()); - if (IsTextChannel(channel.Type)) { - channel_row[m_columns.m_type] = RenderType::TextChannel; - channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name); - } -#ifdef WITH_VOICE - else { - channel_row[m_columns.m_type] = RenderType::VoiceChannel; - channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name); - add_voice_participants(channel, channel_row->children()); - } -#endif - channel_row[m_columns.m_id] = channel.ID; - channel_row[m_columns.m_sort] = *channel.Position + OrphanChannelSortOffset; - channel_row[m_columns.m_nsfw] = channel.NSFW(); - add_threads(channel, channel_row); - } - - for (const auto &[category_id, channels] : categories) { - const auto category = discord.GetChannel(category_id); - if (!category.has_value()) continue; - auto cat_row = *m_model->append(guild_row.children()); - cat_row[m_columns.m_type] = RenderType::Category; - cat_row[m_columns.m_id] = category_id; - cat_row[m_columns.m_name] = Glib::Markup::escape_text(*category->Name); - cat_row[m_columns.m_sort] = *category->Position; - cat_row[m_columns.m_expanded] = true; - // m_view.expand_row wont work because it might not have channels - - for (const auto &channel : channels) { - auto channel_row = *m_model->append(cat_row.children()); - if (IsTextChannel(channel.Type)) { - channel_row[m_columns.m_type] = RenderType::TextChannel; - channel_row[m_columns.m_name] = "#" + Glib::Markup::escape_text(*channel.Name); - } -#ifdef WITH_VOICE - else { - channel_row[m_columns.m_type] = RenderType::VoiceChannel; - channel_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name); - add_voice_participants(channel, channel_row->children()); - } -#endif - channel_row[m_columns.m_id] = channel.ID; - channel_row[m_columns.m_sort] = *channel.Position; - channel_row[m_columns.m_nsfw] = channel.NSFW(); - add_threads(channel, channel_row); - } - } - - return guild_row; -} - -Gtk::TreeModel::iterator ChannelList::UpdateCreateChannelCategory(const ChannelData &channel) { - const auto iter = GetIteratorForGuildFromID(*channel.GuildID); - if (!iter) return {}; - - auto cat_row = *m_model->append(iter->children()); - cat_row[m_columns.m_type] = RenderType::Category; - cat_row[m_columns.m_id] = channel.ID; - cat_row[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name); - cat_row[m_columns.m_sort] = *channel.Position; - cat_row[m_columns.m_expanded] = true; - - return cat_row; -} - -Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel) { - auto thread_iter = m_model->append(children); - auto thread_row = *thread_iter; - thread_row[m_columns.m_type] = RenderType::Thread; - thread_row[m_columns.m_id] = channel.ID; - thread_row[m_columns.m_name] = "- " + Glib::Markup::escape_text(*channel.Name); - thread_row[m_columns.m_sort] = static_cast<int64_t>(channel.ID); - thread_row[m_columns.m_nsfw] = false; - - return thread_iter; -} - -#ifdef WITH_VOICE -Gtk::TreeModel::iterator ChannelList::CreateVoiceParticipantRow(const UserData &user, const Gtk::TreeNodeChildren &parent) { - auto row = *m_model->append(parent); - row[m_columns.m_type] = RenderType::VoiceParticipant; - row[m_columns.m_id] = user.ID; - row[m_columns.m_name] = user.GetDisplayNameEscaped(); - - const auto voice_state = Abaddon::Get().GetDiscordClient().GetVoiceState(user.ID); - if (voice_state.has_value()) { - row[m_columns.m_voice_flags] = voice_state->second; - } - - auto &img = Abaddon::Get().GetImageManager(); - row[m_columns.m_icon] = img.GetPlaceholder(VoiceParticipantIconSize); - const auto cb = [this, user_id = user.ID](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - auto iter = GetIteratorForRowFromIDOfType(user_id, RenderType::VoiceParticipant); - if (iter) (*iter)[m_columns.m_icon] = pb->scale_simple(VoiceParticipantIconSize, VoiceParticipantIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(user.GetAvatarURL("png", "32"), sigc::track_obj(cb, *this)); - - return row; -} -#endif - -void ChannelList::UpdateChannelCategory(const ChannelData &channel) { - auto iter = GetIteratorForRowFromID(channel.ID); - if (!iter) return; - - (*iter)[m_columns.m_sort] = *channel.Position; - (*iter)[m_columns.m_name] = Glib::Markup::escape_text(*channel.Name); -} - -// todo this all needs refactoring for shooore -Gtk::TreeModel::iterator ChannelList::GetIteratorForTopLevelFromID(Snowflake id) { - for (const auto &child : m_model->children()) { - if ((child[m_columns.m_type] == RenderType::Guild || child[m_columns.m_type] == RenderType::Folder) && child[m_columns.m_id] == id) { - return child; - } else if (child[m_columns.m_type] == RenderType::Folder) { - for (const auto &folder_child : child->children()) { - if (folder_child[m_columns.m_id] == id) { - return folder_child; - } - } - } - } - return {}; -} - -Gtk::TreeModel::iterator ChannelList::GetIteratorForGuildFromID(Snowflake id) { - for (const auto &child : m_model->children()) { - if (child[m_columns.m_type] == RenderType::Guild && child[m_columns.m_id] == id) { - return child; - } else if (child[m_columns.m_type] == RenderType::Folder) { - for (const auto &folder_child : child->children()) { - if (folder_child[m_columns.m_id] == id) { - return folder_child; - } - } - } - } - return {}; -} - -Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromID(Snowflake id) { - std::queue<Gtk::TreeModel::iterator> queue; - for (const auto &child : m_model->children()) - for (const auto &child2 : child.children()) - queue.push(child2); - - while (!queue.empty()) { - auto item = queue.front(); - if ((*item)[m_columns.m_id] == id && (*item)[m_columns.m_type] != RenderType::Guild) return item; - for (const auto &child : item->children()) - queue.push(child); - queue.pop(); - } - - return {}; -} - -Gtk::TreeModel::iterator ChannelList::GetIteratorForRowFromIDOfType(Snowflake id, RenderType type) { - std::queue<Gtk::TreeModel::iterator> queue; - for (const auto &child : m_model->children()) - for (const auto &child2 : child.children()) - queue.push(child2); - - while (!queue.empty()) { - auto item = queue.front(); - if ((*item)[m_columns.m_type] == type && (*item)[m_columns.m_id] == id) return item; - for (const auto &child : item->children()) - queue.push(child); - queue.pop(); - } - - return {}; -} - -bool ChannelList::IsTextChannel(ChannelType type) { - return type == ChannelType::GUILD_TEXT || type == ChannelType::GUILD_NEWS; -} - -// this should be unncessary but something is behaving strange so its just in case -void ChannelList::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const { - (*iter)[m_columns.m_expanded] = false; -} - -void ChannelList::OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) { - // restore previous expansion - for (auto it = iter->children().begin(); it != iter->children().end(); it++) { - if ((*it)[m_columns.m_expanded]) - m_view.expand_row(m_filter_model->get_path(it), false); - } - - // try and restore selection if previous collapsed - if (auto selection = m_view.get_selection(); selection && !selection->get_selected()) { - selection->select(m_last_selected); - } - - (*iter)[m_columns.m_expanded] = true; -} - -bool ChannelList::SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool is_currently_selected) { - if (auto selection = m_view.get_selection()) { - if (auto row = selection->get_selected()) { - m_last_selected = m_filter_model->get_path(row); - } - } - - auto type = (*model->get_iter(path))[m_columns.m_type]; - return type == RenderType::TextChannel || type == RenderType::DM || type == RenderType::Thread; -} - -void ChannelList::AddPrivateChannels() { - auto header_row = *m_model->append(); - header_row[m_columns.m_type] = RenderType::DMHeader; - header_row[m_columns.m_sort] = -1; - header_row[m_columns.m_name] = "<b>Direct Messages</b>"; - m_dm_header = m_model->get_path(header_row); - - auto &discord = Abaddon::Get().GetDiscordClient(); - auto &img = Abaddon::Get().GetImageManager(); - - const auto dm_ids = discord.GetPrivateChannels(); - for (const auto dm_id : dm_ids) { - const auto dm = discord.GetChannel(dm_id); - if (!dm.has_value()) continue; - - std::optional<UserData> top_recipient; - const auto recipients = dm->GetDMRecipients(); - if (!recipients.empty()) - top_recipient = recipients[0]; - - auto iter = m_model->append(header_row->children()); - auto row = *iter; - row[m_columns.m_type] = RenderType::DM; - row[m_columns.m_id] = dm_id; - row[m_columns.m_name] = Glib::Markup::escape_text(dm->GetDisplayName()); - row[m_columns.m_sort] = static_cast<int64_t>(-(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id)); - row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize); - row[m_columns.m_expanded] = true; - -#ifdef WITH_VOICE - for (auto user_id : discord.GetUsersInVoiceChannel(dm_id)) { - if (const auto user = discord.GetUser(user_id); user.has_value()) { - CreateVoiceParticipantRow(*user, row->children()); - } - } -#endif - - SetDMChannelIcon(iter, *dm); - } -} - -void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) { - auto header_row = m_model->get_iter(m_dm_header); - auto &img = Abaddon::Get().GetImageManager(); - - auto iter = m_model->append(header_row->children()); - auto row = *iter; - row[m_columns.m_type] = RenderType::DM; - row[m_columns.m_id] = dm.ID; - row[m_columns.m_name] = Glib::Markup::escape_text(dm.GetDisplayName()); - row[m_columns.m_sort] = static_cast<int64_t>(-(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID)); - row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize); - - SetDMChannelIcon(iter, dm); -} - -void ChannelList::SetDMChannelIcon(Gtk::TreeIter iter, const ChannelData &dm) { - auto &img = Abaddon::Get().GetImageManager(); - - std::optional<UserData> top_recipient; - const auto recipients = dm.GetDMRecipients(); - if (!recipients.empty()) - top_recipient = recipients[0]; - - if (dm.HasIcon()) { - const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - if (iter) - (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(dm.GetIconURL(), sigc::track_obj(cb, *this)); - } else if (dm.Type == ChannelType::DM && top_recipient.has_value()) { - const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - if (iter) - (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL(top_recipient->GetAvatarURL("png", "32"), sigc::track_obj(cb, *this)); - } else { // GROUP_DM - std::string hash; - switch (dm.ID.GetUnixMilliseconds() % 8) { - case 0: - hash = "ee9275c5a437f7dc7f9430ba95f12ebd"; - break; - case 1: - hash = "9baf45aac2a0ec2e2dab288333acb9d9"; - break; - case 2: - hash = "7ba11ffb1900fa2b088cb31324242047"; - break; - case 3: - hash = "f90fca70610c4898bc57b58bce92f587"; - break; - case 4: - hash = "e2779af34b8d9126b77420e5f09213ce"; - break; - case 5: - hash = "c6851bd0b03f1cca5a8c1e720ea6ea17"; - break; - case 6: - hash = "f7e38ac976a2a696161c923502a8345b"; - break; - case 7: - default: - hash = "3cb840d03313467838d658bbec801fcd"; - break; - } - const auto cb = [this, iter](const Glib::RefPtr<Gdk::Pixbuf> &pb) { - if (iter) - (*iter)[m_columns.m_icon] = pb->scale_simple(DMIconSize, DMIconSize, Gdk::INTERP_BILINEAR); - }; - img.LoadFromURL("https://discord.com/assets/" + hash + ".png", sigc::track_obj(cb, *this)); - } -} - -void ChannelList::RedrawUnreadIndicatorsForChannel(const ChannelData &channel) { - if (channel.GuildID.has_value()) { - auto iter = GetIteratorForGuildFromID(*channel.GuildID); - if (iter) m_model->row_changed(m_model->get_path(iter), iter); - } - if (channel.ParentID.has_value()) { - auto iter = GetIteratorForRowFromIDOfType(*channel.ParentID, RenderType::Category); - if (iter) m_model->row_changed(m_model->get_path(iter), iter); - } -} - -void ChannelList::OnMessageAck(const MessageAckData &data) { - // trick renderer into redrawing - m_model->row_changed(Gtk::TreeModel::Path("0"), m_model->get_iter("0")); // 0 is always path for dm header - auto iter = GetIteratorForRowFromID(data.ChannelID); - if (iter) m_model->row_changed(m_model->get_path(iter), iter); - auto channel = Abaddon::Get().GetDiscordClient().GetChannel(data.ChannelID); - if (channel.has_value()) { - RedrawUnreadIndicatorsForChannel(*channel); - } -} - -void ChannelList::OnMessageCreate(const Message &msg) { - auto iter = GetIteratorForRowFromID(msg.ChannelID); - if (iter) m_model->row_changed(m_model->get_path(iter), iter); // redraw - const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(msg.ChannelID); - if (!channel.has_value()) return; - if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM) { - if (iter) - (*iter)[m_columns.m_sort] = static_cast<int64_t>(-msg.ID); - } - RedrawUnreadIndicatorsForChannel(*channel); -} - -bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) { - if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) { - if (m_view.get_path_at_pos(static_cast<int>(ev->x), static_cast<int>(ev->y), m_path_for_menu)) { - auto row = (*m_filter_model->get_iter(m_path_for_menu)); - switch (static_cast<RenderType>(row[m_columns.m_type])) { - case RenderType::Guild: - OnGuildSubmenuPopup(); - m_menu_guild.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - break; - case RenderType::Category: - OnCategorySubmenuPopup(); - m_menu_category.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - break; - case RenderType::TextChannel: - OnChannelSubmenuPopup(); - m_menu_channel.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - break; -#ifdef WITH_VOICE - case RenderType::VoiceChannel: - OnVoiceChannelSubmenuPopup(); - m_menu_voice_channel.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - break; -#endif - case RenderType::DM: { - OnDMSubmenuPopup(); - const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(static_cast<Snowflake>(row[m_columns.m_id])); - if (channel.has_value()) { - m_menu_dm_close.set_label(channel->Type == ChannelType::DM ? "Close" : "Leave"); - m_menu_dm_close.show(); - } else - m_menu_dm_close.hide(); - m_menu_dm.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - } break; - case RenderType::Thread: { - OnThreadSubmenuPopup(); - m_menu_thread.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev)); - break; - } break; - default: - break; - } - } - return true; - } - return false; -} - -void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::iterator &new_parent) { - // duplicate the row data under the new parent and then delete the old row - auto row = *m_model->append(new_parent->children()); - // would be nice to be able to get all columns out at runtime so i dont need this -#define M(name) \ - row[m_columns.name] = static_cast<decltype(m_columns.name)::ElementType>((*iter)[m_columns.name]); - M(m_type); - M(m_id); - M(m_name); - M(m_icon); - M(m_icon_anim); - M(m_sort); - M(m_nsfw); - M(m_expanded); - M(m_color); -#undef M - - // recursively move children - // weird construct to work around iterator invalidation (at least i think thats what the problem was) - const auto tmp = iter->children(); - const auto children = std::vector<Gtk::TreeRow>(tmp.begin(), tmp.end()); - for (size_t i = 0; i < children.size(); i++) - MoveRow(children[i], row); - - // delete original - m_model->erase(iter); -} - -void ChannelList::OnGuildSubmenuPopup() { - const auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsGuildMuted(id)) - m_menu_guild_toggle_mute.set_label("Unmute"); - else - m_menu_guild_toggle_mute.set_label("Mute"); - - const auto guild = discord.GetGuild(id); - const auto self_id = discord.GetUserData().ID; - m_menu_guild_leave.set_sensitive(!(guild.has_value() && guild->OwnerID == self_id)); -} - -void ChannelList::OnCategorySubmenuPopup() { - const auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - if (Abaddon::Get().GetDiscordClient().IsChannelMuted(id)) - m_menu_category_toggle_mute.set_label("Unmute"); - else - m_menu_category_toggle_mute.set_label("Mute"); -} - -void ChannelList::OnChannelSubmenuPopup() { - const auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); -#ifdef WITH_LIBHANDY - const auto perms = discord.HasChannelPermission(discord.GetUserData().ID, id, Permission::VIEW_CHANNEL); - m_menu_channel_open_tab.set_sensitive(perms); -#endif - if (discord.IsChannelMuted(id)) - m_menu_channel_toggle_mute.set_label("Unmute"); - else - m_menu_channel_toggle_mute.set_label("Mute"); -} - -#ifdef WITH_VOICE -void ChannelList::OnVoiceChannelSubmenuPopup() { - const auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) { - m_menu_voice_channel_join.set_sensitive(false); - m_menu_voice_channel_disconnect.set_sensitive(discord.GetVoiceChannelID() == id); - } else { - m_menu_voice_channel_join.set_sensitive(true); - m_menu_voice_channel_disconnect.set_sensitive(false); - } -} -#endif - -void ChannelList::OnDMSubmenuPopup() { - auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - auto &discord = Abaddon::Get().GetDiscordClient(); - if (discord.IsChannelMuted(id)) - m_menu_dm_toggle_mute.set_label("Unmute"); - else - m_menu_dm_toggle_mute.set_label("Mute"); - -#ifdef WITH_VOICE - if (discord.IsVoiceConnected() || discord.IsVoiceConnecting()) { - m_menu_dm_join_voice.set_sensitive(false); - m_menu_dm_disconnect_voice.set_sensitive(discord.GetVoiceChannelID() == id); - } else { - m_menu_dm_join_voice.set_sensitive(true); - m_menu_dm_disconnect_voice.set_sensitive(false); - } -#endif -} - -void ChannelList::OnThreadSubmenuPopup() { - m_menu_thread_archive.set_visible(false); - m_menu_thread_unarchive.set_visible(false); - - auto &discord = Abaddon::Get().GetDiscordClient(); - auto iter = m_model->get_iter(m_path_for_menu); - if (!iter) return; - const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]); - - if (discord.IsChannelMuted(id)) - m_menu_thread_toggle_mute.set_label("Unmute"); - else - m_menu_thread_toggle_mute.set_label("Mute"); - - auto channel = discord.GetChannel(id); - if (!channel.has_value() || !channel->ThreadMetadata.has_value()) return; - if (!discord.HasGuildPermission(discord.GetUserData().ID, *channel->GuildID, Permission::MANAGE_THREADS)) return; - - m_menu_thread_archive.set_visible(!channel->ThreadMetadata->IsArchived); - m_menu_thread_unarchive.set_visible(channel->ThreadMetadata->IsArchived); -} - -ChannelList::type_signal_action_channel_item_select ChannelList::signal_action_channel_item_select() { - return m_signal_action_channel_item_select; -} - -ChannelList::type_signal_action_guild_leave ChannelList::signal_action_guild_leave() { - return m_signal_action_guild_leave; -} - -ChannelList::type_signal_action_guild_settings ChannelList::signal_action_guild_settings() { - return m_signal_action_guild_settings; -} - -#ifdef WITH_LIBHANDY -ChannelList::type_signal_action_open_new_tab ChannelList::signal_action_open_new_tab() { - return m_signal_action_open_new_tab; -} -#endif - -#ifdef WITH_VOICE -ChannelList::type_signal_action_join_voice_channel ChannelList::signal_action_join_voice_channel() { - return m_signal_action_join_voice_channel; -} - -ChannelList::type_signal_action_disconnect_voice ChannelList::signal_action_disconnect_voice() { - return m_signal_action_disconnect_voice; -} -#endif - -ChannelList::ModelColumns::ModelColumns() { - add(m_type); - add(m_id); - add(m_name); - add(m_icon); - add(m_icon_anim); - add(m_sort); - add(m_nsfw); - add(m_expanded); - add(m_color); - add(m_voice_flags); -} |