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

Applets

clock.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright 2007 by Aaron Seigo <aseigo@kde.org>                        *
00003  *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>               *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
00019  ***************************************************************************/
00020 
00021 #include "clock.h"
00022 
00023 #include <math.h>
00024 
00025 #include <QApplication>
00026 #include <QBitmap>
00027 #include <QGraphicsScene>
00028 #include <QMatrix>
00029 #include <QPaintEvent>
00030 #include <QPainter>
00031 #include <QPainterPath>
00032 #include <QPixmap>
00033 #include <QStyleOptionGraphicsItem>
00034 #include <QVBoxLayout>
00035 #include <QHBoxLayout>
00036 #include <QCheckBox>
00037 #include <QPushButton>
00038 #include <QSpinBox>
00039 
00040 #include <KConfigDialog>
00041 #include <KDebug>
00042 #include <KLocale>
00043 #include <KIcon>
00044 #include <KSharedConfig>
00045 #include <KTimeZoneWidget>
00046 #include <KDialog>
00047 
00048 #include <Plasma/Dialog>
00049 #include <Plasma/PaintUtils>
00050 #include <Plasma/Svg>
00051 #include <Plasma/Theme>
00052 
00053 Clock::Clock(QObject *parent, const QVariantList &args)
00054     : ClockApplet(parent, args),
00055       m_showSecondHand(false),
00056       m_showTimezoneString(false),
00057       m_repaintCache(RepaintAll),
00058       m_secondHandUpdateTimer(0)
00059 {
00060     KGlobal::locale()->insertCatalog("libplasmaclock");
00061 
00062     setHasConfigurationInterface(true);
00063     resize(125, 125);
00064     setAspectRatioMode(Plasma::Square);
00065 
00066     m_theme = new Plasma::Svg(this);
00067     m_theme->setImagePath("widgets/clock");
00068     m_theme->setContainsMultipleImages(true);
00069     m_theme->resize(size());
00070 
00071     connect(m_theme, SIGNAL(repaintNeeded()), this, SLOT(repaintNeeded()));
00072 }
00073 
00074 Clock::~Clock()
00075 {
00076 }
00077 
00078 void Clock::init()
00079 {
00080     ClockApplet::init();
00081 
00082     KConfigGroup cg = config();
00083     m_showSecondHand = cg.readEntry("showSecondHand", false);
00084     m_showTimezoneString = cg.readEntry("showTimezoneString", false);
00085     m_fancyHands = cg.readEntry("fancyHands", false);
00086     setCurrentTimezone(cg.readEntry("timezone", localTimezone()));
00087 
00088     connectToEngine();
00089 }
00090 
00091 void Clock::connectToEngine()
00092 {
00093     Plasma::DataEngine* timeEngine = dataEngine("time");
00094     if (m_showSecondHand) {
00095         timeEngine->connectSource(currentTimezone(), this, 500);
00096     } else {
00097         timeEngine->connectSource(currentTimezone(), this, 6000, Plasma::AlignToMinute);
00098     }
00099 }
00100 
00101 void Clock::constraintsEvent(Plasma::Constraints constraints)
00102 {
00103     ClockApplet::constraintsEvent(constraints);
00104 
00105     if (constraints & Plasma::FormFactorConstraint) {
00106         setBackgroundHints(NoBackground);
00107     }
00108 
00109     if (constraints & Plasma::SizeConstraint) {
00110         m_theme->resize(size());
00111     }
00112 
00113     m_repaintCache = RepaintAll;
00114 }
00115 
00116 QPainterPath Clock::shape() const
00117 {
00118     if (m_theme->hasElement("hint-square-clock")) {
00119         return Applet::shape();
00120     }
00121 
00122     QPainterPath path;
00123     // we adjust by 2px all around to allow for smoothing the jaggies
00124     // if the ellipse is too small, we'll get a nastily jagged edge around the clock
00125     path.addEllipse(boundingRect().adjusted(-2, -2, 2, 2));
00126     return path;
00127 }
00128 
00129 void Clock::dataUpdated(const QString& source, const Plasma::DataEngine::Data &data)
00130 {
00131     Q_UNUSED(source);
00132     m_time = data["Time"].toTime();
00133 
00134     if (m_time.minute() == m_lastTimeSeen.minute() &&
00135         m_time.second() == m_lastTimeSeen.second()) {
00136         // avoid unnecessary repaints
00137         return;
00138     }
00139 
00140     if (m_time.minute() != m_lastTimeSeen.minute()) {
00141         m_repaintCache = RepaintHands;
00142     }
00143 
00144     if (Plasma::ToolTipManager::self()->isVisible(this)) {
00145         updateTipContent();
00146     }
00147 
00148     if (m_secondHandUpdateTimer) {
00149         m_secondHandUpdateTimer->stop();
00150     }
00151 
00152     m_lastTimeSeen = m_time;
00153     update();
00154 }
00155 
00156 void Clock::createClockConfigurationInterface(KConfigDialog *parent)
00157 {
00158     //TODO: Make the size settable
00159     QWidget *widget = new QWidget();
00160     ui.setupUi(widget);
00161     parent->addPage(widget, i18n("General"), icon());
00162 
00163     ui.showSecondHandCheckBox->setChecked(m_showSecondHand);
00164     ui.showTimezoneStringCheckBox->setChecked(m_showTimezoneString);
00165 }
00166 
00167 void Clock::clockConfigAccepted()
00168 {
00169     KConfigGroup cg = config();
00170     m_showSecondHand = ui.showSecondHandCheckBox->isChecked();
00171     m_showTimezoneString = ui.showTimezoneStringCheckBox->isChecked();
00172 
00173     cg.writeEntry("showSecondHand", m_showSecondHand);
00174     cg.writeEntry("showTimezoneString", m_showTimezoneString);
00175     update();
00176 
00177     dataEngine("time")->disconnectSource(currentTimezone(), this);
00178     connectToEngine();
00179 
00180     //TODO: why we don't call updateConstraints()?
00181     constraintsEvent(Plasma::AllConstraints);
00182     emit configNeedsSaving();
00183 }
00184 
00185 void Clock::changeEngineTimezone(const QString &oldTimezone, const QString &newTimezone)
00186 {
00187     dataEngine("time")->disconnectSource(oldTimezone, this);
00188     Plasma::DataEngine* timeEngine = dataEngine("time");
00189 
00190     if (m_showSecondHand) {
00191         timeEngine->connectSource(newTimezone, this, 500);
00192     } else {
00193         timeEngine->connectSource(newTimezone, this, 6000, Plasma::AlignToMinute);
00194     }
00195 
00196     m_repaintCache = RepaintAll;
00197 }
00198 
00199 void Clock::repaintNeeded()
00200 {
00201     m_repaintCache = RepaintAll;
00202     update();
00203 }
00204 
00205 void Clock::moveSecondHand()
00206 {
00207     //kDebug() << "moving second hand";
00208     update();
00209 }
00210 
00211 void Clock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName)
00212 {
00213     // this code assumes the following conventions in the svg file:
00214     // - the _vertical_ position of the hands should be set with respect to the center of the face
00215     // - the _horizontal_ position of the hands does not matter
00216     // - the _shadow_ elements should have the same vertical position as their _hand_ element counterpart
00217 
00218     QRectF elementRect;
00219     QString name = handName + "HandShadow";
00220     if (m_theme->hasElement(name)) {
00221         p->save();
00222 
00223         elementRect = m_theme->elementRect(name);
00224         static const QPoint offset = QPoint(2, 3);
00225 
00226         p->translate(rect.width()/2+offset.x(), rect.height()/2+offset.y());
00227         p->rotate(rotation);
00228         p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation);
00229         m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name);
00230 
00231         p->restore();
00232     }
00233 
00234     p->save();
00235 
00236     name = handName + "Hand";
00237     elementRect = m_theme->elementRect(name);
00238 
00239     p->translate(rect.width()/2, rect.height()/2);
00240     p->rotate(rotation);
00241     p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation);
00242     m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name);
00243 
00244     p->restore();
00245 }
00246 
00247 void Clock::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &rect)
00248 {
00249     Q_UNUSED(option)
00250 
00251     // compute hand angles
00252     const qreal minutes = 6.0 * m_time.minute() - 180;
00253     const qreal hours = 30.0 * m_time.hour() - 180 +
00254                         ((m_time.minute() / 59.0) * 30.0);
00255     qreal seconds = 0;
00256     if (m_showSecondHand) {
00257         static const double anglePerSec = 6;
00258         seconds = anglePerSec * m_time.second() - 180;
00259 
00260         if (m_fancyHands) {
00261             if (!m_secondHandUpdateTimer) {
00262                 m_secondHandUpdateTimer = new QTimer(this);
00263                 connect(m_secondHandUpdateTimer, SIGNAL(timeout()), this, SLOT(moveSecondHand()));
00264             }
00265 
00266             if (!m_secondHandUpdateTimer->isActive()) {
00267                 //kDebug() << "starting second hand movement";
00268                 m_secondHandUpdateTimer->start(50);
00269                 m_animationStart = QTime::currentTime().msec();
00270             } else {
00271                 static const int runTime = 500;
00272                 static const double m = 1; // Mass
00273                 static const double b = 1; // Drag coefficient
00274                 static const double k = 1.5; // Spring constant
00275                 static const double PI = 3.141592653589793; // the universe is irrational
00276                 static const double gamma = b / (2 * m); // Dampening constant
00277                 static const double omega0 = sqrt(k / m);
00278                 static const double omega1 = sqrt(omega0 * omega0 - gamma * gamma);
00279                 const double elapsed = QTime::currentTime().msec() - m_animationStart;
00280                 const double t = (4 * PI) * (elapsed / runTime);
00281                 const double val = 1 + exp(-gamma * t) * -cos(omega1 * t);
00282 
00283                 if (elapsed > runTime) {
00284                     m_secondHandUpdateTimer->stop();
00285                 } else {
00286                     seconds += -anglePerSec + (anglePerSec * val);
00287                 }
00288             }
00289         } else {
00290             if (!m_secondHandUpdateTimer) {
00291                 m_secondHandUpdateTimer = new QTimer(this);
00292                 connect(m_secondHandUpdateTimer, SIGNAL(timeout()), this, SLOT(moveSecondHand()));
00293             }
00294 
00295             if (!m_secondHandUpdateTimer->isActive()) {
00296                 m_secondHandUpdateTimer->start(50);
00297                 seconds += 1;
00298             } else {
00299                 m_secondHandUpdateTimer->stop();
00300             }
00301         }
00302     }
00303 
00304     // paint face and glass cache
00305     if (m_repaintCache == RepaintAll) {
00306         m_faceCache = QPixmap(rect.size());
00307         m_glassCache = QPixmap(rect.size());
00308         m_faceCache.fill(Qt::transparent);
00309         m_glassCache.fill(Qt::transparent);
00310 
00311         QPainter facePainter(&m_faceCache);
00312         QPainter glassPainter(&m_glassCache);
00313         facePainter.setRenderHint(QPainter::SmoothPixmapTransform);
00314         glassPainter.setRenderHint(QPainter::SmoothPixmapTransform);
00315 
00316         m_theme->paint(&facePainter, rect, "ClockFace");
00317 
00318         // optionally paint the time string
00319         if (m_showTimezoneString || shouldDisplayTimezone()) {
00320             QString time = prettyTimezone();
00321             QFontMetrics fm(QApplication::font());
00322             const int margin = 4;
00323 
00324             if (!time.isEmpty()) {
00325                 const qreal labelHeight = fm.height() + 2 * margin;
00326                 // for small clocks, compute a minimum offset
00327                 qreal labelOffset = m_theme->elementRect("HandCenterScrew").height() / 2 + labelHeight;
00328                 // for larger clocks, add a relative component to the offset
00329                 if ((rect.height() / 2) / 3 > labelHeight) {
00330                     labelOffset += rect.height() / 2 * 0.05;
00331                 }
00332                 QRect textRect(rect.width() / 2 - fm.width(time) / 2, rect.height() / 2 - labelOffset,
00333                       fm.width(time), fm.height());
00334 
00335                 facePainter.setPen(Qt::NoPen);
00336                 QColor background = Plasma::Theme::defaultTheme()->color(Plasma::Theme::BackgroundColor);
00337                 background.setAlphaF(0.5);
00338                 facePainter.setBrush(background);
00339 
00340                 facePainter.setRenderHint(QPainter::Antialiasing, true);
00341                 facePainter.drawPath(Plasma::PaintUtils::roundedRectangle(textRect.adjusted(-margin, -margin, margin, margin), margin));
00342                 facePainter.setRenderHint(QPainter::Antialiasing, false);
00343 
00344                 facePainter.setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
00345 
00346                 facePainter.setFont(Plasma::Theme::defaultTheme()->font(Plasma::Theme::DefaultFont));
00347                 facePainter.drawText(textRect, Qt::AlignCenter, time);
00348             }
00349         }
00350 
00351         glassPainter.save();
00352         QRectF elementRect = QRectF(QPointF(0, 0), m_theme->elementSize("HandCenterScrew"));
00353         glassPainter.translate(rect.width() / 2 - elementRect.width() / 2, rect.height() / 2 - elementRect.height() / 2);
00354         m_theme->paint(&glassPainter, elementRect, "HandCenterScrew");
00355         glassPainter.restore();
00356 
00357         m_theme->paint(&glassPainter, rect, "Glass");
00358 
00359         // get vertical translation, see drawHand() for more details
00360         m_verticalTranslation = m_theme->elementRect("ClockFace").center().y();
00361     }
00362 
00363     // paint hour and minute hands cache
00364     if (m_repaintCache == RepaintHands || m_repaintCache == RepaintAll) {
00365         m_handsCache = QPixmap(rect.size());
00366         m_handsCache.fill(Qt::transparent);
00367 
00368         QPainter handsPainter(&m_handsCache);
00369         handsPainter.setRenderHint(QPainter::SmoothPixmapTransform);
00370 
00371         drawHand(&handsPainter, rect, m_verticalTranslation, hours, "Hour");
00372         drawHand(&handsPainter, rect, m_verticalTranslation, minutes, "Minute");
00373     }
00374 
00375     // reset repaint cache flag
00376     m_repaintCache = RepaintNone;
00377 
00378     // paint caches and second hand
00379     p->setRenderHint(QPainter::SmoothPixmapTransform);
00380     p->drawPixmap(rect, m_faceCache, rect);
00381     p->drawPixmap(rect, m_handsCache, rect);
00382     if (m_showSecondHand) {
00383         drawHand(p, rect, m_verticalTranslation, seconds, "Second");
00384     }
00385     p->drawPixmap(rect, m_glassCache, rect);
00386 
00387 }
00388 
00389 #include "clock.moc"

Applets

Skip menu "Applets"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference 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