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

Copyright (c) 1996-2002 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 <cstdlib>
#include <ctime>
#include <time.h>

#include <qcheckbox.h>
#include <qcursor.h>
#include <qgroupbox.h>
#include <qimage.h>
#include <qpainter.h>
#include <qtimer.h>
#include <qtooltip.h>
#include <qclipboard.h>
#include <qtabwidget.h>
#include <qwidgetstack.h>
#include <qcombobox.h>

#include <kdebug.h>
#include <kcolorbutton.h>
#include <kiconloader.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <kprocess.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kstringhandler.h>
#include <kfiledialog.h>
#include <kfontrequester.h>
#include <kglobalsettings.h>
#include <kconfigdialogmanager.h>

#include "clock.h"
#include "datepicker.h"
#include "zone.h"
#include "analog.h"
#include "digital.h"
#include "fuzzy.h"
#include "prefs.h"

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

// Settings

KConfigDialogSingle::KConfigDialogSingle(Zone *zone, QWidget *parent,
					 const char *name, Prefs * prefs,
					 KDialogBase::DialogType dialogType,
                                         bool modal) :
    KConfigDialog(parent, name, prefs, dialogType,
	          KDialogBase::Default | KDialogBase::Ok |
	          KDialogBase::Apply | KDialogBase::Cancel,
	          KDialogBase::Ok,
                  modal), _prefs(prefs)
{
    // As a temporary mesure until the kicker applet's app name is set to the
    // applets name so KDialogBase gets the right info.
    setPlainCaption(i18n("Configure - Clock"));
    setIcon(SmallIcon("date"));

    settings = new SettingsWidgetImp(prefs, zone, 0, "General");
    connect(settings->kcfg_Type, SIGNAL(activated(int)), SLOT(selectPage(int)));

    // Digital
    digitalPage = new DigitalWidget(0, "DigitalClock");
    settings->widgetStack->addWidget(digitalPage, 1);

    // Analog
    analogPage = new AnalogWidget(0, "AnalogClock");
    settings->widgetStack->addWidget(analogPage, 2);

    // Fuzzy
    fuzzyPage = new FuzzyWidget(0, "FuzzyClock");
    settings->widgetStack->addWidget(fuzzyPage, 3);

    connect(settings->kcfg_PlainShowDate, SIGNAL(toggled(bool)),
            SLOT(dateToggled()));
    connect(digitalPage->kcfg_DigitalShowDate, SIGNAL(toggled(bool)),
            SLOT(dateToggled()));
    connect(analogPage->kcfg_AnalogShowDate, SIGNAL(toggled(bool)),
            SLOT(dateToggled()));
    connect(fuzzyPage->kcfg_FuzzyShowDate, SIGNAL(toggled(bool)),
            SLOT(dateToggled()));

    addPage(settings, i18n("General"), QString::fromLatin1("package_settings"));
}

void KConfigDialogSingle::updateSettings()
{
    settings->OkApply();
}

void KConfigDialogSingle::updateWidgets()
{
    selectPage( _prefs->type() );
}

void KConfigDialogSingle::updateWidgetsDefault()
{
    KConfigSkeletonItem *item = _prefs->findItem("Type");
    item->swapDefault();
    selectPage( _prefs->type() );
    item->swapDefault();
    // This is ugly, but kcfg_Type does not have its correct setting
    // at this point in time.
    QTimer::singleShot(0, this, SLOT(dateToggled()));
}

void KConfigDialogSingle::selectPage(int p)
{
    settings->widgetStack->raiseWidget( p );
    dateToggled();
}

void KConfigDialogSingle::dateToggled()
{
    bool showDate;
    switch( settings->kcfg_Type->currentItem() )
    {
      case Prefs::EnumType::Plain:
         showDate = settings->kcfg_PlainShowDate->isChecked();
         break;
      case Prefs::EnumType::Digital:
         showDate = digitalPage->kcfg_DigitalShowDate->isChecked();
         break;
      case Prefs::EnumType::Analog:
         showDate = analogPage->kcfg_AnalogShowDate->isChecked();
         break;
      case Prefs::EnumType::Fuzzy:
      default:
         showDate = fuzzyPage->kcfg_FuzzyShowDate->isChecked();
         break;
    }
    settings->dateBox->setEnabled(showDate);
}

