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

 TopLevel IRC Channel/query Window

 $$Id: toplevel.cpp 288737 2004-02-17 16:11:56Z asj $$

 This is the main window with which the user interacts.  It handles
 both normal channel conversations and private conversations.

 2 classes are defined, the UserControlMenu and KSircToplevel.  The
 user control menu is used as a list of user defineable menus used by
 KSircToplevel.

 KSircTopLevel:

 Signals:

 outputLine(QString &):
 output_toplevel(QString):

 closing(KSircTopLevel *, QString channel):

 changeChannel(QString old, QString new):

 currentWindow(KSircTopLevel *):

 Slots:



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

#include "toplevel.h"
#include "alistbox.h"
#include "chanparser.h"
#include "ksopts.h"
#include "control_message.h"
#include "displayMgr.h"
#include "NewWindowDialog.h"
#include "usercontrolmenu.h"
#include "topic.h"
#include "charSelector.h"
#include "ksview.h"
#include "logfile.h"
#include "servercontroller.h"
#include "ioDCC.h"

#include "KSTicker/ksticker.h"

#include <stdlib.h>

#include <qaccel.h>
#include <qclipboard.h>
#include <qregexp.h>
#include <qcursor.h>
#include <qtimer.h>
#include <qlayout.h>
#include <qtextcodec.h>
#include <qvbox.h>
#include <qlabel.h>

#include <kmenubar.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kwin.h>
#include <knotifyclient.h>
#include <kpopupmenu.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <ktempfile.h>
#include <kio/netaccess.h>
#include <kstatusbar.h>
#include <kstdaction.h>
#include <kaction.h>
#include <kcharsets.h>
#include <kglobalsettings.h>
#include <kstdguiitem.h>

extern DisplayMgr *displayMgr;
//QPopupMenu *KSircTopLevel::user_controls = 0L;
QPtrList<UserControlMenu> *KSircTopLevel::user_menu = 0L;
QStringList KSircTopLevel::cmd_menu;

void
KSircTopLevel::initColors()
{
  QColorGroup cg_mainw = kapp->palette().active();
  cg_mainw.setColor(QColorGroup::Base, ksopts->backgroundColor);
  cg_mainw.setColor(QColorGroup::Text, ksopts->textColor);
  cg_mainw.setColor(QColorGroup::Link, ksopts->linkColor);
  cg_mainw.setColor(QColorGroup::Highlight, ksopts->selBackgroundColor);
  cg_mainw.setColor(QColorGroup::HighlightedText, ksopts->selForegroundColor);
  mainw->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
  nicks->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
  linee->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
  lag->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
  cg_mainw.setColor(QColorGroup::Background, ksopts->backgroundColor);
  cg_mainw.setColor(QColorGroup::Foreground, ksopts->textColor);
  ksTopic->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
  selector->setFont( ksopts->defaultFont.family() );
  mainw->setFont( ksopts->defaultFont );
  nicks->setFont( ksopts->defaultFont );
  linee->setFont( ksopts->defaultFont );
  ksTopic->setFont( ksopts->defaultFont );

}

KSircTopLevel::KSircTopLevel(KSircProcess *_proc, const KSircChannel &channelInfo, const char * name)
    : KMainWindow(0, name, 0/*no WDestructiveClose !*/),
      UnicodeMessageReceiver(_proc),
      lastBeep( QTime::currentTime() ),
      m_channelInfo(channelInfo)

