From 4aed2c8219774f5d797760606b8489a92ddc5163 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kicker/libkicker/kickertip.cpp | 557 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 557 insertions(+) create mode 100644 kicker/libkicker/kickertip.cpp (limited to 'kicker/libkicker/kickertip.cpp') diff --git a/kicker/libkicker/kickertip.cpp b/kicker/libkicker/kickertip.cpp new file mode 100644 index 000000000..403641443 --- /dev/null +++ b/kicker/libkicker/kickertip.cpp @@ -0,0 +1,557 @@ +/***************************************************************** + +Copyright (c) 2004 Zack Rusin + Sami Kyostil + Aaron J. Seigo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "global.h" + +#include "kickertip.h" +#include "kickerSettings.h" + +// putting this #include higher results in compile errors +#include + +static const int DEFAULT_FRAMES_PER_SECOND = 30; + +KickerTip* KickerTip::m_self = 0; +int KickerTip::m_tippingEnabled = 1; + +void KickerTip::Client::updateKickerTip() const +{ + if (KickerTip::the()->isTippingFor(dynamic_cast(this)) && + KickerTip::the()->isVisible()) + { + KickerTip::the()->display(); + } +} + +KickerTip* KickerTip::the() +{ + if (!m_self) + { + m_self = new KickerTip(0); + } + + return m_self; +} + +KickerTip::KickerTip(QWidget * parent) + : QWidget(parent, "animtt",WX11BypassWM), + m_richText(0), + m_mimeFactory(0), + m_dissolveSize(0), + m_dissolveDelta(-1), + m_direction(KPanelApplet::Up), + m_dirty(false), + m_toolTipsEnabled(KickerSettings::showToolTips()), + m_tippingFor(0) +{ + setFocusPolicy(NoFocus); + setBackgroundMode(NoBackground); + resize(0, 0); + hide(); + connect(&m_frameTimer, SIGNAL(timeout()), SLOT(internalUpdate())); +} + +KickerTip::~KickerTip() +{ + delete m_richText; + delete m_mimeFactory; +} + +void KickerTip::display() +{ + if (!tippingEnabled()) + { + return; + } + + { + // prevent tips from showing when the active window is fullscreened + NETRootInfo ri(qt_xdisplay(), NET::ActiveWindow); + NETWinInfo wi(qt_xdisplay(), ri.activeWindow(), ri.rootWindow(), NET::WMState); + if (wi.state() & NET::FullScreen) + { + return; + } + } + + QWidget *widget = const_cast(m_tippingFor); + KickerTip::Client *client = dynamic_cast(widget); + + if (!client) + { + return; + } + + // delete the mimefactory and create a new one so any old pixmaps used in the + // richtext area are freed but the mimefactory is ready to be added to in + // the call to updateKickerTip + delete m_mimeFactory; + m_mimeFactory = new QMimeSourceFactory(); + + // Declare interchange object and define defaults. + Data data; + data.maskEffect = Dissolve; + data.duration = 2000; + data.direction = KPanelApplet::Up; + data.mimeFactory = m_mimeFactory; + + // Tickle the information out of the bastard. + client->updateKickerTip(data); + + // Hide the tip if there is nothing to show + if (data.message.isEmpty() && data.subtext.isEmpty() && data.icon.isNull()) + { + hide(); + return; + } + + delete m_richText; + m_richText = new QSimpleRichText("

" + data.message + "

" + + data.subtext + "

", font(), QString::null, 0, + m_mimeFactory); + m_richText->setWidth(640); + m_direction = data.direction; + + if (KickerSettings::mouseOversShowIcon()) + { + m_icon = data.icon; + } + else if (KickerSettings::mouseOversShowText()) + { + m_icon = QPixmap(); + } + else + { + // don't bother since we have NOTHING to show + return; + } + + m_maskEffect = isVisible() ? Plain : data.maskEffect; + m_dissolveSize = 24; + m_dissolveDelta = -1; + + displayInternal(); + + m_frameTimer.start(1000 / DEFAULT_FRAMES_PER_SECOND); + + // close the message window after given mS + if (data.duration > 0) + { + disconnect(&m_timer, SIGNAL(timeout()), 0, 0); + connect(&m_timer, SIGNAL(timeout()), SLOT(hide())); + m_timer.start(data.duration, true); + } + else + { + m_timer.stop(); + } + + move(KickerLib::popupPosition(m_direction, this, m_tippingFor)); + show(); +} + +void KickerTip::paintEvent(QPaintEvent * e) +{ + if (m_dirty) + { + displayInternal(); + m_dirty = false; + } + + QPainter p(this); + p.drawPixmap(e->rect().topLeft(), m_pixmap, e->rect()); +} + +void KickerTip::mousePressEvent(QMouseEvent * /*e*/) +{ + QToolTip::setGloballyEnabled(m_toolTipsEnabled); + hide(); +} + +static void drawRoundRect(QPainter &p, const QRect &r) +{ + static int line[8] = { 1, 3, 4, 5, 6, 7, 7, 8 }; + static int border[8] = { 1, 2, 1, 1, 1, 1, 1, 1 }; + int xl, xr, y1, y2; + QPen pen = p.pen(); + bool drawBorder = pen.style() != QPen::NoPen; + + if (r.width() < 16 || r.height() < 16) + { + p.drawRect(r); + return; + } + + p.fillRect(r.x(), r.y() + 8, r.width(), r.height() - 16, p.brush()); + p.fillRect(r.x() + 8, r.y(), r.width() - 16, r.height(), p.brush()); + + p.setPen(p.brush().color()); + + for (int i = 0; i < 8; i++) + { + xl = i; + xr = r.width() - i - 1; + y1 = 7; + y2 = 7 - (line[i] - 1); + + p.drawLine(xl, y1, xl, y2); + p.drawLine(xr, y1, xr, y2); + + y1 = r.height() - y1 - 1; + y2 = r.height() - y2 - 1; + + p.drawLine(xl, y1, xl, y2); + p.drawLine(xr, y1, xr, y2); + + } + + if (drawBorder) + { + p.setPen(pen); + + if (r.height() > 16) + { + p.drawLine(r.x(), r.y() + 8, r.x(), r.y() + r.height() - 9); + p.drawLine(r.x() + r.width() - 1, r.y() + 8, r.x() + r.width() - 1, r.y() + r.height() - 9); + } + if (r.width() > 16) + { + p.drawLine(r.x() + 8, r.y(), r.x() + r.width() - 9, r.y()); + p.drawLine(r.x() + 8, r.y() + r.height() - 1, r.x() + r.width() - 9, r.y() + r.height() - 1); + } + + for (int i = 0; i < 8; i++) + { + xl = i; + xr = r.width() - i - 1; + y2 = 7 - (line[i] - 1); + y1 = y2 + (border[i] - 1); + + p.drawLine(xl, y1, xl, y2); + p.drawLine(xr, y1, xr, y2); + + y1 = r.height() - y1 - 1; + y2 = r.height() - y2 - 1; + + p.drawLine(xl, y1, xl, y2); + p.drawLine(xr, y1, xr, y2); + + } + } +} + +void KickerTip::plainMask() +{ + QPainter maskPainter(&m_mask); + + m_mask.fill(Qt::black); + + maskPainter.setBrush(Qt::white); + maskPainter.setPen(Qt::NoPen); + //maskPainter.drawRoundRect(m_mask.rect(), 1600 / m_mask.rect().width(), 1600 / m_mask.rect().height()); + drawRoundRect(maskPainter, m_mask.rect()); + setMask(m_mask); + m_frameTimer.stop(); +} + +void KickerTip::dissolveMask() +{ + QPainter maskPainter(&m_mask); + + m_mask.fill(Qt::black); + + maskPainter.setBrush(Qt::white); + maskPainter.setPen(Qt::NoPen); + //maskPainter.drawRoundRect(m_mask.rect(), 1600 / m_mask.rect().width(), 1600 / m_mask.rect().height()); + drawRoundRect(maskPainter, m_mask.rect()); + + m_dissolveSize += m_dissolveDelta; + + if (m_dissolveSize > 0) + { + maskPainter.setRasterOp(Qt::EraseROP); + + int x, y, s; + const int size = 16; + + for (y = 0; y < height() + size; y += size) + { + x = width(); + s = 4 * m_dissolveSize * x / 128; + for (; x > -size; x -= size, s -= 2) + { + if (s < 0) + { + break; + } + maskPainter.drawEllipse(x - s / 2, y - s / 2, s, s); + } + } + } + else if (m_dissolveSize < 0) + { + m_frameTimer.stop(); + m_dissolveDelta = 1; + } + + setMask(m_mask); +} + +void KickerTip::displayInternal() +{ + // we need to check for m_tippingFor here as well as m_richText + // since if one is really persistant and moves the mouse around very fast + // you can trigger a situation where m_tippingFor gets reset to 0 but + // before display() is called! + if (!m_tippingFor || !m_richText) + { + return; + } + + // determine text rectangle + QRect textRect(0, 0, 0, 0); + if (KickerSettings::mouseOversShowText()) + { + textRect.setWidth(m_richText->widthUsed()); + textRect.setHeight(m_richText->height()); + } + + int margin = KDialog::marginHint(); + int height = QMAX(m_icon.height(), textRect.height()) + 2 * margin; + int textX = m_icon.isNull() ? margin : 2 + m_icon.width() + 2 * margin; + int width = textX + textRect.width() + margin; + int textY = (height - textRect.height()) / 2; + + // resize pixmap, mask and widget + bool firstTime = m_dissolveSize == 24; + if (firstTime) + { + m_mask.resize(width, height); + m_pixmap.resize(width, height); + resize(width, height); + if (isVisible()) + { + // we've already been shown before, but we may grow larger. + // in the case of Up or Right displaying tips, this growth can + // result in the tip occluding the panel and causing it to redraw + // once we return back to display() causing horrid flicker + move(KickerLib::popupPosition(m_direction, this, m_tippingFor)); + } + } + + // create and set transparency mask + switch(m_maskEffect) + { + case Plain: + plainMask(); + break; + + case Dissolve: + dissolveMask(); + break; + } + + // draw background + QPainter bufferPainter(&m_pixmap); + bufferPainter.setPen(colorGroup().foreground()); + bufferPainter.setBrush(colorGroup().background()); + //bufferPainter.drawRoundRect(0, 0, width, height, 1600 / width, 1600 / height); + drawRoundRect(bufferPainter, QRect(0, 0, width, height)); + + // draw icon if present + if (!m_icon.isNull()) + { + bufferPainter.drawPixmap(margin, + margin, + m_icon, 0, 0, + m_icon.width(), m_icon.height()); + } + + if (KickerSettings::mouseOversShowText()) + { + // draw text shadow + QColorGroup cg = colorGroup(); + cg.setColor(QColorGroup::Text, cg.background().dark(115)); + int shadowOffset = QApplication::reverseLayout() ? -1 : 1; + m_richText->draw(&bufferPainter, textX + shadowOffset, textY + 1, QRect(), cg); + + // draw text + cg = colorGroup(); + m_richText->draw(&bufferPainter, textX, textY, rect(), cg); + } +} + +void KickerTip::tipFor(const QWidget* w) +{ + if (m_tippingFor) + { + disconnect(m_tippingFor, SIGNAL(destroyed(QObject*)), + this, SLOT(tipperDestroyed(QObject*))); + } + + m_tippingFor = w; + + if (m_tippingFor) + { + connect(m_tippingFor, SIGNAL(destroyed(QObject*)), + this, SLOT(tipperDestroyed(QObject*))); + } +} + +void KickerTip::untipFor(const QWidget* w) +{ + if (isTippingFor(w)) + hide(); +} + +bool KickerTip::isTippingFor(const QWidget* w) const +{ + return m_tippingFor == w; +} + +void KickerTip::tipperDestroyed(QObject* o) +{ + // we can't do a dynamic cast because we are in the process of dieing + // so static it is. + untipFor(static_cast(o)); +} + +void KickerTip::internalUpdate() +{ + m_dirty = true; + repaint(false); +} + +void KickerTip::enableTipping(bool tip) +{ + if (tip) + { + m_tippingEnabled++; + } + else + { + m_tippingEnabled--; + } + + if (m_tippingEnabled < 1 && m_self) + { + m_self->hide(); + } +} + +bool KickerTip::tippingEnabled() +{ + return m_tippingEnabled > 0; +} + +void KickerTip::hide() +{ + tipFor(0); + m_timer.stop(); + m_frameTimer.stop(); + QWidget::hide(); +} + +bool KickerTip::eventFilter(QObject *object, QEvent *event) +{ + if (!tippingEnabled()) + { + return false; + } + + if (!object->isWidgetType()) + { + return false; + } + + QWidget *widget = static_cast(object); + + switch (event->type()) + { + case QEvent::Enter: + if (!KickerSettings::showMouseOverEffects()) + { + return false; + } + + if (!mouseGrabber() && + !qApp->activePopupWidget() && + !isTippingFor(widget)) + { + m_toolTipsEnabled = QToolTip::isGloballyEnabled(); + QToolTip::setGloballyEnabled(false); + + tipFor(widget); + m_timer.stop(); + disconnect(&m_timer, SIGNAL(timeout()), 0, 0); + connect(&m_timer, SIGNAL(timeout()), SLOT(display())); + + // delay to avoid false starts + // e.g. when the user quickly zooms their mouse over + // a button then out of kicker + if (isVisible()) + { + m_timer.start(150, true); + } + else + { + m_timer.start(KickerSettings::mouseOversShowDelay(), true); + } + } + break; + case QEvent::Leave: + QToolTip::setGloballyEnabled(m_toolTipsEnabled); + + m_timer.stop(); + + if (isTippingFor(widget) && isVisible()) + { + disconnect(&m_timer, SIGNAL(timeout()), 0, 0); + connect(&m_timer, SIGNAL(timeout()), SLOT(hide())); + m_timer.start(KickerSettings::mouseOversHideDelay(), true); + } + + tipFor(0); + break; + case QEvent::MouseButtonPress: + QToolTip::setGloballyEnabled(m_toolTipsEnabled); + hide(); + default: + break; + } + + return false; +} + +#include + -- cgit v1.2.1