SettingsWidgetImp::SettingsWidgetImp(Prefs *p, Zone *z, QWidget* parent, const char* name, WFlags fl) :
    SettingsWidget(parent, name, fl), prefs(p), zone(z)
{
    zone->readZoneList(tzListView);
}

void SettingsWidgetImp::OkApply()
{
    zone->getSelectedZonelist(tzListView);
}

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


ClockWidget::ClockWidget(ClockApplet *applet, Prefs* prefs)
    : _applet(applet), _prefs(prefs)
{}


ClockWidget::~ClockWidget()
{}


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


PlainClock::PlainClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name)
    : QLabel(parent, name), ClockWidget(applet, prefs), showSeconds(false)
{
    loadSettings();
    updateClock();
}


int PlainClock::preferedWidthForHeight(int ) const
{
    QString maxLengthTime = KGlobal::locale()->formatTime( QTime( 23, 59 ), showSeconds);
    return fontMetrics().width( maxLengthTime+2 );
}


int PlainClock::preferedHeightForWidth(int /*w*/) const
{
    return fontMetrics().lineSpacing();
}


void PlainClock::updateClock()
{
    QString newStr =
      KGlobal::locale()->formatTime(_applet->clockGetTime(), showSeconds);

    if (newStr != _timeStr) {
        _timeStr = newStr;
        setText(_timeStr);
    }
}

void PlainClock::forceUpdate()
{
    _timeStr = QString::null;
}

void PlainClock::loadSettings()
{
    showSeconds = _prefs->plainShowSeconds();

    setFrameStyle(Panel | (_prefs->plainShowFrame() ? Sunken : 0));
    setAlignment(AlignVCenter | AlignHCenter | SingleLine);

    setFont(_prefs->plainFont());
    QPalette pal = palette();
    pal.setColor( QColorGroup::Foreground, _prefs->plainForegroundColor());
    pal.setColor( QColorGroup::Background, _prefs->plainBackgroundColor());
    setPalette( pal );
}

bool PlainClock::showDate()
{
    return _prefs->plainShowDate();
}

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


DigitalClock::DigitalClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name)
    : QLCDNumber(parent, name), ClockWidget(applet, prefs)
{
    loadSettings();
    updateClock();
}


DigitalClock::~DigitalClock()
{
    delete _buffer;
}


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


int DigitalClock::preferedHeightForWidth(int w) const
{
    if (w < 0) w = 0;
    return((w / numDigits() * 2) + 6);
}


