/***************************************************************************
                          kkeyled.cpp  -  description
                             -------------------
    begin                : Mon Apr 23 13:06:31 CEST 2001
    copyright            : (C) 2001 by Dieter Landolt
    email                : dieter.landolt@secs.ch
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
#include <klocale.h>
#include <qwidget.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <ksimpleconfig.h>
#include <kicondialog.h>
#include <kiconloader.h>
#include <ktoolbarbutton.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qevent.h>
#include <qframe.h>
#include <kpushbutton.h>
#include <qcheckbox.h>
#include <qtooltip.h>
#include <qwhatsthis.h> 
#include <kmainwindow.h>
#include <kglobal.h>

#include "dockwidget.h"
#include "kkeyled.h"
#include "kkeyleddtlcfg.h"

extern "C" {
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
}

Kkeyled::Kkeyled(QWidget *parent, const char *name, Qt::WFlags f)
        : KMainWindow(parent, name, f)
{
     KGlobal::config()->reparseConfiguration();

     /* Create and Connect to the try widgets */
     nl = new DockWidget(this,"numlock",DockWidget::Numlock);
     cl = new DockWidget(this,"capslock",DockWidget::Capslock);
     sl = new DockWidget(this,"scrollock",DockWidget::Scrollock);

     setCaption(i18n("Keyboard led setup Window"));
     blink = new QTimer(this);

     connect(blink,SIGNAL(timeout()),SLOT(checkState()));
     connect(this,SIGNAL(numLockKeyChanged(State)),nl, SLOT(setState(State)));
     connect(this,SIGNAL(capsLockKeyChanged(State)),cl, SLOT(setState(State)));
     connect(this,SIGNAL(scrollLockKeyChanged(State)),sl, SLOT(setState(State)));

     connect(nl,SIGNAL(stateChanged(bool)), this, SLOT(setNumLockState(bool)));
     connect(cl,SIGNAL(stateChanged(bool)), this, SLOT(setCapsLockState(bool)));
     connect(sl,SIGNAL(stateChanged(bool)), this, SLOT(setScrollLockState(bool)));

     blink->start(125); // sets the Timer to 1/8 sek.

     /* start the design for the config window */
     /*
     *******************************
     *              vb             *
     * *************************** *
     * *            hb             *
     * * **********  *********** * *
     * * *        *  *         * * *
     * * *   vb1  *  *   vb2   * * *
     * * *        *  *         * * *
     * * *        *  *         * * *
     * * *        *  *         * * *
     * * **********  *********** * *
     * *************************** *
     *******************************

     */
     QVBox *vb = new QVBox(this);
     QHBox *hb = new QHBox(vb); // create a hbox inside the vbox for the first entry
     QVBox *vb1 = new QVBox(hb); 
     QVBox *vb2 = new QVBox(hb); 
     vb->setLineWidth(4);
     vb->setMargin(4);
     vb->setFrameStyle(QFrame::Box | QFrame::Raised);
     cbnl = new QCheckBox(i18n("Numlock"),vb1,"Numlock");
     cbcl = new QCheckBox(i18n("Capslock"),vb1,"Capslock");
     cbsl = new QCheckBox(i18n("Scrollock"),vb1,"Scrollock");


     // add a little button for the what's this help in this dialog, it's easier then
     // point to the led click the right mose key an chose it in the submenu
     // QPushButton *qtb = new QPushButton(QIconSet(BarIcon("contexthelp", kapp), QSize(32,32)),i18n("What's &This?"),vb2,"helpicon");
     QPushButton *qtb = new QPushButton(vb2,"helpicon");
     // qtb->setIconSet(QIconSet(BarIcon("contexthelp")));
     qtb->setPixmap(BarIcon("contexthelp"));
     QToolTip::add(qtb, i18n("What's This?"));
     QWhatsThis::add(qtb, i18n("this button activate's the what's This help"));

     connect(qtb, SIGNAL(clicked()),this, SLOT(whatsThis()));

     // add the Button for the Detailconfig Dialog
     QPushButton *qdd = new QPushButton(i18n("&detail"),vb2);
     QToolTip::add(qdd, i18n("click for detail configuration"));
     QWhatsThis::add(qdd, i18n("click it to make a detailed configuration like the pixmap to use"));

     connect(qdd, SIGNAL(clicked()),this, SLOT(detailconfig()));     

     QToolTip::add(cbnl, i18n("you can change the visibility for the numlock led here"));
     QToolTip::add(cbcl, i18n("you can change the visibility for the capslock led here"));
     QToolTip::add(cbsl, i18n("you can change the visibility for the scrollock led here"));
     QWhatsThis::add(cbnl, i18n("click on the box to toggle the visibilitie\n"
                                "of the numlock key led in the panel."
                              ));
     QWhatsThis::add(cbcl, i18n("click on the box to toggle the visibilitie\n"
                                "of the capslock key led in the panel."
                              ));

     QWhatsThis::add(cbsl, i18n("click on the box to toggle the visibilitie\n"
                                "of the scrollock key led in the panel."
                              ));
     QFrame *myqf = new QFrame(vb);
     myqf->setLineWidth(3);
     myqf->setGeometry( QRect( 120, 170, 281, 20 ) );
     myqf->setFrameStyle( QFrame::HLine | QFrame::Sunken );
     // read the configuration
     restLedstate = new QCheckBox(i18n("restore state"),vb,"restorestate");
     QToolTip::add(restLedstate, i18n("check this box if you like to restore saved state at startup"));
     QWhatsThis::add(restLedstate, i18n("if you activate this box the state of the led's\n"
                                         "will be restored as it was by clicking the save button."
                              ));
     QPushButton *saveLedStateB = new QPushButton(vb,"save");
     saveLedStateB->setText(i18n("&save"));
     QToolTip::add(saveLedStateB, i18n("save the state of the led's"));
     QWhatsThis::add(saveLedStateB, i18n("you can save here the actual state off the\n"
                                        "capslock,numlock and scrolllock key.\n"
                                        "if you checked the box over it will restored at the starttime"
                                        ));
     saveLedStateB->adjustSize();

     // KSimpleConfig conf ("Kkeyledrc");
     KGlobal::config()->setGroup("Trayvisible");
     cbnl->setChecked( KGlobal::config()->readBoolEntry("Numlock",true));
     cbcl->setChecked( KGlobal::config()->readBoolEntry("Capslock",true));
     cbsl->setChecked( KGlobal::config()->readBoolEntry("Scrollock",true));

     // and now reduce the visibility when its nessesary
     if(! cbnl->isOn() )
        nl->hide();
     if(! cbcl->isOn() )
        cl->hide();
     if(! cbsl->isOn() )
        sl->hide();

     // restore the keystat's if it's so configured
     KGlobal::config()->setGroup("Restore");
     restLedstate->setChecked(KGlobal::config()->readBoolEntry("Restore",false));	
     if(restLedstate->isChecked()) {
          setNumLockState(KGlobal::config()->readBoolEntry("Numlock",false));
          setCapsLockState(KGlobal::config()->readBoolEntry("Capslock",false));
          setScrollLockState(KGlobal::config()->readBoolEntry("Scrollock",false));
          nl->setlocked(KGlobal::config()->readBoolEntry("numlocked",false));
          cl->setlocked(KGlobal::config()->readBoolEntry("capslocked",false));
          sl->setlocked(KGlobal::config()->readBoolEntry("scrollocked",false));
     }


     vb->adjustSize();


    // connect the checkboxes for set to visible invsible
    connect(cbnl, SIGNAL(stateChanged(int)),nl, SLOT(changeVisible(int)));
    connect(cbcl, SIGNAL(stateChanged(int)),cl, SLOT(changeVisible(int)));
    connect(cbsl, SIGNAL(stateChanged(int)),sl, SLOT(changeVisible(int)));

    // connect the checkboxes for set to save alltrue the config
    connect(cbnl, SIGNAL(stateChanged(int)),SLOT(configSave(int)));
    connect(cbcl, SIGNAL(stateChanged(int)),SLOT(configSave(int)));
    connect(cbsl, SIGNAL(stateChanged(int)),SLOT(configSave(int)));

    connect(restLedstate, SIGNAL(stateChanged(int)),SLOT(configSave(int)));

    // save a spezific lockstate for restoring
    connect(saveLedStateB, SIGNAL(clicked()), SLOT(configSaveState()));

    // Use the focus Policy
    setFocusPolicy(QWidget::StrongFocus);

    // set a position if no led is as visible configured
    QWidget *desktop = QApplication::desktop();
    int wi = desktop->width();                   // returns screen width
    int hi = desktop->height();                  // returns screen height
    resize(vb->width(),vb->height());
    move(wi - width(),hi - height());

    if( ! cbnl->isOn() && ! cbcl->isOn() && ! cbsl->isOn() ) {
    	setFocus();
        show();
    } else {
	hide();
    }

}

