/*****************************************************************

Copyright (c) 1996-2000 the kicker authors. See file AUTHORS.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

******************************************************************/

#include "clock.h"
#include "conf.h"

#include <qtimer.h>
#include <qdatetime.h>
#include <qlayout.h>
#include <qbitmap.h>
#include <qpainter.h>
#include <qbuttongroup.h>
#include <qradiobutton.h>
#include <qcheckbox.h>
#include <qcolor.h>
#include <qtooltip.h>
#include <qclipboard.h>
#include <qtabwidget.h>
#include <qslider.h>

#include <kstddirs.h>
#include <kcolorbutton.h>
#include <kapp.h>
#include <kprocess.h>
#include <kwin.h>
#include <kiconloader.h>
#include <kglobal.h>
#include <kconfig.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kfontdialog.h>

extern "C"
{
    KPanelApplet* init(QWidget *parent, const QString& configFile)
    {
        KGlobal::locale()->insertCatalogue("clockapplet");
        return new ClockApplet(configFile, KPanelApplet::Normal, KPanelApplet::Preferences, parent, "clockapplet");
    }
}


//************************************************************


// workaround for a strange (Qt?) bug that looks up
// the desktop, just disable the buttons that open the
// offending popup
DatePickerWidget::DatePickerWidget(QWidget *parent)
    : KDatePicker(parent)
{
//     selectMonth->disconnect();
//     selectYear->disconnect();
}


DatePickerWidget::~DatePickerWidget()
{
}


DatePicker::DatePicker(QWidget *parent)
    : QVBox( parent, 0,  WType_Popup | WDestructiveClose )
{
    setFrameStyle( QFrame::PopupPanel | QFrame::Raised );
    picker = new DatePickerWidget(this);
}


DatePicker::~DatePicker()
{
}


//************************************************************


ClockSettings::ClockSettings(QWidget* app, KConfig* conf)
    : applet(app), config(conf), confDlg(0), colAnalogModified(false), colDigitalModified(false), colFuzzyModified(false)
{
    config->setGroup("General");
    if (conf->readEntry("Type", "Analog")=="Analog")
	_type = Analog;
    else
    if (conf->readEntry("Type", "Analog")=="Digital")
	_type = Digital;
    else
        _type = Fuzzy;

    config->setGroup("Digital");
    _lcdStyleDig = config->readBoolEntry("LCD_Style",true);
    _foreColorDig = config->readColorEntry("Foreground_Color", &KApplication::palette().active().text());
    _shadowColorDig = config->readColorEntry("Shadow_Color", &KApplication::palette().active().mid());
    _backColorDig = config->readColorEntry("Background_Color", &KApplication::palette().active().background());
    _showSecsDig = config->readBoolEntry("Show_Seconds",false);
    _showDateDig = config->readBoolEntry("Show_Date",true);
    _blink = config->readBoolEntry("Blink",true);

    config->setGroup("Analog");
    _lcdStyleAna = config->readBoolEntry("LCD_Style",true);
    _foreColorAna = config->readColorEntry("Foreground_Color", &KApplication::palette().active().text());
    _shadowColorAna = config->readColorEntry("Shadow_Color", &KApplication::palette().active().mid());
    _backColorAna = config->readColorEntry("Background_Color", &KApplication::palette().active().background());
    _showSecsAna = config->readBoolEntry("Show_Seconds",true);
    _showDateAna = config->readBoolEntry("Show_Date",false);

    config->setGroup("Fuzzy");
    _foreColorFuz = config->readColorEntry("Foreground_Color", &KApplication::palette().active().text());
    _backColorFuz = config->readColorEntry("Background_Color", &KApplication::palette().active().background());
    _showDateFuz = config->readBoolEntry("Show_Date", false);
    _fontFuz = config->readFontEntry("Font");
    _fuzzynessFuz = config->readNumEntry("Fuzzyness", 0);
}


ClockSettings::~ClockSettings()
{
    delete confDlg;
}


bool ClockSettings::showDate()
{
  if (_type == Digital)
        return _showDateDig;
  else
  if (_type == Analog)
        return _showDateAna;
  else
        return _showDateFuz;
}


QColor ClockSettings::foreColor()
{
  if (_type == Digital)
        return _foreColorDig;
  else
  if (_type == Analog)
        return _foreColorAna;
  else
        return _foreColorFuz;
}


QColor ClockSettings::backColor()
{
  if (_type == Digital)
        return _backColorDig;
  else
  if (_type == Analog)
        return _backColorAna;
  else
        return _backColorFuz;
}


