You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
381 lines
12 KiB
381 lines
12 KiB
/* |
|
* Copyright 2011 by Aaron Seigo <aseigo@kde.org> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU Library General Public License 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 "shadows.h" |
|
|
|
#include <QWidget> |
|
#include <QPainter> |
|
|
|
#ifdef Q_WS_X11 |
|
#include <QX11Info> |
|
#include <X11/Xatom.h> |
|
#include <X11/Xlib.h> |
|
#include <fixx11h.h> |
|
#endif |
|
|
|
#include <kdebug.h> |
|
#include <kglobal.h> |
|
|
|
class Shadows::Private |
|
{ |
|
public: |
|
Private(Shadows *shadows) |
|
: q(shadows), |
|
m_managePixmaps(false) |
|
{ |
|
} |
|
|
|
~Private() |
|
{ |
|
clearPixmaps(); |
|
} |
|
|
|
void clearPixmaps(); |
|
void setupPixmaps(); |
|
void initPixmap(const QString &element); |
|
QPixmap initEmptyPixmap(const QSize &size); |
|
void updateShadow(const QWidget *window, Plasma::FrameSvg::EnabledBorders); |
|
void clearShadow(const QWidget *window); |
|
void updateShadows(); |
|
void windowDestroyed(QObject *deletedObject); |
|
void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); |
|
|
|
Shadows *q; |
|
QList<QPixmap> m_shadowPixmaps; |
|
|
|
QPixmap m_emptyCornerPix; |
|
QPixmap m_emptyCornerLeftPix; |
|
QPixmap m_emptyCornerTopPix; |
|
QPixmap m_emptyCornerRightPix; |
|
QPixmap m_emptyCornerBottomPix; |
|
QPixmap m_emptyVerticalPix; |
|
QPixmap m_emptyHorizontalPix; |
|
|
|
QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data; |
|
QHash<const QWidget *, Plasma::FrameSvg::EnabledBorders> m_windows; |
|
bool m_managePixmaps; |
|
}; |
|
|
|
class ShadowsSingleton |
|
{ |
|
public: |
|
ShadowsSingleton() |
|
{ |
|
} |
|
|
|
Shadows self; |
|
}; |
|
|
|
K_GLOBAL_STATIC(ShadowsSingleton, privateShadowsSelf) |
|
|
|
Shadows::Shadows(QObject *parent, const QString &prefix) |
|
: Plasma::Svg(parent), |
|
d(new Private(this)) |
|
{ |
|
setImagePath(prefix); |
|
connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateShadows())); |
|
} |
|
|
|
Shadows *Shadows::self() |
|
{ |
|
return &privateShadowsSelf->self; |
|
} |
|
|
|
void Shadows::addWindow(const QWidget *window, Plasma::FrameSvg::EnabledBorders enabledBorders) |
|
{ |
|
if (!window || !window->isWindow()) { |
|
return; |
|
} |
|
|
|
d->m_windows[window] = enabledBorders; |
|
d->updateShadow(window, enabledBorders); |
|
connect(window, SIGNAL(destroyed(QObject*)), |
|
this, SLOT(windowDestroyed(QObject*)), Qt::UniqueConnection); |
|
} |
|
|
|
void Shadows::removeWindow(const QWidget *window) |
|
{ |
|
if (!d->m_windows.contains(window)) { |
|
return; |
|
} |
|
|
|
d->m_windows.remove(window); |
|
disconnect(window, 0, this, 0); |
|
d->clearShadow(window); |
|
|
|
if (d->m_windows.isEmpty()) { |
|
d->clearPixmaps(); |
|
} |
|
} |
|
|
|
void Shadows::Private::windowDestroyed(QObject *deletedObject) |
|
{ |
|
m_windows.remove(static_cast<QWidget *>(deletedObject)); |
|
|
|
if (m_windows.isEmpty()) { |
|
clearPixmaps(); |
|
} |
|
} |
|
|
|
void Shadows::Private::updateShadows() |
|
{ |
|
setupPixmaps(); |
|
QHash<const QWidget *, Plasma::FrameSvg::EnabledBorders>::const_iterator i; |
|
for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) { |
|
updateShadow(i.key(), i.value()); |
|
} |
|
} |
|
|
|
void Shadows::Private::initPixmap(const QString &element) |
|
{ |
|
#ifdef Q_WS_X11 |
|
QPixmap pix = q->pixmap(element); |
|
if (!pix.isNull() && pix.handle() == 0) { |
|
Pixmap xPix = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), pix.width(), pix.height(), 32); |
|
QPixmap tempPix = QPixmap::fromX11Pixmap(xPix, QPixmap::ExplicitlyShared); |
|
tempPix.fill(Qt::transparent); |
|
QPainter p(&tempPix); |
|
p.drawPixmap(QPoint(0, 0), pix); |
|
m_shadowPixmaps << tempPix; |
|
m_managePixmaps = true; |
|
} else { |
|
m_shadowPixmaps << pix; |
|
} |
|
#endif |
|
} |
|
|
|
QPixmap Shadows::Private::initEmptyPixmap(const QSize &size) |
|
{ |
|
Pixmap emptyXPix = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), size.width(), size.height(), 32); |
|
QPixmap tempEmptyPix = QPixmap::fromX11Pixmap(emptyXPix, QPixmap::ExplicitlyShared); |
|
tempEmptyPix.fill(Qt::transparent); |
|
return tempEmptyPix; |
|
} |
|
|
|
void Shadows::Private::setupPixmaps() |
|
{ |
|
clearPixmaps(); |
|
initPixmap("shadow-top"); |
|
initPixmap("shadow-topright"); |
|
initPixmap("shadow-right"); |
|
initPixmap("shadow-bottomright"); |
|
initPixmap("shadow-bottom"); |
|
initPixmap("shadow-bottomleft"); |
|
initPixmap("shadow-left"); |
|
initPixmap("shadow-topleft"); |
|
|
|
m_emptyCornerPix = initEmptyPixmap(QSize(1,1)); |
|
m_emptyCornerLeftPix = initEmptyPixmap(QSize(q->elementSize("shadow-topleft").width(), 1)); |
|
m_emptyCornerTopPix = initEmptyPixmap(QSize(1, q->elementSize("shadow-topleft").height())); |
|
m_emptyCornerRightPix = initEmptyPixmap(QSize(q->elementSize("shadow-bottomright").width(), 1)); |
|
m_emptyCornerBottomPix = initEmptyPixmap(QSize(1, q->elementSize("shadow-bottomright").height())); |
|
m_emptyVerticalPix = initEmptyPixmap(QSize(1, q->elementSize("shadow-left").height())); |
|
m_emptyHorizontalPix = initEmptyPixmap(QSize(q->elementSize("shadow-top").width(), 1)); |
|
|
|
} |
|
|
|
|
|
void Shadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders) |
|
{ |
|
#ifdef Q_WS_X11 |
|
//shadow-top |
|
if (enabledBorders & Plasma::FrameSvg::TopBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[0].handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyHorizontalPix.handle(); |
|
} |
|
|
|
//shadow-topright |
|
if (enabledBorders & Plasma::FrameSvg::TopBorder && |
|
enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[1].handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::TopBorder) { |
|
data[enabledBorders] << m_emptyCornerTopPix.handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
data[enabledBorders] << m_emptyCornerRightPix.handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyCornerPix.handle(); |
|
} |
|
|
|
//shadow-right |
|
if (enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[2].handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyVerticalPix.handle(); |
|
} |
|
|
|
//shadow-bottomright |
|
if (enabledBorders & Plasma::FrameSvg::BottomBorder && |
|
enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[3].handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::BottomBorder) { |
|
data[enabledBorders] << m_emptyCornerBottomPix.handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
data[enabledBorders] << m_emptyCornerRightPix.handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyCornerPix.handle(); |
|
} |
|
|
|
//shadow-bottom |
|
if (enabledBorders & Plasma::FrameSvg::BottomBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[4].handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyHorizontalPix.handle(); |
|
} |
|
|
|
//shadow-bottomleft |
|
if (enabledBorders & Plasma::FrameSvg::BottomBorder && |
|
enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[5].handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::BottomBorder) { |
|
data[enabledBorders] << m_emptyCornerBottomPix.handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
data[enabledBorders] << m_emptyCornerLeftPix.handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyCornerPix.handle(); |
|
} |
|
|
|
//shadow-left |
|
if (enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[6].handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyVerticalPix.handle(); |
|
} |
|
|
|
//shadow-topleft |
|
if (enabledBorders & Plasma::FrameSvg::TopBorder && |
|
enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
data[enabledBorders] << m_shadowPixmaps[7].handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::TopBorder) { |
|
data[enabledBorders] << m_emptyCornerTopPix.handle(); |
|
} else if (enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
data[enabledBorders] << m_emptyCornerLeftPix.handle(); |
|
} else { |
|
data[enabledBorders] << m_emptyCornerPix.handle(); |
|
} |
|
#endif |
|
|
|
int left, top, right, bottom = 0; |
|
|
|
QSize marginHint; |
|
if (enabledBorders & Plasma::FrameSvg::TopBorder) { |
|
marginHint = q->elementSize("shadow-hint-top-margin"); |
|
if (marginHint.isValid()) { |
|
top = marginHint.height(); |
|
} else { |
|
top = m_shadowPixmaps[0].height(); // top |
|
} |
|
} else { |
|
top = 1; |
|
} |
|
|
|
if (enabledBorders & Plasma::FrameSvg::RightBorder) { |
|
marginHint = q->elementSize("shadow-hint-right-margin"); |
|
if (marginHint.isValid()) { |
|
right = marginHint.width(); |
|
} else { |
|
right = m_shadowPixmaps[2].width(); // right |
|
} |
|
} else { |
|
right = 1; |
|
} |
|
|
|
if (enabledBorders & Plasma::FrameSvg::BottomBorder) { |
|
marginHint = q->elementSize("shadow-hint-bottom-margin"); |
|
if (marginHint.isValid()) { |
|
bottom = marginHint.height(); |
|
} else { |
|
bottom = m_shadowPixmaps[4].height(); // bottom |
|
} |
|
} else { |
|
bottom = 1; |
|
} |
|
|
|
if (enabledBorders & Plasma::FrameSvg::LeftBorder) { |
|
marginHint = q->elementSize("shadow-hint-left-margin"); |
|
if (marginHint.isValid()) { |
|
left = marginHint.width(); |
|
} else { |
|
left = m_shadowPixmaps[6].width(); // left |
|
} |
|
} else { |
|
left = 1; |
|
} |
|
|
|
data[enabledBorders] << top << right << bottom << left; |
|
} |
|
|
|
void Shadows::Private::clearPixmaps() |
|
{ |
|
#ifdef Q_WS_X11 |
|
if (m_managePixmaps) { |
|
foreach (const QPixmap &pixmap, m_shadowPixmaps) { |
|
XFreePixmap(QX11Info::display(), pixmap.handle()); |
|
} |
|
|
|
XFreePixmap(QX11Info::display(), m_emptyCornerPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyCornerBottomPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyCornerLeftPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyCornerRightPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyCornerTopPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyVerticalPix.handle()); |
|
XFreePixmap(QX11Info::display(), m_emptyHorizontalPix.handle()); |
|
|
|
m_managePixmaps = false; |
|
} |
|
#endif |
|
m_shadowPixmaps.clear(); |
|
data.clear(); |
|
} |
|
|
|
void Shadows::Private::updateShadow(const QWidget *window, Plasma::FrameSvg::EnabledBorders enabledBorders) |
|
{ |
|
#ifdef Q_WS_X11 |
|
if (m_shadowPixmaps.isEmpty()) { |
|
setupPixmaps(); |
|
} |
|
|
|
if (!data.contains(enabledBorders)) { |
|
setupData(enabledBorders); |
|
} |
|
|
|
Display *dpy = QX11Info::display(); |
|
Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False); |
|
|
|
//kDebug() << "going to set the shadow of" << winId() << "to" << data; |
|
XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace, |
|
reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size()); |
|
#endif |
|
} |
|
|
|
void Shadows::Private::clearShadow(const QWidget *window) |
|
{ |
|
#ifdef Q_WS_X11 |
|
Display *dpy = QX11Info::display(); |
|
Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False); |
|
XDeleteProperty(dpy, window->winId(), atom); |
|
#endif |
|
} |
|
|
|
bool Shadows::enabled() const |
|
{ |
|
return hasElement("shadow-left"); |
|
} |
|
|
|
#include "shadows.moc" |
|
|
|
|