Kkeyled::~Kkeyled()
{
}
/** Handels the Timer event and modifies
the Keyboardstate and after sends the signal to 
the right Object 
 */
void Kkeyled::checkState(){
    unsigned int states;
    static unsigned int oldstates=255;
    XkbGetIndicatorState(this->x11Display(),XkbUseCoreKbd,&states);
    if (oldstates != states ) {
        oldstates = states;
        int i, bit;
        for (i=0,bit=1;i<XkbNumIndicators;i++,bit<<=1)
        {
            int flag;
            flag=states&bit;
            switch (i)
            {
              case 1: // Numlock
                if (flag)
                  numLockKeyChanged(On);
                else
                  numLockKeyChanged(Off);
                break;
              case 0: // CapsLock
                if (flag)
                  capsLockKeyChanged(On);
                else
                  capsLockKeyChanged(Off);
                break;
              case 2: //ScrollLock
                if (flag)
                  scrollLockKeyChanged(On);
                else
                  scrollLockKeyChanged(Off);
                break;
              }
        }
    }
}
/** sets the Numlockkey to a spezified value */
void Kkeyled::setNumLockState(bool st){
    unsigned int mask;
    if( !xkb_init())
        return;
    mask = xkb_lock_mask((char*) "NumLock");
    setLockState(st, mask);
}
/** sets the Capslockkey to a spezified value */
void Kkeyled::setCapsLockState(bool st){
    unsigned int mask;
    if( !xkb_init())
        return;
    mask = xkb_lock_mask((char*) "Caps Lock");
    setLockState(st, mask);
}
/** sets the Scrollockkey to a spezified value */
void Kkeyled::setScrollLockState(bool st){
    unsigned int mask;
    if( !xkb_init())
        return;
    mask = xkb_lock_mask((char*) "ScrollLock");
    setLockState(st, mask);
}

