/* BEGIN_COMMON_COPYRIGHT_HEADER * (c)LGPL2+ * * LXDE-Qt - a lightweight, Qt based, desktop toolset * http://razor-qt.org * * Copyright: 2012-2013 Razor team * 2014 LXQt team * Authors: * Kuzma Shapran * * This program or library is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * END_COMMON_COPYRIGHT_HEADER */ #include "lxqtworldclock.h" #include #include #include #include #include #include #include #include #include #include LXQtWorldClock::LXQtWorldClock(const ILXQtPanelPluginStartupInfo &startupInfo): QObject(), ILXQtPanelPlugin(startupInfo), mPopup(NULL), mTimer(new QTimer(this)), mUpdateInterval(1), mAutoRotate(true), mPopupContent(NULL) { mMainWidget = new QWidget(); mContent = new ActiveLabel(); mRotatedWidget = new LXQt::RotatedWidget(*mContent, mMainWidget); mRotatedWidget->setTransferWheelEvent(true); QVBoxLayout *borderLayout = new QVBoxLayout(mMainWidget); borderLayout->setContentsMargins(0, 0, 0, 0); borderLayout->setSpacing(0); borderLayout->addWidget(mRotatedWidget, 0, Qt::AlignCenter); mContent->setObjectName(QLatin1String("WorldClockContent")); mContent->setAlignment(Qt::AlignCenter); settingsChanged(); mTimer->setTimerType(Qt::PreciseTimer); connect(mTimer, SIGNAL(timeout()), SLOT(timeout())); connect(mContent, SIGNAL(wheelScrolled(int)), SLOT(wheelScrolled(int))); } LXQtWorldClock::~LXQtWorldClock() { delete mMainWidget; } void LXQtWorldClock::timeout() { if (QDateTime{}.time().msec() > 500) restartTimer(); updateTimeText(); } void LXQtWorldClock::updateTimeText() { QDateTime now = QDateTime::currentDateTime(); QString timeZoneName = mActiveTimeZone; if (timeZoneName == QLatin1String("local")) timeZoneName = QString::fromLatin1(QTimeZone::systemTimeZoneId()); QTimeZone timeZone(timeZoneName.toLatin1()); QDateTime tzNow = now.toTimeZone(timeZone); bool isUpToDate(true); if (!mShownTime.isValid()) // first time or forced update { isUpToDate = false; if (mUpdateInterval < 60000) mShownTime = tzNow.addSecs(-tzNow.time().msec()); // s else if (mUpdateInterval < 3600000) mShownTime = tzNow.addSecs(-tzNow.time().second()); // m else mShownTime = tzNow.addSecs(-tzNow.time().minute() * 60 - tzNow.time().second()); // h } else { qint64 diff = mShownTime.secsTo(tzNow); if (mUpdateInterval < 60000) { if (diff < 0 || diff >= 1) { isUpToDate = false; mShownTime = tzNow.addSecs(-tzNow.time().msec()); } } else if (mUpdateInterval < 3600000) { if (diff < 0 || diff >= 60) { isUpToDate = false; mShownTime = tzNow.addSecs(-tzNow.time().second()); } } else if (diff < 0 || diff >= 3600) { isUpToDate = false; mShownTime = tzNow.addSecs(-tzNow.time().minute() * 60 - tzNow.time().second()); } } if (!isUpToDate) { mContent->setText(tzNow.toString(preformat(mFormat, timeZone, tzNow))); mRotatedWidget->update(); updatePopupContent(); } } void LXQtWorldClock::setTimeText() { mShownTime = QDateTime(); // force an update updateTimeText(); } void LXQtWorldClock::restartTimer() { mTimer->stop(); // check the time every second even if the clock doesn't show seconds // because otherwise, the shown time might be vey wrong after resume mTimer->setInterval(1000); int delay = static_cast(1000 - (static_cast(QTime::currentTime().msecsSinceStartOfDay()) % 1000)); QTimer::singleShot(delay, Qt::PreciseTimer, this, &LXQtWorldClock::updateTimeText); QTimer::singleShot(delay, Qt::PreciseTimer, mTimer, SLOT(start())); } void LXQtWorldClock::settingsChanged() { PluginSettings *_settings = settings(); QString oldFormat = mFormat; mTimeZones.clear(); QList > array = _settings->readArray(QLatin1String("timeZones")); for (const auto &map : array) { QString timeZoneName = map.value(QLatin1String("timeZone"), QString()).toString(); mTimeZones.append(timeZoneName); mTimeZoneCustomNames[timeZoneName] = map.value(QLatin1String("customName"), QString()).toString(); } if (mTimeZones.isEmpty()) mTimeZones.append(QLatin1String("local")); mDefaultTimeZone = _settings->value(QLatin1String("defaultTimeZone"), QString()).toString(); if (mDefaultTimeZone.isEmpty()) mDefaultTimeZone = mTimeZones[0]; mActiveTimeZone = mDefaultTimeZone; bool longTimeFormatSelected = false; QString formatType = _settings->value(QLatin1String("formatType"), QString()).toString(); QString dateFormatType = _settings->value(QLatin1String("dateFormatType"), QString()).toString(); bool advancedManual = _settings->value(QLatin1String("useAdvancedManualFormat"), false).toBool(); // backward compatibility if (formatType == QLatin1String("custom")) { formatType = QLatin1String("short-timeonly"); dateFormatType = QLatin1String("short"); advancedManual = true; } else if (formatType == QLatin1String("short")) { formatType = QLatin1String("short-timeonly"); dateFormatType = QLatin1String("short"); advancedManual = false; } else if ((formatType == QLatin1String("full")) || (formatType == QLatin1String("long")) || (formatType == QLatin1String("medium"))) { formatType = QLatin1String("long-timeonly"); dateFormatType = QLatin1String("long"); advancedManual = false; } if (formatType == QLatin1String("long-timeonly")) longTimeFormatSelected = true; bool timeShowSeconds = _settings->value(QLatin1String("timeShowSeconds"), false).toBool(); bool timePadHour = _settings->value(QLatin1String("timePadHour"), false).toBool(); bool timeAMPM = _settings->value(QLatin1String("timeAMPM"), false).toBool(); // timezone bool showTimezone = _settings->value(QLatin1String("showTimezone"), false).toBool() && !longTimeFormatSelected; QString timezonePosition = _settings->value(QLatin1String("timezonePosition"), QString()).toString(); QString timezoneFormatType = _settings->value(QLatin1String("timezoneFormatType"), QString()).toString(); // date bool showDate = _settings->value(QLatin1String("showDate"), false).toBool(); QString datePosition = _settings->value(QLatin1String("datePosition"), QString()).toString(); bool dateShowYear = _settings->value(QLatin1String("dateShowYear"), false).toBool(); bool dateShowDoW = _settings->value(QLatin1String("dateShowDoW"), false).toBool(); bool datePadDay = _settings->value(QLatin1String("datePadDay"), false).toBool(); bool dateLongNames = _settings->value(QLatin1String("dateLongNames"), false).toBool(); // advanced QString customFormat = _settings->value(QLatin1String("customFormat"), tr("''HH:mm:ss'
'ddd, d MMM yyyy'
'TT'
'")).toString(); if (advancedManual) mFormat = customFormat; else { QLocale locale = QLocale(QLocale::AnyLanguage, QLocale().country()); if (formatType == QLatin1String("short-timeonly")) mFormat = locale.timeFormat(QLocale::ShortFormat); else if (formatType == QLatin1String("long-timeonly")) mFormat = locale.timeFormat(QLocale::LongFormat); else // if (formatType == QLatin1String("custom-timeonly")) mFormat = QString(QLatin1String("%1:mm%2%3")).arg(timePadHour ? QLatin1String("hh") : QLatin1String("h")).arg(timeShowSeconds ? QLatin1String(":ss") : QLatin1String("")).arg(timeAMPM ? QLatin1String(" A") : QLatin1String("")); if (showTimezone) { QString timezonePortion; if (timezoneFormatType == QLatin1String("short")) timezonePortion = QLatin1String("TTTT"); else if (timezoneFormatType == QLatin1String("long")) timezonePortion = QLatin1String("TTTTT"); else if (timezoneFormatType == QLatin1String("offset")) timezonePortion = QLatin1String("T"); else if (timezoneFormatType == QLatin1String("abbreviation")) timezonePortion = QLatin1String("TTT"); else if (timezoneFormatType == QLatin1String("iana")) timezonePortion = QLatin1String("TT"); else // if (timezoneFormatType == QLatin1String("custom")) timezonePortion = QLatin1String("TTTTTT"); if (timezonePosition == QLatin1String("below")) mFormat = mFormat + QLatin1String("'
'") + timezonePortion; else if (timezonePosition == QLatin1String("above")) mFormat = timezonePortion + QLatin1String("'
'") + mFormat; else if (timezonePosition == QLatin1String("before")) mFormat = timezonePortion + QLatin1String(" ") + mFormat; else // if (timezonePosition == QLatin1String("after")) mFormat = mFormat + QLatin1String(" ") + timezonePortion; } if (showDate) { QString datePortion; if (dateFormatType == QLatin1String("short")) datePortion = locale.dateFormat(QLocale::ShortFormat); else if (dateFormatType == QLatin1String("long")) datePortion = locale.dateFormat(QLocale::LongFormat); else if (dateFormatType == QLatin1String("iso")) datePortion = QLatin1String("yyyy-MM-dd"); else // if (dateFormatType == QLatin1String("custom")) { QString datePortionOrder; QString dateLocale = locale.dateFormat(QLocale::ShortFormat).toLower(); int yearIndex = dateLocale.indexOf("y"); int monthIndex = dateLocale.indexOf("m"); int dayIndex = dateLocale.indexOf("d"); if (yearIndex < dayIndex) // Big-endian (year, month, day) (yyyy MMMM dd, dddd) -> in some Asia countires like China or Japan datePortionOrder = QLatin1String("%1%2%3 %4%5%6"); else if (monthIndex < dayIndex) // Middle-endian (month, day, year) (dddd, MMMM dd yyyy) -> USA datePortionOrder = QLatin1String("%6%5%3 %4%2%1"); else // Little-endian (day, month, year) (dddd, dd MMMM yyyy) -> most of Europe datePortionOrder = QLatin1String("%6%5%4 %3%2%1"); datePortion = datePortionOrder.arg(dateShowYear ? QLatin1String("yyyy") : QLatin1String("")).arg(dateShowYear ? QLatin1String(" ") : QLatin1String("")).arg(dateLongNames ? QLatin1String("MMMM") : QLatin1String("MMM")).arg(datePadDay ? QLatin1String("dd") : QLatin1String("d")).arg(dateShowDoW ? QLatin1String(", ") : QLatin1String("")).arg(dateShowDoW ? (dateLongNames ? QLatin1String("dddd") : QLatin1String("ddd")) : QLatin1String("")); } if (datePosition == QLatin1String("below")) mFormat = mFormat + QLatin1String("'
'") + datePortion; else if (datePosition == QLatin1String("above")) mFormat = datePortion + QLatin1String("'
'") + mFormat; else if (datePosition == QLatin1String("before")) mFormat = datePortion + QLatin1String(" ") + mFormat; else // if (datePosition == QLatin1String("after")) mFormat = mFormat + QLatin1String(" ") + datePortion; } } if ((oldFormat != mFormat)) { int update_interval; QString format = mFormat; format.replace(QRegExp(QLatin1String("'[^']*'")), QString()); //don't support updating on milisecond basis -> big performance hit if (format.contains(QLatin1String("s"))) update_interval = 1000; else if (format.contains(QLatin1String("m"))) update_interval = 60000; else update_interval = 3600000; if (update_interval != mUpdateInterval) { mUpdateInterval = update_interval; restartTimer(); } } bool autoRotate = settings()->value(QLatin1String("autoRotate"), true).toBool(); if (autoRotate != mAutoRotate) { mAutoRotate = autoRotate; realign(); } if (mPopup) { updatePopupContent(); mPopup->adjustSize(); mPopup->setGeometry(calculatePopupWindowPos(mPopup->size())); } setTimeText(); } QDialog *LXQtWorldClock::configureDialog() { return new LXQtWorldClockConfiguration(settings()); } void LXQtWorldClock::wheelScrolled(int delta) { if (mTimeZones.count() > 1) { mActiveTimeZone = mTimeZones[(mTimeZones.indexOf(mActiveTimeZone) + ((delta > 0) ? -1 : 1) + mTimeZones.size()) % mTimeZones.size()]; setTimeText(); } } void LXQtWorldClock::activated(ActivationReason reason) { switch (reason) { case ILXQtPanelPlugin::Trigger: case ILXQtPanelPlugin::MiddleClick: break; default: return; } if (!mPopup) { mPopup = new LXQtWorldClockPopup(mContent); connect(mPopup, SIGNAL(deactivated()), SLOT(deletePopup())); if (reason == ILXQtPanelPlugin::Trigger) { mPopup->setObjectName(QLatin1String("WorldClockCalendar")); mPopup->layout()->setContentsMargins(0, 0, 0, 0); QCalendarWidget *calendarWidget = new QCalendarWidget(mPopup); mPopup->layout()->addWidget(calendarWidget); QString timeZoneName = mActiveTimeZone; if (timeZoneName == QLatin1String("local")) timeZoneName = QString::fromLatin1(QTimeZone::systemTimeZoneId()); QTimeZone timeZone(timeZoneName.toLatin1()); calendarWidget->setFirstDayOfWeek(QLocale(QLocale::AnyLanguage, timeZone.country()).firstDayOfWeek()); calendarWidget->setSelectedDate(QDateTime::currentDateTime().toTimeZone(timeZone).date()); } else { mPopup->setObjectName(QLatin1String("WorldClockPopup")); mPopupContent = new QLabel(mPopup); mPopup->layout()->addWidget(mPopupContent); mPopupContent->setAlignment(mContent->alignment()); updatePopupContent(); } mPopup->adjustSize(); mPopup->setGeometry(calculatePopupWindowPos(mPopup->size())); willShowWindow(mPopup); mPopup->show(); } else { deletePopup(); } } void LXQtWorldClock::deletePopup() { mPopupContent = NULL; mPopup->deleteLater(); mPopup = NULL; } QString LXQtWorldClock::formatDateTime(const QDateTime &datetime, const QString &timeZoneName) { QTimeZone timeZone(timeZoneName.toLatin1()); QDateTime tzNow = datetime.toTimeZone(timeZone); return tzNow.toString(preformat(mFormat, timeZone, tzNow)); } void LXQtWorldClock::updatePopupContent() { if (mPopupContent) { QDateTime now = QDateTime::currentDateTime(); QStringList allTimeZones; bool hasTimeZone = formatHasTimeZone(mFormat); foreach (QString timeZoneName, mTimeZones) { if (timeZoneName == QLatin1String("local")) timeZoneName = QString::fromLatin1(QTimeZone::systemTimeZoneId()); QString formatted = formatDateTime(now, timeZoneName); if (!hasTimeZone) formatted += QLatin1String("
") + QString::fromLatin1(QTimeZone(timeZoneName.toLatin1()).id()); allTimeZones.append(formatted); } mPopupContent->setText(allTimeZones.join(QLatin1String("
"))); } } bool LXQtWorldClock::formatHasTimeZone(QString format) { format.replace(QRegExp(QLatin1String("'[^']*'")), QString()); return format.toLower().contains(QLatin1String("t")); } QString LXQtWorldClock::preformat(const QString &format, const QTimeZone &timeZone, const QDateTime &dateTime) { QString result = format; int from = 0; for (;;) { int apos = result.indexOf(QLatin1Char('\''), from); int tz = result.indexOf(QLatin1Char('T'), from); if ((apos != -1) && (tz != -1)) { if (apos > tz) apos = -1; else tz = -1; } if (apos != -1) { from = apos + 1; apos = result.indexOf(QLatin1Char('\''), from); if (apos == -1) // misformat break; from = apos + 1; } else if (tz != -1) { int length = 1; for (; result[tz + length] == QLatin1Char('T'); ++length); if (length > 6) length = 6; QString replacement; switch (length) { case 1: replacement = timeZone.displayName(dateTime, QTimeZone::OffsetName); if (replacement.startsWith(QLatin1String("UTC"))) replacement = replacement.mid(3); break; case 2: replacement = QString::fromLatin1(timeZone.id()); break; case 3: replacement = timeZone.abbreviation(dateTime); break; case 4: replacement = timeZone.displayName(dateTime, QTimeZone::ShortName); break; case 5: replacement = timeZone.displayName(dateTime, QTimeZone::LongName); break; case 6: replacement = mTimeZoneCustomNames[QString::fromLatin1(timeZone.id())]; } if ((tz > 0) && (result[tz - 1] == QLatin1Char('\''))) { --tz; ++length; } else replacement.prepend(QLatin1Char('\'')); if (result[tz + length] == QLatin1Char('\'')) ++length; else replacement.append(QLatin1Char('\'')); result.replace(tz, length, replacement); from = tz + replacement.length(); } else break; } return result; } void LXQtWorldClock::realign() { if (mAutoRotate) switch (panel()->position()) { case ILXQtPanel::PositionTop: case ILXQtPanel::PositionBottom: mRotatedWidget->setOrigin(Qt::TopLeftCorner); break; case ILXQtPanel::PositionLeft: mRotatedWidget->setOrigin(Qt::BottomLeftCorner); break; case ILXQtPanel::PositionRight: mRotatedWidget->setOrigin(Qt::TopRightCorner); break; } else mRotatedWidget->setOrigin(Qt::TopLeftCorner); } ActiveLabel::ActiveLabel(QWidget *parent) : QLabel(parent) { } void ActiveLabel::wheelEvent(QWheelEvent *event) { emit wheelScrolled(event->delta()); QLabel::wheelEvent(event); } void ActiveLabel::mouseReleaseEvent(QMouseEvent* event) { switch (event->button()) { case Qt::LeftButton: emit leftMouseButtonClicked(); break; case Qt::MidButton: emit middleMouseButtonClicked(); break; default:; } QLabel::mouseReleaseEvent(event); } LXQtWorldClockPopup::LXQtWorldClockPopup(QWidget *parent) : QDialog(parent, Qt::Window | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::Popup | Qt::X11BypassWindowManagerHint) { setLayout(new QHBoxLayout(this)); layout()->setMargin(1); } void LXQtWorldClockPopup::show() { QDialog::show(); activateWindow(); } bool LXQtWorldClockPopup::event(QEvent *event) { if (event->type() == QEvent::Close) emit deactivated(); return QDialog::event(event); }