summaryrefslogtreecommitdiff
path: root/src/components/channels.hpp
blob: 044d0b570797e2a689ce7a77736aec6eb295cffb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#pragma once
#include <gtkmm.h>
#include <string>
#include <queue>
#include <mutex>
#include <unordered_set>
#include <unordered_map>
#include <sigc++/sigc++.h>
#include "discord/discord.hpp"
#include "state.hpp"
#include "channelscellrenderer.hpp"

constexpr static int GuildIconSize = 24;
constexpr static int DMIconSize = 20;
constexpr static int OrphanChannelSortOffset = -100; // forces orphan channels to the top of the list

class ChannelList : public Gtk::ScrolledWindow {
public:
    ChannelList();

    void UpdateListing();
    void SetActiveChannel(Snowflake id);

    // channel list should be populated when this is called
    void UseExpansionState(const ExpansionStateRoot &state);
    ExpansionStateRoot GetExpansionState() const;

    void UsePanedHack(Gtk::Paned &paned);

protected:
    void OnPanedPositionChanged();

    void UpdateNewGuild(const GuildData &guild);
    void UpdateRemoveGuild(Snowflake id);
    void UpdateRemoveChannel(Snowflake id);
    void UpdateChannel(Snowflake id);
    void UpdateCreateChannel(const ChannelData &channel);
    void UpdateGuild(Snowflake id);
    void DeleteThreadRow(Snowflake id);
    void OnChannelMute(Snowflake id);
    void OnChannelUnmute(Snowflake id);
    void OnGuildMute(Snowflake id);
    void OnGuildUnmute(Snowflake id);

    void OnThreadJoined(Snowflake id);
    void OnThreadRemoved(Snowflake id);
    void OnThreadDelete(const ThreadDeleteData &data);
    void OnThreadUpdate(const ThreadUpdateData &data);
    void OnThreadListSync(const ThreadListSyncData &data);

    Gtk::TreeView m_view;

    class ModelColumns : public Gtk::TreeModel::ColumnRecord {
    public:
        ModelColumns();

        Gtk::TreeModelColumn<RenderType> m_type;
        Gtk::TreeModelColumn<uint64_t> m_id;
        Gtk::TreeModelColumn<Glib::ustring> m_name;
        Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> m_icon;
        Gtk::TreeModelColumn<Glib::RefPtr<Gdk::PixbufAnimation>> m_icon_anim;
        Gtk::TreeModelColumn<int64_t> m_sort;
        Gtk::TreeModelColumn<bool> m_nsfw;
        // Gtk::CellRenderer's property_is_expanded only works how i want it to if it has children
        // because otherwise it doesnt count as an "expander" (property_is_expander)
        // so this solution will have to do which i hate but the alternative is adding invisible children
        // to all categories without children and having a filter model but that sounds worse
        // of course its a lot better than the absolute travesty i had before
        Gtk::TreeModelColumn<bool> m_expanded;
    };

    ModelColumns m_columns;
    Glib::RefPtr<Gtk::TreeStore> m_model;

    Gtk::TreeModel::iterator AddGuild(const GuildData &guild);
    Gtk::TreeModel::iterator UpdateCreateChannelCategory(const ChannelData &channel);
    Gtk::TreeModel::iterator CreateThreadRow(const Gtk::TreeNodeChildren &children, const ChannelData &channel);

    void UpdateChannelCategory(const ChannelData &channel);

    // separation necessary because a channel and guild can share the same id
    Gtk::TreeModel::iterator GetIteratorForGuildFromID(Snowflake id);
    Gtk::TreeModel::iterator GetIteratorForChannelFromID(Snowflake id);

    bool IsTextChannel(ChannelType type);

    void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const;
    void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
    bool SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
    bool OnButtonPressEvent(GdkEventButton *ev);

    void MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::iterator &new_parent);

    Gtk::TreeModel::Path m_last_selected;
    Gtk::TreeModel::Path m_dm_header;

    void AddPrivateChannels();
    void UpdateCreateDMChannel(const ChannelData &channel);

    void OnMessageAck(const MessageAckData &data);

    void OnMessageCreate(const Message &msg);
    Gtk::TreeModel::Path m_path_for_menu;

    // cant be recovered through selection
    Gtk::TreeModel::iterator m_temporary_thread_row;

    Gtk::Menu m_menu_guild;
    Gtk::MenuItem m_menu_guild_copy_id;
    Gtk::MenuItem m_menu_guild_settings;
    Gtk::MenuItem m_menu_guild_leave;
    Gtk::MenuItem m_menu_guild_mark_as_read;
    Gtk::MenuItem m_menu_guild_toggle_mute;

    Gtk::Menu m_menu_category;
    Gtk::MenuItem m_menu_category_copy_id;
    Gtk::MenuItem m_menu_category_toggle_mute;

    Gtk::Menu m_menu_channel;
    Gtk::MenuItem m_menu_channel_copy_id;
    Gtk::MenuItem m_menu_channel_mark_as_read;
    Gtk::MenuItem m_menu_channel_toggle_mute;

    Gtk::Menu m_menu_dm;
    Gtk::MenuItem m_menu_dm_copy_id;
    Gtk::MenuItem m_menu_dm_close;
    Gtk::MenuItem m_menu_dm_toggle_mute;

    Gtk::Menu m_menu_thread;
    Gtk::MenuItem m_menu_thread_copy_id;
    Gtk::MenuItem m_menu_thread_leave;
    Gtk::MenuItem m_menu_thread_archive;
    Gtk::MenuItem m_menu_thread_unarchive;
    Gtk::MenuItem m_menu_thread_mark_as_read;
    Gtk::MenuItem m_menu_thread_toggle_mute;

    void OnGuildSubmenuPopup();
    void OnCategorySubmenuPopup();
    void OnChannelSubmenuPopup();
    void OnDMSubmenuPopup();
    void OnThreadSubmenuPopup();

    bool m_updating_listing = false;

    Snowflake m_active_channel;

    // (GetIteratorForChannelFromID is rather slow)
    // only temporary since i dont want to worry about maintaining this map
    std::unordered_map<Snowflake, Gtk::TreeModel::iterator> m_tmp_channel_map;

public:
    typedef sigc::signal<void, Snowflake> type_signal_action_channel_item_select;
    typedef sigc::signal<void, Snowflake> type_signal_action_guild_leave;
    typedef sigc::signal<void, Snowflake> type_signal_action_guild_settings;

    type_signal_action_channel_item_select signal_action_channel_item_select();
    type_signal_action_guild_leave signal_action_guild_leave();
    type_signal_action_guild_settings signal_action_guild_settings();

protected:
    type_signal_action_channel_item_select m_signal_action_channel_item_select;
    type_signal_action_guild_leave m_signal_action_guild_leave;
    type_signal_action_guild_settings m_signal_action_guild_settings;
};