/***************************************************************************
 *                                                                         *
 *   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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/




#include <qapplication.h>
#include <qmap.h>
#include <qmessagebox.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qthread.h>
#include <qvaluelist.h>

#include "config_file.h"
#include "debug.h"
#include "kadu.h"

#include "activate.h"
#include "chat_manager.h"
#include "misc.h"
#include "pending_msgs.h"

#include "globalhotkeys.h"

#include <X11/Xlib.h>




GlobalHotkeys *globalHotkeys;




extern "C" int globalhotkeys_init()
{
	kdebugf();
	globalHotkeys = new GlobalHotkeys();
	MainConfigurationWindow::registerUiFile( dataPath("kadu/modules/configuration/globalhotkeys.ui"), globalHotkeys );
	kdebugf2();
	return 0;
}


extern "C" void globalhotkeys_close()
{
	kdebugf();
	MainConfigurationWindow::unregisterUiFile( dataPath("kadu/modules/configuration/globalhotkeys.ui"), globalHotkeys );
	delete globalHotkeys;
	globalHotkeys = NULL;
	kdebugf2();
}




Hotkey::Hotkey( bool _shift, bool _control, bool _alt, bool _altgr, bool _super, int _keycode, QString _comment )
{
	shift   = _shift;
	control = _control;
	alt     = _alt;
	altgr   = _altgr;
	super   = _super;
	keycode = _keycode;
	comment = _comment;
}


Hotkey::Hotkey( QString hotkeystring )
{
	// remove "white" characters from begining and end of the string
	hotkeystring = hotkeystring.stripWhiteSpace();
	// initial values
	shift   = false;
	control = false;
	alt     = false;
	altgr   = false;
	super   = false;
	keycode = 0;
	// return if the string is empty
	if( hotkeystring.isEmpty() ) return;
	// split hotkeystring into parts using "+" as the separator
	QStringList parts = QStringList::split( QString("+"), hotkeystring, true );
	// set Shift
	if( parts.contains( "Shift" ) > 0 )
		shift = true;
	// set Control
	if( parts.contains( "Control" ) > 0 )
		control = true;
	// set Alt
	if( parts.contains( "Alt" ) > 0 )
		alt = true;
	// set AltGr
	if( parts.contains( "AltGr" ) > 0 )
		altgr = true;
	// set Super
	if( parts.contains( "Super" ) > 0 )
		super = true;
	bool ok;
	keycode = parts.last().toInt( &ok );
	if( ! ok )  // if the last part of hotkey string is not an integer
	{
		KeySym keysym = XStringToKeysym( parts.last().ascii() );
		if( keysym != NoSymbol )
		{
			keycode = XKeysymToKeycode( qt_xdisplay(), keysym );
		}
	}
	comment = hotkeystring;
}


bool Hotkey::equals( Hotkey *h )
{
	if( shift   != h->shift   ) return false;
	if( control != h->control ) return false;
	if( alt     != h->alt     ) return false;
	if( altgr   != h->altgr   ) return false;
	if( super   != h->super   ) return false;
	if( keycode != h->keycode ) return false;
	return true;
}




void HotkeyThread::setDisplay( Display *_display )
{
	display = _display;
}


void HotkeyThread::setHotkeys( QMap<QString,Hotkey*> _hotkeys )
{
	hotkeys = _hotkeys;
}


void HotkeyThread::run()
{
	XEvent event;
	bool mod_shift;
	bool mod_control;
	bool mod_alt;
	bool mod_altgr;
	bool mod_super;
	Hotkey *hotkey;
	for(;;)
	{
		// get next event (if the events queue is empty, XNextEvent will block and wait for an event to occur)
		XNextEvent( display, &event );
		// is it KeyPress event?
		if( event.type == KeyPress )
		{
			// read modifiers state from current event
			mod_shift   = ( ( event.xkey.state & GLOBALHOTKEYS_X11SHIFTMASK   ) != 0 );
			mod_control = ( ( event.xkey.state & GLOBALHOTKEYS_X11CONTROLMASK ) != 0 );
			mod_alt     = ( ( event.xkey.state & GLOBALHOTKEYS_X11ALTMASK     ) != 0 );
			mod_altgr   = ( ( event.xkey.state & GLOBALHOTKEYS_X11ALTGRMASK   ) != 0 );
			mod_super   = ( ( event.xkey.state & GLOBALHOTKEYS_X11SUPERMASK   ) != 0 );
			// create the hotkey
			hotkey = new Hotkey( mod_shift, mod_control, mod_alt, mod_altgr, mod_super, event.xkey.keycode );
			// thread: lock application
			qApp->lock();
			// check hotkeys and execute related commands
			if( hotkey->equals( hotkeys["ShowKadusMainWindow"] ) )
			{
				if( ! kadu->isVisible() )
				{
					// show and activate Kadu's main window
					kadu->show();
					kadu->raise();
					activateWindow( kadu->winId() );
				}
				else if( kadu->isVisible() && ( ! kadu->isActiveWindow() ) )
				{
					// (*) calling hide() and show() too fast may result in X11 error
					// (*) "X Error: BadWindow (invalid Window parameter) 3" - it seems to be q Qt bug
					// (*) to avoid warnings being reported we have to handle messages
					QtMsgHandler previousmsghandler = qInstallMsgHandler( EmptyMsgHandler );
					// hide Kadu's main window first to avoid virtual desktop switching
					kadu->hide();
					// (*) flush() the application
					qApp->flush();
					// (*) wait
					qApp->unlock();
					while( kadu->isVisible() )
						usleep( 1000 );
					usleep( 30000 );
					qApp->lock();
					// show Kadu's main window
					kadu->show();
					// (*) wait
					qApp->unlock();
					while( ! kadu->isVisible() )
						usleep( 1000 );
					qApp->lock();
					// activate Kadu's main window
					kadu->raise();
					activateWindow( kadu->winId() );
					// (*) restore previous messages handler
					qInstallMsgHandler( previousmsghandler );
				}
			}
			else if( hotkey->equals( hotkeys["HideKadusMainWindow"] ) )
			{
				if( kadu->isVisible() )
				{
					// hide Kadu's main window
					kadu->hide();
				}
			}
			else if( hotkey->equals( hotkeys["ShowHideKadusMainWindow"] ) )
			{
				if( ! kadu->isVisible() )
				{
					// show and activate Kadu's main window
					kadu->show();
					kadu->raise();
					activateWindow( kadu->winId() );
				}
				else if( kadu->isVisible() && ( ! kadu->isActiveWindow() ) )
				{
					// (*) calling hide() and show() too fast may result in X11 error
					// (*) "X Error: BadWindow (invalid Window parameter) 3" - it seems to be q Qt bug
					// (*) to avoid warnings being reported we have to handle messages
					QtMsgHandler previousmsghandler = qInstallMsgHandler( EmptyMsgHandler );
					// hide Kadu's main window first to avoid virtual desktop switching
					kadu->hide();
					// (*) flush() the application
					qApp->flush();
					// (*) wait
					qApp->unlock();
					while( kadu->isVisible() )
						usleep( 1000 );
					usleep( 30000 );
					qApp->lock();
					// show Kadu's main window
					kadu->show();
					// (*) wait
					qApp->unlock();
					while( ! kadu->isVisible() )
						usleep( 1000 );
					qApp->lock();
					// activate Kadu's main window
					kadu->raise();
					activateWindow( kadu->winId() );
					// (*) restore previous messages handler
					qInstallMsgHandler( previousmsghandler );
				}
				else if( kadu->isVisible() )
				{
					// hide Kadu's main window
					kadu->hide();
				}
			}
			else if( hotkey->equals( hotkeys["OpenIncomingChatWindow"] ) )
			{
				if( pending.count() > 0 )
				{
					// open window for pending message(s)
					chat_manager->openPendingMsgs( true );
					// activate it
					QWidget *win = chat_manager->chats()[ chat_manager->chats().count() - 1 ];  // last created chat widget
					while( win->parent() )
						win = static_cast<QWidget *>(win->parent());
					win->show();
					win->raise();
					activateWindow( win->winId() );
				}
				else
				{
					// show window with new unread message(s)
					CONST_FOREACH( chat, chat_manager->chats() )
					{
						if( (*chat)->newMessagesCount() > 0 )
						{
							// reopen the chat (needed when Tabs module is active)
							chat_manager->openChatWidget( gadu, (*chat)->users()->toUserListElements() );
							// activate the window
							QWidget *win = (*chat);
							while( win->parent() )
								win = static_cast<QWidget *>(win->parent());
							win->show();
							win->raise();
							activateWindow( win->winId() );
							// done - only one window
							break;
						}
					}
				}
			}
			else if( hotkey->equals( hotkeys["OpenAllIncomingChatWindows"] ) )
			{
				// open all windows for pending message(s)
				while( pending.count() > 0 )
				{
					// open the window
					chat_manager->openPendingMsgs( true );
					// activate it
					QWidget *win = chat_manager->chats()[ chat_manager->chats().count() - 1 ];  // last created chat widget
					while( win->parent() )
						win = static_cast<QWidget *>(win->parent());
					win->show();
					win->raise();
					activateWindow( win->winId() );
				}
				// show all windows with new unread message(s)
				CONST_FOREACH( chat, chat_manager->chats() )
				{
					if( (*chat)->newMessagesCount() > 0 )
					{
						// reopen the chat (needed when Tabs module is active)
						chat_manager->openChatWidget( gadu, (*chat)->users()->toUserListElements() );
						// activate the window
						QWidget *win = (*chat);
						while( win->parent() )
							win = static_cast<QWidget *>(win->parent());
						win->show();
						win->raise();
						activateWindow( win->winId() );
					}
				}
			}
			else if( hotkey->equals( hotkeys["MinimizeOpenedChatWindows"] ) )
			{
				CONST_FOREACH( chat, chat_manager->chats() )
				{
					// minimize the window
					QWidget *win = (*chat);
					while( win->parent() )
						win = static_cast<QWidget *>(win->parent());
					if( ! win->isMinimized() )
						win->showMinimized();
				}
			}
			else if( hotkey->equals( hotkeys["RestoreMinimizedChatWindows"] ) )
			{
				CONST_FOREACH( chat, chat_manager->chats() )
				{
					// restore the window
					QWidget *win = (*chat);
					while( win->parent() )
						win = static_cast<QWidget *>(win->parent());
					if( win->isMinimized() )
						win->showNormal();
				}
			}
			else if( hotkey->equals( hotkeys["CloseAllChatWindows"] ) )
			{
				// list of windows to close (needed by Tabs module - we cannot close the same window multiple times!)
				QValueList<QWidget*> wins;
				CONST_FOREACH( chat, chat_manager->chats() )
				{
					// add the window to the list
					QWidget *win = (*chat);
					while( win->parent() )
						win = static_cast<QWidget *>(win->parent());
					if( wins.contains( win ) == 0 )  // if this window is not on the list yet
						wins.push_back( win );
				}
				// close the windows from the list
				for( QValueList<QWidget*>::iterator I = wins.begin(); I != wins.end(); I++ )
				{
					(*I)->close();
				}
			}
			else if( hotkey->equals( hotkeys["OpenChatWith"] ) )
			{
				if( openchatwithwindow.isNull() )  // if the "open chat with ..." window is not opened
				{
					// open the window
					openchatwithwindow = new OpenChatWith( kadu );
					openchatwithwindow->move(
							( qApp->desktop()->width()  - openchatwithwindow->width()  ) / 2,
							( qApp->desktop()->height() - openchatwithwindow->height() ) / 2
						);
					openchatwithwindow->show();
					openchatwithwindow->raise();
					activateWindow( openchatwithwindow->winId() );
				}
				else
				{
					// close and delete the window
					openchatwithwindow->close( true );
				}
			}


			// thread: unlock application
			qApp->unlock();
		}
	}
}




GlobalHotkeys::GlobalHotkeys() : QObject( NULL, "globalhotkeys" )
{
	// configuration handling
	createDefaultConfiguration();
	// read the configuration and force its usage
	display = NULL;
	configurationUpdated();
}


GlobalHotkeys::~GlobalHotkeys()
{
	if( hotkeyThread.running() )
	{
		hotkeyThread.terminate();
		hotkeyThread.wait();
	}
	if( display != NULL )
	{
		XCloseDisplay( display );
	}
}


void GlobalHotkeys::mainConfigurationWindowCreated( MainConfigurationWindow *mainConfigurationWindow )
{
	ConfigGroupBox *groupKadu  = mainConfigurationWindow->configGroupBox( "Shortcuts", "Global hotkeys", "Kadu"  );
	ConfigGroupBox *groupChats = mainConfigurationWindow->configGroupBox( "Shortcuts", "Global hotkeys", "Chats" );
	new HotkeyEdit( "GlobalHotkeys", "ShowKadusMainWindow"        , "Show Kadu's main window"           , "", groupKadu  );
	new HotkeyEdit( "GlobalHotkeys", "HideKadusMainWindow"        , "Hide Kadu's main window"           , "", groupKadu  );
	new HotkeyEdit( "GlobalHotkeys", "ShowHideKadusMainWindow"    , "Show/hide Kadu's main window"      , "", groupKadu  );
	new HotkeyEdit( "GlobalHotkeys", "OpenIncomingChatWindow"     , "Open incoming chat window"         , "", groupChats );
	new HotkeyEdit( "GlobalHotkeys", "OpenAllIncomingChatWindows" , "Open all incoming chat windows"    , "", groupChats );
	new HotkeyEdit( "GlobalHotkeys", "MinimizeOpenedChatWindows"  , "Minimize all opened chat windows"  , "", groupChats );
	new HotkeyEdit( "GlobalHotkeys", "RestoreMinimizedChatWindows", "Restore all minimized chat windows", "", groupChats );
	new HotkeyEdit( "GlobalHotkeys", "CloseAllChatWindows"        , "Close all chat windows"            , "", groupChats );
	new HotkeyEdit( "GlobalHotkeys", "OpenChatWith"               , "Open chat with ..."                , "", groupChats );

}


void GlobalHotkeys::createDefaultConfiguration()
{
	config_file.addVariable( "GlobalHotkeys", "ShowKadusMainWindow"        , "" );
	config_file.addVariable( "GlobalHotkeys", "HideKadusMainWindow"        , "" );
	config_file.addVariable( "GlobalHotkeys", "ShowHideKadusMainWindow"    , "" );
	config_file.addVariable( "GlobalHotkeys", "OpenIncomingChatWindow"     , "" );
	config_file.addVariable( "GlobalHotkeys", "OpenAllIncomingChatWindows" , "" );
	config_file.addVariable( "GlobalHotkeys", "MinimizeOpenedChatWindows"  , "" );
	config_file.addVariable( "GlobalHotkeys", "RestoreMinimizedChatWindows", "" );
	config_file.addVariable( "GlobalHotkeys", "CloseAllChatWindows"        , "" );
	config_file.addVariable( "GlobalHotkeys", "OpenChatWith"               , "" );

}


void GlobalHotkeys::configurationUpdated()
{
	// update configuration data
	showKadusMainWindow         = config_file.readEntry( "GlobalHotkeys", "ShowKadusMainWindow"         ).stripWhiteSpace();
	hideKadusMainWindow         = config_file.readEntry( "GlobalHotkeys", "HideKadusMainWindow"         ).stripWhiteSpace();
	showHideKadusMainWindow     = config_file.readEntry( "GlobalHotkeys", "ShowHideKadusMainWindow"     ).stripWhiteSpace();
	openIncomingChatWindow      = config_file.readEntry( "GlobalHotkeys", "OpenIncomingChatWindow"      ).stripWhiteSpace();
	openAllIncomingChatWindows  = config_file.readEntry( "GlobalHotkeys", "OpenAllIncomingChatWindows"  ).stripWhiteSpace();
	minimizeOpenedChatWindows   = config_file.readEntry( "GlobalHotkeys", "MinimizeOpenedChatWindows"   ).stripWhiteSpace();
	restoreMinimizedChatWindows = config_file.readEntry( "GlobalHotkeys", "RestoreMinimizedChatWindows" ).stripWhiteSpace();
	closeAllChatWindows         = config_file.readEntry( "GlobalHotkeys", "CloseAllChatWindows"         ).stripWhiteSpace();
	openChatWith                = config_file.readEntry( "GlobalHotkeys", "OpenChatWith"                ).stripWhiteSpace();

	// stop hotkeys thread
	if( hotkeyThread.running() )
	{
		hotkeyThread.terminate();
		hotkeyThread.wait();
	}
	// new X11 display
	if( display != NULL )
	{
		XCloseDisplay( display );
	}
	display = XOpenDisplay( 0 );
	hotkeyThread.setDisplay( display );
	// update and grab hotkeys
	QMap<QString,Hotkey*> hotkeys;
	hotkeys.insert( "ShowKadusMainWindow"        , new Hotkey( showKadusMainWindow         ) );
	hotkeys.insert( "HideKadusMainWindow"        , new Hotkey( hideKadusMainWindow         ) );
	hotkeys.insert( "ShowHideKadusMainWindow"    , new Hotkey( showHideKadusMainWindow     ) );
	hotkeys.insert( "OpenIncomingChatWindow"     , new Hotkey( openIncomingChatWindow      ) );
	hotkeys.insert( "OpenAllIncomingChatWindows" , new Hotkey( openAllIncomingChatWindows  ) );
	hotkeys.insert( "MinimizeOpenedChatWindows"  , new Hotkey( minimizeOpenedChatWindows   ) );
	hotkeys.insert( "RestoreMinimizedChatWindows", new Hotkey( restoreMinimizedChatWindows ) );
	hotkeys.insert( "CloseAllChatWindows"        , new Hotkey( closeAllChatWindows         ) );
	hotkeys.insert( "OpenChatWith"               , new Hotkey( openChatWith                ) );

	hotkeyThread.setHotkeys( hotkeys );
	grabHotkeys( hotkeys );
	// start hotkeys thread
	hotkeyThread.start();
}


void GlobalHotkeys::grabHotkeys( QMap<QString,Hotkey*> hotkeys )
{
	uint modifiers;
	for( QMap<QString,Hotkey*>::Iterator I = hotkeys.begin(); I != hotkeys.end(); I++ )
	{
		if( (*I)->keycode != 0 )
		{
			modifiers = 0;
			modifiers |= ( (*I)->shift   ? GLOBALHOTKEYS_X11SHIFTMASK   : 0 );
			modifiers |= ( (*I)->control ? GLOBALHOTKEYS_X11CONTROLMASK : 0 );
			modifiers |= ( (*I)->alt     ? GLOBALHOTKEYS_X11ALTMASK     : 0 );
			modifiers |= ( (*I)->altgr   ? GLOBALHOTKEYS_X11ALTGRMASK   : 0 );
			modifiers |= ( (*I)->super   ? GLOBALHOTKEYS_X11SUPERMASK   : 0 );
			// install empty messages handler to avoid warning being printed to the output
			messageshandled = 0;
			QtMsgHandler previousmsghandler = qInstallMsgHandler( EmptyMsgHandler );
			// pure hotkey
			XGrabKey(
				display, (*I)->keycode, modifiers,
				DefaultRootWindow( display ), False, GrabModeAsync, GrabModeAsync
				);
			// hotkey with CapsLock
			XGrabKey(
				display, (*I)->keycode, modifiers | GLOBALHOTKEYS_X11CAPSLOCKMASK,
				DefaultRootWindow( display ), False, GrabModeAsync, GrabModeAsync
				);
			// hotkey with NumLock
			XGrabKey(
				display, (*I)->keycode, modifiers | GLOBALHOTKEYS_X11NUMLOCKMASK,
				DefaultRootWindow( display ), False, GrabModeAsync, GrabModeAsync
				);
			// hotkey with CapsLock and NumLock
			XGrabKey(
				display, (*I)->keycode, modifiers | GLOBALHOTKEYS_X11CAPSLOCKMASK | GLOBALHOTKEYS_X11NUMLOCKMASK,
				DefaultRootWindow( display ), False, GrabModeAsync, GrabModeAsync
				);
			// sync the X11 connection, so that we are sure the X11 errors will be handled now
			XSync( display, false );
			// install the previous messages handler
			qInstallMsgHandler( previousmsghandler );
			// check for X11 errors
			if( messageshandled > 0 )
			{
				QMessageBox *messagebox = new QMessageBox(
						qApp->translate( "@default", "Kadu - Global hotkeys" ),
						qApp->translate( "@default", "Hotkey %% is used by another application." ).replace( "%%", (*I)->comment ),
						QMessageBox::Warning,
						QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton,
						qApp->activeWindow()
					);
				messagebox->show();
				messageshandled = 0;
			}
		}
	}
}




bool HotkeyEdit::x11Event( XEvent *event )
{
	if( ( event->type == KeyPress ) || ( event->type == KeyRelease ) )  // is it mouse KeyPress or KeyRelease event?
	{
		// event data
		uint keycode = event->xkey.keycode;
		KeySym keysym = XKeycodeToKeysym( qt_xdisplay(), keycode, 0 );
		// result string
		QString hotkeystring = "";
		// get the modifiers
		bool mod_shift;
		bool mod_control;
		bool mod_alt;
		bool mod_altgr;
		bool mod_super;
		mod_shift   = ( ( event->xkey.state & GLOBALHOTKEYS_X11SHIFTMASK   ) != 0 );
		mod_control = ( ( event->xkey.state & GLOBALHOTKEYS_X11CONTROLMASK ) != 0 );
		mod_alt     = ( ( event->xkey.state & GLOBALHOTKEYS_X11ALTMASK     ) != 0 );
		mod_altgr   = ( ( event->xkey.state & GLOBALHOTKEYS_X11ALTGRMASK   ) != 0 );
		mod_super   = ( ( event->xkey.state & GLOBALHOTKEYS_X11SUPERMASK   ) != 0 );
		// checking events
		if( event->type == KeyPress )  // is it mouse KeyPress event?
		{
			// build hotkeystring
			if(
				( keysym != GLOBALHOTKEYS_X11LSHIFT   ) &&
				( keysym != GLOBALHOTKEYS_X11RSHIFT   ) &&
				( keysym != GLOBALHOTKEYS_X11LCONTROL ) &&
				( keysym != GLOBALHOTKEYS_X11RCONTROL ) &&
				( keysym != GLOBALHOTKEYS_X11LALT     ) &&
				( keysym != GLOBALHOTKEYS_X11RALT     ) &&
				( keysym != GLOBALHOTKEYS_X11ALTGR    ) &&
				( keysym != GLOBALHOTKEYS_X11LSUPER   ) &&
				( keysym != GLOBALHOTKEYS_X11RSUPER   )
			)
			{
				if(
						( ! mod_shift ) && ( ! mod_control ) && ( ! mod_alt ) && ( ! mod_altgr ) && ( ! mod_super ) &&
						( keysym == GLOBALHOTKEYS_CONFIGURATIONCLEARKEY )
					)
				{
					// clear key was pressed
					setText( "" );
					lastvalidvalue = "";
				}
				else
				{
					hotkeystring = "";
					hotkeystring += mod_shift   ? "Shift+"   : "";
					hotkeystring += mod_control ? "Control+" : "";
					hotkeystring += mod_alt     ? "Alt+"     : "";
					hotkeystring += mod_altgr   ? "AltGr+"   : "";
					hotkeystring += mod_super   ? "Super+"   : "";
					// keysym string or key code number
					QString keystring;
					if( keysym != NoSymbol )
					{
						keystring = XKeysymToString( keysym );
						if( keystring.isEmpty() )
						{
							// keycode instead of keysym string
							keystring.setNum( keycode );
						}
						else
						{
							// if the keysym is a single lowercase letter
							if( ( keystring.length() == 1 ) && ( keystring.at(0) >= 'a' ) && ( keystring.at(0) <= 'z' ) )
							{
								// make the letter uppercase (just to have a nice looking shortcut)
								keystring = keystring.upper();
							}
						}
					}
					else
					{
						// keycode instead of keysym string
						keystring.setNum( keycode );
					}
					hotkeystring += keystring;
					// set edit field text
					setText( hotkeystring );
				}
			}
			else
			{
				if( ( keysym == GLOBALHOTKEYS_X11LSHIFT   ) || ( keysym == GLOBALHOTKEYS_X11RSHIFT   ) )
					mod_shift   = true;
				if( ( keysym == GLOBALHOTKEYS_X11LCONTROL ) || ( keysym == GLOBALHOTKEYS_X11RCONTROL ) )
					mod_control = true;
				if( ( keysym == GLOBALHOTKEYS_X11LALT     ) || ( keysym == GLOBALHOTKEYS_X11RALT     ) )
					mod_alt     = true;
				if(   keysym == GLOBALHOTKEYS_X11ALTGR                                                 )
					mod_altgr   = true;
				if( ( keysym == GLOBALHOTKEYS_X11LSUPER   ) || ( keysym == GLOBALHOTKEYS_X11RSUPER   ) )
					mod_super   = true;
				hotkeystring = "";
				hotkeystring += mod_shift   ? "Shift+"   : "";
				hotkeystring += mod_control ? "Control+" : "";
				hotkeystring += mod_alt     ? "Alt+"     : "";
				hotkeystring += mod_altgr   ? "AltGr+"   : "";
				hotkeystring += mod_super   ? "Super+"   : "";
				// set edit field text
				setText( hotkeystring );
			}
		}
		else if( event->type == KeyRelease )
		{
			if( ( ! text().isEmpty() ) && ( text().at( text().length() - 1 ) == '+' ) )  // if the hotkey typing is not finished yet ("+" at the end)
			{
				if( ! (
					( keysym != GLOBALHOTKEYS_X11LSHIFT   ) &&
					( keysym != GLOBALHOTKEYS_X11RSHIFT   ) &&
					( keysym != GLOBALHOTKEYS_X11LCONTROL ) &&
					( keysym != GLOBALHOTKEYS_X11RCONTROL ) &&
					( keysym != GLOBALHOTKEYS_X11LALT     ) &&
					( keysym != GLOBALHOTKEYS_X11RALT     ) &&
					( keysym != GLOBALHOTKEYS_X11ALTGR    ) &&
					( keysym != GLOBALHOTKEYS_X11LSUPER   ) &&
					( keysym != GLOBALHOTKEYS_X11RSUPER   )
				) )
				{
					if( ( keysym == GLOBALHOTKEYS_X11LSHIFT   ) || ( keysym == GLOBALHOTKEYS_X11RSHIFT   ) )
						mod_shift   = false;
					if( ( keysym == GLOBALHOTKEYS_X11LCONTROL ) || ( keysym == GLOBALHOTKEYS_X11RCONTROL ) )
						mod_control = false;
					if( ( keysym == GLOBALHOTKEYS_X11LALT     ) || ( keysym == GLOBALHOTKEYS_X11RALT     ) )
						mod_alt     = false;
					if(   keysym == GLOBALHOTKEYS_X11ALTGR                                                 )
						mod_altgr   = false;
					if( ( keysym == GLOBALHOTKEYS_X11LSUPER   ) || ( keysym == GLOBALHOTKEYS_X11RSUPER   ) )
						mod_super   = false;
					hotkeystring = "";
					hotkeystring += mod_shift   ? "Shift+"   : "";
					hotkeystring += mod_control ? "Control+" : "";
					hotkeystring += mod_alt     ? "Alt+"     : "";
					hotkeystring += mod_altgr   ? "AltGr+"   : "";
					hotkeystring += mod_super   ? "Super+"   : "";
					// set edit field text
					if( ! hotkeystring.isEmpty() )
					{
						setText( hotkeystring );
					}
					else
					{
						// reset the text to the last valid value
						setText( lastvalidvalue );
					}
				}
			}
			else
			{
				lastvalidvalue = text();
			}
		}
		// don't forward the event
		return true;
	}
	else if( ( event->type == ButtonPress ) || ( event->type == ButtonRelease ) )  // is it mouse ButtonPress or ButtonRelease event?
	{
		if( event->xbutton.button == Button1 )  // is if left mouse button?
		{
			return false;  // forward the event to Qt
		}
		return true;  // don't forward the event
	}
	return false;  // forward the event to Qt
}


void HotkeyEdit::focusInEvent( QFocusEvent *event )
{
	lastvalidvalue = text();
	// important: call the default focusInEvent
	QLineEdit::focusInEvent( event );
}


void HotkeyEdit::focusOutEvent( QFocusEvent *event )
{
	if( text().at( text().length() - 1 ) == '+' )  // if the hotkey typing is not finished yet ("+" at the end)
	{
		// reset the text to the last valid value
		setText( lastvalidvalue );
	}
	// important: call the default focusOutEvent
	QLineEdit::focusOutEvent( event );
}




void EmptyMsgHandler( QtMsgType type, const char *msg )
{
	messageshandled = 1;
}