void DigitalClock::updateClock()
{
    static bool colon = true;
    QString newStr;
    QTime t(_applet->clockGetTime());

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

    QString format("%02d");

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

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

    if (KGlobal::locale()->use12Clock()) {
        if (h > 12)
            h -= 12;
        else if( h == 0)
            h = 12;

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


    if (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 (blink)
        colon = !colon;
}

void DigitalClock::forceUpdate()
{
    _timeStr = QString::null;
}

void DigitalClock::loadSettings()
{
    showSeconds = _prefs->digitalShowSeconds();
    blink = _prefs->digitalBlink();
    showFrame =  _prefs->digitalShowFrame();
    lcdStyle = _prefs->digitalLCDStyle();
    foreColor = _prefs->digitalForegroundColor();
    shadowColor = _prefs->digitalShadowColor();

    setFrameStyle(Panel | (showFrame ? Sunken : 0));
    setMargin( 4 );
    setSegmentStyle(QLCDNumber::Flat);
    if (lcdStyle)
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
    else
        setBackgroundColor(_prefs->digitalBackgroundColor());

    setNumDigits(showSeconds ? 8:5);

    _buffer = new QPixmap( width(), height() );
}

void DigitalClock::paintEvent( QPaintEvent*)
{
    _buffer->fill( this, 0, 0 );
    QPainter p( _buffer );
    drawContents( &p );
    if (showFrame)
        drawFrame( &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 (lcdStyle)
        pal.setColor( QColorGroup::Foreground, QColor(128,128,128));
    else
        pal.setColor( QColorGroup::Foreground, shadowColor);
    setPalette( pal );
    p->translate( +1, +1 );
    QLCDNumber::drawContents( p );
    if (lcdStyle)
        pal.setColor( QColorGroup::Foreground, Qt::black);
    else
        pal.setColor( QColorGroup::Foreground, foreColor);
    setPalette( pal );
    p->translate( -2, -2 );
    setUpdatesEnabled( TRUE );
    QLCDNumber::drawContents( p );
    p->translate( +1, +1 );
}


// 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 (lcdStyle)
        setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
}

bool DigitalClock::showDate()
{
    return _prefs->digitalShowDate();
}


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


AnalogClock::AnalogClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name)
    : QFrame(parent, name), ClockWidget(applet, prefs), _spPx(NULL)
{
    loadSettings();
}


AnalogClock::~AnalogClock()
{
    delete _spPx;
}

void AnalogClock::initBackgroundPixmap()
{
    //if no antialiasing, use pixmap as-is
    if (antialiasFactor == 1)
    {
      setBackgroundPixmap(KIconLoader("clockapplet").loadIcon("lcd",KIcon::User));
      _bgScale = 1;
    }
    else
    {
        //make a scaled pixmap -- so when image is reduced it'll look "OK".
        _bgScale = antialiasFactor;
        QImage bgImage = KIconLoader("clockapplet").loadIcon("lcd", KIcon::User).convertToImage();
        QPixmap bgPixmap(bgImage.scale(bgImage.width() * _bgScale,
                         bgImage.height() * _bgScale));

        setBackgroundPixmap(bgPixmap);
    }
}

void AnalogClock::updateClock()
{
    if (!showSeconds)
         if (_time.minute() == _applet->clockGetTime().minute())
            return;

    _time = _applet->clockGetTime();
    repaint( false ); //don't erase on redraw
}

void AnalogClock::forceUpdate()
{
    _time = QTime(0,0);
}

void  AnalogClock::loadSettings()
{
    showFrame =  _prefs->analogShowFrame();
    setFrameStyle(Panel | (showFrame ? Sunken : 0));
    lcdStyle = _prefs->analogLCDStyle();
    antialiasFactor = _prefs->analogAntialias()+1;
    showSeconds = _prefs->analogShowSeconds();
    backColor = _prefs->analogBackgroundColor();
    foreColor = _prefs->analogForegroundColor();
    shadowColor = _prefs->analogShadowColor();

    if (lcdStyle)
    {
        initBackgroundPixmap();
    }
    else
    {
        setBackgroundMode(NoBackground); //prevent flicker
    }

    _time = _applet->clockGetTime();
    _spPx = new QPixmap(size().width() * antialiasFactor,
                       size().height() * antialiasFactor);

    repaint();
}

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

    int aaFactor = antialiasFactor;
    int spWidth = size().width() * aaFactor;
    int spHeight = size().height() * aaFactor;

    if ((spWidth != _spPx->size().width()) ||
        (spHeight != _spPx->size().height()))
    {
        delete _spPx;
        _spPx = new QPixmap(spWidth, spHeight);
    }


    QPainter paint;
    paint.begin( _spPx );

    if (lcdStyle)
    {
        //check to see if antialiasing has changed -- bg pixmap will need
        //to be re-created
        if (_bgScale != aaFactor)
        {
            initBackgroundPixmap();
        }
        _spPx->fill(this, 0, 0); //apply bitmap/bg fill
    }
    else
    {
       _spPx->fill(backColor);
    }

    QPointArray pts;
    QPoint cp(spWidth / 2, spHeight / 2);

    int d = QMIN(spWidth,spHeight)-(10 * aaFactor);

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

    paint.setViewport(2,2,spWidth,spHeight);

    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 (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 (lcdStyle) {
            paint.setPen( Qt::black );
            paint.setBrush( Qt::black );
        } else {
            paint.setPen( foreColor );
            paint.setBrush( foreColor );
        }

        paint.setViewport(0,0,spWidth,spHeight);
    }
    paint.end();

    QPainter paintFinal;
    paintFinal.begin(this);

    if (aaFactor != 1)
    {
        QImage spImage = _spPx->convertToImage();
        QImage displayImage = spImage.smoothScale(size());

        paintFinal.drawImage(0, 0, displayImage);
    }
    else
    {
        paintFinal.drawPixmap(0, 0, *_spPx);
    }

    if (showFrame)
        drawFrame( &paintFinal );
}