void ClockSettings::writeSettings()
{
    config->setGroup("General");
    if (_type == Analog )
        config->writeEntry("Type", "Analog");
    else
    if (_type == Digital )
        config->writeEntry("Type", "Digital");
    else
        config->writeEntry("Type", "Fuzzy");

    config->setGroup("Digital");
    config->writeEntry("LCD_Style",_lcdStyleDig);
    if (colDigitalModified) {
        config->writeEntry("Foreground_Color",_foreColorDig);
        config->writeEntry("Shadow_Color",_shadowColorDig);
        config->writeEntry("Background_Color",_backColorDig);
    }
    config->writeEntry("Show_Seconds",_showSecsDig);
    config->writeEntry("Show_Date",_showDateDig);
    config->writeEntry("Blink",_blink);

    config->setGroup("Analog");
    config->writeEntry("LCD_Style",_lcdStyleAna);
    if (colAnalogModified) {
        config->writeEntry("Foreground_Color",_foreColorAna);
        config->writeEntry("Shadow_Color",_shadowColorAna);
        config->writeEntry("Background_Color",_backColorAna);
    }
    config->writeEntry("Show_Seconds",_showSecsAna);
    config->writeEntry("Show_Date",_showDateAna);

    config->setGroup("Fuzzy");
    if (colFuzzyModified) {
        config->writeEntry("Foreground_Color", _foreColorFuz);
        config->writeEntry("Background_Color", _backColorFuz);
    }
    config->writeEntry("Show_Date", _showDateFuz);
    config->writeEntry("Font", _fontFuz);
    config->writeEntry("Fuzzyness", _fuzzynessFuz);

    config->sync();
}


void ClockSettings::openPreferences()
{
    if (confDlg) {
        KWin::setActiveWindow( confDlg->winId());
        return;
    }

    confDlg = new ClockConfDialog( applet, 0, FALSE, WDestructiveClose );
    connect(confDlg->buttonOk, SIGNAL(clicked()), this, SLOT(dlgOkClicked()));
    connect(confDlg->buttonApply, SIGNAL(clicked()), this, SLOT(dlgApplyClicked()));
    connect(confDlg->buttonCancel, SIGNAL(clicked()), this, SLOT(dlgCancelClicked()));
    connect(confDlg, SIGNAL( destroyed() ), SLOT( dlgDeleted() ));
    connect(confDlg->bChooseFont, SIGNAL(clicked()), this, SLOT(dlgbChooseFontClicked()));

    confDlg->digital->setChecked(_type==Digital);
    confDlg->analog->setChecked(_type==Analog);
    confDlg->fuzzy->setChecked(_type==Fuzzy);

    confDlg->showDateDigital->setChecked(_showDateDig);
    confDlg->showSecsDigital->setChecked(_showSecsDig);
    confDlg->blinkingDigital->setChecked(_blink);
    confDlg->lcdDigital->setChecked(_lcdStyleDig);
    confDlg->RadioButton5->setChecked(!_lcdStyleDig);
    confDlg->foregroundDigital->setColor(_foreColorDig);
    confDlg->shadowDigital->setColor(_shadowColorDig);
    confDlg->backgroundDigital->setColor(_backColorDig);

    confDlg->showDateAnalog->setChecked(_showDateAna);
    confDlg->showSecsAnalog->setChecked(_showSecsAna);
    confDlg->lcdAnalog->setChecked(_lcdStyleAna);
    confDlg->RadioButton5_2->setChecked(!_lcdStyleAna);
    confDlg->foregroundAnalog->setColor(_foreColorAna);
    confDlg->shadowAnalog->setColor(_shadowColorAna);
    confDlg->backgroundAnalog->setColor(_backColorAna);

    confDlg->showDateFuzzy->setChecked(_showDateFuz);
    confDlg->fuzzyness->setValue(_fuzzynessFuz);
    confDlg->foregroundFuzzy->setColor(_foreColorFuz);
    confDlg->backgroundFuzzy->setColor(_backColorFuz);
    confDlg->bChooseFont->setFont(_fontFuz);

    confDlg->show();

    if (_type == Digital)
      confDlg->TabWidget2->setCurrentPage(0);
    else
    if (_type == Analog)
      confDlg->TabWidget2->setCurrentPage(1);
    else
      confDlg->TabWidget2->setCurrentPage(2);
}


void ClockSettings::setType(ClockType type)
{
    _type = type;
    if (confDlg) {
        confDlg->digital->setChecked(_type==Digital);
        confDlg->analog->setChecked(_type==Analog);
        confDlg->fuzzy->setChecked(_type==Fuzzy);
    }
}


