/*
 *  Copyright (c) 2004 Gunnar Schmi Dt <gunnar@schmi-dt.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 */

#include <qpainter.h>
#include <qtooltip.h>
#include <qdrawutil.h>
#include <qcursor.h>
#include <qimage.h>
#include <qpopupmenu.h>

#include <kaboutapplication.h>
#include <kpopupmenu.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kapplication.h>
#include <kiconloader.h>
#include <kiconeffect.h>
#include <knotifyclient.h>
#include <kshortcut.h>
#include <kkeynative.h>
#include <kdemacros.h>
#include "kdeexportfix.h"
#include "kbstate.h"
#include "kbstate.moc"

extern "C"
{
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
#define XK_MISCELLANY
#define XK_XKB_KEYS
#include <X11/keysymdef.h>
#include <X11/extensions/XKB.h>

    KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile)
    {
        KGlobal::locale()->insertCatalogue("kbstateapplet");
        KbStateApplet *applet = new KbStateApplet(configFile, KPanelApplet::Normal, KPanelApplet::About, parent, "kbstateapplet");
        return applet;
    }
}

struct ModifierKey {
   const unsigned int mask;
   const KeySym keysym;
   const char *name;
   const char *icon;
   const char *text;
   const char *lockedText;
   const char *latchedText;
   const char *unlatchedText;
};

static ModifierKey modifierKeys[] = {
    { ShiftMask, 0, I18N_NOOP("Shift"), "shiftkey", "",
      I18N_NOOP("The Shift key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Shift key is now active."),
      I18N_NOOP("The Shift key is now inactive.") },
    { ControlMask, 0, I18N_NOOP("Control"), "controlkey", "",
      I18N_NOOP("The Control key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Control key is now active."),
      I18N_NOOP("The Control key is now inactive.") },
    { 0, XK_Alt_L, I18N_NOOP("Alt"), "altkey", "",
      I18N_NOOP("The Alt key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Alt key is now active."),
      I18N_NOOP("The Alt key is now inactive.") },
    { 0, 0, I18N_NOOP("Win"), "superkey", "",
      I18N_NOOP("The Win key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Win key is now active."),
      I18N_NOOP("The Win key is now inactive.") },
    { 0, XK_Meta_L, I18N_NOOP("Meta"), "metakey", "",
      I18N_NOOP("The Meta key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Meta key is now active."),
      I18N_NOOP("The Meta key is now inactive.") },
    { 0, XK_Super_L, I18N_NOOP("Super"), "superkey", "",
      I18N_NOOP("The Super key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Super key is now active."),
      I18N_NOOP("The Super key is now inactive.") },
    { 0, XK_Hyper_L, I18N_NOOP("Hyper"), "hyperkey", "",
      I18N_NOOP("The Hyper key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Hyper key is now active."),
      I18N_NOOP("The Hyper key is now inactive.") },
    { 0, 0, I18N_NOOP("Alt Graph"), "key", I18N_NOOP("æ"),
      I18N_NOOP("The Alt Graph key has been locked and is now active for all of the following keypresses."),
      I18N_NOOP("The Alt Graph key is now active."),
      I18N_NOOP("The Alt Graph key is now inactive.") },
    { 0, XK_Num_Lock, I18N_NOOP("Num Lock"), "lockkey", I18N_NOOP("Num"),
      I18N_NOOP("The Num Lock key has been activated."),
      "",
      I18N_NOOP("The Num Lock key is now inactive.") },
    { LockMask, 0, I18N_NOOP("Caps Lock"), "capskey", "",
      I18N_NOOP("The Caps Lock key has been activated."),
      "",
      I18N_NOOP("The Caps Lock key is now inactive.") },
    { 0, XK_Scroll_Lock, I18N_NOOP("Scroll Lock"), "lockkey", I18N_NOOP("Scroll"),
      I18N_NOOP("The Scroll Lock key has been activated."),
      "",
      I18N_NOOP("The Scroll Lock key is now inactive.") },
    { 0, 0, "", "", "", "", "", "" }
};


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

