00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00124
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
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
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
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
00208 update();
00209 }
00210
00211 void Clock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName)
00212 {
00213
00214
00215
00216
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
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
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;
00273 static const double b = 1;
00274 static const double k = 1.5;
00275 static const double PI = 3.141592653589793;
00276 static const double gamma = b / (2 * m);
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
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
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
00327 qreal labelOffset = m_theme->elementRect("HandCenterScrew").height() / 2 + labelHeight;
00328
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
00360 m_verticalTranslation = m_theme->elementRect("ClockFace").center().y();
00361 }
00362
00363
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
00376 m_repaintCache = RepaintNone;
00377
00378
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"