diff options
| author | Peter Penz <[email protected]> | 2009-01-14 19:26:23 +0000 |
|---|---|---|
| committer | Peter Penz <[email protected]> | 2009-01-14 19:26:23 +0000 |
| commit | 307285e9635a4bf584d6e5d7478876b90ef870f0 (patch) | |
| tree | db36cbbdc7996ab86c38be8f96fe83597d350dfb /src/tooltips | |
| parent | 86d9c40ab71df5b8bd5063251337d5ca0c22380a (diff) | |
Group classes into folders, Dolphin is too big in the meantime for having a flat directory hierarchy. dolphin/src/CMakeLists.txt will be cleaned up later.
svn path=/trunk/KDE/kdebase/apps/; revision=911065
Diffstat (limited to 'src/tooltips')
| -rw-r--r-- | src/tooltips/dolphintooltip.cpp | 50 | ||||
| -rw-r--r-- | src/tooltips/dolphintooltip.h | 47 | ||||
| -rw-r--r-- | src/tooltips/kformattedballoontipdelegate.cpp | 129 | ||||
| -rw-r--r-- | src/tooltips/kformattedballoontipdelegate.h | 45 | ||||
| -rw-r--r-- | src/tooltips/ktooltip.cpp | 558 | ||||
| -rw-r--r-- | src/tooltips/ktooltip.h | 198 | ||||
| -rw-r--r-- | src/tooltips/ktooltip_p.h | 67 | ||||
| -rw-r--r-- | src/tooltips/tooltipmanager.cpp | 307 | ||||
| -rw-r--r-- | src/tooltips/tooltipmanager.h | 91 |
9 files changed, 1492 insertions, 0 deletions
diff --git a/src/tooltips/dolphintooltip.cpp b/src/tooltips/dolphintooltip.cpp new file mode 100644 index 000000000..bc3be33aa --- /dev/null +++ b/src/tooltips/dolphintooltip.cpp @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2008 by Simon St James <[email protected]> * + * * + * 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 of the License, 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 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 "dolphintooltip.h" + +#include <kicon.h> +#include <kio/previewjob.h> +#include <kfileitem.h> + +#include <QtGui/QPixmap> + +DolphinBalloonTooltipDelegate::DolphinBalloonTooltipDelegate() +{ +} + +DolphinBalloonTooltipDelegate::~DolphinBalloonTooltipDelegate() +{ +} + +// Delegate everything to the base class, after re-setting the decorationSize +// to the preview size. +QSize DolphinBalloonTooltipDelegate::sizeHint(const KStyleOptionToolTip* option, const KToolTipItem* item) const +{ + KStyleOptionToolTip updatedStyleOption = *option; + updatedStyleOption.decorationSize = QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT); + return KFormattedBalloonTipDelegate::sizeHint(&updatedStyleOption, item); +} + +void DolphinBalloonTooltipDelegate::paint(QPainter* painter, const KStyleOptionToolTip* option, const KToolTipItem* item) const +{ + KStyleOptionToolTip updatedStyleOption = *option; + updatedStyleOption.decorationSize = QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT); + return KFormattedBalloonTipDelegate::paint(painter, &updatedStyleOption, item); +} diff --git a/src/tooltips/dolphintooltip.h b/src/tooltips/dolphintooltip.h new file mode 100644 index 000000000..5c0682171 --- /dev/null +++ b/src/tooltips/dolphintooltip.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2008 by Simon St James <[email protected]> * + * * + * 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 of the License, 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 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 * + ***************************************************************************/ + +// NOTE: proper documentation will be added once the code is better developed. + +#ifndef DOLPHINTOOLTIP_H +#define DOLPHINTOOLTIP_H + +#include <tooltips/ktooltip.h> +#include <tooltips/kformattedballoontipdelegate.h> + +#include <kio/previewjob.h> +#include <QtCore/QObject> + +class KFileItem; +class QPixmap; + +const int PREVIEW_WIDTH = 256; +const int PREVIEW_HEIGHT = 256; + + +class DolphinBalloonTooltipDelegate : public KFormattedBalloonTipDelegate +{ +public: + DolphinBalloonTooltipDelegate(); + virtual ~DolphinBalloonTooltipDelegate(); + + virtual QSize sizeHint(const KStyleOptionToolTip* option, const KToolTipItem* item) const; + virtual void paint(QPainter* painter, const KStyleOptionToolTip* option, const KToolTipItem* item) const; +}; +#endif diff --git a/src/tooltips/kformattedballoontipdelegate.cpp b/src/tooltips/kformattedballoontipdelegate.cpp new file mode 100644 index 000000000..140c90ef4 --- /dev/null +++ b/src/tooltips/kformattedballoontipdelegate.cpp @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (C) 2008 by Fredrik Höglund <[email protected]> * + * Copyright (C) 2008 by Konstantin Heil <[email protected]> * + * * + * 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 of the License, 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 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 "kformattedballoontipdelegate.h" +#include <QBitmap> +#include <QLinearGradient> +#include <QTextDocument> +#include <kcolorscheme.h> + +KFormattedBalloonTipDelegate::KFormattedBalloonTipDelegate() +{ +} + +KFormattedBalloonTipDelegate::~KFormattedBalloonTipDelegate() +{ +} + +QSize KFormattedBalloonTipDelegate::sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const +{ + QTextDocument doc; + doc.setHtml(item->text()); + const QIcon icon = item->icon(); + + const QSize iconSize = icon.isNull() ? QSize(0, 0) : icon.actualSize(option->decorationSize); + const QSize docSize = doc.size().toSize(); + QSize contentSize = iconSize + docSize; + + // assure that the content height is large enough for the icon and the document + contentSize.setHeight(iconSize.height() > doc.size().height() ? iconSize.height() : doc.size().height()); + return contentSize + QSize(Border * 3, Border * 2); +} + +void KFormattedBalloonTipDelegate::paint(QPainter *painter, + const KStyleOptionToolTip *option, + const KToolTipItem *item) const +{ + QPainterPath path = createPath(*option); + if (haveAlphaChannel()) { + painter->setRenderHint(QPainter::Antialiasing); + painter->translate(.5, .5); + } + + const QColor toColor = option->palette.brush(QPalette::ToolTipBase).color(); + const QColor fromColor = KColorScheme::shade(toColor, KColorScheme::LightShade, 0.2); + + QLinearGradient gradient(option->rect.topLeft(), option->rect.bottomLeft()); + gradient.setColorAt(0.0, fromColor); + gradient.setColorAt(1.0, toColor); + painter->setPen(Qt::NoPen); + painter->setBrush(gradient); + + painter->drawPath(path); + + const QIcon icon = item->icon(); + int x = Border; + const int y = Border; + if (!icon.isNull()) { + const QSize iconSize = icon.actualSize(option->decorationSize); + const QPoint pos(x + option->rect.x(), y + option->rect.y()); + painter->drawPixmap(pos, icon.pixmap(iconSize)); + x += iconSize.width() + Border; + } + + QTextDocument doc; + doc.setHtml(item->text()); + QPixmap bitmap(doc.size().toSize()); + bitmap.fill(Qt::transparent); + QPainter p(&bitmap); + doc.drawContents(&p); + + const QRect docRect(QPoint(x, y), doc.size().toSize()); + painter->drawPixmap(docRect, bitmap); +} + +QRegion KFormattedBalloonTipDelegate::inputShape(const KStyleOptionToolTip *option) const +{ + QBitmap bitmap(option->rect.size()); + bitmap.fill(Qt::color0); + + QPainter p(&bitmap); + p.setPen(QPen(Qt::color1, 1)); + p.setBrush(Qt::color1); + p.drawPath(createPath(*option)); + + return QRegion(bitmap); +} + +QRegion KFormattedBalloonTipDelegate::shapeMask(const KStyleOptionToolTip *option) const +{ + return inputShape(option); +} + +static inline void arc(QPainterPath &path, qreal cx, qreal cy, qreal radius, qreal angle, qreal sweeplength) +{ + path.arcTo(cx-radius, cy-radius, radius * 2, radius * 2, angle, sweeplength); +} + +QPainterPath KFormattedBalloonTipDelegate::createPath(const KStyleOptionToolTip& option) const +{ + const QRect rect = option.rect; + const qreal radius = 5; + + QPainterPath path; + path.moveTo(rect.left(), rect.top() + radius); + arc(path, rect.left() + radius, rect.top() + radius, radius, 180, -90); + arc(path, rect.right() - radius, rect.top() + radius, radius, 90, -90); + arc(path, rect.right() - radius, rect.bottom() - radius, radius, 0, -90); + arc(path, rect.left() + radius, rect.bottom() - radius, radius, 270, -90); + path.closeSubpath(); + + return path; +} diff --git a/src/tooltips/kformattedballoontipdelegate.h b/src/tooltips/kformattedballoontipdelegate.h new file mode 100644 index 000000000..117d6eec7 --- /dev/null +++ b/src/tooltips/kformattedballoontipdelegate.h @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2008 by Fredrik Höglund <[email protected]> * + * Copyright (C) 2008 by Konstantin Heil <[email protected]> * + * * + * 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 of the License, 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 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 KFORMATTEDBALLOONTIPDELEGATE_H +#define KFORMATTEDBALLOONTIPDELEGATE_H + +#include <tooltips/ktooltip.h> +#include <QPainter> + +class KFormattedBalloonTipDelegate : public KToolTipDelegate +{ +public: + KFormattedBalloonTipDelegate(); + virtual ~KFormattedBalloonTipDelegate(); + + virtual QSize sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const; + virtual void paint(QPainter *painter, const KStyleOptionToolTip *option, const KToolTipItem *item) const; + virtual QRegion inputShape(const KStyleOptionToolTip *option) const; + virtual QRegion shapeMask(const KStyleOptionToolTip *option) const; + +private: + QPainterPath createPath(const KStyleOptionToolTip& option) const; + +private: + enum { Border = 8 }; +}; + +#endif diff --git a/src/tooltips/ktooltip.cpp b/src/tooltips/ktooltip.cpp new file mode 100644 index 000000000..a0126fbff --- /dev/null +++ b/src/tooltips/ktooltip.cpp @@ -0,0 +1,558 @@ +/*************************************************************************** + * Copyright (C) 2008 by Fredrik Höglund <[email protected]> * + * * + * 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 of the License, 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 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 "ktooltip.h" + +#include <QApplication> +#include <QMap> +#include <QPixmap> +#include <QPainter> +#include <QVariant> +#include <QIcon> +#include <QWidget> +#include <QToolTip> +#include <QDebug> + +#ifdef Q_WS_X11 +# include <QX11Info> +# include <X11/Xlib.h> +# include <X11/extensions/Xrender.h> +# include <X11/extensions/shape.h> +#endif + +#include "ktooltip_p.h" + + +// compile with XShape older than 1.0 +#ifndef ShapeInput +const int ShapeInput = 2; +#endif + + +class KToolTipItemPrivate +{ +public: + QMap<int, QVariant> map; + int type; +}; + +KToolTipItem::KToolTipItem(const QString &text, int type) + : d(new KToolTipItemPrivate) +{ + d->map[Qt::DisplayRole] = text; + d->type = type; +} + +KToolTipItem::KToolTipItem(const QIcon &icon, const QString &text, int type) + : d(new KToolTipItemPrivate) +{ + d->map[Qt::DecorationRole] = icon; + d->map[Qt::DisplayRole] = text; + d->type = type; +} + +KToolTipItem::~KToolTipItem() +{ + delete d; +} + +int KToolTipItem::type() const +{ + return d->type; +} + +QString KToolTipItem::text() const +{ + return data(Qt::DisplayRole).toString(); +} + +QIcon KToolTipItem::icon() const +{ + return qvariant_cast<QIcon>(data(Qt::DecorationRole)); +} + +QVariant KToolTipItem::data(int role) const +{ + return d->map.value(role); +} + +void KToolTipItem::setData(int role, const QVariant &data) +{ + d->map[role] = data; + KToolTipManager::instance()->update(); +} + + + +// ---------------------------------------------------------------------------- + + +KStyleOptionToolTip::KStyleOptionToolTip() + : fontMetrics(QApplication::font()) +{ +} + + +// ---------------------------------------------------------------------------- + + + +KToolTipDelegate::KToolTipDelegate() : QObject() +{ +} + +KToolTipDelegate::~KToolTipDelegate() +{ +} + +QSize KToolTipDelegate::sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const +{ + QSize size; + size.rwidth() = option->fontMetrics.width(item->text()); + size.rheight() = option->fontMetrics.lineSpacing(); + + QIcon icon = item->icon(); + if (!icon.isNull()) { + const QSize iconSize = icon.actualSize(option->decorationSize); + size.rwidth() += iconSize.width() + 4; + size.rheight() = qMax(size.height(), iconSize.height()); + } + + return size + QSize(20, 20); + +} + +void KToolTipDelegate::paint(QPainter *painter, const KStyleOptionToolTip *option, + const KToolTipItem *item) const +{ + bool haveAlpha = haveAlphaChannel(); + painter->setRenderHint(QPainter::Antialiasing); + + QPainterPath path; + if (haveAlpha) + path.addRoundRect(option->rect.adjusted(0, 0, -1, -1), 25); + else + path.addRect(option->rect.adjusted(0, 0, -1, -1)); + + QColor color = option->palette.color(QPalette::ToolTipBase); + QColor from = color.lighter(105); + QColor to = color.darker(120); + + QLinearGradient gradient(0, 0, 0, 1); + gradient.setCoordinateMode(QGradient::ObjectBoundingMode); + gradient.setColorAt(0, from); + gradient.setColorAt(1, to); + + painter->translate(.5, .5); + painter->setPen(QPen(Qt::black, 1)); + painter->setBrush(gradient); + painter->drawPath(path); + painter->translate(-.5, -.5); + + if (haveAlpha) { + QLinearGradient mask(0, 0, 0, 1); + gradient.setCoordinateMode(QGradient::ObjectBoundingMode); + gradient.setColorAt(0, QColor(0, 0, 0, 192)); + gradient.setColorAt(1, QColor(0, 0, 0, 72)); + painter->setCompositionMode(QPainter::CompositionMode_DestinationIn); + painter->fillRect(option->rect, gradient); + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + } + + QRect textRect = option->rect.adjusted(10, 10, -10, -10); + + QIcon icon = item->icon(); + if (!icon.isNull()) { + const QSize iconSize = icon.actualSize(option->decorationSize); + painter->drawPixmap(textRect.topLeft(), icon.pixmap(iconSize)); + textRect.adjust(iconSize.width() + 4, 0, 0, 0); + } + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, item->text()); +} + +QRegion KToolTipDelegate::inputShape(const KStyleOptionToolTip *option) const +{ + return QRegion(option->rect); +} + +QRegion KToolTipDelegate::shapeMask(const KStyleOptionToolTip *option) const +{ + return QRegion(option->rect); +} + +bool KToolTipDelegate::haveAlphaChannel() const +{ + return KToolTipManager::instance()->haveAlphaChannel(); +} + + + +// ---------------------------------------------------------------------------- + + + +class KAbstractToolTipLabel +{ +public: + KAbstractToolTipLabel() {} + virtual ~KAbstractToolTipLabel() {} + + virtual void showTip(const QPoint &pos, const KToolTipItem *item) = 0; + virtual void moveTip(const QPoint &pos) = 0; + virtual void hideTip() = 0; + +protected: + KStyleOptionToolTip styleOption() const; + KToolTipDelegate *delegate() const; +}; + +KStyleOptionToolTip KAbstractToolTipLabel::styleOption() const +{ + KStyleOptionToolTip option; + KToolTipManager::instance()->initStyleOption(&option); + return option; +} + +KToolTipDelegate *KAbstractToolTipLabel::delegate() const +{ + return KToolTipManager::instance()->delegate(); +} + + +// ---------------------------------------------------------------------------- + + + +class QWidgetLabel : public QWidget, public KAbstractToolTipLabel +{ +public: + QWidgetLabel() : QWidget(0, Qt::ToolTip) {} + void showTip(const QPoint &pos, const KToolTipItem *item); + void moveTip(const QPoint &pos); + void hideTip(); + +private: + void paintEvent(QPaintEvent*); + QSize sizeHint() const; + +private: + const KToolTipItem *currentItem; +}; + +void QWidgetLabel::showTip(const QPoint &pos, const KToolTipItem *item) +{ + currentItem = item; + move(pos); + show(); +} + +void QWidgetLabel::hideTip() +{ + hide(); + currentItem = 0; +} + +void QWidgetLabel::moveTip(const QPoint &pos) +{ + move(pos); +} + +void QWidgetLabel::paintEvent(QPaintEvent*) +{ + KStyleOptionToolTip option = styleOption(); + option.rect = rect(); + + setMask(delegate()->shapeMask(&option)); + + QPainter p(this); + p.setFont(option.font); + p.setPen(QPen(option.palette.brush(QPalette::Text), 0)); + delegate()->paint(&p, &option, currentItem); +} + +QSize QWidgetLabel::sizeHint() const +{ + if (!currentItem) + return QSize(); + + KStyleOptionToolTip option = styleOption(); + return delegate()->sizeHint(&option, currentItem); +} + + + +// ---------------------------------------------------------------------------- + + + +#ifdef Q_WS_X11 + +// X11 specific label that displays the tip in an ARGB window. +class ArgbLabel : public KAbstractToolTipLabel +{ +public: + ArgbLabel(Visual *visual, int depth); + ~ArgbLabel(); + + void showTip(const QPoint &pos, const KToolTipItem *item); + void moveTip(const QPoint &pos); + void hideTip(); + +private: + Window window; + Colormap colormap; + Picture picture; + bool mapped; +}; + +ArgbLabel::ArgbLabel(Visual *visual, int depth) +{ + Display *dpy = QX11Info::display(); + Window root = QX11Info::appRootWindow(); + colormap = XCreateColormap(dpy, QX11Info::appRootWindow(), visual, AllocNone); + + XSetWindowAttributes attr; + attr.border_pixel = 0; + attr.background_pixel = 0; + attr.colormap = colormap; + attr.override_redirect = True; + + window = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, depth, InputOutput, visual, + CWBorderPixel | CWBackPixel | CWColormap | + CWOverrideRedirect, &attr); + + // ### TODO: Set the WM hints so KWin can identify this window as a + // tooltip. + + XRenderPictFormat *format = XRenderFindVisualFormat(dpy, visual); + picture = XRenderCreatePicture(dpy, window, format, 0, 0); + + mapped = false; +} + +ArgbLabel::~ArgbLabel() +{ + Display *dpy = QX11Info::display(); + XRenderFreePicture(dpy, picture); + XDestroyWindow(dpy, window); + XFreeColormap(dpy, colormap); +} + +void ArgbLabel::showTip(const QPoint &pos, const KToolTipItem *item) +{ + Display *dpy = QX11Info::display(); + KStyleOptionToolTip option = styleOption(); + const QSize size = delegate()->sizeHint(&option, item); + option.rect = QRect(QPoint(), size); + + QPixmap pixmap(size); + pixmap.fill(Qt::transparent); + + QPainter p(&pixmap); + p.setFont(option.font); + p.setPen(QPen(option.palette.brush(QPalette::Text), 0)); + delegate()->paint(&p, &option, item); + + // Resize, position and show the window. + XMoveResizeWindow(dpy, window, pos.x(), pos.y(), size.width(), size.height()); + + if (KToolTipManager::instance()->haveAlphaChannel()) { + const QRegion region = delegate()->inputShape(&option); + XShapeCombineRegion(dpy, window, ShapeInput, 0, 0, region.handle(), ShapeSet); + } else { + const QRegion region = delegate()->shapeMask(&option); + XShapeCombineRegion(dpy, window, ShapeBounding, 0, 0, region.handle(), ShapeSet); + } + + XMapWindow(dpy, window); + + // Blit the pixmap with the tip contents to the window. + // Since the window is override-redirect and an ARGB32 window, + // which always has an offscreen pixmap, there's no need to + // wait for an Expose event, or to process those. + XRenderComposite(dpy, PictOpSrc, pixmap.x11PictureHandle(), None, + picture, 0, 0, 0, 0, 0, 0, size.width(), size.height()); + + mapped = true; +} + +void ArgbLabel::moveTip(const QPoint &pos) +{ + if (mapped) + XMoveWindow(QX11Info::display(), window, pos.x(), pos.y()); +} + +void ArgbLabel::hideTip() +{ + if (mapped) { + Display *dpy = QX11Info::display(); + XUnmapWindow(dpy, window); + mapped = false; + } +} + +#endif // Q_WS_X11 + + + + +// ---------------------------------------------------------------------------- + + + + +KToolTipManager *KToolTipManager::s_instance = 0; + +KToolTipManager::KToolTipManager() + : label(0), currentItem(0), m_delegate(0) +{ +#ifdef Q_WS_X11 + Display *dpy = QX11Info::display(); + int screen = DefaultScreen(dpy); + int depth = DefaultDepth(dpy, screen); + Visual *visual = DefaultVisual(dpy, screen); + net_wm_cm_s0 = XInternAtom(dpy, "_NET_WM_CM_S0", False); + haveArgbVisual = false; + + int nvi; + XVisualInfo templ; + templ.screen = screen; + templ.depth = 32; + templ.c_class = TrueColor; + XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | + VisualClassMask, &templ, &nvi); + + for (int i = 0; i < nvi; ++i) + { + XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask) + { + visual = xvi[i].visual; + depth = xvi[i].depth; + haveArgbVisual = true; + break; + } + } + + if (haveArgbVisual) + label = new ArgbLabel(visual, depth); + else +#endif + label = new QWidgetLabel(); +} + +KToolTipManager::~KToolTipManager() +{ + delete label; + delete currentItem; +} + +void KToolTipManager::showTip(const QPoint &pos, KToolTipItem *item) +{ + hideTip(); + label->showTip(pos, item); + currentItem = item; + m_tooltipPos = pos; +} + +void KToolTipManager::hideTip() +{ + label->hideTip(); + delete currentItem; + currentItem = 0; +} + +void KToolTipManager::initStyleOption(KStyleOptionToolTip *option) const +{ + option->direction = QApplication::layoutDirection(); + option->fontMetrics = QFontMetrics(QToolTip::font()); + option->activeCorner = KStyleOptionToolTip::TopLeftCorner; + option->palette = QToolTip::palette(); + option->font = QToolTip::font(); + option->rect = QRect(); + option->state = QStyle::State_None; + option->decorationSize = QSize(32, 32); +} + +bool KToolTipManager::haveAlphaChannel() const +{ +#ifdef Q_WS_X11 + // ### This is a synchronous call - ideally we'd use a selection + // watcher to avoid it. + return haveArgbVisual && + XGetSelectionOwner(QX11Info::display(), net_wm_cm_s0) != None; +#else + return false; +#endif +} + +void KToolTipManager::setDelegate(KToolTipDelegate *delegate) +{ + m_delegate = delegate; +} + +void KToolTipManager::update() +{ + if (currentItem == 0) + return; + label->showTip(m_tooltipPos, currentItem); +} + +KToolTipDelegate *KToolTipManager::delegate() const +{ + return m_delegate; +} + + + +// ---------------------------------------------------------------------------- + + + +namespace KToolTip +{ + void showText(const QPoint &pos, const QString &text, QWidget *widget, const QRect &rect) + { + Q_UNUSED(widget) + Q_UNUSED(rect) + KToolTipItem *item = new KToolTipItem(text); + KToolTipManager::instance()->showTip(pos, item); + } + + void showText(const QPoint &pos, const QString &text, QWidget *widget) + { + showText(pos, text, widget, QRect()); + } + + void showTip(const QPoint &pos, KToolTipItem *item) + { + KToolTipManager::instance()->showTip(pos, item); + } + + void hideTip() + { + KToolTipManager::instance()->hideTip(); + } + + void setToolTipDelegate(KToolTipDelegate *delegate) + { + KToolTipManager::instance()->setDelegate(delegate); + } +} + diff --git a/src/tooltips/ktooltip.h b/src/tooltips/ktooltip.h new file mode 100644 index 000000000..d59f1bf17 --- /dev/null +++ b/src/tooltips/ktooltip.h @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2008 by Fredrik Höglund <[email protected]> * + * * + * 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 of the License, 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 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 KTOOLTIP_H +#define KTOOLTIP_H + +#include <QObject> +#include <QPalette> +#include <QFont> +#include <QRect> +#include <QStyle> +#include <QFontMetrics> + +class QString; +class QIcon; +class QSize; +class QPainter; +class QRegion; + +class KToolTipItemPrivate; + +/** + * KToolTipItem contains the data to be displayed in a tooltip. + * + * Custom data can be stored as QVariants in the object by calling + * setData() with a custom item role, and retrieved and displayed + * by a tooltip delegate by calling data(). + * + * The default tooltip delegate uses Qt::DecorationRole and + * Qt::DisplayRole. + * + * To display the tooltip, call KToolTip::showTip() with a pointer + * to the KToolTipItem. + * + * You can reimplement the setData() and/or data() methods in this + * class to implement on-demand loading of data. + */ +class KToolTipItem +{ +public: + enum ItemType { DefaultType, UserType = 1000 }; + + /** + * Creates a KToolTipItem with @p text and no icon. + */ + explicit KToolTipItem(const QString &text, int type = DefaultType); + + /** + * Creates a KToolTipItem with an @p icon and @p text. + */ + KToolTipItem(const QIcon &icon, const QString &text, int type = DefaultType); + + /** + * Destroys the KToolTipItem. + */ + virtual ~KToolTipItem(); + + /** + * Returns the item type. + */ + int type() const; + + QString text() const; + QIcon icon() const; + + virtual QVariant data(int role) const; + virtual void setData(int role, const QVariant &data); + +private: + KToolTipItemPrivate * const d; +}; + + +class KStyleOptionToolTip +{ +public: + KStyleOptionToolTip(); + enum Corner { TopLeftCorner, TopRightCorner, BottomLeftCorner, BottomRightCorner, NoCorner }; + + Qt::LayoutDirection direction; + QFontMetrics fontMetrics; + QPalette palette; + QRect rect; + QStyle::State state; + QFont font; + QSize decorationSize; + Corner activeCorner; +}; + +/** + * KToolTipDelegate is responsible for providing the size hint and + * painting the tooltips. + */ +class KToolTipDelegate : public QObject +{ + Q_OBJECT +public: + KToolTipDelegate(); + virtual ~KToolTipDelegate(); + + virtual QSize sizeHint(const KStyleOptionToolTip *option, const KToolTipItem *item) const; + + /** + * If haveAlphaChannel() returns true, the paint device will be filled with + * Qt::transparent when this function is called, otherwise the content is + * undefined. + */ + virtual void paint(QPainter *painter, const KStyleOptionToolTip *option, + const KToolTipItem *item) const; + + /** + * Reimplement this function to specify the region of the tooltip + * that accepts input. Any mouse events that occur outside this + * region will be sent to the widget below the tooltip. + * + * The default implementation returns a region containing the + * bounding rect of the tooltip. + * + * This function will only be called if haveAlphaChannel() + * returns true. + */ + virtual QRegion inputShape(const KStyleOptionToolTip *option) const; + + /** + * Reimplement this function to specify a shape mask for the tooltip. + * + * The default implementation returns a region containing the + * bounding rect of the tooltip. + * + * This function will only be called if haveAlphaChannel() + * returns false. + */ + virtual QRegion shapeMask(const KStyleOptionToolTip *option) const; + +protected: + /** + * Returns true if the tooltip has an alpha channel, and false + * otherwise. + * + * Implementors should assume that this condition may change at + * any time during the runtime of the application, as compositing + * can be enabled or disabled in the window manager. + */ + bool haveAlphaChannel() const; + +#if 0 +private Q_SLOTS: + /** + * Schedules a repaint of the tooltip item. + * This slot can be connected to a timer to animate the tooltip. + */ + void update(const KToolTipItem *item); +#endif +}; + + +/** + * KToolTip provides customizable tooltips that can have animations as well as an alpha + * channel, allowing for dynamic transparency effects. + * + * ARGB tooltips work on X11 even when the application isn't using the ARGB visual. + */ +namespace KToolTip +{ + void showText(const QPoint &pos, const QString &text, QWidget *widget, const QRect &rect); + void showText(const QPoint &pos, const QString &text, QWidget *widget = 0); + + /** + * Shows the tip @p item at the global position indicated by @p pos. + * + * Ownership of the item is transferred to KToolTip. The item will be deleted + * automatically when it is hidden. + * + * The tip is shown immediately when this function is called. + */ + void showTip(const QPoint &pos, KToolTipItem *item); + void hideTip(); + + void setToolTipDelegate(KToolTipDelegate *delegate); +} + +#endif diff --git a/src/tooltips/ktooltip_p.h b/src/tooltips/ktooltip_p.h new file mode 100644 index 000000000..332df4854 --- /dev/null +++ b/src/tooltips/ktooltip_p.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2008 by Fredrik Höglund <[email protected]> * + * * + * 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 of the License, 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 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 KTOOLTIP_P_H +#define KTOOLTIP_P_H + +class KAbstractToolTipLabel; +class KStyleOptionToolTip; +class KToolTipDelegate; + +class KToolTipManager +{ +public: + ~KToolTipManager(); + + static KToolTipManager *instance() { + if (!s_instance) + s_instance = new KToolTipManager(); + + return s_instance; + } + + void showTip(const QPoint &pos, KToolTipItem *item); + void hideTip(); + + void initStyleOption(KStyleOptionToolTip *option) const; + bool haveAlphaChannel() const; + + void setDelegate(KToolTipDelegate *delegate); + KToolTipDelegate *delegate() const; + + void update(); + +private: + KToolTipManager(); + + KAbstractToolTipLabel *label; + KToolTipItem *currentItem; + KToolTipDelegate *m_delegate; + + QPoint m_tooltipPos; + +#ifdef Q_WS_X11 + bool haveArgbVisual; + Atom net_wm_cm_s0; +#endif + + static KToolTipManager *s_instance; +}; + +#endif diff --git a/src/tooltips/tooltipmanager.cpp b/src/tooltips/tooltipmanager.cpp new file mode 100644 index 000000000..36a008f33 --- /dev/null +++ b/src/tooltips/tooltipmanager.cpp @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (C) 2008 by Konstantin Heil <[email protected]> * + * * + * 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 of the License, 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 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 "tooltipmanager.h" + +#include "dolphintooltip.h" +#include "dolphinmodel.h" +#include "dolphinsortfilterproxymodel.h" + +#include <kicon.h> +#include <tooltips/ktooltip.h> +#include <kio/previewjob.h> + +#include <QApplication> +#include <QDesktopWidget> +#include <QScrollBar> +#include <QTimer> +#include <QToolTip> + +const int ICON_WIDTH = 128; +const int ICON_HEIGHT = 128; +const int PREVIEW_DELAY = 250; + +K_GLOBAL_STATIC(DolphinBalloonTooltipDelegate, g_delegate) + +ToolTipManager::ToolTipManager(QAbstractItemView* parent, + DolphinSortFilterProxyModel* model) : + QObject(parent), + m_view(parent), + m_dolphinModel(0), + m_proxyModel(model), + m_timer(0), + m_previewTimer(0), + m_waitOnPreviewTimer(0), + m_item(), + m_itemRect(), + m_preview(false), + m_generatingPreview(false), + m_previewIsLate(false), + m_previewPass(0), + m_emptyRenderedKToolTipItem(0), + m_pix() +{ + KToolTip::setToolTipDelegate(g_delegate); + + m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel()); + connect(parent, SIGNAL(entered(const QModelIndex&)), + this, SLOT(requestToolTip(const QModelIndex&))); + connect(parent, SIGNAL(viewportEntered()), + this, SLOT(hideToolTip())); + + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + connect(m_timer, SIGNAL(timeout()), + this, SLOT(prepareToolTip())); + + m_previewTimer = new QTimer(this); + m_previewTimer->setSingleShot(true); + connect(m_previewTimer, SIGNAL(timeout()), + this, SLOT(startPreviewJob())); + + m_waitOnPreviewTimer = new QTimer(this); + m_waitOnPreviewTimer->setSingleShot(true); + connect(m_waitOnPreviewTimer, SIGNAL(timeout()), + this, SLOT(prepareToolTip())); + + // When the mousewheel is used, the items don't get a hovered indication + // (Qt-issue #200665). To assure that the tooltip still gets hidden, + // the scrollbars are observed. + connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(hideTip())); + connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(hideTip())); + + m_view->viewport()->installEventFilter(this); +} + +ToolTipManager::~ToolTipManager() +{ +} + +void ToolTipManager::hideTip() +{ + hideToolTip(); +} + +bool ToolTipManager::eventFilter(QObject* watched, QEvent* event) +{ + if ((watched == m_view->viewport()) && (event->type() == QEvent::Leave)) { + hideToolTip(); + } + + return QObject::eventFilter(watched, event); +} + +void ToolTipManager::requestToolTip(const QModelIndex& index) +{ + // only request a tooltip for the name column and when no selection or + // drag & drop operation is done (indicated by the left mouse button) + if ((index.column() == DolphinModel::Name) && !(QApplication::mouseButtons() & Qt::LeftButton)) { + m_waitOnPreviewTimer->stop(); + KToolTip::hideTip(); + + m_itemRect = m_view->visualRect(index); + const QPoint pos = m_view->viewport()->mapToGlobal(m_itemRect.topLeft()); + m_itemRect.moveTo(pos); + + const QModelIndex dirIndex = m_proxyModel->mapToSource(index); + m_item = m_dolphinModel->itemForIndex(dirIndex); + + // only start the previewJob when the mouse has been over this item for 200 milliseconds, + // this prevents a lot of useless preview jobs when passing rapidly over a lot of items + m_previewTimer->start(200); + m_preview = false; + m_previewIsLate = false; + m_previewPass = 0; + + m_timer->start(500); + } else { + hideToolTip(); + } +} + +void ToolTipManager::hideToolTip() +{ + m_timer->stop(); + m_previewTimer->stop(); + m_waitOnPreviewTimer->stop(); + m_previewIsLate = false; + KToolTip::hideTip(); +} + +void ToolTipManager::prepareToolTip() +{ + if (m_generatingPreview) { + if (m_previewPass == 1) { + // We waited 250msec and the preview is still not finished, + // so show the toolTip with a transparent image of maximal width. + // When the preview finishes, m_previewIsLate will cause + // a direct update of the tooltip, via m_emptyRenderedKToolTipItem. + QPixmap paddedImage(QSize(PREVIEW_WIDTH, 32)); + m_previewIsLate = true; + paddedImage.fill(Qt::transparent); + KToolTipItem* toolTip = new KToolTipItem(paddedImage, m_item.getToolTipText()); + m_emptyRenderedKToolTipItem = toolTip; // make toolTip accessible everywhere + showToolTip(toolTip); + } + + ++m_previewPass; + m_waitOnPreviewTimer->start(250); + } else { + // The preview generation has finished, find out under which circumstances. + if (m_preview && m_previewIsLate) { + // We got a preview, but it is late, the tooltip has already been shown. + // So update the tooltip directly. + if (m_emptyRenderedKToolTipItem != 0) { + m_emptyRenderedKToolTipItem->setData(Qt::DecorationRole, KIcon(m_pix)); + } + return; + } + + KIcon icon; + if (m_preview) { + // We got a preview. + icon = KIcon(m_pix); + } else { + // No preview, so use an icon. + // Force a 128x128 icon, a 256x256 one is far too big. + icon = KIcon(KIcon(m_item.iconName()).pixmap(ICON_WIDTH, ICON_HEIGHT)); + } + + KToolTipItem* toolTip = new KToolTipItem(icon, m_item.getToolTipText()); + showToolTip(toolTip); + } +} + +void ToolTipManager::showToolTip(KToolTipItem* tip) +{ + if (QApplication::mouseButtons() & Qt::LeftButton) { + delete tip; + tip = 0; + // m_emptyRenderedKToolTipItem is an alias for tip. + m_emptyRenderedKToolTipItem = 0; + return; + } + + KStyleOptionToolTip option; + // TODO: get option content from KToolTip or add KToolTip::sizeHint() method + option.direction = QApplication::layoutDirection(); + option.fontMetrics = QFontMetrics(QToolTip::font()); + option.activeCorner = KStyleOptionToolTip::TopLeftCorner; + option.palette = QToolTip::palette(); + option.font = QToolTip::font(); + option.rect = QRect(); + option.state = QStyle::State_None; + option.decorationSize = QSize(32, 32); + + QSize size; + if (m_previewIsLate) { + QPixmap paddedImage(QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT)); + KToolTipItem* maxiTip = new KToolTipItem(paddedImage, m_item.getToolTipText()); + size = g_delegate->sizeHint(&option, maxiTip); + delete maxiTip; + maxiTip = 0; + } + else { + size = g_delegate->sizeHint(&option, tip); + } + const QRect desktop = QApplication::desktop()->screenGeometry(m_itemRect.bottomRight()); + + // m_itemRect defines the area of the item, where the tooltip should be + // shown. Per default the tooltip is shown in the bottom right corner. + // If the tooltip content exceeds the desktop borders, it must be assured that: + // - the content is fully visible + // - the content is not drawn inside m_itemRect + const bool hasRoomToLeft = (m_itemRect.left() - size.width() >= desktop.left()); + const bool hasRoomToRight = (m_itemRect.right() + size.width() <= desktop.right()); + const bool hasRoomAbove = (m_itemRect.top() - size.height() >= desktop.top()); + const bool hasRoomBelow = (m_itemRect.bottom() + size.height() <= desktop.bottom()); + if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) { + delete tip; + tip = 0; + return; + } + + int x = 0; + int y = 0; + if (hasRoomBelow || hasRoomAbove) { + x = QCursor::pos().x() + 16; // TODO: use mouse pointer width instead of the magic value of 16 + if (x + size.width() >= desktop.right()) { + x = desktop.right() - size.width(); + } + y = hasRoomBelow ? m_itemRect.bottom() : m_itemRect.top() - size.height(); + } else { + Q_ASSERT(hasRoomToLeft || hasRoomToRight); + x = hasRoomToRight ? m_itemRect.right() : m_itemRect.left() - size.width(); + + // Put the tooltip at the bottom of the screen. The x-coordinate has already + // been adjusted, so that no overlapping with m_itemRect occurs. + y = desktop.bottom() - size.height(); + } + + KToolTip::showTip(QPoint(x, y), tip); +} + + + +void ToolTipManager::startPreviewJob() +{ + m_generatingPreview = true; + KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << m_item, + PREVIEW_WIDTH, + PREVIEW_HEIGHT); + job->setIgnoreMaximumSize(true); + + connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), + this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&))); + connect(job, SIGNAL(failed(const KFileItem&)), + this, SLOT(previewFailed(const KFileItem&))); +} + + +void ToolTipManager::setPreviewPix(const KFileItem& item, + const QPixmap& pixmap) +{ + if (m_item.url() != item.url()) { + m_generatingPreview = false; + return; + } + + if (m_previewIsLate) { + // always use the maximal width + QPixmap paddedImage(QSize(PREVIEW_WIDTH, pixmap.height())); + paddedImage.fill(Qt::transparent); + QPainter painter(&paddedImage); + painter.drawPixmap((PREVIEW_WIDTH - pixmap.width()) / 2, 0, pixmap); + m_pix = paddedImage; + } else { + m_pix = pixmap; + } + m_preview = true; + m_generatingPreview = false; +} + +void ToolTipManager::previewFailed(const KFileItem& item) +{ + Q_UNUSED(item); + m_generatingPreview = false; +} + +#include "tooltipmanager.moc" diff --git a/src/tooltips/tooltipmanager.h b/src/tooltips/tooltipmanager.h new file mode 100644 index 000000000..96df4819e --- /dev/null +++ b/src/tooltips/tooltipmanager.h @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (C) 2008 by Konstantin Heil <[email protected]> * + * * + * 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 of the License, 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 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 TOOLTIPMANAGER_H +#define TOOLTIPMANAGER_H + +#include <QObject> +#include <QRect> + +#include <kfileitem.h> + +class DolphinModel; +class DolphinSortFilterProxyModel; +class QAbstractItemView; +class QModelIndex; +class QTimer; +class KToolTipItem; + +/** + * @brief Manages the tooltips for an item view. + * + * When hovering an item, a tooltip is shown after + * a short timeout. The tooltip is hidden again when the + * viewport is hovered or the item view has been left. + */ +class ToolTipManager : public QObject +{ + Q_OBJECT + +public: + explicit ToolTipManager(QAbstractItemView* parent, + DolphinSortFilterProxyModel* model); + virtual ~ToolTipManager(); + +public slots: + /** + * Hides the currently shown tooltip. Invoking this method is + * only needed when the tooltip should be hidden although + * an item is hovered. + */ + void hideTip(); + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event); + +private slots: + void requestToolTip(const QModelIndex& index); + void hideToolTip(); + void prepareToolTip(); + void startPreviewJob(); + void setPreviewPix(const KFileItem& item, const QPixmap& pix); + void previewFailed(const KFileItem& item); + +private: + void showToolTip(KToolTipItem* tip); + + QAbstractItemView* m_view; + DolphinModel* m_dolphinModel; + DolphinSortFilterProxyModel* m_proxyModel; + + QTimer* m_timer; + QTimer* m_previewTimer; + QTimer* m_waitOnPreviewTimer; + KFileItem m_item; + QRect m_itemRect; + bool m_preview; + bool m_generatingPreview; + bool m_previewIsLate; + int m_previewPass; + KToolTipItem* m_emptyRenderedKToolTipItem; + QPixmap m_pix; + +}; + +#endif |
