• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

libplasma

applethandle.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2007 by Kevin Ottens <ervin@kde.org>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU Library General Public License as
00006  *   published by the Free Software Foundation; either version 2, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details
00013  *
00014  *   You should have received a copy of the GNU Library General Public
00015  *   License along with this program; if not, write to the
00016  *   Free Software Foundation, Inc.,
00017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018  */
00019 
00020 #include "applethandle_p.h"
00021 
00022 #include <QApplication>
00023 #include <QBitmap>
00024 #include <QtGui/QGraphicsSceneMouseEvent>
00025 #include <QtGui/QLinearGradient>
00026 #include <QtGui/QPainter>
00027 #include <QtGui/QApplication>
00028 
00029 #include <KColorScheme>
00030 #include <KGlobalSettings>
00031 #include <KIcon>
00032 #include <KWindowSystem>
00033 
00034 #include <cmath>
00035 #include <math.h>
00036 
00037 #include "applet.h"
00038 #include "applet_p.h"
00039 #include "containment.h"
00040 #include "corona.h"
00041 #include "paintutils.h"
00042 #include "theme.h"
00043 #include "view.h"
00044 
00045 namespace Plasma
00046 {
00047 
00048 qreal _k_angleForPoints(const QPointF &center, const QPointF &pt1, const QPointF &pt2);
00049 
00050 AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &hoverPos)
00051     : QObject(),
00052       QGraphicsItem(parent),
00053       m_pressedButton(NoButton),
00054       m_containment(parent),
00055       m_applet(applet),
00056       m_opacity(0.0),
00057       m_anim(FadeIn),
00058       m_animId(0),
00059       m_angle(0.0),
00060       m_tempAngle(0.0),
00061       m_scaleWidth(1.0),
00062       m_scaleHeight(1.0),
00063       m_buttonsOnRight(false),
00064       m_pendingFade(false),
00065       m_topview(0),
00066       m_entryPos(hoverPos)
00067 {
00068     KColorScheme colorScheme(QPalette::Active, KColorScheme::View, Theme::defaultTheme()->colorScheme());
00069     m_gradientColor = colorScheme.background(KColorScheme::NormalBackground).color();
00070 
00071     QTransform originalMatrix = m_applet->transform();
00072     m_applet->resetTransform();
00073 
00074     QRectF rect(m_applet->contentsRect());
00075     QPointF center = rect.center();
00076     originalMatrix.translate(center.x(), center.y());
00077 
00078     qreal cosine = originalMatrix.m11();
00079     qreal sine = originalMatrix.m12();
00080 
00081     m_angle = _k_angleForPoints(QPointF(0, 0),
00082                                 QPointF(1, 0),
00083                                 QPointF(cosine, sine));
00084 
00085     m_applet->setParentItem(this);
00086 
00087     rect = QRectF(m_applet->pos(), m_applet->size());
00088     center = rect.center();
00089     QTransform matrix;
00090     matrix.translate(center.x(), center.y());
00091     matrix.rotateRadians(m_angle);
00092     matrix.translate(-center.x(), -center.y());
00093     setTransform(matrix);
00094 
00095     m_hoverTimer = new QTimer(this);
00096     m_hoverTimer->setSingleShot(true);
00097     m_hoverTimer->setInterval(333);
00098 
00099     m_leaveTimer = new QTimer(this);
00100     m_leaveTimer->setSingleShot(true);
00101     m_leaveTimer->setInterval(500);
00102 
00103     connect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(fadeIn()));
00104     connect(m_leaveTimer, SIGNAL(timeout()), this, SLOT(leaveTimeout()));
00105     connect(m_applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed()));
00106 
00107     setAcceptsHoverEvents(true);
00108     m_hoverTimer->start();
00109 
00110     //We got to be able to see the applet while dragging to to another containment,
00111     //so we want a high zValue.
00112     //FIXME: apparently this doesn't work: sometimes an applet still get's drawn behind
00113     //the containment it's being dragged to, sometimes it doesn't.
00114     m_zValue = m_applet->zValue();
00115     m_applet->raise();
00116     m_applet->installSceneEventFilter(this);
00117     setZValue(m_applet->zValue());
00118 }
00119 
00120 AppletHandle::~AppletHandle()
00121 {
00122     if (m_applet) {
00123         m_applet->removeSceneEventFilter(this);
00124 
00125         QRectF rect = QRectF(m_applet->pos(), m_applet->size());
00126         QPointF center = m_applet->mapFromParent(rect.center());
00127 
00128         QPointF newPos = transform().inverted().map(m_applet->pos());
00129         m_applet->setPos(mapToParent(newPos));
00130 
00131         QTransform matrix;
00132         matrix.translate(center.x(), center.y());
00133         matrix.rotateRadians(m_angle);
00134         matrix.translate(-center.x(), -center.y());
00135         m_applet->setTransform(matrix);
00136 
00137         m_applet->setParentItem(m_containment);
00138 
00139         m_applet->setZValue(m_zValue);
00140 
00141         m_applet->update(); // re-render the background, now we've transformed the applet
00142     }
00143     if (m_topview) {
00144         delete m_topview;
00145     }
00146 }
00147 
00148 Applet *AppletHandle::applet() const
00149 {
00150     return m_applet;
00151 }
00152 
00153 QRectF Plasma::AppletHandle::boundingRect() const
00154 {
00155     return m_totalRect;
00156 }
00157 
00158 QPainterPath AppletHandle::shape() const
00159 {
00160     //when the containment changes the applet is resetted to 0
00161     if (m_applet) {
00162         QPainterPath path = PaintUtils::roundedRectangle(m_rect, 10);
00163         return path.united(m_applet->mapToParent(m_applet->shape()));
00164     } else {
00165         return QGraphicsItem::shape();
00166     }
00167 }
00168 
00169 QPainterPath handleRect(const QRectF &rect, int radius, bool onRight)
00170 {
00171     QPainterPath path;
00172     if (onRight) {
00173         // make the left side straight
00174         path.moveTo(rect.left(), rect.top());                                            // Top left
00175         path.lineTo(rect.right() - radius, rect.top());                                 // Top side
00176         path.quadTo(rect.right(), rect.top(), rect.right(), rect.top() + radius);       // Top right corner
00177         path.lineTo(rect.right(), rect.bottom() - radius);                              // Right side
00178         path.quadTo(rect.right(), rect.bottom(), rect.right() - radius, rect.bottom()); // Bottom right corner
00179         path.lineTo(rect.left(), rect.bottom());                                        // Bottom side
00180     } else {
00181         // make the right side straight
00182         path.moveTo(QPointF(rect.left(), rect.top() + radius));
00183         path.quadTo(rect.left(), rect.top(), rect.left() + radius, rect.top());         // Top left corner
00184         path.lineTo(rect.right(), rect.top());                                          // Top side
00185         path.lineTo(rect.right(), rect.bottom());                                       // Right side
00186         path.lineTo(rect.left() + radius, rect.bottom());                               // Bottom side
00187         path.quadTo(rect.left(), rect.bottom(), rect.left(), rect.bottom() - radius);   // Bottom left corner
00188     }
00189 
00190     path.closeSubpath();
00191     return path;
00192 }
00193 
00194 void AppletHandle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00195 {
00196     Q_UNUSED(option);
00197     Q_UNUSED(widget);
00198 
00199     painter->save();
00200     painter->setOpacity(m_opacity);
00201 
00202     painter->save();
00203     painter->setOpacity(m_opacity * 0.8);
00204     painter->setPen(Qt::NoPen);
00205     painter->setRenderHints(QPainter::Antialiasing);
00206 
00207     QPainterPath path = handleRect(m_rect, 10, m_buttonsOnRight);
00208     painter->strokePath(path, m_gradientColor);
00209     painter->fillPath(path, m_gradientColor.lighter());
00210     painter->restore();
00211 
00212     //XXX this code is duplicated in the next function
00213     QPointF basePoint = m_rect.topLeft() + QPointF((HANDLE_WIDTH - ICON_SIZE) / 2, ICON_MARGIN);
00214     QPointF step = QPointF(0, ICON_SIZE + ICON_MARGIN);
00215     QPointF separator = step + QPointF(0, ICON_MARGIN);
00216     //end duplicate code
00217 
00218     QPointF shiftC;
00219     QPointF shiftD;
00220     QPointF shiftR;
00221     QPointF shiftM;
00222 
00223     switch(m_pressedButton)
00224     {
00225     case ConfigureButton:
00226         shiftC = QPointF(2, 2);
00227         break;
00228     case RemoveButton:
00229         shiftD = QPointF(2, 2);
00230         break;
00231     case RotateButton:
00232         shiftR = QPointF(2, 2);
00233         break;
00234     case ResizeButton:
00235         shiftM = QPointF(2, 2);
00236         break;
00237     default:
00238         break;
00239     }
00240 
00241     painter->drawPixmap(basePoint + shiftM, KIcon("transform-scale").pixmap(ICON_SIZE, ICON_SIZE)); //FIXME no transform-resize icon
00242 
00243     basePoint += step;
00244     painter->drawPixmap(basePoint + shiftR, KIcon("transform-rotate").pixmap(ICON_SIZE, ICON_SIZE));
00245 
00246     if (m_applet && m_applet->hasConfigurationInterface()) {
00247         basePoint += step;
00248         painter->drawPixmap(basePoint + shiftC, KIcon("configure").pixmap(ICON_SIZE, ICON_SIZE));
00249     }
00250 
00251     basePoint = m_rect.bottomLeft() + QPointF((HANDLE_WIDTH - ICON_SIZE) / 2, 0) - step;
00252     painter->drawPixmap(basePoint + shiftD, KIcon("edit-delete").pixmap(ICON_SIZE, ICON_SIZE));
00253 
00254     painter->restore();
00255 }
00256 
00257 AppletHandle::ButtonType AppletHandle::mapToButton(const QPointF &point) const
00258 {
00259     //XXX this code is duplicated in the prev. function
00260     QPointF basePoint = m_rect.topLeft() + QPointF((HANDLE_WIDTH - ICON_SIZE) / 2, ICON_MARGIN);
00261     QPointF step = QPointF(0, ICON_SIZE + ICON_MARGIN);
00262     QPointF separator = step + QPointF(0, ICON_MARGIN);
00263    //end duplicate code
00264 
00265     QRectF activeArea = QRectF(basePoint, QSizeF(ICON_SIZE, ICON_SIZE));
00266 
00267     if (activeArea.contains(point)) {
00268         return ResizeButton;
00269     }
00270 
00271     activeArea.translate(step);
00272     if (activeArea.contains(point)) {
00273         return RotateButton;
00274     }
00275 
00276     if (m_applet && m_applet->hasConfigurationInterface()) {
00277         activeArea.translate(step);
00278         if (activeArea.contains(point)) {
00279             return ConfigureButton;
00280         }
00281     }
00282 
00283     activeArea.moveTop(m_rect.bottom() - activeArea.height() - ICON_MARGIN);
00284     if (activeArea.contains(point)) {
00285         return RemoveButton;
00286     }
00287 
00288     return MoveButton;
00289     //return m_applet->mapToParent(m_applet->shape()).contains(point) ? NoButton : MoveButton;
00290 }
00291 
00292 
00293 void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event)
00294 {
00295     //containment recently switched?
00296     if (!m_applet) {
00297         QGraphicsItem::mousePressEvent(event);
00298         return;
00299     }
00300 
00301     if (m_pendingFade) {
00302         //m_pendingFade = false;
00303         return;
00304     }
00305 
00306     if (event->button() == Qt::LeftButton) {
00307         m_pressedButton = mapToButton(event->pos());
00308         //kDebug() << "button pressed:" << m_pressedButton;
00309         if (m_pressedButton != NoButton) {
00310             m_applet->raise();
00311             m_zValue = m_applet->zValue();
00312             setZValue(m_zValue);
00313         }
00314 
00315         if (m_pressedButton == MoveButton) {
00316             m_pos = pos();
00317         }
00318         event->accept();
00319 
00320         update();
00321 
00322         //set mousePos to the position in the applet, in screencoords, so it becomes easy
00323         //to reposition the toplevel view to the correct position.
00324         QPoint localpos = m_containment->view()->mapFromScene(m_applet->scenePos());
00325         m_mousePos = event->screenPos() - m_containment->view()->mapToGlobal(localpos);
00326 
00327         return;
00328     }
00329 
00330     QGraphicsItem::mousePressEvent(event);
00331 }
00332 
00333 bool AppletHandle::leaveCurrentView(const QPoint &pos) const
00334 {
00335     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
00336         if (widget->geometry().contains(pos)) {
00337             //is this widget a plasma view, a different view then our current one,
00338             //AND not a dashboardview?
00339             Plasma::View *v = qobject_cast<Plasma::View *>(widget);
00340             if (v && v != m_applet->containment()->view()
00341                   && v != m_topview
00342                   && v->containment() != m_containment) {
00343                 return true;
00344             }
00345         }
00346     }
00347     return false;
00348 }
00349 
00350 
00351 void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00352 {
00353     //kDebug() << "button pressed:" << m_pressedButton << ", fade pending?" << m_pendingFade;
00354 
00355     if (m_pendingFade) {
00356         startFading(FadeOut, m_entryPos);
00357         m_pendingFade = false;
00358     }
00359 
00360     ButtonType releasedAtButton = mapToButton(event->pos());
00361 
00362     if (m_applet && event->button() == Qt::LeftButton) {
00363         switch (m_pressedButton) {
00364             case ResizeButton:
00365             case RotateButton: {
00366                 if (m_scaleWidth > 0 && m_scaleHeight > 0) {
00367                     QRectF rect(m_applet->boundingRect());
00368                     const qreal newWidth = rect.width() * m_scaleWidth;
00369                     const qreal newHeight = rect.height() * m_scaleHeight;
00370                     m_applet->resetTransform();
00371                     m_applet->resize(newWidth, newHeight);
00372                     scale(1.0/m_scaleWidth, 1.0/m_scaleHeight);
00373                     moveBy((rect.width() - newWidth) / 2, (rect.height() - newHeight) / 2);
00374                     m_scaleWidth = m_scaleHeight = 0;
00375                 }
00376                 QRectF rect = QRectF(m_applet->pos(), m_applet->size());
00377                 QPointF center = rect.center();
00378 
00379                 m_angle += m_tempAngle;
00380                 m_tempAngle = 0;
00381 
00382                 QTransform matrix;
00383                 matrix.translate(center.x(), center.y());
00384                 matrix.rotateRadians(m_angle);
00385                 matrix.translate(-center.x(), -center.y());
00386 
00387                 setTransform(matrix);
00388                 m_applet->update();
00389                 break;
00390             }
00391             case ConfigureButton:
00392                 //FIXME: Remove this call once the configuration management change was done
00393                 if (m_pressedButton == releasedAtButton) {
00394                     m_applet->showConfigurationInterface();
00395                 }
00396                 break;
00397             case RemoveButton:
00398                 if (m_pressedButton == releasedAtButton) {
00399                     forceDisappear();
00400                     m_applet->destroy();
00401                 }
00402                 break;
00403             case MoveButton: {
00404                 if (m_topview) {
00405                     m_topview->hide();
00406                     delete m_topview;
00407                     m_topview = 0;
00408                     m_applet->d->ghostView = 0;
00409                     m_applet->update();
00410                 }
00411 
00412                 //find out if we were dropped on a panel or something
00413                 if (leaveCurrentView(event->screenPos())) {
00414                     startFading(FadeOut, m_entryPos);
00415                     Plasma::View *v = Plasma::View::topLevelViewAt(event->screenPos());
00416                     if (v && v != m_containment->view()) {
00417                         Containment *c = v->containment();
00418                         QPoint pos = v->mapFromGlobal(event->screenPos());
00419                         //we actually have been dropped on another containment, so
00420                         //move there: we have a screenpos, we need a scenepos
00421                         //FIXME how reliable is this transform?
00422                         switchContainment(c, v->mapToScene(pos));
00423                     }
00424                 } else {
00425                     // test for containment change
00426                     //kDebug() << "testing for containment change, sceneBoundingRect = " << m_containment->sceneBoundingRect();
00427                     if (!m_containment->sceneBoundingRect().contains(m_applet->scenePos())) {
00428                         // see which containment it belongs to
00429                         Corona * corona = qobject_cast<Corona*>(scene());
00430                         if (corona) {
00431                             QList<Containment*> containments = corona->containments();
00432                             for (int i = 0; i < containments.size(); ++i) {
00433                                 QPointF pos;
00434                                 QGraphicsView *v;
00435                                 v = containments[i]->view();
00436                                 if (v) {
00437                                     pos = v->mapToScene(v->mapFromGlobal(event->screenPos() - m_mousePos));
00438 
00439                                     if (containments[i]->sceneBoundingRect().contains(pos)) {
00440                                         //kDebug() << "new containment = " << containments[i];
00441                                         //kDebug() << "rect = " << containments[i]->sceneBoundingRect();
00442                                         // add the applet to the new containment and take it from the old one
00443                                         //kDebug() << "moving to other containment with position" << pos;;
00444                                         switchContainment(containments[i], pos);
00445                                         break;
00446                                     }
00447                                 }
00448                             }
00449                         }
00450                     }
00451                 }
00452                 break;
00453             }
00454             default:
00455                 break;
00456         }
00457     }
00458 
00459     m_pressedButton = NoButton;
00460     update();
00461 }
00462 
00463 qreal _k_distanceForPoint(QPointF point)
00464 {
00465     return std::sqrt(point.x()*point.x()+point.y()*point.y());
00466 }
00467 
00468 qreal _k_angleForPoints(const QPointF &center, const QPointF &pt1, const QPointF &pt2)
00469 {
00470     QPointF vec1 = pt1 - center;
00471     QPointF vec2 = pt2 - center;
00472 
00473     qreal alpha = std::atan2(vec1.y(), vec1.x());
00474     qreal beta = std::atan2(vec2.y(), vec2.x());
00475 
00476     return beta - alpha;
00477 }
00478 
00479 void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00480 {
00481     static const qreal snapAngle = M_PI_2 /* $i 3.14159 / 2.0 */;
00482 
00483     if (!m_applet) {
00484         QGraphicsItem::mouseMoveEvent(event);
00485         return;
00486     }
00487 
00488     //Track how much the mouse has moved.
00489     QPointF deltaScene  = event->scenePos() - event->lastScenePos();
00490 
00491     if (m_pressedButton == MoveButton) {
00492         m_pos += deltaScene;
00493 
00494         //Are we moving out of the current view?
00495         bool toTopLevel = leaveCurrentView(event->screenPos());
00496 
00497         if (!toTopLevel) {
00498             setPos(m_pos);
00499             if (m_topview) {
00500                 //We were on a toplevel view, but are moving back on the scene
00501                 //again. destroy the toplevel view:
00502                 m_topview->hide();
00503                 delete m_topview;
00504                 m_topview = 0;
00505                 m_applet->d->ghostView = 0;
00506             }
00507         } else {
00508             //set the screenRect correctly. the screenRect contains the bounding
00509             //rect of the applet in screen coordinates. m_mousePos contains the
00510             //position of the mouse relative to the applet, in screen coords.
00511             QRect screenRect = QRect(event->screenPos() - m_mousePos,
00512                                      m_applet->screenRect().size());
00513 
00514             //kDebug() << "screenRect = " << screenRect;
00515 
00516             if (!m_topview) { //create a new toplevel view
00517                 m_topview = new View(m_containment, -1, 0);
00518 
00519                 m_topview->setTrackContainmentChanges(false);
00520                 m_topview->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint
00521                                                       | Qt::WindowStaysOnTopHint);
00522                 m_topview->setWallpaperEnabled(false);
00523                 m_topview->resize(screenRect.size());
00524                 m_topview->setSceneRect(m_applet->sceneBoundingRect());
00525                 m_topview->centerOn(m_applet);
00526 
00527                 //We might have to scale the view, because we might be zoomed out.
00528                 qreal scale = screenRect.width() / m_applet->boundingRect().width();
00529                 m_topview->scale(scale, scale);
00530 
00531                 //Paint a mask based on the applets shape.
00532                 //TODO: I think it's nicer to have this functionality in Applet.
00533                 //TODO: When the corona tiled background is disabled, disable the
00534                 //mask when compositing is enabled.
00535                 //FIXME: the mask doesn't function correctly when zoomed out.
00536                 QBitmap bitmap(screenRect.size());
00537                 QPainter * shapePainter = new QPainter();
00538                 shapePainter->begin(&bitmap);
00539                 shapePainter->fillRect(0, 0, screenRect.width(),
00540                                              screenRect.height(),
00541                                              Qt::white);
00542                 shapePainter->setBrush(Qt::black);
00543                 shapePainter->drawPath(m_applet->shape());
00544                 shapePainter->end();
00545                 delete shapePainter;
00546                 m_topview->setMask(bitmap);
00547 
00548                 m_topview->show();
00549 
00550                 m_applet->d->ghostView = m_containment->view();
00551 
00552                 //TODO: non compositing users are screwed: masking looks terrible.
00553                 //Consider always enabling the applet background. Stuff like the analog clock
00554                 //looks absolutely terrible when masked, while the minor rounded corners of most
00555                 //themes should look quite ok. I said should, since shape() doesn't really
00556                 //function correctly right now for applets drawing standard backgrounds.
00557             }
00558 
00559             m_topview->setGeometry(screenRect);
00560         }
00561 
00562     } else if (m_pressedButton == RotateButton ||
00563                m_pressedButton == ResizeButton) {
00564         if (_k_distanceForPoint(deltaScene) <= 1.0) {
00565             return;
00566         }
00567 
00568         QPointF pressPos = mapFromScene(event->buttonDownScenePos(Qt::LeftButton));
00569 
00570         QRectF rect = QRectF(m_applet->pos(), m_applet->size());
00571         QPointF center = rect.center();
00572 
00573         if (m_pressedButton == RotateButton) {
00574             m_tempAngle = _k_angleForPoints(center, pressPos, event->pos());
00575 
00576             if (fabs(remainder(m_angle+m_tempAngle, snapAngle)) < 0.15) {
00577                 m_tempAngle = m_tempAngle - remainder(m_angle+m_tempAngle, snapAngle);
00578             }
00579 
00580             m_scaleWidth = m_scaleHeight = 1.0;
00581         } else {
00582             qreal w = m_applet->size().width();
00583             qreal h = m_applet->size().height();
00584             QSizeF min = m_applet->minimumSize();
00585             QSizeF max = m_applet->maximumSize();
00586 
00587             // If the applet doesn't have a minimum size, calculate based on a
00588             // minimum content area size of 16x16
00589             if (min.isEmpty()) {
00590                 min = m_applet->boundingRect().size() - m_applet->boundingRect().size();
00591                 min += QSizeF(16, 16);
00592             }
00593 
00594             bool ignoreAspectRatio = m_applet->aspectRatioMode() == Plasma::IgnoreAspectRatio;
00595 
00596             if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
00597                 ignoreAspectRatio = !ignoreAspectRatio;
00598             }
00599 
00600             if (ignoreAspectRatio) {
00601                 // free resizing
00602                 qreal newScaleWidth = 0;
00603                 qreal newScaleHeight = 0;
00604 
00605                 QPointF startDistance(pressPos - center);
00606                 QPointF currentDistance(event->pos() - center);
00607                 newScaleWidth = currentDistance.x() / startDistance.x();
00608                 newScaleHeight = currentDistance.y() / startDistance.y();
00609 
00610                 if (qAbs(w - (newScaleWidth * w)) <= KGlobalSettings::dndEventDelay()) {
00611                     newScaleWidth = 1.0;
00612                 }
00613                 if (qAbs(h - (newScaleHeight * h)) <= KGlobalSettings::dndEventDelay()) {
00614                     newScaleHeight = 1.0;
00615                 }
00616 
00617                 if (newScaleHeight * h < min.height()) {
00618                     m_scaleHeight = min.height() / h;
00619                 } else if (newScaleHeight * h > max.height()) {
00620                     m_scaleHeight = max.height() / h;
00621                 } else {
00622                     m_scaleHeight = newScaleHeight;
00623                 }
00624                 if (newScaleWidth * w < min.width()) {
00625                     m_scaleWidth = min.width() / w;
00626                 } else if (newScaleWidth * w > max.width()) {
00627                     m_scaleWidth = max.width() / w;
00628                 } else {
00629                     m_scaleWidth = newScaleWidth;
00630                 }
00631             } else {
00632                 // maintain aspect ratio
00633                 qreal newScale = 0;
00634 
00635                 newScale = _k_distanceForPoint(event->pos()-center) / _k_distanceForPoint(pressPos-center);
00636                 if (qAbs(h - (newScale * h)) <= KGlobalSettings::dndEventDelay()) {
00637                     newScale = 1.0;
00638                 }
00639 
00640                 if (newScale * w < min.width() || newScale * h < min.height()) {
00641                     m_scaleWidth = m_scaleHeight = qMax(min.width() / w, min.height() / h);
00642                 } else if (newScale * w > max.width() && newScale * h > max.height()) {
00643                     m_scaleWidth = m_scaleHeight = qMin(max.width() / w, max.height() / h);
00644                 } else {
00645                     m_scaleHeight = m_scaleWidth = newScale;
00646                 }
00647             }
00648         }
00649 
00650         QTransform matrix;
00651         matrix.translate(center.x(), center.y());
00652         matrix.rotateRadians(m_angle+m_tempAngle);
00653         matrix.scale(m_scaleWidth, m_scaleHeight);
00654         matrix.translate(-center.x(), -center.y());
00655         setTransform(matrix);
00656     } else {
00657         QGraphicsItem::mouseMoveEvent(event);
00658     }
00659 }
00660 
00661 
00662 //pos relative to scene
00663 void AppletHandle::switchContainment(Containment *containment, const QPointF &pos)
00664 {
00665     if (containment->containmentType() != Containment::PanelContainment) {
00666         //FIXME assuming everything else behaves like desktop?
00667         kDebug() << "desktop";
00668         m_containment = containment;
00669     }
00670 
00671     Applet *applet = m_applet;
00672     m_applet = 0; //make sure we don't try to act on the applet again
00673     applet->removeSceneEventFilter(this);
00674     forceDisappear(); //takes care of event filter and killing handle
00675     applet->disconnect(this); //make sure the applet doesn't tell us to do anything
00676     applet->setZValue(m_zValue);
00677     containment->addApplet(applet, containment->mapFromScene(pos));
00678     update();
00679 }
00680 
00681 QVariant AppletHandle::itemChange(GraphicsItemChange change, const QVariant &value)
00682 {
00683     if (change == ItemPositionHasChanged && m_applet) {
00684         m_applet->updateConstraints(Plasma::LocationConstraint);
00685     }
00686     return QGraphicsItem::itemChange(change, value);
00687 }
00688 
00689 void AppletHandle::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
00690 {
00691     Q_UNUSED(event);
00692     //kDebug() << "hover enter";
00693 
00694     //if a disappear was scheduled stop the timer
00695     m_leaveTimer->stop();
00696     //schedule appear
00697     m_hoverTimer->start();
00698 }
00699 
00700 void AppletHandle::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
00701 {
00702     Q_UNUSED(event);
00703     m_leaveTimer->stop();
00704 }
00705 
00706 void AppletHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
00707 {
00708     Q_UNUSED(event);
00709     m_hoverTimer->stop();
00710 
00711     if (m_pressedButton != NoButton) {
00712         m_pendingFade = true;
00713     } else {
00714         //wait a moment to hide the handle in order to recheck the mouse position
00715         m_leaveTimer->start();
00716     }
00717 }
00718 
00719 bool AppletHandle::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
00720 {
00721     if (watched == m_applet && event->type() == QEvent::GraphicsSceneHoverLeave) {
00722         hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event));
00723     }
00724 
00725     return false;
00726 }
00727 
00728 void AppletHandle::fadeAnimation(qreal progress)
00729 {
00730     qreal endOpacity = (m_anim == FadeIn) ? 1.0 : 0.0;
00731     m_opacity += (endOpacity - m_opacity) * progress;
00732     //kDebug() << "progress" << progress << "m_opacity" << m_opacity << endOpacity;
00733     if (progress >= 1.0 && m_anim == FadeOut) {
00734         emit disappearDone(this);
00735     }
00736 
00737     update();
00738 }
00739 
00740 void AppletHandle::fadeIn()
00741 {
00742     startFading(FadeIn, m_entryPos);
00743 }
00744 
00745 void AppletHandle::leaveTimeout()
00746 {
00747     startFading(FadeOut, m_entryPos);
00748 }
00749 
00750 void AppletHandle::appletDestroyed()
00751 {
00752     m_applet = 0;
00753     deleteLater();
00754 }
00755 
00756 void AppletHandle::appletResized()
00757 {
00758     prepareGeometryChange();
00759     calculateSize();
00760     update();
00761 }
00762 
00763 void AppletHandle::startFading(FadeType anim, const QPointF &hoverPos)
00764 {
00765     if (m_animId != 0) {
00766         Animator::self()->stopCustomAnimation(m_animId);
00767     }
00768 
00769     m_hoverTimer->stop();
00770     m_leaveTimer->stop();
00771 
00772     m_entryPos = hoverPos;
00773     qreal time = 250;
00774 
00775     if (!m_applet || (anim == FadeOut && m_hoverTimer->isActive())) {
00776         // fading out before we've started fading in
00777         fadeAnimation(1.0);
00778         return;
00779     }
00780 
00781     if (anim == FadeIn) {
00782         //kDebug() << m_entryPos.x() << m_applet->pos().x();
00783         prepareGeometryChange();
00784         bool wasOnRight = m_buttonsOnRight;
00785         m_buttonsOnRight = m_entryPos.x() > (m_applet->size().width() / 2);
00786         calculateSize();
00787         QPolygonF region = mapToParent(m_rect).intersected(parentWidget()->boundingRect());
00788         //kDebug() << region << m_rect << mapToParent(m_rect) << parentWidget()->boundingRect();
00789         if (region != mapToParent(m_rect)) {
00790             // switch sides
00791             //kDebug() << "switch sides";
00792             m_buttonsOnRight = !m_buttonsOnRight;
00793             calculateSize();
00794             QPolygonF region2 = mapToParent(m_rect).intersected(parentWidget()->boundingRect());
00795             if (region2 != mapToParent(m_rect)) {
00796                 // ok, both sides failed to be perfect... which one is more perfect?
00797                 QRectF f1 = region.boundingRect();
00798                 QRectF f2 = region2.boundingRect();
00799                 //kDebug() << "still not a perfect world" << f2.width() << f2.height() << f1.width() << f1.height();
00800                 if ((f2.width() * f2.height()) < (f1.width() * f1.height())) {
00801                     //kDebug() << "we did better the first time";
00802                     m_buttonsOnRight = !m_buttonsOnRight;
00803                     calculateSize();
00804                 }
00805             }
00806         }
00807 
00808         if (wasOnRight != m_buttonsOnRight && m_anim == FadeIn && anim == FadeIn && m_opacity <= 1) {
00809             m_opacity = 0.0;
00810         }
00811 
00812         time *= 1.0 - m_opacity;
00813     } else {
00814         time *= m_opacity;
00815     }
00816 
00817     m_anim = anim;
00818     m_animId = Animator::self()->customAnimation(40, (int)time, Animator::EaseInOutCurve, this, "fadeAnimation");
00819 }
00820 
00821 
00822 void AppletHandle::forceDisappear()
00823 {
00824     setAcceptsHoverEvents(false);
00825     startFading(FadeOut, m_entryPos);
00826 }
00827 
00828 void AppletHandle::calculateSize()
00829 {
00830     int requiredHeight =  ICON_MARGIN + //first margin
00831                           (ICON_SIZE + ICON_MARGIN) * 4 + //XXX remember to update this if the number of buttons changes
00832                           ICON_MARGIN;  //blank space before the close button
00833 
00834     if (m_applet->hasConfigurationInterface()) {
00835         requiredHeight += (ICON_SIZE + ICON_MARGIN);
00836     }
00837 
00838     int top = m_applet->contentsRect().top();
00839 
00840     if (requiredHeight > m_applet->contentsRect().height()) {
00841         top += (m_applet->contentsRect().height() - requiredHeight) / 2.0;
00842     } else {
00843         requiredHeight = m_applet->contentsRect().height();
00844     }
00845 
00846     if (m_buttonsOnRight) {
00847         //put the rect on the right of the applet
00848         m_rect = QRectF(m_applet->size().width(), top, HANDLE_WIDTH, requiredHeight);
00849     } else {
00850         //put the rect on the left of the applet
00851         m_rect = QRectF(-HANDLE_WIDTH, top, HANDLE_WIDTH, requiredHeight);
00852     }
00853 
00854     m_rect = m_applet->mapToParent(m_rect).boundingRect();
00855     m_totalRect = m_rect.united(m_applet->geometry());
00856 }
00857 
00858 } // Plasma Namespace
00859 
00860 #include "applethandle_p.moc"

libplasma

Skip menu "libplasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal