changed skin handling and added themes support for skins
3
HISTORY
@ -9,3 +9,6 @@ Version 0.0.2
|
|||||||
|
|
||||||
- added some more tokens with more detailed audio information in displaychannel -> statusinfo
|
- added some more tokens with more detailed audio information in displaychannel -> statusinfo
|
||||||
- added hasVPS for current scheduling in displaychannel -> epginfo
|
- added hasVPS for current scheduling in displaychannel -> epginfo
|
||||||
|
- added common channel logo path for all skins
|
||||||
|
- changed skin handling so that every skin is directly shown in VDR OSD Menu
|
||||||
|
- added Theme support, each skin can now have various themes
|
||||||
|
2
Makefile
@ -160,6 +160,8 @@ install-themes:
|
|||||||
install-skins:
|
install-skins:
|
||||||
mkdir -p $(DESTDIR)$(PLGRESDIR)/skins
|
mkdir -p $(DESTDIR)$(PLGRESDIR)/skins
|
||||||
cp -r skins/* $(DESTDIR)$(PLGRESDIR)/skins
|
cp -r skins/* $(DESTDIR)$(PLGRESDIR)/skins
|
||||||
|
mkdir -p $(DESTDIR)$(PLGRESDIR)/dtd
|
||||||
|
cp -r dtd/* $(DESTDIR)$(PLGRESDIR)/dtd
|
||||||
|
|
||||||
install: install-lib install-i18n install-themes install-skins
|
install: install-lib install-i18n install-themes install-skins
|
||||||
|
|
||||||
|
3
README
@ -47,6 +47,9 @@ XML skins and epg images. The following paths can be set at startup:
|
|||||||
-s <SKINPATH>, --skinpath=<SKINPATH>
|
-s <SKINPATH>, --skinpath=<SKINPATH>
|
||||||
Path to the XML skins (Default: <ResourceDirectory>/plugins/skindesigner/skins/)
|
Path to the XML skins (Default: <ResourceDirectory>/plugins/skindesigner/skins/)
|
||||||
|
|
||||||
|
-l <LOGOPATH>, --logopath=<LOGOPATH>
|
||||||
|
Path to common logo set for all skins (Default: <ResourceDirectory>/plugins/skindesigner/logos/)
|
||||||
|
|
||||||
-e path, --epgimages=path
|
-e path, --epgimages=path
|
||||||
Path to the epgimages (Default: <CacheDirectory>/epgimages/)
|
Path to the epgimages (Default: <CacheDirectory>/epgimages/)
|
||||||
|
|
||||||
|
38
config.c
@ -5,6 +5,7 @@
|
|||||||
cDesignerConfig::cDesignerConfig() {
|
cDesignerConfig::cDesignerConfig() {
|
||||||
epgImagePathSet = false;
|
epgImagePathSet = false;
|
||||||
skinPathSet = false;
|
skinPathSet = false;
|
||||||
|
logoPathSet = false;
|
||||||
//Common
|
//Common
|
||||||
logoExtension = "png";
|
logoExtension = "png";
|
||||||
numLogosPerSizeInitial = 30;
|
numLogosPerSizeInitial = 30;
|
||||||
@ -23,12 +24,14 @@ cDesignerConfig::~cDesignerConfig() {
|
|||||||
void cDesignerConfig::SetPathes(void) {
|
void cDesignerConfig::SetPathes(void) {
|
||||||
if (!skinPathSet)
|
if (!skinPathSet)
|
||||||
skinPath = cString::sprintf("%s/skins/", cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
skinPath = cString::sprintf("%s/skins/", cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
||||||
|
if (!logoPathSet)
|
||||||
|
logoPath = cString::sprintf("%s/logos/", cPlugin::ResourceDirectory(PLUGIN_NAME_I18N));
|
||||||
if (!epgImagePathSet)
|
if (!epgImagePathSet)
|
||||||
epgImagePath = cString::sprintf("%s/epgimages/", cPlugin::CacheDirectory(PLUGIN_NAME_I18N));
|
epgImagePath = cString::sprintf("%s/epgimages/", cPlugin::CacheDirectory(PLUGIN_NAME_I18N));
|
||||||
|
|
||||||
dsyslog("skindesigner: using Skin Directory %s", *skinPath);
|
dsyslog("skindesigner: using Skin Directory %s", *skinPath);
|
||||||
|
dsyslog("skindesigner: using common ChannelLogo Directory %s", *logoPath);
|
||||||
dsyslog("skindesigner: using EPG Images Directory %s", *epgImagePath);
|
dsyslog("skindesigner: using EPG Images Directory %s", *epgImagePath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDesignerConfig::SetSkinPath(cString path) {
|
void cDesignerConfig::SetSkinPath(cString path) {
|
||||||
@ -36,11 +39,44 @@ void cDesignerConfig::SetSkinPath(cString path) {
|
|||||||
skinPathSet = true;
|
skinPathSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cDesignerConfig::SetLogoPath(cString path) {
|
||||||
|
logoPath = CheckSlashAtEnd(*path);
|
||||||
|
logoPathSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
void cDesignerConfig::SetEpgImagePath(cString path) {
|
void cDesignerConfig::SetEpgImagePath(cString path) {
|
||||||
epgImagePath = CheckSlashAtEnd(*path);
|
epgImagePath = CheckSlashAtEnd(*path);
|
||||||
epgImagePathSet = true;
|
epgImagePathSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cDesignerConfig::ReadSkins(void) {
|
||||||
|
DIR *folder = NULL;
|
||||||
|
struct dirent *dirEntry;
|
||||||
|
folder = opendir(skinPath);
|
||||||
|
if (!folder) {
|
||||||
|
esyslog("skindesigner: no skins found in %s", *skinPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (dirEntry = readdir(folder)) {
|
||||||
|
string dirEntryName = dirEntry->d_name;
|
||||||
|
int dirEntryType = dirEntry->d_type;
|
||||||
|
if (!dirEntryName.compare(".") || !dirEntryName.compare("..") || dirEntryType != DT_DIR)
|
||||||
|
continue;
|
||||||
|
skins.push_back(dirEntryName);
|
||||||
|
}
|
||||||
|
dsyslog("skindesigner %d skins found in %s", skins.size(), *skinPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDesignerConfig::GetSkin(string &skin) {
|
||||||
|
if (skinIterator == skins.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
skin = *skinIterator;
|
||||||
|
skinIterator++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void cDesignerConfig::SetChannelLogoSize(void) {
|
void cDesignerConfig::SetChannelLogoSize(void) {
|
||||||
cImageLoader imgLoader;
|
cImageLoader imgLoader;
|
||||||
imgLoader.DeterminateChannelLogoSize(logoWidth, logoHeight);
|
imgLoader.DeterminateChannelLogoSize(logoWidth, logoHeight);
|
||||||
|
16
config.h
@ -15,17 +15,23 @@ private:
|
|||||||
cString CheckSlashAtEnd(std::string path);
|
cString CheckSlashAtEnd(std::string path);
|
||||||
bool epgImagePathSet;
|
bool epgImagePathSet;
|
||||||
bool skinPathSet;
|
bool skinPathSet;
|
||||||
|
bool logoPathSet;
|
||||||
public:
|
public:
|
||||||
cDesignerConfig();
|
cDesignerConfig();
|
||||||
~cDesignerConfig();
|
~cDesignerConfig();
|
||||||
bool SetupParse(const char *Name, const char *Value);
|
bool SetupParse(const char *Name, const char *Value);
|
||||||
void SetSkinPath(cString path);
|
|
||||||
void SetEpgImagePath(cString path);
|
|
||||||
void SetPathes(void);
|
void SetPathes(void);
|
||||||
|
void SetSkinPath(cString path);
|
||||||
|
void SetLogoPath(cString path);
|
||||||
|
void SetEpgImagePath(cString path);
|
||||||
|
void ReadSkins(void);
|
||||||
|
void InitSkinIterator(void) { skinIterator = skins.begin(); };
|
||||||
|
bool GetSkin(string &skin);
|
||||||
void SetChannelLogoSize(void);
|
void SetChannelLogoSize(void);
|
||||||
void CheckDecimalPoint(void);
|
void CheckDecimalPoint(void);
|
||||||
cString logoExtension;
|
cString logoExtension;
|
||||||
cString skinPath;
|
cString skinPath;
|
||||||
|
cString logoPath;
|
||||||
cString epgImagePath;
|
cString epgImagePath;
|
||||||
int numLogosPerSizeInitial;
|
int numLogosPerSizeInitial;
|
||||||
int limitLogoCache;
|
int limitLogoCache;
|
||||||
@ -35,12 +41,14 @@ public:
|
|||||||
int logoHeight;
|
int logoHeight;
|
||||||
bool replaceDecPoint;
|
bool replaceDecPoint;
|
||||||
char decPoint;
|
char decPoint;
|
||||||
|
vector<string> skins;
|
||||||
|
vector<string>::iterator skinIterator;
|
||||||
};
|
};
|
||||||
#ifdef DEFINE_CONFIG
|
#ifdef DEFINE_CONFIG
|
||||||
bool firstDisplay = true;
|
bool firstDisplay = true;
|
||||||
cDesignerConfig config;
|
cDesignerConfig config;
|
||||||
cFontManager *fontManager;
|
cFontManager *fontManager = NULL;
|
||||||
cImageCache *imgCache;
|
cImageCache *imgCache = NULL;
|
||||||
cTheme Theme;
|
cTheme Theme;
|
||||||
#else
|
#else
|
||||||
extern bool firstDisplay;
|
extern bool firstDisplay;
|
||||||
|
78
designer.c
@ -1,20 +1,13 @@
|
|||||||
#include "designer.h"
|
#include "designer.h"
|
||||||
#include "libcore/helpers.h"
|
#include "libcore/helpers.h"
|
||||||
|
|
||||||
cSkinDesigner::cSkinDesigner(void) : cSkin("skindesigner", &::Theme) {
|
cSkinDesigner::cSkinDesigner(string skin) : cSkin(skin.c_str(), &::Theme) {
|
||||||
|
init = true;
|
||||||
|
this->skin = skin;
|
||||||
|
|
||||||
backupSkin = NULL;
|
backupSkin = NULL;
|
||||||
useBackupSkin = false;
|
useBackupSkin = false;
|
||||||
|
|
||||||
SetOSDSize();
|
|
||||||
osdSkin = Setup.OSDSkin;
|
|
||||||
osdTheme = Setup.OSDTheme;
|
|
||||||
|
|
||||||
config.SetPathes();
|
|
||||||
config.SetChannelLogoSize();
|
|
||||||
config.CheckDecimalPoint();
|
|
||||||
fontManager = new cFontManager();
|
|
||||||
imgCache = new cImageCache();
|
|
||||||
|
|
||||||
globals = NULL;
|
globals = NULL;
|
||||||
channelTemplate = NULL;
|
channelTemplate = NULL;
|
||||||
menuTemplate = NULL;
|
menuTemplate = NULL;
|
||||||
@ -22,17 +15,7 @@ cSkinDesigner::cSkinDesigner(void) : cSkin("skindesigner", &::Theme) {
|
|||||||
replayTemplate = NULL;
|
replayTemplate = NULL;
|
||||||
volumeTemplate = NULL;
|
volumeTemplate = NULL;
|
||||||
audiotracksTemplate = NULL;
|
audiotracksTemplate = NULL;
|
||||||
|
dsyslog("skindesigner: skin %s started", skin.c_str());
|
||||||
cStopWatch watch;
|
|
||||||
bool ok = LoadTemplates();
|
|
||||||
if (!ok) {
|
|
||||||
esyslog("skindesigner: error during loading of templates - using LCARS as backup");
|
|
||||||
backupSkin = new cSkinLCARS();
|
|
||||||
useBackupSkin = true;
|
|
||||||
} else {
|
|
||||||
CacheTemplates();
|
|
||||||
watch.Stop("templates loaded and cache created");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cSkinDesigner::~cSkinDesigner(void) {
|
cSkinDesigner::~cSkinDesigner(void) {
|
||||||
@ -44,10 +27,43 @@ cSkinDesigner::~cSkinDesigner(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *cSkinDesigner::Description(void) {
|
const char *cSkinDesigner::Description(void) {
|
||||||
return "SkinDesigner";
|
return skin.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSkinDesigner::Init(void) {
|
||||||
|
dsyslog("skindesigner: initializing skin %s", skin.c_str());
|
||||||
|
SetOSDSize();
|
||||||
|
osdSkin = Setup.OSDSkin;
|
||||||
|
osdTheme = Setup.OSDTheme;
|
||||||
|
|
||||||
|
config.SetChannelLogoSize();
|
||||||
|
config.CheckDecimalPoint();
|
||||||
|
|
||||||
|
if (fontManager)
|
||||||
|
delete fontManager;
|
||||||
|
fontManager = new cFontManager();
|
||||||
|
if (imgCache)
|
||||||
|
delete imgCache;
|
||||||
|
imgCache = new cImageCache();
|
||||||
|
imgCache->SetPathes();
|
||||||
|
|
||||||
|
cStopWatch watch;
|
||||||
|
bool ok = LoadTemplates();
|
||||||
|
if (!ok) {
|
||||||
|
esyslog("skindesigner: error during loading of templates - using LCARS as backup");
|
||||||
|
backupSkin = new cSkinLCARS();
|
||||||
|
useBackupSkin = true;
|
||||||
|
} else {
|
||||||
|
CacheTemplates();
|
||||||
|
watch.Stop("templates loaded and cache created");
|
||||||
|
}
|
||||||
|
init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayChannel *cSkinDesigner::DisplayChannel(bool WithInfo) {
|
cSkinDisplayChannel *cSkinDesigner::DisplayChannel(bool WithInfo) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayChannel *displayChannel = NULL;
|
cSkinDisplayChannel *displayChannel = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -59,6 +75,9 @@ cSkinDisplayChannel *cSkinDesigner::DisplayChannel(bool WithInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayMenu *cSkinDesigner::DisplayMenu(void) {
|
cSkinDisplayMenu *cSkinDesigner::DisplayMenu(void) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayMenu *displayMenu = NULL;
|
cSkinDisplayMenu *displayMenu = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -71,6 +90,9 @@ cSkinDisplayMenu *cSkinDesigner::DisplayMenu(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayReplay *cSkinDesigner::DisplayReplay(bool ModeOnly) {
|
cSkinDisplayReplay *cSkinDesigner::DisplayReplay(bool ModeOnly) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayReplay *displayReplay = NULL;
|
cSkinDisplayReplay *displayReplay = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -82,6 +104,9 @@ cSkinDisplayReplay *cSkinDesigner::DisplayReplay(bool ModeOnly) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayVolume *cSkinDesigner::DisplayVolume(void) {
|
cSkinDisplayVolume *cSkinDesigner::DisplayVolume(void) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayVolume *displayVolume = NULL;
|
cSkinDisplayVolume *displayVolume = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -93,6 +118,9 @@ cSkinDisplayVolume *cSkinDesigner::DisplayVolume(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayTracks *cSkinDesigner::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) {
|
cSkinDisplayTracks *cSkinDesigner::DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayTracks *displayTracks = NULL;
|
cSkinDisplayTracks *displayTracks = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -104,6 +132,9 @@ cSkinDisplayTracks *cSkinDesigner::DisplayTracks(const char *Title, int NumTrack
|
|||||||
}
|
}
|
||||||
|
|
||||||
cSkinDisplayMessage *cSkinDesigner::DisplayMessage(void) {
|
cSkinDisplayMessage *cSkinDesigner::DisplayMessage(void) {
|
||||||
|
if (init) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
cSkinDisplayMessage *displayMessage = NULL;
|
cSkinDisplayMessage *displayMessage = NULL;
|
||||||
if (!useBackupSkin) {
|
if (!useBackupSkin) {
|
||||||
ReloadCaches();
|
ReloadCaches();
|
||||||
@ -261,6 +292,7 @@ void cSkinDesigner::CacheTemplates(void) {
|
|||||||
dsyslog("skindesigner: fonts cached");
|
dsyslog("skindesigner: fonts cached");
|
||||||
dsyslog("skindesigner: caching images...");
|
dsyslog("skindesigner: caching images...");
|
||||||
imgCache->Clear();
|
imgCache->Clear();
|
||||||
|
imgCache->SetPathes();
|
||||||
channelTemplate->CacheImages();
|
channelTemplate->CacheImages();
|
||||||
menuTemplate->CacheImages();
|
menuTemplate->CacheImages();
|
||||||
messageTemplate->CacheImages();
|
messageTemplate->CacheImages();
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
class cSkinDesigner : public cSkin {
|
class cSkinDesigner : public cSkin {
|
||||||
private:
|
private:
|
||||||
|
bool init;
|
||||||
|
string skin;
|
||||||
cSkinLCARS *backupSkin;
|
cSkinLCARS *backupSkin;
|
||||||
bool useBackupSkin;
|
bool useBackupSkin;
|
||||||
cRect osdSize;
|
cRect osdSize;
|
||||||
@ -34,7 +36,7 @@ private:
|
|||||||
bool OsdSizeChanged(void);
|
bool OsdSizeChanged(void);
|
||||||
bool ThemeChanged(void);
|
bool ThemeChanged(void);
|
||||||
public:
|
public:
|
||||||
cSkinDesigner(void);
|
cSkinDesigner(string skin);
|
||||||
virtual ~cSkinDesigner(void);
|
virtual ~cSkinDesigner(void);
|
||||||
virtual const char *Description(void);
|
virtual const char *Description(void);
|
||||||
virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo);
|
virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo);
|
||||||
@ -43,6 +45,8 @@ public:
|
|||||||
virtual cSkinDisplayVolume *DisplayVolume(void);
|
virtual cSkinDisplayVolume *DisplayVolume(void);
|
||||||
virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks);
|
virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks);
|
||||||
virtual cSkinDisplayMessage *DisplayMessage(void);
|
virtual cSkinDisplayMessage *DisplayMessage(void);
|
||||||
|
void Init(void);
|
||||||
|
void ActivateBackupSkin(void) { useBackupSkin = true; };
|
||||||
void Reload(void);
|
void Reload(void);
|
||||||
void ListAvailableFonts(void);
|
void ListAvailableFonts(void);
|
||||||
};
|
};
|
||||||
|
@ -97,6 +97,11 @@ bool FileExists(const string &path, const string &name, const string &ext) {
|
|||||||
return (stat (fileName.str().c_str(), &buffer) == 0);
|
return (stat (fileName.str().c_str(), &buffer) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FolderExists(const string &path) {
|
||||||
|
struct stat buffer;
|
||||||
|
return stat(path.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
bool FirstFileInFolder(string &path, string &extension, string &fileName) {
|
bool FirstFileInFolder(string &path, string &extension, string &fileName) {
|
||||||
DIR *folder = NULL;
|
DIR *folder = NULL;
|
||||||
struct dirent *file;
|
struct dirent *file;
|
||||||
|
@ -13,6 +13,7 @@ std::string CutText(string &text, int width, string fontName, int fontSize);
|
|||||||
std::string StrToLowerCase(string str);
|
std::string StrToLowerCase(string str);
|
||||||
bool isNumber(const string& s);
|
bool isNumber(const string& s);
|
||||||
bool FileExists(const string &path, const string &name, const string &ext);
|
bool FileExists(const string &path, const string &name, const string &ext);
|
||||||
|
bool FolderExists(const string &path);
|
||||||
bool FirstFileInFolder(string &path, string &extension, string &fileName);
|
bool FirstFileInFolder(string &path, string &extension, string &fileName);
|
||||||
|
|
||||||
class splitstring : public std::string {
|
class splitstring : public std::string {
|
||||||
|
@ -26,6 +26,21 @@ cImageCache::~cImageCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cImageCache::SetPathes(void) {
|
||||||
|
string logoPathSkin = *cString::sprintf("%s%s/themes/%s/logos/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme);
|
||||||
|
if (FolderExists(logoPathSkin)) {
|
||||||
|
logoPath = logoPathSkin;
|
||||||
|
} else {
|
||||||
|
logoPath = *config.logoPath;
|
||||||
|
}
|
||||||
|
iconPath = *cString::sprintf("%s%s/themes/%s/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme);
|
||||||
|
skinPartsPath = *cString::sprintf("%s%s/themes/%s/skinparts/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme);
|
||||||
|
|
||||||
|
dsyslog("skindesigner: using channel logo path %s", logoPath.c_str());
|
||||||
|
dsyslog("skindesigner: using icon path %s", iconPath.c_str());
|
||||||
|
dsyslog("skindesigner: using skinparts path %s", skinPartsPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void cImageCache::CacheLogo(int width, int height) {
|
void cImageCache::CacheLogo(int width, int height) {
|
||||||
if (config.numLogosPerSizeInitial == 0)
|
if (config.numLogosPerSizeInitial == 0)
|
||||||
return;
|
return;
|
||||||
@ -125,14 +140,13 @@ bool cImageCache::LogoExists(string channelID) {
|
|||||||
const cChannel *channel = Channels.GetByChannelID(chanID);
|
const cChannel *channel = Channels.GetByChannelID(chanID);
|
||||||
if (!channel)
|
if (!channel)
|
||||||
return false;
|
return false;
|
||||||
string logoPath = *cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
|
|
||||||
string logoLower = StrToLowerCase(channel->Name());
|
string logoLower = StrToLowerCase(channel->Name());
|
||||||
string logoExt = *config.logoExtension;
|
string logoExt = *config.logoExtension;
|
||||||
bool logoExists = FileExists(logoPath, logoLower, logoExt);
|
bool logoExists = FileExists(logoPath.c_str(), logoLower, logoExt);
|
||||||
if (logoExists) {
|
if (logoExists) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
logoExists = FileExists(logoPath, channelID, logoExt);
|
logoExists = FileExists(logoPath.c_str(), channelID, logoExt);
|
||||||
if (logoExists) {
|
if (logoExists) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -265,8 +279,8 @@ bool cImageCache::LoadIcon(eImageType type, string name) {
|
|||||||
subdir = "menuicons";
|
subdir = "menuicons";
|
||||||
else if (type == itIcon)
|
else if (type == itIcon)
|
||||||
subdir = "icons";
|
subdir = "icons";
|
||||||
cString iconPath = cString::sprintf("%s%s/graphics/%s/", *config.skinPath, Setup.OSDTheme, *subdir);
|
cString subIconPath = cString::sprintf("%s%s/", iconPath.c_str(), *subdir);
|
||||||
success = LoadImage(name, *iconPath, "png");
|
success = LoadImage(name, *subIconPath, "png");
|
||||||
if (success) {
|
if (success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -276,21 +290,20 @@ bool cImageCache::LoadIcon(eImageType type, string name) {
|
|||||||
bool cImageCache::LoadLogo(const cChannel *channel) {
|
bool cImageCache::LoadLogo(const cChannel *channel) {
|
||||||
if (!channel)
|
if (!channel)
|
||||||
return false;
|
return false;
|
||||||
cString logoPath = cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
|
|
||||||
string channelID = StrToLowerCase(*(channel->GetChannelID().ToString()));
|
string channelID = StrToLowerCase(*(channel->GetChannelID().ToString()));
|
||||||
string logoLower = StrToLowerCase(channel->Name());
|
string logoLower = StrToLowerCase(channel->Name());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
success = LoadImage(channelID.c_str(), *logoPath, *config.logoExtension);
|
success = LoadImage(channelID.c_str(), logoPath.c_str(), *config.logoExtension);
|
||||||
if (success)
|
if (success)
|
||||||
return true;
|
return true;
|
||||||
success = LoadImage(logoLower.c_str(), *logoPath, *config.logoExtension);
|
success = LoadImage(logoLower.c_str(), logoPath.c_str(), *config.logoExtension);
|
||||||
if (success)
|
if (success)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cImageCache::LoadSeparatorLogo(string name) {
|
bool cImageCache::LoadSeparatorLogo(string name) {
|
||||||
cString separatorPath = cString::sprintf("%s%s/logos/separatorlogos/", *config.skinPath, Setup.OSDTheme);
|
cString separatorPath = cString::sprintf("%sseparatorlogos/", logoPath.c_str());
|
||||||
string nameLower = StrToLowerCase(name.c_str());
|
string nameLower = StrToLowerCase(name.c_str());
|
||||||
bool success = false;
|
bool success = false;
|
||||||
success = LoadImage(nameLower.c_str(), *separatorPath, *config.logoExtension);
|
success = LoadImage(nameLower.c_str(), *separatorPath, *config.logoExtension);
|
||||||
@ -301,8 +314,7 @@ bool cImageCache::LoadSeparatorLogo(string name) {
|
|||||||
|
|
||||||
bool cImageCache::LoadSkinpart(string name) {
|
bool cImageCache::LoadSkinpart(string name) {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
cString iconPath = cString::sprintf("%s%s/graphics/skinparts/", *config.skinPath, Setup.OSDTheme);
|
success = LoadImage(name, skinPartsPath.c_str(), "png");
|
||||||
success = LoadImage(name, *iconPath, "png");
|
|
||||||
if (success) {
|
if (success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ public:
|
|||||||
~cImageCache();
|
~cImageCache();
|
||||||
void Lock(void) { mutex.Lock(); }
|
void Lock(void) { mutex.Lock(); }
|
||||||
void Unlock(void) { mutex.Unlock(); }
|
void Unlock(void) { mutex.Unlock(); }
|
||||||
|
void SetPathes(void);
|
||||||
//channel logos
|
//channel logos
|
||||||
void CacheLogo(int width, int height);
|
void CacheLogo(int width, int height);
|
||||||
cImage *GetLogo(string channelID, int width, int height);
|
cImage *GetLogo(string channelID, int width, int height);
|
||||||
@ -41,6 +42,9 @@ private:
|
|||||||
static cMutex mutex;
|
static cMutex mutex;
|
||||||
static string items[16];
|
static string items[16];
|
||||||
cImage *tempStaticLogo;
|
cImage *tempStaticLogo;
|
||||||
|
string logoPath;
|
||||||
|
string iconPath;
|
||||||
|
string skinPartsPath;
|
||||||
map<string, cImage*> iconCache;
|
map<string, cImage*> iconCache;
|
||||||
map<string, cImage*> channelLogoCache;
|
map<string, cImage*> channelLogoCache;
|
||||||
map<string, cImage*> skinPartsCache;
|
map<string, cImage*> skinPartsCache;
|
||||||
|
@ -27,7 +27,13 @@ bool cImageLoader::LoadImage(const char *path, int width, int height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) {
|
void cImageLoader::DeterminateChannelLogoSize(int &width, int &height) {
|
||||||
cString logoPath = cString::sprintf("%s%s/logos/", *config.skinPath, Setup.OSDTheme);
|
cString logoPath;
|
||||||
|
cString logoPathSkin = cString::sprintf("%s%s/themes/%s/logos/", *config.skinPath, Setup.OSDSkin, Setup.OSDTheme);
|
||||||
|
if (FolderExists(*logoPathSkin)) {
|
||||||
|
logoPath = logoPathSkin;
|
||||||
|
} else {
|
||||||
|
logoPath = config.logoPath;
|
||||||
|
}
|
||||||
cString logoExt = config.logoExtension;
|
cString logoExt = config.logoExtension;
|
||||||
DIR *folder = NULL;
|
DIR *folder = NULL;
|
||||||
struct dirent *file;
|
struct dirent *file;
|
||||||
|
@ -177,11 +177,14 @@ void cXmlParser::DeleteDocument(void) {
|
|||||||
|
|
||||||
string cXmlParser::GetPath(string xmlFile) {
|
string cXmlParser::GetPath(string xmlFile) {
|
||||||
string activeSkin = Setup.OSDSkin;
|
string activeSkin = Setup.OSDSkin;
|
||||||
string theme = "default";
|
string activeTheme = Setup.OSDTheme;
|
||||||
if (!activeSkin.compare("skindesigner")) {
|
string path = "";
|
||||||
theme = Setup.OSDTheme;
|
if (!xmlFile.compare("globals.xml")) {
|
||||||
|
path = *cString::sprintf("%s%s/themes/%s/%s", *config.skinPath, activeSkin.c_str(), activeTheme.c_str(), xmlFile.c_str());
|
||||||
|
} else {
|
||||||
|
path = *cString::sprintf("%s%s/xmlfiles/%s", *config.skinPath, activeSkin.c_str(), xmlFile.c_str());
|
||||||
}
|
}
|
||||||
return *cString::sprintf("%s%s/xmlfiles/%s", *config.skinPath, theme.c_str(), xmlFile.c_str());
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cXmlParser::ParseGlobalColors(xmlNodePtr node) {
|
void cXmlParser::ParseGlobalColors(xmlNodePtr node) {
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <vdr/plugin.h>
|
#include <vdr/plugin.h>
|
||||||
#include <vdr/skinlcars.h>
|
|
||||||
|
|
||||||
#define DEFINE_CONFIG 1
|
#define DEFINE_CONFIG 1
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -26,7 +25,7 @@ static const char *MAINMENUENTRY = "Skin Designer";
|
|||||||
|
|
||||||
class cPluginSkinDesigner : public cPlugin {
|
class cPluginSkinDesigner : public cPlugin {
|
||||||
private:
|
private:
|
||||||
cSkinDesigner *skinDesigner;
|
vector<cSkinDesigner*> skins;
|
||||||
public:
|
public:
|
||||||
cPluginSkinDesigner(void);
|
cPluginSkinDesigner(void);
|
||||||
virtual ~cPluginSkinDesigner();
|
virtual ~cPluginSkinDesigner();
|
||||||
@ -51,7 +50,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cPluginSkinDesigner::cPluginSkinDesigner(void) {
|
cPluginSkinDesigner::cPluginSkinDesigner(void) {
|
||||||
skinDesigner = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cPluginSkinDesigner::~cPluginSkinDesigner() {
|
cPluginSkinDesigner::~cPluginSkinDesigner() {
|
||||||
@ -60,6 +58,7 @@ cPluginSkinDesigner::~cPluginSkinDesigner() {
|
|||||||
const char *cPluginSkinDesigner::CommandLineHelp(void) {
|
const char *cPluginSkinDesigner::CommandLineHelp(void) {
|
||||||
return
|
return
|
||||||
" -s <SKINPATH>, --skinpath=<SKINPATH> Set directory where xml skins are stored\n"
|
" -s <SKINPATH>, --skinpath=<SKINPATH> Set directory where xml skins are stored\n"
|
||||||
|
" -l <LOGOPATH>, --logopath=<LOGOPATH> Set directory where a common logo set for all skins is stored\n"
|
||||||
" -e <EPGIMAGESPATH>, --epgimages=<IMAGESPATH> Set directory where epgimages are stored\n";
|
" -e <EPGIMAGESPATH>, --epgimages=<IMAGESPATH> Set directory where epgimages are stored\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +66,20 @@ bool cPluginSkinDesigner::ProcessArgs(int argc, char *argv[]) {
|
|||||||
// Implement command line argument processing here if applicable.
|
// Implement command line argument processing here if applicable.
|
||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
{ "epgimages", required_argument, NULL, 'e' },
|
{ "epgimages", required_argument, NULL, 'e' },
|
||||||
|
{ "logopath", required_argument, NULL, 'l' },
|
||||||
{ "skinpath", required_argument, NULL, 's' },
|
{ "skinpath", required_argument, NULL, 's' },
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "e:s:", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "e:s:l:", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'e':
|
case 'e':
|
||||||
config.SetEpgImagePath(cString(optarg));
|
config.SetEpgImagePath(cString(optarg));
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
config.SetLogoPath(cString(optarg));
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
config.SetSkinPath(cString(optarg));
|
config.SetSkinPath(cString(optarg));
|
||||||
break;
|
break;
|
||||||
@ -92,13 +95,24 @@ bool cPluginSkinDesigner::Initialize(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool cPluginSkinDesigner::Start(void) {
|
bool cPluginSkinDesigner::Start(void) {
|
||||||
|
bool trueColorAvailable = true;
|
||||||
if (!cOsdProvider::SupportsTrueColor()) {
|
if (!cOsdProvider::SupportsTrueColor()) {
|
||||||
esyslog("skindesigner: No TrueColor OSD found! Using default Skin LCARS!");
|
esyslog("skindesigner: No TrueColor OSD found! Using default Skin LCARS!");
|
||||||
return new cSkinLCARS();
|
trueColorAvailable = false;
|
||||||
} else
|
} else
|
||||||
dsyslog("skindesigner: TrueColor OSD found");
|
dsyslog("skindesigner: TrueColor OSD found");
|
||||||
skinDesigner = new cSkinDesigner();
|
config.SetPathes();
|
||||||
return skinDesigner;
|
config.ReadSkins();
|
||||||
|
config.InitSkinIterator();
|
||||||
|
string skin = "";
|
||||||
|
while (config.GetSkin(skin)) {
|
||||||
|
cSkinDesigner *newSkin = new cSkinDesigner(skin);
|
||||||
|
skins.push_back(newSkin);
|
||||||
|
if (!trueColorAvailable) {
|
||||||
|
newSkin->ActivateBackupSkin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPluginSkinDesigner::Stop(void) {
|
void cPluginSkinDesigner::Stop(void) {
|
||||||
@ -148,16 +162,26 @@ const char **cPluginSkinDesigner::SVDRPHelpPages(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cString cPluginSkinDesigner::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) {
|
cString cPluginSkinDesigner::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) {
|
||||||
|
|
||||||
|
cSkinDesigner *activeSkin = NULL;
|
||||||
|
for (vector<cSkinDesigner*>::iterator skin = skins.begin(); skin != skins.end(); skin++) {
|
||||||
|
string activeSkinName = Setup.OSDSkin;
|
||||||
|
string currentSkinName = (*skin)->Description();
|
||||||
|
if (!currentSkinName.compare(activeSkinName)) {
|
||||||
|
activeSkin = *skin;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeSkin)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (strcasecmp(Command, "RELD") == 0) {
|
if (strcasecmp(Command, "RELD") == 0) {
|
||||||
if (skinDesigner) {
|
activeSkin->Reload();
|
||||||
skinDesigner->Reload();
|
return "SKINDESIGNER reload of templates and caches forced.";
|
||||||
return "SKINDESIGNER reload of templates and caches forced.";
|
|
||||||
}
|
|
||||||
} else if (strcasecmp(Command, "LSTF") == 0) {
|
} else if (strcasecmp(Command, "LSTF") == 0) {
|
||||||
if (skinDesigner) {
|
activeSkin->ListAvailableFonts();
|
||||||
skinDesigner->ListAvailableFonts();
|
return "SKINDESIGNER available fonts listed in syslog.";
|
||||||
return "SKINDESIGNER available fonts listed in syslog.";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE globals SYSTEM "../../dtd/globals.dtd">
|
<!DOCTYPE globals SYSTEM "../../../../dtd/globals.dtd">
|
||||||
|
|
||||||
<globals>
|
<globals>
|
||||||
<!--
|
<!--
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B |
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 413 B After Width: | Height: | Size: 413 B |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 147 B After Width: | Height: | Size: 147 B |
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 788 B After Width: | Height: | Size: 788 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 531 B |
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 861 B After Width: | Height: | Size: 861 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 859 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |