// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*-
/* This file is part of the KDE project
   Copyright (C) 2004  Esben Mose Hansen <kde@mosehansen.dk>
   Copyright (C) by Andrew Stanley-Jones
   Copyright (C) 2000 by Carsten Pfeiffer <pfeiffer@kde.org>

   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; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <khelpmenu.h>
#include <klineedit.h>
#include <klocale.h>
#include <kwindowsystem.h>
#include <kdebug.h>

#include "klipperpopup.h"
#include "history.h"
#include "klipper.h"
#include "popupproxy.h"

namespace {
    static const int TOP_HISTORY_ITEM_INDEX = 2;
}

// #define DEBUG_EVENTS__

#ifdef DEBUG_EVENTS__
kdbgstream& operator<<( kdbgstream& stream, const QKeyEvent& e ) {
    stream << "(QKeyEvent(text=" << e.text() << ",key=" << e.key() << ( e.isAccepted()?",accepted":",ignored)" ) << ",count=" << e.count();
    if ( e.modifiers() & Qt::AltModifier ) {
        stream << ",ALT";
    }
    if ( e.modifiers() & Qt::ControlModifier ) {
        stream << ",CTRL";
    }
    if ( e.modifiers() & Qt::MetaModifier ) {
        stream << ",META";
    }
    if ( e.modifiers() & Qt::ShiftModifier ) {
        stream << ",SHIFT";
    }
    if ( e.isAutoRepeat() ) {
        stream << ",AUTOREPEAT";
    }
    stream << ")";

    return stream;
}
#endif

/**
 * Exactly the same as KLineEdit, except that ALL key events are swallowed.
 *
 * We need this to avoid infinite loop when sending events to the search widget
 */
class KLineEditBlackKey : public KLineEdit {
public:
    KLineEditBlackKey( QWidget* parent )
        : KLineEdit( parent )
        {}

    ~KLineEditBlackKey() {
    }
protected:
    virtual void keyPressEvent( QKeyEvent* e ) {
        KLineEdit::keyPressEvent( e );
        e->accept();

    }

};

KlipperPopup::KlipperPopup( History* history )
    : m_dirty( true ),
      m_qsEmpty( i18n( "<empty clipboard>" ) ),
      m_qsNoMatch( i18n( "<no matches>" ) ),
      m_history( history ),
      m_helpmenu( new KHelpMenu( this, Klipper::aboutData(), false ) ),
      m_popupProxy( 0 ),
      m_filterWidget( 0 ),
      m_filterWidgetAction( 0 ),
      m_nHistoryItems( 0 )
{
    KWindowInfo i = KWindowSystem::windowInfo( winId(), NET::WMGeometry );
    QRect g = i.geometry();
    QRect screen = KGlobalSettings::desktopGeometry(g.center());
    int menu_height = ( screen.height() ) * 3/4;
    int menu_width = ( screen.width() )  * 1/3;

    m_popupProxy = new PopupProxy( this, menu_height, menu_width );

    connect( this, SIGNAL( aboutToShow() ), SLOT( slotAboutToShow() ) );
}

KlipperPopup::~KlipperPopup() {

}

void KlipperPopup::slotAboutToShow() {
    if ( m_filterWidget ) {
        if ( !m_filterWidget->text().isEmpty() ) {
            m_dirty = true;
            m_filterWidget->clear();
            m_filterWidgetAction->setVisible(false);
        }
    }
    ensureClean();

}

void KlipperPopup::ensureClean() {
    // If the history is unchanged since last menu build, the is no reason
    // to rebuild it,
    if ( m_dirty ) {
        rebuild();
    }

}

void KlipperPopup::buildFromScratch() {
    addTitle(KIcon("klipper"), i18n("Klipper - Clipboard Tool"));

    m_filterWidget = new KLineEditBlackKey(this);
    m_filterWidgetAction = new QWidgetAction(this);
    m_filterWidgetAction->setDefaultWidget(m_filterWidget);

    addAction(m_filterWidgetAction);
    m_filterWidget->setFocusPolicy( Qt::NoFocus );
    m_filterWidgetAction->setVisible(false);

    QListIterator<QAction *> i(m_actions);
    for (int i = 0; i < m_actions.count(); i++) {
        if (i == 0)
            addSeparator();

        if (i + 1 == m_actions.count()) {
            addMenu(m_helpmenu->menu())->setIcon(KIcon("help-contents"));
            addSeparator();
        }

        addAction(m_actions.at(i));
    }

    if ( KGlobalSettings::insertTearOffHandle() ) {
        setTearOffEnabled(true);
    }

}