{
    // prevent us from being quitted when closing a channel-window. Only
    // closing the servercontroller shall quit.
    // KMainWindow will deref() us in closeEvent
    kapp->ref();


  m_topic = QString::null;

  QCString kstl_name = QCString(QObject::name()) + "_" + "toplevel";
  setName(kstl_name);

  if(!m_channelInfo.channel().isEmpty()) {
    setCaption(m_channelInfo.channel());
    caption = m_channelInfo.channel();
  }
  else
  {
    caption = QString::null;
  }

  Buffer = FALSE;

  have_focus = 0;
  tab_pressed = -1; // Tab (nick completion not pressed yet)
  tab_start = -1;
  tab_end = -1;

  m_gotMsgWithoutFocus = false;

  KickWinOpen = false;
  current_size = size();

  ksopts->channelSetup(ksircProcess()->serverName(), m_channelInfo.channel());
  m_channelInfo.setEncoding(ksopts->chan(m_channelInfo).encoding);

  selector = new charSelector();
  connect(selector, SIGNAL(clicked()), this, SLOT(insertText()));
  selector->setFont(ksopts->defaultFont.family());

  file = new QPopupMenu(this, QCString(QObject::name()) + "_popup_file");
  file->setCheckable(true);

  KAction *act = KStdAction::openNew( this, SLOT( newWindow() ), actionCollection() );
  act->plug( file );
  file->insertItem(i18n("New Ser&ver..."), servercontroller::self(), SLOT(new_connection()), Key_F2);
  file->insertSeparator();
  file->insertItem(i18n("&DCC Manager..."), this, SLOT(showDCCMgr()));
  file->insertItem(i18n("&Save to Logfile..."), this, SLOT(saveCurrLog()), CTRL + Key_S);

  tsitem = file->insertItem(i18n("Time St&amp"), this, SLOT(toggleTimestamp()), CTRL + Key_T);
  file->setItemChecked(tsitem, ksopts->chan(m_channelInfo).timeStamp);

  fjpitem = file->insertItem(i18n("Hide Join/Part Messages"), this, SLOT(toggleFilterJoinPart()));
  file->setItemChecked(fjpitem, ksopts->chan(m_channelInfo).filterJoinPart);

  file->insertItem(i18n("Character &Table"), selector, SLOT(show()), CTRL + Key_H);
  beepitem = file->insertItem(i18n("N&otify on Change"), this, SLOT(toggleBeep()), CTRL + Key_P);
  file->setItemChecked(beepitem, ksopts->chan(m_channelInfo).beepOnMsg);

  encodingAction = new KSelectAction( i18n( "&Encoding" ), 0, this );
  connect( encodingAction, SIGNAL( activated() ), this, SLOT( setEncoding() ) );
  QStringList encodings = KGlobal::charsets()->descriptiveEncodingNames();

  topicitem = file->insertItem(i18n("S&how Topic"), this, SLOT(toggleTopic()), CTRL + Key_O);
  if (isPrivateChat() || m_channelInfo.channel().startsWith("!no_channel")) {
      file->setItemEnabled(topicitem, false);
  }
  else {
      file->setItemChecked(topicitem, ksopts->chan(m_channelInfo).topicShow);
  }

  tickeritem = file->insertItem(i18n("Ticker &Mode"), this, SLOT(toggleTicker()));

  // remove utf16/ucs2 as it just doesn't work for IRC
  QStringList::Iterator encodingIt = encodings.begin();
  while ( encodingIt != encodings.end() ) {
    if ( ( *encodingIt ).find( "utf16" ) != -1 ||
         ( *encodingIt ).find( "iso-10646" ) != -1 )
      encodingIt = encodings.remove( encodingIt );
    else
      ++encodingIt;
  }
  encodings.prepend( i18n( "Default" ) );

  encodingAction->setItems( encodings );
  encodingAction->plug( file );

  int eindex = encodings.findIndex(ksopts->chan(m_channelInfo).encoding);
  if(eindex == -1)
      encodingAction->setCurrentItem( 0 );
  else
      encodingAction->setCurrentItem(eindex);
  setEncoding();

  file->insertSeparator();
  act = KStdAction::close( this, SLOT( terminate() ), actionCollection() );
  act->plug( file );

  kmenu = menuBar();
  kmenu->insertItem(i18n("&Channel"), file, KST_CHANNEL_ID, -1);
  kmenu->setAccel(Key_F, KST_CHANNEL_ID);

  /*
   * Ok, let's look at the basic widget "layout"
   * Everything belongs to q QFrame F, this is use so we
   * can give the KApplication a single main client widget, which is needs.
   *
   * A QVbox and a QHbox is used to ctronl the 3 sub widget
   * The Modified QListBox is then added side by side with the User list box.
   * The SLE is then fit bello.
   */

  // kstInside does not setup fonts, etc, it simply handles sizing

  top = new QVBox( this );

  ksTopic = new KSircTopic( top );
  ksTopic->setFont(ksopts->defaultFont);
  connect( ksTopic, SIGNAL( topicChange( const QString & ) ),
           this, SLOT( setTopicIntern( const QString & ) ) );

  QCString kstn = QCString(QObject::name()) + "_";

  pan = new QSplitter(QSplitter::Horizontal, top, kstn + "splitter");
#if KDE_IS_VERSION(3,1,92)
  pan->setOpaqueResize( KGlobalSettings::opaqueResize() );
#else
  pan->setOpaqueResize( true );
#endif

  mainw = new KSircView(ksircProcess(), pan, kstn + "KSircView" );
  mainw->setFocusPolicy(QWidget::NoFocus);

  nicks_box = new QVBox(pan);

  channelButtons = new chanButtons(ksircProcess(), nicks_box);
  connect(channelButtons, SIGNAL(mode(QString, int, QString)),
           this, SLOT(setMode(QString, int, QString)));

  nicks = new aListBox(nicks_box, kstn + "aListBox");
  nicks->setFocusPolicy(QWidget::NoFocus);
  //nicks->hide(); // default = only the main widget

  lag = new QLabel(nicks_box);
  lag->setFrameStyle(QFrame::Panel|QFrame::Sunken);
  lag->setAlignment(Qt::AlignCenter | Qt::SingleLine);
  lag->setText(i18n("Lag: Wait"));


  QValueList<int> sizes;
  sizes << int(width()*0.85) << int(width()*0.15);
  pan->setSizes(sizes);
  pan->setResizeMode( mainw, QSplitter::Stretch );
  pan->setResizeMode( nicks_box, QSplitter::Stretch );

//  f = new kstInside(top, QString(QObject::name()) + "_" + "kstIFrame");
  top->setStretchFactor(pan, 1);

  setCentralWidget(top);  // Tell the KApplication what the main widget is.

  logFile = 0;
  if ( ksopts->chan(m_channelInfo).logging && (m_channelInfo.channel() != "!no_channel" ))
  {
      logFile = new LogFile( m_channelInfo.channel(), m_channelInfo.server() );
      logFile->open();
  }

  // get basic variable

//  mainw = f->mainw;
//  nicks = f->nicks;
  //  pan = f->pan;

  clearWindow();

  if(isPrivateChat()){
      KConfig conf("ksirc/winlog/" + channelInfo.server() + "-" + channelInfo.channel(),
		   false, false, "data");
      QString group = "Message-History";

      if(conf.hasGroup( group )){
	  conf.setGroup( group );

	  QStringList strlist = conf.readListEntry("History");
	  if(strlist.count() > 0){
	      mainw->enableTimeStamps(true);
	      for ( QStringList::Iterator it = strlist.begin();
		    it != strlist.end();
		    ++it ) {
		  mainw->addRichText(*it);
	      }
	  }
	  strlist.clear();
      }
  }

  mainw->enableTimeStamps(ksopts->chan(m_channelInfo).timeStamp);

  edit = new QPopupMenu(this);
  act = KStdAction::copy( mainw, SLOT( copy() ), actionCollection() );
  act->plug( edit );
  act = KStdAction::paste( this, SLOT( pasteToWindow() ), actionCollection() );
  act->plug( edit );
  edit->insertItem(i18n("C&lear Window"), this, SLOT(clearWindow()), CTRL + Key_L);
  kmenu->insertItem(i18n("&Edit"), edit, -1, -1);

  linee = new aHistLineEdit(top, "");

  initColors();

  //  ksb_main->addWidget(linee, mainw->width());
//  ksb_main->insertItem(, KSB_MAIN_LAG, true);

  // don't show the nick lists in a private chat or the default window
  if (isPrivateChat() || m_channelInfo.channel().startsWith("!no_channel"))
  {
      nicks_box->hide();
      ksTopic->hide();
      //    nicks->hide();
      //    lag->hide();
      //    channelButtons->hide();
  }
  else
  {
      nicks_box->show();

      if(file->isItemChecked(topicitem)){
          ksTopic->show();
//          channelButtons->show();
      }
      else{
          ksTopic->hide();
          //          channelButtons->hide();
      }
      // channelButtons->show();
      // lag->show();
      // nicks->show();
  }


  connect(mainw, SIGNAL(pasteReq( const QString& )),
          this, SLOT( slotTextDropped( const QString& )));

  nicks->setFont(ksopts->defaultFont);

  // setup line editor

  linee->setFocusPolicy(QWidget::StrongFocus);
  linee->setFont(ksopts->defaultFont);

  if(ksopts->oneLineEntry == true) {
      linee->setWordWrap(QTextEdit::NoWrap);
  }
  else {
      linee->setWordWrap(QTextEdit::WidgetWidth);
  }

  connect(linee, SIGNAL(gotFocus()),
          this, SLOT(gotFocus()));
  connect(linee, SIGNAL(lostFocus()),
          this, SLOT(lostFocus()));
  connect(linee, SIGNAL(pasteText(const QString&)),
          this, SLOT(slotTextDropped(const QString&)));
  connect(linee, SIGNAL(notTab()),
          this, SLOT(lineeNotTab()));

  connect( linee, SIGNAL( gotReturnPressed() ),
           this, SLOT( returnPressed() ) );

  linee->setFocus();  // Give SLE focus
  linee->slotMaybeResize();

  lines = 0;          // Set internal line counter to 0

  ticker = 0x0;

  /*
   * Set generic run time variables
   *
   */

  opami = FALSE;
  continued_line = FALSE;
//  on_root = FALSE;

  /*
   * Load basic pics and images
   * This should use on of the KDE finder commands
   */

  KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());

  /*
   * Create our basic parser object
   */

  ChanParser = new ChannelParser(this);


  /*
   * Create the user Controls popup menu, and connect it with the
   * nicks list on the lefthand side (highlighted()).
   *
   */

  if(user_menu == 0)
    user_menu = UserControlMenu::parseKConfig();

  user_controls = new QPopupMenu(this);
  kmenu->insertItem(i18n("&Users"), user_controls);

  command = new QPopupMenu(this);

  setupCommandMenu();

  int i = 0;
  QDict<QPopupMenu> cml;
  for(QStringList::Iterator it = cmd_menu.begin();
      it != cmd_menu.end();
      ++it){
      QString top, item;
      top = (*it).section('/', 0, 0);
      item = (*it).section('/', 1, 1);
      if(!cml[top]) {
          cml.insert(top, new QPopupMenu(this));
          command->insertItem(top, cml[top]);
      }
      cml[top]->insertItem(item, this, SLOT(cmd_process(int)), 0, i);

      i++;
  }

  kmenu->insertItem(i18n("C&ommand"), command);

  kmenu->insertItem( KStdGuiItem::help().text(), helpMenu( QString::null, false ));

  connect(user_controls, SIGNAL(activated(int)),
          this, SLOT(UserParseMenu(int)));

  connect(nicks, SIGNAL(contextMenuRequested(int)), this,
          SLOT(UserSelected(int)));
  connect(nicks, SIGNAL(selectedNick(const QString &)),
          this, SLOT(openQueryFromNick(const QString &)));
  connect(nicks, SIGNAL(mouseButtonClicked ( int, QListBoxItem *, const QPoint &)),
          this, SLOT(pasteToNickList(int, QListBoxItem *, const QPoint &)));
  connect(nicks, SIGNAL(textDropped( const QListBoxItem *, const QString& )),
          this, SLOT(dndTextToNickList(const QListBoxItem *, const QString&)));

  UserUpdateMenu();  // Must call to update Popup.

  accel = new QAccel(this, "accel");

  accel->connectItem(accel->insertItem(SHIFT + Key_PageUp),
                     this,
                     SLOT(AccelScrollUpPage()));
  accel->connectItem(accel->insertItem(SHIFT + Key_PageDown),
                     this,
                     SLOT(AccelScrollDownPage()));

  /*
   * Pageup/dn
   * Added for stupid wheel mice
   */

  accel->connectItem(accel->insertItem(Key_PageUp),
                     this,
                     SLOT(AccelScrollUpPage()));
  accel->connectItem(accel->insertItem(Key_PageDown),
                     this,
                     SLOT(AccelScrollDownPage()));

  /*
   * These are not presently implemented, so let's not fill the logs.
  accel->connectItem(accel->insertItem(CTRL + Key_Return),
                     this,
                     SLOT(AccelPriorMsgNick()));

  accel->connectItem(accel->insertItem(CTRL + SHIFT + Key_Return),
                     this,
                     SLOT(AccelNextMsgNick()));
  */

  accel->connectItem(accel->insertItem(Key_Tab), // adds TAB accelerator
                     this,                         // connected to the main
                     SLOT(TabNickCompletionNormal()));  // TabNickCompletion() slot
  accel->connectItem(accel->insertItem(SHIFT+Key_Tab), // adds TAB accelerator
                     this,                         // connected to the main
                     SLOT(TabNickCompletionShift()));  // TabNickCompletion() slot
  accel->connectItem(accel->insertItem(CTRL + Key_N),
                     this, SLOT(newWindow()));