// the background pixmap disappears during a style change
void AnalogClock::styleChange(QStyle &)
{
    if (lcdStyle)
    {
       initBackgroundPixmap();
    }
}

bool AnalogClock::showDate()
{
    return _prefs->analogShowDate();
}


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


FuzzyClock::FuzzyClock(ClockApplet *applet, Prefs *prefs, QWidget *parent, const char *name)
    : QFrame(parent, name), ClockWidget(applet, prefs)
{
    loadSettings();
    hourNames   << 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");

    // 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
    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");

    dayTime << i18n("Night")
            << i18n("Early morning") << i18n("Morning") << i18n("Almost noon")
            << i18n("Noon") << i18n("Afternoon") << i18n("Evening")
            << i18n("Late evening");

    _time = _applet->clockGetTime();
    alreadyDrawing=false;
    repaint();
}

void FuzzyClock::deleteMyself() 
{
    if(alreadyDrawing) // try again later
        QTimer::singleShot(1000, this, SLOT(deleteMyself()));
    else
        delete this;
}


int FuzzyClock::preferedWidthForHeight(int ) const
{
    QFontMetrics fm(fontFuz);
    return fm.width(_timeStr) + 8;
}


int FuzzyClock::preferedHeightForWidth(int ) const
{
    QFontMetrics fm(fontFuz);
    return fm.width(_timeStr) + 8;
}


void FuzzyClock::updateClock()
{
  if (_time.hour() == _applet->clockGetTime().hour() &&
      _time.minute() == _applet->clockGetTime().minute())
     return;

  _time = _applet->clockGetTime();
  repaint();
}

void FuzzyClock::forceUpdate()
{
    _time = QTime(0,0);
}

void FuzzyClock::loadSettings(){
    foreColor = _prefs->fuzzyForegroundColor();
    showFrame = _prefs->fuzzyShowFrame();
    fuzzyness = _prefs->fuzzyness();
    setFrameStyle(Panel | (showFrame ? Sunken : 0));
    setBackgroundColor(_prefs->fuzzyBackgroundColor());
    fontFuz = _prefs->fuzzyFont();
}

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

    if(!_applet) 
        return;

    alreadyDrawing = true;
    QString newTimeStr;

    if (fuzzyness == 1 || fuzzyness == 2) {
      int minute = _time.minute();
      int sector = 0;
      int realHour = 0;

      if (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("%");
      if (phStart >= 0) { // protect yourself from translations
          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
          }
          if (phStart >= 0)
              newTimeStr.replace(phStart, phLength, hourNames[realHour]);
          newTimeStr.replace(0, 1, QString(newTimeStr.at(0).upper()));
      }
    } else if (fuzzyness == 3) {
        newTimeStr = dayTime[_time.hour() / 3];
    } else {
        int dow = _applet->clockGetDate().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;
        _applet->resizeRequest();
    }

    p->setFont(fontFuz);
    p->setPen(foreColor);
    if (_applet->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);
    }
    alreadyDrawing = false;
}

bool FuzzyClock::showDate()
{
    return _prefs->fuzzyShowDate();
}


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