KbStateApplet::KbStateApplet(const QString& configFile, Type t, int actions,
                       QWidget *parent, const char *name)
  : KPanelApplet( configFile, t, actions, parent, name )
{
   for (int i = 0; i < 8; i++) {
      icons[i] = 0;
   }
   instance = new KInstance ("kbstateapplet");
   loadConfig();
   initMasks();
   slow   = new TimeoutIcon (instance, "", this, "slow");

   //startTimer(100); // ten times a second
   connect(kapp, SIGNAL(kdisplayPaletteChanged()), SLOT(paletteChanged()));
   
   kapp->installX11EventFilter (this);
   int opcode_rtn, error_rtn;
   XkbQueryExtension (this->x11Display(), &opcode_rtn, &xkb_base_event_type,  &error_rtn, NULL, NULL);
   XkbSelectEvents (this->x11Display(), XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);
   
   buildPopupMenu();
}

KbStateApplet::~KbStateApplet() {
   kapp->removeX11EventFilter (this);
   setCustomMenu(0);
   delete instance;
   delete popup;
   delete sizePopup;
}
// Builds, connects _popup menu
void KbStateApplet::buildPopupMenu()
{
   sizePopup=new KPopupMenu(this);
   sizePopup->setCheckable( true );
   sizePopup->insertItem(i18n("Small"),  13);
   sizePopup->insertItem(i18n("Medium"), 20);
   sizePopup->insertItem(i18n("Large"),  26);
   connect(sizePopup,SIGNAL(activated(int)), this, SLOT(setIconDim(int)));

   showPopup=new KPopupMenu(this);
   showPopup->setCheckable( true );
   modifierItem=showPopup->insertItem(i18n("Modifier Keys"), this, SLOT(toggleModifier()));
   lockkeysItem=showPopup->insertItem(i18n("Lock Keys"),     this, SLOT(toggleLockkeys()));
   slowkeysItem=showPopup->insertItem(i18n("Slow Keys Status"), this, SLOT(toggleSlowkeys()));

   popup = new KPopupMenu(this);
   popup->insertTitle(0, i18n("Keyboard Status Applet"));
   popup->insertItem(i18n("Set Icon Size"),sizePopup);
   popup->insertItem(i18n("Show"),showPopup);
   popup->insertItem(i18n("Configure AccessX Features..."), this, SLOT(configureAccessX()));
   popup->insertSeparator();
   popup->insertItem(i18n("About"), this, SLOT(about()));
   setCustomMenu(popup);
   updateMenu();
}

void KbStateApplet::updateMenu()
{  if (popup) {
      popup->setItemChecked (modifierItem, showModifiers);
      popup->setItemChecked (lockkeysItem, showLockkeys);
      popup->setItemChecked (slowkeysItem, showSlowkeys);
      sizePopup->setItemChecked(13, size == 13);
      sizePopup->setItemChecked(20, size == 20);
      sizePopup->setItemChecked(26, size == 26);
   }
}

void calculateSizes (int space, int modifiers, int lockkeys, bool slowkeys,
                     int &lines, int &length, int &size)
// Calculates the layout based on a given number of modifiers, lockkeys and
// 0 or 1 icon for slowkeys.
// Output:
// lines:  number of lines
// length: number of icons per line
// size:   size of the icons
{
   // Calculate lines and length
   lines = space>=size ? space/size : 1;
   
   if (lines == 1)
      length = modifiers + lockkeys + (slowkeys?1:0);
   else {// => lines > 2
      length = (modifiers+lockkeys+(slowkeys?1:0)+(lines-1))/lines;
   
      // As we want to have some line breaks, we need to do some corrections:
      // Calculate the number of lines that are really needed:
      int linesNeeded = (modifiers+length-1)/length + (lockkeys+length-1)/length;
      if ((modifiers % length == 0) && (lockkeys % length == 0) && slowkeys)
         linesNeeded++;

      // If we more lines than we have, try with more icons per line:
      while (linesNeeded > lines) {
         length++;
         linesNeeded = (modifiers+length-1)/length + (lockkeys+length-1)/length;
         if ((modifiers % length == 0) && (lockkeys % length == 0) && slowkeys)
            linesNeeded++;
      }
      lines = linesNeeded;
      size = space/lines;
   }
}

int KbStateApplet::widthForHeight(int h) const {
   int lines, length;
   int size = this->size;

   calculateSizes (h, showModifiers?modifiers.count():0,
                      showLockkeys?lockkeys.count():0,
                      showSlowkeys, lines, length, size);

   return length*size;
}
int KbStateApplet::heightForWidth(int w) const {
   int lines, length;
   int size = this->size;

   calculateSizes (w, showModifiers?modifiers.count():0,
                      showLockkeys?lockkeys.count():0,
                      showSlowkeys, lines, length, size);

   return length*size;
}

void KbStateApplet::mousePressEvent(QMouseEvent *e) {
   if (e->button() == RightButton)
      popup->popup(e->globalPos());
}

void KbStateApplet::setIconDim (int size) {
   this->size = size;
   saveConfig();
   updateMenu();
   update();
   updateGeometry();
   emit updateLayout();
}

void KbStateApplet::resizeEvent( QResizeEvent*e ) {
   QWidget::resizeEvent(e);
   layout();
}

void KbStateApplet::layout() {
   int size = this->size;

   int lines, length, x,y,dx,dy, ldx,ldy;
   if (orientation() == Vertical) {
      calculateSizes (width(), showModifiers?modifiers.count():0,
                      showLockkeys?lockkeys.count():0,
                      showSlowkeys, lines, length, size);
      x  = (width()-lines*size)/2;
      y  = 0;
      dx = 0;
      dy = size;
      ldx= size;
      ldy= 0;
   }
   else {
      calculateSizes (height(), showModifiers?modifiers.count():0,
                      showLockkeys?lockkeys.count():0,
                      showSlowkeys, lines, length, size);
      x  = 0;
      y  = (height()-lines*size)/2;
      dx = size;
      dy = 0;
      ldx= 0;
      ldy= size;
   }
   
   StatusIcon *icon;
   int item = 1;
   for (icon = modifiers.first(); icon; icon = modifiers.next()) {
      if (showModifiers) {
         icon->setGeometry (x, y, size, size);
         icon->show();
         icon->update();
         item++; x+=dx; y+=dy;
         if (item > length) {
            x = x - length*dx + ldx;
            y = y - length*dy + ldy;
            item = 1;
         }
      }
      else
         icon->hide();
   }
   int numLockkeys = showLockkeys?lockkeys.count():0;
   if (showSlowkeys && (numLockkeys % length == 0)) {
      slow->setGeometry (x, y, size, size);
      slow->show();
      slow->update();
      item++; x+=dx; y+=dy;
   }
   else
      slow->hide();

   if (lines > 1) {
      if (item != 1) {
         x = x - (item-1)*dx + ldx;
         y = y - (item-1)*dy + ldy;
      }
      item = 1;
   }
   for (icon = lockkeys.first(); icon; icon = lockkeys.next()) {
      if (showLockkeys) {
         icon->setGeometry (x, y, size, size);
         icon->show();
         icon->update();
         item++; x+=dx; y+=dy;
         if (item > length) {
            x = x - length*dx + ldx;
            y = y - length*dy + ldy;
            item = 1;
         }
      }
      else
         icon->hide();
   }
   if (showSlowkeys && (numLockkeys % length != 0)) {
      slow->setGeometry (x, y, size, size);
      slow->show();
      slow->update();
   }
}

void KbStateApplet::paletteChanged() {
   for (int i = 0; i < 8; i++) {
      if (icons[i])
         icons[i]->updateImages();
   }
   slow->update();
}

void KbStateApplet::initMasks() {
   for (int i = 0; i < 8; i++) {
      if (icons[i])
         delete icons[i];
      icons[i] = 0;
   }
   state = 0;
   
   for (int i = 0; modifierKeys[i].name != ""; i++) {
      int mask = modifierKeys[i].mask;
      if (mask == 0)
         if (modifierKeys[i].keysym != 0)
            mask = XkbKeysymToModifiers (this->x11Display(), modifierKeys[i].keysym);
         else if (modifierKeys[i].name == "Win")
            mask = KKeyNative::modX(KKey::WIN);
         else
            mask = XkbKeysymToModifiers (this->x11Display(), XK_Mode_switch)
                 | XkbKeysymToModifiers (this->x11Display(), XK_ISO_Level3_Shift)
                 | XkbKeysymToModifiers (this->x11Display(), XK_ISO_Level3_Latch)
                 | XkbKeysymToModifiers (this->x11Display(), XK_ISO_Level3_Lock);
      
      int map = 0;
      for (map = 0; map < 8; map++) {
         if ((mask & (1 << map)) != 0)
            break;
      }
      if ((map <= 7) && !(icons[map])) {
         icons[map] = new KeyIcon (i, instance, this, modifierKeys[i].name);
         QToolTip::add (icons[map], i18n (modifierKeys[i].name));
         connect (icons[map], SIGNAL(stateChangeRequest (KeyIcon*,bool,bool)),
                                SLOT(stateChangeRequest (KeyIcon*,bool,bool)));
         if (modifierKeys[i].latchedText != "")
            modifiers.append(icons[map]);
         else
            lockkeys.append(icons[map]);
      }
   }
}

