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

Plasma

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 "private/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 #include <QtGui/QMenu>
00029 
00030 #include <kcolorscheme.h>
00031 #include <kglobalsettings.h>
00032 #include <kicon.h>
00033 #include <kiconloader.h>
00034 #include <kwindowsystem.h>
00035 
00036 #include <cmath>
00037 #include <math.h>
00038 
00039 #include "applet.h"
00040 #include "applet_p.h"
00041 #include "containment.h"
00042 #include "corona.h"
00043 #include "paintutils.h"
00044 #include "theme.h"
00045 #include "view.h"
00046 #include "framesvg.h"
00047 
00048 namespace Plasma
00049 {
00050 
00051 qreal _k_distanceForPoint(QPointF point);
00052 qreal _k_pointAngle(QPointF in);
00053 QPointF _k_rotatePoint(QPointF in, qreal rotateAngle);
00054 
00055 AppletHandle::AppletHandle(Containment *parent, Applet *applet, const QPointF &hoverPos)
00056     : QObject(),
00057       QGraphicsItem(parent),
00058       m_pressedButton(NoButton),
00059       m_containment(parent),
00060       m_applet(applet),
00061       m_iconSize(KIconLoader::SizeSmall),
00062       m_opacity(0.0),
00063       m_anim(FadeIn),
00064       m_animId(0),
00065       m_angle(0.0),
00066       m_backgroundBuffer(0),
00067       m_currentView(applet->view()),
00068       m_entryPos(hoverPos),
00069       m_buttonsOnRight(false),
00070       m_pendingFade(false)
00071 {
00072     KColorScheme colorScheme(QPalette::Active, KColorScheme::View,
00073                              Theme::defaultTheme()->colorScheme());
00074     m_gradientColor = colorScheme.background(KColorScheme::NormalBackground).color();
00075 
00076     m_originalGeom = m_applet->geometry();
00077     m_originalTransform = m_applet->transform();
00078 
00079     QTransform originalMatrix = m_applet->transform();
00080     m_applet->resetTransform();
00081 
00082     QRectF rect(m_applet->contentsRect());
00083     QPointF center = rect.center();
00084     originalMatrix.translate(center.x(), center.y());
00085 
00086     qreal cosine = originalMatrix.m11();
00087     qreal sine = originalMatrix.m12();
00088 
00089     m_angle = _k_pointAngle(QPointF(cosine, sine));
00090 
00091     m_applet->setParentItem(this);
00092 
00093     rect = QRectF(m_applet->pos(), m_applet->size());
00094     center = rect.center();
00095     QTransform matrix;
00096     matrix.translate(center.x(), center.y());
00097     matrix.rotateRadians(m_angle);
00098     matrix.translate(-center.x(), -center.y());
00099     setTransform(matrix);
00100 
00101     m_hoverTimer = new QTimer(this);
00102     m_hoverTimer->setSingleShot(true);
00103     m_hoverTimer->setInterval(333);
00104 
00105     m_leaveTimer = new QTimer(this);
00106     m_leaveTimer->setSingleShot(true);
00107     m_leaveTimer->setInterval(500);
00108 
00109     connect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()));
00110     connect(m_leaveTimer, SIGNAL(timeout()), this, SLOT(leaveTimeout()));
00111     connect(m_applet, SIGNAL(destroyed(QObject*)), this, SLOT(appletDestroyed()));
00112 
00113     setAcceptsHoverEvents(true);
00114     m_hoverTimer->start();
00115 
00116     //icons
00117     m_configureIcons = new Svg(this);
00118     m_configureIcons->setImagePath("widgets/configuration-icons");
00119     //FIXME: this should be of course true, but works only if false
00120     m_configureIcons->setContainsMultipleImages(true);
00121 
00122     m_background = new FrameSvg(this);
00123     m_background->setImagePath("widgets/background");
00124 
00125     //We got to be able to see the applet while dragging to to another containment,
00126     //so we want a high zValue.
00127     //FIXME: apparently this doesn't work: sometimes an applet still get's drawn behind
00128     //the containment it's being dragged to, sometimes it doesn't.
00129     m_zValue = m_applet->zValue() - 1;
00130     m_applet->raise();
00131     m_applet->installSceneEventFilter(this);
00132     setZValue(m_applet->zValue());
00133 }
00134 
00135 AppletHandle::~AppletHandle()
00136 {
00137     detachApplet();
00138     delete m_backgroundBuffer;
00139 }
00140 
00141 Applet *AppletHandle::applet() const
00142 {
00143     return m_applet;
00144 }
00145 
00146 void AppletHandle::detachApplet ()
00147 {
00148     if (!m_applet) {
00149         return;
00150     }
00151 
00152     disconnect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()));
00153     disconnect(m_leaveTimer, SIGNAL(timeout()), this, SLOT(leaveTimeout()));
00154     m_applet->disconnect(this);
00155 
00156     m_applet->removeSceneEventFilter(this);
00157 
00158     QRectF appletGeomLocal = m_applet->geometry();
00159     QPointF center = mapToParent(appletGeomLocal.center());
00160     QPointF appletPos = QPointF(center.x()-appletGeomLocal.width()/2, center.y()-appletGeomLocal.height()/2);
00161     m_applet->setPos(appletPos);
00162 
00163     // transform is relative to the applet
00164     QTransform t;
00165     t.translate(appletGeomLocal.width()/2, appletGeomLocal.height()/2);
00166     t.rotateRadians(m_angle);
00167     t.translate(-appletGeomLocal.width()/2, -appletGeomLocal.height()/2);
00168     m_applet->setTransform(t);
00169 
00170     m_applet->setParentItem(m_containment);
00171 
00172     m_applet->setZValue(m_zValue);
00173 
00174     if (m_applet->geometry() != m_originalGeom || m_applet->transform() != m_originalTransform) {
00175         emit m_applet->appletTransformedByUser();
00176     }
00177 
00178     m_applet->update(); // re-render the background, now we've transformed the applet
00179 
00180     m_applet = 0;
00181 }
00182 
00183 QRectF Plasma::AppletHandle::boundingRect() const
00184 {
00185     return m_totalRect;
00186 }
00187 
00188 QPainterPath AppletHandle::shape() const
00189 {
00190     //when the containment changes the applet is reset to 0
00191     if (m_applet) {
00192         QPainterPath path = PaintUtils::roundedRectangle(m_decorationRect, 10);
00193         return path.united(m_applet->mapToParent(m_applet->shape()));
00194     } else {
00195         return QGraphicsItem::shape();
00196     }
00197 }
00198 
00199 QPainterPath handleRect(const QRectF &rect, int radius, bool onRight)
00200 {
00201     QPainterPath path;
00202     if (onRight) {
00203         // make the left side straight
00204         path.moveTo(rect.left(), rect.top());                                            // Top left
00205         path.lineTo(rect.right() - radius, rect.top());                                 // Top side
00206         path.quadTo(rect.right(), rect.top(),
00207                     rect.right(), rect.top() + radius);    // Top right corner
00208         path.lineTo(rect.right(), rect.bottom() - radius); // Right side
00209         path.quadTo(rect.right(), rect.bottom(),
00210                     rect.right() - radius, rect.bottom()); // Bottom right corner
00211         path.lineTo(rect.left(), rect.bottom());           // Bottom side
00212     } else {
00213         // make the right side straight
00214         path.moveTo(QPointF(rect.left(), rect.top() + radius));
00215         path.quadTo(rect.left(), rect.top(),
00216                     rect.left() + radius, rect.top());     // Top left corner
00217         path.lineTo(rect.right(), rect.top());             // Top side
00218         path.lineTo(rect.right(), rect.bottom());          // Right side
00219         path.lineTo(rect.left() + radius, rect.bottom());  // Bottom side
00220         path.quadTo(rect.left(), rect.bottom(),
00221                     rect.left(), rect.bottom() - radius);  // Bottom left corner
00222     }
00223 
00224     path.closeSubpath();
00225     return path;
00226 }
00227 
00228 void AppletHandle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00229 {
00230     Q_UNUSED(option);
00231     Q_UNUSED(widget);
00232 
00233     //kDebug() << m_opacity << m_anim << FadeOut;
00234     if (qFuzzyCompare(m_opacity + 1.0, 1.0)) {
00235         if (m_anim == FadeOut) {
00236             //kDebug() << "WOOOOOOOOO";
00237             QTimer::singleShot(0, this, SLOT(emitDisappear()));
00238         }
00239         return;
00240     }
00241 
00242     qreal translation;
00243 
00244     if (m_buttonsOnRight) {
00245         //kDebug() << "translating by" << m_opacity
00246         //         << (-(1 - m_opacity) * m_rect.width()) << m_rect.width();
00247         translation = -(1 - m_opacity) * m_rect.width();
00248     } else {
00249         translation = (1 - m_opacity) * m_rect.width();
00250     }
00251 
00252     painter->translate(translation, 0);
00253 
00254     painter->setPen(Qt::NoPen);
00255     painter->setRenderHints(QPainter::Antialiasing);
00256 
00257     int iconMargin = m_iconSize / 2;
00258 
00259     const QSize pixmapSize(int(m_decorationRect.width()),
00260                            int(m_decorationRect.height()) + m_iconSize * 4 + 1);
00261     const QSize iconSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
00262 
00263     //regenerate our buffer?
00264     if (m_animId > 0 || !m_backgroundBuffer || m_backgroundBuffer->size() != pixmapSize) {
00265         QColor transparencyColor = Qt::black;
00266         transparencyColor.setAlphaF(qMin(m_opacity, qreal(0.99)));
00267 
00268         QLinearGradient g(QPoint(0, 0), QPoint(m_decorationRect.width(), 0));
00269         //fading out panel
00270         if (m_rect.height() > qreal(minimumHeight()) * 1.25) {
00271             if (m_buttonsOnRight) {
00272                 qreal opaquePoint =
00273                     (m_background->marginSize(LeftMargin) - translation) / m_decorationRect.width();
00274                 //kDebug() << "opaquePoint" << opaquePoint
00275                 //         << m_background->marginSize(LeftMargin) << m_decorationRect.width();
00276                 g.setColorAt(0.0, Qt::transparent);
00277                 g.setColorAt(qMax(0.0, opaquePoint - 0.05), Qt::transparent);
00278                 g.setColorAt(opaquePoint, transparencyColor);
00279                 g.setColorAt(1.0, transparencyColor);
00280             } else {
00281                 qreal opaquePoint =
00282                     1 - ((m_background->marginSize(RightMargin) + translation) / m_decorationRect.width());
00283                 g.setColorAt(1.0, Qt::transparent);
00284                 g.setColorAt(opaquePoint, Qt::transparent);
00285                 g.setColorAt(qMax(0.0, opaquePoint - 0.05), transparencyColor);
00286                 g.setColorAt(0.0, transparencyColor);
00287             }
00288         //complete panel
00289         } else {
00290             g.setColorAt(0.0, transparencyColor);
00291         }
00292 
00293         m_background->resizeFrame(m_decorationRect.size());
00294 
00295         if (!m_backgroundBuffer || m_backgroundBuffer->size() != pixmapSize) {
00296             delete m_backgroundBuffer;
00297             m_backgroundBuffer = new QPixmap(pixmapSize);
00298         }
00299         m_backgroundBuffer->fill(Qt::transparent);
00300         QPainter buffPainter(m_backgroundBuffer);
00301 
00302         m_background->paintFrame(&buffPainter);
00303 
00304         //+1 because otherwise due to rounding errors when rotated could appear one pixel
00305         //of the icon at the border of the applet
00306         //QRectF iconRect(QPointF(pixmapSize.width() - m_iconSize + 1, m_iconSize), iconSize);
00307         QRectF iconRect(QPointF(0, m_decorationRect.height() + 1), iconSize);
00308         if (m_buttonsOnRight) {
00309             iconRect.moveLeft(
00310                 pixmapSize.width() - m_iconSize - m_background->marginSize(LeftMargin));
00311             m_configureIcons->paint(&buffPainter, iconRect, "size-diagonal-tr2bl");
00312         } else {
00313             iconRect.moveLeft(m_background->marginSize(RightMargin));
00314             m_configureIcons->paint(&buffPainter, iconRect, "size-diagonal-tl2br");
00315         }
00316 
00317         iconRect.translate(0, m_iconSize);
00318         m_configureIcons->paint(&buffPainter, iconRect, "rotate");
00319 
00320         if (m_applet && m_applet->hasConfigurationInterface()) {
00321             iconRect.translate(0, m_iconSize);
00322             m_configureIcons->paint(&buffPainter, iconRect, "configure");
00323         }
00324         iconRect.translate(0, m_iconSize);
00325         m_configureIcons->paint(&buffPainter, iconRect, "close");
00326 
00327         buffPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00328         //blend the background
00329         buffPainter.fillRect(m_backgroundBuffer->rect(), g);
00330         //blend the icons
00331         //buffPainter.fillRect(QRect(QPoint((int)m_decorationRect.width(), 0), QSize(m_iconSize + 1,
00332         //                                  (int)m_decorationRect.height())), transparencyColor);
00333     }
00334 
00335     painter->drawPixmap(m_decorationRect.toRect(), *m_backgroundBuffer,
00336                         QRect(QPoint(0, 0), m_decorationRect.size().toSize()));
00337 
00338     //XXX this code is duplicated in the next function
00339     QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_MARGIN, iconMargin);
00340     QPointF step = QPointF(0, m_iconSize + iconMargin);
00341     QPointF separator = step + QPointF(0, iconMargin);
00342     //end duplicate code
00343 
00344     QPointF shiftC;
00345     QPointF shiftD;
00346     QPointF shiftR;
00347     QPointF shiftM;
00348 
00349     switch(m_pressedButton)
00350     {
00351     case ConfigureButton:
00352         shiftC = QPointF(2, 2);
00353         break;
00354     case RemoveButton:
00355         shiftD = QPointF(2, 2);
00356         break;
00357     case RotateButton:
00358         shiftR = QPointF(2, 2);
00359         break;
00360     case ResizeButton:
00361         shiftM = QPointF(2, 2);
00362         break;
00363     default:
00364         break;
00365     }
00366 
00367     QRectF sourceIconRect(QPointF(0, m_decorationRect.height() + 1), iconSize);
00368     if (m_buttonsOnRight) {
00369         sourceIconRect.moveLeft(
00370             pixmapSize.width() - m_iconSize - m_background->marginSize(LeftMargin));
00371     } else {
00372         sourceIconRect.moveLeft(m_background->marginSize(RightMargin));
00373     }
00374 
00375     if (m_applet && m_applet->aspectRatioMode() != FixedSize) {
00376         //resize
00377         painter->drawPixmap(
00378             QRectF(basePoint + shiftM, iconSize), *m_backgroundBuffer, sourceIconRect);
00379         basePoint += step;
00380     }
00381 
00382     //rotate
00383     sourceIconRect.translate(0, m_iconSize);
00384     painter->drawPixmap(QRectF(basePoint + shiftR, iconSize), *m_backgroundBuffer, sourceIconRect);
00385 
00386     if (m_applet && m_applet->hasConfigurationInterface()) {
00387         basePoint += step;
00388         sourceIconRect.translate(0, m_iconSize);
00389         painter->drawPixmap(
00390             QRectF(basePoint + shiftC, iconSize), *m_backgroundBuffer, sourceIconRect);
00391     }
00392 
00393     //close
00394     basePoint = m_rect.bottomLeft() + QPointF(HANDLE_MARGIN, 0) - step;
00395     sourceIconRect.translate(0, m_iconSize);
00396     painter->drawPixmap(QRectF(basePoint + shiftD, iconSize), *m_backgroundBuffer, sourceIconRect);
00397 }
00398 
00399 void AppletHandle::emitDisappear()
00400 {
00401     emit disappearDone(this);
00402 }
00403 
00404 AppletHandle::ButtonType AppletHandle::mapToButton(const QPointF &point) const
00405 {
00406     int iconMargin = m_iconSize / 2;
00407     //XXX this code is duplicated in the prev. function
00408     QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_MARGIN, iconMargin);
00409     QPointF step = QPointF(0, m_iconSize + iconMargin);
00410     QPointF separator = step + QPointF(0, iconMargin);
00411    //end duplicate code
00412 
00413     QRectF activeArea = QRectF(basePoint, QSizeF(m_iconSize, m_iconSize));
00414 
00415     if (m_applet && m_applet->aspectRatioMode() != FixedSize) {
00416         if (activeArea.contains(point)) {
00417             return ResizeButton;
00418         }
00419         activeArea.translate(step);
00420     }
00421 
00422     if (activeArea.contains(point)) {
00423         return RotateButton;
00424     }
00425 
00426     if (m_applet && m_applet->hasConfigurationInterface()) {
00427         activeArea.translate(step);
00428         if (activeArea.contains(point)) {
00429             return ConfigureButton;
00430         }
00431     }
00432 
00433     activeArea.moveTop(m_rect.bottom() - activeArea.height() - iconMargin);
00434     if (activeArea.contains(point)) {
00435         return RemoveButton;
00436     }
00437 
00438     return MoveButton;
00439     //return m_applet->mapToParent(m_applet->shape()).contains(point) ? NoButton : MoveButton;
00440 }
00441 
00442 void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event)
00443 {
00444     //containment recently switched?
00445     if (!m_applet) {
00446         QGraphicsItem::mousePressEvent(event);
00447         return;
00448     }
00449 
00450     if (m_pendingFade) {
00451         //m_pendingFade = false;
00452         return;
00453     }
00454 
00455     if (event->button() == Qt::LeftButton) {
00456         m_pressedButton = mapToButton(event->pos());
00457         //kDebug() << "button pressed:" << m_pressedButton;
00458         if (m_pressedButton != NoButton) {
00459             m_applet->raise();
00460             m_zValue = m_applet->zValue();
00461             setZValue(m_zValue);
00462         }
00463 
00464         if (m_pressedButton == MoveButton) {
00465             m_pos = pos();
00466         }
00467 
00468         if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
00469             m_origAppletCenter = mapToScene(m_applet->geometry().center());
00470             m_origAppletSize = QPointF(m_applet->size().width(), m_applet->size().height());
00471 
00472             // resize
00473             if (m_buttonsOnRight) {
00474                 m_resizeStaticPoint = mapToScene(m_applet->geometry().bottomLeft());
00475             } else {
00476                 m_resizeStaticPoint = mapToScene(m_applet->geometry().bottomRight());
00477             }
00478             m_resizeGrabPoint = mapToScene(event->pos());
00479             QPointF cursorRelativeToStatic = m_resizeGrabPoint - m_resizeStaticPoint;
00480 
00481             // rotate
00482             m_rotateAngleOffset = m_angle - _k_pointAngle(mapToScene(event->pos()) - m_origAppletCenter);
00483         }
00484 
00485         event->accept();
00486 
00487         update();
00488 
00489         //set mousePos to the position in the applet, in screencoords, so it becomes easy
00490         //to reposition the toplevel view to the correct position.
00491         if (m_currentView && m_applet) {
00492             QPoint localpos = m_currentView->mapFromScene(m_applet->scenePos());
00493             m_mousePos = event->screenPos() - m_currentView->mapToGlobal(localpos);
00494         }
00495         return;
00496     }
00497 
00498     QGraphicsItem::mousePressEvent(event);
00499 }
00500 
00501 bool AppletHandle::leaveCurrentView(const QPoint &pos) const
00502 {
00503     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
00504         if (widget->geometry().contains(pos)) {
00505             //is this widget a plasma view, a different view then our current one,
00506             //AND not a dashboardview?
00507             Plasma::View *v = qobject_cast<Plasma::View *>(widget);
00508             if (v && v != m_currentView && v->containment() != m_containment) {
00509                 return true;
00510             }
00511         }
00512     }
00513     return false;
00514 }
00515 
00516 void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00517 {
00518     //kDebug() << "button pressed:" << m_pressedButton << ", fade pending?" << m_pendingFade;
00519 
00520     if (m_pendingFade) {
00521         startFading(FadeOut, m_entryPos);
00522         m_pendingFade = false;
00523     }
00524 
00525     ButtonType releasedAtButton = mapToButton(event->pos());
00526 
00527     if (m_applet && event->button() == Qt::LeftButton) {
00528         switch (m_pressedButton) {
00529         case ConfigureButton:
00530             //FIXME: Remove this call once the configuration management change was done
00531             if (m_pressedButton == releasedAtButton) {
00532                 m_applet->showConfigurationInterface();
00533             }
00534             break;
00535         case RemoveButton:
00536             if (m_pressedButton == releasedAtButton) {
00537                 forceDisappear();
00538                 m_applet->destroy();
00539             }
00540             break;
00541         case MoveButton:
00542         {
00543                 // test for containment change
00544                 //kDebug() << "testing for containment change, sceneBoundingRect = "
00545                 //         << m_containment->sceneBoundingRect();
00546                 if (!m_containment->sceneBoundingRect().contains(m_applet->scenePos())) {
00547                     // see which containment it belongs to
00548                     Corona * corona = qobject_cast<Corona*>(scene());
00549                     if (corona) {
00550                         QList<Containment*> containments = corona->containments();
00551                         for (int i = 0; i < containments.size(); ++i) {
00552                             QPointF pos;
00553                             QGraphicsView *v;
00554                             v = containments[i]->view();
00555                             if (v) {
00556                                 pos = v->mapToScene(
00557                                     v->mapFromGlobal(event->screenPos() - m_mousePos));
00558 
00559                                 if (containments[i]->sceneBoundingRect().contains(pos)) {
00560                                     //kDebug() << "new containment = " << containments[i];
00561                                     //kDebug() << "rect = " << containments[i]->sceneBoundingRect();
00562                                     // add the applet to the new containment and take it from the old one
00563                                     //kDebug() << "moving to other containment with position" << pos;;
00564                                     switchContainment(containments[i], pos);
00565                                     break;
00566                                 }
00567                             }
00568                         }
00569                     }
00570                 }
00571             break;
00572         }
00573         default:
00574             break;
00575         }
00576     }
00577 
00578     m_pressedButton = NoButton;
00579     update();
00580 }
00581 
00582 qreal _k_distanceForPoint(QPointF point)
00583 {
00584     return std::sqrt(point.x() * point.x() + point.y() * point.y());
00585 }
00586 
00587 qreal _k_pointAngle(QPointF in)
00588 {
00589     qreal r = sqrt(in.x()*in.x() + in.y()*in.y());
00590     qreal cosine = in.x()/r;
00591     qreal sine = in.y()/r;
00592 
00593     if (sine>=0) {
00594         return acos(cosine);
00595     } else {
00596         return -acos(cosine);
00597     }
00598 }
00599 
00600 QPointF _k_rotatePoint(QPointF in, qreal rotateAngle)
00601 {
00602     qreal r = sqrt(in.x()*in.x() + in.y()*in.y());
00603     qreal cosine = in.x()/r;
00604     qreal sine = in.y()/r;
00605 
00606     qreal angle;
00607     if (sine>=0) {
00608         angle = acos(cosine);
00609     } else {
00610         angle = -acos(cosine);
00611     }
00612 
00613     qreal newAngle = angle + rotateAngle;
00614     return QPointF(r*cos(newAngle), r*sin(newAngle));
00615 }
00616 
00617 void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00618 {
00619     static const qreal snapAngle = M_PI_2 /* $i 3.14159 / 2.0 */;
00620 
00621     if (!m_applet) {
00622         QGraphicsItem::mouseMoveEvent(event);
00623         return;
00624     }
00625 
00626     //Track how much the mouse has moved.
00627     QPointF deltaScene  = event->scenePos() - event->lastScenePos();
00628 
00629     if (m_pressedButton == MoveButton) {
00630         m_pos += deltaScene;
00631         if (leaveCurrentView(event->screenPos())) {
00632             Plasma::View *v = Plasma::View::topLevelViewAt(event->screenPos());
00633             if (v && v != m_currentView) {
00634                 Containment *c = v->containment();
00635                 QPoint pos = v->mapFromGlobal(event->screenPos());
00636                 //we actually have been dropped on another containment, so
00637                 //move there: we have a screenpos, we need a scenepos
00638                 //FIXME how reliable is this transform?
00639                 switchContainment(c, v->mapToScene(pos));
00640             } else {
00641                 setPos(m_pos);
00642             }
00643         } else {
00644             setPos(m_pos);
00645         }
00646     } else if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
00647         QPointF cursorPoint = mapToScene(event->pos());
00648 
00649         // the code below will adjust these based on the type of operation
00650         QPointF newSize;
00651         QPointF newCenter;
00652         qreal newAngle;
00653 
00654         // get size limits
00655         QSizeF min = m_applet->minimumSize();
00656         QSizeF max = m_applet->maximumSize();
00657 
00658         if (min.isEmpty()) {
00659             min = m_applet->effectiveSizeHint(Qt::MinimumSize);
00660         }
00661 
00662         if (max.isEmpty()) {
00663             max = m_applet->effectiveSizeHint(Qt::MaximumSize);
00664         }
00665 
00666         // If the applet doesn't have a minimum size, calculate based on a
00667         // minimum content area size of 16x16 (KIconLoader::SizeSmall)
00668         if (min.isEmpty()) {
00669             min = m_applet->boundingRect().size() - m_applet->contentsRect().size();
00670             min = QSizeF(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
00671         }
00672 
00673         if (m_pressedButton == RotateButton) {
00674             newSize = m_origAppletSize;
00675             newCenter = m_origAppletCenter;
00676 
00677             QPointF centerRelativePoint = cursorPoint - m_origAppletCenter;
00678             if (_k_distanceForPoint(centerRelativePoint) < 10) {
00679                 newAngle = m_angle;
00680             } else {
00681                 qreal cursorAngle = _k_pointAngle(centerRelativePoint);
00682                 newAngle = m_rotateAngleOffset + cursorAngle;
00683                 if (fabs(remainder(newAngle, snapAngle)) < 0.15) {
00684                     newAngle = newAngle - remainder(newAngle, snapAngle);
00685                 }
00686             }
00687         } else {
00688             // un-rotate screen points so we can read differences of coordinates
00689             QPointF rStaticPoint = _k_rotatePoint(m_resizeStaticPoint, -m_angle);
00690             QPointF rCursorPoint = _k_rotatePoint(cursorPoint, -m_angle);
00691             QPointF rGrabPoint = _k_rotatePoint(m_resizeGrabPoint, -m_angle);
00692 
00693             if (m_buttonsOnRight) {
00694                 newSize = m_origAppletSize + QPointF(rCursorPoint.x() - rGrabPoint.x(), rGrabPoint.y() - rCursorPoint.y());
00695             } else {
00696                 newSize = m_origAppletSize + QPointF(rGrabPoint.x() - rCursorPoint.x(), rGrabPoint.y() - rCursorPoint.y());
00697             }
00698 
00699             // if preserving aspect ratio, project the calculated size point to the line
00700             // theough the origin and the original size point
00701             if ((m_applet->aspectRatioMode() != Plasma::IgnoreAspectRatio &&
00702                  !(event->modifiers() & Qt::ControlModifier)) ||
00703                  (m_applet->aspectRatioMode() == Plasma::IgnoreAspectRatio &&
00704                   (event->modifiers() & Qt::ControlModifier))) {
00705                 qreal ox = m_origAppletSize.x();
00706                 qreal oy = m_origAppletSize.y();
00707                 qreal sx = newSize.x();
00708                 qreal sy = newSize.y();
00709 
00710                 qreal x = ox*(sx*ox+sy*oy)/(ox*ox+oy*oy);
00711                 qreal y = (oy/ox)*x;
00712                 newSize = QPointF(x, y);
00713 
00714                 // limit size, preserve ratio
00715                 newSize.rx() = qMin(max.width(), qMax(min.width(), newSize.x()));
00716                 newSize.ry() = newSize.x()*(oy/ox);
00717                 newSize.ry() = qMin(max.height(), qMax(min.height(), newSize.y()));
00718                 newSize.rx() = newSize.y()/(oy/ox);
00719             } else {
00720                 // limit size
00721                 newSize.rx() = qMin(max.width(), qMax(min.width(), newSize.x()));
00722                 newSize.ry() = qMin(max.height(), qMax(min.height(), newSize.y()));
00723             }
00724 
00725             // move center such that the static corner remains in the same place
00726             if (m_buttonsOnRight) {
00727                 newCenter =  _k_rotatePoint(QPointF(rStaticPoint.x() + newSize.x()/2, rStaticPoint.y() - newSize.y()/2), m_angle);
00728             } else {
00729                 newCenter =  _k_rotatePoint(QPointF(rStaticPoint.x() - newSize.x()/2, rStaticPoint.y() - newSize.y()/2), m_angle);
00730             }
00731 
00732             newAngle = m_angle;
00733         }
00734 
00735         // set position of applet handle
00736         QPointF newHandlePosInScene = newCenter - (m_applet->pos() + newSize/2);
00737         QPointF newHandlePos = parentItem()->mapFromScene(newHandlePosInScene);
00738         setPos(newHandlePos);
00739 
00740         // set applet size
00741         m_applet->resize(newSize.x(), newSize.y());
00742 
00743         // set applet handle rotation - rotate around center of applet
00744         QTransform t;
00745         QPointF appletCenter = m_applet->geometry().center();
00746         t.translate(appletCenter.x(), appletCenter.y());
00747         t.rotateRadians(newAngle);
00748         t.translate(-appletCenter.x(), -appletCenter.y());
00749         setTransform(t);
00750         m_angle = newAngle;
00751 
00752         m_applet->update();
00753     } else {
00754         QGraphicsItem::mouseMoveEvent(event);
00755     }
00756 }
00757 
00758 //pos relative to scene
00759 void AppletHandle::switchContainment(Containment *containment, const QPointF &pos)
00760 {
00761     m_containment = containment;
00762     Applet *applet = m_applet;
00763     m_applet = 0; //make sure we don't try to act on the applet again
00764     applet->removeSceneEventFilter(this);
00765     forceDisappear(); //takes care of event filter and killing handle
00766     applet->disconnect(this); //make sure the applet doesn't tell us to do anything
00767     applet->setZValue(m_zValue);
00768     containment->addApplet(applet, containment->mapFromScene(pos), false);
00769     update();
00770 }
00771 
00772 QVariant AppletHandle::itemChange(GraphicsItemChange change, const QVariant &value)
00773 {
00774     if (change == ItemPositionHasChanged && m_applet) {
00775         m_applet->updateConstraints(Plasma::LocationConstraint);
00776     }
00777     return QGraphicsItem::itemChange(change, value);
00778 }
00779 
00780 void AppletHandle::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
00781 {
00782     Q_UNUSED(event);
00783     //kDebug() << "hover enter";
00784 
00785     //if a disappear was scheduled stop the timer
00786     if (m_leaveTimer->isActive()) {
00787         m_leaveTimer->stop();
00788     }
00789     // if we're already fading out, fade back in
00790     else if (m_animId != 0 && m_anim == FadeOut) {
00791         startFading(FadeIn, m_entryPos, true);
00792     }
00793 }
00794 
00795 void AppletHandle::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
00796 {
00797     hoverEnterEvent(event);
00798 }
00799 
00800 void AppletHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
00801 {
00802     Q_UNUSED(event);
00803 
00804     foreach (QWidget *widget, QApplication::topLevelWidgets()) {
00805       QMenu *menu = qobject_cast<QMenu*>(widget);
00806       if (menu && menu->isVisible()) {
00807           connect(menu, SIGNAL(aboutToHide()), this, SLOT(leaveTimeout()));
00808           return;
00809       }
00810     }
00811 
00812 
00813     // if we haven't even showed up yet, remove the handle
00814     if (m_hoverTimer->isActive()) {
00815         m_hoverTimer->stop();
00816         QTimer::singleShot(0, this, SLOT(emitDisappear()));
00817     } else if (m_pressedButton != NoButton) {
00818         m_pendingFade = true;
00819     } else {
00820         //wait a moment to hide the handle in order to recheck the mouse position
00821         m_leaveTimer->start();
00822     }
00823 }
00824 
00825 bool AppletHandle::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
00826 {
00827     if (watched == m_applet && event->type() == QEvent::GraphicsSceneHoverLeave) {
00828         hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event));
00829     }
00830 
00831     return false;
00832 }
00833 
00834 void AppletHandle::fadeAnimation(qreal progress)
00835 {
00836     //qreal endOpacity = (m_anim == FadeIn) ? 1.0 : -1.0;
00837     if (m_anim == FadeIn) {
00838         m_opacity = progress;
00839     } else {
00840         m_opacity = 1 - progress;
00841     }
00842 
00843     //kDebug() << "progress" << progress << "m_opacity" << m_opacity << m_anim << "(" << FadeIn << ")";
00844     if (qFuzzyCompare(progress, qreal(1.0))) {
00845         m_animId = 0;
00846         delete m_backgroundBuffer;
00847         m_backgroundBuffer = 0;
00848     }
00849 
00850     update();
00851 }
00852 
00853 void AppletHandle::hoverTimeout()
00854 {
00855     startFading(FadeIn, m_entryPos);
00856 }
00857 
00858 void AppletHandle::leaveTimeout()
00859 {
00860     if (!isUnderMouse()) {
00861         startFading(FadeOut, m_entryPos);
00862     }
00863 }
00864 
00865 void AppletHandle::appletDestroyed()
00866 {
00867     m_applet = 0;
00868 }
00869 
00870 void AppletHandle::appletResized()
00871 {
00872     prepareGeometryChange();
00873     calculateSize();
00874     update();
00875 }
00876 
00877 void AppletHandle::setHoverPos(const QPointF &hoverPos)
00878 {
00879     m_entryPos = hoverPos;
00880 }
00881 
00882 void AppletHandle::startFading(FadeType anim, const QPointF &hoverPos, bool preserveSide)
00883 {
00884     if (m_animId != 0) {
00885         Animator::self()->stopCustomAnimation(m_animId);
00886     }
00887 
00888     m_entryPos = hoverPos;
00889     qreal time = 100;
00890 
00891     if (!m_applet) {
00892         m_anim = FadeOut;
00893         fadeAnimation(1.0);
00894         return;
00895     }
00896 
00897     if (anim == FadeIn) {
00898         //kDebug() << m_entryPos.x() << m_applet->pos().x();
00899         prepareGeometryChange();
00900         bool wasOnRight = m_buttonsOnRight;
00901         if (!preserveSide) {
00902             m_buttonsOnRight = m_entryPos.x() > (m_applet->size().width() / 2);
00903         }
00904         calculateSize();
00905         QPolygonF region = mapToParent(m_rect).intersected(parentWidget()->boundingRect());
00906         //kDebug() << region << m_rect << mapToParent(m_rect) << parentWidget()->boundingRect();
00907         if (region != mapToParent(m_rect)) {
00908             // switch sides
00909             //kDebug() << "switch sides";
00910             m_buttonsOnRight = !m_buttonsOnRight;
00911             calculateSize();
00912             QPolygonF region2 = mapToParent(m_rect).intersected(parentWidget()->boundingRect());
00913             if (region2 != mapToParent(m_rect)) {
00914                 // ok, both sides failed to be perfect... which one is more perfect?
00915                 QRectF f1 = region.boundingRect();
00916                 QRectF f2 = region2.boundingRect();
00917                 //kDebug() << "still not a perfect world"
00918                 //         << f2.width() << f2.height() << f1.width() << f1.height();
00919                 if ((f2.width() * f2.height()) < (f1.width() * f1.height())) {
00920                     //kDebug() << "we did better the first time";
00921                     m_buttonsOnRight = !m_buttonsOnRight;
00922                     calculateSize();
00923                 }
00924             }
00925         }
00926 
00927         if (wasOnRight != m_buttonsOnRight &&
00928             m_anim == FadeIn &&
00929             anim == FadeIn &&
00930             m_opacity <= 1) {
00931             m_opacity = 0.0;
00932         }
00933 
00934         time *= 1.0 - m_opacity;
00935     } else {
00936         time *= m_opacity;
00937     }
00938 
00939     m_anim = anim;
00940     //kDebug() << "animating for " << time << "ms";
00941     m_animId = Animator::self()->customAnimation(80 * (time / 1000.0), (int)time,
00942                                                  Animator::EaseInCurve, this, "fadeAnimation");
00943 }
00944 
00945 void AppletHandle::forceDisappear()
00946 {
00947     setAcceptsHoverEvents(false);
00948     startFading(FadeOut, m_entryPos);
00949 }
00950 
00951 int AppletHandle::minimumHeight()
00952 {
00953     int iconMargin = m_iconSize / 2;
00954     int requiredHeight =  iconMargin  + //first margin
00955                           (m_iconSize + iconMargin) * 4 + //XXX remember to update this if the number of buttons changes
00956                           iconMargin ;  //blank space before the close button
00957 
00958     if (m_applet && m_applet->hasConfigurationInterface()) {
00959         requiredHeight += (m_iconSize + iconMargin);
00960     }
00961 
00962     return requiredHeight;
00963 }
00964 
00965 void AppletHandle::calculateSize()
00966 {
00967     KIconLoader *iconLoader = KIconLoader::global();
00968     //m_iconSize = iconLoader->currentSize(KIconLoader::Small); //does not work with double sized icon
00969     m_iconSize = iconLoader->loadIcon("transform-scale", KIconLoader::Small).width(); //workaround
00970 
00971     int handleHeight = qMax(minimumHeight(), int(m_applet->contentsRect().height() * 0.8));
00972     int handleWidth = m_iconSize + 2 * HANDLE_MARGIN;
00973     int top =
00974         m_applet->contentsRect().top() + (m_applet->contentsRect().height() - handleHeight) / 2.0;
00975 
00976     qreal marginLeft, marginTop, marginRight, marginBottom;
00977     m_background->getMargins(marginLeft, marginTop, marginRight, marginBottom);
00978 
00979     if (m_buttonsOnRight) {
00980         //put the rect on the right of the applet
00981         m_rect = QRectF(m_applet->size().width(), top, handleWidth, handleHeight);
00982     } else {
00983         //put the rect on the left of the applet
00984         m_rect = QRectF(-handleWidth, top, handleWidth, handleHeight);
00985     }
00986 
00987     if (m_applet->contentsRect().height() > qreal(minimumHeight()) * 1.25) {
00988         int addedMargin = marginLeft / 2;
00989 
00990         // now we check to see if the shape is smaller than the contents,
00991         // and that the shape is not just the bounding rect; in those cases
00992         // we have a shaped guy and we draw a full panel;
00993         // TODO: allow applets to mark when they have translucent areas and
00994         //       should therefore skip this test?
00995         if (!m_applet->shape().contains(m_applet->contentsRect())) {
00996             QPainterPath p;
00997             p.addRect(m_applet->boundingRect());
00998             if (m_applet->shape() != p) {
00999                 addedMargin = m_applet->contentsRect().width() / 2;
01000             }
01001         }
01002 
01003         if (m_buttonsOnRight) {
01004             marginLeft += addedMargin;
01005         } else {
01006             marginRight += addedMargin;
01007         }
01008     }
01009 
01010     m_rect = m_applet->mapToParent(m_rect).boundingRect();
01011     m_decorationRect = m_rect.adjusted(-marginLeft, -marginTop, marginRight, marginBottom);
01012     m_totalRect = m_decorationRect.united(m_applet->geometry());
01013 }
01014 
01015 } // Plasma Namespace
01016 
01017 #include "applethandle_p.moc"
01018 

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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