ClockApplet::ClockApplet(const QString& configFile, Type t, int actions,
                         QWidget *parent, const char *name)
    : KPanelApplet(configFile, t, actions, parent, name),
      _calendar(0),
      _disableCalendar(false),
      _clock(0),
      _prefs(new Prefs(sharedConfig())),
      zone(new Zone(config())),
      menu(0),
      m_tooltip(this)
{
    _prefs->readConfig();
    configFileName = configFile.latin1();
    TZoffset = zone->calc_TZ_offset( zone->zone() );
    setBackgroundOrigin( QWidget::AncestorOrigin );

    _date = new QLabel(this);
    _date->setAlignment(AlignVCenter | AlignHCenter | WordBreak);
    _date->setBackgroundOrigin( QWidget::AncestorOrigin );
    _date->installEventFilter(this);   // catch mouse clicks

    slotApplySettings();    // initialize clock widget
    slotUpdate();

    _timer = new QTimer(this);
    connect(_timer, SIGNAL(timeout()), SLOT(slotUpdate()));
    _timer->start(500);

    if (kapp->authorizeKAction("kicker_rmb"))
    {
        menu = new KPopupMenu();
        connect(menu, SIGNAL(aboutToShow()), SLOT(aboutToShowContextMenu()));
        connect(menu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int)));
        setCustomMenu(menu);
    }
}


ClockApplet::~ClockApplet()
{
    //reverse for the moment 
    KGlobal::locale()->removeCatalogue("clockapplet");
    KGlobal::locale()->removeCatalogue("timezones"); // For time zone translations

    delete _prefs; _prefs = 0;
    delete zone; zone = 0;
    delete menu; menu = 0;
    if (_calendar)
        _calendar->close();
    config()->sync();
}

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

    int clockWidth = _clock->preferedWidthForHeight(h-shareDateHeight);

    int w;
    if (!showDate) {
        w = clockWidth;
        if (applyGeometry) {
            _clock->widget()->setFixedSize(w, h);
        }
    } else {
        int dateWidth = _date->sizeHint().width() + 4;
        if (dateToSide) {
            w = clockWidth + dateWidth;
            if (applyGeometry) {
                _clock->widget()->setFixedSize(clockWidth, h);
                _date->setFixedSize(dateWidth, h);
                _date->move(clockWidth, 0);
            }
        } else {
            w = (clockWidth > dateWidth ? clockWidth : dateWidth);
            if (applyGeometry) {
                _clock->widget()->setFixedSize(w, h - shareDateHeight);
                _clock->widget()->setMinimumSize(w,h - shareDateHeight);
		_date->setFixedSize(w, shareDateHeight);
                _date->move(0, _clock->widget()->height());
            }
        }
    }

    return w;
}


int ClockApplet::heightForWidth(int w) const
{
    bool applyGeometry = (position() == pLeft) || (position() == pRight);
    int clockHeight = _clock->preferedHeightForWidth(w);

    if (applyGeometry) {
        _clock->widget()->setFixedSize(w, clockHeight);
    }

    // add some (8) pixel in height for date, if visible
    if (showDate) {
        if (applyGeometry) {
            _date->setFixedSize(w, _date->minimumSizeHint().height() + 8);
            _date->move(0, clockHeight);
        }
        clockHeight += _date->height();

    }

    return clockHeight;
}

void ClockApplet::preferences()
{
    preferences(false);
}

void ClockApplet::preferences(bool timezone)
{
  KConfigDialogSingle *dialog = dynamic_cast<KConfigDialogSingle*>(KConfigDialog::exists(configFileName));

  if (!dialog)
  {
    dialog = new KConfigDialogSingle(zone, this, configFileName, _prefs, KDialogBase::Swallow );
    connect(dialog, SIGNAL(settingsChanged()), this, SLOT(slotApplySettings()));
  }

  if (timezone)
  {
      dialog->settings->tabs->setCurrentPage(1);
  }

  dialog->show();
}