bool KbStateApplet::x11Event (XEvent *evt) {
   if (evt->type == xkb_base_event_type + XkbEventCode) {
      XkbEvent *kbevt = (XkbEvent *)evt;
      switch (kbevt->any.xkb_type) {
      case XkbStateNotify:
         timerEvent (0);
         break;
      case XkbAccessXNotify:
         switch (kbevt->accessx.detail) {
         case XkbAXN_SKPress:
            slow->setGlyth(i18n("a (the first letter in the alphabet)", "a"));
            break;
         case XkbAXN_SKAccept:
            slow->setImage("keypressok");
            break;
         case XkbAXN_SKRelease:
            slow->setGlyth(" ");
            slow->setImage("unlatched");
            break;
         case XkbAXN_SKReject:
            slow->setImage("keypressno", kbevt->accessx.sk_delay>150?kbevt->accessx.sk_delay:150);
            break;
         case XkbAXN_BKAccept:
            break;
         case XkbAXN_BKReject:
            break;
         }
         break;
      case XkbControlsNotify:
         break;
      case XkbExtensionDeviceNotify:
         /* This is a hack around the fact that XFree86's XKB doesn't give AltGr notifications */
         break;
      default:
         break;
      }
   }
   return false;
}

void KbStateApplet::timerEvent(QTimerEvent*) {
   XkbStateRec state_return;
   XkbGetState (this->x11Display(), XkbUseCoreKbd, &state_return);
   unsigned char latched = XkbStateMods (&state_return);
   unsigned char locked  = XkbModLocks  (&state_return);
   int mods = ((int)latched)<<8 | locked;
   
   if (state != mods) {
      state = mods;
      for (int i = 0; i < 8; i++) {
         if (icons[i])
            icons[i]->setState ((latched&(1<<i)) != 0, (locked&(1<<i)) != 0);
      }
   }
}

void KbStateApplet::stateChangeRequest (KeyIcon *source, bool latched, bool locked) {
   for (int i = 0; i < 8; i++) {
      if (icons[i] == source) {
         if (locked)
            XkbLockModifiers (this->x11Display(), XkbUseCoreKbd, 1<<i, 1<<i);
         else if (latched) {
            XkbLockModifiers (this->x11Display(), XkbUseCoreKbd, 1<<i, 0);
            XkbLatchModifiers (this->x11Display(), XkbUseCoreKbd, 1<<i, 1<<i);
         }
         else {
            XkbLockModifiers (this->x11Display(), XkbUseCoreKbd, 1<<i, 0);
            XkbLatchModifiers (this->x11Display(), XkbUseCoreKbd, 1<<i, 0);
         }
      }
   }
}


void KbStateApplet::toggleModifier() {
   showModifiers = !showModifiers;
   updateMenu();
   layout();
   updateGeometry();
   emit updateLayout();
}

void KbStateApplet::toggleLockkeys() {
   showLockkeys = !showLockkeys;
   updateMenu();
   layout();
   updateGeometry();
   emit updateLayout();
}

void KbStateApplet::toggleSlowkeys() {
   showSlowkeys = !showSlowkeys;
   updateMenu();
   layout();
   updateGeometry();
   emit updateLayout();
}

void KbStateApplet::configureAccessX() {
   kapp->startServiceByDesktopName("kcmaccess");
}

void KbStateApplet::about() {
   KAboutData about("kbstateapplet", I18N_NOOP("Keyboard Status Applet"), "0.2",
                    I18N_NOOP("Panel applet that shows the state of the modifier keys"), KAboutData::License_GPL_V2, "(C) 2004 Gunnar Schmi Dt");
   KAboutApplication a(&about, this);
   a.exec();
}

