merge master
14
.github/workflows/ci.yml
vendored
@ -95,9 +95,9 @@ jobs:
|
||||
del /f /s /q "${{ runner.workspace }}\build\.ninja_log"
|
||||
del /f /s /q "${{ runner.workspace }}\build\abaddon.ilk"
|
||||
del /f /s /q "${{ runner.workspace }}\build\CMakeCache.txt"
|
||||
xcopy /E /I "${{ github.workspace }}\css" "${{ runner.workspace }}\build\css"
|
||||
xcopy /E /I "${{ github.workspace }}\res" "${{ runner.workspace }}\build\res"
|
||||
xcopy /E /I "${{ github.workspace }}\fonts" "${{ runner.workspace }}\build\fonts"
|
||||
xcopy /E /I "${{ github.workspace }}\res\css" "${{ runner.workspace }}\build\css"
|
||||
xcopy /E /I "${{ github.workspace }}\res\res" "${{ runner.workspace }}\build\res"
|
||||
xcopy /E /I "${{ github.workspace }}\res\fonts" "${{ runner.workspace }}\build\fonts"
|
||||
mkdir "${{ runner.workspace }}\build\share"
|
||||
xcopy /E /I "${{ github.workspace }}\ci\gtk-for-windows\gtk-nsis-pack\share\glib-2.0" "${{ runner.workspace }}\build\share\glib-2.0"
|
||||
copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper.exe" "${{ runner.workspace }}\build\gspawn-win64-helper.exe"
|
||||
@ -137,8 +137,8 @@ jobs:
|
||||
run: |
|
||||
mkdir "${{ runner.workspace }}/artifactdir"
|
||||
cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon"
|
||||
cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css"
|
||||
cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res"
|
||||
cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css"
|
||||
cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res"
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -190,8 +190,8 @@ jobs:
|
||||
run: |
|
||||
mkdir "${{ runner.workspace }}/artifactdir"
|
||||
cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon"
|
||||
cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css"
|
||||
cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res"
|
||||
cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css"
|
||||
cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res"
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v2
|
||||
|
9
.gitmodules
vendored
@ -1,15 +1,12 @@
|
||||
[submodule "vcpkg"]
|
||||
path = ci/vcpkg
|
||||
url = https://github.com/microsoft/vcpkg/
|
||||
[submodule "thirdparty/simpleini"]
|
||||
path = thirdparty/simpleini
|
||||
url = https://github.com/brofield/simpleini
|
||||
[submodule "thirdparty/IXWebSocket"]
|
||||
path = thirdparty/IXWebSocket
|
||||
url = https://github.com/machinezone/ixwebsocket
|
||||
[submodule "ci/vcpkg"]
|
||||
path = ci/vcpkg
|
||||
url = https://github.com/microsoft/vcpkg
|
||||
[submodule "ci/gtk-for-windows"]
|
||||
path = ci/gtk-for-windows
|
||||
url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer
|
||||
[submodule "subprojects/ixwebsocket"]
|
||||
path = subprojects/ixwebsocket
|
||||
url = https://github.com/machinezone/ixwebsocket
|
||||
|
@ -18,17 +18,10 @@ set(USE_OPEN_SSL TRUE)
|
||||
find_package(IXWebSocket QUIET)
|
||||
if (NOT IXWebSocket_FOUND)
|
||||
message("ixwebsocket was not found and will be included as a submodule")
|
||||
add_subdirectory(thirdparty/IXWebSocket)
|
||||
add_subdirectory(subprojects/ixwebsocket)
|
||||
include_directories(IXWEBSOCKET_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(SI_NO_CONVERSION) # only CSimpleIniA is used
|
||||
find_package(simpleini QUIET)
|
||||
if (NOT simpleini_FOUND)
|
||||
message("simpleini was not found and will be included as a submodule")
|
||||
include_directories(thirdparty/simpleini)
|
||||
endif()
|
||||
|
||||
if(MINGW OR WIN32)
|
||||
link_libraries(ws2_32)
|
||||
endif()
|
||||
@ -41,28 +34,16 @@ if(WIN32)
|
||||
link_libraries(${Fontconfig_LIBRARIES})
|
||||
endif()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||
configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||
|
||||
file(GLOB ABADDON_SOURCES
|
||||
"*.h"
|
||||
"*.hpp"
|
||||
"*.cpp"
|
||||
"discord/*.hpp"
|
||||
"discord/*.cpp"
|
||||
"components/*.hpp"
|
||||
"components/*.cpp"
|
||||
"windows/*.hpp"
|
||||
"windows/*.cpp"
|
||||
"windows/guildsettings/*.hpp"
|
||||
"windows/guildsettings/*.cpp"
|
||||
"windows/profile/*.hpp"
|
||||
"windows/profile/*.cpp"
|
||||
"dialogs/*.hpp"
|
||||
"dialogs/*.cpp"
|
||||
file(GLOB_RECURSE ABADDON_SOURCES
|
||||
"src/*.h"
|
||||
"src/*.hpp"
|
||||
"src/*.cpp"
|
||||
)
|
||||
|
||||
add_executable(abaddon ${ABADDON_SOURCES})
|
||||
target_include_directories(abaddon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(abaddon PUBLIC ${PROJECT_SOURCE_DIR}/src)
|
||||
target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR})
|
||||
target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS})
|
||||
target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS})
|
||||
|
15
README.md
@ -4,7 +4,7 @@ Alternative Discord client made in C++ with GTK
|
||||
|
||||
<img src="/.readme/s3.png">
|
||||
|
||||
[😎Discord Server](https://discord.gg/wkCU3vuzG5)
|
||||
<a href="https://discord.gg/wkCU3vuzG5"><img src="https://discord.com/api/guilds/858156817711890443/widget.png?style=shield"></a>
|
||||
|
||||
Current features:
|
||||
* Not Electron
|
||||
@ -32,7 +32,7 @@ Current features:
|
||||
### Building manually (recommended if not on Windows):
|
||||
#### Windows:
|
||||
1. `git clone https://github.com/uowuo/abaddon && cd abaddon`
|
||||
2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows simpleini:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows`
|
||||
2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows`
|
||||
3. `mkdir build && cd build`
|
||||
4. `cmake -G"Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=c:\path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVCPKG_TARGET_TRIPLET=x64-windows ..`
|
||||
5. Build with Visual Studio
|
||||
@ -75,7 +75,6 @@ On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/
|
||||
* [IXWebSocket](https://github.com/machinezone/IXWebSocket)
|
||||
* [libcurl](https://curl.se/)
|
||||
* [zlib](https://zlib.net/)
|
||||
* [simpleini](https://github.com/brofield/simpleini)
|
||||
* [SQLite3](https://www.sqlite.org/index.html)
|
||||
|
||||
### TODO:
|
||||
@ -178,18 +177,24 @@ Used in profile popup:
|
||||
|
||||
### Settings
|
||||
Settings are configured (for now) by editing abaddon.ini
|
||||
The format is similar to the standard Windows ini format **except**:
|
||||
* `#` is used to begin comments as opposed to `;`
|
||||
* Section and key names are case-sensitive
|
||||
|
||||
You should edit these while the client is closed even though there's an option to reload while running
|
||||
This listing is organized by section.
|
||||
For example, memory_db would be set by adding `memory_db = true` under the line `[discord]`
|
||||
|
||||
#### discord
|
||||
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
|
||||
* api_base (string) - override base url for Discord API
|
||||
* memory_db (true or false, default false) - if true, Discord data will be kept in memory as opposed to on disk
|
||||
* token (string) - Discord token used to login, this can be set from the menu
|
||||
* prefetch (true or false, default false) - if true, new messages will cause the avatar and image attachments to be automatically downloaded
|
||||
|
||||
#### http
|
||||
* user_agent (string) - sets the user-agent to use in HTTP requests to the Discord API (not including media/images)
|
||||
* concurrent (int, default 10) - how many images can be concurrently retrieved
|
||||
* concurrent (int, default 20) - how many images can be concurrently retrieved
|
||||
|
||||
#### gui
|
||||
* member_list_discriminator (true or false, default true) - show user discriminators in the member list
|
||||
@ -199,8 +204,6 @@ For example, memory_db would be set by adding `memory_db = true` under the line
|
||||
* animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used
|
||||
* animated_guild_hover_only (true or false, default true) - only animate guild icons when the guild is being hovered over
|
||||
* owner_crown (true or false, default true) - show a crown next to the owner
|
||||
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
|
||||
* api_base (string) - override base url for Discord API
|
||||
|
||||
#### style
|
||||
* linkcolor (string) - color to use for links in messages
|
||||
|
@ -1,15 +0,0 @@
|
||||
set(simpleini_LIBRARY_NAME simpleini)
|
||||
|
||||
find_path(simpleini_INCLUDE_DIR
|
||||
NAMES SimpleIni.h
|
||||
HINTS /usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
PATH_SUFFIXES ${simpleini_LIBRARY_NAME})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(simpleini
|
||||
REQUIRED_VARS
|
||||
simpleini_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(simpleini_INCLUDE_DIR)
|
1507
discord/store.cpp
@ -1,172 +0,0 @@
|
||||
#pragma once
|
||||
#include "../util.hpp"
|
||||
#include "objects.hpp"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#ifdef GetMessage // fuck you windows.h
|
||||
#undef GetMessage
|
||||
#endif
|
||||
|
||||
class Store {
|
||||
public:
|
||||
Store(bool mem_store = false);
|
||||
~Store();
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
void SetUser(Snowflake id, const UserData &user);
|
||||
void SetChannel(Snowflake id, const ChannelData &chan);
|
||||
void SetGuild(Snowflake id, const GuildData &guild);
|
||||
void SetRole(Snowflake id, const RoleData &role);
|
||||
void SetMessage(Snowflake id, const Message &message);
|
||||
void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data);
|
||||
void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm);
|
||||
void SetEmoji(Snowflake id, const EmojiData &emoji);
|
||||
void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban);
|
||||
|
||||
// slap const on everything even tho its not *really* const
|
||||
|
||||
std::optional<ChannelData> GetChannel(Snowflake id) const;
|
||||
std::optional<EmojiData> GetEmoji(Snowflake id) const;
|
||||
std::optional<GuildData> GetGuild(Snowflake id) const;
|
||||
std::optional<GuildMember> GetGuildMember(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::optional<Message> GetMessage(Snowflake id) const;
|
||||
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
|
||||
std::optional<RoleData> GetRole(Snowflake id) const;
|
||||
std::optional<UserData> GetUser(Snowflake id) const;
|
||||
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::vector<BanData> GetBans(Snowflake guild_id) const;
|
||||
|
||||
std::vector<Message> GetLastMessages(Snowflake id, size_t num) const;
|
||||
std::vector<Snowflake> GetChannelMessageIDs(Snowflake id) const;
|
||||
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
|
||||
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const; // public
|
||||
|
||||
void ClearGuild(Snowflake id);
|
||||
void ClearChannel(Snowflake id);
|
||||
void ClearBan(Snowflake guild_id, Snowflake user_id);
|
||||
|
||||
using users_type = std::unordered_map<Snowflake, UserData>;
|
||||
using channels_type = std::unordered_map<Snowflake, ChannelData>;
|
||||
using guilds_type = std::unordered_map<Snowflake, GuildData>;
|
||||
using roles_type = std::unordered_map<Snowflake, RoleData>;
|
||||
using messages_type = std::unordered_map<Snowflake, Message>;
|
||||
using members_type = std::unordered_map<Snowflake, std::unordered_map<Snowflake, GuildMember>>; // [guild][user]
|
||||
using permission_overwrites_type = std::unordered_map<Snowflake, std::unordered_map<Snowflake, PermissionOverwrite>>; // [channel][user/role]
|
||||
using emojis_type = std::unordered_map<Snowflake, EmojiData>;
|
||||
|
||||
const std::unordered_set<Snowflake> &GetChannels() const;
|
||||
const std::unordered_set<Snowflake> &GetGuilds() const;
|
||||
|
||||
void ClearAll();
|
||||
|
||||
void BeginTransaction();
|
||||
void EndTransaction();
|
||||
|
||||
private:
|
||||
Message GetMessageBound(sqlite3_stmt *stmt) const;
|
||||
|
||||
void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction);
|
||||
|
||||
std::unordered_set<Snowflake> m_channels;
|
||||
std::unordered_set<Snowflake> m_guilds;
|
||||
|
||||
bool CreateTables();
|
||||
bool CreateStatements();
|
||||
void Cleanup();
|
||||
|
||||
template<typename T>
|
||||
void Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const;
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
Bind(sqlite3_stmt *stmt, int index, T val) const;
|
||||
|
||||
void Bind(sqlite3_stmt *stmt, int index, int num) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, uint64_t num) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, const std::string &str) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, bool val) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, std::nullptr_t) const;
|
||||
bool RunInsert(sqlite3_stmt *stmt);
|
||||
bool FetchOne(sqlite3_stmt *stmt) const;
|
||||
|
||||
template<typename T>
|
||||
void Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const;
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
Get(sqlite3_stmt *stmt, int index, T &out) const;
|
||||
|
||||
void Get(sqlite3_stmt *stmt, int index, int &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, uint64_t &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, std::string &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, bool &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, Snowflake &out) const;
|
||||
bool IsNull(sqlite3_stmt *stmt, int index) const;
|
||||
void Reset(sqlite3_stmt *stmt) const;
|
||||
|
||||
std::filesystem::path m_db_path;
|
||||
mutable sqlite3 *m_db;
|
||||
mutable int m_db_err;
|
||||
mutable sqlite3_stmt *m_set_user_stmt;
|
||||
mutable sqlite3_stmt *m_get_user_stmt;
|
||||
mutable sqlite3_stmt *m_set_perm_stmt;
|
||||
mutable sqlite3_stmt *m_get_perm_stmt;
|
||||
mutable sqlite3_stmt *m_set_msg_stmt;
|
||||
mutable sqlite3_stmt *m_get_msg_stmt;
|
||||
mutable sqlite3_stmt *m_set_role_stmt;
|
||||
mutable sqlite3_stmt *m_get_role_stmt;
|
||||
mutable sqlite3_stmt *m_set_emote_stmt;
|
||||
mutable sqlite3_stmt *m_get_emote_stmt;
|
||||
mutable sqlite3_stmt *m_set_member_stmt;
|
||||
mutable sqlite3_stmt *m_get_member_stmt;
|
||||
mutable sqlite3_stmt *m_set_guild_stmt;
|
||||
mutable sqlite3_stmt *m_get_guild_stmt;
|
||||
mutable sqlite3_stmt *m_set_chan_stmt;
|
||||
mutable sqlite3_stmt *m_get_chan_stmt;
|
||||
mutable sqlite3_stmt *m_set_ban_stmt;
|
||||
mutable sqlite3_stmt *m_get_ban_stmt;
|
||||
mutable sqlite3_stmt *m_clear_ban_stmt;
|
||||
mutable sqlite3_stmt *m_get_bans_stmt;
|
||||
mutable sqlite3_stmt *m_set_msg_interaction_stmt;
|
||||
mutable sqlite3_stmt *m_get_last_msgs_stmt;
|
||||
mutable sqlite3_stmt *m_get_msg_ids_stmt;
|
||||
mutable sqlite3_stmt *m_get_pins_stmt;
|
||||
mutable sqlite3_stmt *m_get_threads_stmt;
|
||||
mutable sqlite3_stmt *m_clear_chan_stmt;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline void Store::Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const {
|
||||
if (opt.has_value())
|
||||
Bind(stmt, index, *opt);
|
||||
else
|
||||
sqlite3_bind_null(stmt, index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
Store::Bind(sqlite3_stmt *stmt, int index, T val) const {
|
||||
Bind(stmt, index, static_cast<typename std::underlying_type<T>::type>(val));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void Store::Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const {
|
||||
if (sqlite3_column_type(stmt, index) == SQLITE_NULL)
|
||||
out = std::nullopt;
|
||||
else {
|
||||
T v;
|
||||
Get(stmt, index, v);
|
||||
out = std::optional<T>(v);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
Store::Get(sqlite3_stmt *stmt, int index, T &out) const {
|
||||
out = static_cast<T>(sqlite3_column_int(stmt, index));
|
||||
}
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
115
settings.cpp
@ -1,115 +0,0 @@
|
||||
#include "settings.hpp"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
SettingsManager::SettingsManager(std::string filename)
|
||||
: m_filename(filename) {
|
||||
if (!std::filesystem::exists(filename)) {
|
||||
std::fstream fs;
|
||||
fs.open(filename, std::ios::out);
|
||||
fs.close();
|
||||
}
|
||||
|
||||
auto rc = m_ini.LoadFile(filename.c_str());
|
||||
m_ok = rc == SI_OK;
|
||||
}
|
||||
|
||||
void SettingsManager::Reload() {
|
||||
m_ok = m_ini.LoadFile(m_filename.c_str()) == SI_OK;
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetSettingString(const std::string §ion, const std::string &key, std::string fallback) const {
|
||||
return m_ini.GetValue(section.c_str(), key.c_str(), fallback.c_str());
|
||||
}
|
||||
|
||||
int SettingsManager::GetSettingInt(const std::string §ion, const std::string &key, int fallback) const {
|
||||
return std::stoul(GetSettingString(section, key, std::to_string(fallback)));
|
||||
}
|
||||
|
||||
bool SettingsManager::GetSettingBool(const std::string §ion, const std::string &key, bool fallback) const {
|
||||
return GetSettingString(section, key, fallback ? "true" : "false") != "false";
|
||||
}
|
||||
|
||||
bool SettingsManager::IsValid() const {
|
||||
return m_ok;
|
||||
}
|
||||
|
||||
void SettingsManager::Close() {
|
||||
m_ini.SaveFile(m_filename.c_str());
|
||||
}
|
||||
|
||||
bool SettingsManager::GetUseMemoryDB() const {
|
||||
return GetSettingBool("discord", "memory_db", false);
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetUserAgent() const {
|
||||
return GetSettingString("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36");
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetDiscordToken() const {
|
||||
return GetSettingString("discord", "token");
|
||||
}
|
||||
|
||||
bool SettingsManager::GetShowMemberListDiscriminators() const {
|
||||
return GetSettingBool("gui", "member_list_discriminator", true);
|
||||
}
|
||||
|
||||
bool SettingsManager::GetShowStockEmojis() const {
|
||||
#ifdef _WIN32
|
||||
return GetSettingBool("gui", "stock_emojis", false);
|
||||
#else
|
||||
return GetSettingBool("gui", "stock_emojis", true);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SettingsManager::GetShowCustomEmojis() const {
|
||||
return GetSettingBool("gui", "custom_emojis", true);
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetLinkColor() const {
|
||||
return GetSettingString("style", "linkcolor", "rgba(40, 200, 180, 255)");
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetChannelsExpanderColor() const {
|
||||
return GetSettingString("style", "expandercolor", "rgba(255, 83, 112, 255)");
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetNSFWChannelColor() const {
|
||||
return GetSettingString("style", "nsfwchannelcolor", "#ed6666");
|
||||
}
|
||||
|
||||
int SettingsManager::GetCacheHTTPConcurrency() const {
|
||||
return GetSettingInt("http", "concurrent", 20);
|
||||
}
|
||||
|
||||
bool SettingsManager::GetPrefetch() const {
|
||||
return GetSettingBool("discord", "prefetch", false);
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetMainCSS() const {
|
||||
return GetSettingString("gui", "css", "main.css");
|
||||
}
|
||||
|
||||
bool SettingsManager::GetShowAnimations() const {
|
||||
return GetSettingBool("gui", "animations", true);
|
||||
}
|
||||
|
||||
bool SettingsManager::GetShowOwnerCrown() const {
|
||||
return GetSettingBool("gui", "owner_crown", true);
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetGatewayURL() const {
|
||||
return GetSettingString("discord", "gateway", "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream");
|
||||
}
|
||||
|
||||
std::string SettingsManager::GetAPIBaseURL() const {
|
||||
return GetSettingString("discord", "api_base", "https://discord.com/api/v9");
|
||||
}
|
||||
|
||||
bool SettingsManager::GetAnimatedGuildHoverOnly() const {
|
||||
return GetSettingBool("gui", "animated_guild_hover_only", true);
|
||||
}
|
||||
|
||||
bool SettingsManager::GetSaveState() const {
|
||||
return GetSettingBool("gui", "save_state", true);
|
||||
}
|
62
settings.hpp
@ -1,62 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <SimpleIni.h>
|
||||
|
||||
class SettingsManager {
|
||||
public:
|
||||
SettingsManager(std::string filename);
|
||||
void Reload();
|
||||
|
||||
void Close();
|
||||
bool GetUseMemoryDB() const;
|
||||
std::string GetUserAgent() const;
|
||||
std::string GetDiscordToken() const;
|
||||
bool GetShowMemberListDiscriminators() const;
|
||||
bool GetShowStockEmojis() const;
|
||||
bool GetShowCustomEmojis() const;
|
||||
int GetCacheHTTPConcurrency() const;
|
||||
bool GetPrefetch() const;
|
||||
std::string GetMainCSS() const;
|
||||
bool GetShowAnimations() const;
|
||||
bool GetShowOwnerCrown() const;
|
||||
std::string GetGatewayURL() const;
|
||||
std::string GetAPIBaseURL() const;
|
||||
bool GetAnimatedGuildHoverOnly() const;
|
||||
bool GetSaveState() const;
|
||||
|
||||
// i would like to use Gtk::StyleProperty for this, but it will not work on windows
|
||||
// #1 it's missing from the project files for the version used by vcpkg
|
||||
// #2 it's still broken and doesn't function even when added to the solution
|
||||
// #3 it's a massive pain in the ass to try and bump the version to a functioning version
|
||||
// because they switch build systems to nmake/meson (took months to get merged in vcpkg)
|
||||
// #4 c++ build systems sucks
|
||||
// three options are: use gtk4 with updated vcpkg, try and port it myself, or use msys2 instead of vcpkg
|
||||
// im leaning towards msys
|
||||
std::string GetLinkColor() const;
|
||||
std::string GetChannelsExpanderColor() const;
|
||||
std::string GetNSFWChannelColor() const;
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
template<typename T>
|
||||
void SetSetting(std::string section, std::string key, T value) {
|
||||
m_ini.SetValue(section.c_str(), key.c_str(), std::to_string(value).c_str());
|
||||
m_ini.SaveFile(m_filename.c_str());
|
||||
}
|
||||
|
||||
void SetSetting(std::string section, std::string key, std::string value) {
|
||||
m_ini.SetValue(section.c_str(), key.c_str(), value.c_str());
|
||||
m_ini.SaveFile(m_filename.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string GetSettingString(const std::string §ion, const std::string &key, std::string fallback = "") const;
|
||||
int GetSettingInt(const std::string §ion, const std::string &key, int fallback) const;
|
||||
bool GetSettingBool(const std::string §ion, const std::string &key, bool fallback) const;
|
||||
|
||||
private:
|
||||
bool m_ok;
|
||||
std::string m_filename;
|
||||
CSimpleIniA m_ini;
|
||||
};
|
@ -23,12 +23,12 @@
|
||||
|
||||
Abaddon::Abaddon()
|
||||
: m_settings(Platform::FindConfigFile())
|
||||
, m_discord(m_settings.GetUseMemoryDB()) // stupid but easy
|
||||
, m_discord(GetSettings().UseMemoryDB) // stupid but easy
|
||||
, m_emojis(GetResPath("/emojis.bin")) {
|
||||
LoadFromSettings();
|
||||
|
||||
// todo: set user agent for non-client(?)
|
||||
std::string ua = m_settings.GetUserAgent();
|
||||
std::string ua = GetSettings().UserAgent;
|
||||
m_discord.SetUserAgent(ua);
|
||||
|
||||
m_discord.signal_gateway_ready().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReady));
|
||||
@ -43,7 +43,7 @@ Abaddon::Abaddon()
|
||||
m_discord.signal_thread_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnThreadUpdate));
|
||||
m_discord.signal_message_sent().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageSent));
|
||||
m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect));
|
||||
if (m_settings.GetPrefetch())
|
||||
if (GetSettings().Prefetch)
|
||||
m_discord.signal_message_create().connect([this](const Message &message) {
|
||||
if (message.Author.HasAvatar())
|
||||
m_img_mgr.Prefetch(message.Author.GetAvatarURL());
|
||||
@ -54,10 +54,6 @@ Abaddon::Abaddon()
|
||||
});
|
||||
}
|
||||
|
||||
Abaddon::~Abaddon() {
|
||||
m_settings.Close();
|
||||
}
|
||||
|
||||
Abaddon &Abaddon::Get() {
|
||||
static Abaddon instance;
|
||||
return instance;
|
||||
@ -82,9 +78,30 @@ int Abaddon::StartGTK() {
|
||||
|
||||
m_main_window = std::make_unique<MainWindow>();
|
||||
m_main_window->set_title(APP_TITLE);
|
||||
m_main_window->UpdateComponents();
|
||||
m_main_window->set_position(Gtk::WIN_POS_CENTER);
|
||||
|
||||
if (!m_settings.IsValid()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be opened!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
if (!m_emojis.Load()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
if (!m_discord.IsStoreValid()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// store must be checked before this can be called
|
||||
m_main_window->UpdateComponents();
|
||||
|
||||
// crashes for some stupid reason if i put it somewhere else
|
||||
SetupUserMenu();
|
||||
|
||||
@ -112,33 +129,19 @@ int Abaddon::StartGTK() {
|
||||
|
||||
ActionReloadCSS();
|
||||
|
||||
m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::StopDiscord), false);
|
||||
|
||||
if (!m_settings.IsValid()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
if (!m_emojis.Load()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
if (!m_discord.IsStoreValid()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.set_position(Gtk::WIN_POS_CENTER);
|
||||
dlg.run();
|
||||
return 1;
|
||||
}
|
||||
m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::OnShutdown), false);
|
||||
|
||||
m_main_window->show();
|
||||
return m_gtk_app->run(*m_main_window);
|
||||
}
|
||||
|
||||
void Abaddon::OnShutdown() {
|
||||
StopDiscord();
|
||||
m_settings.Close();
|
||||
}
|
||||
|
||||
void Abaddon::LoadFromSettings() {
|
||||
std::string token = m_settings.GetDiscordToken();
|
||||
std::string token = GetSettings().DiscordToken;
|
||||
if (token.size()) {
|
||||
m_discord_token = token;
|
||||
m_discord.UpdateToken(m_discord_token);
|
||||
@ -246,8 +249,8 @@ void Abaddon::DiscordOnThreadUpdate(const ThreadUpdateData &data) {
|
||||
}
|
||||
}
|
||||
|
||||
const SettingsManager &Abaddon::GetSettings() const {
|
||||
return m_settings;
|
||||
SettingsManager::Settings &Abaddon::GetSettings() {
|
||||
return m_settings.GetSettings();
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gtk::CssProvider> Abaddon::GetStyleProvider() {
|
||||
@ -365,7 +368,7 @@ void Abaddon::SetupUserMenu() {
|
||||
}
|
||||
|
||||
void Abaddon::SaveState() {
|
||||
if (!m_settings.GetSaveState()) return;
|
||||
if (!GetSettings().SaveState) return;
|
||||
|
||||
AbaddonApplicationState state;
|
||||
state.ActiveChannel = m_main_window->GetChatActiveChannel();
|
||||
@ -385,7 +388,7 @@ void Abaddon::SaveState() {
|
||||
}
|
||||
|
||||
void Abaddon::LoadState() {
|
||||
if (!m_settings.GetSaveState()) return;
|
||||
if (!GetSettings().SaveState) return;
|
||||
|
||||
const auto data = ReadWholeFile(GetStateCachePath("/state.json"));
|
||||
if (data.empty()) return;
|
||||
@ -489,7 +492,7 @@ void Abaddon::ActionSetToken() {
|
||||
m_discord_token = dlg.GetToken();
|
||||
m_discord.UpdateToken(m_discord_token);
|
||||
m_main_window->UpdateComponents();
|
||||
m_settings.SetSetting("discord", "token", m_discord_token);
|
||||
GetSettings().DiscordToken = m_discord_token;
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,16 +555,9 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) {
|
||||
return;
|
||||
|
||||
Snowflake before_id = m_main_window->GetChatOldestListedMessage();
|
||||
auto knownset = m_discord.GetMessageIDsForChannel(id);
|
||||
std::vector<Snowflake> knownvec(knownset.begin(), knownset.end());
|
||||
std::sort(knownvec.begin(), knownvec.end());
|
||||
auto latest = std::find_if(knownvec.begin(), knownvec.end(), [&before_id](Snowflake x) -> bool { return x == before_id; });
|
||||
int distance = std::distance(knownvec.begin(), latest);
|
||||
auto msgs = m_discord.GetMessagesBefore(id, before_id);
|
||||
|
||||
if (distance >= 50) {
|
||||
std::vector<Message> msgs;
|
||||
for (auto it = knownvec.begin() + distance - 50; it != knownvec.begin() + distance; it++)
|
||||
msgs.push_back(*m_discord.GetMessage(*it));
|
||||
if (msgs.size() >= 50) {
|
||||
m_main_window->UpdateChatPrependHistory(msgs);
|
||||
return;
|
||||
}
|
||||
@ -703,7 +699,7 @@ bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) {
|
||||
void Abaddon::ActionReloadCSS() {
|
||||
try {
|
||||
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
|
||||
m_css_provider->load_from_path(GetCSSPath("/" + m_settings.GetMainCSS()));
|
||||
m_css_provider->load_from_path(GetCSSPath("/" + GetSettings().MainCSS));
|
||||
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider);
|
@ -14,7 +14,6 @@
|
||||
class Abaddon {
|
||||
private:
|
||||
Abaddon();
|
||||
~Abaddon();
|
||||
Abaddon(const Abaddon &) = delete;
|
||||
Abaddon &operator=(const Abaddon &) = delete;
|
||||
Abaddon(Abaddon &&) = delete;
|
||||
@ -24,6 +23,8 @@ public:
|
||||
static Abaddon &Get();
|
||||
|
||||
int StartGTK();
|
||||
void OnShutdown();
|
||||
|
||||
void StartDiscord();
|
||||
void StopDiscord();
|
||||
|
||||
@ -74,7 +75,7 @@ public:
|
||||
void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code);
|
||||
void DiscordOnThreadUpdate(const ThreadUpdateData &data);
|
||||
|
||||
const SettingsManager &GetSettings() const;
|
||||
SettingsManager::Settings &GetSettings();
|
||||
|
||||
Glib::RefPtr<Gtk::CssProvider> GetStyleProvider();
|
||||
|
@ -2,9 +2,9 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "../abaddon.hpp"
|
||||
#include "../imgmanager.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "imgmanager.hpp"
|
||||
#include "util.hpp"
|
||||
#include "statusindicator.hpp"
|
||||
|
||||
ChannelList::ChannelList()
|
||||
@ -263,11 +263,9 @@ void ChannelList::UpdateGuild(Snowflake id) {
|
||||
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
|
||||
if (!iter || !guild.has_value()) return;
|
||||
|
||||
static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
|
||||
|
||||
(*iter)[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild->Name) + "</b>";
|
||||
(*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
|
||||
if (show_animations && guild->HasAnimatedIcon()) {
|
||||
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;
|
||||
@ -436,9 +434,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
|
||||
guild_row[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild.Name) + "</b>";
|
||||
guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
|
||||
|
||||
static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
|
||||
|
||||
if (show_animations && guild.HasAnimatedIcon()) {
|
||||
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;
|
||||
@ -998,7 +994,7 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
|
||||
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
||||
|
||||
const static bool hover_only = Abaddon::Get().GetSettings().GetAnimatedGuildHoverOnly();
|
||||
const bool hover_only = Abaddon::Get().GetSettings().AnimatedGuildHoverOnly;
|
||||
const bool is_hovered = flags & Gtk::CELL_RENDERER_PRELIT;
|
||||
auto anim = m_property_pixbuf_animation.get_value();
|
||||
|
||||
@ -1069,7 +1065,7 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr<Cairo::Cont
|
||||
cr->move_to(x1, y1);
|
||||
cr->line_to(x2, y2);
|
||||
cr->line_to(x3, y3);
|
||||
static const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetChannelsExpanderColor());
|
||||
const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor);
|
||||
cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue());
|
||||
cr->stroke();
|
||||
|
||||
@ -1115,7 +1111,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
|
||||
|
||||
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
|
||||
|
||||
const static auto nsfw_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetNSFWChannelColor());
|
||||
const auto nsfw_color = Gdk::RGBA(Abaddon::Get().GetSettings().NSFWChannelColor);
|
||||
if (m_property_nsfw.get_value())
|
||||
m_renderer_text.property_foreground_rgba() = nsfw_color;
|
||||
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
|
@ -1,7 +1,7 @@
|
||||
#include <filesystem>
|
||||
#include "chatinputindicator.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
constexpr static const int MaxUsersInIndicator = 4;
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <gtkmm.h>
|
||||
#include <unordered_map>
|
||||
#include "../discord/message.hpp"
|
||||
#include "../discord/user.hpp"
|
||||
#include "discord/message.hpp"
|
||||
#include "discord/user.hpp"
|
||||
|
||||
class ChatInputIndicator : public Gtk::Box {
|
||||
public:
|
@ -1,6 +1,6 @@
|
||||
#include "chatmessage.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "util.hpp"
|
||||
#include "lazyimage.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
@ -231,11 +231,13 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
|
||||
}
|
||||
} break;
|
||||
case MessageType::RECIPIENT_ADD: {
|
||||
if (data->Mentions.size() == 0) break;
|
||||
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
|
||||
const auto &added = data->Mentions[0];
|
||||
b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->Username + "</span> added <span color='#eeeeee'>" + added.Username + "</span></span></i>");
|
||||
} break;
|
||||
case MessageType::RECIPIENT_REMOVE: {
|
||||
if (data->Mentions.size() == 0) break;
|
||||
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
|
||||
const auto &added = data->Mentions[0];
|
||||
if (adder->ID == added.ID)
|
||||
@ -355,7 +357,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const EmbedData &emb
|
||||
}
|
||||
return false;
|
||||
});
|
||||
static auto color = Abaddon::Get().GetSettings().GetLinkColor();
|
||||
static auto color = Abaddon::Get().GetSettings().LinkColor;
|
||||
title_label->override_color(Gdk::RGBA(color));
|
||||
title_label->set_markup("<b>" + Glib::Markup::escape_text(*embed.Title) + "</b>");
|
||||
}
|
||||
@ -653,6 +655,14 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
|
||||
return author->GetEscapedBoldString<false>();
|
||||
};
|
||||
|
||||
// if the message wasnt fetched from store it might have an un-fetched reference
|
||||
std::optional<std::shared_ptr<Message>> referenced_message = data.ReferencedMessage;
|
||||
if (data.MessageReference.has_value() && data.MessageReference->MessageID.has_value() && !referenced_message.has_value()) {
|
||||
auto refd = discord.GetMessage(*data.MessageReference->MessageID);
|
||||
if (refd.has_value())
|
||||
referenced_message = std::make_shared<Message>(std::move(*refd));
|
||||
}
|
||||
|
||||
if (data.Interaction.has_value()) {
|
||||
const auto user = *discord.GetUser(data.Interaction->User.ID);
|
||||
|
||||
@ -664,16 +674,16 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
|
||||
} else {
|
||||
lbl->set_markup(user.GetEscapedBoldString<false>());
|
||||
}
|
||||
} else if (data.ReferencedMessage.has_value()) {
|
||||
if (data.ReferencedMessage.value().get() == nullptr) {
|
||||
} else if (referenced_message.has_value()) {
|
||||
if (referenced_message.value() == nullptr) {
|
||||
lbl->set_markup("<i>deleted message</i>");
|
||||
} else {
|
||||
const auto &referenced = *data.ReferencedMessage.value().get();
|
||||
const auto &referenced = *referenced_message.value();
|
||||
Glib::ustring text;
|
||||
if (referenced.Content == "") {
|
||||
if (referenced.Attachments.size() > 0) {
|
||||
if (referenced.Content.empty()) {
|
||||
if (!referenced.Attachments.empty()) {
|
||||
text = "<i>attachment</i>";
|
||||
} else if (referenced.Embeds.size() > 0) {
|
||||
} else if (!referenced.Embeds.empty()) {
|
||||
text = "<i>embed</i>";
|
||||
}
|
||||
} else {
|
||||
@ -788,7 +798,6 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
|
||||
int mstart, mend;
|
||||
if (!match.fetch_pos(0, mstart, mend)) break;
|
||||
const bool is_animated = match.fetch(0)[1] == 'a';
|
||||
const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
|
||||
|
||||
const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart);
|
||||
const auto chars_end = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mend);
|
||||
@ -796,7 +805,7 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
|
||||
auto end_it = buf->get_iter_at_offset(chars_end);
|
||||
|
||||
startpos = mend;
|
||||
if (is_animated && show_animations) {
|
||||
if (is_animated && Abaddon::Get().GetSettings().ShowAnimations) {
|
||||
const auto mark_start = buf->create_mark(start_it, false);
|
||||
end_it.backward_char();
|
||||
const auto mark_end = buf->create_mark(end_it, false);
|
||||
@ -825,7 +834,9 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
|
||||
buf->delete_mark(mark_start);
|
||||
buf->delete_mark(mark_end);
|
||||
auto it = buf->erase(start_it, end_it);
|
||||
buf->insert_pixbuf(it, pixbuf->scale_simple(EmojiSize, EmojiSize, Gdk::INTERP_BILINEAR));
|
||||
int width, height;
|
||||
GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), width, height, EmojiSize, EmojiSize);
|
||||
buf->insert_pixbuf(it, pixbuf->scale_simple(width, height, Gdk::INTERP_BILINEAR));
|
||||
};
|
||||
img.LoadFromURL(EmojiData::URLFromID(match.fetch(2)), sigc::track_obj(cb, tv));
|
||||
}
|
||||
@ -835,11 +846,8 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
|
||||
}
|
||||
|
||||
void ChatMessageItemContainer::HandleEmojis(Gtk::TextView &tv) {
|
||||
static const bool stock_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis();
|
||||
static const bool custom_emojis = Abaddon::Get().GetSettings().GetShowCustomEmojis();
|
||||
|
||||
if (stock_emojis) HandleStockEmojis(tv);
|
||||
if (custom_emojis) HandleCustomEmojis(tv);
|
||||
if (Abaddon::Get().GetSettings().ShowStockEmojis) HandleStockEmojis(tv);
|
||||
if (Abaddon::Get().GetSettings().ShowCustomEmojis) HandleCustomEmojis(tv);
|
||||
}
|
||||
|
||||
void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) {
|
||||
@ -959,9 +967,6 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) {
|
||||
auto buf = tv.get_buffer();
|
||||
Glib::ustring text = GetText(buf);
|
||||
|
||||
// i'd like to let this be done thru css like .message-link { color: #bitch; } but idk how
|
||||
static auto link_color = Abaddon::Get().GetSettings().GetLinkColor();
|
||||
|
||||
int startpos = 0;
|
||||
Glib::MatchInfo match;
|
||||
while (rgx->match(text, startpos, match)) {
|
||||
@ -970,7 +975,7 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) {
|
||||
std::string link = match.fetch(0);
|
||||
auto tag = buf->create_tag();
|
||||
m_link_tagmap[tag] = link;
|
||||
tag->property_foreground_rgba() = Gdk::RGBA(link_color);
|
||||
tag->property_foreground_rgba() = Gdk::RGBA(Abaddon::Get().GetSettings().LinkColor);
|
||||
tag->set_property("underline", 1); // stupid workaround for vcpkg bug (i think)
|
||||
|
||||
const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart);
|
||||
@ -1128,7 +1133,7 @@ ChatMessageHeader::ChatMessageHeader(const Message &data)
|
||||
m_content_box_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
|
||||
m_meta_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
|
||||
m_avatar_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
|
||||
if (Abaddon::Get().GetSettings().GetShowAnimations()) {
|
||||
if (Abaddon::Get().GetSettings().ShowAnimations) {
|
||||
m_content_box_ev.signal_enter_notify_event().connect(on_enter_cb);
|
||||
m_content_box_ev.signal_leave_notify_event().connect(on_leave_cb);
|
||||
m_meta_ev.signal_enter_notify_event().connect(on_enter_cb);
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <gtkmm.h>
|
||||
#include "../discord/discord.hpp"
|
||||
#include "discord/discord.hpp"
|
||||
|
||||
class ChatMessageItemContainer : public Gtk::Box {
|
||||
public:
|
@ -1,6 +1,6 @@
|
||||
#include "chatwindow.hpp"
|
||||
#include "chatmessage.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "chatinputindicator.hpp"
|
||||
#include "ratelimitindicator.hpp"
|
||||
#include "chatinput.hpp"
|
@ -2,7 +2,7 @@
|
||||
#include <gtkmm.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include "../discord/discord.hpp"
|
||||
#include "discord/discord.hpp"
|
||||
#include "completer.hpp"
|
||||
|
||||
class ChatMessageHeader;
|
@ -1,7 +1,7 @@
|
||||
#include <unordered_set>
|
||||
#include "completer.hpp"
|
||||
#include "../abaddon.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "abaddon.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
constexpr const int CompleterHeight = 150;
|
||||
constexpr const int MaxCompleterEntries = 30;
|
@ -2,7 +2,7 @@
|
||||
#include <gtkmm.h>
|
||||
#include <functional>
|
||||
#include "lazyimage.hpp"
|
||||
#include "../discord/snowflake.hpp"
|
||||
#include "discord/snowflake.hpp"
|
||||
|
||||
constexpr static int CompleterImageSize = 24;
|
||||
|