/***************************************************************************
 *   Copyright (C) 2003-2005 by liuspider                                  *
 *   liuspider@users.sourceforge.net                                       *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/
#define Uses_SCIM_EVENT
#define USE_SCIM_KDE_SETTINGS
#include <skimpluginmanager.h>

#include <qlabel.h>
#include <qcheckbox.h>
#include <qlayout.h>
#include <qtooltip.h> 
#include <klocale.h>
#include <kkeynative.h> 

#include <klineedit.h>
#include <qmessagebox.h>
#include <keditlistbox.h>

#include "skimkeygrabber.h"

#if defined(Q_WS_X11)
#include <X11/Xlib.h>
#include <x11/scim_x11_utils.h>
const int XKeyPress = KeyPress;
const int XKeyRelease = KeyRelease;
#undef KeyPress
#undef KeyRelease

inline void checkBoxEnabled(bool is_mask, QCheckBox * checkbox)
{
    if(is_mask)
        checkbox->setChecked(true);
}

inline void hideCheckBox(bool is_enabled, QCheckBox * checkbox)
{
    if(!is_enabled)
        checkbox->hide();
}

#endif

class SkimKeyGrabber::SkimKeyGrabberPrivate
{
    public:
        scim::KeyboardLayout   keyboard_layout;
        int                    valid_key_mask;
#ifdef Q_WS_X11
        scim::KeyEvent         scimkey;
        Display               *m_display;
#else
        QValueList<int>        keys;
#endif
};

SkimKeyGrabber::SkimKeyGrabber(QWidget *parent, const char *name)
 : KDialogBase(KDialogBase::Plain, 0, parent, name, true, i18n("Grab Key"), KDialogBase::Cancel)
{
    setInputMethodEnabled(false);
    enableButtonOK(false);
    enableButtonCancel(true);
    QVBoxLayout * vb = new QVBoxLayout(plainPage());
    QLabel * lb = new QLabel(i18n("<center>Please press shortcut...</center><p>When you release a key this dialog will be closed automatically.</p>"), plainPage());
    lb->setAlignment(Qt::WordBreak);
//     lb->setAlignment(Qt::AlignCenter);
    vb->addWidget(lb);
    vb->addItem(new QSpacerItem(0, 10));
    QHBoxLayout * hb = new QHBoxLayout(vb, 3);
    ctrl = new QCheckBox(i18n("Control"), plainPage());
    hb->addWidget(ctrl);
    alt = new QCheckBox(i18n("Alt"), plainPage());
    hb->addWidget(alt);
    shift = new QCheckBox(i18n("Shift"), plainPage());
    hb->addWidget(shift);
    release = new QCheckBox(i18n("Release"), plainPage());
    hb->addWidget(release);

    d = new SkimKeyGrabberPrivate();
    d->keyboard_layout = scim::scim_get_default_keyboard_layout ();
    scim::KeyEvent key;
    scim::scim_string_to_key (key, scim::String(ScimKdeSettings::_Hotkeys_FrontEnd_ValidKeyMask().latin1()));

    d->valid_key_mask = (key.mask > 0)?(key.mask):0xFFFF;
    d->valid_key_mask |= scim::SCIM_KEY_ReleaseMask;

    hideCheckBox(key.is_alt_down(), alt);
    hideCheckBox(key.is_control_down(), ctrl);
    hideCheckBox(key.is_shift_down(), shift);
#ifdef Q_WS_X11
    QHBoxLayout * hb2 = new QHBoxLayout(vb, 3);
    capslock = new QCheckBox(i18n("CapsLock"), plainPage());
    hb2->addWidget(capslock);
    meta = new QCheckBox(i18n("Meta"), plainPage());
    hb2->addWidget(meta);
    hyper = new QCheckBox(i18n("Hyper"), plainPage());
    hb2->addWidget(hyper);
    super = new QCheckBox(i18n("Super"), plainPage());
    hb2->addWidget(super);

    hideCheckBox(key.is_caps_lock_down(), capslock);
    hideCheckBox(key.is_meta_down(), meta);
    hideCheckBox(key.is_hyper_down(), hyper);
    hideCheckBox(key.is_super_down(), super);

    d->m_display = qt_xdisplay();
#endif
}

SkimKeyGrabber::~SkimKeyGrabber()
{
    delete d;
}

#ifdef Q_WS_X11
void SkimKeyGrabber::addCheckModifier(QCheckBox * checkbox, UINT16 modifier)
{
    if(checkbox->isChecked())
        d->scimkey.mask |= modifier;
}

#endif

int SkimKeyGrabber::exec() {
    grabKeyboard();
#ifdef Q_WS_X11
    d->scimkey.mask = scim::SCIM_KEY_NullMask;
#endif
    int result = KDialogBase::exec();

    if( result == QDialog::Accepted)
    {
#ifdef Q_WS_X11
        addCheckModifier(release, scim::SCIM_KEY_ReleaseMask);
        addCheckModifier(ctrl, scim::SCIM_KEY_ControlMask);
        addCheckModifier(alt, scim::SCIM_KEY_AltMask);
        addCheckModifier(shift, scim::SCIM_KEY_ShiftMask);
        addCheckModifier(capslock, scim::SCIM_KEY_CapsLockMask);
        addCheckModifier(meta, scim::SCIM_KEY_MetaMask);
        addCheckModifier(hyper, scim::SCIM_KEY_HyperMask);
        addCheckModifier(super, scim::SCIM_KEY_SuperMask);

        d->scimkey.mask &= d->valid_key_mask;
        d->scimkey.layout = d->keyboard_layout;

        scim::String s;
        scim_key_to_string(s, d->scimkey);
        finalKeys = QString::fromLatin1(s.c_str());
#else
        QValueList<int> oldkeys = d->keys;
        d->keys.clear();
        checkKeys(Qt::Key_Control, oldkeys, d->keys, ctrl);
        checkKeys(Qt::Key_Shift, oldkeys, d->keys, shift);
        checkKeys(Qt::Key_Alt, oldkeys, d->keys, alt);
        d->keys += oldkeys;
        releaseKeyboard();

        for(uint i = 0; i<d->keys.size(); i++) {
            int qtKeyCode = d->keys[i];
            int mod;
            switch (qtKeyCode) {
                case Qt::Key_Control:
                    mod = scim::SCIM_KEY_ControlMask;
                break;
                case Qt::Key_Shift:
                    mod = scim::SCIM_KEY_ShiftMask;
                break;
                case Qt::Key_Alt:
                    mod = scim::SCIM_KEY_AltMask;
                break;
                case Qt::Key_Meta:
                    mod = scim::SCIM_KEY_MetaMask;
                break;
                default:
                    mod = scim::SCIM_KEY_NullMask;
            }
        KKeyNative b = KKey(qtKeyCode);
    //       std::cout << "code mod sym " << b.code() << " " << b.mod() << " " << b.sym() << "\n";

        scim::KeyEvent scimkey(b.sym(), mod);
        scimkey.layout = d->keyboard_layout;
    //       std::cout << " scimkey ascii: " << scimkey.get_ascii_code()<< "\n";
        scim::String s;
        scim_key_to_string(s, scimkey);

        if( i == d->keys.size() - 1 && (s == "Control" || s == "Shift" || s == "Alt") ) {
            if(release->isChecked())
                s += "+" + s;
        }
        finalKeys += QString::fromLatin1(s.c_str()) + "+";
        }
        if(release->isChecked())
            finalKeys += "KeyRelease";
        else
            finalKeys = finalKeys.left(finalKeys.length() - 1);
#endif
    }

    return result;
}
void SkimKeyGrabber::checkKeys(int key, QValueList<int> & oldkeys, QValueList<int> & newkeys, QCheckBox * cb) {
    if(!oldkeys.contains(key) && cb->isChecked()) {
      newkeys.push_back(key);
      oldkeys.remove(key);
    }
}
#ifdef Q_WS_X11
bool SkimKeyGrabber::x11Event ( XEvent * event )
{
    bool ret = false;
    if(event->type == XKeyPress)
    {
        XKeyEvent & xkey = event->xkey;
        d->scimkey = scim_x11_keyevent_x11_to_scim(d->m_display, xkey);

        checkBoxEnabled(d->scimkey.is_shift_down(), shift);
        checkBoxEnabled(d->scimkey.is_control_down(), ctrl);
        checkBoxEnabled(d->scimkey.is_alt_down(), alt);
        checkBoxEnabled(d->scimkey.is_caps_lock_down(), capslock);
        checkBoxEnabled(d->scimkey.is_meta_down(), meta);
        checkBoxEnabled(d->scimkey.is_super_down(), super);
        checkBoxEnabled(d->scimkey.is_hyper_down(), hyper);

        d->scimkey.mask &= d->valid_key_mask;
        ret = true;
    }
    else if(event->type == XKeyRelease)
    {
        KDialogBase::accept();
        ret = true;
    }

    return ret;
}

#else
void SkimKeyGrabber::keyPressEvent ( QKeyEvent * e ) {
    if(!e->isAutoRepeat() && !keys.contains(e->key())) {
        d->keys.push_back(e->key());
        switch (e->key()) {
            case Qt::Key_Control:
                ctrl->setChecked(true);
            break;
            case Qt::Key_Shift:
                shift->setChecked(true);
            break;
            case Qt::Key_Alt:
                alt->setChecked(true);
            break;
        }
    }

    e->accept();
}

void SkimKeyGrabber::keyReleaseEvent ( QKeyEvent * e ) {
    e->accept();
    KDialogBase::accept();
}
#endif

SkimShortcutEditor::SkimShortcutEditor(QWidget *parent, const char *name)
  : QWidget(parent, name)
{
    setInputMethodEnabled(false);
    (new QHBoxLayout(this))->setAutoAdd(true);
    lineEditor = new KLineEdit(this);
    lineEditor->setInputMethodEnabled(false);
    button = new QToolButton(this);
    button->setUsesTextLabel(true);
    button->setTextLabel("...");
    connect(button, SIGNAL(clicked()), SLOT(invokeGrabber()));
}

void SkimShortcutEditor::invokeGrabber() 
{
    SkimKeyGrabber g;
    if(g.exec() == QDialog::Accepted)
      lineEditor->setText(g.getShortcut());
}

SkimShortcutListEditor::SkimShortcutListEditor(QWidget *parent, const char *name)
 : KDialogBase(KDialogBase::Plain, 0, parent, name, true, 
   i18n("Edit Shortcut List"), KDialogBase::Ok | KDialogBase::Cancel)
{
    editor = new SkimShortcutEditor(plainPage());
    KEditListBox::CustomEditor * customeditor = 
      new KEditListBox::CustomEditor(editor, editor->lineEditor);
    listBox = new KEditListBox(i18n("Edit Shortcuts"), *customeditor, plainPage());
    (new QVBoxLayout(plainPage()))->addWidget(listBox);
    connect(listBox, SIGNAL(added(const QString &)), SLOT(verifyShortcut(const QString &)));
}

void SkimShortcutListEditor::setStringList(QStringList & s)
{
  listBox->insertStringList(s);
}

QString SkimShortcutListEditor::getCombinedString()
{
  return listBox->items().join(",");
}

void SkimShortcutListEditor::verifyShortcut(const QString &k)
{
  scim::KeyEventList keylist;
  scim::String str = k.latin1();
  if(!scim::scim_string_to_key_list(keylist, str))
  {
    QListBox * l = listBox->listBox();
    if( QListBoxItem * i = l->findItem(k))
    {
      l->removeItem(l->index(i));
    }
    QMessageBox::warning(this, i18n("Warning"), i18n("\"%1\" is not a valid shortcut!").arg(k),
      QMessageBox::Ok, QMessageBox::NoButton );
  }
}


SkimEditShortcutButton::SkimEditShortcutButton(QWidget *parent, const char *name)
  : QToolButton(parent, name), lineEditor(0) 
{
    setUsesTextLabel(true);
    setTextLabel("...");
    connect(this, SIGNAL(clicked()), SLOT(invokeShortcutListEditor()));
}


void SkimEditShortcutButton::invokeShortcutListEditor()
{
    SkimShortcutListEditor ed;
    QStringList keys = QStringList::split(",", shortcuts);
    ed.setStringList(keys);
    if(ed.exec() == QDialog::Accepted) {
      if(shortcuts != ed.getCombinedString()) {
//         shortcuts = ed.getCombinedString();
        emit setEditorText(ed.getCombinedString());
      }
    }
}
void SkimEditShortcutButton::setShortcuts(const QString & s)
{
    shortcuts = s.simplifyWhiteSpace().replace(' ', "");
}

#include "skimkeygrabber.moc"