//  accel->connectItem(accel->insertItem(CTRL + Key_S),
//                     this, SLOT(toggleTimestamp()));

  // Drag & Drop
  connect( mainw, SIGNAL( textDropped(const QString&) ),
           SLOT( slotTextDropped(const QString&) ));
  connect( mainw, SIGNAL( urlsDropped(const QStringList&) ),
           SLOT( slotDropURLs(const QStringList&) ));
  connect( nicks, SIGNAL( urlsDropped( const QStringList&, const QString& )),
           SLOT( slotDccURLs( const QStringList&, const QString& )));
  connect( this, SIGNAL( changed(bool, QString) ), this, SLOT( doChange(bool, QString) ));

  mainw->setAcceptFiles( isPrivateChat() );
  resize(600, 360);

}

KSircTopLevel::~KSircTopLevel()
{
    // Cleanup and shutdown
    //  if(this == ksircProcess()->getWindowList()["default"])
    //    write(sirc_stdin, "/quit\n", 7); // tell dsirc to close
    //
    //  if(ksircProcess()->getWindowList()[m_channelInfo.channel()])
    //    ksircProcess()->getWindowList().remove(m_channelInfo.channel());

    //  if((m_channelInfo.channel()[0] == '#') || (m_channelInfo.channel()[0] == '&')){
    //    QString str = QString("/part ") + m_channelInfo.channel() + "\n";
    //    emit outputLine(str);
    //  }

    kdDebug(5008) << "~KSircTopLevel in" << endl;
    if (  ksopts->autoSaveHistory )
    {
        if ( isPublicChat() ) {
            kdDebug(5008) << "*** parting channel: " << m_channelInfo.channel() << endl;
            QString str = QString("/part ") + m_channelInfo.channel() + "\n";
            emit outputUnicodeLine(str);
        }
        else {
            QStringList strlist;

            mainw->addLine("user|X", ksopts->channelColor, " Saved log of previous messages");

            mainw->enableTimeStamps(true);

            for(KSirc::TextParagIterator ksit = mainw->firstParag();
                ksit.atEnd() == 0;
                ++ksit) {
                QString rt = ksit.richText();
                if(rt.contains("<img src=\"user|servinfo\">"))
                    continue;

                kdDebug(5008) << rt << endl;

                strlist += rt;

            }

            KConfig conf("ksirc/winlog/" + channelInfo().server() +
                         "-" + channelInfo().channel(),
                         false, false, "data");
            QString group = "Message-History";

            conf.setGroup( group );

            conf.writeEntry("History", strlist);
            conf.sync();

        }
    }
    delete ticker;
    delete user_controls;
    delete ChanParser;
    delete selector;
    delete channelButtons;
    delete logFile;
    kdDebug(5008) << "~KSircToplevel out" << endl;
}

void KSircTopLevel::setMode(QString mode, int mode_type, QString currentNick)
{
  QString command;
  if (mode_type == 0)
    command = QString::fromLatin1("/mode %1 %2\n").arg(m_channelInfo.channel()).arg(mode);
  else
    command = QString::fromLatin1("/mode %1 %2\n").arg(currentNick).arg(mode);
  sirc_write(command);
  linee->setFocus();
}

void KSircTopLevel::setEncoding()
{
  int index = encodingAction->currentItem();
  if ( index == 0 ) {// default (locale) encoding
      ksopts->chan(m_channelInfo).encoding = QString::null;
      UnicodeMessageReceiver::setEncoding( QString::null );
  }
  else {
      ksopts->chan(m_channelInfo).encoding = encodingAction->currentText();
      UnicodeMessageReceiver::setEncoding( KGlobal::charsets()->encodingForName( encodingAction->currentText() ) );
  }
  ksopts->save(KSOptions::Channels);
}

