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

Copyright (c) 1996-2001 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 <qtooltip.h>

#include <klocale.h>
#include <kapplication.h>
#include <kdebug.h>

#include "config.h"

#include "kicker.h"
#include "k_mnu.h"

#include "kbutton.h"
#include "kbutton.moc"

#if defined(HAVE_XTEST)
#   define XK_MISCELLANY
#   include <X11/Xlib.h>
#   include <X11/keysymdef.h>
#   include <X11/extensions/XTest.h>

    static unsigned short g_keycodeForSuperL, g_keycodeForSuperR;
#endif

KButton::KButton( QWidget* parent )
    : PanelPopupButton( parent, "KButton" )
{
    QToolTip::add(this, i18n("Click here to browse and start applications"));
    setTitle(i18n("K Menu"));

    setPopup( Kicker::kicker()->KMenu() );
    Kicker::kicker()->setKButton( this );
    setIcon("kmenu");

#if defined(HAVE_XTEST)
    // Get the Win keycodes
    // FIXME: This should be retrieved from KKeyServer, since the Win key might not be mapped to Super
    g_keycodeForSuperL = XKeysymToKeycode( qt_xdisplay(), XK_Super_L );
    g_keycodeForSuperR = XKeysymToKeycode( qt_xdisplay(), XK_Super_R );

    // Make sure that the Win key is not in repeat-when-pressed mode
    XKeyboardControl kbd;
    kbd.auto_repeat_mode = AutoRepeatModeOff;
    kbd.key = g_keycodeForSuperL;
    XChangeKeyboardControl( qt_xdisplay(), KBKey | KBAutoRepeatMode, &kbd );
    kbd.key = g_keycodeForSuperR;
    XChangeKeyboardControl( qt_xdisplay(), KBKey | KBAutoRepeatMode, &kbd );

    // Grab the Win keys
    if( g_keycodeForSuperL || g_keycodeForSuperR ) {
        //kdDebug(125) << "g_keycodeForSuperL = " << g_keycodeForSuperL
        //    << ", g_keycodeForSuperR = " << g_keycodeForSuperR << endl;
        if( g_keycodeForSuperL )
            XGrabKey( qt_xdisplay(), g_keycodeForSuperL, 0, qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
        if( g_keycodeForSuperR )
            XGrabKey( qt_xdisplay(), g_keycodeForSuperR, 0, qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
        kapp->installX11EventFilter( this );
    }
#endif
}

KButton::~KButton()
{
    if (Kicker::kicker())
       Kicker::kicker()->setKButton( 0 );
}

void KButton::properties()
{
    KApplication::startServiceByDesktopName("kmenuedit", QStringList(),
                                            0, 0, 0, "", true);
}

void KButton::initPopup()
{
//    kdDebug(1210) << "KButton::initPopup()" << endl;

    // this hack is required to ensure the correct popup position
    // when the size of the recent application part of the menu changes
    // please don't remove this _again_
    Kicker::kicker()->KMenu()->initialize();
}

#if defined(HAVE_XTEST)
extern Time qt_x_time;

#define UNGRAB() \
            XUngrabKeyboard( qt_xdisplay(), event->xkey.time ); \
            XUngrabPointer( qt_xdisplay(), event->xkey.time ); \
            s_bWatching = false

bool KButton::x11Event( XEvent* event )
{
    static bool s_bWatching = false;
    bool bHandled = false;

    if( !s_bWatching ) {
        if( event->type == KeyPress ) {
            //kdDebug(125) << "key pressed: window " << event->xkey.window << endl;
            // 115 = Super_L
            if (event->xkey.state == 0 && event->xkey.keycode &&
                (event->xkey.keycode == g_keycodeForSuperL ||
                 event->xkey.keycode == g_keycodeForSuperR)) {
                if( XGrabKeyboard(qt_xdisplay(), qt_xrootwin(), FALSE, GrabModeAsync, GrabModeAsync, qt_x_time) == GrabSuccess ) {
                    //kdDebug(125) << "grab!" << endl;
                    XGrabPointer( qt_xdisplay(), qt_xrootwin(), FALSE, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, qt_x_time );
                    s_bWatching = true;
                    bHandled = true;
                }
            }
        }
    }
    else {
        if( event->type == KeyPress ) {
            UNGRAB();
            XTestFakeKeyEvent( qt_xdisplay(), event->xkey.keycode, true, 0 );
            //kdDebug(125) << "sent" << endl;
            bHandled = true;
        }
        else if( event->type == KeyRelease ) {
            UNGRAB();
            if (event->xkey.keycode &&
                (event->xkey.keycode == g_keycodeForSuperL ||
                 event->xkey.keycode == g_keycodeForSuperR))
            {
                //kdDebug(125) << "Win released! " << popup()->isVisible() << endl;
                if (popup()->isVisible())
                    popup()->hide();
                else
                    slotExecMenu();
            }
            bHandled = true;
        }
        else if( event->type == ButtonPress || event->type == ButtonRelease ) {
            UNGRAB();
            XTestFakeButtonEvent( qt_xdisplay(), event->xbutton.button, event->type == ButtonPress, 0 );
            bHandled = true;
        }
    }

    return bHandled;
}
#else
bool KButton::x11Event( XEvent* )
{
    return false;
}
#endif