void KlipperPopup::rebuild( const QString& filter ) {
    if (actions().isEmpty()) {
        buildFromScratch();
    } else {
        for ( int i=0; i<m_nHistoryItems; i++ ) {
            Q_ASSERT(TOP_HISTORY_ITEM_INDEX < actions().count());
            removeAction(actions().at(TOP_HISTORY_ITEM_INDEX));
        }
    }

    // We search case insensitive until no uppercased characters appear in the search term
    Qt::CaseSensitivity caseSens = (filter.toLower() == filter ? Qt::CaseInsensitive : Qt::CaseSensitive);
    QRegExp filterexp( filter, caseSens );

    QPalette palette = m_filterWidget->palette();
    if ( filterexp.isValid() ) {
        palette.setColor( m_filterWidget->foregroundRole(), palette.color(foregroundRole()) );
    } else {
        palette.setColor( m_filterWidget->foregroundRole(), Qt::red );
    }
    m_nHistoryItems = m_popupProxy->buildParent( TOP_HISTORY_ITEM_INDEX, filterexp );
    if ( m_nHistoryItems == 0 ) {
        if ( m_history->empty() ) {
            insertAction(actions().at(TOP_HISTORY_ITEM_INDEX), new QAction(m_qsEmpty, this));
        } else {
            palette.setColor( m_filterWidget->foregroundRole(), Qt::red );
            insertAction(actions().at(TOP_HISTORY_ITEM_INDEX), new QAction(m_qsNoMatch, this));
        }
        m_nHistoryItems++;
    } else {
        if ( history()->topIsUserSelected() ) {
            actions().at(TOP_HISTORY_ITEM_INDEX)->setCheckable(true);
            actions().at(TOP_HISTORY_ITEM_INDEX)->setChecked(true);
        }
    }
    m_filterWidget->setPalette( palette );
    m_dirty = false;
}

void KlipperPopup::plugAction( QAction* action ) {
    m_actions.append(action);
}


/* virtual */
void KlipperPopup::keyPressEvent( QKeyEvent* e ) {
    // If alt-something is pressed, select a shortcut
    // from the menu. Do this by sending a keyPress
    // without the alt-modifier to the superobject.
    if ( e->modifiers() & Qt::AltModifier ) {
        QKeyEvent ke( QEvent::KeyPress,
                      e->key(),
                      e->modifiers() ^ Qt::AltModifier,
                      e->text(),
                      e->isAutoRepeat(),
                      e->count() );
        KMenu::keyPressEvent( &ke );
#ifdef DEBUG_EVENTS__
        kDebug() << "Passing this event to ancestor (KMenu): " << e << "->" << ke;
#endif
        if (ke.isAccepted()) {
            e->accept();
            return;
        } else {
            e->ignore();
        }
    }

    // Otherwise, send most events to the search
    // widget, except a few used for navigation:
    // These go to the superobject.
    switch( e->key() ) {
    case Qt::Key_Up:
    case Qt::Key_Down:
    case Qt::Key_Right:
    case Qt::Key_Left:
    case Qt::Key_Tab:
    case Qt::Key_Backtab:
    case Qt::Key_Escape:
    case Qt::Key_Return:
    case Qt::Key_Enter:
    {
#ifdef DEBUG_EVENTS__
        kDebug() << "Passing this event to ancestor (KMenu): " << e;
#endif
        KMenu::keyPressEvent(e);
        if (activeAction() ==  m_filterWidgetAction)
            setActiveAction(actions().at(TOP_HISTORY_ITEM_INDEX));

        break;
    }
    default:
    {
#ifdef DEBUG_EVENTS__
        kDebug() << "Passing this event down to child (KLineEdit): " << e;
#endif
        setActiveAction(actions().at(actions().indexOf(m_filterWidgetAction)));
        QString lastString = m_filterWidget->text();
        QApplication::sendEvent(m_filterWidget, e);

        if (m_filterWidget->text().isEmpty()) {
            if (m_filterWidgetAction->isVisible())
                m_filterWidgetAction->setVisible(false);
        }
        else if (!m_filterWidgetAction->isVisible() )
            m_filterWidgetAction->setVisible(true);

        if (m_filterWidget->text() != lastString) {
            slotHistoryChanged();
            rebuild(m_filterWidget->text());
        }

        break;
    } //default:
    } //case
}


void KlipperPopup::slotSetTopActive()
{
  if (actions().size() > TOP_HISTORY_ITEM_INDEX) {
    setActiveAction(actions().at(TOP_HISTORY_ITEM_INDEX));
  }
}

#include "klipperpopup.moc"
