1 /***************************************************************************
2 * Copyright (C) 2007-2008 by Riccardo Iaconelli <riccardo@kde.org> *
3 * Copyright (C) 2007-2008 by Sebastian Kuegler <sebas@kde.org> *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
19 ***************************************************************************/
26 #include <QtGui/QPainter>
27 #include <QtGui/QStyleOptionGraphicsItem>
28 #include <QtGui/QSpinBox>
29 #include <QtCore/QTimeLine>
30 #include <QtGui/QGraphicsView>
31 #include <QtGui/QGraphicsSceneMouseEvent>
32 #include <QtCore/QDate>
37 #include <KSharedConfig>
38 #include <KTimeZoneWidget>
40 #include <KColorScheme>
41 #include <KGlobalSettings>
42 #include <KConfigDialog>
43 #include <KDatePicker>
44 #include <Plasma/Theme>
45 #include <Plasma/Dialog>
46 #include <Plasma/ToolTipManager>
49 Clock::Clock(QObject
*parent
, const QVariantList
&args
)
50 : ClockApplet(parent
, args
),
51 m_plainClockFont(KGlobalSettings::generalFont()),
52 m_useCustomColor(false),
58 m_showTimezone(false),
59 m_dateTimezoneBesides(false),
63 KGlobal::locale()->insertCatalog("libplasmaclock");
64 setHasConfigurationInterface(true);
76 KConfigGroup cg
= config();
78 m_showTimezone
= cg
.readEntry("showTimezone", !isLocalTimezone());
80 kDebug() << "showTimezone:" << m_showTimezone
;
82 m_showDate
= cg
.readEntry("showDate", false);
83 m_showYear
= cg
.readEntry("showYear", false);
84 m_showDay
= cg
.readEntry("showDay", true);
86 m_showSeconds
= cg
.readEntry("showSeconds", false);
87 m_plainClockFont
= cg
.readEntry("plainClockFont", m_plainClockFont
);
88 m_useCustomColor
= cg
.readEntry("useCustomColor", false);
89 if (m_useCustomColor
) {
90 m_plainClockColor
= cg
.readEntry("plainClockColor", m_plainClockColor
);
92 m_plainClockColor
= KColorScheme(QPalette::Active
, KColorScheme::View
, Plasma::Theme::defaultTheme()->colorScheme()).foreground().color();
95 QFontMetricsF
metrics(KGlobalSettings::smallestReadableFont());
96 QString timeString
= KGlobal::locale()->formatTime(QTime(23, 59), m_showSeconds
);
97 setMinimumSize(metrics
.size(Qt::TextSingleLine
, timeString
));
99 dataEngine("time")->connectSource(currentTimezone(), this, updateInterval(), intervalAlignment());
100 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(updateColors()));
103 void Clock::constraintsEvent(Plasma::Constraints constraints
)
105 ClockApplet::constraintsEvent(constraints
);
107 if (constraints
& Plasma::SizeConstraint
) {
112 void Clock::updateSize()
114 Plasma::FormFactor f
= formFactor();
116 if (f
!= Plasma::Vertical
&& f
!= Plasma::Horizontal
) {
117 QFontMetricsF
metrics(KGlobalSettings::smallestReadableFont());
118 // calculates based on size of "23:59"!
119 QString timeString
= KGlobal::locale()->formatTime(QTime(23, 59), m_showSeconds
);
120 setMinimumSize(metrics
.size(Qt::TextSingleLine
, timeString
));
123 // more magic numbers
130 if (m_showDate
|| showTimezone()) {
131 QFont
f(KGlobalSettings::smallestReadableFont());
132 QFontMetrics
metrics(f
);
133 // if there's enough vertical space, wrap the words
134 if (contentsRect().height() < f
.pointSize() * 6) {
135 QSize s
= metrics
.size(Qt::TextSingleLine
, m_dateString
);
136 w
= s
.width() + metrics
.width(" ");
138 //kDebug(96669) << "uS: singleline" << w;
140 QSize s
= metrics
.size(Qt::TextWordWrap
, m_dateString
);
143 //kDebug(96669) << "uS: wordwrap" << w;
146 if (!m_dateTimezoneBesides
) {
147 w
= qMax(w
, (int)(contentsRect().height() * aspect
));
148 h
= h
+(int)(contentsRect().width() / aspect
);
150 w
= w
+(int)(contentsRect().height() * aspect
);
151 h
= qMax(h
, (int)(contentsRect().width() / aspect
));
154 w
= (int)(contentsRect().height() * aspect
);
155 h
= (int)(contentsRect().width() / aspect
);
158 if (f
== Plasma::Horizontal
) {
159 // We have a fixed height, set some sensible width
162 //kDebug() << "DR" << m_dateRect.width() << "CR" << contentsRect().height() * aspect;
163 // kDebug(96669) << contentsRect();
165 // We have a fixed width, set some sensible height
169 // kDebug(96669) << "minZize: " << minimumSize();
172 bool Clock::showTimezone() const
174 return m_showTimezone
|| shouldDisplayTimezone();
177 void Clock::dataUpdated(const QString
&source
, const Plasma::DataEngine::Data
&data
)
180 m_time
= data
["Time"].toTime();
181 m_date
= data
["Date"].toDate();
183 if (Plasma::ToolTipManager::self()->isVisible(this)) {
187 // avoid unnecessary repaints
188 if ((m_showSeconds
&& m_time
.second() != m_lastTimeSeen
.second()) ||
189 m_time
.minute() != m_lastTimeSeen
.minute()) {
190 m_lastTimeSeen
= m_time
;
195 void Clock::createClockConfigurationInterface(KConfigDialog
*parent
)
197 //TODO: Make the size settable
198 QWidget
*widget
= new QWidget();
200 parent
->addPage(widget
, i18n("General"), icon());
202 ui
.showDate
->setChecked(m_showDate
);
203 ui
.showYear
->setChecked(m_showYear
);
204 ui
.showDay
->setChecked(m_showDay
);
205 ui
.secondsCheckbox
->setChecked(m_showSeconds
);
206 ui
.showTimeZone
->setChecked(m_showTimezone
);
207 ui
.plainClockFontBold
->setChecked(m_plainClockFont
.bold());
208 ui
.plainClockFontItalic
->setChecked(m_plainClockFont
.italic());
209 ui
.plainClockFont
->setCurrentFont(m_plainClockFont
);
210 ui
.useCustomColor
->setChecked(m_useCustomColor
);
211 ui
.plainClockColor
->setColor(m_plainClockColor
);
214 void Clock::clockConfigAccepted()
216 KConfigGroup cg
= config();
218 m_showTimezone
= ui
.showTimeZone
->isChecked();
219 cg
.writeEntry("showTimezone", m_showTimezone
);
221 m_plainClockFont
= ui
.plainClockFont
->currentFont();
222 //We need this to happen before we disconnect/reconnect sources to ensure
223 //that the update interval is set properly.
224 if (m_showSeconds
!= ui
.secondsCheckbox
->isChecked()) {
225 m_showSeconds
= !m_showSeconds
;
226 cg
.writeEntry("showSeconds", m_showSeconds
);
228 dataEngine("time")->disconnectSource(currentTimezone(), this);
229 dataEngine("time")->connectSource(currentTimezone(), this, updateInterval(), intervalAlignment());
232 m_showDate
= ui
.showDate
->checkState() == Qt::Checked
;
233 cg
.writeEntry("showDate", m_showDate
);
234 m_showYear
= ui
.showYear
->checkState() == Qt::Checked
;
235 cg
.writeEntry("showYear", m_showYear
);
236 m_showDay
= ui
.showDay
->checkState() == Qt::Checked
;
237 cg
.writeEntry("showDay", m_showDay
);
238 m_showSeconds
= ui
.secondsCheckbox
->checkState() == Qt::Checked
;
239 cg
.writeEntry("showSeconds", m_showSeconds
);
241 m_useCustomColor
= ui
.useCustomColor
->isChecked();
242 if (m_useCustomColor
) {
243 m_plainClockColor
= ui
.plainClockColor
->color();
245 m_plainClockColor
= KColorScheme(QPalette::Active
, KColorScheme::View
, Plasma::Theme::defaultTheme()->colorScheme()).foreground().color();
248 m_plainClockFont
.setBold(ui
.plainClockFontBold
->checkState() == Qt::Checked
);
249 m_plainClockFont
.setItalic(ui
.plainClockFontItalic
->checkState() == Qt::Checked
);
251 cg
.writeEntry("plainClockFont", m_plainClockFont
);
252 cg
.writeEntry("useCustomColor", m_useCustomColor
);
253 cg
.writeEntry("plainClockColor", m_plainClockColor
);
255 constraintsEvent(Plasma::SizeConstraint
);
257 emit
sizeHintChanged(Qt::PreferredSize
);
258 emit
configNeedsSaving();
261 void Clock::changeEngineTimezone(const QString
&oldTimezone
, const QString
&newTimezone
)
263 dataEngine("time")->disconnectSource(oldTimezone
, this);
264 dataEngine("time")->connectSource(newTimezone
, this, updateInterval(), intervalAlignment());
267 QRectF
Clock::normalLayout(int subtitleWidth
, int subtitleHeight
, const QRect
&contentsRect
)
269 Q_UNUSED(subtitleWidth
);
271 QRectF myRect
= QRectF(contentsRect
.left(),
272 contentsRect
.bottom() - subtitleHeight
,
273 contentsRect
.width(),
274 contentsRect
.bottom());
276 //p->fillRect(myRect, QBrush(QColor("green")));
278 // Now find out how much space is left for painting the time
279 m_timeRect
= QRect(contentsRect
.left(),
281 contentsRect
.width(),
282 contentsRect
.height() - subtitleHeight
);
287 QRectF
Clock::sideBySideLayout(int subtitleWidth
, int subtitleHeight
, const QRect
&contentsRect
)
289 QRectF myRect
= QRectF(contentsRect
.right()-subtitleWidth
,
290 (contentsRect
.bottom()-subtitleHeight
)/2,
293 // kDebug(96669) << "myRect: " << myRect;
294 // p->fillRect(myRect, QBrush(QColor("grey")));
296 // Now find out how much space is left for painting the time
297 m_timeRect
= QRect(contentsRect
.left(),
299 contentsRect
.right() - subtitleWidth
,
300 contentsRect
.bottom());
305 void Clock::paintInterface(QPainter
*p
, const QStyleOptionGraphicsItem
*option
, const QRect
&contentsRect
)
309 if (!m_time
.isValid() || !m_date
.isValid()) {
313 p
->setPen(QPen(m_plainClockColor
));
314 p
->setRenderHint(QPainter::SmoothPixmapTransform
);
315 p
->setRenderHint(QPainter::Antialiasing
);
317 /* ... helps debugging contentsRect and sizing ...
318 QColor c = QColor(Qt::blue);
321 p->drawRect(contentsRect);
324 // Paint the date, conditionally, and let us know afterwards how much
325 // space is left for painting the time on top of it.
327 QString timeString
= KGlobal::locale()->formatTime(m_time
, m_showSeconds
);
328 QFont smallFont
= KGlobalSettings::smallestReadableFont();
330 if (m_showDate
|| showTimezone()) {
333 KLocale
tmpLocale(*KGlobal::locale());
334 tmpLocale
.setDateFormat("%e"); // day number of the month
335 QString day
= tmpLocale
.formatDate(m_date
);
336 tmpLocale
.setDateFormat("%b"); // short form of the month
337 QString month
= tmpLocale
.formatDate(m_date
);
340 tmpLocale
.setDateFormat("%Y"); // whole year
341 QString year
= tmpLocale
.formatDate(m_date
);
342 dateString
= i18nc("@label Short date: "
343 "%1 day in the month, %2 short month name, %3 year",
344 "%1 %2 %3", day
, month
, year
);
346 dateString
= i18nc("@label Short date: "
347 "%1 day in the month, %2 short month name",
348 "%1 %2", day
, month
);
352 tmpLocale
.setDateFormat("%a"); // short weekday
353 QString weekday
= tmpLocale
.formatDate(m_date
);
354 dateString
= i18nc("@label Day of the week with date: "
355 "%1 short day name, %2 short date",
356 "%1, %2", weekday
, dateString
);
359 if (showTimezone()) {
360 QString currentTimezone
= prettyTimezone();
361 dateString
= i18nc("@label Date with currentTimezone: "
362 "%1 day of the week with date, %2 currentTimezone",
363 "%1 %2", dateString
, currentTimezone
);
365 } else if (showTimezone()) {
366 dateString
= prettyTimezone();
369 dateString
= dateString
.trimmed();
371 if (m_dateString
!= dateString
) {
372 // If this string has changed (for example due to changes in the config
373 // we have to reset the sizing of the applet
374 m_dateString
= dateString
;
379 // magic 10 is for very big spaces,
380 // where there's enough space to grow without harming time space
381 QFontMetrics
fm(smallFont
);
382 smallFont
.setPixelSize(qMax(contentsRect
.height()/10, fm
.ascent()));
383 // kDebug(96669) << "=========";
384 // kDebug(96669) << "contentsRect: " << contentsRect;
386 m_dateRect
= preparePainter(p
, contentsRect
, smallFont
, dateString
);
387 // kDebug(96669) << "m_dateRect: " << m_dateRect;
389 int subtitleHeight
= m_dateRect
.height();
390 int subtitleWidth
= m_dateRect
.width();
391 // kDebug(96669) << "subtitleWitdh: " << subtitleWitdh;
392 // kDebug(96669) << "subtitleHeight: " << subtitleHeight;
394 if (m_dateTimezoneBesides
) {
395 //kDebug(96669) << contentsRect.height() << subtitleHeight << smallFont.pixelSize();
396 if (contentsRect
.height() - subtitleHeight
>= smallFont
.pixelSize() || formFactor() != Plasma::Horizontal
) {
397 // to small to display the time on top of the date/timezone
398 // put them side by side
399 // kDebug(96669) << "switching to normal";
400 m_dateTimezoneBesides
= false;
401 dateRect
= normalLayout(subtitleWidth
, subtitleHeight
, contentsRect
);
403 dateRect
= sideBySideLayout(subtitleWidth
, subtitleHeight
, contentsRect
);
406 /* kDebug(96669) << "checking timezone placement"
407 << contentsRect.height() << dateRect.height() << subtitleHeight
408 << smallFont.pixelSize() << smallFont.pointSize();*/
409 if (contentsRect
.height() - subtitleHeight
< smallFont
.pixelSize() && formFactor() == Plasma::Horizontal
) {
410 // to small to display the time on top of the date/timezone
411 // put them side by side
412 // kDebug(96669) << "switching to s-b-s";
413 m_dateTimezoneBesides
= true;
414 dateRect
= sideBySideLayout(subtitleWidth
, subtitleHeight
, contentsRect
);
416 dateRect
= normalLayout(subtitleWidth
, subtitleHeight
, contentsRect
);
420 m_timeRect
= contentsRect
;
422 // kDebug(96669) << "timeRect: " << m_timeRect;
423 // p->fillRect(timeRect, QBrush(QColor("red")));
425 // kDebug(96669) << m_time;
426 // Choose a relatively big font size to start with
427 m_plainClockFont
.setPointSizeF(qMax(m_timeRect
.height(), KGlobalSettings::smallestReadableFont().pointSize()));
428 preparePainter(p
, m_timeRect
, m_plainClockFont
, timeString
, true);
430 if (!m_dateString
.isEmpty()) {
431 if (m_dateTimezoneBesides
) {
432 QFontMetrics
fm(m_plainClockFont
);
433 //kDebug() << dateRect << m_timeRect << fm.boundingRect(m_timeRect, Qt::AlignCenter, timeString);
434 QRect br
= fm
.boundingRect(m_timeRect
, Qt::AlignCenter
, timeString
);
436 QFontMetrics
smallfm(smallFont
);
437 dateRect
.moveLeft(br
.right() + qMin(0, br
.left()) + smallfm
.width(" "));
440 // When we're relatively low, force everything into a single line
442 p
->setFont(smallFont
);
444 if (formFactor() == Plasma::Horizontal
&& (contentsRect
.height() < smallFont
.pointSize()*6)) {
445 p
->drawText(dateRect
, Qt::TextSingleLine
| Qt::AlignHCenter
, m_dateString
);
447 p
->drawText(dateRect
, Qt::TextWordWrap
| Qt::AlignHCenter
, m_dateString
);
453 QTextOption
textOption(m_dateTimezoneBesides
? Qt::AlignCenter
| Qt::AlignVCenter
: Qt::AlignCenter
);
454 textOption
.setWrapMode(QTextOption::NoWrap
);
455 p
->drawText(m_timeRect
, timeString
, textOption
);
458 QRect
Clock::preparePainter(QPainter
*p
, const QRect
&rect
, const QFont
&font
, const QString
&text
, bool singleline
)
461 QFont tmpFont
= font
;
464 // Starting with the given font, decrease its size until it'll fit in the
465 // given rect allowing wrapping where possible
470 tmpFont
.setPointSize(qMax(KGlobalSettings::smallestReadableFont().pointSize(), tmpFont
.pointSize() - 1));
473 QFontMetrics
fm(tmpFont
);
474 int flags
= (singleline
|| ((formFactor() == Plasma::Horizontal
) &&
475 (contentsRect().height() < tmpFont
.pointSize()*6))) ?
476 Qt::TextSingleLine
: Qt::TextWordWrap
;
478 tmpRect
= fm
.boundingRect(rect
, flags
, text
);
479 } while (tmpFont
.pointSize() > KGlobalSettings::smallestReadableFont().pointSize() &&
480 (tmpRect
.width() > rect
.width() || tmpRect
.height() > rect
.height()));
486 int Clock::updateInterval() const
488 return m_showSeconds
? 1000 : 60000;
491 Plasma::IntervalAlignment
Clock::intervalAlignment() const
493 return m_showSeconds
? Plasma::NoAlignment
: Plasma::AlignToMinute
;
496 void Clock::updateColors()
498 if (!m_useCustomColor
) {
499 m_plainClockColor
= KColorScheme(QPalette::Active
, KColorScheme::View
, Plasma::Theme::defaultTheme()->colorScheme()).foreground().color();