/** set the keystate */
void Kkeyled::setLockState(bool st, unsigned int &mask){
    if( mask == 0 )
        return;
    if(st) {
          XkbLockModifiers ( this->x11Display(), XkbUseCoreKbd, mask, mask);
    } else {
          XkbLockModifiers ( this->x11Display(), XkbUseCoreKbd, mask, 0);
    }
}


/** overwright the show event for repositioning the window on top
 of the icon */
void Kkeyled::showEvent(QShowEvent *e){
    if ( nl->globalPos->x() > 0) { // Neupositionierung nur wenn statische Variable > 0;
       QWidget *desktop = QApplication::desktop();
       int wi = desktop->width();                   // returns screen width
       // int hi = desktop->height();                  // returns screen height
       QPoint nPos;   
       if(nl->globalPos->y() -height() > 0 ) { // it's not on top
          nPos.setY(nl->globalPos->y() -height());
          nPos.setX(nl->globalPos->x() - (width() / 2));
       } else { 
          if( nl->globalPos->y() > 0 ) { 
               nPos.setY(nl->globalPos->y() + nl->height() );
               nPos.setX(nl->globalPos->x() - (width() / 2));
          }
       }
       if ( nPos.x() < 0 ) {
          nPos.setX(nl->globalPos->x() + nl->width());
       } else {
          if ( nl->globalPos->x() + (width() / 2)  > wi ) {
               nPos.setX(nl->globalPos->x() - width());
          }
       }
       move(nPos);
    }
    QWidget::showEvent(e); // execute the normal showevent
    raise();

}
/** Saving the Configfile */
void Kkeyled::configSave(int){
	KGlobal::config()->setGroup("Trayvisible");
	KGlobal::config()->writeEntry("Numlock",cbnl->isOn(),true,false,false);
	KGlobal::config()->writeEntry("Capslock",cbcl->isOn(),true,false,false);
	KGlobal::config()->writeEntry("Scrollock",cbsl->isOn(),true,false,false);
	KGlobal::config()->setGroup("Restore");
	KGlobal::config()->writeEntry("Restore",restLedstate->isOn(),true,false,false);
        /* 
           To be shure we have allready the focus.
           it's nessesary when we like to check the lost of the focus.
           we lost the focus when no led checkbosx is checked to be visibel
           the user moves also to a other window then this
           the user comes back and checks also a led to be visibel, the mainwindow need's the 
           also to set as active.

	*/
	setActiveWindow();
}

