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

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
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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