void ClockSettings::dlgOkClicked()
{
    dlgApplyClicked();
    delete	confDlg;
}


void ClockSettings::dlgApplyClicked()
{
    if (confDlg->digital->isChecked())
        _type = Digital;
    else
    if (confDlg->analog->isChecked())
        _type = Analog;
    else
        _type = Fuzzy;

    _showDateDig = confDlg->showDateDigital->isChecked();
    _showSecsDig = confDlg->showSecsDigital->isChecked();
    _blink = confDlg->blinkingDigital->isChecked();
    _lcdStyleDig = confDlg->lcdDigital->isChecked();

    // stick to palette defaults unless the user
    // actually modifies a color
    if ((_foreColorDig != confDlg->foregroundDigital->color())||
        (_shadowColorDig != confDlg->shadowDigital->color())||
        (_backColorDig != confDlg->backgroundDigital->color())) {
        colDigitalModified = true;
        _foreColorDig = confDlg->foregroundDigital->color();
        _shadowColorDig = confDlg->shadowDigital->color();
        _backColorDig = confDlg->backgroundDigital->color();
    }

    _showDateAna = confDlg->showDateAnalog->isChecked();
    _showSecsAna = confDlg->showSecsAnalog->isChecked();
    _lcdStyleAna = confDlg->lcdAnalog->isChecked();
    if ((_foreColorAna != confDlg->foregroundAnalog->color())||
        (_shadowColorAna != confDlg->shadowAnalog->color())||
        (_backColorAna != confDlg->backgroundAnalog->color())) {
        colAnalogModified = true;
        _foreColorAna = confDlg->foregroundAnalog->color();
        _shadowColorAna = confDlg->shadowAnalog->color();
        _backColorAna = confDlg->backgroundAnalog->color();
    }

    _showDateFuz = confDlg->showDateFuzzy->isChecked();
    _fuzzynessFuz = confDlg->fuzzyness->value();
    _fontFuz = confDlg->bChooseFont->font();
    if ((_foreColorFuz != confDlg->foregroundFuzzy->color())||
        (_backColorFuz != confDlg->backgroundFuzzy->color())) {
        colFuzzyModified = true;
        _foreColorFuz = confDlg->foregroundFuzzy->color();
        _backColorFuz = confDlg->backgroundFuzzy->color();
    }

    writeSettings();
    emit(newSettings());
}


void ClockSettings::dlgCancelClicked()
{
    delete	confDlg;
}


void ClockSettings::dlgDeleted()
{
    confDlg = 0;
}


void ClockSettings::dlgbChooseFontClicked()
{
  KFontDialog fd(0L, "Font Dialog", false, true);

  fd.setFont(confDlg->bChooseFont->font());

  if (fd.exec()  == KFontDialog::Accepted)
  {
    _fontFuz = fd.font();
    confDlg->bChooseFont->setFont(_fontFuz);
  }
}


//************************************************************


DigitalClock::DigitalClock(ClockSettings* set, QWidget *parent, const char *name)
    : QLCDNumber(parent, name), settings(set)
{
    setFrameStyle(Panel | Sunken);
    setMargin( 4 );
    setSegmentStyle(QLCDNumber::Flat);

    if (settings->lcdStyle())
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
    else
        setBackgroundColor(settings->backColor());

    setNumDigits(settings->showSeconds()? 8:5);

    ampm = KGlobal::locale()->use12Clock();

    buffer = new QPixmap( width(), height() );

    slotUpdate();
}


DigitalClock::~DigitalClock()
{
}


QSize DigitalClock::sizeHint() const
{
    QSize s = QLCDNumber::sizeHint();
    return QSize( s.width() , s.height() + 6 );
}


// this is needed because QLCDNumber::sizeHint().height() is
// hard-coded in qlcdnumber.cpp (as of qt 2.2.0), but actually the
// needed height will vary based on the width available and the number
// of digits to display
int DigitalClock::heightForWidth(int w) const
{
    return((w / numDigits() * 2) + 6);
}


int DigitalClock::widthForHeight(int h) const
{
    if (h > 29) h = 29;
    return (numDigits()*h*5/11)+2;
}