void ClockApplet::slotApplySettings()
{
    // ugly workaround for FuzzyClock: sometimes FuzzyClock
    // hasn't finished drawing when getting deleted, so we
    // ask FuzzyClock to delete itself appropriately
    if( _clock && _clock->widget()->inherits("FuzzyClock") ) {
        FuzzyClock* f = static_cast<FuzzyClock*>(_clock);
        f->deleteMyself();
    }
    else
        delete _clock;

    switch (_prefs->type()) {
        case Prefs::EnumType::Plain:
            _clock = new PlainClock(this, _prefs, this);
            break;
        case Prefs::EnumType::Analog:
            _clock = new AnalogClock(this, _prefs, this);
            break;
        case Prefs::EnumType::Fuzzy:
            _clock = new FuzzyClock(this, _prefs, this);
            break;
        case Prefs::EnumType::Digital:
        default:
            _clock = new DigitalClock(this, _prefs, this);
            break;
    }

    // See if the clock wants to show the date.
    showDate = _clock->showDate();
    if(showDate){
        dateFont = _prefs->dateFont();
        dateForeColor = _prefs->dateForegroundColor();
        QColor dateBackColor = _prefs->dateBackgroundColor();
        _date->setBackgroundColor(dateBackColor);

        _date->setFont(dateFont);
        updateDateLabel();
        QPalette pal = _date->palette();
        pal.setColor(QColorGroup::Foreground, dateForeColor);
        pal.setColor(QColorGroup::Background, dateBackColor);
        _date->setPalette(pal);
    }
    else
    {
        zone->resetZone();
	updateDateLabel();
    }
    TZoffset = zone->calc_TZ_offset( zone->zone() );

    setBackground();

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

    _clock->forceUpdate(); /* force repaint */
    _clock->updateClock();

    if (showDate) {
        _date->show();
        _date->repaint(true);
    } else
        _date->hide();

    emit(updateLayout());
}

void ClockApplet::setBackground()
{
    bool toreset = true;

    // if the clock has the same background color that we do,
    // let's propagate the background brush too
    if ((!_clock->widget()->paletteBackgroundPixmap() &&
        _clock->widget()->paletteBackgroundColor() == paletteBackgroundColor())
        || paletteBackgroundPixmap())
    {
        _clock->widget()->setBackgroundOrigin(AncestorOrigin);
        const QPixmap* brush = paletteBackgroundPixmap();

        if (brush)
        {
	    toreset = false;
            _clock->widget()->setPaletteBackgroundPixmap(*brush);

            if (_date && _date->palette().active().background() == palette().active().background())
            {
                _date->setBackgroundOrigin(AncestorOrigin);
                _date->setPaletteBackgroundPixmap(*brush);
            }
        }
    }
   
    if (toreset) {
        _clock->widget()->setPaletteBackgroundPixmap(QPixmap());
        _date->setPaletteBackgroundPixmap(QPixmap());
    }

}

void ClockApplet::slotUpdate()
{
  if (_lastDate != clockGetDate())
    updateDateLabel();

  _clock->updateClock();
}

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


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

void ClockApplet::toggleCalendar()
{
    if (_calendar && !_disableCalendar) {
        // calls slotCalendarDeleted which does the cleanup for us
        _calendar->close();
        return;
    }
    if (_calendar || _disableCalendar)
        return;
    _calendar = new DatePicker(this, _lastDate, _prefs);
    connect( _calendar, SIGNAL( destroyed() ), SLOT( slotCalendarDeleted() ));

    QSize size = _prefs->calendarSize();
    int w;
    int h;

    if (size != QSize())
    {
        _calendar->resize(size);
        // Add 28 px. to width poperly as said in API
        w = size.width() + 28;
        h = size.height();
    }
    else
    {
        // Add 28 px. to width poperly as said in API
        w = _calendar->sizeHint().width() + 28;
        h = _calendar->sizeHint().height();
    }

    QPoint c = mapToGlobal(QPoint(0,0));

    // some extra spacing is included if aligned on a desktop edge
    switch (position()) {
        case KPanelApplet::pLeft:
            c.setX(c.x()+width()+2);
            break;
        case KPanelApplet::pRight:
            c.setX(c.x()-w-2);
            break;
        case KPanelApplet::pTop:
            c.setY(c.y()+height()+2);
            break;
        case KPanelApplet::pBottom:
            c.setY(c.y()-h-2);
            break;
    }

    // make calendar fully visible
    QRect deskR = KGlobalSettings::desktopGeometry(c);

    if (c.y()+h > deskR.bottom())
        c.setY(deskR.bottom()-h-1);
    if (c.x()+w > deskR.right())
        c.setX(deskR.right()-w-1);

    _calendar->move(c);
    _calendar->show();
}


