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

 AHistLineEdit

 $$Id: ahistlineedit.cpp,v 1.34.2.1 2003/01/13 22:28:27 asj Exp $$

 An extended KLineEdit with history scroll back and focus controls.

 Function:

   Each time a line is saved, to a max of 256, when the user hits enter.
   The arrow keys are used to scroll through the history list, rolling
   around at the end.

   When focus is gained or lost it emits the approriate signals. This
   is so the toplevel can track who has focus.

 signals:
   gotFocus: duh!

   lostFocus: no shit sherlock

 Implementation:

   protected:

     keyPressEvent: Filter key presses looking for up arrow, down
       arrow or enter.  UpArrow saves the current line at the end then
       scroll. No more processing.  DownArrow does the opposite.  Enter
       saves the line, but then passes on the event for normal
       processing.

     focusInEvent: emits needed signal
     focusOutEvent: ditto

   Variables:
     QStrList: current list of history items.
     current: what I think is the current list item.

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


#include "ahistlineedit.h"
#include "colorpicker.h"
#include "ksopts.h"
#include <qapplication.h>
#include <qclipboard.h>
#include <kdebug.h>
#include <kstdaction.h>
#include <kstdaccel.h>
#include <kshortcut.h>

aHistLineEdit::aHistLineEdit(QWidget *parent, const char *name)
  : KLineEdit(parent, name)
{
  current = hist.append(QString::null); // Set the current as blank
}

void aHistLineEdit::keyPressEvent( QKeyEvent *e )
{
  // we get some key-events via QEvent::AccelOverride. By accept()ing them,
  // we prevent accels being fired.
  bool accept = true;
  bool ignore = false;
  bool callKeyPressEvent = false;


  if ( e->key() != Key_Tab && e->key() != Key_Shift)
    emit notTab();

  // those keycodes correspond to the ones in ksirc.pl in sub hook_fixcolours
  if ( e->state() == ControlButton ) {
    QString s = text();
    int curPos = cursorPosition();
    switch ( e->key() ) {
    case Key_B:
      s.insert( cursorPosition(), 0xde );
      setText(s);
      setCursorPosition(curPos + 1);
      break;
    case Key_U:
      s.insert( cursorPosition(), 0xd7 );
      setText(s);
      setCursorPosition(curPos + 1);
      break;
    case Key_R:
      s.insert( cursorPosition(), 0x9f );
      setText(s);
      setCursorPosition(curPos + 1);
      break;
    case Key_K:
      if ( ksopts->colorPicker ) 
        ColourPickerPopUp();
      else
      {
          s.insert(  cursorPosition(), 0xaa );
          setText(s);
          setCursorPosition(curPos + 1);
      }
      break;
    case Key_I:
      s.insert( cursorPosition(), "~i");
      setText(s);
      setCursorPosition(curPos + 2);
      break;
    default:
        accept = false;
        ignore = false;
        callKeyPressEvent = true;
    }
  }
  else if(e->state() == 0){
    switch(e->key()){
    case Key_Return:
    case Key_Enter:
        doEnterKey();
        accept = false;
        ignore = false;
        callKeyPressEvent = true;
        break;
    case Key_Up:
      if ((*current) != text()) { // change in hist <-> text() then save text 
        *current = text(); 
      }
      if (current == hist.begin()) { // if at begin of list then wrap around 
        current = hist.fromLast(); 
      } else --current; // previous entry 
      setText(*current);
      break;
    case Key_Down:
      if ((*current) != text()) { // change in hist <-> text() then save text 
        *current = text();
      }
      if (current == hist.fromLast()) { // if at end of list then wrap around 
        current = hist.begin();
      } else ++current; // next entry
      setText(*current);
      break;
    default:
      accept = false;
      ignore = false;
      callKeyPressEvent = true;
    }
  }
  // QLineEdit falsely converts Alt+C to the character 'c', so to work around
  //  this, we just don't pass any Alt+? key on to the keyPressEvent() method.
  else if (e->state() == AltButton) {
      switch(e->key()){
      case Key_Return:
      case Key_Enter:
          doEnterKey();
          accept = false;
          ignore = false;
          callKeyPressEvent = true;
          break;
      default:
          accept = false;
          ignore = false;
          callKeyPressEvent = true;
      }
  }
  else if (e->state() == ShiftButton){
      switch(e->key()){
      case Key_Return:
      case Key_Enter:
          doEnterKey();
          accept = false;
          ignore = false;
          callKeyPressEvent = true;
          break;
      default:
          accept = false;
          ignore = false;
          callKeyPressEvent = true;
      }
  }
  else {
      accept = false;
      ignore = false;
      callKeyPressEvent = true;
  }

  if ( accept ) {
      e->accept();
  }
  else {
      if ( ignore ) {
          e->ignore();
      }
      else {
          if ( callKeyPressEvent ) {
              QLineEdit::keyPressEvent(e);
          }
      }
  }
}

void aHistLineEdit::doEnterKey()
{
    // strategy: always append an empty line to the end
    if ((*current).isEmpty()) { // current is empty
        if (!text().isEmpty()) {
            // text() has something -> store it in current and add a new empty one
            *current = text();
            hist.append(QString::null); // always add empty line at end
            if (hist.count() >= 256) { // if appended check if it will go beyond the max number of entries
                hist.remove(hist.begin()); // if so then delete the first entry
            }
        }
    } else { // current has content
        if (!text().isEmpty()) {
            // both !isEmpty() and != -> append at end
            current = hist.fromLast();     // goto last entry which is empty
            *current = text();             // change content to text()
            hist.append(QString::null);    // always add empty line at end
            if (hist.count() >= 256) {     // if appended check if it will go beyond the max number of entries
                hist.remove(hist.begin()); // if so then delete the first entry
            }
        }
    }
    current = hist.fromLast(); // set current history item back to the last item in the list
}

bool aHistLineEdit::processKeyEvent( QKeyEvent *e )
{
    /*
     * Only put key sequences in here you
     * want us to ignore and to pass upto
     * parent widgets for handeling
     */

    bool eat = false;

//    kdDebug() << "Key: " << KShortcut( KKey( e ) ).toString() << " StdAccel: " << KStdAccel::paste().toString() << endl;

    if ( KStdAccel::paste().contains(KKey( e ))) {
        e->ignore();
        eat = true;
    }

    return eat;
}


void aHistLineEdit::ColourPickerPopUp()
{
  ColorPicker picker( this );
  if ( picker.exec() == QDialog::Accepted )
  {
      QString s = text();
      int curPos = cursorPosition();
      QString colString = picker.colorString();
      colString.prepend( 0xaa );
      s.insert( curPos, colString );
      setText( s );
      setCursorPosition( curPos + colString.length() );
  }
}

void aHistLineEdit::focusInEvent(QFocusEvent *e)
{
  KLineEdit::focusInEvent(e);
  emit gotFocus();
}

void aHistLineEdit::mousePressEvent ( QMouseEvent *e )
{
  if(e->button() == MidButton){
    QApplication::clipboard()->setSelectionMode( true );
    emit pasteText( QApplication::clipboard()->text() );
    QApplication::clipboard()->setSelectionMode( false );
  }
  else{
    KLineEdit::mousePressEvent(e);
  }
}

bool aHistLineEdit::eventFilter( QObject *o, QEvent *e )
{
    if ( o == this && e->type() == QEvent::AccelOverride )
        if(processKeyEvent( static_cast<QKeyEvent*>( e ) ) == true)
            return true;

    return KLineEdit::eventFilter( o, e );
}

#include "ahistlineedit.moc"