void DigitalClock::slotUpdate()
{
    static bool colon = true;
    QString newStr;
    QTime t(QTime::currentTime());

    int h = t.hour();
    int m = t.minute();
    int s = t.second();

    QString format("%02d");

    QString sep(!colon && settings->blink() ? " " : ":");

    if (settings->showSeconds())
        format += sep + "%02d";

    if (!ampm)
        format.prepend("%02d" + sep);
    else
    {
        if (h > 12)
            h -= 12;
        else if( h == 0)
            h = 12;

        format.prepend("%2d" + sep);
    }

    if (settings->showSeconds())
        newStr.sprintf(format.latin1(), h, m, s);
    else
        newStr.sprintf(format.latin1(), h, m);

    if(newStr != timeStr){
        timeStr = newStr;
        setUpdatesEnabled( FALSE );
        display(timeStr);
        setUpdatesEnabled( TRUE );
        repaint( FALSE );
    }
    if(settings->blink())
        colon = !colon;
}


void DigitalClock::relayout()
{
    setUpdatesEnabled( FALSE );
    setNumDigits(settings->showSeconds()? 8:5);
    setUpdatesEnabled( TRUE );
    slotUpdate();
}


void DigitalClock::paintEvent( QPaintEvent*)
{
    buffer->fill( this, 0, 0 );
    QPainter p( buffer );
    drawFrame( &p );
    drawContents( &p );
    p.end();
    bitBlt( this, 0, 0, buffer, 0, 0);
}


// yes, the colors for the lcd-lock are hardcoded,
// but other colors would break the lcd-lock anyway
void DigitalClock::drawContents( QPainter * p)
{
    setUpdatesEnabled( FALSE );
    QPalette pal = palette();
    if (settings->lcdStyle())
        pal.setColor( QColorGroup::Foreground, QColor(128,128,128));
    else
        pal.setColor( QColorGroup::Foreground, settings->shadowColor());
    setPalette( pal );
    p->translate( +1, +1 );
    QLCDNumber::drawContents( p );
    if (settings->lcdStyle())
        pal.setColor( QColorGroup::Foreground, Qt::black);
    else
        pal.setColor( QColorGroup::Foreground, settings->foreColor());
    setPalette( pal );
    p->translate( -2, -2 );
    setUpdatesEnabled( TRUE );
    QLCDNumber::drawContents( p );
}


// reallocate buffer pixmap
void DigitalClock::resizeEvent ( QResizeEvent *)
{
    delete buffer;
    buffer = new QPixmap( width(), height() );
}


// the background pixmap disappears during a style change
void DigitalClock::styleChange ( QStyle &)
{
    if (settings->lcdStyle())
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
}


//************************************************************


AnalogClock::AnalogClock(ClockSettings* set, QWidget *parent, const char *name)
    : QFrame(parent, name), settings(set)
{
    setFrameStyle(Panel | Sunken);
    if (settings->lcdStyle())
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
    else
        setBackgroundColor(settings->backColor());

    time = QTime::currentTime();
    repaint( );
}


AnalogClock::~AnalogClock()
{
}


void AnalogClock::slotUpdate()
{
    if (!settings->showSeconds())
        if (time.minute()==QTime::currentTime().minute())
            return;

    time = QTime::currentTime();
    repaint( );
}


void AnalogClock::paintEvent( QPaintEvent * )
{
    if ( !isVisible() )
        return;

    QPainter paint;
    paint.begin( this );

    drawFrame( &paint );

    QPointArray pts;
    QPoint cp = rect().center();

    int d = QMIN(width(),height())-10;

    if (settings->lcdStyle()) {
        paint.setPen( QColor(100,100,100) );
        paint.setBrush( QColor(100,100,100) );
    } else {
        paint.setPen( settings->shadowColor() );
        paint.setBrush( settings->shadowColor() );
    }

    paint.setViewport(2,2,width(),height());

    for ( int c=0 ; c < 2 ; c++ ) {
        QWMatrix matrix;
        matrix.translate( cp.x(), cp.y() );
        matrix.scale( d/1000.0F, d/1000.0F );

        // hour
        float h_angle = 30*(time.hour()%12-3) + time.minute()/2;
        matrix.rotate( h_angle );
        paint.setWorldMatrix( matrix );
        pts.setPoints( 4, -20,0,  0,-20, 300,0, 0,20 );
        paint.drawPolygon( pts );
        matrix.rotate( -h_angle );

        // minute
        float m_angle = (time.minute()-15)*6;
        matrix.rotate( m_angle );
        paint.setWorldMatrix( matrix );
        pts.setPoints( 4, -10,0, 0,-10, 400,0, 0,10 );
        paint.drawPolygon( pts );
        matrix.rotate( -m_angle );

        if (settings->showSeconds()) {   // second
            float s_angle = (time.second()-15)*6;
            matrix.rotate( s_angle );
            paint.setWorldMatrix( matrix );
            pts.setPoints(4,0,0,0,0,400,0,0,0);
            paint.drawPolygon( pts );
            matrix.rotate( -s_angle );
        }

        QWMatrix matrix2;
        matrix2.translate( cp.x(), cp.y() );
        matrix2.scale( d/1000.0F, d/1000.0F );

        // quadrante
        for ( int i=0 ; i < 12 ; i++ ) {
            paint.setWorldMatrix( matrix2 );
            paint.drawLine( 460,0, 500,0 );	// draw hour lines
            // paint.drawEllipse( 450, -15, 30, 30 );
            matrix2.rotate( 30 );
        }

        if (settings->lcdStyle()) {
            paint.setPen( Qt::black );
            paint.setBrush( Qt::black );
        } else {
            paint.setPen( settings->foreColor() );
            paint.setBrush( settings->foreColor() );
        }

        paint.setViewport(0,0,width(),height());
    }
    paint.end();
}