void ClockApplet::openContextMenu()
{
    if (!menu || !kapp->authorizeKAction("kicker_rmb"))
        return;

    menu->exec( QCursor::pos() );
}

void ClockApplet::contextMenuActivated(int result)
{
    if ((result >= 0) && (result < 100)) {
        _prefs->setType(result);
        _prefs->writeConfig();
        slotApplySettings();
        return;
    };

    if ((result >= 500) && (result < 600)) {
        showZone(result-500);
        return;
    };

    KProcess proc;
    switch (result) {
      case 102:
          preferences();
          break;
      case 103:
          proc << locate("exe", "kdesu");
          proc << "--nonewdcop";
          proc << QString("%1 clock --lang %2")
                  .arg(locate("exe", "kcmshell"))
                  .arg(KGlobal::locale()->language());
          proc.start(KProcess::DontCare);
          break;
      case 104:
          proc << locate("exe", "kcmshell");
          proc << "language";
              proc.start(KProcess::DontCare);
          break;
      case 110:
          preferences(true);
          break;
    } /* switch() */
}

void ClockApplet::aboutToShowContextMenu()
{
    bool bImmutable = config()->isImmutable();

    menu->clear();
    menu->insertTitle( SmallIcon( "clock" ), i18n( "Clock" ) );

    KLocale *loc = KGlobal::locale();
    QDateTime dt = QDateTime::currentDateTime();
    dt = dt.addSecs(TZoffset);

    KPopupMenu *copyMenu = new KPopupMenu( menu );
    copyMenu->insertItem(loc->formatDateTime(dt), 201);
    copyMenu->insertItem(loc->formatDate(dt.date()), 202);
    copyMenu->insertItem(loc->formatDate(dt.date(), true), 203);
    copyMenu->insertItem(loc->formatTime(dt.time()), 204);
    copyMenu->insertItem(loc->formatTime(dt.time(), true), 205);
    copyMenu->insertItem(dt.date().toString(), 206);
    copyMenu->insertItem(dt.time().toString(), 207);
    copyMenu->insertItem(dt.toString(), 208);
    connect( copyMenu, SIGNAL( activated(int) ), this, SLOT( slotCopyMenuActivated(int) ) );

    if (!bImmutable)
    {
        KPopupMenu *zoneMenu = new KPopupMenu( menu );
        connect(zoneMenu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int)));
        int i;
        for (i=0; i<=zone->remoteZoneCount(); i++)
            zoneMenu->insertItem(i==0 ? i18n("Local Timezone")
			:i18n(zone->zone(i).utf8()).replace("_", " "), 500+i);
        zoneMenu->setItemChecked(500+zone->zoneIndex(),true);
        zoneMenu->insertSeparator();
        zoneMenu->insertItem(SmallIcon("configure"), i18n("&Configure Timezones..."), 110);

        KPopupMenu *type_menu = new KPopupMenu(menu);
        connect(type_menu, SIGNAL(activated(int)), SLOT(contextMenuActivated(int)));
        type_menu->insertItem(i18n("&Plain"), Prefs::EnumType::Plain, 1);
        type_menu->insertItem(i18n("&Digital"), Prefs::EnumType::Digital, 2);
        type_menu->insertItem(i18n("&Analog"), Prefs::EnumType::Analog, 3);
        type_menu->insertItem(i18n("&Fuzzy"), Prefs::EnumType::Fuzzy, 4);
        type_menu->setItemChecked(_prefs->type(),true);

        menu->insertItem(i18n("&Type"), type_menu, 101, 1);
        menu->insertItem(i18n("Show Time&zone"), zoneMenu, 110, 2);
        if (kapp->authorize("user/root"))
        {
            menu->insertItem(SmallIcon("date"), i18n("&Adjust Date && Time..."), 103, 4);
        }
        menu->insertItem(SmallIcon("kcontrol"), i18n("Date && Time &Format..."), 104, 5);
    }

    menu->insertItem(SmallIcon("editcopy"), i18n("C&opy to Clipboard"), copyMenu, 105, 6);
    if (!bImmutable)
    {
        menu->insertSeparator(7);
        menu->insertItem(SmallIcon("configure"), i18n("&Configure Clock..."), 102, 8);
    }
}


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