/** Saving the state of the Le'ds to Config file Configfile */
void Kkeyled::configSaveState(){
     // KSimpleConfig conf ("Kkeyledrc");
	KGlobal::config()->setGroup("Restore");
	KGlobal::config()->writeEntry("Numlock",nl->getState(),true,false,false);
	KGlobal::config()->writeEntry("Capslock",cl->getState(),true,false,false);
	KGlobal::config()->writeEntry("Scrollock",sl->getState(),true,false,false);

	KGlobal::config()->writeEntry("numlocked", nl->isLocked(),true,false,false);
	KGlobal::config()->writeEntry("capslocked", cl->isLocked(),true,false,false);
	KGlobal::config()->writeEntry("scrollocked", sl->isLocked(),true,false,false);

}

/** Initialize the xkeyboard */
int Kkeyled::xkb_init(){
	int xkb_opcode, xkb_event, xkb_error;
	int xkb_lmaj = XkbMajorVersion;
	int xkb_lmin = XkbMinorVersion;
	return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin )
		&& XkbQueryExtension( this->x11Display(), &xkb_opcode, &xkb_event, &xkb_error,
					&xkb_lmaj, &xkb_lmin );
}

/** Modifie the Keyboardmask */
unsigned int Kkeyled::xkb_mask_modifier( XkbDescPtr xkb, const char *name ){
	unsigned int mask=0;
	int i;
	if( !xkb || !xkb->names )
		return 0;

	if  ( strcmp(name, "Caps Lock") == 0 )
		return 2;     // this is a workaround then function does not return this 2 witch works
                              // needs moor debuging to find the real Problem here

	for( i = 0; i <= XkbNumVirtualMods; i++ ) {
		char* modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] );
		// qDebug("Name = *%s* modStr = *%s*\n", name,  modStr);
		if( modStr != NULL && strcmp(name, modStr) == 0 )
		{
			// found the keyname and convert it
			XkbVirtualModsToReal( xkb, 1 << i, &mask );
			// qDebug("Mask = %i / i = %i\n", mask,  i);
		return mask;
		}
    	}
	return 0;
}

/** generallfunction to return the mask for the modifier key */
unsigned int Kkeyled::xkb_lock_mask(char* keyname){
    XkbDescPtr xkb;
    if(( xkb = XkbGetKeyboard( this->x11Display(), XkbAllComponentsMask, XkbUseCoreKbd )) != NULL )
        {
        unsigned int mask = xkb_mask_modifier( xkb, keyname );
        XkbFreeKeyboard( xkb, 0, True );
        return mask;
        }
    return 0;
}
/** Starts and handles the detailconfig dialog */
void Kkeyled::detailconfig(){
     kkeyleddtlcfg *lcfgdia = new kkeyleddtlcfg(this);
     // connect signals for reconfiguration
     connect(lcfgdia,SIGNAL(configChanged()),cl,SLOT(readConfig()));
     connect(lcfgdia,SIGNAL(configChanged()),nl,SLOT(readConfig()));
     connect(lcfgdia,SIGNAL(configChanged()),sl,SLOT(readConfig()));

     lcfgdia->show(); 
     hide();
}


/*!
    \fn Kkeyled::show()
    If the windosstile is overwritten by the sessionmanagement set it first to the state we need
*/
void Kkeyled::show()
{
   WFlags f;
   f = Qt::WStyle_Customize | Qt::WStyle_StaysOnTop | Qt::WStyle_NoBorder | Qt::WX11BypassWM;
   if ( ! testWFlags(f)) {
   	setWFlags(f);
   	reparent(0,getWFlags(),pos());
   }
  QWidget::show();
  setActiveWindow();
  raise();
}
/** Hides the window if at lease one led is on */
void Kkeyled::focusOutEvent( QFocusEvent * ){
    if( cbnl->isOn() || cbcl->isOn() || cbsl->isOn() ) {
        hide();
    } else {
	setActiveWindow();
    }
#ifdef debug
	qDebug("Focus lost\n");
#endif
 }