// the background pixmap disappears during a style change
void AnalogClock::styleChange ( QStyle &)
{
    if (settings->lcdStyle())
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
}


//************************************************************


FuzzyClock::FuzzyClock(ClockSettings* set, QWidget *parent, const char *name)
    : QFrame(parent, name), settings(set)
{
    setFrameStyle(Panel | Sunken);
    setBackgroundColor(settings->backColor());

    _parent = static_cast<ClockApplet *>(parent);

    time = QTime::currentTime();
    repaint( );
}


FuzzyClock::~FuzzyClock()
{
}


void FuzzyClock::slotUpdate()
{
  if (time.minute()==QTime::currentTime().minute())
        return;
  else {
        time = QTime::currentTime();
        repaint( );
  }
}


void FuzzyClock::drawContents(QPainter *p)
{
  if (!isVisible())
    return;

  QString newTimeStr;

  if (settings->fuzzyness() == 1 || settings->fuzzyness() == 2) {
    QStringList hourNames = QStringList() << i18n("hour","one") << i18n("hour","two")
		<< i18n("hour","three") << i18n("hour","four") << i18n("hour","five")
		<< i18n("hour","six") << i18n("hour","seven") << i18n("hour","eight")
		<< i18n("hour","nine") << i18n("hour","ten") << i18n("hour","eleven")
		<< i18n("hour","twelve");

    QStringList normalFuzzy; // xgettext:no-c-format
    normalFuzzy << i18n("%0 o'clock") // xgettext:no-c-format
                << i18n("five past %0") // xgettext:no-c-format
		<< i18n("ten past %0") // xgettext:no-c-format
                << i18n("quarter past %0") // xgettext:no-c-format
                << i18n("twenty past %0") // xgettext:no-c-format
		<< i18n("twenty five past %0") // xgettext:no-c-format
                << i18n("half past %0") // xgettext:no-c-format
                << i18n("twenty five to %1") // xgettext:no-c-format
		<< i18n("twenty to %1") // xgettext:no-c-format
                << i18n("quarter to %1") // xgettext:no-c-format
                << i18n("ten to %1") // xgettext:no-c-format
		<< i18n("five to %1") // xgettext:no-c-format
                << i18n("%1 o'clock"); // xgettext:no-c-format
    QStringList normalFuzzyOne; // xgettext:no-c-format
    normalFuzzyOne << i18n("one","%0 o'clock") // xgettext:no-c-format
                   << i18n("one","five past %0") // xgettext:no-c-format
                   << i18n("one","ten past %0") // xgettext:no-c-format
                   << i18n("one","quarter past %0") // xgettext:no-c-format
                   << i18n("one","twenty past %0") // xgettext:no-c-format
                   << i18n("one","twenty five past %0") // xgettext:no-c-format
                   << i18n("one","half past %0") // xgettext:no-c-format
                   << i18n("one","twenty five to %1") // xgettext:no-c-format
                   << i18n("one","twenty to %1") // xgettext:no-c-format
                   << i18n("one","quarter to %1") // xgettext:no-c-format
                   << i18n("one","ten to %1") // xgettext:no-c-format
                   << i18n("one","five to %1") // xgettext:no-c-format
                   << i18n("one","%1 o'clock"); // xgettext:no-c-format
    int minute = time.minute();
    int sector = 0;
    int realHour = 0;

    if (settings->fuzzyness() == 1) {
      if (minute > 2)
        sector = (minute - 3) / 5 + 1;
    } else {
      if (minute > 6)
        sector = ((minute - 7) / 15 + 1) * 3;
    }

    newTimeStr = normalFuzzy[sector];
    int phStart = newTimeStr.find("%");
    int phLength = newTimeStr.find(" ", phStart) - phStart;

    // larrosa: we want the exact length, in case the translation needs it,
    // in other case, we would cut off the end of the translation.
    if (phLength < 0) phLength = newTimeStr.length() - phStart;
    int deltaHour = newTimeStr.mid(phStart + 1, phLength - 1).toInt();

    if ((time.hour() + deltaHour) % 12 > 0)
      realHour = (time.hour() + deltaHour) % 12 - 1;
    else
      realHour = 12 - ((time.hour() + deltaHour) % 12 + 1);
    if (realHour==0) {
      newTimeStr = normalFuzzyOne[sector];
      phStart = newTimeStr.find("%");
      // larrosa: Note that length is the same,
      // so we only have to update phStart
    }
    newTimeStr.replace(phStart, phLength, hourNames[realHour]);
    newTimeStr.replace(0, 1, QString(newTimeStr.at(0).upper()));


  } else if (settings->fuzzyness() == 3) {
    QStringList dayTime = QStringList() << i18n("Night")
	<< i18n("Early morning") << i18n("Morning") << i18n("Almost noon")
	<< i18n("Noon") << i18n("Afternoon") << i18n("Evening")
	<< i18n("Late evening");
    newTimeStr = dayTime[time.hour() / 3];
  } else {
    int dow = QDate::currentDate().dayOfWeek();

    if (dow == 1)
      newTimeStr = i18n("Start of week");
    else if (dow >= 2 && dow <= 4)
      newTimeStr = i18n("Middle of week");
    else if (dow == 5)
      newTimeStr = i18n("End of week");
    else
      newTimeStr = i18n("Weekend!");
  }

  if (timeStr != newTimeStr) {
    timeStr = newTimeStr;
    _parent->intUpdateLayout();
  }

  p->setFont(settings->font());
  p->setPen(settings->foreColor());
  if (_parent->getOrientation() == Vertical) {
    p->rotate(90);
    p->drawText(4, -2, height() - 8, -(width()) + 2, AlignCenter, timeStr);
  } else {
    p->drawText(4, 2, width() - 8, height() - 4, AlignCenter, timeStr);
  }

}


