add more spacing
[personal-kdebase.git] / workspace / plasma / applets / digital-clock / clock.cpp
bloba4c74a4d4facd0647917be7fec968b6106e8e339
1 /***************************************************************************
2 * Copyright (C) 2007-2008 by Riccardo Iaconelli <riccardo@kde.org> *
3 * Copyright (C) 2007-2008 by Sebastian Kuegler <sebas@kde.org> *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
22 #include "clock.h"
24 #include <math.h>
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>
34 #include <KDebug>
35 #include <KLocale>
36 #include <KIcon>
37 #include <KSharedConfig>
38 #include <KTimeZoneWidget>
39 #include <KDialog>
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),
53 m_plainClockColor(),
54 m_showDate(false),
55 m_showYear(false),
56 m_showDay(false),
57 m_showSeconds(false),
58 m_showTimezone(false),
59 m_dateTimezoneBesides(false),
60 m_dateString(0),
61 m_layout(0)
63 KGlobal::locale()->insertCatalog("libplasmaclock");
64 setHasConfigurationInterface(true);
65 resize(150, 75);
68 Clock::~Clock()
72 void Clock::init()
74 ClockApplet::init();
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);
91 } else {
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) {
108 updateSize();
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
124 int aspect = 2;
125 if (m_showSeconds) {
126 aspect = 3;
129 int w, h;
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(" ");
137 h = s.height();
138 //kDebug(96669) << "uS: singleline" << w;
139 } else {
140 QSize s = metrics.size(Qt::TextWordWrap, m_dateString);
141 w = s.width();
142 h = s.height();
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);
149 } else {
150 w = w+(int)(contentsRect().height() * aspect);
151 h = qMax(h, (int)(contentsRect().width() / aspect));
153 } else {
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
160 setMinimumWidth(w);
161 setMinimumHeight(0);
162 //kDebug() << "DR" << m_dateRect.width() << "CR" << contentsRect().height() * aspect;
163 // kDebug(96669) << contentsRect();
164 } else {
165 // We have a fixed width, set some sensible height
166 setMinimumHeight(h);
167 setMinimumWidth(0);
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)
179 Q_UNUSED(source);
180 m_time = data["Time"].toTime();
181 m_date = data["Date"].toDate();
183 if (Plasma::ToolTipManager::self()->isVisible(this)) {
184 updateTipContent();
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;
191 update();
195 void Clock::createClockConfigurationInterface(KConfigDialog *parent)
197 //TODO: Make the size settable
198 QWidget *widget = new QWidget();
199 ui.setupUi(widget);
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();
244 } else {
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);
256 update();
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(),
280 contentsRect.top(),
281 contentsRect.width(),
282 contentsRect.height() - subtitleHeight);
284 return myRect;
287 QRectF Clock::sideBySideLayout(int subtitleWidth, int subtitleHeight, const QRect &contentsRect)
289 QRectF myRect = QRectF(contentsRect.right()-subtitleWidth,
290 (contentsRect.bottom()-subtitleHeight)/2,
291 subtitleWidth,
292 subtitleHeight);
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(),
298 contentsRect.top(),
299 contentsRect.right() - subtitleWidth,
300 contentsRect.bottom());
302 return myRect;
305 void Clock::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
307 Q_UNUSED(option);
309 if (!m_time.isValid() || !m_date.isValid()) {
310 return;
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);
319 c.setAlphaF(.5);
320 p->setBrush(c);
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.
326 QRectF dateRect;
327 QString timeString = KGlobal::locale()->formatTime(m_time, m_showSeconds);
328 QFont smallFont = KGlobalSettings::smallestReadableFont();
330 if (m_showDate || showTimezone()) {
331 QString dateString;
332 if (m_showDate) {
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);
339 if (m_showYear) {
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);
345 } else {
346 dateString = i18nc("@label Short date: "
347 "%1 day in the month, %2 short month name",
348 "%1 %2", day, month);
351 if (m_showDay) {
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;
375 updateSize();
378 // Check sizes
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);
402 } else {
403 dateRect = sideBySideLayout(subtitleWidth, subtitleHeight, contentsRect);
405 } else {
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);
415 } else {
416 dateRect = normalLayout(subtitleWidth, subtitleHeight, contentsRect);
419 } else {
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
441 QFont f = p->font();
442 p->setFont(smallFont);
444 if (formFactor() == Plasma::Horizontal && (contentsRect.height() < smallFont.pointSize()*6)) {
445 p->drawText(dateRect, Qt::TextSingleLine | Qt::AlignHCenter, m_dateString);
446 } else {
447 p->drawText(dateRect, Qt::TextWordWrap | Qt::AlignHCenter, m_dateString);
450 p->setFont(f);
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)
460 QRect tmpRect;
461 QFont tmpFont = font;
462 bool first = true;
464 // Starting with the given font, decrease its size until it'll fit in the
465 // given rect allowing wrapping where possible
466 do {
467 if (first) {
468 first = false;
469 } else {
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()));
482 p->setFont(tmpFont);
483 return tmpRect;
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();
500 update();
503 #include "clock.moc"