completely change how we save and load containments and panels.
it all happens from one file now using nested groups. this has two major effects: - one file to rule them all for any given corona; this makes things even nicer for use in other apps, btw. - the ability to easily save, send/share and restore corona configuration layouts; something i've wanted from the start svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=741158
This commit is contained in:
parent
1e1b63b9fe
commit
ed8c50e03f
96
applet.cpp
96
applet.cpp
@ -71,8 +71,6 @@ class Applet::Private
|
|||||||
public:
|
public:
|
||||||
Private(KService::Ptr service, int uniqueID)
|
Private(KService::Ptr service, int uniqueID)
|
||||||
: appletId(uniqueID),
|
: appletId(uniqueID),
|
||||||
globalConfig(0),
|
|
||||||
appletConfig(0),
|
|
||||||
appletDescription(service),
|
appletDescription(service),
|
||||||
package(0),
|
package(0),
|
||||||
background(0),
|
background(0),
|
||||||
@ -170,7 +168,8 @@ public:
|
|||||||
QString xmlPath = package->filePath("mainconfigxml");
|
QString xmlPath = package->filePath("mainconfigxml");
|
||||||
if (!xmlPath.isEmpty()) {
|
if (!xmlPath.isEmpty()) {
|
||||||
QFile file(xmlPath);
|
QFile file(xmlPath);
|
||||||
configXml = new ConfigXml(config(), &file);
|
// FIXME: KConfigSkeleton doesn't play well with KConfigGroup =/
|
||||||
|
//configXml = new ConfigXml(applet->config(), &file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!package->filePath("mainconfigui").isEmpty()) {
|
if (!package->filePath("mainconfigui").isEmpty()) {
|
||||||
@ -303,17 +302,6 @@ public:
|
|||||||
return s_maxAppletId;
|
return s_maxAppletId;
|
||||||
}
|
}
|
||||||
|
|
||||||
KSharedConfig::Ptr config() {
|
|
||||||
if (!appletConfig) {
|
|
||||||
QString file = KStandardDirs::locateLocal("appdata",
|
|
||||||
"applets/" + instanceName() + "rc",
|
|
||||||
true);
|
|
||||||
appletConfig = KSharedConfig::openConfig(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return appletConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString instanceName()
|
QString instanceName()
|
||||||
{
|
{
|
||||||
if (!appletDescription.isValid()) {
|
if (!appletDescription.isValid()) {
|
||||||
@ -346,8 +334,6 @@ public:
|
|||||||
//TODO: examine the usage of memory here; there's a pretty large
|
//TODO: examine the usage of memory here; there's a pretty large
|
||||||
// number of members at this point.
|
// number of members at this point.
|
||||||
uint appletId;
|
uint appletId;
|
||||||
KSharedConfig::Ptr globalConfig;
|
|
||||||
KSharedConfig::Ptr appletConfig;
|
|
||||||
KPluginInfo appletDescription;
|
KPluginInfo appletDescription;
|
||||||
Package* package;
|
Package* package;
|
||||||
QList<QObject*> watchedForFocus;
|
QList<QObject*> watchedForFocus;
|
||||||
@ -392,15 +378,6 @@ Applet::Applet(QObject* parentObject, const QVariantList& args)
|
|||||||
Applet::~Applet()
|
Applet::~Applet()
|
||||||
{
|
{
|
||||||
needsFocus(false);
|
needsFocus(false);
|
||||||
|
|
||||||
if (d->appletConfig) {
|
|
||||||
d->appletConfig->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d->globalConfig) {
|
|
||||||
d->globalConfig->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,11 +390,6 @@ uint Applet::id() const
|
|||||||
return d->appletId;
|
return d->appletId;
|
||||||
}
|
}
|
||||||
|
|
||||||
KConfigGroup Applet::config() const
|
|
||||||
{
|
|
||||||
return KConfigGroup(d->config(), "General");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Applet::save(KConfigGroup* group) const
|
void Applet::save(KConfigGroup* group) const
|
||||||
{
|
{
|
||||||
group->writeEntry("plugin", pluginName());
|
group->writeEntry("plugin", pluginName());
|
||||||
@ -436,12 +408,8 @@ void Applet::save(KConfigGroup* group) const
|
|||||||
//group->writeEntry("transform", transformToString(transform()));
|
//group->writeEntry("transform", transformToString(transform()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Containment* c = containment();
|
KConfigGroup appletConfigGroup(group, "Configuration");
|
||||||
if (c) {
|
saveState(&appletConfigGroup);
|
||||||
group->writeEntry("containment", c->id());
|
|
||||||
}
|
|
||||||
|
|
||||||
saveState(group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::saveState(KConfigGroup* group) const
|
void Applet::saveState(KConfigGroup* group) const
|
||||||
@ -452,17 +420,50 @@ void Applet::saveState(KConfigGroup* group) const
|
|||||||
KConfigGroup Applet::config(const QString &group) const
|
KConfigGroup Applet::config(const QString &group) const
|
||||||
{
|
{
|
||||||
KConfigGroup cg = config();
|
KConfigGroup cg = config();
|
||||||
return KConfigGroup(cg.config(), instanceName() + '-' + group);
|
return KConfigGroup(&cg, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
KConfigGroup Applet::config() const
|
||||||
|
{
|
||||||
|
if (d->isContainment) {
|
||||||
|
const Containment *asContainment = qobject_cast<Containment*>(const_cast<Applet*>(this));
|
||||||
|
Q_ASSERT(asContainment);
|
||||||
|
|
||||||
|
KConfigGroup containmentConfig;
|
||||||
|
if (asContainment->corona()) {
|
||||||
|
containmentConfig = KConfigGroup(asContainment->corona()->config(), "Containments");
|
||||||
|
} else {
|
||||||
|
containmentConfig = KConfigGroup(KGlobal::config(), "Containments");
|
||||||
|
}
|
||||||
|
|
||||||
|
containmentConfig = KConfigGroup(&containmentConfig, QString::number(d->appletId));
|
||||||
|
return containmentConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
KConfigGroup appletConfig;
|
||||||
|
if (containment()) {
|
||||||
|
appletConfig = containment()->config();
|
||||||
|
appletConfig = KConfigGroup(&appletConfig, "Applets");
|
||||||
|
} else {
|
||||||
|
kWarning() << "requesting config for" << name() << "without a containment!";
|
||||||
|
appletConfig = KConfigGroup(KGlobal::config(), "Applets");
|
||||||
|
}
|
||||||
|
|
||||||
|
appletConfig = KConfigGroup(&appletConfig, QString::number(d->appletId));
|
||||||
|
return KConfigGroup(&appletConfig, "Configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
KConfigGroup Applet::globalConfig() const
|
KConfigGroup Applet::globalConfig() const
|
||||||
{
|
{
|
||||||
if ( !d->globalConfig ) {
|
KConfigGroup globalAppletConfig;
|
||||||
QString file = KStandardDirs::locateLocal( "config", "plasma_" + globalName() + "rc" );
|
if (containment() && containment()->corona()) {
|
||||||
d->globalConfig = KSharedConfig::openConfig( file );
|
KSharedConfig::Ptr coronaConfig = containment()->corona()->config();
|
||||||
|
globalAppletConfig = KConfigGroup(coronaConfig, "AppletGlobals");
|
||||||
|
} else {
|
||||||
|
globalAppletConfig = KConfigGroup(KGlobal::config(), "AppletGlobals");
|
||||||
}
|
}
|
||||||
|
|
||||||
return KConfigGroup(d->globalConfig, "General");
|
return KConfigGroup(&globalAppletConfig, globalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::destroy()
|
void Applet::destroy()
|
||||||
@ -471,13 +472,7 @@ void Applet::destroy()
|
|||||||
d->configXml->setDefaults();
|
d->configXml->setDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d->appletConfig) {
|
config().deleteGroup();
|
||||||
foreach (const QString& group, d->appletConfig->groupList()) {
|
|
||||||
d->appletConfig->deleteGroup(group);
|
|
||||||
}
|
|
||||||
d->appletConfig = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,6 +744,7 @@ QSizeF Applet::sizeHint() const
|
|||||||
int bottom = 0;
|
int bottom = 0;
|
||||||
|
|
||||||
d->getBorderSize(left, top, right, bottom);
|
d->getBorderSize(left, top, right, bottom);
|
||||||
|
QSizeF borderSize = QSizeF(left + right, top + bottom);
|
||||||
|
|
||||||
//kDebug() << "Applet content size hint: " << contentSizeHint() << "plus our borders" << left << right << top << bottom;
|
//kDebug() << "Applet content size hint: " << contentSizeHint() << "plus our borders" << left << right << top << bottom;
|
||||||
|
|
||||||
@ -890,7 +886,7 @@ QSizeF Applet::contentSizeHint() const
|
|||||||
return layout()->sizeHint();
|
return layout()->sizeHint();
|
||||||
}
|
}
|
||||||
|
|
||||||
return QSizeF(0, 0);
|
return contentSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Applet::globalName() const
|
QString Applet::globalName() const
|
||||||
@ -1179,6 +1175,8 @@ void Applet::setGeometry(const QRectF& geometry)
|
|||||||
if (managingLayout()) {
|
if (managingLayout()) {
|
||||||
managingLayout()->invalidate();
|
managingLayout()->invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateConstraints(Plasma::SizeConstraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPos(geometry.topLeft());
|
setPos(geometry.topLeft());
|
||||||
|
@ -262,12 +262,12 @@ class PLASMA_EXPORT Containment : public Applet
|
|||||||
*/
|
*/
|
||||||
void setFormFactor(Plasma::FormFactor formFactor);
|
void setFormFactor(Plasma::FormFactor formFactor);
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
/**
|
||||||
* Returns the Corona (if any) that this Containment is hosted by
|
* Returns the Corona (if any) that this Containment is hosted by
|
||||||
*/
|
*/
|
||||||
Corona* corona() const;
|
Corona* corona() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent * event);
|
void contextMenuEvent(QGraphicsSceneContextMenuEvent * event);
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
|
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
|
||||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
|
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
|
||||||
|
120
corona.cpp
120
corona.cpp
@ -50,7 +50,9 @@ class Corona::Private
|
|||||||
public:
|
public:
|
||||||
Private()
|
Private()
|
||||||
: immutable(false),
|
: immutable(false),
|
||||||
mimetype("text/x-plasmoidservicename")
|
mimetype("text/x-plasmoidservicename"),
|
||||||
|
configName("plasma-appletsrc"),
|
||||||
|
config(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +67,8 @@ public:
|
|||||||
|
|
||||||
bool immutable;
|
bool immutable;
|
||||||
QString mimetype;
|
QString mimetype;
|
||||||
|
QString configName;
|
||||||
|
KSharedConfigPtr config;
|
||||||
QList<Containment*> containments;
|
QList<Containment*> containments;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,18 +121,18 @@ QString Corona::appletMimeType()
|
|||||||
void Corona::saveApplets(const QString &config) const
|
void Corona::saveApplets(const QString &config) const
|
||||||
{
|
{
|
||||||
KConfig cg(config);
|
KConfig cg(config);
|
||||||
foreach (const QString& group, cg.groupList()) {
|
d->configName = config;
|
||||||
cg.deleteGroup(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList containmentIds;
|
QStringList containmentIds;
|
||||||
|
KConfigGroup containments(&cg, "Containments");
|
||||||
foreach (const Containment *containment, d->containments) {
|
foreach (const Containment *containment, d->containments) {
|
||||||
QString cid = QString::number(containment->id());
|
QString cid = QString::number(containment->id());
|
||||||
KConfigGroup containmentConfig(&cg, cid.append("-containment"));
|
KConfigGroup containmentConfig(&containments, cid);
|
||||||
containment->saveConstraints(&containmentConfig);
|
containment->saveConstraints(&containmentConfig);
|
||||||
containment->save(&containmentConfig);
|
containment->save(&containmentConfig);
|
||||||
|
KConfigGroup applets(&containmentConfig, "Applets");
|
||||||
foreach (const Applet* applet, containment->applets()) {
|
foreach (const Applet* applet, containment->applets()) {
|
||||||
KConfigGroup appletConfig(&cg, QString::number(applet->id()).append("-applet"));
|
KConfigGroup appletConfig(&applets, QString::number(applet->id()));
|
||||||
applet->save(&appletConfig);
|
applet->save(&appletConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,93 +140,75 @@ void Corona::saveApplets(const QString &config) const
|
|||||||
|
|
||||||
void Corona::saveApplets() const
|
void Corona::saveApplets() const
|
||||||
{
|
{
|
||||||
saveApplets("plasma-appletsrc");
|
saveApplets(d->configName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corona::loadApplets(const QString& configname)
|
void Corona::loadApplets(const QString& configName)
|
||||||
{
|
{
|
||||||
clearApplets();
|
clearApplets();
|
||||||
|
d->configName = configName;
|
||||||
|
|
||||||
KConfig config(configname, KConfig::SimpleConfig);
|
KConfig config(configName, KConfig::SimpleConfig);
|
||||||
|
KConfigGroup containments(&config, "Containments");
|
||||||
|
|
||||||
QList<KConfigGroup> applets;
|
foreach (const QString& group, containments.groupList()) {
|
||||||
QHash<int, Containment*> containments;
|
KConfigGroup containmentConfig(&containments, group);
|
||||||
foreach (const QString& group, config.groupList()) {
|
int cid = group.toUInt();
|
||||||
KConfigGroup appletConfig(&config, group);
|
kDebug() << "got a containment in the config, trying to make a" << containmentConfig.readEntry("plugin", QString()) << "from" << group;
|
||||||
if (group.endsWith("containment")) {
|
Containment *c = addContainment(containmentConfig.readEntry("plugin", QString()), QVariantList(),
|
||||||
int cid = group.left(group.indexOf('-')).toUInt();
|
|
||||||
Containment *c = addContainment(appletConfig.readEntry("plugin", QString()), QVariantList(),
|
|
||||||
cid, true);
|
cid, true);
|
||||||
if (c) {
|
|
||||||
addItem(c);
|
|
||||||
containments.insert(c->id(), c);
|
|
||||||
c->loadConstraints(&appletConfig);
|
|
||||||
//kDebug() << "Containment" << c->id() << "geometry is" << c->geometry().toRect() << "config'd with" << appletConfig.name();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// it's an applet, let's grab the containment association
|
|
||||||
//kDebug() << "insert multi" << group;
|
|
||||||
applets.append(appletConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//int maxContainment = containments.size();
|
|
||||||
//kDebug() << "number of applets?" << applets.count();
|
|
||||||
foreach (KConfigGroup cg, applets) {
|
|
||||||
int cid = cg.readEntry("containment", 0);
|
|
||||||
//kDebug() << "trying to load applet " << cg.name() << " in containment " << cid;
|
|
||||||
|
|
||||||
Containment* c = containments.value(cid, 0);
|
|
||||||
|
|
||||||
if (!c) {
|
if (!c) {
|
||||||
kDebug() << "couldn't find containment " << cid << ", skipping this applet";
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//kDebug() << "creating applet " << cg.name() << "in containment" << cid << "at geometry" << cg.readEntry("geometry", QRectF());
|
addItem(c);
|
||||||
int appId = cg.name().left(cg.name().indexOf('-')).toUInt();
|
c->loadConstraints(&containmentConfig);
|
||||||
Applet *applet = c->addApplet(cg.readEntry("plugin", QString()), QVariantList(),
|
//kDebug() << "Containment" << c->id() << "geometry is" << c->geometry().toRect() << "config'd with" << appletConfig.name();
|
||||||
appId, cg.readEntry("geometry", QRectF()), true);
|
KConfigGroup applets(&containmentConfig, "Applets");
|
||||||
|
|
||||||
|
foreach (const QString &appletGroup, applets.groupList()) {
|
||||||
|
kDebug() << "reading from applet group" << appletGroup;
|
||||||
|
int appId = appletGroup.toUInt();
|
||||||
|
KConfigGroup appletConfig(&applets, appletGroup);
|
||||||
|
kDebug() << "the name is" << appletConfig.name();
|
||||||
|
Applet *applet = c->addApplet(appletConfig.readEntry("plugin", QString()), QVariantList(),
|
||||||
|
appId, appletConfig.readEntry("geometry", QRectF()), true);
|
||||||
|
Q_UNUSED(applet)
|
||||||
|
// FIXME: the transform does not stick; it gets set then almost immediately reset.
|
||||||
|
// find out why and then reenable this
|
||||||
|
/*
|
||||||
QList<qreal> m = cg.readEntry("transform", QList<qreal>());
|
QList<qreal> m = cg.readEntry("transform", QList<qreal>());
|
||||||
if (m.count() == 9) {
|
if (m.count() == 9) {
|
||||||
QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
|
QTransform t(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
|
||||||
// FIXME: the transform does not stick; it gets set then almost immediately reset.
|
applet->setTransform(t);
|
||||||
// find out why and then reenable this
|
|
||||||
//applet->setTransform(t);
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Containment* c, containments) {
|
|
||||||
QString cid = QString::number(c->id());
|
|
||||||
KConfigGroup containmentConfig(&config, cid.append("-containment"));
|
|
||||||
c->setImmutable(containmentConfig.isImmutable());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d->containments.count() < 1) {
|
if (d->containments.count() < 1) {
|
||||||
loadDefaultSetup();
|
loadDefaultSetup();
|
||||||
} else {
|
} else {
|
||||||
foreach (Containment* containment, d->containments) {
|
foreach (Containment* containment, d->containments) {
|
||||||
|
QString cid = QString::number(containment->id());
|
||||||
containment->init();
|
containment->init();
|
||||||
|
KConfigGroup containmentConfig(&containments, cid);
|
||||||
|
containment->setImmutable(containmentConfig.isImmutable());
|
||||||
|
|
||||||
foreach(Applet* applet, containment->applets()) {
|
foreach(Applet* applet, containment->applets()) {
|
||||||
applet->init();
|
applet->init();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Containment* containment, d->containments) {
|
|
||||||
// we need to manually flush the constraints changes
|
|
||||||
// because we may not get back to the event loop before
|
|
||||||
// view set up
|
|
||||||
containment->flushUpdatedConstraints();
|
containment->flushUpdatedConstraints();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setImmutable(config.isImmutable());
|
setImmutable(config.isImmutable());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corona::loadApplets()
|
void Corona::loadApplets()
|
||||||
{
|
{
|
||||||
loadApplets("plasma-appletsrc");
|
loadApplets(d->configName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corona::loadDefaultSetup()
|
void Corona::loadDefaultSetup()
|
||||||
@ -300,6 +286,15 @@ void Corona::clearApplets()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KSharedConfigPtr Corona::config()
|
||||||
|
{
|
||||||
|
if (!d->config) {
|
||||||
|
d->config = KSharedConfig::openConfig(d->configName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d->config;
|
||||||
|
}
|
||||||
|
|
||||||
Containment* Corona::addContainment(const QString& name, const QVariantList& args, uint id, bool delayedInit)
|
Containment* Corona::addContainment(const QString& name, const QVariantList& args, uint id, bool delayedInit)
|
||||||
{
|
{
|
||||||
QString pluginName = name;
|
QString pluginName = name;
|
||||||
@ -344,6 +339,17 @@ Containment* Corona::addContainment(const QString& name, const QVariantList& arg
|
|||||||
return containment;
|
return containment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Corona::destroyContainment(Containment *c)
|
||||||
|
{
|
||||||
|
if (!d->containments.contains(c)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->containments.removeAll(c);
|
||||||
|
c->config().deleteGroup();
|
||||||
|
c->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
Applet* Corona::addApplet(const QString& name, const QVariantList& args, uint id, const QRectF& geometry)
|
Applet* Corona::addApplet(const QString& name, const QVariantList& args, uint id, const QRectF& geometry)
|
||||||
{
|
{
|
||||||
if (d->containments.size() < 1) {
|
if (d->containments.size() < 1) {
|
||||||
|
10
corona.h
10
corona.h
@ -93,6 +93,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void clearApplets();
|
void clearApplets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the config file used to store the configuration for this Corona
|
||||||
|
*/
|
||||||
|
KSharedConfig::Ptr config();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
/**
|
/**
|
||||||
* Load applets from the default config file
|
* Load applets from the default config file
|
||||||
@ -137,6 +142,11 @@ public Q_SLOTS:
|
|||||||
Containment* addContainment(const QString& name, const QVariantList& args = QVariantList(),
|
Containment* addContainment(const QString& name, const QVariantList& args = QVariantList(),
|
||||||
uint id = 0, bool delayInit = false);
|
uint id = 0, bool delayInit = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a given containment from the corona
|
||||||
|
*/
|
||||||
|
void destroyContainment(Containment *containment);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Containment, if any, for a given physical screen
|
* Returns the Containment, if any, for a given physical screen
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user