QSize FuzzyClock::sizeHint() const
{
  QFontMetrics fm(settings->font());
  if (_parent->getOrientation() == Vertical)
    return QSize(fm.height() + 4, fm.width(timeStr) + 8);
  else
    return QSize(fm.width(timeStr) + 8, fm.height() + 4);
}


//************************************************************


ClockApplet::ClockApplet(const QString& configFile, Type t, int actions,
                         QWidget *parent, const char *name)
    : KPanelApplet(configFile, t, actions, parent, name),
      cal(0), disableCalendar(false), clock(0)
{
    settings = new ClockSettings(this, config());
    connect(settings, SIGNAL(newSettings()), SLOT(slotApplySettings()));

    date = new QLabel(this);
    QFont newFont(font());
    newFont.setPixelSize(10);
    setBackgroundMode(QWidget::X11ParentRelative);
    date->setBackgroundMode(QWidget::X11ParentRelative);
    date->setFont(newFont);
    date->installEventFilter(this);   // catch mouse clicks
    lastDate = QDate::currentDate();
    date->setText(KGlobal::locale()->formatDate(lastDate, true));
    QToolTip::add(date, KGlobal::locale()->formatDate(lastDate, false));

    timer = new QTimer(this);

    slotApplySettings();    // initialize clock widget

    connect(timer, SIGNAL(timeout()), SLOT(slotUpdate()));
    timer->start(1000);
}


ClockApplet::~ClockApplet()
{
    if ( cal )
        cal->close();
    delete settings;
}