QTime ClockApplet::clockGetTime()
{
    return QTime::currentTime().addSecs(TZoffset);
}

QDate ClockApplet::clockGetDate()
{
    return QDateTime::currentDateTime().addSecs(TZoffset).date();
}

void ClockApplet::showZone(int z)
{
    zone->resetZone(z);
    TZoffset = zone->calc_TZ_offset( zone->zone() );
    updateDateLabel();
    _clock->forceUpdate(); /* force repaint */
    slotUpdate();
    zone->writeSettings();
}

void ClockApplet::nextZone()
{
    if (!showDate)
	return;
    zone->nextZone();
    showZone(zone->zoneIndex());
}

void ClockApplet::prevZone()
{
    if (!showDate)
	return;
    zone->prevZone();
    showZone(zone->zoneIndex());
}

void ClockApplet::mousePressEvent(QMouseEvent *ev)
{
    switch (ev->button()) {
	case QMouseEvent::LeftButton:
		toggleCalendar();
		break;
	case QMouseEvent::RightButton:
		openContextMenu();
		break;
	case QMouseEvent::MidButton:
		nextZone();
		QToolTip::remove(_clock->widget());
		break;
        default:
            break;
    }
}

void ClockApplet::wheelEvent(QWheelEvent* e)
{
    if( e->delta() < 0 )
	prevZone();
    else
	nextZone();
    QToolTip::remove(_clock->widget());
}

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

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

void ClockApplet::updateDateLabel()
{
    _lastDate = clockGetDate();

    if (zone->zoneIndex())
    {
	QString zone_s = i18n(zone->zone().utf8());
	_date->setText(
          zone_s.mid(zone_s.find('/') + 1).replace("_", " ")
	);
    }
    else
    {
	QString dateStr = KGlobal::locale()->formatDate(_lastDate, true);
	if ( position() == KPanelApplet::pLeft ||
	     position() == KPanelApplet::pRight ) {
	  /* search year number (4 digits) and insert a space for line-break */
	  int p = dateStr.find(QRegExp("\\d\\d\\d\\d"));
	  if (p==0) dateStr[4]=' '; else if (p>=0) dateStr.insert(p," ");
	}
	_date->setText(dateStr);
    }
    QTimer::singleShot(0, this, SLOT(fixupLayout()));
}


void ClockApplet::fixupLayout() {
    _date->resize(_date->minimumSizeHint());
    emit updateLayout();
}

int ClockApplet::type() { return _prefs->type(); }


ClockAppletToolTip::ClockAppletToolTip( ClockApplet* clock ) : QToolTip( clock ), m_clock( clock ) {}

void ClockAppletToolTip::maybeTip( const QPoint & /*point*/ )
{
    if (m_clock->type() == Prefs::EnumType::Fuzzy)
    {
        // show full time (incl. hour) as tooltip for Fuzzy clock
        tip(m_clock->geometry(), KGlobal::locale()->formatDateTime(QDateTime::currentDateTime().addSecs(m_clock->TZoffset), false));
    }
    else
    {
        tip(m_clock->geometry(), KGlobal::locale()->formatDate(m_clock->clockGetDate(), false));
    }
}

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

#include "clock.moc"