void KSircTopLevel::setupCommandMenu()
{
    if(cmd_menu.empty() == true){
        cmd_menu.append(i18n("Help") + "/" + "help");
        cmd_menu.append(i18n("Client") + "/" + "alias");
        cmd_menu.append(i18n("User") + "/" + "away");
        cmd_menu.append(i18n("Client") + "/" + "bye");
        cmd_menu.append(i18n("Client") + "/" + "cd");
        cmd_menu.append(i18n("Basic") + "/" + "ctcp");
        cmd_menu.append(i18n("Basic") + "/" + "dcc");
        cmd_menu.append(i18n("Operator") + "/" + "deop");
        cmd_menu.append(i18n("User") + "/" + "describe");
        cmd_menu.append(i18n("Client") + "/" + "eval");
        cmd_menu.append(i18n("Client") + "/" + "exit");
        cmd_menu.append(i18n("Client") + "/" + "hop");
        cmd_menu.append(i18n("Basic") + "/" + "ignore");
        cmd_menu.append(i18n("Server") + "/" + "info");
        cmd_menu.append(i18n("Channel") + "/" + "invite" + "/" + "*chan*");
        cmd_menu.append(i18n("Basic") + "/" + "join");
        cmd_menu.append(i18n("Operator") + "/" + "kick");
        cmd_menu.append(i18n("Oper") + "/" + "kill");
        cmd_menu.append(i18n("Basic") + "/" + "leave");
        cmd_menu.append(i18n("Server") + "/" + "links");
        cmd_menu.append(i18n("Basic") + "/" + "list");
        cmd_menu.append(i18n("Basic") + "/" + "ll");
        cmd_menu.append(i18n("Client") + "/" + "load");
        cmd_menu.append(i18n("Server") + "/" + "lusers");
        cmd_menu.append(i18n("Server") + "/" + "map");
        cmd_menu.append(i18n("Basic") + "/" + "me");
        cmd_menu.append(i18n("Channel") + "/" + "mode" + "/" + "*chan*");
        cmd_menu.append(i18n("Server") + "/" + "motd");
        cmd_menu.append(i18n("Basic") + "/" + "msg");
        cmd_menu.append(i18n("Basic") + "/" + "nick");
        cmd_menu.append(i18n("Basic") + "/" + "notice");
        cmd_menu.append(i18n("Basic") + "/" + "notify");
        cmd_menu.append(i18n("Operator") + "/" + "op");
        cmd_menu.append(i18n("Oper") + "/" + "oper");
        cmd_menu.append(i18n("Basic") + "/" + "query");
        cmd_menu.append(i18n("Channel") + "/" + "part"+ "/" + "*chan*");
        cmd_menu.append(i18n("Basic") + "/" + "ping");
        cmd_menu.append(i18n("Client") + "/" + "quit");
        cmd_menu.append(i18n("Server") + "/" + "quote");
        cmd_menu.append(i18n("Oper") + "/" + "rping");
        cmd_menu.append(i18n("Oper") + "/" + "rehash");
        cmd_menu.append(i18n("Basic") + "/" + "say");
        cmd_menu.append(i18n("Server") + "/" + "stats");
        cmd_menu.append(i18n("Client") + "/" + "system");
        cmd_menu.append(i18n("Server") + "/" + "time");
        cmd_menu.append(i18n("Channel") + "/" + "topic" + "/" + "*chan*");
        cmd_menu.append(i18n("Client") + "/" + "version");
        cmd_menu.append(i18n("Oper") + "/" + "wallops");
        cmd_menu.append(i18n("Channel") + "/" + "who" + "/" + "*chan*");
        cmd_menu.append(i18n("Basic") + "/" + "whois");
        cmd_menu.append(i18n("Basic") + "/" + "whowas");
        cmd_menu.append(i18n("Basic") + "/" + "wi");
        cmd_menu.append(i18n("Help") + "/" + "newuser");
        cmd_menu.append(i18n("Channel") + "/" + "ban");
        cmd_menu.append(i18n("Channel") + "/" + "unban");
        cmd_menu.append(i18n("Channel") + "/" + "clrban" + "/" + "*chan*");
        cmd_menu.append(i18n("Channel") + "/" + "banlist" + "/" + "*chan*");
        cmd_menu.append(i18n("Basic") + "/" + "pig");
        cmd_menu.append(i18n("Channel") + "/" + "wallop");
        cmd_menu.append(i18n("Client") + "/" + "exec");
        cmd_menu.append(i18n("Basic") + "/" + "url");
        cmd_menu.sort();
    }
}

void KSircTopLevel::insertText()
{
  linee->insert(selector->currentText());
}

void KSircTopLevel::show()
{
    KMainWindow::show();
    linee->setFocus();
    mainw->scrollToBottom(true);
}

enum {
    KSTUp = 1,
    KSTDown = 2
};

void KSircTopLevel::TabNickCompletionShift()
{
    TabNickCompletion(KSTDown);
}

void KSircTopLevel::TabNickCompletionNormal()
{
    TabNickCompletion(KSTUp);
}

void KSircTopLevel::TabNickCompletion(int dir)
{
  /*
   * Gets current text from lined find the last item and try and perform
   * a nick completion, then return and reset the line.
   */

  int start, end;
  int extra = 0;
  bool first = false;
  QString s;

  if(tab_pressed == -1){
    s = linee->text();
    tab_saved = s;
    end = linee->cursorPosition() - 1;
    start = s.findRev(" ", end, FALSE);
    tab_start = start;
    tab_end = end;
    first = true;
  }
  else{
    s = tab_saved;
    start = tab_start;
    end = tab_end;
  }

  if(dir == KSTDown){
      if(tab_pressed > 0)
          tab_pressed -= 1;
  }
  else {
      tab_pressed += 1;
  }


  if(s.length() == 0){
      if(tab_nick.isEmpty()) {
	  KNotifyClient::beep();
	  lineeNotTab();
	  return;
      }
      QString line = tab_nick + ": "; // tab_nick holds the last night since we haven't overritten it yet.
      linee->setText(line);
      linee->setCursorPosition(line.length());
      connect(linee, SIGNAL(notTab()),
	      this, SLOT(lineeNotTab()));
      return;
  }

  if (start == -1) {
      tab_nick = findNick(s.mid(0, end+1), tab_pressed);

      if(first && (tab_nick.isNull() == TRUE)){
	  KNotifyClient::beep();
	  lineeNotTab();
          return;
      }
      else if(tab_nick.isNull() == TRUE){
          tab_pressed -= 1;
          tab_nick = findNick(s.mid(0, end+1), tab_pressed);
      }
      s.replace(0, end + 1, "");
      if(s[0] == ':')
          s.replace(0, 2, "");
      s.prepend(tab_nick + ": ");
      extra = 2;
  }
  else {
      tab_nick = findNick(s.mid(start + 1, end - start), tab_pressed);
      if(first && (tab_nick.isNull() == TRUE)){
	  KNotifyClient::beep();
	  lineeNotTab();
	  return;
      }
      else if(tab_nick.isNull() == TRUE){
      tab_pressed -= 1;
      tab_nick = findNick(s.mid(start + 1, end - start), tab_pressed);
    }

    if((uint) end == s.length() - 1){ /* if we're at the end add a space */
      s.replace(start + 1, end - start, tab_nick + " ");
      extra = 1;
    }
    else {
      s.replace(start + 1, end - start, tab_nick);
      extra = 0;
    }
  }

  int tab = tab_pressed;

  linee->setText(s);
  linee->setCursorPosition(start + tab_nick.length() + 1 + extra);

  tab_pressed = tab; // setText causes lineeTextChanged to get called and erase tab_pressed

  connect(linee, SIGNAL(notTab()),
          this, SLOT(lineeNotTab()));

}

void KSircTopLevel::sirc_receive(QString str, bool broadcast)
{

  /*
   * read and parse output from dsirc.
   * call reader, split the read input into lines, parse the lines
   * then print line by line into the main text area.
   *
   * PROBLEMS: if a line terminates in mid line, it will get borken oddly
   *
   */

  if(!Buffer){
    if( !str.isEmpty() ){
        LineBuffer.append( BufferedLine( str, broadcast ) );
    }

    bool addressed = false;
    BufferedLine line;

    // be careful not to use a QValueList iterator here, as it is possible
    // that we enter a local event loop (think of the ssfeprompt dialog!)
    // which might trigger a socketnotifier activation which in turn
    // might result in the execution of code that modifies the LineBuffer,
    // which would invalidate iterators (Simon)
    while ( LineBuffer.begin() != LineBuffer.end() )
    {
        QString plainText("");
        line = *LineBuffer.begin();
        LineBuffer.remove( LineBuffer.begin() );

        // Get the need list box item, with colour, etc all set
        if (parse_input(line.message, plainText))
        {
            // If we should add anything, add it.
            // Don't announce server messages as they are
            // spread through all channels anyway

	    bool addressedLine = false;

	    if(line.message.contains( ">~o")){ /* highlighted with our nick */
		addressedLine = true;
	    }

	    // detect /msg's -- needed when auto-create-window is off
	    if ( line.message.find( QRegExp( "^\\[~b.+~b\\].+$" ) ) == 0 )
		addressedLine = true;

	    if (  addressedLine == true && line.message.startsWith("* " + ksircProcess()->getNick()))
		addressedLine = false;

            if ( addressedLine )
		addressed = true;

            if ( !line.wasBroadcast ) {
                // This line is addressed to me if it contains my nick, or is in a private chat
                emit changed( addressedLine || isPrivateChat(), plainText );
            }
        }
    }
    LineBuffer.clear(); // Clear it since it's been added
  }
  else{
    LineBuffer.append( BufferedLine( str, broadcast ) );
  }

  QValueList<int> list;
  QValueList<int>::iterator it;
  QString values;
  list = pan->sizes();
  it = list.begin();
  while( it != list.end()){
      values += QString("%1 ").arg(*it);
      ++it;
  }
}
void KSircTopLevel::sirc_line_return(const QString &text)
{

  /* Take line from SLE, and output if to dsirc */

  QString s = text;

  if(s.length() == 0)
    return;

  tab_pressed = -1; // new line, zero the counter.
  s += '\n'; // Append a need carriage return :)

  sirc_write(s);

  linee->setText("");
  linee->slotMaybeResize();

}