int ClockApplet::widthForHeight(int h) const
{
    int shareDateHeight = 0;
    bool dateToSide = false;
    if (settings->showDate()) {
        if (h < 32)
            dateToSide = true;
        else   // put date underneath
            shareDateHeight = date->sizeHint().height();
    }

    int cw;
    if (settings->type()==ClockSettings::Digital)  // hack: :-(
        cw = static_cast<DigitalClock*>(clock)->widthForHeight(h - shareDateHeight);
    else if (settings->type()==ClockSettings::Fuzzy)
        cw = clock->sizeHint().width()+4;
    else    // analog clock
        cw = h - shareDateHeight;

    int w;
    if ( ! settings->showDate()) {
        w = cw;
        clock->setFixedSize(cw, h);
    } else {  // showing date
        date->setAlignment(AlignCenter);
        int dateWidth = date->sizeHint().width() + 4;
        if (dateToSide) {
            w = cw + dateWidth;
            clock->setFixedSize(cw, h);
            date->setFixedSize(dateWidth, h);
            date->move(cw, 0);
        } else {
            w = (cw > dateWidth ? cw : dateWidth);
            clock->setFixedSize(w, h - shareDateHeight);
            date->setFixedSize(w, shareDateHeight);
            date->move(0, clock->height());
        }
    }  // else showing date
    return w;
}


int ClockApplet::heightForWidth(int w) const
{
    int ch=0;
    // set height for clock (time)
    if (settings->type()==ClockSettings::Digital)
        ch = clock->heightForWidth(w);
    else
    if (settings->type() == ClockSettings::Fuzzy)
        ch = clock->sizeHint().height();
    else
        ch = w;
    clock->setFixedSize(w, ch);
    // add in height for date, if showing
    if (settings->showDate()) {
        date->setFixedWidth(w);
        date->setAlignment(AlignHCenter | WordBreak);
        // (above) first set date to a fixed width, then set for WordBreak;
        // now the needed height can be determined
        date->setFixedHeight(date->heightForWidth(w));
        // (above) date->sizeHint().height() is not used because qlabel.cpp
        // (in qt 2.2.0) indicates that sizeHint does not work well with
        // the wordbreak flag
        date->move(0, ch);
        ch += date->height();
    }

    return ch;
}


void ClockApplet::preferences()
{
    settings->openPreferences();
}


void ClockApplet::slotApplySettings()
{
    delete clock;

    if (settings->type()==ClockSettings::Analog) {
        AnalogClock* aclock = new AnalogClock(settings, this);
        connect(timer, SIGNAL(timeout()), aclock, SLOT(slotUpdate()));
        clock = aclock;
    } else if (settings->type()==ClockSettings::Digital) {
        DigitalClock* dclock = new DigitalClock(settings, this);
        connect(timer, SIGNAL(timeout()), dclock, SLOT(slotUpdate()));
        connect(this, SIGNAL(layoutChanged()), dclock, SLOT(relayout()));
        clock = dclock;
    } else {
        FuzzyClock *fclock = new FuzzyClock(settings, this);
        connect(timer, SIGNAL(timeout()), fclock, SLOT(slotUpdate()));
        clock = fclock;
    }
    QToolTip::add(clock,KGlobal::locale()->formatDate(lastDate, false));

    clock->installEventFilter(this);   // catch mouse clicks
    clock->show();

    if (settings->showDate())
        date->show();
    else
        date->hide();

    emit(updateLayout());
}


void ClockApplet::slotUpdate()
{
    // timer fires every second, update date-label when
    // necessary...
    if (lastDate != QDate::currentDate()) {
        lastDate = QDate::currentDate();
        date->setText(KGlobal::locale()->formatDate(lastDate, true));

        QString dateStr = KGlobal::locale()->formatDate(lastDate, false);
        QToolTip::add(clock,dateStr);
        QToolTip::add(date,dateStr);
    }
}


void ClockApplet::slotCalendarDeleted()
{
    cal = 0L;
    // don't reopen the calendar immediately ...
    disableCalendar = true;
    QTimer::singleShot(100, this, SLOT(slotEnableCalendar()));
}



void ClockApplet::slotEnableCalendar()
{
    disableCalendar = false;
}


void ClockApplet::openCalendar()
{
    if (disableCalendar)
        return;

    cal = new DatePicker(this);
    connect( cal, SIGNAL( destroyed() ), SLOT( slotCalendarDeleted() ));
    Direction d  = popupDirection();
    QRect deskR = QApplication::desktop()->rect();

    // some extra spacing is included if aligned on a desktop edge
    QPoint c = mapToGlobal(pos());;

    if (d == KPanelApplet::Up){
        c.setY(c.y()-cal->sizeHint().height()-2);
        if(c.x() + cal->sizeHint().width() > deskR.right())
            c.setX(deskR.right()-cal->sizeHint().width()-1);
    }
    else if (d == KPanelApplet::Down){
        c.setY(c.y()+height()+2);
        if(c.x() + cal->sizeHint().width() > deskR.right())
            c.setX(deskR.right()-cal->sizeHint().width()-1);
    }
    else if (d == KPanelApplet::Right){
        c.setX(c.x()+width()+2);
        if(c.y() + cal->sizeHint().height() > deskR.bottom())
            c.setY(deskR.bottom()-cal->sizeHint().height()-1);
    }
    else{ // left
        c.setX(c.x()-cal->sizeHint().width()-2);
        if(c.y() + cal->sizeHint().height() > deskR.bottom())
            c.setY(deskR.bottom()-cal->sizeHint().height()-1);
    }

    cal->move(c);
    qApp->processEvents(); // make sure it is moved before showing
    cal->show();
}


