diff --git a/applets/digital-clock/package/contents/ui/DigitalClock.qml b/applets/digital-clock/package/contents/ui/DigitalClock.qml --- a/applets/digital-clock/package/contents/ui/DigitalClock.qml +++ b/applets/digital-clock/package/contents/ui/DigitalClock.qml @@ -71,6 +71,14 @@ onShowDateChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } onUse24hFormatChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } + Connections { + target: plasmoid + onContextualActionsAboutToShow: { + ClipboardMenu.secondsIncluded = main.showSeconds; + ClipboardMenu.currentDate = main.currentTime; + } + } + Connections { target: plasmoid.configuration onSelectedTimeZonesChanged: { diff --git a/applets/digital-clock/package/contents/ui/main.qml b/applets/digital-clock/package/contents/ui/main.qml --- a/applets/digital-clock/package/contents/ui/main.qml +++ b/applets/digital-clock/package/contents/ui/main.qml @@ -99,6 +99,9 @@ } Component.onCompleted: { + plasmoid.setAction("clipboard", i18n("Copy to Clipboard"), "edit-copy"); + ClipboardMenu.setupMenu(plasmoid.action("clipboard")); + root.initTimezones(); if (KCMShell.authorize("clock.desktop").length > 0) { plasmoid.setAction("clockkcm", i18n("Adjust Date and Time..."), "preferences-system-time"); diff --git a/applets/digital-clock/plugin/CMakeLists.txt b/applets/digital-clock/plugin/CMakeLists.txt --- a/applets/digital-clock/plugin/CMakeLists.txt +++ b/applets/digital-clock/plugin/CMakeLists.txt @@ -11,11 +11,13 @@ timezonemodel.cpp timezonesi18n.cpp digitalclockplugin.cpp + clipboardmenu.cpp ) add_library(digitalclockplugin SHARED ${digitalclockplugin_SRCS}) target_link_libraries(digitalclockplugin Qt5::Core Qt5::Qml + Qt5::Widgets # for QAction... KF5::CoreAddons KF5::I18n) diff --git a/applets/digital-clock/plugin/clipboardmenu.h b/applets/digital-clock/plugin/clipboardmenu.h new file mode 100644 --- /dev/null +++ b/applets/digital-clock/plugin/clipboardmenu.h @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Kai Uwe Broulik + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef CLIPBOARDMENU_H +#define CLIPBOARDMENU_H + +#include +#include + +class QAction; + +class ClipboardMenu : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDateTime currentDate READ currentDate WRITE setCurrentDate NOTIFY currentDateChanged) + Q_PROPERTY(bool secondsIncluded READ secondsIncluded WRITE setSecondsIncluded NOTIFY secondsIncludedChanged) + +public: + explicit ClipboardMenu(QObject *parent = nullptr); + virtual ~ClipboardMenu(); + + QDateTime currentDate() const; + void setCurrentDate(const QDateTime &date); + + bool secondsIncluded() const; + void setSecondsIncluded(const bool &secondsIncluded); + + Q_INVOKABLE void setupMenu(QAction *action); + +signals: + void currentDateChanged(); + void secondsIncludedChanged(); + + +private: + QDateTime m_currentDate; + bool m_secondsIncluded = false; +}; + +#endif // CLIPBOARDMENU_H diff --git a/applets/digital-clock/plugin/clipboardmenu.cpp b/applets/digital-clock/plugin/clipboardmenu.cpp new file mode 100644 --- /dev/null +++ b/applets/digital-clock/plugin/clipboardmenu.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2016 Kai Uwe Broulik + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#include "clipboardmenu.h" +#include "klocalizedstring.h" + +#include +#include +#include + +ClipboardMenu::ClipboardMenu(QObject *parent) : QObject(parent) +{ + +} + +ClipboardMenu::~ClipboardMenu() = default; + +QDateTime ClipboardMenu::currentDate() const +{ + return m_currentDate; +} + +void ClipboardMenu::setCurrentDate(const QDateTime ¤tDate) +{ + if (m_currentDate != currentDate) { + m_currentDate = currentDate; + emit currentDateChanged(); + } +} + +bool ClipboardMenu::secondsIncluded() const +{ + return m_secondsIncluded; +} + +void ClipboardMenu::setSecondsIncluded(const bool &secondsIncluded) +{ + if (m_secondsIncluded != secondsIncluded) { + m_secondsIncluded = secondsIncluded; + emit secondsIncludedChanged(); + } +} + +void ClipboardMenu::setupMenu(QAction *action) +{ + QMenu *menu = new QMenu; + +/* + * The refresh rate of m_currentDate depends of what is shown in the plasmoid. + * If only minutes are shown, the value is updated at the full minute and + * seconds are always 0 or 59 and thus useless/confusing to offer for copy. + * Use a reference to the config's showSeconds to decide if seconds are sent + * to the clipboard. There was no workaround found ... + */ + connect(menu, &QMenu::aboutToShow, this, [this, menu] { + menu->clear(); + + const QDate date = m_currentDate.date(); + const QTime time = m_currentDate.time(); + const QRegExp rx ("[^0-9:]"); + const QChar ws = QLatin1Char(' '); + QString s; + QAction *a; + + s = date.toString(Qt::SystemLocaleShortDate); + a = menu->addAction(s); + a->setData(s); + s = date.toString(Qt::ISODate); + a = menu->addAction(s); + a->setData(s); + s = date.toString(Qt::SystemLocaleLongDate); + a = menu->addAction(s); + a->setData(s); + + menu->addSeparator(); + + s = time.toString(Qt::SystemLocaleShortDate); + a = menu->addAction(s); + a->setData(s); + if (m_secondsIncluded) { + s = time.toString(Qt::SystemLocaleLongDate); + s.replace(rx, QString()); + a = menu->addAction(s); + a->setData(s); + s = time.toString(Qt::SystemLocaleLongDate); + a = menu->addAction(s); + a->setData(s); + } + + menu->addSeparator(); + + s = date.toString(Qt::SystemLocaleShortDate) + ws + time.toString(Qt::SystemLocaleShortDate); + a = menu->addAction(s); + a->setData(s); + if (m_secondsIncluded) { + s = date.toString(Qt::SystemLocaleShortDate) + ws + time.toString(Qt::SystemLocaleLongDate).replace(rx, ""); + a = menu->addAction(s); + a->setData(s); + s = date.toString(Qt::SystemLocaleShortDate) + ws + time.toString(Qt::SystemLocaleLongDate); + a = menu->addAction(s); + a->setData(s); + } + s = date.toString(Qt::ISODate) + ws + time.toString(Qt::SystemLocaleShortDate); + a = menu->addAction(s); + a->setData(s); + if (m_secondsIncluded) { + s = date.toString(Qt::ISODate) + ws + time.toString(Qt::SystemLocaleLongDate).replace(rx, ""); + a = menu->addAction(s); + a->setData(s); + s = date.toString(Qt::ISODate) + ws + time.toString(Qt::SystemLocaleLongDate); + a = menu->addAction(s); + a->setData(s); + } + s = date.toString(Qt::SystemLocaleLongDate) + ws + time.toString(Qt::SystemLocaleShortDate); + a = menu->addAction(s); + a->setData(s); + + menu->addSeparator(); + + QMenu *otherCalendarsMenu = menu->addMenu(i18n("Other Calendars")); + +/* Add ICU Calendars if QLocale is ready for + Chinese, Coptic, Ethiopic, (Gregorian), Hebrew, Indian, Islamic, Persian + + otherCalendarsMenu->addSeparator(); +*/ + s = QString::number(m_currentDate.toMSecsSinceEpoch() / 1000); + a = otherCalendarsMenu->addAction(i18nc("unix timestamp (seconds since 1.1.1970)", "%1 (UNIX Time)", s)); + a->setData(s); + s = QString::number(qreal(2440587.5) + qreal(m_currentDate.toMSecsSinceEpoch()) / qreal(86400000), 'f', 5); + a = otherCalendarsMenu->addAction(i18nc("for astronomers (days and decimals since ~7000 years ago)", "%1 (Julian Date)", s)); + a->setData(s); + }); + + connect(menu, &QMenu::triggered, menu, [](QAction *action) { + qApp->clipboard()->setText(action->data().toString()); + qApp->clipboard()->setText(action->data().toString(), QClipboard::Selection); + }); + + // QMenu cannot have QAction as parent and setMenu doesn't take ownership + connect(action, &QObject::destroyed, menu, &QObject::deleteLater); + + action->setMenu(menu); +} diff --git a/applets/digital-clock/plugin/digitalclockplugin.cpp b/applets/digital-clock/plugin/digitalclockplugin.cpp --- a/applets/digital-clock/plugin/digitalclockplugin.cpp +++ b/applets/digital-clock/plugin/digitalclockplugin.cpp @@ -17,6 +17,7 @@ */ #include "digitalclockplugin.h" +#include "clipboardmenu.h" #include "timezonemodel.h" #include "timezonesi18n.h" @@ -30,11 +31,21 @@ return new TimezonesI18n(); } +static QObject *clipboardMenu_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + + return new ClipboardMenu(); +} + void DigitalClockPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.digitalclock")); qmlRegisterType(uri, 1, 0, "TimeZoneModel"); qmlRegisterType(uri, 1, 0, "TimeZoneFilterProxy"); qmlRegisterSingletonType(uri, 1, 0, "TimezonesI18n", timezonesi18n_singletontype_provider); + + qmlRegisterSingletonType(uri, 1, 0, "ClipboardMenu", clipboardMenu_singletontype_provider); }