forked from OpenGamers/abaddon
Merge branch 'resources' (closes #29)
This commit is contained in:
@@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
|
|
||||||
project(abaddon)
|
project(abaddon)
|
||||||
|
|
||||||
|
set(ABADDON_RESOURCE_DIR "/usr/share/abaddon" CACHE PATH "Fallback directory for resources on Linux")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
||||||
|
|
||||||
@@ -35,6 +37,8 @@ if(WIN32)
|
|||||||
link_libraries(${Fontconfig_LIBRARIES})
|
link_libraries(${Fontconfig_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
file(GLOB ABADDON_SOURCES
|
file(GLOB ABADDON_SOURCES
|
||||||
"*.h"
|
"*.h"
|
||||||
"*.hpp"
|
"*.hpp"
|
||||||
@@ -54,6 +58,7 @@ file(GLOB ABADDON_SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_executable(abaddon ${ABADDON_SOURCES})
|
add_executable(abaddon ${ABADDON_SOURCES})
|
||||||
|
target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR})
|
||||||
target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS})
|
target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS})
|
||||||
target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS})
|
target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS})
|
||||||
target_include_directories(abaddon PUBLIC ${SQLite3_INCLUDE_DIRS})
|
target_include_directories(abaddon PUBLIC ${SQLite3_INCLUDE_DIRS})
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -54,7 +54,11 @@ Or, do steps 1 and 2, and open CMakeLists.txt in Visual Studio if `vcpkg integra
|
|||||||
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned, unpackaged, requires gtkmm3 (e.g. from homebrew)
|
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned, unpackaged, requires gtkmm3 (e.g. from homebrew)
|
||||||
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now), requires gtkmm3. built on Ubuntu 18.04 + gcc9
|
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now), requires gtkmm3. built on Ubuntu 18.04 + gcc9
|
||||||
|
|
||||||
⚠️ Make sure you start from the directory where `css` and `res` are or else stuff will be broken
|
⚠️ If you use Windows, make sure to start from the directory containing `css` and `res`
|
||||||
|
|
||||||
|
If you don't use Windows, `css` and `res` can be loaded from `/usr/share/abaddon`
|
||||||
|
|
||||||
|
`abaddon.ini` will also be automatically used if located at `~/.config/abaddon/abaddon.ini` and there is no `abaddon.ini` in the working directory
|
||||||
|
|
||||||
#### Dependencies:
|
#### Dependencies:
|
||||||
* [gtkmm](https://www.gtkmm.org/en/)
|
* [gtkmm](https://www.gtkmm.org/en/)
|
||||||
@@ -196,3 +200,8 @@ For example, memory_db would be set by adding `memory_db = true` under the line
|
|||||||
|
|
||||||
#### misc
|
#### misc
|
||||||
* linkcolor (string) - color to use for links in messages
|
* linkcolor (string) - color to use for links in messages
|
||||||
|
|
||||||
|
### Environment variables
|
||||||
|
|
||||||
|
* ABADDON_NO_FC (Windows only) - don't use custom font config
|
||||||
|
* ABADDON_CONFIG - change path of configuration file to use. relative to cwd or can be absolute
|
||||||
|
|||||||
26
abaddon.cpp
26
abaddon.cpp
@@ -21,8 +21,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Abaddon::Abaddon()
|
Abaddon::Abaddon()
|
||||||
: m_settings("abaddon.ini")
|
: m_settings(Platform::FindConfigFile())
|
||||||
, m_emojis("res/emojis.bin")
|
, m_emojis(GetResPath("/emojis.bin"))
|
||||||
, m_discord(m_settings.GetUseMemoryDB()) { // stupid but easy
|
, m_discord(m_settings.GetUseMemoryDB()) { // stupid but easy
|
||||||
LoadFromSettings();
|
LoadFromSettings();
|
||||||
|
|
||||||
@@ -425,6 +425,24 @@ void Abaddon::on_user_menu_remove_recipient() {
|
|||||||
m_discord.RemoveGroupDMRecipient(m_main_window->GetChatActiveChannel(), m_shown_user_menu_id);
|
m_discord.RemoveGroupDMRecipient(m_main_window->GetChatActiveChannel(), m_shown_user_menu_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Abaddon::GetCSSPath() {
|
||||||
|
const static auto path = Platform::FindResourceFolder() + "/css";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Abaddon::GetResPath() {
|
||||||
|
const static auto path = Platform::FindResourceFolder() + "/res";
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Abaddon::GetCSSPath(const std::string &path) {
|
||||||
|
return GetCSSPath() + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Abaddon::GetResPath(const std::string &path) {
|
||||||
|
return GetResPath() + path;
|
||||||
|
}
|
||||||
|
|
||||||
void Abaddon::ActionConnect() {
|
void Abaddon::ActionConnect() {
|
||||||
if (!m_discord.IsStarted())
|
if (!m_discord.IsStarted())
|
||||||
StartDiscord();
|
StartDiscord();
|
||||||
@@ -637,11 +655,11 @@ bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) {
|
|||||||
void Abaddon::ActionReloadCSS() {
|
void Abaddon::ActionReloadCSS() {
|
||||||
try {
|
try {
|
||||||
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
|
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
|
||||||
m_css_provider->load_from_path(m_settings.GetMainCSS());
|
m_css_provider->load_from_path(GetCSSPath("/" + m_settings.GetMainCSS()));
|
||||||
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
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);
|
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider);
|
||||||
m_css_low_provider->load_from_path("./css/application-low-priority.css");
|
m_css_low_provider->load_from_path(GetCSSPath("/application-low-priority.css"));
|
||||||
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||||
} catch (Glib::Error &e) {
|
} catch (Glib::Error &e) {
|
||||||
Gtk::MessageDialog dlg(*m_main_window, "css failed to load (" + e.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
Gtk::MessageDialog dlg(*m_main_window, "css failed to load (" + e.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ public:
|
|||||||
|
|
||||||
void ManageHeapWindow(Gtk::Window *window);
|
void ManageHeapWindow(Gtk::Window *window);
|
||||||
|
|
||||||
|
static std::string GetCSSPath();
|
||||||
|
static std::string GetResPath();
|
||||||
|
static std::string GetCSSPath(const std::string &path);
|
||||||
|
static std::string GetResPath(const std::string &path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ShowGuildVerificationGateDialog(Snowflake guild_id);
|
void ShowGuildVerificationGateDialog(Snowflake guild_id);
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ ChatInputIndicator::ChatInputIndicator()
|
|||||||
m_label.show();
|
m_label.show();
|
||||||
|
|
||||||
// try loading gif
|
// try loading gif
|
||||||
if (!std::filesystem::exists("./res/typing_indicator.gif")) return;
|
const static auto path = Abaddon::GetResPath("/typing_indicator.gif");
|
||||||
auto gif_data = ReadWholeFile("./res/typing_indicator.gif");
|
if (!std::filesystem::exists(path)) return;
|
||||||
|
auto gif_data = ReadWholeFile(path);
|
||||||
auto loader = Gdk::PixbufLoader::create();
|
auto loader = Gdk::PixbufLoader::create();
|
||||||
loader->signal_size_prepared().connect([&](int inw, int inh) {
|
loader->signal_size_prepared().connect([&](int inw, int inh) {
|
||||||
int w, h;
|
int w, h;
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ MemberListUserRow::MemberListUserRow(const std::optional<GuildData> &guild, cons
|
|||||||
static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown();
|
static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown();
|
||||||
if (crown && guild.has_value() && guild->OwnerID == data.ID) {
|
if (crown && guild.has_value() && guild->OwnerID == data.ID) {
|
||||||
try {
|
try {
|
||||||
auto pixbuf = Gdk::Pixbuf::create_from_file("./res/crown.png", 12, 12);
|
const static auto crown_path = Abaddon::GetResPath("/crown.png");
|
||||||
|
auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12);
|
||||||
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
||||||
m_crown->set_valign(Gtk::ALIGN_CENTER);
|
m_crown->set_valign(Gtk::ALIGN_CENTER);
|
||||||
m_crown->set_margin_end(8);
|
m_crown->set_margin_end(8);
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ RateLimitIndicator::RateLimitIndicator()
|
|||||||
add(m_img);
|
add(m_img);
|
||||||
m_label.show();
|
m_label.show();
|
||||||
|
|
||||||
if (std::filesystem::exists("./res/clock.png")) {
|
const static auto clock_path = Abaddon::GetResPath("/clock.png");
|
||||||
|
if (std::filesystem::exists(clock_path)) {
|
||||||
try {
|
try {
|
||||||
const auto pixbuf = Gdk::Pixbuf::create_from_file("./res/clock.png");
|
const auto pixbuf = Gdk::Pixbuf::create_from_file(clock_path);
|
||||||
int w, h;
|
int w, h;
|
||||||
GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), w, h, 20, 10);
|
GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), w, h, 20, 10);
|
||||||
m_img.property_pixbuf() = pixbuf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
|
m_img.property_pixbuf() = pixbuf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
|
||||||
|
|||||||
1
config.h.in
Normal file
1
config.h.in
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#define ABADDON_DEFAULT_RESOURCE_DIR "@ABADDON_RESOURCE_DIR@"
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "imgmanager.hpp"
|
#include "imgmanager.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
#include "abaddon.hpp"
|
||||||
|
|
||||||
ImageManager::ImageManager() {
|
ImageManager::ImageManager() {
|
||||||
m_cb_dispatcher.connect(sigc::mem_fun(*this, &ImageManager::RunCallbacks));
|
m_cb_dispatcher.connect(sigc::mem_fun(*this, &ImageManager::RunCallbacks));
|
||||||
@@ -112,7 +113,7 @@ Glib::RefPtr<Gdk::Pixbuf> ImageManager::GetPlaceholder(int size) {
|
|||||||
return m_pixs.at(name);
|
return m_pixs.at(name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto buf = Gdk::Pixbuf::create_from_file("res/decamarks.png", size, size);
|
auto buf = Gdk::Pixbuf::create_from_file(Abaddon::Get().GetResPath() + "/decamarks.png", size, size);
|
||||||
m_pixs[name] = buf;
|
m_pixs[name] = buf;
|
||||||
return buf;
|
return buf;
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
|
|||||||
79
platform.cpp
79
platform.cpp
@@ -1,6 +1,24 @@
|
|||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
|
bool IsFolder(std::string_view path) {
|
||||||
|
std::error_code ec;
|
||||||
|
const auto status = std::filesystem::status(path, ec);
|
||||||
|
if (ec) return false;
|
||||||
|
return status.type() == std::filesystem::file_type::directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFile(std::string_view path) {
|
||||||
|
std::error_code ec;
|
||||||
|
const auto status = std::filesystem::status(path, ec);
|
||||||
|
if (ec) return false;
|
||||||
|
return status.type() == std::filesystem::file_type::regular;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(_MSC_VER)
|
#if defined(_WIN32) && defined(_MSC_VER)
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -59,3 +77,64 @@ bool Platform::SetupFonts() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
std::string Platform::FindResourceFolder() {
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Platform::FindConfigFile() {
|
||||||
|
const auto x = std::getenv("ABADDON_CONFIG");
|
||||||
|
if (x != nullptr)
|
||||||
|
return x;
|
||||||
|
return "./abaddon.ini";
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
std::string Platform::FindResourceFolder() {
|
||||||
|
static std::string found_path;
|
||||||
|
static bool found = false;
|
||||||
|
if (found) return found_path;
|
||||||
|
|
||||||
|
const static std::string home_path = std::getenv("HOME") + "/.local/share/abaddon"s;
|
||||||
|
|
||||||
|
for (const auto &path : { "."s, home_path, std::string(ABADDON_DEFAULT_RESOURCE_DIR) }) {
|
||||||
|
if (IsFolder(path + "/res") && IsFolder(path + "/css")) {
|
||||||
|
found_path = path;
|
||||||
|
found = true;
|
||||||
|
return found_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("cant find a resources folder, will try to load from cwd");
|
||||||
|
found_path = ".";
|
||||||
|
found = true;
|
||||||
|
return found_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Platform::FindConfigFile() {
|
||||||
|
const auto x = std::getenv("ABADDON_CONFIG");
|
||||||
|
if (x != nullptr)
|
||||||
|
return x;
|
||||||
|
|
||||||
|
const auto home_path = std::string(std::getenv("HOME")) + "/.config/abaddon/abaddon.ini";
|
||||||
|
for (const auto path : { "./abaddon.ini"s, home_path }) {
|
||||||
|
if (IsFile(path)) return path;
|
||||||
|
}
|
||||||
|
puts("can't find configuration file!");
|
||||||
|
return "./abaddon.ini";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::string Platform::FindResourceFolder() {
|
||||||
|
puts("unknown OS, trying to load resources from cwd");
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Platform::FindConfigFile() {
|
||||||
|
const auto x = std::getenv("ABADDON_CONFIG");
|
||||||
|
if (x != nullptr)
|
||||||
|
return x;
|
||||||
|
puts("unknown OS, trying to load config from cwd");
|
||||||
|
return "./abaddon.ini";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
bool SetupFonts();
|
bool SetupFonts();
|
||||||
|
std::string FindResourceFolder();
|
||||||
|
std::string FindConfigFile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ bool SettingsManager::GetPrefetch() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string SettingsManager::GetMainCSS() const {
|
std::string SettingsManager::GetMainCSS() const {
|
||||||
return GetSettingString("gui", "css", "./css/main.css");
|
return GetSettingString("gui", "css", "main.css");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SettingsManager::GetShowAnimations() const {
|
bool SettingsManager::GetShowAnimations() const {
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ GuildSettingsMembersListItem::GuildSettingsMembersListItem(const GuildData &guil
|
|||||||
static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown();
|
static bool crown = Abaddon::Get().GetSettings().GetShowOwnerCrown();
|
||||||
if (crown && guild.OwnerID == member.User->ID) {
|
if (crown && guild.OwnerID == member.User->ID) {
|
||||||
try {
|
try {
|
||||||
auto pixbuf = Gdk::Pixbuf::create_from_file("./res/crown.png", 12, 12);
|
const static auto crown_path = Abaddon::GetResPath("/crown.png");
|
||||||
|
auto pixbuf = Gdk::Pixbuf::create_from_file(crown_path, 12, 12);
|
||||||
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
m_crown = Gtk::manage(new Gtk::Image(pixbuf));
|
||||||
m_crown->set_valign(Gtk::ALIGN_CENTER);
|
m_crown->set_valign(Gtk::ALIGN_CENTER);
|
||||||
m_crown->set_margin_start(10);
|
m_crown->set_margin_start(10);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ ConnectionItem::ConnectionItem(const ConnectionData &conn)
|
|||||||
, m_box(Gtk::ORIENTATION_HORIZONTAL) {
|
, m_box(Gtk::ORIENTATION_HORIZONTAL) {
|
||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
try {
|
try {
|
||||||
pixbuf = Gdk::Pixbuf::create_from_file("./res/" + conn.Type + ".png", 32, 32);
|
pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + conn.Type + ".png"), 32, 32);
|
||||||
} catch (const Glib::Exception &e) {}
|
} catch (const Glib::Exception &e) {}
|
||||||
std::string url;
|
std::string url;
|
||||||
if (conn.Type == "github")
|
if (conn.Type == "github")
|
||||||
@@ -53,7 +53,8 @@ ConnectionItem::ConnectionItem(const ConnectionData &conn)
|
|||||||
m_overlay.add(m_box);
|
m_overlay.add(m_box);
|
||||||
if (conn.IsVerified) {
|
if (conn.IsVerified) {
|
||||||
try {
|
try {
|
||||||
static auto pb = Gdk::Pixbuf::create_from_file("./res/checkmark.png", 24, 24);
|
const static auto checkmarks_path = Abaddon::GetResPath("/checkmark.png");
|
||||||
|
static auto pb = Gdk::Pixbuf::create_from_file(checkmarks_path, 24, 24);
|
||||||
m_check = Gtk::manage(new Gtk::Image(pb));
|
m_check = Gtk::manage(new Gtk::Image(pb));
|
||||||
m_check->get_style_context()->add_class("profile-connection-check");
|
m_check->get_style_context()->add_class("profile-connection-check");
|
||||||
m_check->set_margin_end(25);
|
m_check->set_margin_end(25);
|
||||||
|
|||||||
@@ -111,9 +111,9 @@ void ProfileWindow::OnFetchProfile(const UserProfileData &data) {
|
|||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
try {
|
try {
|
||||||
if (name == "verifiedbot")
|
if (name == "verifiedbot")
|
||||||
pixbuf = Gdk::Pixbuf::create_from_file("./res/checkmark.png", 24, 24);
|
pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/checkmark.png"), 24, 24);
|
||||||
else
|
else
|
||||||
pixbuf = Gdk::Pixbuf::create_from_file("./res/" + name + ".png", 24, 24);
|
pixbuf = Gdk::Pixbuf::create_from_file(Abaddon::GetResPath("/" + name + ".png"), 24, 24);
|
||||||
} catch (const Glib::Exception &e) {
|
} catch (const Glib::Exception &e) {
|
||||||
pixbuf = Abaddon::Get().GetImageManager().GetPlaceholder(24);
|
pixbuf = Abaddon::Get().GetImageManager().GetPlaceholder(24);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user