void ClockApplet::openContextMenu()
{
    KPopupMenu *copyMenu = new KPopupMenu();
    KLocale *loc = KGlobal::locale();

    QDateTime dt = QDateTime::currentDateTime();
    copyMenu->insertItem( loc->formatDateTime( dt ) );
    copyMenu->insertItem( loc->formatDate( dt.date() ) );
    copyMenu->insertItem( loc->formatDate( dt.date(), true ) );
    copyMenu->insertItem( loc->formatTime( dt.time() ) );
    copyMenu->insertItem( loc->formatTime( dt.time(), true ) );
    copyMenu->insertItem( dt.date().toString() );
    copyMenu->insertItem( dt.time().toString() );
    copyMenu->insertItem( dt.toString() );
    connect( copyMenu, SIGNAL( activated(int) ), this, SLOT( copyMenuActivated(int) ) );

    KPopupMenu *menu = new KPopupMenu();
    menu->insertTitle( SmallIcon( "clock" ), i18n( "Clock" ) );

    KPopupMenu *type = new KPopupMenu(menu);
    type->insertItem( i18n( "&Digital" ), 0, 1 );
    type->insertItem( i18n( "&Analog" ), 1, 2 );
    type->insertItem( i18n( "&Fuzzy" ), 2, 3 );
    if (settings->type() == ClockSettings::Digital)    
        type->setItemChecked(0, true);
    else
    if (settings->type() == ClockSettings::Analog)
        type->setItemChecked(1, true);
    else
        type->setItemChecked(2, true);

    menu->insertItem(i18n("&Type"), type, 3, 1);
    menu->insertItem(SmallIcon("configure"), i18n("&Preferences..."), 4, 2);
    menu->insertItem(SmallIcon("date"), i18n("&Adjust Date && Time..."), 5, 3);
    menu->insertItem(i18n("Date && Time &Format..."), 6, 4);
    menu->insertItem(SmallIcon("editcopy"), i18n("&Copy"), copyMenu, 7, 5 );

    int result = menu->exec( QCursor::pos() );

    if ( result == 0 ) {
        settings->setType(ClockSettings::Digital);        
        settings->writeSettings();
        slotApplySettings();
    }
    else if ( result == 1 ) {
        settings->setType(ClockSettings::Analog);
        settings->writeSettings();
        slotApplySettings();
    }
    else if ( result == 2 ) {
        settings->setType(ClockSettings::Fuzzy);
        settings->writeSettings();
        slotApplySettings();
    }
    else if ( result == 4 ) {
        settings->openPreferences();
    }
    else if( result == 5 ) {
        KProcess proc;
        proc << locate("exe", "kdesu");
        proc << locate("exe", "kcmshell");
        proc << "clock";
        proc.start(KProcess::DontCare);
    }
    else if( result == 6 ) {
        KProcess proc;
        proc << locate("exe", "kcmshell");
        proc << "language";
        proc.start(KProcess::DontCare);
    }

    delete copyMenu;
    delete menu;
}


void ClockApplet::copyMenuActivated( int id )
{
  QPopupMenu *m = (QPopupMenu *) sender();
  QString s = m->text( id );
  QApplication::clipboard()->setText( s );
}


void ClockApplet::mousePressEvent(QMouseEvent *ev)
{
    if (ev->button() == QMouseEvent::LeftButton)
        openCalendar();
    if (ev->button() == QMouseEvent::RightButton)
        openContextMenu();
}


void ClockApplet::resizeEvent( QResizeEvent *)
{
    emit(layoutChanged());
}


// catch the mouse clicks of our child widgets
bool ClockApplet::eventFilter( QObject *o, QEvent *e )
{
    if ( ( o == clock || o == date ) && e->type() == QEvent::MouseButtonPress ) {
	mousePressEvent(static_cast<QMouseEvent*>(e) );
	return TRUE;
    }

    return KPanelApplet::eventFilter(o, e);
}

#include "clock.moc"
