File indexing completed on 2024-04-28 04:03:11

0001 /*
0002     This file is part of Knights, a chess board for KDE SC 4.
0003     SPDX-FileCopyrightText: 2010, 2011 Miha Čančula <miha@noughmad.eu>
0004 
0005     Plasma analog-clock drawing code:
0006     SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
0007     SPDX-FileCopyrightText: 2007 Riccardo Iaconelli <riccardo@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0010 */
0011 
0012 #include "clock.h"
0013 
0014 #include <math.h>
0015 
0016 #include <KSvg/Svg>
0017 
0018 #include <QPainter>
0019 #include <QPaintEvent>
0020 
0021 using namespace Knights;
0022 
0023 Clock::Clock(QWidget* parent) : QWidget(parent),
0024     m_theme(new KSvg::Svg(this)),
0025     m_repaintCache(RepaintNone),
0026     m_verticalTranslation(0.) {
0027 
0028     m_theme->imageSet()->setBasePath(QStringLiteral("plasma/desktoptheme"));
0029     m_theme->setImagePath(QStringLiteral("widgets/clock") );
0030     m_theme->setContainsMultipleImages(true);
0031 }
0032 
0033 Clock::~Clock() {
0034     delete m_theme;
0035 }
0036 
0037 void Clock::showEvent( QShowEvent *event ) {
0038     setClockSize( size() );
0039     QWidget::showEvent( event );
0040 }
0041 
0042 void Clock::resizeEvent( QResizeEvent * ) {
0043     setClockSize( size() );
0044 }
0045 
0046 void Clock::setClockSize(const QSize &size) {
0047     int dim = qMin(size.width(), size.height());
0048     QSize newSize = QSize(dim, dim);
0049 
0050     if (newSize != m_faceCache.size()) {
0051         m_faceCache = QPixmap(newSize);
0052         m_handsCache = QPixmap(newSize);
0053         m_glassCache = QPixmap(newSize);
0054 
0055         m_theme->resize(newSize);
0056         m_repaintCache = RepaintAll;
0057     }
0058 }
0059 
0060 void Clock::setTime(const QTime &time) {
0061     if (m_repaintCache == RepaintNone)
0062         m_repaintCache = RepaintHands;
0063     this->time = time;
0064     update();
0065 }
0066 
0067 void Clock::setTime(int miliSeconds) {
0068     setTime(QTime().addMSecs(miliSeconds));
0069 }
0070 
0071 void Clock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName) {
0072     // this code assumes the following conventions in the svg file:
0073     // - the _vertical_ position of the hands should be set with respect to the center of the face
0074     // - the _horizontal_ position of the hands does not matter
0075     // - the _shadow_ elements should have the same vertical position as their _hand_ element counterpart
0076 
0077     QRectF elementRect;
0078     QString name = handName + QLatin1String( "HandShadow" );
0079     if (m_theme->hasElement(name)) {
0080         p->save();
0081 
0082         elementRect = m_theme->elementRect(name);
0083         if( rect.height() < 64 )
0084             elementRect.setWidth( elementRect.width() * 2.5 );
0085         static const QPoint offset = QPoint(2, 3);
0086 
0087         p->translate(rect.x() + (rect.width() / 2) + offset.x(), rect.y() + (rect.height() / 2) + offset.y());
0088         p->rotate(rotation);
0089         p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation);
0090         m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name);
0091 
0092         p->restore();
0093     }
0094 
0095     p->save();
0096 
0097     name = handName + QLatin1String("Hand");
0098     elementRect = m_theme->elementRect(name);
0099     if (rect.height() < 64)
0100         elementRect.setWidth(elementRect.width() * 2.5);
0101 
0102     p->translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
0103     p->rotate(rotation);
0104     p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation);
0105     m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name);
0106 
0107     p->restore();
0108 }
0109 
0110 void Clock::paintInterface(QPainter *p, const QRect &rect) {
0111     const bool m_showSecondHand = true;
0112 
0113     // compute hand angles
0114     // Because this clock shows time remainig, all the angles are negative
0115     const qreal minutes = -6.0 * time.minute() - 0.1 * time.second() - 180;
0116     const qreal hours = -30.0 * time.hour() - 0.5 * time.minute() - 180;
0117     qreal seconds = 0;
0118     if (m_showSecondHand) {
0119         static const double anglePerSec = 6;
0120         seconds = -anglePerSec * ( time.second() + time.msec() / 1000.0 ) - 180;
0121     }
0122 
0123     // paint face and glass cache
0124     QRect faceRect = m_faceCache.rect();
0125     if (m_repaintCache == RepaintAll) {
0126         m_faceCache.fill(Qt::transparent);
0127         m_glassCache.fill(Qt::transparent);
0128 
0129         QPainter facePainter(&m_faceCache);
0130         QPainter glassPainter(&m_glassCache);
0131         facePainter.setRenderHint(QPainter::SmoothPixmapTransform);
0132         glassPainter.setRenderHint(QPainter::SmoothPixmapTransform);
0133 
0134         m_theme->paint(&facePainter, m_faceCache.rect(), QStringLiteral("ClockFace") );
0135 
0136         glassPainter.save();
0137         QRectF elementRect = QRectF(QPointF(0, 0), m_theme->elementSize(QStringLiteral("HandCenterScrew")));
0138         glassPainter.translate(faceRect.width() / 2 - elementRect.width() / 2, faceRect.height() / 2 - elementRect.height() / 2);
0139         m_theme->paint(&glassPainter, elementRect, QStringLiteral("HandCenterScrew"));
0140         glassPainter.restore();
0141 
0142         m_theme->paint(&glassPainter, faceRect, QStringLiteral("Glass"));
0143 
0144         // get vertical translation, see drawHand() for more details
0145         m_verticalTranslation = m_theme->elementRect(QStringLiteral("ClockFace")).center().y();
0146     }
0147 
0148     // paint hour and minute hands cache
0149     if (m_repaintCache == RepaintHands || m_repaintCache == RepaintAll) {
0150         m_handsCache.fill(Qt::transparent);
0151 
0152         QPainter handsPainter(&m_handsCache);
0153         handsPainter.drawPixmap(faceRect, m_faceCache, faceRect);
0154         handsPainter.setRenderHint(QPainter::SmoothPixmapTransform);
0155 
0156         drawHand(&handsPainter, faceRect, m_verticalTranslation, hours, QStringLiteral("Hour"));
0157         drawHand(&handsPainter, faceRect, m_verticalTranslation, minutes, QStringLiteral("Minute"));
0158     }
0159 
0160     // reset repaint cache flag
0161     m_repaintCache = RepaintNone;
0162 
0163     // paint caches and second hand
0164     QRect targetRect = faceRect;
0165     if (targetRect.width() < rect.width())
0166         targetRect.moveLeft((rect.width() - targetRect.width()) / 2);
0167     if (targetRect.height() < rect.height())
0168         targetRect.moveTop((rect.height() - targetRect.height()) / 2);
0169 
0170     p->drawPixmap(targetRect, m_handsCache, faceRect);
0171     if (m_showSecondHand) {
0172         p->setRenderHint(QPainter::SmoothPixmapTransform);
0173         drawHand(p, targetRect, m_verticalTranslation, seconds, QStringLiteral("Second"));
0174     }
0175     p->drawPixmap(targetRect, m_glassCache, faceRect);
0176 }
0177 
0178 void Clock::paintEvent( QPaintEvent * ) {
0179     QPainter paint(this);
0180 
0181     paint.setRenderHint(QPainter::Antialiasing);
0182     paintInterface(&paint, rect());
0183 }
0184 
0185 #include "moc_clock.cpp"