mirror of
https://github.com/RetroShare/RetroShare.git
synced 2026-09-12 19:50:17 +05:00
Merge pull request #3122 from samuel-asleep/master-clean
Implement deterministic colored default logos for Friend Nodes, Chann…
This commit is contained in:
commit
b681603cb0
@ -24,6 +24,7 @@
|
||||
#include "PostedListWidgetWithModel.h"
|
||||
#include "PostedUserNotify.h"
|
||||
#include "gui/gxs/GxsGroupShareKey.h"
|
||||
#include "gui/gxs/GxsIdDetails.h"
|
||||
#include "gui/settings/rsharesettings.h"
|
||||
#include "gui/common/GroupTreeWidget.h"
|
||||
#include "util/misc.h"
|
||||
@ -236,7 +237,7 @@ void PostedDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupDa
|
||||
groupItemInfo.icon = image;
|
||||
}
|
||||
else
|
||||
groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/postedlinks.png");
|
||||
groupItemInfo.icon = GxsIdDetails::makeDefaultGroupIcon(postedGroupData->mMeta.mGroupId, ":icons/board.png", GxsIdDetails::ORIGINAL);
|
||||
|
||||
groupItemInfo.description = QString::fromUtf8(postedGroupData->mDescription.c_str());
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "gui/WikiPoos/WikiEditDialog.h"
|
||||
#include "gui/settings/rsharesettings.h"
|
||||
#include "gui/gxs/WikiGroupDialog.h"
|
||||
#include "gui/gxs/GxsIdDetails.h"
|
||||
#include "util/DateTime.h"
|
||||
|
||||
#include <retroshare/rswiki.h>
|
||||
@ -76,7 +77,7 @@
|
||||
#define IMAGE_NEWFORUM ":/images/new_forum16.png"
|
||||
#define IMAGE_FORUMAUTHD ":/images/konv_message2.png"
|
||||
#define IMAGE_COPYLINK ":/images/copyrslink.png"
|
||||
#define IMAGE_WIKI ":/icons/png/wiki.png"
|
||||
#define IMAGE_WIKI ":icons/wiki.png"
|
||||
#define IMAGE_EDIT ":/icons/png/pencil-edit-button.png"
|
||||
|
||||
|
||||
@ -730,7 +731,7 @@ void WikiDialog::GroupMetaDataToGroupItemInfo(const RsGroupMetaData &groupInfo,
|
||||
groupItemInfo.lastpost = DateTime::DateTimeFromTime_t(groupInfo.mLastPost);
|
||||
groupItemInfo.subscribeFlags = groupInfo.mSubscribeFlags;
|
||||
|
||||
groupItemInfo.icon = QIcon(IMAGE_WIKI);
|
||||
groupItemInfo.icon = GxsIdDetails::makeDefaultGroupIcon(groupInfo.mGroupId, IMAGE_WIKI, GxsIdDetails::ORIGINAL);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ bool AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, cons
|
||||
rsChats->getAvatarData(RsPeerId(sslId), data, size);
|
||||
if (size == 0) {
|
||||
if (!defaultImage.isEmpty()) {
|
||||
avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage);
|
||||
avatar = GxsIdDetails::makeDefaultGroupIconFromString(QString::fromStdString(sslId.toStdString()), ":icons/person.png", GxsIdDetails::LARGE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -109,13 +109,15 @@ bool AvatarDefs::getAvatarFromGpgId(const RsPgpId& gpgId, QPixmap &avatar, const
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
avatar = FilesDefs::getPixmapFromQtResourcePath(defaultImage);
|
||||
return false;
|
||||
}
|
||||
if (size == 0) {
|
||||
if (!defaultImage.isEmpty()) {
|
||||
avatar = GxsIdDetails::makeDefaultGroupIconFromString(QString::fromStdString(gpgId.toStdString()), ":icons/person.png", GxsIdDetails::LARGE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* load image */
|
||||
GxsIdDetails::loadPixmapFromData(data, size, avatar);
|
||||
GxsIdDetails::loadPixmapFromData(data, size, avatar, GxsIdDetails::LARGE);
|
||||
|
||||
free(data);
|
||||
|
||||
|
||||
@ -1027,7 +1027,19 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons
|
||||
}
|
||||
|
||||
if (!foundAvatar || sslAvatar.isNull()) {
|
||||
sslAvatar = FilesDefs::getPixmapFromQtResourcePath(AVATAR_DEFAULT_IMAGE);
|
||||
if (hn && !hn->child_node_indices.empty()) {
|
||||
// Use first child node if available
|
||||
std::string sslIdStr = mLocations[hn->child_node_indices[0]].node_info.id.toStdString();
|
||||
sslAvatar = GxsIdDetails::makeDefaultGroupIconFromString(QString::fromStdString(sslIdStr), ":icons/person.png", GxsIdDetails::LARGE);
|
||||
}
|
||||
else if (bestNodeInformation != nullptr) {
|
||||
// Fallback: Use the bestNodeInformation ID if no children exist
|
||||
std::string bestSslIdStr = bestNodeInformation->node_info.id.toStdString();
|
||||
sslAvatar = GxsIdDetails::makeDefaultGroupIconFromString(QString::fromStdString(bestSslIdStr), ":icons/person.png", GxsIdDetails::LARGE);
|
||||
}
|
||||
else {
|
||||
sslAvatar = FilesDefs::getPixmapFromQtResourcePath(AVATAR_DEFAULT_IMAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mDisplayStatusIcon) {
|
||||
@ -1489,4 +1501,3 @@ bool RsFriendListModel::isProfileExpanded(const EntryIndex& e) const
|
||||
|
||||
return mExpandedProfiles.find(s) != mExpandedProfiles.end();
|
||||
}
|
||||
|
||||
|
||||
@ -71,15 +71,81 @@
|
||||
//const int kRecognTagType_Dev_Patcher = 4;
|
||||
//const int kRecognTagType_Dev_Developer = 5;
|
||||
|
||||
#define ICON_CACHE_STORAGE_TIME 240
|
||||
#define DELAY_BETWEEN_ICON_CACHE_CLEANING 120
|
||||
#define NUM_AVATAR_SIZES 4 // Number of AvatarSize enum values (SMALL, MEDIUM, LARGE, ORIGINAL)
|
||||
|
||||
uint32_t GxsIdDetails::mImagesAllocated = 0;
|
||||
time_t GxsIdDetails::mLastIconCacheCleaning = time(NULL);
|
||||
std::map<RsGxsId,std::pair<time_t,QPixmap>[4] > GxsIdDetails::mDefaultIconCache ;
|
||||
std::map<RsGxsId,std::pair<time_t,QPixmap>[NUM_AVATAR_SIZES] > GxsIdDetails::mDefaultIconCache ;
|
||||
std::map<std::string,std::pair<time_t,QPixmap>[NUM_AVATAR_SIZES] > GxsIdDetails::mDefaultGroupIconCache ;
|
||||
|
||||
QMutex GxsIdDetails::mMutex;
|
||||
QMutex GxsIdDetails::mIconCacheMutex;
|
||||
|
||||
#define ICON_CACHE_STORAGE_TIME 240
|
||||
#define DELAY_BETWEEN_ICON_CACHE_CLEANING 120
|
||||
static std::string groupIconCacheKey(const RsGxsGroupId& id, const QString& iconPath)
|
||||
{
|
||||
std::string key = id.toStdString();
|
||||
key.append("|");
|
||||
key.append(iconPath.toStdString());
|
||||
return key;
|
||||
}
|
||||
|
||||
static std::string groupIconCacheKeyFromString(const QString& idStr, const QString& iconPath)
|
||||
{
|
||||
std::string key = idStr.toStdString();
|
||||
key.append("|");
|
||||
key.append(iconPath.toStdString());
|
||||
return key;
|
||||
}
|
||||
|
||||
template<typename CacheMap>
|
||||
static void cleanupIconCache(CacheMap& cache, time_t now, int& nb_deleted, uint32_t& size_deleted, uint32_t& total_size, const char* label)
|
||||
{
|
||||
for(auto it(cache.begin());it!=cache.end();)
|
||||
{
|
||||
bool all_empty = true ;
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Examining pixmaps sizes for " << label << " " << it->first << "." << std::endl;
|
||||
#endif
|
||||
|
||||
for(int i=0;i<NUM_AVATAR_SIZES;++i)
|
||||
if(it->second[i].first>0)
|
||||
{
|
||||
if(it->second[i].first + ICON_CACHE_STORAGE_TIME < now && it->second[i].second.isDetached())
|
||||
{
|
||||
int s = it->second[i].second.width()*it->second[i].second.height()*4;
|
||||
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Deleting pixmap " << it->first << " size " << i << " " << s << " bytes." << std::endl;
|
||||
#endif
|
||||
|
||||
it->second[i].second = QPixmap();
|
||||
it->second[i].first = 0;
|
||||
++nb_deleted;
|
||||
size_deleted += s;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_empty = false;
|
||||
total_size += it->second[i].second.width()*it->second[i].second.height()*4;
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Keeping " << it->first << " size " << i << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(all_empty)
|
||||
{
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Deleting entry " << it->first << " because no pixmaps are stored here. " << std::endl;
|
||||
#endif
|
||||
it = cache.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void ReputationItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
@ -420,6 +486,13 @@ const QPixmap GxsIdDetails::makeDefaultIcon(const RsGxsId& id, AvatarSize size)
|
||||
if(id.isNull())
|
||||
std::cerr << "Weird: null ID" << std::endl;
|
||||
|
||||
// Bounds check to prevent array overflow
|
||||
if((int)size >= NUM_AVATAR_SIZES || (int)size < 0)
|
||||
{
|
||||
std::cerr << "Warning: invalid avatar size " << (int)size << ", using MEDIUM" << std::endl;
|
||||
size = MEDIUM;
|
||||
}
|
||||
|
||||
QMutexLocker lock(&mIconCacheMutex);
|
||||
auto& it = mDefaultIconCache[id];
|
||||
|
||||
@ -447,6 +520,107 @@ const QPixmap GxsIdDetails::makeDefaultIcon(const RsGxsId& id, AvatarSize size)
|
||||
return image;
|
||||
}
|
||||
|
||||
QPixmap GxsIdDetails::generateColoredIcon(const QString& idStr, const QString& iconPath, int size)
|
||||
{
|
||||
// Generate a color from the ID hash
|
||||
uint hash = qHash(idStr);
|
||||
|
||||
// Use hash to determine hue (0-359), with fixed saturation and lightness for pastel look
|
||||
int hue = hash % 360;
|
||||
int saturation = 150; // Mid saturation for pastel colors
|
||||
int lightness = 180; // Light for pastel colors
|
||||
|
||||
QColor backgroundColor = QColor::fromHsl(hue, saturation, lightness);
|
||||
|
||||
// Create the pixmap
|
||||
QPixmap pixmap(size, size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
|
||||
// Draw colored circle
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(backgroundColor);
|
||||
painter.drawEllipse(0, 0, size, size);
|
||||
|
||||
// Load and overlay the category icon
|
||||
QPixmap categoryIcon(iconPath);
|
||||
if (!categoryIcon.isNull())
|
||||
{
|
||||
// Scale icon to ~70% of total size
|
||||
int iconSize = static_cast<int>(size * 0.7);
|
||||
QPixmap scaledIcon = categoryIcon.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
// Center the icon
|
||||
int x = (size - scaledIcon.width()) / 2;
|
||||
int y = (size - scaledIcon.height()) / 2;
|
||||
painter.drawPixmap(x, y, scaledIcon);
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
const QPixmap GxsIdDetails::makeDefaultGroupIcon(const RsGxsGroupId& id, const QString& iconPath, AvatarSize size)
|
||||
{
|
||||
checkCleanImagesCache();
|
||||
|
||||
time_t now = time(NULL);
|
||||
|
||||
if(id.isNull())
|
||||
std::cerr << "Weird: null ID" << std::endl;
|
||||
|
||||
// Bounds check to prevent array overflow
|
||||
if((int)size >= NUM_AVATAR_SIZES || (int)size < 0)
|
||||
{
|
||||
std::cerr << "Warning: invalid avatar size " << (int)size << ", using MEDIUM" << std::endl;
|
||||
size = MEDIUM;
|
||||
}
|
||||
|
||||
QMutexLocker lock(&mIconCacheMutex);
|
||||
auto& it = mDefaultGroupIconCache[groupIconCacheKey(id, iconPath)];
|
||||
|
||||
if(it[(int)size].second.width() > 0)
|
||||
{
|
||||
it[(int)size].first = now;
|
||||
return it[(int)size].second;
|
||||
}
|
||||
|
||||
int S = 0;
|
||||
|
||||
switch(size)
|
||||
{
|
||||
case SMALL: S = 48 ; break;
|
||||
default:
|
||||
case MEDIUM: S = 96 ; break;
|
||||
case ORIGINAL:
|
||||
case LARGE: S = 192 ; break;
|
||||
}
|
||||
|
||||
QPixmap pixmap = generateColoredIcon(QString::fromStdString(id.toStdString()), iconPath, S);
|
||||
|
||||
it[(int)size] = std::make_pair(now, pixmap);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
const QPixmap GxsIdDetails::makeDefaultGroupIcon(const QString& idStr, const QString& iconPath, AvatarSize size)
|
||||
{
|
||||
// Delegate to the RsGxsGroupId overload to avoid duplicating caching logic
|
||||
RsGxsGroupId id(idStr.toStdString());
|
||||
return makeDefaultGroupIcon(id, iconPath, size);
|
||||
}
|
||||
|
||||
const QPixmap GxsIdDetails::makeDefaultGroupIconFromString(const QString& idStr, const QString& iconPath, AvatarSize size)
|
||||
{
|
||||
// Delegate to the existing QString overload so that all string-based
|
||||
// calls share the same cache keys and entries.
|
||||
return makeDefaultGroupIcon(idStr, iconPath, size);
|
||||
}
|
||||
|
||||
void GxsIdDetails::debug_dumpImagesCache()
|
||||
{
|
||||
QMutexLocker lock(&mIconCacheMutex);
|
||||
@ -457,7 +631,25 @@ void GxsIdDetails::debug_dumpImagesCache()
|
||||
{
|
||||
std::cerr << " Identity " << it.first << ":" << std::endl;
|
||||
|
||||
for(uint32_t i=0;i<4;++i)
|
||||
for(uint32_t i=0;i<NUM_AVATAR_SIZES;++i)
|
||||
{
|
||||
std::cerr << " Size #" << i << ": " ;
|
||||
|
||||
if(it.second[i].first>0)
|
||||
{
|
||||
int s = it.second[i].second.width()*it.second[i].second.height()*4;
|
||||
std::cerr << " Present. Size=" << s << " bytes. Age: " << time(nullptr)-it.second[i].first << " secs. ago. Used: " << !it.second[i].second.isDetached() << std::endl;
|
||||
}
|
||||
else
|
||||
std::cerr << " None." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto& it:mDefaultGroupIconCache)
|
||||
{
|
||||
std::cerr << " Group " << it.first << ":" << std::endl;
|
||||
|
||||
for(uint32_t i=0;i<NUM_AVATAR_SIZES;++i)
|
||||
{
|
||||
std::cerr << " Size #" << i << ": " ;
|
||||
|
||||
@ -489,52 +681,12 @@ void GxsIdDetails::checkCleanImagesCache()
|
||||
|
||||
QMutexLocker lock(&mIconCacheMutex);
|
||||
|
||||
for(auto it(mDefaultIconCache.begin());it!=mDefaultIconCache.end();)
|
||||
{
|
||||
bool all_empty = true ;
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Examining pixmaps sizes for " << it->first << "." << std::endl;
|
||||
#endif
|
||||
|
||||
for(int i=0;i<4;++i)
|
||||
if(it->second[i].first>0)
|
||||
{
|
||||
if(it->second[i].first + ICON_CACHE_STORAGE_TIME < now && it->second[i].second.isDetached())
|
||||
{
|
||||
int s = it->second[i].second.width()*it->second[i].second.height()*4;
|
||||
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Deleting pixmap " << it->first << " size " << i << " " << s << " bytes." << std::endl;
|
||||
#endif
|
||||
|
||||
it->second[i].second = QPixmap();
|
||||
it->second[i].first = 0;
|
||||
++nb_deleted;
|
||||
size_deleted += s;
|
||||
}
|
||||
else
|
||||
{
|
||||
all_empty = false;
|
||||
total_size += it->second[i].second.width()*it->second[i].second.height()*4;
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Keeking " << it->first << " size " << i << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(all_empty)
|
||||
{
|
||||
#ifdef DEBUG_GXSIDDETAILS
|
||||
std::cerr << " Deleting entry " << it->first << " because no pixmaps are stored here. " << std::endl;
|
||||
#endif
|
||||
it = mDefaultIconCache.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
cleanupIconCache(mDefaultIconCache, now, nb_deleted, size_deleted, total_size, "identity");
|
||||
cleanupIconCache(mDefaultGroupIconCache, now, nb_deleted, size_deleted, total_size, "group");
|
||||
|
||||
mLastIconCacheCleaning = now;
|
||||
std::cerr << "(II) Removed " << nb_deleted << " (" << size_deleted << " bytes) unused icons. Cache contains " << mDefaultIconCache.size() << " icons (" << total_size << " bytes)"<< std::endl;
|
||||
std::cerr << "(II) Removed " << nb_deleted << " (" << size_deleted << " bytes) unused icons. Cache contains "
|
||||
<< (mDefaultIconCache.size() + mDefaultGroupIconCache.size()) << " icons (" << total_size << " bytes)"<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include <QString>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <retroshare/rsidentity.h>
|
||||
|
||||
class QLabel;
|
||||
@ -113,6 +115,27 @@ public:
|
||||
// These two methods use a cache so as to minimize the memory impact of avatars.
|
||||
|
||||
static const QPixmap makeDefaultIcon(const RsGxsId& id, AvatarSize size = MEDIUM);
|
||||
static const QPixmap makeDefaultGroupIcon(const RsGxsGroupId& id, const QString& iconPath, AvatarSize size = MEDIUM);
|
||||
static const QPixmap makeDefaultGroupIcon(const QString& idStr, const QString& iconPath, AvatarSize size = MEDIUM);
|
||||
/*!
|
||||
* \brief makeDefaultGroupIconFromString
|
||||
*
|
||||
* Create a default group icon for identifiers that are not GXS group IDs
|
||||
* (e.g. RsPeerId, RsPgpId) but are only available as strings.
|
||||
*
|
||||
* This overload uses a distinct cache key strategy from the
|
||||
* makeDefaultGroupIcon(const QString&, ...) overload, to avoid collisions
|
||||
* with icons generated for real RsGxsGroupId-based group identifiers. In
|
||||
* particular, the full string identifier (and optionally a type-specific
|
||||
* prefix in the implementation) is used as the cache key rather than
|
||||
* assuming that the string encodes a GXS group ID.
|
||||
*
|
||||
* Use this function when you need a "group-like" icon for non-GXS ID
|
||||
* types (such as RsPeerId or RsPgpId) and you only have a textual
|
||||
* representation. For actual GXS group identifiers, prefer:
|
||||
* - makeDefaultGroupIcon(const RsGxsGroupId&, const QString&, AvatarSize)
|
||||
* - makeDefaultGroupIcon(const QString&, const QString&, AvatarSize)
|
||||
*/
|
||||
static bool loadPixmapFromData(const unsigned char *data, size_t data_len, QPixmap& pix, AvatarSize size = MEDIUM);
|
||||
static void checkCleanImagesCache();
|
||||
static void debug_dumpImagesCache();
|
||||
@ -140,6 +163,7 @@ private:
|
||||
qreal shapeangle, qreal angle,
|
||||
quint16 size, QColor fillColor);
|
||||
static QPixmap drawIdentIcon(QString hash, quint16 width, bool rotate);
|
||||
static QPixmap generateColoredIcon(const QString& idStr, const QString& iconPath, int size);
|
||||
|
||||
private slots:
|
||||
void objectDestroyed(QObject *object);
|
||||
@ -172,6 +196,7 @@ protected:
|
||||
|
||||
static uint32_t mImagesAllocated;
|
||||
static std::map<RsGxsId,std::pair<time_t,QPixmap>[4] > mDefaultIconCache;
|
||||
static std::map<std::string,std::pair<time_t,QPixmap>[4] > mDefaultGroupIconCache;
|
||||
static time_t mLastIconCacheCleaning;
|
||||
|
||||
int mCheckTimerId;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "CreateGxsChannelMsg.h"
|
||||
#include "GxsChannelUserNotify.h"
|
||||
#include "gui/gxs/GxsGroupShareKey.h"
|
||||
#include "gui/gxs/GxsIdDetails.h"
|
||||
#include "gui/feeds/GxsChannelPostItem.h"
|
||||
#include "gui/settings/rsharesettings.h"
|
||||
#include "gui/common/GroupTreeWidget.h"
|
||||
@ -426,7 +427,7 @@ void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *gro
|
||||
groupItemInfo.icon = image;
|
||||
}
|
||||
else
|
||||
groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(":icons/png/channel.png");
|
||||
groupItemInfo.icon = GxsIdDetails::makeDefaultGroupIcon(channelGroupData->mMeta.mGroupId, ":icons/channel.png", GxsIdDetails::ORIGINAL);
|
||||
|
||||
groupItemInfo.description = QString::fromUtf8(channelGroupData->mDescription.c_str());
|
||||
}
|
||||
|
||||
@ -267,6 +267,10 @@
|
||||
<file>icons/yahoo.png</file>
|
||||
<file>icons/yandex.png</file>
|
||||
<file>icons/png/markdown-mark.png</file>
|
||||
<file>icons/board.png</file>
|
||||
<file>icons/channel.png</file>
|
||||
<file>icons/person.png</file>
|
||||
<file>icons/wiki.png</file>
|
||||
<file>icons/png/channel.png</file>
|
||||
<file>icons/png/circles-black.png</file>
|
||||
<file>icons/png/circles-gray.png</file>
|
||||
|
||||
BIN
retroshare-gui/src/gui/icons/board.png
Normal file
BIN
retroshare-gui/src/gui/icons/board.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
retroshare-gui/src/gui/icons/channel.png
Normal file
BIN
retroshare-gui/src/gui/icons/channel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
retroshare-gui/src/gui/icons/person.png
Normal file
BIN
retroshare-gui/src/gui/icons/person.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
retroshare-gui/src/gui/icons/wiki.png
Normal file
BIN
retroshare-gui/src/gui/icons/wiki.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Loading…
Reference in New Issue
Block a user