// $Id$ // // kclock - Clock screen saver for KDE // Copyright (c) 2003 Melchior FRANZ // // License: GPL v2 // Author: Melchior FRANZ // Dependencies: libart_lgpl_2 http://www.levien.com/libart/ // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kclock.h" #include "kclock.moc" const int COLOR_BUTTON_WIDTH = 80; const int TIMER_INTERVALL = 100; const int MAX_CLOCK_SIZE = 10; const int DEFAULT_CLOCK_SIZE = 3; const bool DEFAULT_KEEP_CENTERED = false; extern "C" { KDE_EXPORT const char *kss_applicationName = "kclock.kss"; KDE_EXPORT const char *kss_description = I18N_NOOP("Clock"); KDE_EXPORT const char *kss_version = "1.0"; KDE_EXPORT KScreenSaver *kss_create(WId id) { return new KClockSaver(id); } KDE_EXPORT TQDialog *kss_setup() { return new KClockSetup(); } } //----------------------------------------------------------------------------- KClockSetup::KClockSetup(TQWidget *parent, const char *name) : KDialogBase(parent, name, true, i18n( "Setup Clock Screen Saver" ), Ok|Cancel|Help, Ok, true), m_saver(0) { readSettings(); setButtonText( Help, i18n( "A&bout" ) ); TQWidget *main = makeMainWidget(); TQVBoxLayout *top = new TQVBoxLayout(main, 0, spacingHint()); TQHBoxLayout *hbox = new TQHBoxLayout; top->addLayout( hbox ); TQGroupBox *colgroup = new TQGroupBox(i18n("Colors"), main); colgroup->setColumnLayout( 0,Qt::Horizontal ); TQGridLayout *grid = new TQGridLayout( colgroup->layout(), 5, 2, spacingHint() ); TQLabel *label = new TQLabel(i18n("&Hour-hand:"), colgroup); KColorButton *colorButton = new KColorButton(m_hourColor, colgroup); colorButton->setFixedWidth(COLOR_BUTTON_WIDTH); label->setBuddy(colorButton); connect(colorButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotHourColor(const TQColor &))); grid->addWidget( label, 1, 1 ); grid->addWidget( colorButton, 1, 2 ); label = new TQLabel(i18n("&Minute-hand:"), colgroup); colorButton = new KColorButton(m_minColor, colgroup); colorButton->setFixedWidth(COLOR_BUTTON_WIDTH); label->setBuddy(colorButton); connect(colorButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotMinColor(const TQColor &))); grid->addWidget( label, 2, 1 ); grid->addWidget( colorButton, 2, 2 ); label = new TQLabel(i18n("&Second-hand:"), colgroup); colorButton = new KColorButton(m_secColor, colgroup); colorButton->setFixedWidth(COLOR_BUTTON_WIDTH); label->setBuddy(colorButton); connect(colorButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotSecColor(const TQColor &))); grid->addWidget( label, 3, 1 ); grid->addWidget( colorButton, 3, 2 ); label = new TQLabel(i18n("Scal&e:"), colgroup); colorButton = new KColorButton(m_scaleColor, colgroup); colorButton->setFixedWidth(COLOR_BUTTON_WIDTH); label->setBuddy(colorButton); connect(colorButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotScaleColor(const TQColor &))); grid->addWidget( label, 4, 1 ); grid->addWidget( colorButton, 4, 2 ); label = new TQLabel(i18n("&Background:"), colgroup); colorButton = new KColorButton(m_bgndColor, colgroup); colorButton->setFixedWidth(COLOR_BUTTON_WIDTH); label->setBuddy(colorButton); connect(colorButton, TQT_SIGNAL(changed(const TQColor &)), TQT_SLOT(slotBgndColor(const TQColor &))); grid->addWidget( label, 5, 1 ); grid->addWidget( colorButton, 5, 2 ); hbox->addWidget(colgroup); TQWidget *m_preview = new TQWidget(main); m_preview->setFixedSize(220, 165); m_preview->show(); m_saver = new KClockSaver(m_preview->winId()); hbox->addWidget(m_preview); label = new TQLabel( i18n( "Si&ze:" ), main ); top->addWidget( label ); TQSlider *qs = new TQSlider(0, MAX_CLOCK_SIZE, 1, m_size,Qt::Horizontal, main); label->setBuddy( qs ); qs->setTickInterval(1); qs->setTickmarks(TQSlider::Below); connect(qs, TQT_SIGNAL(valueChanged(int)), this, TQT_SLOT(slotSliderMoved(int))); top->addWidget( qs ); bool rtl = kapp->reverseLayout(); TQHBox *qsscale = new TQHBox(main); label = new TQLabel(i18n("Small"), qsscale); label->setAlignment(rtl ? AlignRight : AlignLeft); label = new TQLabel(i18n("Medium"), qsscale); label->setAlignment(AlignHCenter); label = new TQLabel(i18n("Big"), qsscale); label->setAlignment(rtl ? AlignLeft : AlignRight); top->addWidget(qsscale); TQCheckBox *keepCentered = new TQCheckBox(i18n("&Keep clock centered"), main); keepCentered->setChecked(m_keepCentered); connect(keepCentered, TQT_SIGNAL(stateChanged(int)), TQT_SLOT(slotKeepCenteredChanged(int))); top->addWidget(keepCentered); top->addStretch(); } KClockSetup::~KClockSetup() { delete m_saver; } void KClockSetup::readSettings() { TDEConfig *config = TDEGlobal::config(); config->setGroup("Settings"); m_keepCentered = config->readBoolEntry("KeepCentered", DEFAULT_KEEP_CENTERED); m_size = config->readUnsignedNumEntry("Size", DEFAULT_CLOCK_SIZE); if (m_size > MAX_CLOCK_SIZE) m_size = MAX_CLOCK_SIZE; config->setGroup("Colors"); TQColor c = TQt::black; m_bgndColor = config->readColorEntry("Background", &c); c = TQt::white; m_scaleColor = config->readColorEntry("Scale", &c); m_hourColor = config->readColorEntry("HourHand", &c); m_minColor = config->readColorEntry("MinuteHand", &c); c = TQt::red; m_secColor = config->readColorEntry("SecondHand", &c); if (m_saver) { m_saver->setBgndColor(m_bgndColor); m_saver->setScaleColor(m_scaleColor); m_saver->setHourColor(m_hourColor); m_saver->setMinColor(m_minColor); m_saver->setSecColor(m_secColor); } } void KClockSetup::slotOk() { TDEConfig *config = TDEGlobal::config(); config->setGroup("Settings"); config->writeEntry("Size", m_size); config->writeEntry("KeepCentered", m_keepCentered); config->setGroup("Colors"); config->writeEntry("Background", m_bgndColor); config->writeEntry("Scale", m_scaleColor); config->writeEntry("HourHand", m_hourColor); config->writeEntry("MinuteHand", m_minColor); config->writeEntry("SecondHand", m_secColor); config->sync(); accept(); } void KClockSetup::slotHelp() { KMessageBox::about(this, "" + i18n( "Clock Screen Saver
" "Version 1.0
" "Melchior FRANZ (c) 2003") + "
mfranz@kde.org" "
", TQString(), KMessageBox::AllowLink); } void KClockSetup::slotBgndColor(const TQColor &color) { m_bgndColor = color; if (m_saver) m_saver->setBgndColor(m_bgndColor); } void KClockSetup::slotScaleColor(const TQColor &color) { m_scaleColor = color; if (m_saver) m_saver->setScaleColor(m_scaleColor); } void KClockSetup::slotHourColor(const TQColor &color) { m_hourColor = color; if (m_saver) m_saver->setHourColor(m_hourColor); } void KClockSetup::slotMinColor(const TQColor &color) { m_minColor = color; if (m_saver) m_saver->setMinColor(m_minColor); } void KClockSetup::slotSecColor(const TQColor &color) { m_secColor = color; if (m_saver) m_saver->setSecColor(m_secColor); } void KClockSetup::slotSliderMoved(int v) { if (m_saver) m_saver->restart(m_size = v); } void KClockSetup::slotKeepCenteredChanged(int c) { if (m_saver) m_saver->setKeepCentered(m_keepCentered = c); } //----------------------------------------------------------------------------- KClockPainter::KClockPainter(int width, int height) : m_width(width), m_height(height) { m_buf = new TQ_UINT8[m_width * m_height * 3]; // build Cartesian coordinate system ranging from -1000 to +1000; // points with positive x and y are in the top right quarter m_matrix[0] = m_width / 2000.0; m_matrix[1] = 0; m_matrix[2] = 0; m_matrix[3] = m_height / -2000.0; m_matrix[4] = m_width / 2.0; m_matrix[5] = m_height / 2.0; } KClockPainter::~KClockPainter() { delete[] m_buf; } void KClockPainter::copy(KClockPainter *p) { memcpy(m_buf, p->image(), m_width * m_height * 3); } void KClockPainter::drawToImage(TQImage *q, int xoffs = 0, int yoffs = 0) { unsigned char *src = (unsigned char *)image(); for (int y = 0; y < m_height; y++) { TQRgb *dest = reinterpret_cast(q->scanLine(y + yoffs)) + xoffs; for (int x = 0; x < m_width; x++, src += 3) *dest++ = tqRgba(src[0], src[1], src[2], 255); } } void KClockPainter::setColor(const TQColor &c) { m_color = (c.red() << 24) | (c.green() << 16) | (c.blue() << 8) | 255; } void KClockPainter::setShadowColor(const TQColor &c) { m_shadow = (c.red() << 24) | (c.green() << 16) | (c.blue() << 8) | 255; } void KClockPainter::fill(const TQColor &c) { art_rgb_fill_run(m_buf, c.red(), c.green(), c.blue(), m_width * m_height); } void KClockPainter::drawRadial(double alpha, double r0, double r1, double width) { ArtVpath *vx, *v = art_new(ArtVpath, 3); v[0].code = ART_MOVETO_OPEN; v[0].x = r0; v[0].y = 0; v[1].code = ART_LINETO; v[1].x = r1; v[1].y = 0; v[2].code = ART_END; double m[6] = {0, 0, 0, 0, 0, 0}; art_affine_rotate(m, 90 - alpha); vx = art_vpath_affine_transform(v, m); art_free(v); v = art_vpath_affine_transform(vx, m_matrix); art_free(vx); double w = width * m_matrix[0]; if (w < 1.0) w = 1.0; ArtSVP *path = art_svp_vpath_stroke(v, ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_CAP_BUTT, w, 4, .5); art_rgb_svp_alpha(path, 0, 0, m_width, m_height, m_color, m_buf, 3 * m_width, 0); art_svp_free(path); art_free(v); } void KClockPainter::drawDisc(double r) { ArtVpath *v, *vx = art_vpath_new_circle(0, 0, r); v = art_vpath_affine_transform(vx, m_matrix); art_free(vx); ArtSVP *path = art_svp_from_vpath(v); art_rgb_svp_alpha(path, 0, 0, m_width, m_height, m_color, m_buf, 3 * m_width, 0); art_svp_free(path); path = art_svp_vpath_stroke(v, ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_CAP_BUTT, 2, 4, .5); art_free(v); art_rgb_svp_alpha(path, 0, 0, m_width, m_height, m_color, m_buf, 3 * m_width, 0); art_svp_free(path); } void KClockPainter::drawHand(const TQColor &c, double angle, double length, double width, bool disc = true) { const double shadow_width = 1.0; setColor(m_shadow); if (disc) drawDisc(width * 1.3 + shadow_width); drawRadial(angle, .75 * width, length + shadow_width, width + shadow_width); setColor(c); if (disc) drawDisc(width * 1.3); drawRadial(angle, .75 * width, length, width); } //----------------------------------------------------------------------------- KClockSaver::KClockSaver(WId id) : KScreenSaver(id), m_showSecond(true) { readSettings(); setBackgroundColor(m_bgndColor); connect(&m_timer, TQT_SIGNAL(timeout()), TQT_SLOT(slotTimeout())); start(m_size); m_timer.start(TIMER_INTERVALL); } KClockSaver::~KClockSaver() { m_timer.stop(); stop(); } void KClockSaver::start(int size) { m_diameter = int(TQMIN(width(), height()) * (size + 4) / 14.0); m_x = (width() - m_diameter) / 2; m_y = (height() - m_diameter) / 2; m_image = new TQImage(m_diameter, m_diameter, 32); m_scale = new KClockPainter(m_diameter, m_diameter); m_clock = new KClockPainter(m_diameter, m_diameter); m_clock->setShadowColor(tqRgb((m_bgndColor.red() + m_scaleColor.red()) / 2, (m_bgndColor.green() + m_scaleColor.green()) / 2, (m_bgndColor.blue() + m_scaleColor.blue()) / 2)); drawScale(); forceRedraw(); } void KClockSaver::stop() { delete m_clock; delete m_scale; delete m_image; } void KClockSaver::restart(int size) { m_timer.stop(); stop(); start(size); drawScale(); m_timer.start(TIMER_INTERVALL); } void KClockSaver::setKeepCentered(bool b) { m_keepCentered = b; if (b) { m_x = (width() - m_diameter) / 2; m_y = (height() - m_diameter) / 2; } forceRedraw(); } void KClockSaver::readSettings() { TDEConfig *config = TDEGlobal::config(); config->setGroup("Settings"); m_keepCentered = config->readBoolEntry("KeepCentered", DEFAULT_KEEP_CENTERED); m_size = config->readUnsignedNumEntry("Size", DEFAULT_CLOCK_SIZE); if (m_size > MAX_CLOCK_SIZE) m_size = MAX_CLOCK_SIZE; config->setGroup("Colors"); TQColor c = TQt::black; m_bgndColor = config->readColorEntry("Background", &c); c = TQt::white; m_scaleColor = config->readColorEntry("Scale", &c); m_hourColor = config->readColorEntry("HourHand", &c); m_minColor = config->readColorEntry("MinuteHand", &c); c = TQt::red; m_secColor = config->readColorEntry("SecondHand", &c); } void KClockSaver::drawScale() { m_scale->fill(m_bgndColor); m_scale->setColor(m_scaleColor); for (int i = 0; i < 360; i += 6) if (i % 30) m_scale->drawRadial(i, 920, 980, 15); else m_scale->drawRadial(i, 825, 980, 40); forceRedraw(); } void KClockSaver::drawClock() { double hour_angle = m_hour * 30.0 + m_minute * .5; double minute_angle = m_minute * 6.0 + m_second * .1; double second_angle = m_second * 6.0; m_clock->copy(m_scale); m_clock->drawHand(m_hourColor, hour_angle, 600, 55, false); m_clock->drawHand(m_minColor, minute_angle, 900, 40); if (m_showSecond) m_clock->drawHand(m_secColor, second_angle, 900, 30); } void KClockSaver::slotTimeout() { TQTime t = TQTime::currentTime(); int s = t.second(); if (s == m_second) return; m_second = m_showSecond ? s : 0; m_hour = t.hour(); m_minute = t.minute(); drawClock(); TQPainter p(this); if (width() < 256) { // intended for the control module preview: always fill the whole area TQImage *img = new TQImage(width(), height(), 32); img->fill(tqRgb(m_bgndColor.red(), m_bgndColor.green(), m_bgndColor.blue())); m_clock->drawToImage(img, m_x, m_y); p.drawImage(0, 0, *img); delete img; } else { m_clock->drawToImage(m_image); p.drawImage(m_x, m_y, *m_image); } if (!m_keepCentered) { static int xstep = 1; static int ystep = -1; int i; m_x += xstep; if (m_x <= 0) m_x = 0, xstep = 1; else if (m_x >= (i = width() - m_diameter)) m_x = i, xstep = -1; m_y += ystep; if (m_y <= 0) m_y = 0, ystep = 1; else if (m_y >= (i = height() - m_diameter)) m_y = i, ystep = -1; } }