void KbStateApplet::loadConfig()
{
   KConfig *c = config();
   c->setGroup("General");
   size = c->readNumEntry("IconDim", 20);
   showModifiers = c->readBoolEntry("Modifierkeys visible",true);
   showLockkeys  = c->readBoolEntry("Lockkeys visible",true);
   showSlowkeys  = c->readBoolEntry("Slowkeys status visible",true);
}

void KbStateApplet::saveConfig()
{
   KConfig *c = config();
   c->setGroup("General");
   c->writeEntry("IconDim", size);
   c->writeEntry("Modifierkeys visible",    showModifiers);
   c->writeEntry("Lockkeys visible",        showLockkeys);
   c->writeEntry("Slowkeys status visible", showSlowkeys);
   c->sync();
}

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

KeyIcon::KeyIcon (int keyId, KInstance *instance,
                  QWidget *parent, const char *name)
 : StatusIcon (modifierKeys[keyId].name, parent, name) {
   this->instance = instance;
   this->keyId = keyId;
   this->tristate = (modifierKeys[keyId].latchedText != "");
   isLocked  = false;
   isLatched = false;
   updateImages ();
   connect (this, SIGNAL(clicked()), SLOT(clickedSlot()));
}

KeyIcon::~KeyIcon () {
}

void KeyIcon::setState (bool latched, bool locked) {
   latched = latched | locked;

   if (tristate) {
      if (locked & !isLocked) {
         KNotifyClient::Instance instance(this->instance);
         KNotifyClient::event (winId(),
                               "modifierkey-locked",
                               i18n(modifierKeys[keyId].lockedText));
      }
      else if (latched & !isLatched) {
         KNotifyClient::Instance instance(this->instance);
         KNotifyClient::event (winId(),
                               "modifierkey-latched",
                               i18n(modifierKeys[keyId].latchedText));
      }
      else if (isLatched & !latched) {
         KNotifyClient::Instance instance(this->instance);
         KNotifyClient::event (winId(),
                               "modifierkey-unlatched",
                               i18n(modifierKeys[keyId].unlatchedText));
      }
   }
   else {
      if (latched & !isLatched) {
         KNotifyClient::Instance instance(this->instance);
         KNotifyClient::event (winId(),
                               "lockkey-locked",
                               i18n(modifierKeys[keyId].lockedText));
      }
      if (isLatched & !latched) {
         KNotifyClient::Instance instance(this->instance);
         KNotifyClient::event (winId(),
                               "lockkey-unlocked",
                               i18n(modifierKeys[keyId].unlatchedText));
      }
   }
   isLatched = latched;
   isLocked  = locked;
   update();
}

void KeyIcon::clickedSlot () {
   if (tristate)
      emit stateChangeRequest (this, !isLocked, isLatched&!isLocked);
   else
      emit stateChangeRequest (this, false, !isLocked);
}


void KeyIcon::resizeEvent( QResizeEvent*e )
{
   QWidget::resizeEvent(e);
   updateImages();
}

void KeyIcon::updateImages () {
   int size = width()<height() ? width() : height();

   locked    = instance->iconLoader()->loadIcon("lockoverlay", KIcon::Panel, size-4);
   latched   = instance->iconLoader()->loadIcon(modifierKeys[keyId].icon, KIcon::NoGroup, size-4);
   unlatched = instance->iconLoader()->loadIcon(modifierKeys[keyId].icon, KIcon::NoGroup, size-4);
   
   QImage img = latched.convertToImage();
   KIconEffect::colorize(img,  KGlobalSettings::highlightedTextColor(), 1.0);
   latched.convertFromImage (img);
   
   img = unlatched.convertToImage();
   KIconEffect::colorize(img, KGlobalSettings::textColor(), 1.0);
   unlatched.convertFromImage (img);

   update();
}