void KSircTopLevel::sirc_write(const QString &str)
{
  /*
   * Parse line forcommand we handle
   */
  QString command = str, plain = str.lower().simplifyWhiteSpace();
  if(plain.startsWith("/join ") ||
     plain.startsWith("/j ") ||
     plain.startsWith("/query ")) {

      QString s = plain.mid(plain.find(' ')+1);

      QStringList channels = QStringList::split(",", s);
      QStringList::ConstIterator it = channels.begin();

      for( ; it != channels.end(); ++it){
          QString name = *it;
	  //kdDebug(5008) << "Doing " << name << endl;
	  QRegExp rx("(\\S+)\\s*(\\S*)");
	  rx.search(name);
	  KSircChannel ci(m_channelInfo.server(),
			  rx.cap(1),   // channel
			  rx.cap(2));  // optional key
	  linee->setText(QString::null);
	  emit open_toplevel(ci);
/*          if(name[0] != '#'){
              emit open_toplevel(name);
              linee->setText(QString::null);
          }
          else {
              emit outputUnicodeLine(plain + "\n");
              emit open_toplevel(encoder()->fromUnicode(name));
	      }
*/
      }
      // Finish sending /join
      return;
  }
  else if(plain.startsWith("/server ")) {
    plain.simplifyWhiteSpace();
    QRegExp rx("/server (\\S+) *(\\S*) *(\\S*)");
    if(rx.search(str) >= 0){
      QString server = rx.cap(1);
      QString port = rx.cap(2);
      QString pass = rx.cap(3);

      bool ssl = false;
      if(server.startsWith("+")){
	server.replace(0, 1, "");
        ssl = true;
      }

      KSircServer serv(server, port, QString::null, pass, ssl);
      servercontroller::self()->new_ksircprocess(serv);

      return;
    }
  }
  else if(plain.startsWith("/part") ||
          plain.startsWith("/leave") ||
          plain.startsWith("/hop")) {
    QApplication::postEvent(this, new QCloseEvent()); // WE'RE DEAD
    linee->setText(QString::null);
    return;
  }
  else if( plain.startsWith( "/bye" ) ||
           plain.startsWith( "/exit" ) ||
           plain.startsWith( "/quit" )) {
    linee->setText( QString::null );
    emit requestQuit( command.ascii() );
    return;
  }
  else if ( plain.startsWith( "/away" ) ) {
    QString awayEvalCommand = QString::fromLatin1( "/eval $publicAway = %1\n" ).arg( ksopts->publicAway ? '1' : '0' );
    emit outputUnicodeLine( awayEvalCommand );
  }

  //
  // Look at the command, if we're assigned a channel name, default
  // messages, etc to the right place.  This include /me, etc
  //

  if(!isSpecialWindow()) { // channel or private chat
    if(plain[0].latin1() != '/'){
      command.prepend(QString::fromLatin1("/msg %1 ").arg(m_channelInfo.channel()));
    }
    else if(plain.startsWith("/me ")) {
      command.remove(0, 3);
      command.prepend(QString("/de ") + m_channelInfo.channel());
    }
  }

  // Write out line

  emit outputUnicodeLine(command);
  /*
   * as opposed to what some might think
   * it doesn't matter when you call stb
   */
  mainw->scrollToBottom(true);
}

bool KSircTopLevel::parse_input(const QString &string, QString &plainText)
{
  /*
   * Parsing routines are broken into 3 majour sections
   *
   * 1. Search and replace evil characters. The string is searched for
   * each character in turn (evil[i]), and the character string in
   * evil_rep[i] is replaced.
   *
   * 2. Parse control messages, add pixmaps, and colourize required
   * lines.  Caption is also set as required.
   *
   * 3. Create and return the ircListItem.
   *
   */

  /*
   * No-output get's set to 1 if we should ignore the line
   */

  /*
   * This is the second generation parsing code.
   * Each line type is determined by the first 3 characters on it.
   * Each line type is then sent to a parsing function.
   */
  parseResult *pResult = ChanParser->parse(string);

  parseSucc *item = dynamic_cast<parseSucc *>(pResult);
  parseError *err = dynamic_cast<parseError *>(pResult);

  QString logString;

  if(item)
  {
    parseJoinPart *jp = dynamic_cast<parseJoinPart *>(pResult);
    if(jp &&
       ksopts->chan(m_channelInfo).filterJoinPart){
      delete pResult;
      return true;
    }
    else if (!item->string.isEmpty()) {
	logString = mainw->
	    addLine( item->pm, item->colour, item->string );
        if(ticker != NULL){
            ticker->mergeString(item->string, item->colour);
        }
    } else {
      delete pResult;
      return false;
    }
  }
  else if (err)
  {
    if(err->err.isEmpty() == FALSE)
    {
      kdWarning() << err->err << ": " << string << endl;
      delete pResult;
      return false;
    }
    if (!err->str.isEmpty()) {
	logString = mainw->addLine( "user|error", ksopts->errorColor, err->str );
    }

  }
  else
  {
	logString = mainw->addLine( QString::null, ksopts->textColor, string );
        if(ticker != NULL){
	    ticker->mergeString(string);
	}
	// If it contains our nick, move the speaker to the top
	// of the nick completion list
	if(string.contains("~o")){
	    QRegExp rx("<(\\S+)>");
	    if ( (rx.search(logString) >= 0) &&
		 (rx.cap(1) != ksircProcess()->getNick()) ) {
		//kdDebug(5008) << "addCompleteNick: " << rx.cap(1) << endl;
		addCompleteNick( rx.cap(1) );
	    }
        }

  }
  delete pResult;

  if ( !logString.isEmpty() && logFile )
      logFile->log( logString );

  if(!plainText.isNull())
      plainText = logString;

  return true;
}

void KSircTopLevel::returnPressed()
{

    QString s = linee->text();

    uint i;
    QChar c;
    for(i = 0; (c = s[i]); i++){
        switch(c.unicode()){
        case 0336:
            s[i] = 002;
            break;
        case 0327:
            s[i] = 037;
            break;
        case 0237:
            s[i] = 026;
            break;
        case 0252:
            s[i] = 003;
            break;
        }
    }

    while(s.length() > IRC_SAFE_MAX_LINE){
        int wrap = s.findRev(' ', IRC_SAFE_MAX_LINE);
        if(wrap == -1)
          wrap = IRC_SAFE_MAX_LINE;
        sirc_line_return(s.left(wrap));
        s = s.mid(wrap + 1);
    }
    if(!s.stripWhiteSpace().isEmpty())
	sirc_line_return(s);
    else {
	linee->setText("");
	linee->slotMaybeResize();
    }
}

