3 // kclock - Clock screen saver for KDE
4 // Copyright (c) 2003 Melchior FRANZ
7 // Author: Melchior FRANZ <mfranz@kde.org>
8 // Dependencies: libart_lgpl_2 http://www.levien.com/libart/
13 #include <qcheckbox.h>
15 #include <qdatetime.h>
16 #include <q3groupbox.h>
24 #include <QGridLayout>
25 #include <QHBoxLayout>
26 #include <QVBoxLayout>
28 #include <kapplication.h>
29 #include <kcolorbutton.h>
33 #include <kmessagebox.h>
35 #include <libart_lgpl/art_affine.h>
36 #include <libart_lgpl/art_misc.h>
37 #include <libart_lgpl/art_rgb.h>
38 #include <libart_lgpl/art_rgb_svp.h>
39 #include <libart_lgpl/art_svp_vpath.h>
40 #include <libart_lgpl/art_svp_vpath_stroke.h>
41 #include <libart_lgpl/art_vpath.h>
47 const int COLOR_BUTTON_WIDTH
= 80;
48 const int TIMER_INTERVALL
= 100;
49 const int MAX_CLOCK_SIZE
= 10;
50 const int DEFAULT_CLOCK_SIZE
= 3;
51 const bool DEFAULT_KEEP_CENTERED
= false;
57 KDE_EXPORT
const char *kss_applicationName
= "kclock.kss";
58 KDE_EXPORT
const char *kss_description
= I18N_NOOP("Clock");
59 KDE_EXPORT
const char *kss_version
= "1.0";
61 KDE_EXPORT KScreenSaver
*kss_create(WId id
) {
62 return new KClockSaver(id
);
65 KDE_EXPORT QDialog
*kss_setup() {
66 return new KClockSetup();
73 //-----------------------------------------------------------------------------
75 KClockSetup::KClockSetup(QWidget
*parent
, const char *name
)
76 : KDialogBase(parent
, name
, true, i18n( "Setup Clock Screen Saver" ),
77 Ok
|Cancel
|Help
, Ok
, true),
82 setButtonText( Help
, i18n( "A&bout" ) );
83 QWidget
*main
= makeMainWidget();
85 QVBoxLayout
*top
= new QVBoxLayout(main
, 0, spacingHint());
87 QHBoxLayout
*hbox
= new QHBoxLayout
;
88 top
->addLayout( hbox
);
90 Q3GroupBox
*colgroup
= new Q3GroupBox(i18n("Colors"), main
);
91 colgroup
->setColumnLayout( 0, Horizontal
);
92 QGridLayout
*grid
= new QGridLayout( colgroup
->layout(),
93 5, 2, spacingHint() );
95 QLabel
*label
= new QLabel(i18n("&Hour-hand:"), colgroup
);
96 KColorButton
*colorButton
= new KColorButton(m_hourColor
, colgroup
);
97 colorButton
->setFixedWidth(COLOR_BUTTON_WIDTH
);
98 label
->setBuddy(colorButton
);
99 connect(colorButton
, SIGNAL(changed(const QColor
&)),
100 SLOT(slotHourColor(const QColor
&)));
101 grid
->addWidget( label
, 1, 1 );
102 grid
->addWidget( colorButton
, 1, 2 );
104 label
= new QLabel(i18n("&Minute-hand:"), colgroup
);
105 colorButton
= new KColorButton(m_minColor
, colgroup
);
106 colorButton
->setFixedWidth(COLOR_BUTTON_WIDTH
);
107 label
->setBuddy(colorButton
);
108 connect(colorButton
, SIGNAL(changed(const QColor
&)),
109 SLOT(slotMinColor(const QColor
&)));
110 grid
->addWidget( label
, 2, 1 );
111 grid
->addWidget( colorButton
, 2, 2 );
113 label
= new QLabel(i18n("&Second-hand:"), colgroup
);
114 colorButton
= new KColorButton(m_secColor
, colgroup
);
115 colorButton
->setFixedWidth(COLOR_BUTTON_WIDTH
);
116 label
->setBuddy(colorButton
);
117 connect(colorButton
, SIGNAL(changed(const QColor
&)),
118 SLOT(slotSecColor(const QColor
&)));
119 grid
->addWidget( label
, 3, 1 );
120 grid
->addWidget( colorButton
, 3, 2 );
122 label
= new QLabel(i18n("Scal&e:"), colgroup
);
123 colorButton
= new KColorButton(m_scaleColor
, colgroup
);
124 colorButton
->setFixedWidth(COLOR_BUTTON_WIDTH
);
125 label
->setBuddy(colorButton
);
126 connect(colorButton
, SIGNAL(changed(const QColor
&)),
127 SLOT(slotScaleColor(const QColor
&)));
128 grid
->addWidget( label
, 4, 1 );
129 grid
->addWidget( colorButton
, 4, 2 );
131 label
= new QLabel(i18n("&Background:"), colgroup
);
132 colorButton
= new KColorButton(m_bgndColor
, colgroup
);
133 colorButton
->setFixedWidth(COLOR_BUTTON_WIDTH
);
134 label
->setBuddy(colorButton
);
135 connect(colorButton
, SIGNAL(changed(const QColor
&)),
136 SLOT(slotBgndColor(const QColor
&)));
137 grid
->addWidget( label
, 5, 1 );
138 grid
->addWidget( colorButton
, 5, 2 );
140 hbox
->addWidget(colgroup
);
142 QWidget
*m_preview
= new QWidget(main
);
143 m_preview
->setFixedSize(220, 165);
145 m_saver
= new KClockSaver(m_preview
->winId());
146 hbox
->addWidget(m_preview
);
148 label
= new QLabel( i18n( "Si&ze:" ), main
);
149 top
->addWidget( label
);
150 QSlider
*qs
= new QSlider(0, MAX_CLOCK_SIZE
, 1, m_size
, Horizontal
, main
);
151 label
->setBuddy( qs
);
152 qs
->setTickInterval(1);
153 qs
->setTickmarks(QSlider::Below
);
154 connect(qs
, SIGNAL(valueChanged(int)), this, SLOT(slotSliderMoved(int)));
155 top
->addWidget( qs
);
157 bool rtl
= kapp
->reverseLayout();
158 Q3HBox
*qsscale
= new Q3HBox(main
);
159 label
= new QLabel(i18n("Small"), qsscale
);
160 label
->setAlignment(rtl
? AlignRight
: AlignLeft
);
161 label
= new QLabel(i18n("Medium"), qsscale
);
162 label
->setAlignment(AlignHCenter
);
163 label
= new QLabel(i18n("Big"), qsscale
);
164 label
->setAlignment(rtl
? AlignLeft
: AlignRight
);
165 top
->addWidget(qsscale
);
167 QCheckBox
*keepCentered
= new QCheckBox(i18n("&Keep clock centered"), main
);
168 keepCentered
->setChecked(m_keepCentered
);
169 connect(keepCentered
, SIGNAL(stateChanged(int)), SLOT(slotKeepCenteredChanged(int)));
170 top
->addWidget(keepCentered
);
174 KClockSetup::~KClockSetup()
179 void KClockSetup::readSettings()
181 KConfig
*config
= KGlobal::config();
183 config
->setGroup("Settings");
184 m_keepCentered
= config
->readBoolEntry("KeepCentered", DEFAULT_KEEP_CENTERED
);
185 m_size
= config
->readUnsignedNumEntry("Size", DEFAULT_CLOCK_SIZE
);
186 if (m_size
> MAX_CLOCK_SIZE
)
187 m_size
= MAX_CLOCK_SIZE
;
189 config
->setGroup("Colors");
190 QColor c
= Qt::black
;
191 m_bgndColor
= config
->readColorEntry("Background", &c
);
194 m_scaleColor
= config
->readColorEntry("Scale", &c
);
195 m_hourColor
= config
->readColorEntry("HourHand", &c
);
196 m_minColor
= config
->readColorEntry("MinuteHand", &c
);
199 m_secColor
= config
->readColorEntry("SecondHand", &c
);
202 m_saver
->setBgndColor(m_bgndColor
);
203 m_saver
->setScaleColor(m_scaleColor
);
204 m_saver
->setHourColor(m_hourColor
);
205 m_saver
->setMinColor(m_minColor
);
206 m_saver
->setSecColor(m_secColor
);
211 void KClockSetup::slotOk()
213 KConfig
*config
= KGlobal::config();
214 config
->setGroup("Settings");
215 config
->writeEntry("Size", m_size
);
216 config
->writeEntry("KeepCentered", m_keepCentered
);
218 config
->setGroup("Colors");
219 config
->writeEntry("Background", m_bgndColor
);
220 config
->writeEntry("Scale", m_scaleColor
);
221 config
->writeEntry("HourHand", m_hourColor
);
222 config
->writeEntry("MinuteHand", m_minColor
);
223 config
->writeEntry("SecondHand", m_secColor
);
229 void KClockSetup::slotHelp()
231 KMessageBox::about(this, "<qt>" + i18n(
232 "Clock Screen Saver<br>"
234 "<nobr>Melchior FRANZ (c) 2003</nobr>") +
235 "<br><a href=\"mailto:mfranz@kde.org\">mfranz@kde.org</a>"
236 "</qt>", QString::null
, KMessageBox::AllowLink
);
240 void KClockSetup::slotBgndColor(const QColor
&color
)
244 m_saver
->setBgndColor(m_bgndColor
);
248 void KClockSetup::slotScaleColor(const QColor
&color
)
250 m_scaleColor
= color
;
252 m_saver
->setScaleColor(m_scaleColor
);
256 void KClockSetup::slotHourColor(const QColor
&color
)
260 m_saver
->setHourColor(m_hourColor
);
264 void KClockSetup::slotMinColor(const QColor
&color
)
268 m_saver
->setMinColor(m_minColor
);
272 void KClockSetup::slotSecColor(const QColor
&color
)
276 m_saver
->setSecColor(m_secColor
);
280 void KClockSetup::slotSliderMoved(int v
)
283 m_saver
->restart(m_size
= v
);
287 void KClockSetup::slotKeepCenteredChanged(int c
)
290 m_saver
->setKeepCentered(m_keepCentered
= c
);
297 //-----------------------------------------------------------------------------
299 KClockPainter::KClockPainter(int width
, int height
)
303 m_buf
= new Q_UINT8
[m_width
* m_height
* 3];
304 // build Cartesian coordinate system ranging from -1000 to +1000;
305 // points with positive x and y are in the top right quarter
306 m_matrix
[0] = m_width
/ 2000.0;
309 m_matrix
[3] = m_height
/ -2000.0;
310 m_matrix
[4] = m_width
/ 2.0;
311 m_matrix
[5] = m_height
/ 2.0;
315 KClockPainter::~KClockPainter()
321 void KClockPainter::copy(KClockPainter
*p
)
323 memcpy(m_buf
, p
->image(), m_width
* m_height
* 3);
327 void KClockPainter::drawToImage(QImage
*q
, int xoffs
= 0, int yoffs
= 0)
329 unsigned char *src
= (unsigned char *)image();
330 for (int y
= 0; y
< m_height
; y
++) {
331 QRgb
*dest
= reinterpret_cast<QRgb
*>(q
->scanLine(y
+ yoffs
)) + xoffs
;
332 for (int x
= 0; x
< m_width
; x
++, src
+= 3)
333 *dest
++ = qRgba(src
[0], src
[1], src
[2], 255);
338 void KClockPainter::setColor(const QColor
&c
)
340 m_color
= (c
.red() << 24) | (c
.green() << 16) | (c
.blue() << 8) | 255;
344 void KClockPainter::setShadowColor(const QColor
&c
)
346 m_shadow
= (c
.red() << 24) | (c
.green() << 16) | (c
.blue() << 8) | 255;
350 void KClockPainter::fill(const QColor
&c
)
352 art_rgb_fill_run(m_buf
, c
.red(), c
.green(), c
.blue(), m_width
* m_height
);
356 void KClockPainter::drawRadial(double alpha
, double r0
, double r1
, double width
)
358 ArtVpath
*vx
, *v
= art_new(ArtVpath
, 3);
359 v
[0].code
= ART_MOVETO_OPEN
;
362 v
[1].code
= ART_LINETO
;
367 double m
[6] = {0, 0, 0, 0, 0, 0};
368 art_affine_rotate(m
, 90 - alpha
);
369 vx
= art_vpath_affine_transform(v
, m
);
371 v
= art_vpath_affine_transform(vx
, m_matrix
);
374 double w
= width
* m_matrix
[0];
378 ArtSVP
*path
= art_svp_vpath_stroke(v
, ART_PATH_STROKE_JOIN_MITER
,
379 ART_PATH_STROKE_CAP_BUTT
, w
, 4, .5);
380 art_rgb_svp_alpha(path
, 0, 0, m_width
, m_height
, m_color
, m_buf
, 3 * m_width
, 0);
386 void KClockPainter::drawDisc(double r
)
388 ArtVpath
*v
, *vx
= art_vpath_new_circle(0, 0, r
);
389 v
= art_vpath_affine_transform(vx
, m_matrix
);
392 ArtSVP
*path
= art_svp_from_vpath(v
);
393 art_rgb_svp_alpha(path
, 0, 0, m_width
, m_height
, m_color
, m_buf
, 3 * m_width
, 0);
396 path
= art_svp_vpath_stroke(v
, ART_PATH_STROKE_JOIN_MITER
,
397 ART_PATH_STROKE_CAP_BUTT
, 2, 4, .5);
400 art_rgb_svp_alpha(path
, 0, 0, m_width
, m_height
, m_color
, m_buf
, 3 * m_width
, 0);
405 void KClockPainter::drawHand(const QColor
&c
, double angle
, double length
,
406 double width
, bool disc
= true)
408 const double shadow_width
= 1.0;
411 drawDisc(width
* 1.3 + shadow_width
);
412 drawRadial(angle
, .75 * width
, length
+ shadow_width
, width
+ shadow_width
);
416 drawDisc(width
* 1.3);
417 drawRadial(angle
, .75 * width
, length
, width
);
425 //-----------------------------------------------------------------------------
427 KClockSaver::KClockSaver(WId id
)
432 setBackgroundColor(m_bgndColor
);
433 connect(&m_timer
, SIGNAL(timeout()), SLOT(slotTimeout()));
435 m_timer
.start(TIMER_INTERVALL
);
439 KClockSaver::~KClockSaver()
446 void KClockSaver::start(int size
)
448 m_diameter
= int(QMIN(width(), height()) * (size
+ 4) / 14.0);
449 m_x
= (width() - m_diameter
) / 2;
450 m_y
= (height() - m_diameter
) / 2;
452 m_image
= new QImage(m_diameter
, m_diameter
, 32);
453 m_scale
= new KClockPainter(m_diameter
, m_diameter
);
454 m_clock
= new KClockPainter(m_diameter
, m_diameter
);
456 m_clock
->setShadowColor(qRgb((m_bgndColor
.red() + m_scaleColor
.red()) / 2,
457 (m_bgndColor
.green() + m_scaleColor
.green()) / 2,
458 (m_bgndColor
.blue() + m_scaleColor
.blue()) / 2));
464 void KClockSaver::stop()
472 void KClockSaver::restart(int size
)
478 m_timer
.start(TIMER_INTERVALL
);
482 void KClockSaver::setKeepCentered(bool b
)
486 m_x
= (width() - m_diameter
) / 2;
487 m_y
= (height() - m_diameter
) / 2;
493 void KClockSaver::readSettings()
495 KConfig
*config
= KGlobal::config();
497 config
->setGroup("Settings");
498 m_keepCentered
= config
->readBoolEntry("KeepCentered", DEFAULT_KEEP_CENTERED
);
499 m_size
= config
->readUnsignedNumEntry("Size", DEFAULT_CLOCK_SIZE
);
500 if (m_size
> MAX_CLOCK_SIZE
)
501 m_size
= MAX_CLOCK_SIZE
;
503 config
->setGroup("Colors");
504 QColor c
= Qt::black
;
505 m_bgndColor
= config
->readColorEntry("Background", &c
);
508 m_scaleColor
= config
->readColorEntry("Scale", &c
);
509 m_hourColor
= config
->readColorEntry("HourHand", &c
);
510 m_minColor
= config
->readColorEntry("MinuteHand", &c
);
513 m_secColor
= config
->readColorEntry("SecondHand", &c
);
517 void KClockSaver::drawScale()
519 m_scale
->fill(m_bgndColor
);
520 m_scale
->setColor(m_scaleColor
);
522 for (int i
= 0; i
< 360; i
+= 6)
524 m_scale
->drawRadial(i
, 920, 980, 15);
526 m_scale
->drawRadial(i
, 825, 980, 40);
532 void KClockSaver::drawClock()
534 double hour_angle
= m_hour
* 30.0 + m_minute
* .5;
535 double minute_angle
= m_minute
* 6.0 + m_second
* .1;
536 double second_angle
= m_second
* 6.0;
538 m_clock
->copy(m_scale
);
539 m_clock
->drawHand(m_hourColor
, hour_angle
, 600, 55, false);
540 m_clock
->drawHand(m_minColor
, minute_angle
, 900, 40);
543 m_clock
->drawHand(m_secColor
, second_angle
, 900, 30);
547 void KClockSaver::slotTimeout()
549 QTime t
= QTime::currentTime();
554 m_second
= m_showSecond
? s
: 0;
556 m_minute
= t
.minute();
562 // intended for the control module preview: always fill the whole area
563 QImage
*img
= new QImage(width(), height(), 32);
564 img
->fill(qRgb(m_bgndColor
.red(), m_bgndColor
.green(), m_bgndColor
.blue()));
565 m_clock
->drawToImage(img
, m_x
, m_y
);
566 p
.drawImage(0, 0, *img
);
569 m_clock
->drawToImage(m_image
);
570 p
.drawImage(m_x
, m_y
, *m_image
);
573 if (!m_keepCentered
) {
574 static int xstep
= 1;
575 static int ystep
= -1;
581 else if (m_x
>= (i
= width() - m_diameter
))
587 else if (m_y
>= (i
= height() - m_diameter
))