use the ctivity class and Kactivities

just building, issues with linking with kactivities, not used yet
This commit is contained in:
Marco Martin 2013-10-16 21:01:39 +02:00
parent 26c5d923fb
commit 800e5ec91c
11 changed files with 972 additions and 0 deletions

View File

@ -179,6 +179,7 @@ void Containment::restore(KConfigGroup &group)
setLocation((Plasma::Types::Location)group.readEntry("location", (int)d->location)); setLocation((Plasma::Types::Location)group.readEntry("location", (int)d->location));
setFormFactor((Plasma::Types::FormFactor)group.readEntry("formfactor", (int)d->formFactor)); setFormFactor((Plasma::Types::FormFactor)group.readEntry("formfactor", (int)d->formFactor));
d->lastScreen = group.readEntry("lastScreen", d->lastScreen);
setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper)); setWallpaper(group.readEntry("wallpaperplugin", ContainmentPrivate::defaultWallpaper));
@ -245,6 +246,7 @@ void Containment::save(KConfigGroup &g) const
} }
group.writeEntry("screen", d->screen); group.writeEntry("screen", d->screen);
group.writeEntry("lastScreen", d->lastScreen);
group.writeEntry("formfactor", (int)d->formFactor); group.writeEntry("formfactor", (int)d->formFactor);
group.writeEntry("location", (int)d->location); group.writeEntry("location", (int)d->location);
group.writeEntry("activityId", d->activityId); group.writeEntry("activityId", d->activityId);
@ -452,6 +454,11 @@ int Containment::screen() const
return d->screen; return d->screen;
} }
int Containment::lastScreen() const
{
return d->lastScreen;
}
void Containment::setDrawWallpaper(bool drawWallpaper) void Containment::setDrawWallpaper(bool drawWallpaper)
{ {
if (d->drawWallpaper == drawWallpaper) { if (d->drawWallpaper == drawWallpaper) {

View File

@ -148,6 +148,13 @@ class PLASMA_EXPORT Containment : public Applet
*/ */
int screen() const; int screen() const;
/**
* @return the last screen number this containment had
* only returns -1 if it's never ever been on a screen
* @since 4.5
*/
int lastScreen() const;
/** /**
* @reimp * @reimp
* @sa Applet::save(KConfigGroup &) * @sa Applet::save(KConfigGroup &)

View File

@ -175,6 +175,22 @@ Containment *Corona::containmentForScreen(int screen) const
return 0; return 0;
} }
Containment *Corona::containmentForScreen(int screen, const QString &defaultPluginIfNonExistent)
{
Containment *containment = containmentForScreen(screen);
if (!containment && !defaultPluginIfNonExistent.isEmpty()) {
// screen requests are allowed to bypass immutability
if (screen >= 0 && screen < numScreens()) {
containment = d->addContainment(defaultPluginIfNonExistent, QVariantList(), 0);
if (containment) {
containment->setScreen(screen);
}
}
}
return containment;
}
QList<Containment*> Corona::containments() const QList<Containment*> Corona::containments() const
{ {
return d->containments; return d->containments;

View File

@ -99,6 +99,17 @@ public:
*/ */
Containment *containmentForScreen(int screen) const; Containment *containmentForScreen(int screen) const;
/**
* Returns the Containment for a given physical screen and desktop, creating one
* if none exists
*
* @param screen number of the physical screen to locate
* @param defaultPluginIfNonExistent the plugin to load by default; "null" is an empty
* Containment and "default" creates the default plugin
* @since 4.6
*/
Containment *containmentForScreen(int screen, const QString &defaultPluginIfNonExistent);
/** /**
* Returns the number of screens available to plasma. * Returns the number of screens available to plasma.
* Subclasses should override this method as the default * Subclasses should override this method as the default

View File

@ -132,6 +132,10 @@ void ContainmentPrivate::setScreen(int newScreen)
#endif #endif
KConfigGroup c = q->config(); KConfigGroup c = q->config();
c.writeEntry("screen", screen); c.writeEntry("screen", screen);
if (newScreen != -1) {
lastScreen = newScreen;
c.writeEntry("lastScreen", lastScreen);
}
emit q->configNeedsSaving(); emit q->configNeedsSaving();
emit q->screenChanged(oldScreen, newScreen, q); emit q->screenChanged(oldScreen, newScreen, q);
} }

View File

@ -50,6 +50,7 @@ public:
formFactor(Types::Planar), formFactor(Types::Planar),
location(Types::Floating), location(Types::Floating),
screen(-1), // no screen screen(-1), // no screen
lastScreen(-1), // never had a screen
type(Plasma::Types::NoContainmentType), type(Plasma::Types::NoContainmentType),
drawWallpaper(false) drawWallpaper(false)
{ {
@ -103,6 +104,7 @@ public:
QString wallpaper; QString wallpaper;
QHash<QString, ContainmentActions*> localActionPlugins; QHash<QString, ContainmentActions*> localActionPlugins;
int screen; int screen;
int lastScreen;
QString activityId; QString activityId;
Types::ContainmentType type; Types::ContainmentType type;
bool drawWallpaper : 1; bool drawWallpaper : 1;

View File

@ -43,10 +43,12 @@ set(widgetexplorer_SRC
) )
add_executable(plasma-shell add_executable(plasma-shell
activity.cpp
main.cpp main.cpp
containmentconfigview.cpp containmentconfigview.cpp
currentcontainmentactionsmodel.cpp currentcontainmentactionsmodel.cpp
desktopview.cpp desktopview.cpp
kidenticongenerator.cpp
panelview.cpp panelview.cpp
panelconfigview.cpp panelconfigview.cpp
panelshadows.cpp panelshadows.cpp
@ -75,6 +77,8 @@ target_link_libraries(plasma-shell
KF5::KDeclarative KF5::KDeclarative
KF5::KI18n KF5::KI18n
KF5::XmlGui KF5::XmlGui
KF5::KIconThemes
/opt/kde5/lib64/libkactivities.so
) )
target_include_directories(plasma-shell PRIVATE "${CMAKE_BINARY_DIR}") target_include_directories(plasma-shell PRIVATE "${CMAKE_BINARY_DIR}")

401
src/shell/activity.cpp Normal file
View File

@ -0,0 +1,401 @@
/*
* Copyright 2010 Chani Armitage <chani@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "shellcorona.h"
#include "kidenticongenerator.h"
#include <QDebug>
#include <QPixmap>
#include <QString>
#include <QSize>
#include <QFile>
#include <kconfig.h>
#include <kicon.h>
#include <kstandarddirs.h>
#include <kwindowsystem.h>
#include <Plasma/Containment>
#include <Plasma/Corona>
#include <kactivities/controller.h>
#include <kactivities/consumer.h>
#include "activity.h"
Activity::Activity(const QString &id, Plasma::Corona *parent)
: QObject(parent),
m_id(id),
m_plugin("default"),
m_info(new KActivities::Info(id, this)),
m_activityConsumer(new KActivities::Consumer(this)),
m_corona(parent),
m_current(false)
{
m_name = m_info->name();
m_icon = m_info->icon();
connect(m_info, SIGNAL(infoChanged()), this, SLOT(activityChanged()));
connect(m_info, SIGNAL(stateChanged(KActivities::Info::State)), this, SLOT(activityStateChanged(KActivities::Info::State)));
connect(m_info, SIGNAL(started()), this, SLOT(opened()));
connect(m_info, SIGNAL(stopped()), this, SLOT(closed()));
connect(m_info, SIGNAL(removed()), this, SLOT(removed()));
connect(m_activityConsumer, SIGNAL(currentActivityChanged(QString)), this, SLOT(checkIfCurrent()));
checkIfCurrent();
//find your containments
foreach (Plasma::Containment *cont, m_corona->containments()) {
if (cont->containmentType() == Plasma::Types::DesktopContainment ||
cont->containmentType() == Plasma::Types::CustomContainment) {
insertContainment(cont);
}
}
//qDebug() << m_containments.size();
}
Activity::~Activity()
{
}
void Activity::activityChanged()
{
setName(m_info->name());
setIcon(m_info->icon());
}
void Activity::activityStateChanged(KActivities::Info::State state)
{
Q_UNUSED(state)
emit stateChanged();
}
QString Activity::id()
{
return m_id;
}
QString Activity::name()
{
return m_name;
}
QPixmap Activity::pixmap(const QSize &size)
{
if (m_info->isValid() && !m_info->icon().isEmpty()) {
return KIcon(m_info->icon()).pixmap(size);
} else {
return KIdenticonGenerator::self()->generatePixmap(size.width(), m_id);
}
}
bool Activity::isCurrent()
{
return m_current;
//TODO maybe plasmaapp should cache the current activity to reduce dbus calls?
}
void Activity::checkIfCurrent()
{
const bool current = m_id == m_activityConsumer->currentActivity();
if (current != m_current) {
m_current = current;
emit currentStatusChanged();
}
}
KActivities::Info::State Activity::state()
{
return m_info->state();
}
void Activity::remove()
{
KActivities::Controller().removeActivity(m_id);
}
void Activity::removed()
{
if (! m_containments.isEmpty()) {
//FIXME only m_corona has authority to remove properly
qDebug() << "!!!!! if your widgets are locked you've hit a BUG now";
foreach (Plasma::Containment *c, m_containments) {
c->destroy();
}
}
const QString name = "activities/" + m_id;
QFile::remove(KStandardDirs::locateLocal("appdata", name));
}
Plasma::Containment* Activity::containmentForScreen(int screen)
{
Plasma::Containment *containment = m_containments.value(screen);
if (!containment) {
qDebug() << "adding containment for" << screen;
// first look to see if there are any unnasigned containments that are candidates for
// being sucked into this Activity
foreach (Plasma::Containment *c, m_corona->containments()) {
if ((c->containmentType() == Plasma::Types::DesktopContainment ||
c->containmentType() == Plasma::Types::CustomContainment) &&
c->activity().isEmpty() &&
m_containments.key(c, -2) == -2) {
containment = c;
containment->setScreen(screen);
break;
}
}
if (!containment) {
// we ask for the containment for the screen with a default plugin, because
// this allows the corona to either grab one for us that already exists matching
// screen, or create a new one. this also works regardless of immutability
containment = m_corona->containmentForScreen(screen, m_plugin);
if (!containment || !containment->activity().isEmpty()) {
// possibly a plugin failure, let's go for the default
containment = m_corona->containmentForScreen(screen, "default");
}
//we don't want to steal contaiments from other activities
if (!containment) {
// we failed to even get the default; we're screwed.
Q_ASSERT(false);
return 0;
}
if (!containment->activity().isEmpty() &&
containment->activity() != m_id) {
// we got a containment, but it belongs to some other activity; let's unassign it
// from a screen and grab a new one
containment->setScreen(-1);
containment = m_corona->containmentForScreen(screen, m_plugin);
if (!containment) {
// possibly a plugin failure, let's go for the default
containment = m_corona->containmentForScreen(screen, "default");
}
if (containment) {
containment->setScreen(screen);
}
}
}
if (containment) {
insertContainment(containment, screen);
m_corona->requestConfigSync();
}
} else if (containment->screen() != screen) {
// ensure the containment _also_ knows which screen we think it is on;
// can happen when swapping between activities without stopping them first
containment->setScreen(screen);
}
return containment;
}
void Activity::activate()
{
KActivities::Controller().setCurrentActivity(m_id);
}
void Activity::ensureActive()
{
if (m_containments.isEmpty()) {
opened();
}
checkScreens();
}
void Activity::checkScreens()
{
//ensure there's a containment for every screen & desktop.
int numScreens = m_corona->numScreens();
for (int screen = 0; screen < numScreens; ++screen) {
containmentForScreen(screen);
}
}
void Activity::setName(const QString &name)
{
if (m_name == name) {
return;
}
m_name = name;
}
void Activity::setIcon(const QString &icon)
{
if (m_icon == icon) {
return;
}
m_icon = icon;
}
void Activity::save(KConfig &external)
{
foreach (const QString &group, external.groupList()) {
KConfigGroup cg(&external, group);
cg.deleteGroup();
}
//TODO: multi-screen saving/restoring, where each screen can be
// independently restored: put each screen's containments into a
// different group, e.g. [Screens][0][Containments], [Screens][1][Containments], etc
KConfigGroup dest(&external, "Containments");
KConfigGroup dummy;
foreach (Plasma::Containment *c, m_containments) {
c->save(dummy);
KConfigGroup group(&dest, QString::number(c->id()));
c->config().copyTo(&group);
}
external.sync();
}
void Activity::close()
{
KActivities::Controller().stopActivity(m_id);
}
void Activity::closed()
{
const QString name = "activities/" + m_id;
KConfig external(name, KConfig::SimpleConfig, QStandardPaths::GenericDataLocation);
//passing an empty string for the group name turns a kconfig into a kconfiggroup
KConfigGroup group = external.group(QString());
m_corona->exportLayout(group, m_containments.values());
//hrm, shouldn't the containments' deleted signals have done this for us?
if (!m_containments.isEmpty()) {
qDebug() << "isn't close supposed to *remove* containments??";
m_containments.clear();
}
}
void Activity::replaceContainment(Plasma::Containment* containment)
{
insertContainment(containment, true);
}
void Activity::insertContainment(Plasma::Containment* cont, bool force)
{
int screen = cont->lastScreen();
qDebug() << screen;
if (screen == -1) {
//the migration can't set lastScreen, so maybe we need to assign the containment here
qDebug() << "found a lost one";
screen = 0;
}
if (!force && m_containments.contains(screen)) {
//this almost certainly means someone has been meddling where they shouldn't
//but we should protect them from harm anyways
qDebug() << "@!@!@!@!@!@@@@rejecting containment!!!";
cont->setActivity(QString());
return;
}
insertContainment(cont, screen);
}
void Activity::insertContainment(Plasma::Containment* containment, int screen)
{
//ensure it's hooked up
containment->setActivity(m_id);
m_containments.insert(screen, containment);
connect(containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed(QObject*)));
}
void Activity::containmentDestroyed(QObject *object)
{
//safe here because we are not accessing it
Plasma::Containment *deletedCont = static_cast<Plasma::Containment *>(object);
QHash<int, Plasma::Containment*>::iterator i;
for (i = m_containments.begin(); i != m_containments.end(); ++i) {
Plasma::Containment *cont = i.value();
if (cont == deletedCont) {
m_containments.remove(i.key());
break;
}
}
}
void Activity::open()
{
KActivities::Controller().startActivity(m_id);
}
void Activity::opened()
{
if (!m_containments.isEmpty()) {
qDebug() << "already open!";
return;
}
const QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString("/activities/" + m_id);
qDebug() << "&&&&&&&&&&&&&&&" << fileName;
if (QFile::exists(fileName)) {
{
KConfig external(fileName, KConfig::SimpleConfig);
foreach (Plasma::Containment *newContainment, m_corona->importLayout(external.group(QByteArray()))) {
insertContainment(newContainment);
//ensure it's hooked up (if something odd happened we don't want orphan containments)
newContainment->setActivity(m_id);
}
}
QFile::remove(fileName);
}
if (m_containments.isEmpty()) {
//TODO check if we need more for screens/desktops
qDebug() << "open failed (bad file?). creating new containment";
checkScreens();
}
m_corona->requireConfigSync();
}
void Activity::setDefaultPlugin(const QString &plugin)
{
m_plugin = plugin;
//FIXME save&restore this setting
}
const KActivities::Info * Activity::info() const
{
return m_info;
}
#include "activity.moc"
// vim: sw=4 sts=4 et tw=100

170
src/shell/activity.h Normal file
View File

@ -0,0 +1,170 @@
/*
* Copyright 2010 Chani Armitage <chani@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef ACTIVITY_H
#define ACTIVITY_H
#include <QObject>
#include <QHash>
#include <kactivities/info.h>
class QSize;
class QString;
class QPixmap;
class KConfig;
namespace KActivities
{
class Consumer;
} // namespace KActivities
namespace Plasma
{
class Containment;
class Corona;
} // namespace Plasma
class DesktopCorona;
/**
* This class represents one activity.
* an activity has an ID and a name, from nepomuk.
* it also is associated with one or more containments.
*
* do NOT construct these yourself; use DesktopCorona::activity()
*/
class Activity : public QObject
{
Q_OBJECT
public:
Activity(const QString &id, Plasma::Corona *parent = 0);
~Activity();
QString id();
QString name();
QPixmap pixmap(const QSize &size); //FIXME do we want diff. sizes? updates?
enum State {
Invalid = KActivities::Info::Invalid,
Running = KActivities::Info::Running,
Starting = KActivities::Info::Starting,
Stopped = KActivities::Info::Stopped,
Stopping = KActivities::Info::Stopping,
PreCreation = 32
};
/**
* whether this is the currently active activity
*/
bool isCurrent();
/**
* state of the activity
*/
KActivities::Info::State state();
/**
* save (copy) the activity out to an @p external config
*/
void save(KConfig &external);
/**
* return the containment that belongs on @p screen and @p desktop
* or null if none exists
*/
Plasma::Containment* containmentForScreen(int screen);
/**
* make this activity's containments the active ones, loading them if necessary
*/
void ensureActive();
/**
* set the plugin to use when creating new containments
*/
void setDefaultPlugin(const QString &plugin);
/**
* @returns the info object for this activity
*/
const KActivities::Info * info() const;
Q_SIGNALS:
void infoChanged();
void stateChanged();
void currentStatusChanged();
public Q_SLOTS:
void setName(const QString &name);
void setIcon(const QString &icon);
/**
* delete the activity forever
*/
void remove();
/**
* make this activity the current activity
*/
void activate();
/**
* save and remove all our containments
*/
void close();
/**
* load the saved containment(s) for this activity
*/
void open();
/**
* forcibly insert a containment, replacing the one on its screen/desktop
*/
void replaceContainment(Plasma::Containment* containment);
private Q_SLOTS:
void containmentDestroyed(QObject *object);
void activityChanged();
void activityStateChanged(KActivities::Info::State);
void checkIfCurrent();
void removed();
void opened();
void closed();
private:
void insertContainment(Plasma::Containment* cont, bool force=false);
void insertContainment(Plasma::Containment* containment, int screen);
void checkScreens();
QString m_id;
QString m_name;
QString m_icon;
QString m_plugin;
QHash<int, Plasma::Containment*> m_containments;
KActivities::Info *m_info;
KActivities::Consumer *m_activityConsumer;
Plasma::Corona *m_corona;
bool m_current;
};
#endif

View File

@ -0,0 +1,302 @@
/*
* Copyright 2010 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library/Lesser General Public License
* version 2, or (at your option) any later version, as published by the
* Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library/Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kidenticongenerator.h"
#include <QHash>
#include <QPainter>
#include <QDebug>
#include <QCryptographicHash>
#include <kiconeffect.h>
#include <Plasma/Svg>
#include <Plasma/Theme>
#define VALUE_LIMIT_UP 192
#define VALUE_LIMIT_DOWN 64
class KIdenticonGenerator::Private {
public:
QPixmap generatePattern(int size, quint32 hash, QIcon::Mode mode);
QString elementName(const QString & element, QIcon::Mode mode);
QColor colorForHash(quint32 hash) const;
quint32 hash(const QString & data);
static KIdenticonGenerator * instance;
Plasma::Theme *theme;
Plasma::Svg shapes;
Plasma::Svg svg;
};
QPixmap KIdenticonGenerator::Private::generatePattern(int size, quint32 hash, QIcon::Mode mode)
{
// We are dividing the pixmap into 9 blocks - 3 x 3
int blockSize = size / 3;
// pulling parts of the hash
quint32 tmp = hash;
quint8 block[4];
block[0] = tmp & 31; tmp >>= 5;
block[1] = tmp & 31; tmp >>= 5;
block[2] = tmp & 31; tmp >>= 5;
// Painting alpha channel
QPixmap pixmapAlpha(size, size);
pixmapAlpha.fill(Qt::black);
QPainter painterAlpha(& pixmapAlpha);
QRectF rect(0, 0, blockSize + 0.5, blockSize + 0.5);
for (int i = 0; i < 4; i++) {
// Painting the corner item
rect.moveTopLeft(QPoint(0, 0));
shapes.paint(& painterAlpha, rect, "shape" + QString::number(block[0] + 1));
// Painting side item
rect.moveTopLeft(QPoint(blockSize, 0));
shapes.paint(& painterAlpha, rect, "shape" + QString::number(block[1] + 1));
// Rotating the canvas to paint other edges
painterAlpha.translate(size, 0);
painterAlpha.rotate(90);
}
// Painting center item
rect.moveTopLeft(QPoint(blockSize, blockSize));
shapes.paint(& painterAlpha, rect, "shape" + QString::number(block[2] + 1));
painterAlpha.end();
// Painting final pixmap
QPixmap pixmapResult(size, size);
pixmapResult.fill(Qt::transparent);
// QRadialGradient gradient(50, 50, 100);
// gradient.setColorAt(0, color.lighter());
// gradient.setColorAt(1, color.darker());
QPainter resultPainter(& pixmapResult);
// resultPainter.fillRect(0, 0, size, size, gradient);
svg.paint(& resultPainter, QRect(0, 0, size, size), elementName("content", mode));
resultPainter.end();
pixmapResult.setAlphaChannel(pixmapAlpha);
// QImage itmp = pixmapResult.toImage();
// KIconEffect::colorize(itmp, colorForHash(hash), 1.0);
// pixmapResult = pixmapResult.fromImage(itmp);
return pixmapResult;
}
QColor KIdenticonGenerator::Private::colorForHash(quint32 hash) const
{
// Color is chosen according to hash
QColor color;
// Getting the value from color svg, but we must restrain it to
// values in range from VALUE_LIMIT_DOWN to VALUE_LIMIT_UP
int value = theme->color(Plasma::Theme::TextColor).value();
if (value < VALUE_LIMIT_DOWN) {
value = VALUE_LIMIT_DOWN;
} else if (value > VALUE_LIMIT_UP) {
value = VALUE_LIMIT_UP;
}
color.setHsv(
hash % 359 + 1, // hue depending on hash
250, // high saturation level
value
);
return color;
}
QString KIdenticonGenerator::Private::elementName(const QString & element, QIcon::Mode mode)
{
QString prefix;
switch (mode) {
case QIcon::Normal:
prefix = "normal-";
break;
case QIcon::Disabled:
prefix = "disabled-";
break;
case QIcon::Selected:
prefix = "selected-";
break;
case QIcon::Active:
prefix = "active-";
break;
default:
break;
}
if (svg.hasElement(prefix + element)) {
return prefix + element;
} else {
return element;
}
}
quint32 KIdenticonGenerator::Private::hash(const QString & data)
{
// qHash function doesn't give random enough results
// and gives similar hashes for similar strings.
QByteArray bytes = QCryptographicHash::hash(data.toUtf8(), QCryptographicHash::Md5);
// Generating hash
quint32 hash = 0;
char * hashBytes = (char *) & hash;
for (int i = 0; i < bytes.size(); i++) {
// Using XOR for mixing the bytes because
// it is fast and cryptographically safe
// (more than enough for our use-case)
hashBytes[i % 4] ^= bytes.at(i);
}
return hash;
}
KIdenticonGenerator * KIdenticonGenerator::Private::instance = NULL;
KIdenticonGenerator * KIdenticonGenerator::self()
{
if (!Private::instance) {
Private::instance = new KIdenticonGenerator();
}
return Private::instance;
}
KIdenticonGenerator::KIdenticonGenerator()
: d(new Private())
{
d->theme = new Plasma::Theme(0);
// loading SVGs
d->shapes.setImagePath("widgets/identiconshapes");
d->shapes.setContainsMultipleImages(true);
d->svg.setImagePath("widgets/identiconsvg");
d->svg.setContainsMultipleImages(true);
}
KIdenticonGenerator::~KIdenticonGenerator()
{
delete d->theme;
}
#define generateIconModes( PARAM ) \
for (int omode = QIcon::Normal; omode <= QIcon::Selected; omode++) { \
QIcon::Mode mode = (QIcon::Mode)omode; \
result.addPixmap(generatePixmap(size, PARAM, mode), mode); \
}
QIcon KIdenticonGenerator::generate(int size, quint32 hash)
{
QIcon result;
generateIconModes(hash);
return result;
}
QIcon KIdenticonGenerator::generate(int size, const QString & data)
{
QIcon result;
generateIconModes(data);
return result;
}
QIcon KIdenticonGenerator::generate(int size, const QIcon & icon)
{
QIcon result;
generateIconModes(icon);
return result;
}
QPixmap KIdenticonGenerator::generatePixmap(int size, QString id, QIcon::Mode mode)
{
return generatePixmap(size, d->hash(id), mode);
}
QPixmap KIdenticonGenerator::generatePixmap(int size, quint32 hash, QIcon::Mode mode)
{
QPixmap pixmap(size, size);
pixmap.fill(Qt::transparent);
// Painting background and the pattern
{
QPainter painter(& pixmap);
d->svg.paint(& painter, QRect(0, 0, size, size), d->elementName("background", mode));
painter.drawPixmap(0, 0, d->generatePattern(size, hash, mode));
painter.end();
}
// coloring the painted image
QImage itmp = pixmap.toImage();
KIconEffect::colorize(itmp, d->colorForHash(hash), 1.0);
if (mode == QIcon::Disabled) {
KIconEffect::toGray(itmp, 0.7);
}
pixmap = pixmap.fromImage(itmp);
// Drawing the overlay
{
QPainter painter(& pixmap);
d->svg.paint(& painter, QRect(0, 0, size, size), d->elementName("overlay", mode));
}
return pixmap;
}
QPixmap KIdenticonGenerator::generatePixmap(int size, const QIcon & icon, QIcon::Mode mode)
{
QPixmap pixmap(size, size);
pixmap.fill(Qt::transparent);
QRect paintRect(0, 0, size, size);
// Painting background and the pattern
QPainter painter(& pixmap);
d->svg.paint(& painter, QRect(0, 0, size, size), d->elementName("background", mode));
icon.paint(& painter, paintRect, Qt::AlignCenter, mode);
painter.end();
return pixmap;
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2010 Ivan Cukic <ivan.cukic(at)kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library/Lesser General Public License
* version 2, or (at your option) any later version, as published by the
* Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library/Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIDENTICONGENERATOR_H
#define KIDENTICONGENERATOR_H
#include <QPixmap>
#include <QIcon>
#include <Plasma/Svg>
class KIdenticonGenerator {
public:
static KIdenticonGenerator * self();
~KIdenticonGenerator();
QPixmap generatePixmap(int size, QString id, QIcon::Mode mode = QIcon::Normal);
QPixmap generatePixmap(int size, quint32 hash, QIcon::Mode mode = QIcon::Normal);
QPixmap generatePixmap(int size, const QIcon & icon, QIcon::Mode mode = QIcon::Normal);
QIcon generate(int size, const QString & data);
QIcon generate(int size, quint32 hash);
QIcon generate(int size, const QIcon & icon);
private:
KIdenticonGenerator();
class Private;
Private * const d;
};
#endif // KIDENTICONGENERATOR_H