void KSircTopLevel::UserSelected(int index)
{
  if(index >= 0)
    user_controls->popup(this->cursor().pos());
}

void KSircTopLevel::UserParseMenu(int id)
{
  if(nicks->currentItem() < 0)
      return;

  QString s;
  s = QString("/eval $dest_nick='%1';\n").arg(nicks->text(nicks->currentItem()));
  sirc_write(s);
  // set $$dest_chan variable
  s = QString("/eval $dest_chan='%1';\n").arg(m_channelInfo.channel());
  sirc_write(s);
  QString action = user_menu->at(id)->action;
  if (action.length() && action[0] == '/')
      action.remove(0, 1);
  s = QString("/eval &docommand(eval{\"%1\"});\n").arg(action);
  s.replace(QRegExp("\\$\\$"), "$");
  sirc_write(s);
}

void KSircTopLevel::UserUpdateMenu()
{
  int i = 0;
  UserControlMenu *ucm;

  user_controls->clear();
  for(ucm = user_menu->first(); ucm != 0; ucm = user_menu->next(), i++){
    if(ucm->type == UserControlMenu::Seperator){
      user_controls->insertSeparator();
    }
    else{
      user_controls->insertItem(ucm->title, i);
      if(ucm->accel)
        user_controls->setAccel(i, ucm->accel);
      if((ucm->op_only == TRUE) && (opami == FALSE))
        user_controls->setItemEnabled(i, FALSE);
    }
  }
}

void KSircTopLevel::AccelScrollDownPage()
{
   mainw->verticalScrollBar()->addPage();
}

void KSircTopLevel::AccelScrollUpPage()
{
   mainw->verticalScrollBar()->subtractPage();
}

void KSircTopLevel::newWindow()
{
  NewWindowDialog w(KSircChannel(m_channelInfo.server(), QString::null));
  connect(&w, SIGNAL(openTopLevel(const KSircChannel &)), SIGNAL(open_toplevel(const KSircChannel &)));
  w.exec();
}

void KSircTopLevel::closeEvent(QCloseEvent *e)
{
  KMainWindow::closeEvent( e );
  e->accept();

   //Let's not part the channel till we are acutally delete.
  // We should always get a close event, *BUT* we will always be deleted.
  //   if( isPublicChat() ) {
  //       QString str = QString("/part ") + m_channelInfo.channel() + "\n";
  //       emit outputLine(str);
  //   }

  // Hide ourselves until we finally die
  hide();
  qApp->flushX();
  // Let's say we're closing
  kdDebug(5008) << "Toplevel closing: " << m_channelInfo.channel() << endl;
  emit closing(this, m_channelInfo.channel());
}

void KSircTopLevel::focusChange(QWidget *w)
{
    QWidget *me = this;
    if(w == me){
        gotFocus();
    }
    else {
        lostFocus();
    }
}

void KSircTopLevel::gotFocus()
{
    if(isVisible() == TRUE){
        if(have_focus == 0){
	    if(m_channelInfo.channel()[0] != '!' ){
		QString str = QString("/join %1").arg(m_channelInfo.channel());
		if(m_channelInfo.key().length() > 0){
		    str.append(" " + m_channelInfo.key());
		}
		kdDebug(5008) << "Doing join: " << str << " / " << m_channelInfo.channel() << endl;
                str.append("\n");
                emit outputUnicodeLine(str);
            }
            have_focus = 1;
            emit currentWindow(this);
        }
    }
    if(m_gotMsgWithoutFocus == true){
        m_gotMsgWithoutFocus = false;
        servercontroller::self()->decreaseNotificationCount(QString("%1 -> %2 got message").arg(ksircProcess()->serverID()).arg(m_channelInfo.channel()));
    }

}

void KSircTopLevel::lostFocus()
{
  if(have_focus == 1){
    have_focus = 0;
  }
}

void KSircTopLevel::control_message(int command, QString str)
{
  switch(command){
  case CHANGE_CHANNEL: // 001 is defined as changeChannel
    {
      KSircChannel ci(m_channelInfo.server(), QString::null);
      QRegExp rx("([^!]+)!+([^!]+)!*([^!]*)");
      if(rx.search(str) < 0){
          ci.setChannel(str);
      }
      else{
          ci.setChannel(rx.cap(2));
          ci.setKey(rx.cap(3));
      }
      //kdDebug(5008) << "CHANGE NAME: " << ci.channel() << " key: " << ci.key() << endl;
      emit changeChannel(m_channelInfo.channel(), ci.channel());
      ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
      m_channelInfo.setChannel(ci.channel());
      m_channelInfo.setKey(ci.key());
      m_channelInfo.setEncoding(ksopts->chan(m_channelInfo).encoding);
      if ( !logFile && ksopts->chan(m_channelInfo).logging )
      {
          logFile = new LogFile( m_channelInfo.channel(), ksircProcess()->serverName() );
          logFile->open();
      }
      setName(m_channelInfo.server().utf8() + "_" + m_channelInfo.channel().utf8() + "_" + "toplevel");
      pan->setName(QCString(QObject::name()) + "_" + "QSplitter");
      kmenu->setName(QCString(QObject::name()) + "_ktoolframe");
      linee->setName(QCString(QObject::name()) + "_" + "LineEnter");
      kmenu->show();
      setCaption(m_channelInfo.channel());
      emit currentWindow(this);

      if(m_channelInfo.channel()[0] == '#' || m_channelInfo.channel()[0] == '&'){
	  QString str = QString("/join %1 %2\n").arg(m_channelInfo.channel()).arg(m_channelInfo.key());
	  kdDebug(5008) << "Doing /join " << m_channelInfo.channel() << " :" << str << endl;
          emit outputUnicodeLine(str);
          emit outputLine("/eval $query=''\n");
      }
      else if (m_channelInfo.channel()[0] != '!')
      {
          emit outputUnicodeLine(QString("/eval $query='%1'\n").arg(m_channelInfo.channel()));
      }

      bool isPrivate = isPrivateChat();

      mainw->setAcceptFiles( isPrivate );

      if ( isPrivate )
      {
          nicks_box->hide();
          ksTopic->hide();
          file->setItemEnabled(topicitem, false);
      }
      else
      {
          file->setItemEnabled(topicitem, true);
          file->setItemChecked(topicitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow);
          if(file->isItemChecked(topicitem)){
              ksTopic->show();
          //    channelButtons->show();
          }
          nicks_box->show();
          pan->show();
//          channelButtons->show();
//          nicks->show();
//        lag->show();
      }
      if(layout()) {
          //kdDebug(5008) << "Redoeing layout" << endl;
          layout()->invalidate();
          layout()->activate();
      }
      else {
          //kdDebug(5008) << "No layout" << endl;
      }
      pan->refresh();

      ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
      file->setItemChecked(topicitem, ksopts->chan(m_channelInfo).topicShow);
      file->setItemChecked(beepitem, ksopts->chan(m_channelInfo).beepOnMsg);
      file->setItemChecked(tsitem, ksopts->chan(m_channelInfo).timeStamp);
      file->setItemChecked(fjpitem, ksopts->chan(m_channelInfo).filterJoinPart);
      int eindex = encodingAction->items().findIndex(ksopts->chan(m_channelInfo).encoding);
      //kdDebug(5008) << "in change channel we want: " << ksopts->chan(m_channelInfo).encoding << " eindex: " << eindex << endl;
      encodingAction->setCurrentItem(eindex < 0 ? 0 : eindex);
      setEncoding();
      mainw->enableTimeStamps(ksopts->chan(m_channelInfo).timeStamp);

      break;
    }
  case STOP_UPDATES:
    Buffer = TRUE;
    break;
  case RESUME_UPDATES:
    Buffer = FALSE;
    if(LineBuffer.isEmpty() == FALSE)
      sirc_receive(QString::null);
    break;
  case REREAD_CONFIG:
    emit freezeUpdates(TRUE); // Stop the list boxes update
    selector->setFont( ksopts->defaultFont.family() );
    mainw->setFont( ksopts->defaultFont );
    nicks->setFont( ksopts->defaultFont );
    linee->setFont( ksopts->defaultFont );
    ksTopic->setFont( ksopts->defaultFont );
    UserUpdateMenu();  // Must call to update Popup.
    emit freezeUpdates(FALSE); // Stop the list boxes update
    initColors();
    ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
    file->setItemChecked(topicitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow);
    file->setItemChecked(beepitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg);
    file->setItemChecked(tsitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp);
    {
	int eindex = encodingAction->items().findIndex(ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].encoding);
	//kdDebug(5008) << "in re-readconfig we want: " << ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].encoding << " eindex: " << eindex << endl;
	encodingAction->setCurrentItem(eindex < 0 ? 0 : eindex);
	setEncoding();
    }
    mainw->enableTimeStamps(ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp);
    if(ksopts->oneLineEntry == true) {
        linee->setWordWrap(QTextEdit::NoWrap);
    }
    else {
        linee->setWordWrap(QTextEdit::WidgetWidth);
    }
    mainw->scrollToBottom(true);
    update();
    break;
  case SET_LAG:
    if(str.isNull() == FALSE){
      bool ok = TRUE;

      str.truncate(6);
      double lag = str.toDouble(&ok);
      if(ok == TRUE){
        lag -= (lag*100.0 - int(lag*100.0))/100.0;
        QString s_lag;
        s_lag.sprintf("Lag: %.2f", lag);
        this->lag->setText(s_lag);
      }
      else{
        this->lag->setText(str);
      }
    }
    break;
  case RESET_NOTIF:
    m_gotMsgWithoutFocus = 0;
    break;
  default:
    kdDebug(5008) << "Unkown control message: " << str << endl;
  }
}

