1
/***************************************************************************
2
* Copyright (C) 2007-2008 by Riccardo Iaconelli <riccardo@kde.org> *
3
* Copyright (C) 2007-2008 by Sebastian Kuegler <sebas@kde.org> *
4
* Copyright (C) 2008-2009 by Davide Bettio <davide.bettio@kdemail.net> *
5
* Copyright (C) 2009 by John Layt <john@layt.net> *
7
* This program is free software; you can redistribute it and/or modify *
8
* it under the terms of the GNU General Public License as published by *
9
* the Free Software Foundation; either version 2 of the License, or *
10
* (at your option) any later version. *
12
* This program is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15
* GNU General Public License for more details. *
17
* You should have received a copy of the GNU General Public License *
18
* along with this program; if not, write to the *
19
* Free Software Foundation, Inc., *
20
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
21
***************************************************************************/
23
#include "clockapplet.h"
27
#include <QtGui/QPainter>
28
#include <QtGui/QStyleOptionGraphicsItem>
29
#include <QtGui/QSpinBox>
30
#include <QtGui/QClipboard>
31
#include <QtCore/QTimeLine>
32
#include <QtGui/QGraphicsProxyWidget>
33
#include <QtGui/QGraphicsSceneMouseEvent>
34
#include <QtGui/QGraphicsView>
35
#include <QtDBus/QDBusInterface>
36
#include <QtDBus/QDBusPendingCall>
37
#include <QtCore/QDate>
38
#include <QtCore/QTimer>
39
#include <QtDBus/QDBusConnectionInterface>
41
#include <KColorScheme>
42
#include <KConfigDialog>
43
#include <KConfigGroup>
44
#include <KDatePicker>
48
#include <KGlobalSettings>
51
#include <KPassivePopup>
53
#include <KServiceTypeTrader>
55
#include <KToolInvocation>
56
#include <KMessageBox>
58
#include <Plasma/Containment>
59
#include <Plasma/Corona>
60
#include <Plasma/DataEngine>
61
#include <Plasma/Dialog>
62
#include <Plasma/Extender>
63
#include <Plasma/ExtenderItem>
64
#include <Plasma/Theme>
65
#include <Plasma/Label>
69
#include "ui_timezonesConfig.h"
70
#include "ui_generalConfig.h"
72
class ClockApplet::Private
75
Private(ClockApplet *clockapplet)
77
timezone(ClockApplet::localTimezoneUntranslated()),
79
adjustSystemTimeAction(0),
86
Ui::timezonesConfig timezonesUi;
87
Ui::generalConfig generalUi;
89
QString defaultTimezone;
91
QStringList selectedTimezones;
93
QAction *adjustSystemTimeAction;
94
QString prettyTimezone;
96
Plasma::Calendar *calendarWidget;
99
bool forceTzDisplay : 1;
101
void addTzToTipText(QString &subText, const QString& tz)
103
Plasma::DataEngine::Data data = q->dataEngine("time")->query(tz);
105
subText.append("<tr><td align=\"right\">");
107
subText.append("UTC");
109
subText.append(data["Timezone City"].toString().replace('_', " "));
111
subText.append(":</td><td>");
113
subText.append(KGlobal::locale()->formatTime(data["Time"].toTime(), false).replace(' ', " "))
115
.append(q->calendar()->formatDate(data["Date"].toDate()).replace(' ', " "))
116
.append("</td></tr>");
119
void createCalendarExtender()
121
if (!q->extender()->hasItem("calendar")) {
122
Plasma::ExtenderItem *eItem = new Plasma::ExtenderItem(q->extender());
123
eItem->setName("calendar");
124
q->initExtenderItem(eItem);
130
QString tmpStr = "isHoliday" + QString(':') + calendarWidget->holidaysRegions().join(QString(','))
131
+ QString(':') + QDate::currentDate().toString(Qt::ISODate);
132
bool isHoliday = q->dataEngine("calendar")->query(tmpStr).value(tmpStr).toBool();
134
Plasma::ExtenderItem *todayExtender = q->extender()->item("today");
136
if (!todayExtender && isHoliday) {
137
Plasma::ExtenderItem *eItem = new Plasma::ExtenderItem(q->extender());
138
eItem->setName("today");
139
q->initExtenderItem(eItem);
140
} else if (todayExtender && !isHoliday) {
141
todayExtender->destroy();
145
void setPrettyTimezone()
147
QString timezonetranslated = i18n(timezone.toUtf8().data());
148
if (timezone == "UTC") {
149
prettyTimezone = timezonetranslated;
150
} else if (!q->isLocalTimezone()) {
151
QStringList tzParts = timezonetranslated.split('/', QString::SkipEmptyParts);
152
if (tzParts.count() == 1) {
153
prettyTimezone = timezonetranslated;
154
} else if (tzParts.count() > 0) {
155
prettyTimezone = tzParts.last();
157
prettyTimezone = timezonetranslated;
160
prettyTimezone = localTimezone();
163
prettyTimezone = prettyTimezone.replace('_', ' ');
167
ClockApplet::ClockApplet(QObject *parent, const QVariantList &args)
168
: Plasma::PopupApplet(parent, args),
171
setPopupIcon(QIcon());
172
setPassivePopup(true);
175
ClockApplet::~ClockApplet()
177
delete d->clipboardMenu;
181
void ClockApplet::speakTime(const QTime &time)
183
if (!d->announceInterval) {
187
if ((time.minute() % d->announceInterval) == 0) {
188
// If KTTSD not running, start it.
189
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kttsd")) {
191
if (KToolInvocation::startServiceByDesktopName("kttsd", QStringList(), &error)) {
192
KPassivePopup::message(i18n("Starting Jovie Text-to-Speech Service Failed"), error, static_cast<QWidget *>(0));
197
QDBusInterface ktts("org.kde.kttsd", "/KSpeech", "org.kde.KSpeech");
198
ktts.asyncCall("setApplicationName", "plasmaclock");
200
if (time.minute() == 0) {
201
if (KGlobal::locale()->use12Clock()) {
202
if (time.hour() < 12) {
203
text = i18ncp("Text sent to the text to speech service "
204
"when minutes==0 and it is AM",
205
"It is 1 o clock a m",
206
"It is %1 o clock a m",
209
text = i18ncp("Text sent to the text to speech service "
210
"when minutes==0 and it is PM",
211
"It is 1 o clock p m",
212
"It is %1 o clock p m",
216
text = i18ncp("Text sent to the text to speech service "
217
"when minutes==0 and it is the 24 hour clock",
223
if (KGlobal::locale()->use12Clock()) {
224
if (time.hour() < 12) {
225
text = i18nc("Text sent to the text to speech service for AM",
230
text = i18nc("Text sent to the text to speech service for PM",
236
text = i18nc("Text sent to the text to speech service for the 24 hour clock",
242
ktts.asyncCall("say", text, 0);
246
void ClockApplet::toolTipAboutToShow()
251
void ClockApplet::toolTipHidden()
253
Plasma::ToolTipManager::self()->clearContent(this);
256
void ClockApplet::updateTipContent()
258
Plasma::ToolTipContent tipData;
260
// the main text contains the current timezone's time and date
261
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
262
QString mainText = d->prettyTimezone + ' ';
263
mainText += KGlobal::locale()->formatTime(data["Time"].toTime(), false) + "<br>";
264
QDate tipDate = data["Date"].toDate();
265
mainText += calendar()->formatDate(tipDate);
266
tipData.setMainText(mainText);
268
QString subText("<table>");
269
if (!isLocalTimezone()) {
270
d->addTzToTipText(subText, localTimezone());
273
foreach (const QString &tz, getSelectedTimezones()) {
274
if (tz == currentTimezone()) {
278
d->addTzToTipText(subText, tz);
281
if (!subText.isEmpty()) {
282
subText.prepend("<table>");
283
subText.append("</table>");
286
if (d->calendarWidget->dateHasDetails(tipDate)) {
287
subText.append("<br>").append(d->calendarWidget->dateDetails(tipDate).join("<br>"));
290
tipData.setSubText(subText);
292
// query for custom content
293
Plasma::ToolTipContent customContent = toolTipContent();
294
if (customContent.image().isNull()) {
295
tipData.setImage(KIcon(icon()).pixmap(IconSize(KIconLoader::Desktop)));
297
tipData.setImage(customContent.image());
300
if (!customContent.mainText().isEmpty()) {
301
// add their main text
302
tipData.setMainText(customContent.mainText() + "<br>" + tipData.mainText());
305
if (!customContent.subText().isEmpty()) {
306
// add their sub text
307
tipData.setSubText(customContent.subText() + "<br>" + tipData.subText());
310
tipData.setAutohide(false);
311
Plasma::ToolTipManager::self()->setContent(this, tipData);
314
void ClockApplet::updateClockApplet()
316
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
317
updateClockApplet(data);
320
void ClockApplet::updateClockApplet(const Plasma::DataEngine::Data &data)
322
bool updateSelectedDate = (d->calendarWidget->currentDate() == d->calendarWidget->date());
323
d->calendarWidget->setCurrentDate(data["Date"].toDate());
325
if (updateSelectedDate){
326
d->calendarWidget->setDate(d->calendarWidget->currentDate());
329
const QTime t = d->lastTimeSeen;
330
d->lastTimeSeen = data["Time"].toTime();
331
if (d->lastTimeSeen.minute() != t.minute() || d->lastTimeSeen.hour() != t.hour()) {
332
speakTime(d->lastTimeSeen);
336
QTime ClockApplet::lastTimeSeen() const
338
return d->lastTimeSeen;
341
void ClockApplet::resetLastTimeSeen()
343
d->lastTimeSeen = QTime();
346
Plasma::ToolTipContent ClockApplet::toolTipContent()
348
return Plasma::ToolTipContent();
351
void ClockApplet::createConfigurationInterface(KConfigDialog *parent)
353
createClockConfigurationInterface(parent);
355
QWidget *generalWidget = new QWidget();
356
d->generalUi.setupUi(generalWidget);
357
parent->addPage(generalWidget, i18nc("General configuration page", "General"), Applet::icon());
358
d->generalUi.interval->setValue(d->announceInterval);
360
d->calendarWidget->createConfigurationInterface(parent);
362
QWidget *widget = new QWidget();
363
d->timezonesUi.setupUi(widget);
364
d->timezonesUi.searchLine->addTreeWidget(d->timezonesUi.timeZones);
366
parent->addPage(widget, i18n("Time Zones"), "preferences-desktop-locale");
368
foreach (const QString &tz, d->selectedTimezones) {
369
d->timezonesUi.timeZones->setSelected(tz, true);
372
updateClockDefaultsTo();
373
int defaultSelection = d->timezonesUi.clockDefaultsTo->findData(d->defaultTimezone);
374
if (defaultSelection < 0) {
375
defaultSelection = 0; //if it's something unexpected default to local
376
//kDebug() << d->defaultTimezone << "not in list!?";
378
d->timezonesUi.clockDefaultsTo->setCurrentIndex(defaultSelection);
380
connect(d->generalUi.interval, SIGNAL(valueChanged(int)), parent, SLOT(settingsModified()));
381
connect(d->timezonesUi.timeZones, SIGNAL(itemChanged(QTreeWidgetItem*,int)), parent, SLOT(settingsModified()));
382
connect(d->timezonesUi.timeZones, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(updateClockDefaultsTo()));
383
connect(d->timezonesUi.clockDefaultsTo,SIGNAL(activated(const QString &)), parent, SLOT(settingsModified()));
386
void ClockApplet::createClockConfigurationInterface(KConfigDialog *parent)
391
void ClockApplet::clockConfigChanged()
396
void ClockApplet::configChanged()
398
if (isUserConfiguring()) {
402
KConfigGroup cg = config();
403
d->selectedTimezones = cg.readEntry("timeZones", QStringList() << "UTC");
404
d->timezone = cg.readEntry("timezone", d->timezone);
405
d->defaultTimezone = cg.readEntry("defaultTimezone", d->timezone);
406
d->forceTzDisplay = d->timezone != d->defaultTimezone;
407
d->setPrettyTimezone();
408
d->announceInterval = cg.readEntry("announceInterval", 0);
410
clockConfigChanged();
412
if (isUserConfiguring()) {
413
constraintsEvent(Plasma::SizeConstraint);
416
// d->calendarWidget->configAccepted(cg); is called in configAccepted(),
417
// as is setCurrentTimezone
418
// so we only need to do this in the case where the user hasn't been
419
// configuring things
420
d->calendarWidget->applyConfiguration(cg);
421
Plasma::DataEngine::Data data = dataEngine("time")->query(d->timezone);
422
d->calendarWidget->setDate(data["Date"].toDate());
426
void ClockApplet::clockConfigAccepted()
431
void ClockApplet::configAccepted()
433
KConfigGroup cg = config();
435
cg.writeEntry("timeZones", d->timezonesUi.timeZones->selection());
437
if (d->timezonesUi.clockDefaultsTo->currentIndex() == 0) {
438
//The first position in timezonesUi.clockDefaultsTo is "Local"
439
d->defaultTimezone = localTimezoneUntranslated();
441
d->defaultTimezone = d->timezonesUi.clockDefaultsTo->itemData(d->timezonesUi.clockDefaultsTo->currentIndex()).toString();
444
cg.writeEntry("defaultTimezone", d->defaultTimezone);
445
QString cur = currentTimezone();
446
setCurrentTimezone(d->defaultTimezone);
447
changeEngineTimezone(cur, d->defaultTimezone);
449
d->calendarWidget->configAccepted(cg);
451
cg.writeEntry("announceInterval", d->generalUi.interval->value());
453
clockConfigAccepted();
455
emit configNeedsSaving();
458
void ClockApplet::updateClockDefaultsTo()
460
QString oldSelection = d->timezonesUi.clockDefaultsTo->currentText();
461
d->timezonesUi.clockDefaultsTo->clear();
462
d->timezonesUi.clockDefaultsTo->addItem(localTimezone(), localTimezone());
463
foreach(const QString &tz, d->timezonesUi.timeZones->selection()) {
464
d->timezonesUi.clockDefaultsTo->addItem(KTimeZoneWidget::displayName(KTimeZone(tz)), tz);
466
int newPosition = d->timezonesUi.clockDefaultsTo->findText(oldSelection);
467
if (newPosition >= 0) {
468
d->timezonesUi.clockDefaultsTo->setCurrentIndex(newPosition);
470
if (d->timezonesUi.clockDefaultsTo->count() > 1) {
471
d->timezonesUi.clockDefaultsTo->setEnabled(true);
473
// Only "Local" in timezonesUi.clockDefaultsTo
474
d->timezonesUi.clockDefaultsTo->setEnabled(false);
478
void ClockApplet::changeEngineTimezone(const QString &oldTimezone, const QString &newTimezone)
480
// reimplemented by subclasses to get the new data
481
Q_UNUSED(oldTimezone);
482
Q_UNUSED(newTimezone);
485
bool ClockApplet::shouldDisplayTimezone() const
487
return d->forceTzDisplay;
490
QList<QAction *> ClockApplet::contextualActions()
492
if (!d->clipboardMenu) {
493
d->clipboardMenu = new KMenu(i18n("C&opy to Clipboard"));
494
d->clipboardMenu->setIcon(KIcon("edit-copy"));
495
connect(d->clipboardMenu, SIGNAL(aboutToShow()), this, SLOT(updateClipboardMenu()));
496
connect(d->clipboardMenu, SIGNAL(triggered(QAction*)), this, SLOT(copyToClipboard(QAction*)));
498
KService::List offers = KServiceTypeTrader::self()->query("KCModule", "Library == 'kcm_clock'");
499
if (!offers.isEmpty() && hasAuthorization("LaunchApp")) {
500
d->adjustSystemTimeAction = new QAction(this);
501
d->adjustSystemTimeAction->setText(i18n("Adjust Date and Time..."));
502
d->adjustSystemTimeAction->setIcon(KIcon(icon()));
503
connect(d->adjustSystemTimeAction, SIGNAL(triggered()), this, SLOT(launchTimeControlPanel()));
507
QList<QAction*> contextualActions;
508
contextualActions << d->clipboardMenu->menuAction();
510
if (d->adjustSystemTimeAction) {
511
contextualActions << d->adjustSystemTimeAction;
513
return contextualActions;
516
void ClockApplet::launchTimeControlPanel()
518
KService::List offers = KServiceTypeTrader::self()->query("KCModule", "Library == 'kcm_clock'");
519
if (offers.isEmpty()) {
525
KService::Ptr service = offers.first();
526
KRun::run(*service, urls, 0);
529
void ClockApplet::wheelEvent(QGraphicsSceneWheelEvent *event)
531
if (d->selectedTimezones.count() < 1) {
537
if (isLocalTimezone()) {
538
if (event->delta() > 0) {
539
newTimezone = d->selectedTimezones.last();
541
newTimezone = d->selectedTimezones.first();
544
int current = d->selectedTimezones.indexOf(currentTimezone());
546
if (event->delta() > 0) {
547
int previous = current - 1;
549
newTimezone = localTimezoneUntranslated();
551
newTimezone = d->selectedTimezones.at(previous);
554
int next = current + 1;
555
if (next > d->selectedTimezones.count() - 1) {
556
newTimezone = localTimezoneUntranslated();
558
newTimezone = d->selectedTimezones.at(next);
563
QString cur = currentTimezone();
564
setCurrentTimezone(newTimezone);
565
changeEngineTimezone(cur, newTimezone);
569
void ClockApplet::initExtenderItem(Plasma::ExtenderItem *item)
571
if (item->name() == "calendar") {
572
item->setTitle(i18n("Calendar"));
573
item->setIcon("view-pim-calendar");
574
item->setWidget(d->calendarWidget);
575
} else if (item->name() == "today") {
576
item->setTitle(i18n("Today"));
577
item->setIcon("view-pim-calendar");
578
d->label = new Plasma::Label();
579
item->setWidget(d->label);
583
void ClockApplet::init()
585
Plasma::ToolTipManager::self()->registerWidget(this);
587
d->calendarWidget = new Plasma::Calendar();
588
d->calendarWidget->setAutomaticUpdateEnabled(false);
589
d->calendarWidget->setMinimumSize(QSize(230, 220));
590
d->createCalendarExtender();
595
QTimer::singleShot(0, this, SLOT(createToday()));
598
void ClockApplet::focusInEvent(QFocusEvent* event)
601
d->calendarWidget->setFlag(QGraphicsItem::ItemIsFocusable);
602
d->calendarWidget->setFocus();
605
void ClockApplet::popupEvent(bool show)
611
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
612
d->calendarWidget->setDate(data["Date"].toDate());
615
void ClockApplet::constraintsEvent(Plasma::Constraints constraints)
617
Q_UNUSED(constraints)
620
void ClockApplet::setCurrentTimezone(const QString &tz)
622
if (d->timezone == tz) {
626
if (tz == localTimezone()) {
627
// catch people accidentally passing in the translation of "Local"
628
d->timezone = localTimezoneUntranslated();
633
d->forceTzDisplay = d->timezone != d->defaultTimezone;
634
d->setPrettyTimezone();
636
Plasma::DataEngine::Data data = dataEngine("time")->query(d->timezone);
637
d->calendarWidget->setDate(data["Date"].toDate());
639
KConfigGroup cg = config();
640
cg.writeEntry("timezone", d->timezone);
641
emit configNeedsSaving();
644
QString ClockApplet::currentTimezone() const
649
QString ClockApplet::prettyTimezone() const
651
return d->prettyTimezone;
654
QStringList ClockApplet::getSelectedTimezones() const
656
return d->selectedTimezones;
659
bool ClockApplet::isLocalTimezone() const
661
return d->timezone == localTimezoneUntranslated();
664
QString ClockApplet::localTimezone()
666
return i18nc("Local time zone", "Local");
669
QString ClockApplet::localTimezoneUntranslated()
674
void ClockApplet::updateClipboardMenu()
676
d->clipboardMenu->clear();
677
QList<QAction*> actions;
678
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
679
QDateTime dateTime = QDateTime(data["Date"].toDate(), data["Time"].toTime());
681
d->clipboardMenu->addAction(calendar()->formatDate(dateTime.date(), KLocale::LongDate));
682
d->clipboardMenu->addAction(calendar()->formatDate(dateTime.date(), KLocale::ShortDate));
683
// Display ISO Date format if not already displayed
684
if (KGlobal::locale()->dateFormatShort() != "%Y-%m-%d") {
685
d->clipboardMenu->addAction(calendar()->formatDate(dateTime.date(), "%Y-%m-%d"));
688
QAction *sep0 = new QAction(this);
689
sep0->setSeparator(true);
690
d->clipboardMenu->addAction(sep0);
692
d->clipboardMenu->addAction(KGlobal::locale()->formatTime(dateTime.time(), false));
693
d->clipboardMenu->addAction(KGlobal::locale()->formatTime(dateTime.time(), true));
695
QAction *sep1 = new QAction(this);
696
sep1->setSeparator(true);
697
d->clipboardMenu->addAction(sep1);
699
KLocale tempLocale(*KGlobal::locale());
700
tempLocale.setCalendar(calendar()->calendarType());
701
d->clipboardMenu->addAction(tempLocale.formatDateTime(dateTime, KLocale::LongDate));
702
d->clipboardMenu->addAction(tempLocale.formatDateTime(dateTime, KLocale::LongDate, true));
703
d->clipboardMenu->addAction(tempLocale.formatDateTime(dateTime, KLocale::ShortDate));
704
d->clipboardMenu->addAction(tempLocale.formatDateTime(dateTime, KLocale::ShortDate, true));
705
// Display ISO DateTime format if not already displayed
706
if (tempLocale.dateFormatShort() != "%Y-%m-%d") {
707
tempLocale.setDateFormatShort("%Y-%m-%d");
708
d->clipboardMenu->addAction(tempLocale.formatDateTime(dateTime, KLocale::ShortDate, true));
711
QAction *sep2 = new QAction(this);
712
sep2->setSeparator(true);
713
d->clipboardMenu->addAction(sep2);
715
QMenu *calendarMenu = d->clipboardMenu->addMenu( i18nc( "@item:inmenu Submenu for alternative calendar dates", "Other Calendars" ) );
716
QStringList calendars = KCalendarSystem::calendarSystems();
717
foreach ( const QString &cal, calendars ) {
718
if (cal != calendar()->calendarType()) {
719
KCalendarSystem *tempCal = KCalendarSystem::create(cal);
720
QString text = tempCal->formatDate(dateTime.date(), KLocale::LongDate) + " (" + KCalendarSystem::calendarLabel(cal) + ')';
721
calendarMenu->addAction(text);
722
text = tempCal->formatDate(dateTime.date(), KLocale::ShortDate) + " (" + KCalendarSystem::calendarLabel(cal) + ')';
723
calendarMenu->addAction(text);
729
void ClockApplet::copyToClipboard(QAction* action)
731
QString text = action->text();
732
text.remove(QChar('&'));
734
QApplication::clipboard()->setText(text);
737
const KCalendarSystem *ClockApplet::calendar() const
739
return d->calendarWidget->calendar();
742
#include "clockapplet.moc"