void KeyIcon::drawButton (QPainter *p) {
   QColor black;

   int x = (width()-locked.width())/2;
   int y = (height()-locked.height())/2;
   int o = 0;
   if (isLocked || isLatched) {
      qDrawShadePanel (p, 0, 0, width(), height(), colorGroup(), true, 1, NULL);
      p->fillRect (1,1,width()-2,height()-2, KGlobalSettings::highlightColor());
      p->drawPixmap (x+1,y+1, latched);
      black = KGlobalSettings::highlightedTextColor();
      o = 1;
   }
   else {
      qDrawShadePanel (p, 0, 0, width(), height(), colorGroup(), false, 1, NULL);
      p->drawPixmap (x,y, unlatched);
      black = KGlobalSettings::textColor();
   }

   QString text = i18n(modifierKeys[keyId].text);
   if (!text.isEmpty() && !text.isNull()) {
      QFont font = KGlobalSettings::generalFont();
      font.setWeight(QFont::Black);
      QFontMetrics metrics(font);
      QRect rect = metrics.boundingRect (text);
      int size;
      if (modifierKeys[keyId].name == "Alt Graph")
         size = rect.width()>rect.height()?rect.width():rect.height();
      else
         size = rect.width()>12*rect.height()/5?rect.width():12*rect.height()/5;

      if (font.pixelSize() != -1)
         font.setPixelSize (font.pixelSize()*width()*19/size/32);
      else
         font.setPointSizeFloat (font.pointSizeFloat()*width()*19/size/32);

      p->setPen (black);
      p->setFont (font);
      if (modifierKeys[keyId].name == "Alt Graph")
      p->drawText (o,o, width(), height(), Qt::AlignCenter, text);
      else
         p->drawText (o,o, width(), height()*(251)/384, Qt::AlignCenter, text);
   }
   if (tristate && isLocked) {
      p->drawPixmap(x+o,y+o, locked);
   }
}

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

TimeoutIcon::TimeoutIcon (KInstance *instance, const QString &text, QWidget *parent, const char *name)
 : StatusIcon (text, parent, name) {
   this->instance = instance;
   glyth = " ";
   setImage ("unlatched");
   connect (&timer, SIGNAL(timeout()), this, SLOT(timeout()));
}

TimeoutIcon::~TimeoutIcon () {
}

void TimeoutIcon::update () {
   int size = width()<height() ? width() : height();
   if (pixmap.width() != size)
      pixmap = instance->iconLoader()->loadIcon(iconname, KIcon::NoGroup, size);

   QImage img = pixmap.convertToImage();
   KIconEffect::colorize(img, KGlobalSettings::textColor(), 1.0);
   pixmap.convertFromImage (img);

   image = pixmap;
   QWidget::update();
}

void TimeoutIcon::setGlyth (const QString &glyth) {
   timer.stop();
   this->glyth = glyth;

   QImage img = pixmap.convertToImage();
   KIconEffect::colorize(img, KGlobalSettings::textColor(), 1.0);
   pixmap.convertFromImage (img);

   image = pixmap;
   update();
}

void TimeoutIcon::setImage (const QString &name, int timeout) {
   timer.stop();
   iconname = name;
   int size = width()<height() ? width() : height();
   pixmap = instance->iconLoader()->loadIcon(iconname, KIcon::NoGroup, size);

   QImage img = pixmap.convertToImage();
   KIconEffect::colorize(img, KGlobalSettings::textColor(), 1.0);
   pixmap.convertFromImage (img);

   image = pixmap;
   update();
   if (timeout > 0)
      timer.start (timeout, true);
}

void TimeoutIcon::timeout () {
   setGlyth (" ");
   setImage ("unlatched");
}


void TimeoutIcon::drawButton (QPainter *p) {
   p->drawPixmap(0,0, image);

   QFont font = KGlobalSettings::generalFont();
   font.setWeight(QFont::Black);
   QFontMetrics metrics(font);
   QRect rect = metrics.boundingRect (glyth);
   int size = rect.width() > rect.height() ? rect.width() : rect.height();
   if (font.pixelSize() != -1)
      font.setPixelSize (font.pixelSize()*width()*19/size/64);
   else
      font.setPointSizeFloat (font.pointSizeFloat()*width()*19/size/64);

   p->setPen (KGlobalSettings::textColor());
   p->setFont (font);
   p->drawText (0,0, width()/2, height()/2, Qt::AlignCenter, glyth);
}

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

StatusIcon::StatusIcon (const QString &text, QWidget *parent, const char *name)
 : QPushButton (text, parent, name) {
   setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
}

StatusIcon::~StatusIcon () {
}

QSize StatusIcon::minimumSizeHint () const {
   return QSize (0,0);
}