void KSircTopLevel::setTopic( const QString &topic )
{
    m_topic = topic;
    ksTopic->setText( topic );
}

void KSircTopLevel::toggleTimestamp()
{
    ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp = !ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp;
    ksopts->save(KSOptions::Channels);
    mainw->enableTimeStamps( ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp );
    file->setItemChecked( tsitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp );
}

void KSircTopLevel::toggleFilterJoinPart()
{
    ksopts->chan(m_channelInfo).filterJoinPart = !ksopts->chan(m_channelInfo).filterJoinPart;
    ksopts->save(KSOptions::Channels);
    file->setItemChecked( fjpitem, ksopts->chan(m_channelInfo).filterJoinPart );
}

QString KSircTopLevel::findNick(QString part, uint which)
{
    QStringList matches;

    for (QStringList::ConstIterator it = completeNicks.begin();
	 it != completeNicks.end();
	 ++it)
    {
	if ((*it).left(part.length()).lower() == part.lower() && nicks->findNick(*it) >= 0) {
	    matches.append(*it);
	}
    }

    for(uint i=0; i < nicks->count(); i++){
	if (matches.contains(nicks->text(i)))
	    continue;
	if(nicks->text(i).length() >= part.length()){
	    if(nicks->text(i).lower().startsWith( part.lower())){
		if(nicks->text(i) != ksircProcess()->getNick() ){ // Don't match your own nick
		    matches.append(nicks->text(i));
		}
	    }
	}
    }
    if(matches.count() > 0){
	if(which < matches.count())
	    return *(matches.at(which));
	else
	    return QString::null;
    }
    return QString::null;

}

void KSircTopLevel::removeCompleteNick(const QString &nick)
{
  QStringList::Iterator it = completeNicks.find(nick);
  if (it != completeNicks.end())
    completeNicks.remove(it);
}

void KSircTopLevel::addCompleteNick(const QString &nick)
{
  removeCompleteNick(nick);
  completeNicks.prepend(nick);
}

void KSircTopLevel::changeCompleteNick(const QString &oldNick, const QString &newNick)
{
  QStringList::Iterator it = completeNicks.find(oldNick);
  if (it != completeNicks.end())
    *it = newNick;
}

void KSircTopLevel::openQueryFromNick(const QString &nick)
{
    KSircChannel ci(m_channelInfo.server(), nick.lower());
    emit open_toplevel(ci);
}

void KSircTopLevel::pasteToWindow()
{
    // Ctrl-V
  //kdDebug(5008) << "Going to paste: " << KApplication::clipboard()->text( QClipboard::Clipboard ) << endl;
  slotTextDropped(KApplication::clipboard()->text( QClipboard::Clipboard ) );
}

void KSircTopLevel::pasteToNickList(int button,
				    QListBoxItem *item,
				    const QPoint &)
{
    if(button == Qt::MidButton && item){
	KSircChannel ci(m_channelInfo.server(), item->text().lower());
	emit open_toplevel(ci);

	QStringList lines = QStringList::split( '\n',
						KApplication::clipboard()->text( QClipboard::Selection ),
						true );
	QStringList::ConstIterator it = lines.begin();
	QStringList::ConstIterator end = lines.end();
	for (; it != end; ++it )
	{
	    if ((*it).isEmpty())
		continue;

	    QString str =
		QString("/msg ") + item->text().lower() + " " +
                *it + "\n";
	    emit outputUnicodeLine(str);

	}
    }

}

void KSircTopLevel::dndTextToNickList(const QListBoxItem *item,
				      const QString& text)
{
    if(item){
	KSircChannel ci(m_channelInfo.server(), item->text().lower());
	emit open_toplevel(ci);

	QStringList lines = QStringList::split( '\n',
						text,
						true );
	QStringList::ConstIterator it = lines.begin();
	QStringList::ConstIterator end = lines.end();
	for (; it != end; ++it )
	{
	    if ((*it).isEmpty())
		continue;

	    QString str =
		QString("/msg ") + item->text().lower() + " " +
		*it + "\n";
	    emit outputUnicodeLine(str);

	}
    }

}

enum KSircTopLevelCommands{ Ask, Parse, Escape };
void KSircTopLevel::slotTextDropped( const QString& _text )
{
  if (_text.isEmpty())
    return;
  QString text = linee->text();
  int curPos = linee->cursorPosition();

  text = text.mid( 0, curPos ) + _text + text.mid( curPos );

  if (text[text.length()-1] != '\n')
     text += "\n";
  int lines = text.contains("\n");
  int approx_lines = text.length() / 75;
  if ( lines > approx_lines )
     approx_lines = lines;
  if (lines > 4) {
      int result =  KMessageBox::warningContinueCancel(this,
                i18n("You are about to send %1 lines of text.\nDo you really want to send that much?").arg(approx_lines),
                QString::null, i18n("Send"));
      if (result != KMessageBox::Continue)
      {
//        linee->setText("");
        return;
      }
  }

  tab_pressed = -1;
  if(lines > 1){
    linee->setUpdatesEnabled(FALSE);

    QStringList lines = QStringList::split( '\n', text, true );
    QStringList::ConstIterator it = lines.begin();
    QStringList::ConstIterator end = lines.end();
    KSircTopLevelCommands commands = Ask;
    for (; it != end; ++it )
    {
        if ((*it).isEmpty())
            continue;
        QString line = *it;
        if ( line[0].latin1() == '/' )
        {
           if ( commands == Ask )
                switch ( KMessageBox::questionYesNo( this, i18n(
                    "The text you pasted contains lines that start with /.\n"
                    "Should they be interpreted as IRC commands?" ) ) )
                {
                    case KMessageBox::Yes:
                        commands = Parse;
                        break;
                    case KMessageBox::No:
                        commands = Escape;
                        break;
                }
            if ( commands == Escape )
                line.prepend( "/say " );
        }
        linee->setText( line );
        sirc_line_return(line);
    }

    linee->setText("");
    linee->setUpdatesEnabled(TRUE);
    linee->update();
  }
  else{
    text.replace(QRegExp("\n"), "");
    linee->setText(text);
    linee->setCursorPosition( curPos + _text.length() );
  }
}

