diff --git a/kcms/dateandtime/dtime.cpp b/kcms/dateandtime/dtime.cpp index 33f7f1508..9cfcdd55b 100644 --- a/kcms/dateandtime/dtime.cpp +++ b/kcms/dateandtime/dtime.cpp @@ -1,490 +1,489 @@ /* * dtime.cpp * * Copyright (C) 1998 Luca Montecchiani * * Plasma analog-clock drawing code: * * Copyright 2007 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "dtime.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include "timedated_interface.h" #include "helper.h" Dtime::Dtime(QWidget * parent, bool haveTimeDated): QWidget(parent), m_haveTimedated(haveTimeDated) { setupUi(this); connect(setDateTimeAuto, &QCheckBox::toggled, this, &Dtime::serverTimeCheck); connect(setDateTimeAuto, &QCheckBox::toggled, this, &Dtime::configChanged); timeServerList->setEditable(false); connect(timeServerList, static_cast(&QComboBox::activated), this, &Dtime::configChanged); connect(timeServerList, &QComboBox::editTextChanged, this, &Dtime::configChanged); connect(setDateTimeAuto, &QCheckBox::toggled, timeServerList, &QComboBox::setEnabled); timeServerList->setEnabled(false); timeServerList->setEditable(true); if (!haveTimeDated) { findNTPutility(); if (ntpUtility.isEmpty()) { QString toolTip = i18n("No NTP utility has been found. " "Install 'ntpdate' or 'rdate' command to enable automatic " "updating of date and time."); setDateTimeAuto->setEnabled(false); setDateTimeAuto->setToolTip(toolTip); timeServerList->setToolTip(toolTip); } } QVBoxLayout *v2 = new QVBoxLayout( timeBox ); v2->setContentsMargins( 0, 0, 0, 0 ); kclock = new Kclock( timeBox ); kclock->setObjectName(QStringLiteral("Kclock")); kclock->setMinimumSize(150,150); v2->addWidget( kclock ); - v2->addSpacing( KDialog::spacingHint() ); + v2->addSpacing( style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing) ); QHBoxLayout *v3 = new QHBoxLayout( ); v2->addLayout( v3 ); v3->addStretch(); timeEdit = new QTimeEdit( timeBox ); timeEdit->setWrapping(true); timeEdit->setDisplayFormat(KLocale::global()->use12Clock() ? "hh:mm:ss ap" : "HH:mm:ss"); v3->addWidget(timeEdit); v3->addStretch(); QString wtstr = i18n("Here you can change the system time. Click into the" " hours, minutes or seconds field to change the relevant value, either" " using the up and down buttons to the right or by entering a new value."); timeEdit->setWhatsThis( wtstr ); connect(timeEdit, &QTimeEdit::timeChanged, this, &Dtime::set_time); connect(cal, &KDatePicker::dateChanged, this, &Dtime::changeDate); connect(&internalTimer, &QTimer::timeout, this, &Dtime::timeout); kclock->setEnabled(false); //Timezone connect(tzonelist, &K4TimeZoneWidget::itemSelectionChanged, this, &Dtime::configChanged); tzonesearch->setTreeWidget(tzonelist); } void Dtime::currentZone() { KTimeZone localZone = KSystemTimeZones::local(); if (localZone.abbreviations().isEmpty()) { m_local->setText(i18nc("%1 is name of time zone", "Current local time zone: %1", K4TimeZoneWidget::displayName(localZone))); } else { m_local->setText(i18nc("%1 is name of time zone, %2 is its abbreviation", "Current local time zone: %1 (%2)", K4TimeZoneWidget::displayName(localZone), QString::fromUtf8(localZone.abbreviations().first()))); } } void Dtime::serverTimeCheck() { // Enable time and date if the ntp utility is missing bool enabled = ntpUtility.isEmpty() || !setDateTimeAuto->isChecked(); cal->setEnabled(enabled); timeEdit->setEnabled(enabled); //kclock->setEnabled(enabled); } void Dtime::findNTPutility(){ QByteArray envpath = qgetenv("PATH"); if (!envpath.isEmpty() && envpath.startsWith(':')) { envpath.remove(0, 1); } QStringList path = {"/sbin", "/usr/sbin"}; if (!envpath.isEmpty()) { path += QFile::decodeName(envpath).split(QLatin1Char(':')); } else { path += {"/bin", "/usr/bin"}; } const auto possible_ntputilities = {"ntpdate", "rdate"}; for (const QString &possible_ntputility : possible_ntputilities) { auto ntpUtility = QStandardPaths::findExecutable(possible_ntputility, path); if (!ntpUtility.isEmpty()) { qDebug() << "ntpUtility = " << ntpUtility; return; } } qDebug() << "ntpUtility not found!"; } void Dtime::set_time() { if( ontimeout ) return; internalTimer.stop(); time = timeEdit->time(); kclock->setTime( time ); emit timeChanged( true ); } void Dtime::changeDate(const QDate &d) { date = d; emit timeChanged( true ); } void Dtime::configChanged(){ emit timeChanged( true ); } void Dtime::load() { QString currentTimeZone; if (m_haveTimedated) { OrgFreedesktopTimedate1Interface timeDatedIface(QStringLiteral("org.freedesktop.timedate1"), QStringLiteral("/org/freedesktop/timedate1"), QDBusConnection::systemBus()); //the server list is not relevant for timesyncd, it fetches it from the network timeServerList->setVisible(false); timeServerLabel->setVisible(false); setDateTimeAuto->setEnabled(timeDatedIface.canNTP()); setDateTimeAuto->setChecked(timeDatedIface.nTP()); currentTimeZone = timeDatedIface.timezone(); } else { // The config is actually written to the system config, but the user does not have any local config, // since there is nothing writing it. KConfig _config( QStringLiteral("kcmclockrc"), KConfig::NoGlobals ); KConfigGroup config(&_config, "NTP"); timeServerList->clear(); timeServerList->addItems(config.readEntry("servers", i18n("Public Time Server (pool.ntp.org),\ asia.pool.ntp.org,\ europe.pool.ntp.org,\ north-america.pool.ntp.org,\ oceania.pool.ntp.org")).split(',', QString::SkipEmptyParts)); setDateTimeAuto->setChecked(config.readEntry("enabled", false)); if (ntpUtility.isEmpty()) { timeServerList->setEnabled(false); } currentTimeZone = KSystemTimeZones::local().name(); } // Reset to the current date and time time = QTime::currentTime(); date = QDate::currentDate(); cal->setDate(date); // start internal timer internalTimer.start( 1000 ); timeout(); //Timezone currentZone(); tzonelist->setSelected(currentTimeZone, true); emit timeChanged(false); } QString Dtime::selectedTimeZone() const { QStringList selectedZones(tzonelist->selection()); if (!selectedZones.isEmpty()) { return selectedZones.first(); } return QString(); } QStringList Dtime::ntpServers() const { // Save the order, but don't duplicate! QStringList list; if( timeServerList->count() != 0) list.append(timeServerList->currentText()); for ( int i=0; icount();i++ ) { QString text = timeServerList->itemText(i); if( !list.contains(text) ) list.append(text); // Limit so errors can go away and not stored forever if( list.count() == 10) break; } return list; } bool Dtime::ntpEnabled() const { return setDateTimeAuto->isChecked(); } QDateTime Dtime::userTime() const { return QDateTime(date, QTime(timeEdit->time())); } void Dtime::processHelperErrors( int code ) { if( code & ClockHelper::NTPError ) { KMessageBox::error( this, i18n("Unable to contact time server: %1.", timeServer) ); setDateTimeAuto->setChecked( false ); } if( code & ClockHelper::DateError ) { KMessageBox::error( this, i18n("Can not set date.")); } if( code & ClockHelper::TimezoneError) KMessageBox::error( this, i18n("Error setting new time zone."), i18n("Time zone Error")); } void Dtime::timeout() { // get current time time = QTime::currentTime(); ontimeout = true; timeEdit->setTime(time); ontimeout = false; kclock->setTime( time ); } QString Dtime::quickHelp() const { return i18n("

Date & Time

This system settings module can be used to set the system date and" " time. As these settings do not only affect you as a user, but rather the whole system, you" " can only change these settings when you start the System Settings as root. If you do not have" " the root password, but feel the system time should be corrected, please contact your system" " administrator."); } Kclock::Kclock(QWidget *parent) : QWidget(parent) { m_theme = new Plasma::Svg(this); m_theme->setImagePath(QStringLiteral("widgets/clock")); m_theme->setContainsMultipleImages(true); } Kclock::~Kclock() { delete m_theme; } void Kclock::showEvent( QShowEvent *event ) { setClockSize( size() ); QWidget::showEvent( event ); } void Kclock::resizeEvent( QResizeEvent * ) { setClockSize( size() ); } void Kclock::setClockSize(const QSize &size) { int dim = qMin(size.width(), size.height()); QSize newSize = QSize(dim, dim) * devicePixelRatioF(); if (newSize != m_faceCache.size()) { m_faceCache = QPixmap(newSize); m_handsCache = QPixmap(newSize); m_glassCache = QPixmap(newSize); m_faceCache.setDevicePixelRatio(devicePixelRatioF()); m_handsCache.setDevicePixelRatio(devicePixelRatioF()); m_glassCache.setDevicePixelRatio(devicePixelRatioF()); m_theme->resize(QSize(dim, dim)); m_repaintCache = RepaintAll; } } void Kclock::setTime(const QTime &time) { if (time.minute() != this->time.minute() || time.hour() != this->time.hour()) { if (m_repaintCache == RepaintNone) { m_repaintCache = RepaintHands; } } this->time = time; update(); } void Kclock::drawHand(QPainter *p, const QRect &rect, const qreal verticalTranslation, const qreal rotation, const QString &handName) { // this code assumes the following conventions in the svg file: // - the _vertical_ position of the hands should be set with respect to the center of the face // - the _horizontal_ position of the hands does not matter // - the _shadow_ elements should have the same vertical position as their _hand_ element counterpart QRectF elementRect; QString name = handName + "HandShadow"; if (m_theme->hasElement(name)) { p->save(); elementRect = m_theme->elementRect(name); if( rect.height() < 64 ) elementRect.setWidth( elementRect.width() * 2.5 ); static const QPoint offset = QPoint(2, 3); p->translate(rect.x() + (rect.width() / 2) + offset.x(), rect.y() + (rect.height() / 2) + offset.y()); p->rotate(rotation); p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); p->restore(); } p->save(); name = handName + "Hand"; elementRect = m_theme->elementRect(name); if (rect.height() < 64) { elementRect.setWidth(elementRect.width() * 2.5); } p->translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); p->rotate(rotation); p->translate(-elementRect.width()/2, elementRect.y()-verticalTranslation); m_theme->paint(p, QRectF(QPointF(0, 0), elementRect.size()), name); p->restore(); } void Kclock::paintInterface(QPainter *p, const QRect &rect) { const bool m_showSecondHand = true; // compute hand angles const qreal minutes = 6.0 * time.minute() - 180; const qreal hours = 30.0 * time.hour() - 180 + ((time.minute() / 59.0) * 30.0); qreal seconds = 0; if (m_showSecondHand) { static const double anglePerSec = 6; seconds = anglePerSec * time.second() - 180; } // paint face and glass cache QRect faceRect = m_faceCache.rect(); - QRect targetRect = QRect(QPoint(0, 0), QSize(m_faceCache.width() / devicePixelRatioF(), m_faceCache.height() / devicePixelRatioF())); + QRect targetRect = QRect(QPoint(0, 0), QSize(qRound(m_faceCache.width() / devicePixelRatioF()), qRound(m_faceCache.height() / devicePixelRatioF()))); if (m_repaintCache == RepaintAll) { m_faceCache.fill(Qt::transparent); m_glassCache.fill(Qt::transparent); QPainter facePainter(&m_faceCache); QPainter glassPainter(&m_glassCache); facePainter.setRenderHint(QPainter::SmoothPixmapTransform); glassPainter.setRenderHint(QPainter::SmoothPixmapTransform); m_theme->paint(&facePainter, targetRect, QStringLiteral("ClockFace")); glassPainter.save(); QRectF elementRect = QRectF(QPointF(0, 0), m_theme->elementSize(QStringLiteral("HandCenterScrew"))); glassPainter.translate(faceRect.width() / (2 * devicePixelRatioF()) - elementRect.width() / 2, faceRect.height() / (2 * devicePixelRatioF()) - elementRect.height() / 2); m_theme->paint(&glassPainter, elementRect, QStringLiteral("HandCenterScrew")); glassPainter.restore(); m_theme->paint(&glassPainter, targetRect, QStringLiteral("Glass")); // get vertical translation, see drawHand() for more details m_verticalTranslation = m_theme->elementRect(QStringLiteral("ClockFace")).center().y(); } // paint hour and minute hands cache if (m_repaintCache == RepaintHands || m_repaintCache == RepaintAll) { m_handsCache.fill(Qt::transparent); QPainter handsPainter(&m_handsCache); handsPainter.drawPixmap(targetRect, m_faceCache, faceRect); handsPainter.setRenderHint(QPainter::SmoothPixmapTransform); drawHand(&handsPainter, targetRect, m_verticalTranslation, hours, QStringLiteral("Hour")); drawHand(&handsPainter, targetRect, m_verticalTranslation, minutes, QStringLiteral("Minute")); } // reset repaint cache flag m_repaintCache = RepaintNone; // paint caches and second hand if (targetRect.width() < rect.width()) { targetRect.moveLeft((rect.width() - targetRect.width()) / 2); } p->drawPixmap(targetRect, m_handsCache, faceRect); if (m_showSecondHand) { p->setRenderHint(QPainter::SmoothPixmapTransform); drawHand(p, targetRect, m_verticalTranslation, seconds, QStringLiteral("Second")); } p->drawPixmap(targetRect, m_glassCache, faceRect); } void Kclock::paintEvent( QPaintEvent * ) { QPainter paint(this); paint.setRenderHint(QPainter::Antialiasing); paintInterface(&paint, rect()); } diff --git a/kcms/dateandtime/main.cpp b/kcms/dateandtime/main.cpp index 8aec086d7..3bab61c30 100644 --- a/kcms/dateandtime/main.cpp +++ b/kcms/dateandtime/main.cpp @@ -1,212 +1,211 @@ /* * main.cpp * * Copyright (C) 1998 Luca Montecchiani * Copyright (C) 2015 David Edmundson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "main.h" #include #include #include #include #include -#include #include #include #include #include "dtime.h" #include "helper.h" #include #include #include "timedated_interface.h" K_PLUGIN_FACTORY(KlockModuleFactory, registerPlugin();) KclockModule::KclockModule(QWidget *parent, const QVariantList &) : KCModule(parent) { auto reply = QDBusConnection::systemBus().call(QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/org/freedesktop/DBus"), QStringLiteral("org.freedesktop.DBus"), QStringLiteral("ListActivatableNames"))); if (!reply.arguments().isEmpty() && reply.arguments().first().value().contains(QLatin1String("org.freedesktop.timedate1"))) { m_haveTimedated = true; } KAboutData *about = new KAboutData(QStringLiteral("kcmclock"), i18n("KDE Clock Control Module"), QStringLiteral("1.0"), QString(), KAboutLicense::GPL, i18n("(c) 1996 - 2001 Luca Montecchiani")); about->addAuthor(i18n("Luca Montecchiani"), i18n("Original author"), QStringLiteral("m.luca@usa.net")); about->addAuthor(i18n("Paul Campbell"), i18n("Current Maintainer"), QStringLiteral("paul@taniwha.com")); about->addAuthor(i18n("Benjamin Meyer"), i18n("Added NTP support"), QStringLiteral("ben+kcmclock@meyerhome.net")); setAboutData( about ); setQuickHelp( i18n("

Date & Time

This control module can be used to set the system date and" " time. As these settings do not only affect you as a user, but rather the whole system, you" " can only change these settings when you start the System Settings as root. If you do not have" " the root password, but feel the system time should be corrected, please contact your system" " administrator.")); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(KDialog::spacingHint()); + layout->setSpacing(style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing) ); dtime = new Dtime(this, m_haveTimedated); layout->addWidget(dtime); connect(dtime, SIGNAL(timeChanged(bool)), this, SIGNAL(changed(bool))); setButtons(Help|Apply); if (m_haveTimedated) { setAuthAction(KAuth::Action(QStringLiteral("org.freedesktop.timedate1.set-time"))); } else { //auth action name will be automatically guessed from the KCM name qWarning() << "Timedated not found, using legacy saving mode"; setNeedsAuthorization(true); } } bool KclockModule::kauthSave() { QVariantMap helperargs; helperargs[QStringLiteral("ntp")] = true; helperargs[QStringLiteral("ntpServers")] = dtime->ntpServers(); helperargs[QStringLiteral("ntpEnabled")] = dtime->ntpEnabled(); if (!dtime->ntpEnabled()) { QDateTime newTime = dtime->userTime(); qDebug() << "Set date to " << dtime->userTime(); helperargs[QStringLiteral("date")] = true; helperargs[QStringLiteral("newdate")] = QString::number(newTime.toTime_t()); helperargs[QStringLiteral("olddate")] = QString::number(::time(nullptr)); } QString selectedTimeZone = dtime->selectedTimeZone(); if (!selectedTimeZone.isEmpty()) { helperargs[QStringLiteral("tz")] = true; helperargs[QStringLiteral("tzone")] = selectedTimeZone; } else { helperargs[QStringLiteral("tzreset")] = true; // make the helper reset the timezone } Action action = authAction(); action.setArguments(helperargs); ExecuteJob *job = action.execute(); bool rc = job->exec(); if (!rc) { KMessageBox::error(this, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); } return rc; } bool KclockModule::timedatedSave() { OrgFreedesktopTimedate1Interface timedateIface(QStringLiteral("org.freedesktop.timedate1"), QStringLiteral("/org/freedesktop/timedate1"), QDBusConnection::systemBus()); bool rc = true; //final arg in each method is "user-interaction" i.e whether it's OK for polkit to ask for auth //we cannot send requests up front then block for all replies as we need NTP to be disabled before we can make a call to SetTime //timedated processes these in parallel and will return an error otherwise auto reply = timedateIface.SetNTP(dtime->ntpEnabled(), true); reply.waitForFinished(); if (reply.isError()) { KMessageBox::error(this, i18n("Unable to change NTP settings")); qWarning() << "Failed to enable NTP" << reply.error().name() << reply.error().message(); rc = false; } if (!dtime->ntpEnabled()) { qint64 timeDiff = dtime->userTime().toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch(); //*1000 for milliseconds -> microseconds auto reply = timedateIface.SetTime(timeDiff * 1000, true, true); reply.waitForFinished(); if (reply.isError()) { KMessageBox::error(this, i18n("Unable to set current time")); qWarning() << "Failed to set current time" << reply.error().name() << reply.error().message(); rc = false; } } QString selectedTimeZone = dtime->selectedTimeZone(); if (!selectedTimeZone.isEmpty()) { auto reply = timedateIface.SetTimezone(selectedTimeZone, true); reply.waitForFinished(); if (reply.isError()) { KMessageBox::error(this, i18n("Unable to set timezone")); qWarning() << "Failed to set timezone" << reply.error().name() << reply.error().message(); rc = false; } } return rc; } void KclockModule::save() { setDisabled(true); bool success = false; if (m_haveTimedated) { success = timedatedSave(); } else { success = kauthSave(); } if (success) { QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/kde/kcmshell_clock"), QStringLiteral("org.kde.kcmshell_clock"), QStringLiteral("clockUpdated")); QDBusConnection::sessionBus().send(msg); } // NOTE: super nasty hack #1 // Try to work around time mismatch between KSystemTimeZones' update of local // timezone and reloading of data, so that the new timezone is taken into account. // The Ultimate solution to this would be if KSTZ emitted a signal when a new // local timezone was found. // setDisabled(false) happens in load(), since QTimer::singleShot is non-blocking if (!m_haveTimedated) { QTimer::singleShot(5000, this, SLOT(load())); } else { load(); } } void KclockModule::load() { dtime->load(); setDisabled(false); } #include "main.moc"