void KSircTopLevel::clearWindow()
{
    kdDebug(5008) << "Doing clear on main window" << endl;
    mainw->clear();
}

void KSircTopLevel::lineeNotTab()
{
  tab_pressed = -1;
  disconnect(linee, SIGNAL(notTab()),
	     this, SLOT(lineeNotTab()));
  addCompleteNick(tab_nick);
}

void KSircTopLevel::toggleRootWindow()
{
}

bool KSircTopLevel::event( QEvent *e)
{
    if(e->type() == QEvent::ApplicationPaletteChange ||
       e->type() == QEvent::ApplicationFontChange)
    {
	QTimer::singleShot(750, this, SLOT(initColors()));
	initColors();
    }
    return KMainWindow::event(e);
}

void KSircTopLevel::saveCurrLog()
{
    KURL url = KURL( KFileDialog::getSaveFileName(QString::null,
                                            "*.log", 0L,
                                            i18n("Save Chat/Query Logfile")));
    if( url.isEmpty())
      return;

    KTempFile temp;
    QTextStream *str = temp.textStream();

    *str << mainw->plainText();

    temp.close();
#if KDE_IS_VERSION(3,1,92)
    KIO::NetAccess::upload(temp.name(), url, this);
#else
    KIO::NetAccess::upload(temp.name(), url);
#endif
}


void KSircTopLevel::slotDropURLs( const QStringList& urls )
{
    if ( !isPrivateChat() )
        return;

    slotDccURLs( urls, m_channelInfo.channel() );
}

// sends the list of urls to $dest_nick
void KSircTopLevel::slotDccURLs( const QStringList& urls, const QString& nick )
{
    if ( urls.isEmpty() || nick.isEmpty() )
        return;

    QStringList::ConstIterator it = urls.begin();
    // QString s("/eval &docommand(eval{\"dcc send " + nick + " %1\"});\n");
    QString s("/dcc send " + nick + " %1\n");
    for ( ; it != urls.end(); ++it ) {
        QString file( *it );
        kdDebug(5008) << "........ " << file << endl;
        if ( !file.isEmpty() )
            sirc_write(s.arg( file ));
    }
}

bool KSircTopLevel::isPrivateChat() const
{
    return ((m_channelInfo.channel()[0] != '!') && (m_channelInfo.channel()[0] != '&') &&
            (m_channelInfo.channel()[0] != '#'));
}

bool KSircTopLevel::isPublicChat() const
{
    return ((m_channelInfo.channel()[0] == '#') || (m_channelInfo.channel()[0] == '&'));
}

bool KSircTopLevel::isSpecialWindow() const
{
    return (m_channelInfo.channel()[0] == '!');
}

void KSircTopLevel::outputUnicodeLine( const QString &message )
{
    emit outputLine( encoder()->fromUnicode( message ) );
}

void KSircTopLevel::setTopicIntern( const QString &topic )
{
    QString command = QString::fromLatin1( "/topic %1 %2\n" ).arg( m_channelInfo.channel() ).arg( topic );
    sirc_write( command );
    linee->setFocus();
}

void KSircTopLevel::doChange(bool pers, QString text)
{
    QTime ctime = QTime::currentTime();
    if (ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg) {
        // beep only every 2 seconds otherwise it'll get very noisy in the room ;)
        if ( QABS( lastBeep.secsTo( ctime ) ) > 1 ) {
	    if ( pers ) {
		int winId = !ticker ? topLevelWidget()->winId() : ticker->winId();
		KNotifyClient::event(winId,
				     QString::fromLatin1("ChannelPersonal"),
				     i18n("Your nick appeared on channel %1").arg(m_channelInfo.channel()));
	    } else {
		int winId = !ticker ? topLevelWidget()->winId() : ticker->winId();
		KNotifyClient::event(winId,
				     QString::fromLatin1("ChannelChanged"),
				     i18n("Channel %1 changed").arg(m_channelInfo.channel()));
	    }
	    lastBeep = ctime;
        }
    }

    if(have_focus == 0 && pers == true && m_gotMsgWithoutFocus == false){
	m_gotMsgWithoutFocus = true;
        servercontroller::self()->increaseNotificationCount(QString("%1 -> %2").arg(ksircProcess()->serverID()).arg(m_channelInfo.channel()), text);
    }
}

void KSircTopLevel::toggleBeep()
{
    ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg = !ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg;
    ksopts->save(KSOptions::Channels);
    file->setItemChecked(beepitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg);
}

void KSircTopLevel::toggleTopic()
{
	if(file->isItemChecked(topicitem)){
		ksTopic->hide();
//                channelButtons->hide();
                ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow = false;
	}
	else {
		ksTopic->show();
//                channelButtons->show();
                ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow = true;
	}
        file->setItemChecked(topicitem, !file->isItemChecked(topicitem));
        ksopts->save(KSOptions::Channels);
}

void KSircTopLevel::toggleTicker()
{
    //kdDebug(5008) << "Got toggle ticker" << endl;
    if(ticker){
        show();
        displayMgr->show(this);
        linee->setFocus(); // Give focus back to the linee, someone takes it away on new create
        tickerpoint = ticker->pos();
        tickersize = ticker->size();
        delete ticker;
        ticker = 0x0;
    }
    else {
        ticker = new KSTicker(0x0, m_channelInfo.channel().utf8() + "_ticker");
        ticker->setCaption(m_channelInfo.channel());
        if(tickerpoint.isNull() == false)
            ticker->move(tickerpoint);
        if(tickersize.isEmpty() == false)
            ticker->resize(tickersize);
        ticker->show();
        displayMgr->hide(this);

        connect(ticker, SIGNAL(doubleClick()), this, SLOT(toggleTicker()));
    }
}

bool KSircTopLevel::atBottom()
{
    return mainw->verticalScrollBar()->maxValue() - mainw->verticalScrollBar()->value() < 20;
}

void KSircTopLevel::cmd_process(int id)
{
    if(cmd_menu.at(id) != cmd_menu.end()) {
        QString item, arg, out;
        item = cmd_menu[id].section('/', 1, 1);
        arg = cmd_menu[id].section('/', 2, 2);

        out = "/" + item;

        if(arg == "*chan*")
            out.append(" " + m_channelInfo.channel());

        out.append(" ");

        linee->insertAt(out, 0, 0);
        linee->setCursorPosition(linee->cursorPosition() + out.length());
    }
}

void KSircTopLevel::showDCCMgr()
{
    KSircMessageReceiver *kmr = ksircProcess()->mrList().find("!dcc");
    if(kmr){
	KSircIODCC *dcc = static_cast<KSircIODCC *>(kmr);
	if(dcc){
            dcc->showMgr();
	}
    }
}

#include "toplevel.moc"

// vim: ts=2 sw=2 et
