/***************************************************************************
 *   fqterm, a terminal emulator for both BBS and *nix.                    *
 *   Copyright (C) 2008 fqterm development group.                          *
 *                                                                         *
 *   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; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.               *
 ***************************************************************************/



#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <QClipboard>
#include <QDesktopServices>
#include <QDialog>
#include <QFileDialog>
#include <QFontDialog>
#include <QFontMetrics>
#include <QInputDialog>
#include <QImageReader>
#include <QMenu>
#include <QMessageBox>
#include <QStatusBar>
#include <QTextEdit>
#include <QTimer>
#include <QUrl>
#include <QtScript/QScriptValue>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QTemporaryFile>
#include <QRegExp>
#include <QNetworkProxy>
#include "addrdialog.h"
#include "articledialog.h"
#include "msgdialog.h"
#include "osdmessage.h"
#include "popwidget.h"
#include "progressBar.h"
#include "common.h"
#include "fqterm_buffer.h"
#include "fqterm_canvas.h"
#include "fqterm_config.h"
#include "fqterm_convert.h"
#include "fqterm_filedialog.h"
#include "fqterm_frame.h"
#include "fqterm_http.h"
#include "fqterm_ip_location.h"
#include "fqterm_param.h"
#include "fqterm_path.h"
#include "fqterm_screen.h"
#include "fqterm_session.h"
#include "fqterm_sound.h"
#include "fqterm_window.h"
#include "fqterm_wndmgr.h"
#include "imageviewer.h"
#include "sshlogindialog.h"
#include "statusBar.h"
#include "zmodemdialog.h"
#ifdef HAVE_PYTHON
#include <Python.h>
#include "fqterm_python.h"
#endif //HAVE_PYTHON
#include "fqterm_scriptengine.h"


namespace FQTerm {

char FQTermWindow::directions_[][5] =  {
  // 4
  "\x1b[1~",  // 0 HOME
  "\x1b[4~",  // 1 END
  "\x1b[5~",  // 2 PAGE UP
  "\x1b[6~",  // 3 PAGE DOWN
  // 3
  "\x1b[A",  // 4 UP
  "\x1b[B",  // 5 DOWN
  "\x1b[D",  // 6 LEFT
  "\x1b[C" // 7 RIGHT
};
/*
class FAQ: public QObject {
  Q_OBJECT;
 public slots:
  FAQ() {a = 100;}

  int get() {return a;}
  
 private:
  int a;
};
*/

//constructor
FQTermWindow::FQTermWindow(FQTermConfig *config, FQTermFrame *frame, FQTermParam param,
                           int addressIndex, QWidget *parent,
                           const char *name, Qt::WindowFlags wflags)
    : QMainWindow(parent, wflags),
      frame_(frame),
      isSelecting_(false),
      addressIndex_(addressIndex),
      sound_(NULL),
      isMouseClicked_(false),
      blinkStatus_(true),
      isUrlUnderLined_(false),
#ifdef HAVE_PYTHON
      // the system wide script
      pythonScriptLoaded_(false),
      // python thread module
      pModule(NULL),
      pDict(NULL),
#endif
      script_engine_(NULL),
      externalEditor_(NULL) {
  // isMouseX11_ = false;
  session_ = new FQTermSession(config, param);
  screen_ = new FQTermScreen(this, session_);

  config_ = config;
  zmodemDialog_ = new zmodemDialog(this);

  externalEditor_ = new FQTermExternalEditor(this);

  pageViewMessage_ = new PageViewMessage(this);
  tabBlinkTimer_ = new QTimer;

  setWindowIcon(QIcon("noicon"));
  setWindowTitle(param.name_);
  setMouseTracking(true);
  setFocusProxy(screen_);
  setCentralWidget(screen_);
  addMenu();
  setStatusBar(NULL);


  FQ_VERIFY(connect(pageViewMessage_, SIGNAL(hideAt(const QRect&)),
     screen_, SLOT(widgetHideAt(const QRect&))));

  FQ_VERIFY(connect(frame_, SIGNAL(bossColor()),
    screen_, SLOT(bossColor())));

  FQ_VERIFY(connect(frame_, SIGNAL(fontAntiAliasing(bool)),
    screen_, SLOT(setFontAntiAliasing(bool))));

  FQ_VERIFY(connect(frame_, SIGNAL(changeLanguage()),
                    this, SLOT(recreateMenu())));
  FQ_VERIFY(connect(frame_, SIGNAL(bossColor()),
                    screen_, SLOT(bossColor())));
  FQ_VERIFY(connect(frame_, SIGNAL(updateScroll()),
                    screen_, SLOT(updateScrollBar())));

  FQ_VERIFY(connect(screen_, SIGNAL(inputEvent(const QString&)),
                    session_, SLOT(handleInput(const QString&))));

  FQ_VERIFY(connect(session_, SIGNAL(zmodemStateChanged(int, int, const char*)),
                    this, SLOT(ZmodemState(int, int, const char *))));
  FQ_VERIFY(connect(zmodemDialog_, SIGNAL(canceled()),
                    session_, SLOT(cancelZmodem())));
  FQ_VERIFY(connect(session_, SIGNAL(connectionClosed()),
                    this, SLOT(connectionClosed())));
  FQ_VERIFY(connect(session_, SIGNAL(startAlert()), this, SLOT(startBlink())));
  FQ_VERIFY(connect(session_, SIGNAL(stopAlert()), this, SLOT(stopBlink())));
  // FQ_VERIFY(connect(session_->decoder_, SIGNAL(mouseMode(bool)),
  //                   this, SLOT(setMouseMode(bool))));
  FQ_VERIFY(connect(session_, SIGNAL(articleCopied(int, const QString)),
                    this, SLOT(articleCopied(int, const QString))));
  FQ_VERIFY(connect(session_, SIGNAL(requestUserPwd(QString*, QString*, bool*)),
                    this, SLOT(requestUserPwd(QString *, QString *, bool *))));
  //connect telnet signal to slots
  // QVERIFY(connect(session_->telnet_, SIGNAL(readyRead(int)),
  //                 this, SLOT(readReady(int))));
  FQ_VERIFY(connect(session_, SIGNAL(sessionUpdated()),
                    this, SLOT(sessionUpdated())));
  FQ_VERIFY(connect(session_, SIGNAL(bellReceived()), this, SLOT(beep())));
  FQ_VERIFY(connect(session_, SIGNAL(onTitleSet(const QString&)), this, SLOT(onTitleSet(const QString&))));

  FQ_VERIFY(connect(session_, SIGNAL(messageAutoReplied()),
                    this, SLOT(messageAutoReplied())));
  FQ_VERIFY(connect(session_, SIGNAL(telnetStateChanged(int)),
                    this, SLOT(TelnetState(int))));
  FQ_VERIFY(connect(session_, SIGNAL(errorMessage(QString)),
                    this, SLOT(showSessionErrorMessage(QString))));

  FQ_VERIFY(connect(tabBlinkTimer_, SIGNAL(timeout()), this, SLOT(blinkTab())));

  FQ_VERIFY(connect(externalEditor_, SIGNAL(done(const QString&)), this, SLOT(externalEditorDone(const QString&))));

  FQ_VERIFY(connect(this, SIGNAL(writeStringSignal(const QString&)), this, SLOT(writeString(const QString&)), Qt::QueuedConnection));
  FQ_VERIFY(connect(this, SIGNAL(writeRawStringSignal(const QString&)), this, SLOT(writeRawString(const QString&)), Qt::QueuedConnection));

#if defined(WIN32)
  popWindow_ = new popWidget(this, frame_);
#else
  popWindow_ = new popWidget(this);
#endif

  const QString &resource_dir = getPath(RESOURCE);

  cursors_[FQTermSession::kHome] = QCursor(
      QPixmap(resource_dir + "cursor/home.xpm"));
  cursors_[FQTermSession::kEnd] = QCursor(
      QPixmap(resource_dir + "cursor/end.xpm"));
  cursors_[FQTermSession::kPageUp] = QCursor(
      QPixmap(resource_dir + "cursor/pageup.xpm"));
  cursors_[FQTermSession::kPageDown] = QCursor(
      QPixmap(resource_dir + "cursor/pagedown.xpm"));
  cursors_[FQTermSession::kUp] = QCursor(
      QPixmap(resource_dir + "cursor/prev.xpm"));
  cursors_[FQTermSession::kDown] = QCursor(
      QPixmap(resource_dir + "cursor/next.xpm"));
  cursors_[FQTermSession::kLeft] = QCursor(
      QPixmap(resource_dir + "cursor/exit.xpm"), 0, 10);
  cursors_[FQTermSession::kRight] = QCursor(
      QPixmap(resource_dir + "cursor/hand.xpm"));
  cursors_[FQTermSession::kNormal] = Qt::ArrowCursor;

  script_engine_ = new FQTermScriptEngine(this);
  if  (session_->param().isAutoLoadScript_
       && !session_->param().autoLoadedScriptFileName_.isEmpty()) {
    const QString &filename = session_->param().autoLoadedScriptFileName_;
#ifdef HAVE_PYTHON
    if (filename.trimmed().endsWith(".py", Qt::CaseInsensitive)){
      initPython(filename);
    } else {
      initPython("");
#endif
      runScript(filename);    
#ifdef HAVE_PYTHON
    }
  } else {
    initPython("");
#endif //HAVE_PYTHON
  }
  session_->setScriptListener(this);
}

FQTermWindow::~FQTermWindow() {
#ifdef HAVE_PYTHON
  finalizePython();
#endif //HAVE_PYTHON
  script_engine_->finalizeScript();
  //  delete telnet_;
  delete session_;
  delete popWindow_;
  delete tabBlinkTimer_;
  delete menu_;
  delete urlMenu_;
  delete screen_;
  delete pageViewMessage_;
  //delete script_engine_;
}

void FQTermWindow::addMenu() {
  urlMenu_ = new QMenu(screen_);
  urlMenu_->addAction(tr("Preview image"), this, SLOT(previewLink()));
  urlMenu_->addAction(tr("Open link"), this, SLOT(openLink()));
  urlMenu_->addAction(tr("Save As..."), this, SLOT(saveLink()));
  urlMenu_->addAction(tr("Copy link address"), this, SLOT(copyLink()));
  urlMenu_->addSeparator();
  urlMenu_->addAction(tr("Share Selected Text and URL!"), this, SLOT(shareIt()));
  const QString &resource_dir = getPath(RESOURCE);

  menu_ = new QMenu(screen_);
#if defined(__APPLE__)
  // Please note that on MacOSX Qt::CTRL corresponds to Command key (apple key),
  // while Qt::Meta corresponds to Ctrl key.
  QKeySequence copy_shortcut(tr("Ctrl+C"));
  QKeySequence paste_shortcut(tr("Ctrl+V"));
#else
  QKeySequence copy_shortcut(tr("Ctrl+Insert"));
  QKeySequence paste_shortcut(tr("Shift+Insert"));
#endif
  menu_->addAction(QPixmap(resource_dir + "pic/copy.png"), tr("Copy"),
                   this, SLOT(copy()), copy_shortcut);
  menu_->addAction(QPixmap(resource_dir + "pic/paste.png"), tr("Paste"),
                   this, SLOT(paste()), paste_shortcut);

  menu_->addAction(QPixmap(resource_dir+"pic/get_article_fulltext.png"), tr("Copy Article"),
                   this, SLOT(copyArticle()));
  menu_->addSeparator();

  QMenu *fontMenu = new QMenu(menu_);
  fontMenu->setTitle(tr("Font"));
  fontMenu->setIcon(QPixmap(resource_dir + "pic/change_fonts.png"));
  for (int i = 0; i < 2; ++i) {
    QAction *act = fontMenu->addAction(
        FQTermParam::getLanguageName(bool(i)) + tr(" Font"),
        this, SLOT(setFont()));
    act->setData(i);
  }
  menu_->addMenu(fontMenu);

  menu_->addAction(QPixmap(resource_dir + "pic/ansi_color.png"), tr("Color"),
                   this, SLOT(setColor()));
  menu_->addSeparator();
  menu_->addAction(QPixmap(resource_dir + "pic/preferences.png"), tr("Setting"),
                   this, SLOT(setting()));
  menu_->addSeparator();
  menu_->addAction(tr("Open Selected As Url"), this, SLOT(openAsUrl()));
  menu_->addAction(tr("Search Selected Text!"), this, SLOT(searchIt()));
  menu_->addAction(tr("Share Selected Text and URL!"), this, SLOT(shareIt()));
}

void FQTermWindow::recreateMenu() {
  delete urlMenu_;
  delete menu_;
  addMenu();
}

//close event received
void FQTermWindow::closeEvent(QCloseEvent *clse) {
  bool toClose = true;
  if (isConnected() && FQTermPref::getInstance()->openWarnOnClose_) {
    QMessageBox mb(tr("FQTerm"),
                   tr("Still connected, do you really want to exit?"),
                   QMessageBox::Warning, QMessageBox::Yes|QMessageBox::Default,
                   QMessageBox::No | QMessageBox::Escape, 0, this);
    if (mb.exec() != QMessageBox::Yes) {
      toClose = false;
    }
  }
  if (toClose) {
    session_->close();
  } else {
    clse->ignore();
    return;
  }
  //We no longer save setting on close from r1036.
  //saveSetting();
}

void FQTermWindow::blinkTab() {
  emit blinkTheTab(this, blinkStatus_);
  blinkStatus_ = !blinkStatus_;
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Mouse & Key                                    */
/*                                                                          */
/* ------------------------------------------------------------------------ */
void FQTermWindow::enterEvent(QEvent*){}

void FQTermWindow::leaveEvent(QEvent*) {
  // TODO: code below doesn't work
  // QPoint temp(0, 0);
  // session_->setSelect(temp, temp, session_->isRectangleCopy_);
  // QApplication::postEvent(screen_, new QPaintEvent(QRect(0, 0, 0, 0));
  // &paintEvent);
}

void FQTermWindow::changeEvent(QEvent *e) {
  if (e->type() == QEvent::WindowStateChange) {
    QWindowStateChangeEvent *event = (QWindowStateChangeEvent*)(e);
    Qt::WindowStates oldState = event->oldState();
    if (!isMaximized()) {
      emit refreshOthers(this);
    }
    if ( (oldState & Qt::WindowMinimized && !isMinimized()) ||
         (!(oldState & Qt::WindowActive) && isActiveWindow())) {
      forcedRepaintScreen();
    }
    if (oldState & Qt::WindowMaximized || isMaximized()) {
      emit resizeSignal(this);
    }
  }
}

void FQTermWindow::resizeEvent(QResizeEvent *qResizeEvent)
{
  emit resizeSignal(this);
}

bool FQTermWindow::event(QEvent *qevent) {
  bool res = false;
  QKeyEvent *keyEvent;
  switch(qevent->type()) {
#ifdef __linux__ 
    case QEvent::ShortcutOverride:    
       keyEvent = (QKeyEvent *)(qevent);   
       if(keyEvent->key() == Qt::Key_W &&    
          keyEvent->modifiers() == Qt::ControlModifier) {    
         keyEvent->accept();   
         res = true;   
       }   
       break;
#endif
    case QEvent::KeyPress:
      keyEvent = (QKeyEvent *)(qevent);
      if (keyEvent->key() == Qt::Key_Tab ||
          keyEvent->key() == Qt::Key_Backtab) {
        // Key_Tab and Key_Backtab are special, if we don't process them here,
        // the default behavoir is to move focus. see QWidget::event.
        keyPressEvent(keyEvent);
        keyEvent->accept();
        res = true;
      }
      break;
    default:
      break;
  }

  res = res || QMainWindow::event(qevent);

  if (qevent->type() == QEvent::HoverMove
      || qevent->type() == QEvent::MouseMove
      || qevent->type() == QEvent::Move) {
    if (res) {
      FQ_TRACE("wndEvent", 10) << "Accept event: " << qevent->type()
                               << " " << getEventName(qevent->type()) << ".";
    } else {
      FQ_TRACE("wndEvent", 10) << "Ignore event: " << qevent->type()
                               << " " << getEventName(qevent->type()) << ".";
    }
  } else {
    if (res) {
      FQ_TRACE("wndEvent", 9) << "Accept event: " << qevent->type()
                              << " " << getEventName(qevent->type()) << ".";
    } else {
      FQ_TRACE("wndEvent", 9) << "Ignore event: " << qevent->type()
                              << " " << getEventName(qevent->type()) << ".";
    }
  }
  return res;
}

void FQTermWindow::mouseDoubleClickEvent(QMouseEvent *mouseevent) {
  if (scriptMouseEvent(mouseevent))
    return;
}

void FQTermWindow::mousePressEvent(QMouseEvent *mouseevent) {
  if (scriptMouseEvent(mouseevent))
    return;
  // stop  the tab blinking
  stopBlink();

  // Left Button for selecting
  if (mouseevent->button() & Qt::LeftButton && !(mouseevent->modifiers())) {
    startSelecting(mouseevent->pos());
  }

  // Right Button
  if ((mouseevent->button() & Qt::RightButton)) {
    if (mouseevent->modifiers() & Qt::ControlModifier) {
      // on Url
      if (!session_->getUrl().isEmpty()) {
        previewLink();
      }
      return ;
    }

#ifdef __APPLE__
    bool additional_modifier = (mouseevent->modifiers() & !Qt::MetaModifier);
#else
    bool additional_modifier = mouseevent->modifiers();
#endif

    if (!(additional_modifier)) {
      // on Url
      if (!session_->getUrl().isEmpty()) {
        urlMenu_->popup(mouseevent->globalPos());
      } else {
        menu_->popup(mouseevent->globalPos());
      }
      return ;
    }
  }
  // Middle Button for paste
  if (mouseevent->button() &Qt::MidButton && !(mouseevent->modifiers())) {
    if (isConnected())
      // on Url
      if (!session_->getUrl().isEmpty()) {
        previewLink();
      } else {
        pasteHelper(false);
      }
    return ;
  }

  // If it is a click, there should be a press event and a release event.
  isMouseClicked_ = true;

  // xterm mouse event
  //session_->sendMouseState(0, me->button(), me->state(), me->pos());
}


void FQTermWindow::mouseMoveEvent(QMouseEvent *mouseevent) {
  bool mouseEventConsumed = scriptMouseEvent(mouseevent);
  QPoint position = mouseevent->pos();
  if (!mouseEventConsumed)
  {

    // selecting by leftbutton
    if ((mouseevent->buttons() &Qt::LeftButton) && isSelecting_) {
      onSelecting(position);
    }
  }
  if (!(session_->param().isSupportMouse_ && isConnected())) {
    return;
  }

  setCursorPosition(position);
  setCursorType(position);


  if (!mouseEventConsumed)
  {
    if (!isUrlUnderLined_ && session_->urlStartPoint() != session_->urlEndPoint()) {
      isUrlUnderLined_ = true;
      urlStartPoint_ = session_->urlStartPoint();
      urlEndPoint_ = session_->urlEndPoint();
	    clientRect_ = QRect(QPoint(0, urlStartPoint_.y()), QSize(session_->getBuffer()->getNumColumns(), urlEndPoint_.y() - urlStartPoint_.y() + 1));
	    repaintScreen();
	  
    } else if (isUrlUnderLined_ && (session_->urlStartPoint() != urlStartPoint_ || session_->urlEndPoint() != urlEndPoint_)) {
      clientRect_ = QRect(QPoint(0, urlStartPoint_.y()), QSize(session_->getBuffer()->getNumColumns(), urlEndPoint_.y() - urlStartPoint_.y() + 1));
      urlStartPoint_ = QPoint();
      urlEndPoint_ = QPoint();
	    repaintScreen();
	    isUrlUnderLined_ = false;

    }
  }
}

static bool isSupportedImage(const QString &name) {
  static QList<QByteArray> image_formats =
      QImageReader::supportedImageFormats();

  return image_formats.contains(name.section(".", -1).toLower().toUtf8());
}

void FQTermWindow::mouseReleaseEvent(QMouseEvent *mouseevent) {
  if (scriptMouseEvent(mouseevent))
    return;
  if (!isMouseClicked_) {
    return ;
  }
  isMouseClicked_ = false;
  // other than Left Button ignored
  if (!(mouseevent->button() & Qt::LeftButton) ||
      (mouseevent->modifiers() & Qt::KeyboardModifierMask)) {
    // no local mouse event
    //session_->sendMouseState(3, me->button(), me->state(), me->pos());
    return ;
  }

  // Left Button for selecting
  QPoint currentMouseCell = screen_->mapToChar(mouseevent->pos());
  if (currentMouseCell != lastMouseCell_ && isSelecting_) {
    finishSelecting(mouseevent->pos());
    return ;
  }
  isSelecting_ = false;

  if (!session_->param().isSupportMouse_ || !isConnected()) {
    return ;
  }

  // url
  QString url = session_->getUrl();
  if (!url.isEmpty()) {
    if (isSupportedImage(url)) {
      previewLink();      
    } else {
      openUrl(url);
    }
    return ;
  }

  processLClick(currentMouseCell);
}

void FQTermWindow::wheelEvent(QWheelEvent *wheelevent) {
  if (scriptWheelEvent(wheelevent)) return;
  int j = wheelevent->delta() > 0 ? 4 : 5;
  if (!(wheelevent->modifiers())) {
    if (FQTermPref::getInstance()->isWheelSupported_ && isConnected()) {
      session_->writeStr(directions_[j]);
    }
    return ;
  }
  //session_->sendMouseState(j, Qt::NoButton, we->state(), we->pos());
}

//keyboard input event
void FQTermWindow::keyPressEvent(QKeyEvent *keyevent) {
  if (keyevent->isAutoRepeat() && !session_->getBuffer()->isAutoRepeatMode()) {
    FQ_TRACE("sendkey", 5)
        << "The terminal is set to not allow repeated key events.";
    keyevent->accept();
    return;
  }
  if (scriptKeyEvent(keyevent))
    return;

  keyevent->accept();

  if (!isConnected()) {
    if (keyevent->key() == Qt::Key_Return || keyevent->key() == Qt::Key_Enter) {
      session_->reconnect();
    } else if (keyevent->key() == Qt::Key_Space) {
        emit(connectionClosed(this));
    }
    return ;
  }

  // stop  the tab blinking
  stopBlink();
  if (!session_->readyForInput()) {
    return;
  }
  // message replying
  session_->leaveIdle();

  Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
  int key = keyevent->key();
  sendKey(key, modifier, keyevent->text());
}
/*
void FQTermWindow::focusInEvent (QFocusEvent *event) {
  QMainWindow::focusInEvent(event);
  screen_->setFocus(Qt::OtherFocusReason);
}
*/

//connect slot
void FQTermWindow::connectHost() {
  pageViewMessage_->display(tr("Not connected"), screen_->mapToPixel(QPoint(1, 1)));
  session_->setProxy(session_->param().proxyType_,
                     session_->param().isAuthentation_,
                     session_->param().proxyHostName_,
                     session_->param().proxyPort_,
                     session_->param().proxyUserName_,
                     session_->param().proxyPassword_);
  session_->connectHost(session_->param().hostAddress_, session_->param().port_);
  config_->setItemValue("global", "lastaddrindex", QString("%1").arg(addressIndex_));
}

bool FQTermWindow::isConnected() {
  return session_->isConnected();
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Telnet State                                   */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void FQTermWindow::sessionUpdated() {
  refreshScreen();
  //send a mouse move event to make mouse-related change
  QMouseEvent* me = new QMouseEvent(
      QEvent::MouseMove, mapFromGlobal(QCursor::pos()),
      QCursor::pos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
  QApplication::postEvent(this, me);
}

void FQTermWindow::requestUserPwd(QString *user, QString *pwd, bool *isOK) {
  SSHLoginDialog login(user, pwd, this);
  *isOK = (login.exec() == QDialog::Accepted);
}

void FQTermWindow::ZmodemState(int type, int value, const char *status) {
  QString strMsg;
  //to be completed
  switch (type) {
    case RcvByteCount:
    case SndByteCount:
      zmodemDialog_->setProgress(value);
      return;
    case RcvTimeout:
      /* receiver did not respond, aborting */
      strMsg = QString(tr("Time out!"));
      break;
    case SndTimeout:
      /* value is # of consecutive send timeouts */
      strMsg = QString(tr("Time out after trying %1 times")).arg(value);
      break;
    case RmtCancel:
      /* remote end has cancelled */
      strMsg = QString(tr("Canceled by remote peer %1")).arg(status);
      break;
    case ProtocolErr:
      /* protocol error has occurred, val=hdr */
      strMsg = QString(
          tr("Unhandled header %1 at state %2")).arg(value).arg(status);
      break;
    case RemoteMessage:
      /* message from remote end */
      strMsg = QString(tr("Msg from remote peer: %1")).arg(status);
      break;
    case DataErr:
      /* data error, val=error count */
      strMsg = QString(tr("Data errors %1")).arg(value);
      break;
    case FileErr:
      /* error writing file, val=errno */
      strMsg = QString(tr("Falied to write file"));
      break;
    case FileBegin:
      /* file transfer begins, str=name */
      FQ_TRACE("window", 1) << "Starting file " << status;
      zmodemDialog_->setFileInfo(session_->bbs2unicode(status), value);
      zmodemDialog_->setProgress(0);
      zmodemDialog_->clearErrorLog();
      zmodemDialog_->show();
      zmodemDialog_->setModal(true);
      return;
    case FileEnd:
      /* file transfer ends, str=name */
      zmodemDialog_->hide();
      return;
    case FileSkip:
      /* file being skipped, str=name */
      strMsg = QString(tr("Skipping file %1")).arg(status);
      break;
  }
  zmodemDialog_->addErrorLog(strMsg);
}

// telnet state slot
void FQTermWindow::TelnetState(int state) {
  switch (state) {
    case TSRESOLVING:
      pageViewMessage_->display(tr("Resolving host name"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSHOSTFOUND:
      pageViewMessage_->display(tr("Host found"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSHOSTNOTFOUND:
      pageViewMessage_->display(tr("Host not found"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSCONNECTING:
      pageViewMessage_->display(tr("Connecting..."), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSHOSTCONNECTED:
      pageViewMessage_->display(tr("Connected"), screen_->mapToPixel(QPoint(1, 1)));
      frame_->updateMenuToolBar();
      break;
    case TSPROXYCONNECTED:
      pageViewMessage_->display(tr("Connected to proxy"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSPROXYAUTH:
      pageViewMessage_->display(tr("Proxy authentation"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSPROXYFAIL:
      pageViewMessage_->display(tr("Proxy failed"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSREFUSED:
      pageViewMessage_->display(tr("Connection refused"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSREADERROR:
      pageViewMessage_->display(tr("Error when reading from server"), screen_->mapToPixel(QPoint(1, 1)), 
                                PageViewMessage::Error);
      break;
    case TSCLOSED:
      pageViewMessage_->display(tr("Connection closed"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSCLOSEFINISH:
      pageViewMessage_->display(tr("Connection close finished"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSCONNECTVIAPROXY:
      pageViewMessage_->display(tr("Connect to host via proxy"), screen_->mapToPixel(QPoint(1, 1)));
      break;
    case TSEGETHOSTBYNAME:
      pageViewMessage_->display(
          tr("Error in gethostbyname"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
      break;
    case TSEINIWINSOCK:
      pageViewMessage_->display(
          tr("Error in startup winsock"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
      break;
    case TSERROR:
      pageViewMessage_->display(
          tr("Error in connection"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
      break;
    case TSPROXYERROR:
      pageViewMessage_->display(tr("Error in proxy"), screen_->mapToPixel(QPoint(1, 1)), PageViewMessage::Error);
      break;
    case TSWRITED:
      break;
  }
}

void FQTermWindow::showSessionErrorMessage(QString reason) {
  if (reason == tr("User Cancel")) {
    return;
  }
  QMessageBox::critical(this, tr("Session error"), reason);
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           UI Slots                                       */
/*                                                                          */
/* ------------------------------------------------------------------------ */


void FQTermWindow::copy() {
  QClipboard *clipboard = QApplication::clipboard();

  QString selected_text = session_->getBuffer()->getTextSelected(
      session_->param().isRectSelect_,
      session_->param().isColorCopy_,
      parseString((const char*)session_->param().escapeString_.toLatin1()));

  QByteArray cstrText = session_->unicode2bbs(selected_text);

  // TODO_UTF16: avoid this replacement.
  if (!FQTermPref::getInstance()->escapeString_.isEmpty()) {
    cstrText.replace(
      parseString((const char*)session_->param().escapeString_.toLatin1()),
      parseString(FQTermPref::getInstance()->escapeString_.toLatin1()));
  }
  selected_text = session_->bbs2unicode(cstrText);

  // TODO_UTF16: there are three modes: Clipboard, Selection, FindBuffer.
  clipboard->setText(selected_text, QClipboard::Clipboard);
  clipboard->setText(selected_text, QClipboard::Selection);
}

void FQTermWindow::paste() {
  pasteHelper(true);
}

void FQTermWindow::writePasting(const QString& content)
{
  QByteArray cstrText = session_->unicode2bbs(content);

  // TODO_UTF16: avoid this replacement.
  if (!FQTermPref::getInstance()->escapeString_.isEmpty()) {
    cstrText.replace(
      parseString(FQTermPref::getInstance()->escapeString_.toLatin1()),
      parseString((const char*)session_->param().escapeString_.toLatin1()));
  }

  if (session_->param().isAutoWrap_) {
    // convert to unicode for word wrap
    QString strText;
    strText = session_->bbs2unicode(cstrText);
    // insert '\n' as needed
    for (uint i = 0; (long)i < strText.length(); i++) {
      uint j = i;
      uint k = 0, l = 0;
      while ((long)j < strText.length() && strText.at(j) != QChar('\n')) {
        if (FQTermPref::getInstance()->widthToWrapWord_ - (l - k) >= 0
          && FQTermPref::getInstance()->widthToWrapWord_ - (l - k) < 2) {
            strText.insert(j, QChar('\n'));
            k = l;
            j++;
            break;
        }
        // double byte or not
        if (strText.at(j).row() == '\0') {
          l++;
        } else {
          l += 2;
        }
        j++;
      }
      i = j;
    }

    cstrText = session_->unicode2bbs(strText);
  }

  session_->write(cstrText, cstrText.length());
}

void FQTermWindow::pasteHelper(bool clip) {
  if (!isConnected()) {
    return ;
  }

  // TODO_UTF16: there are three modes: Clipboard, Selection, FindBuffer.
  QClipboard *clipboard = QApplication::clipboard();

  // TODO_UTF16: what's the termFrame_->clipboardEncodingID_?
  /*
  if (termFrame_->clipboardEncodingID_ == 0) {
  if (clip) {
  cstrText = U2G(clipboard->text(QClipboard::Clipboard));
  }
  else {
  cstrText = U2G(clipboard->text(QClipboard::Selection));
  }

  if (session_->param().serverEncodingID_ == 1) {
  char *str = encodingConverter_.G2B(cstrText, cstrText.length());
  cstrText = str;
  delete [] str;
  }
  } else {
  if (clip) {
  cstrText = U2B(clipboard->text(QClipboard::Clipboard));
  } else {
  cstrText = U2B(clipboard->text(QClipboard::Selection));
  }

  if (session_->param().serverEncodingID_ == 0) {
  char *str = encodingConverter_.B2G(cstrText, cstrText.length());
  cstrText = str;
  delete [] str;
  }
  }
  */

  QString clipStr;

  if (clip) {
    clipStr = clipboard->text(QClipboard::Clipboard);
  } else {
    clipStr = clipboard->text(QClipboard::Selection);
  }

  writePasting(clipStr);
}

void FQTermWindow::openAsUrl() {
  QString selected_text = session_->getBuffer()->getTextSelected(
    session_->param().isRectSelect_,
    session_->param().isColorCopy_,
    parseString((const char*)session_->param().escapeString_.toLatin1()));
  openUrl(selected_text);
}

void FQTermWindow::searchIt()
{
  script_engine_->runScript(getPath(RESOURCE) + "script/search.js");
  QScriptValueList qvl;
  qvl << FQTermPref::getInstance()->searchEngine_;
  if (!script_engine_->scriptCallback("searchSelected", qvl)) {
    //fall back to google.
    QString selected_text = session_->getBuffer()->getTextSelected(
      session_->param().isRectSelect_,
      session_->param().isColorCopy_,
      parseString((const char*)session_->param().escapeString_.toLatin1()));
    QString searchUrl = "http://www.google.com/search?client=fqterm&rls=en&q=" + selected_text + "&sourceid=fqterm";
    QByteArray url = QUrl(searchUrl).toEncoded();
    openUrlImpl(url);
  }
}

void FQTermWindow::shareIt()
{
  script_engine_->runScript(getPath(RESOURCE) + "script/weiboshare.js");
}

void FQTermWindow::fastPost()
{
  QClipboard *clipboard = QApplication::clipboard();
  QString clipStr = clipboard->text(QClipboard::Clipboard);
  QString lineEnd("\n");
  QString postTitle = clipStr.left(clipStr.indexOf(lineEnd));

  sendParsedString("^p");
  writeRawString(postTitle);
  writeRawString(QString("\n\n"));
  writePasting(clipStr);
  sendParsedString("^W\n");
}


void FQTermWindow::externalEditor() {
  externalEditor_->start();
}


void FQTermWindow::externalEditorDone(const QString& str) {
  QByteArray rawStr = session_->unicode2bbs_smart(str);
  session_->write(rawStr, rawStr.length());
}


void FQTermWindow::copyArticle() {
  if (!isConnected()) {
    return ;
  }

  session_->copyArticle();
}

void FQTermWindow::setColor() {
  addrDialog set(this, session_->param());

  set.setCurrentTabIndex(addrDialog::Display);
  int res = set.exec();
  if (res != 0) {
    updateSetting(set.param());
    if (res == 2)
      saveSetting(false);
  }

}

void FQTermWindow::disconnect() {
  session_->close();

  connectionClosed();
}

void FQTermWindow::showIP(bool show) {
  pageViewMessage_->hide();
  if (!show) {
    return;
  }
  QString country, city;
  QString url = session_->getIP();
  FQTermIPLocation *ipLocation = FQTermIPLocation::getInstance();

  QRect screenRect = screen_->rect();
  QPoint messagePos;
  QRect globalIPRectangle = QRect(screen_->mapToRect(ipRectangle_));
  QFontMetrics fm(qApp->font());
  int charHeight = fm.height();

  int midLine = (screenRect.top() + screenRect.bottom()) / 2;
  int ipMidLine = (globalIPRectangle.top() + globalIPRectangle.bottom()) / 2;
  if (ipMidLine < midLine) {
    // "There is Plenty of Room at the Bottom." -- Feyman, 1959
    messagePos.setY(globalIPRectangle.bottom() + 0.618 * charHeight);
  } else {
    messagePos.setY(globalIPRectangle.top()
                    - 0.618 * charHeight
                    - pageViewMessage_->size().height());
  }

  QString displayText;
  if (ipLocation == NULL) {
    displayText = tr("IP database file does NOT exist");
  } else if (!ipLocation->getLocation(url, country, city)) {
    displayText = tr("Invalid IP");
  } else {
    displayText = country + " " + city;
  }

  QRect messageSize(pageViewMessage_->displayCheck(displayText,
                                                   PageViewMessage::Info));
  PageViewMessage::Alignment ali;
  if (messageSize.width() + globalIPRectangle.left() >= screenRect.right()) {
    //"But There is No Room at the Right" -- Curvelet, 2007
    messagePos.setX(globalIPRectangle.right());
    ali = PageViewMessage::TopRight;
  } else {
    messagePos.setX(globalIPRectangle.left());
    ali = PageViewMessage::TopLeft;
  }

  pageViewMessage_->display(
      displayText,
      PageViewMessage::Info,
      0, messagePos, ali);
}

void FQTermWindow::runScript() {
  // get the previous dir
  QStringList fileList;
  FQTermFileDialog fileDialog(config_);

  fileList = fileDialog.getOpenNames("Choose a Javascript file", "JavaScript File (*.js)");

  if (!fileList.isEmpty() && fileList.count() == 1) {
    runScript(fileList.at(0));
  }
}

void FQTermWindow::stopScript() {
  script_engine_->stopScript();
}

void FQTermWindow::viewMessages() {
  msgDialog msg(this);

  QByteArray dlgSize =
//      frame_->config()->getItemValue("global", "msgdialog").toLatin1();
    config_->getItemValue("global", "msgdialog").toLatin1();
  const char *dsize = dlgSize.constData();
  if (!dlgSize.isEmpty()) {
    int x, y, cx, cy;
    sscanf(dsize, "%d %d %d %d", &x, &y, &cx, &cy);
    msg.resize(QSize(cx, cy));
    msg.move(QPoint(x, y));
  } else {
    msg.resize(QSize(300, 500));
    msg.move(QPoint(20,20));
  }

  msg.setMessageText(allMessages_);
  msg.exec();

  QString strSize = QString("%1 %2 %3 %4").arg(msg.x()).arg(msg.y()).arg
                    (msg.width()).arg(msg.height());
//  frame_->config()->setItemValue("global", "msgdialog", strSize);
    config_->setItemValue("global", "msgdialog", strSize);
}

void FQTermWindow::setting() {
  addrDialog set(this, session_->param());
  int res = set.exec();
  if (res != 0) {
    updateSetting(set.param());
    if (res == 2)
      saveSetting(false);
  }
}

void FQTermWindow::toggleAntiIdle() {
  session_->setAntiIdle(!session_->isAntiIdle());
}

void FQTermWindow::toggleAutoReply() {
  session_->setAutoReply(!session_->isAutoReply());
}

void FQTermWindow::toggleAutoReconnect() {
  session_->setAutoReconnect(!session_->param().isAutoReconnect_);
}

void FQTermWindow::connectionClosed() {
  stopBlink();

  pageViewMessage_->display(tr("Connection closed"), screen_->mapToPixel(QPoint(1, 1)));

  frame_->updateMenuToolBar();

  setCursor(cursors_[FQTermSession::kNormal]);

  refreshScreen();

  if (!getSession()->param().isAutoReconnect_ && getSession()->param().isAutoCloseWin_) {
    emit(connectionClosed(this));
  }
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Events                                         */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void FQTermWindow::articleCopied(int e, const QString content) {
  if (e == DAE_FINISH) {
//    articleDialog article(frame_->config(), this);
    articleDialog article(config_, this);

    // Fix focus-losing bug.
    // dsize should be a pointer to a non-temprary object.
    QByteArray dlgSize =
//        frame_->config()->getItemValue("global", "articledialog").toLatin1();
      config_->getItemValue("global", "articledialog").toLatin1();

    const char *dsize = dlgSize.constData();

    if (!dlgSize.isEmpty()) {
      int x, y, cx, cy;
      sscanf(dsize, "%d %d %d %d", &x, &y, &cx, &cy);
      article.resize(QSize(cx, cy));
      article.move(QPoint(x, y));
    } else {
      article.resize(QSize(300, 500));
      article.move(20,20);
    }
    article.articleText_ = content;

    article.ui_.textBrowser->setPlainText(article.articleText_);
    article.exec();
    QString strSize = QString("%1 %2 %3 %4").arg(article.x()).arg(article.y())
                      .arg(article.width()).arg(article.height());
//    frame_->config()->setItemValue("global", "articledialog", strSize);
    config_->setItemValue("global", "articledialog", strSize);
  } else if (e == DAE_TIMEOUT) {
    QMessageBox::warning(this, "timeout", "download article timeout, aborted");
#ifdef HAVE_PYTHON
  } else if (e == PYE_ERROR) {
    QMessageBox::warning(this, "Python script error", pythonErrorMessage_);
  } else if (e == PYE_FINISH) {
    QMessageBox::information(this, "Python script finished",
                             "Python script file executed successfully");
#endif //HAVE_PYTHON          
  }
}

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Aux Func                                       */
/*                                                                          */
/* ------------------------------------------------------------------------ */

QByteArray FQTermWindow::parseString(const QByteArray &cstr, int *len) {
  QByteArray parsed = "";

  if (len != 0) {
    *len = 0;
  }

  for (uint i = 0; (long)i < cstr.length(); i++) {
    if (cstr.at(i) == '^') {
      i++;
      if ((long)i < cstr.length()) {
        parsed += FQTERM_CTRL(cstr.at(i));
        if (len != 0) {
          *len =  *len + 1;
        }
      }
    } else if (cstr.at(i) == '\\') {
        i++;
        if ((long)i < cstr.length()) {
          if (cstr.at(i) == 'n') {
            parsed += CHAR_CR;
          } else if (cstr.at(i) == 'r') {
            parsed += CHAR_LF;
          } else if (cstr.at(i) == 't') {
            parsed += CHAR_TAB;
          }
          if (len != 0) {
            *len =  *len + 1;
          }
        }
      } else {
        parsed += cstr.at(i);
        if (len != 0) {
          *len =  *len + 1;
        }
      }
  }

  return parsed;
}

void FQTermWindow::messageAutoReplied() {
  pageViewMessage_->display("You have messages", PageViewMessage::Info, 0);
}

void FQTermWindow::saveSetting(bool ask /* = true*/) {
  if (addressIndex_ == -1) {
    return ;
  }

  //save these options silently
  FQTermConfig pConf(getPath(USER_CONFIG) + "address.cfg");
  FQTermParam param;
  loadAddress(&pConf, addressIndex_, param);
  param.isAutoCopy_ = session_->param().isAutoCopy_;
  param.isAutoWrap_ = session_->param().isAutoWrap_;
  param.isRectSelect_ = session_->param().isRectSelect_;
  param.isColorCopy_ = session_->param().isColorCopy_;
  saveAddress(&pConf, addressIndex_, param);
  pConf.save(getPath(USER_CONFIG) + "address.cfg");

  if (param == session_->param()) {
    return;
  }
  
  QMessageBox mb("FQTerm", "Setting changed do you want to save it?",
                 QMessageBox::Warning, QMessageBox::Yes | QMessageBox::Default,
                 QMessageBox::No | QMessageBox::Escape, 0, this);
  if (!ask || mb.exec() == QMessageBox::Yes) {
    saveAddress(&pConf, addressIndex_, session_->param());
    pConf.save(getPath(USER_CONFIG) + "address.cfg");
  }
}

int FQTermWindow::externInput(const QByteArray &cstrText) {
  QByteArray cstrParsed = parseString(cstrText);
  return session_->write(cstrParsed, cstrParsed.length());
}

int FQTermWindow::externInput(const QString &cstrText) {
  return externInput(session_->unicode2bbs_smart(cstrText));
}

int FQTermWindow::writeRawString(const QString& str) {
  QByteArray rawStr = session_->unicode2bbs_smart(str);
  return session_->write(rawStr, rawStr.length());
}

void FQTermWindow::sendParsedString(const char *str) {
  int length;
  QByteArray cstr = parseString(str, &length);
  session_->write(cstr, length);
}


bool FQTermWindow::postQtScriptCallback(const QString& func, const QScriptValueList & args) {
  return script_engine_->scriptCallback(func, args);
}

#ifdef HAVE_PYTHON
bool FQTermWindow::postPythonCallback( const QString& func, PyObject* pArgs )
{
  return pythonCallback(func, pArgs);
}
#endif //HAVE_PYTHON

// void FQTermWindow::setMouseMode(bool on) {
//   isMouseX11_ = on;
// }

//  /* 0-left 1-middle 2-right 3-relsase 4/5-wheel
//   *
//   */
//  //void FQTermWindow::sendMouseState( int num,
//  //    Qt::ButtonState btnstate, Qt::ButtonState keystate, const QPoint& pt )
//  void FQTermWindow::sendMouseState(int num, Qt::KeyboardModifier btnstate,
//                           Qt::KeyboardModifier keystate, const QPoint &pt) {
//    /*
//      if(!m_bMouseX11)
//      return;
//
//      QPoint ptc = screen_->mapToChar(pt);
//
//      if(btnstate&Qt::LeftButton)
//      num = num==0?0:num;
//      else if(btnstate&Qt::MidButton)
//      num = num==0?1:num;
//      else if(btnstate&Qt::RightButton)
//      num = num==0?2:num;
//
//      int state = 0;
//      if(keystate&Qt::ShiftModifier)
//      state |= 0x04;
//      if(keystate&Qt::MetaModifier)
//      state |= 0x08;
//      if(keystate&Qt::ControlModifier)
//      state |= 0x10;
//
//      // normal buttons are passed as 0x20 + button,
//      // mouse wheel (buttons 4,5) as 0x5c + button
//      if(num>3) num += 0x3c;
//
//      char mouse[10];
//      sprintf(mouse, "\033[M%c%c%c",
//      num+state+0x20,
//      ptc.x()+1+0x20,
//      ptc.y()+1+0x20);
//      m_pTelnet->write( mouse, strlen(mouse) );
//    */
//  }

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           Python Func                                    */
/*                                                                          */
/* ------------------------------------------------------------------------ */
#ifdef HAVE_PYTHON


void FQTermWindow::runPythonScript() {
  QStringList fileList;
  FQTermFileDialog fileDialog(config_);

  fileList = fileDialog.getOpenNames("Choose a Python file", "Python File (*.py)");

  if (!fileList.isEmpty() && fileList.count() == 1) {
    runPythonScriptFile(fileList.at(0));
  }
}

void FQTermWindow::runPythonScriptFile(const QString& file) {
  QString str(QString("%1").arg(long(this)));
	char* lp = fq_strdup(str.toUtf8().data());
	char *argv[2]={lp,NULL};
    // get the global python thread lock
    PyEval_AcquireLock();
    
    PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;

    // create a thread state object for this thread
    PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
    PyThreadState_Swap(myThreadState);


    PySys_SetArgv(1, argv);

    runPythonFile(file);

    //Clean up this thread's python interpreter reference
    PyThreadState_Swap(NULL);
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);
    PyEval_ReleaseLock(); 
    free((void*)lp);
}

bool FQTermWindow::pythonCallback(const QString & func, PyObject* pArgs) {
	if(!pythonScriptLoaded_) {
		Py_DECREF(pArgs);
		return false;
	};
	
	bool done = false;
	// get the global lock
  PyEval_AcquireLock();
	// get a reference to the PyInterpreterState
      
	//Python thread references
      
	PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;
	// create a thread state object for this thread
	PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
	PyThreadState_Swap(myThreadState);
    
	PyObject *pF = PyString_FromString(func.toUtf8());
	PyObject *pFunc = PyDict_GetItem(pDict, pF);
 	Py_DECREF(pF);

	if (pFunc && PyCallable_Check(pFunc)) 
	{
		PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
		Py_DECREF(pArgs);
		if (pValue != NULL) 
		{
      if (PyBool_Check(pValue) && pValue == Py_True)
			  done = true;
			Py_DECREF(pValue);
		}
		else 
		{
			//QMessageBox::warning(this,"Python script error", getException());
      printf("%p: Python script error\n", this, getException().toUtf8().data());
		}
  }
	else 
	{
		PyErr_Print();
		printf("Cannot find python %s callback function\n", func.toUtf8().data());
	}
      
	// swap my thread state out of the interpreter
	PyThreadState_Swap(NULL);
	// clear out any cruft from thread state object
	PyThreadState_Clear(myThreadState);
   // delete my thread state object
	PyThreadState_Delete(myThreadState);
	// release the lock
	PyEval_ReleaseLock();

	if (func == "autoReply")
		startBlink();

	return done;
}


int FQTermWindow::runPythonFile( const QString& file )
{
  QString buffer("def work_thread():\n"
                 "\ttry:\n\t\texecfile('");
  buffer += QString(file).replace("\\", "\\\\");

  /* Have to do it like this. PyRun_SimpleFile requires you to pass a
   * stdio file pointer, but Vim and the Python DLL are compiled with
   * different options under Windows, meaning that stdio pointers aren't
   * compatible between the two. Yuk.
   *
   * Put the string "execfile('file')" into buffer. But, we need to
   * escape any backslashes or single quotes in the file name, so that
   * Python won't mangle the file name.
 * ---- kafa
   */
  
  /* Put in the terminating "')" and a null */
  buffer += "',{})\n\0";

  buffer += QString("\texcept:\n"
      "\t\texc, val, tb = sys.exc_info()\n"
      "\t\tlines = traceback.format_exception(exc, val, tb)\n"
      "\t\terr = string.join(lines)\n"
      "\t\tprint err\n"
      "\t\tf=open('%1','w')\n"
      "\t\tf.write(err)\n"
      "\t\tf.close()\n").arg(getErrOutputFile(this));

  buffer += QString("\t\tfqterm.formatError(%1)\n").arg((long)this);
  buffer += "\t\texit\n";

  /* Execute the file */
	PyRun_SimpleString("import thread,string,sys,traceback,fqterm");
	PyRun_SimpleString(buffer.toUtf8());
	PyRun_SimpleString(	"thread.start_new_thread(work_thread,())\n");

	return 0;
}

void FQTermWindow::initPython(const QString& file) {

	// get the global python thread lock
  PyEval_AcquireLock();

  // get a reference to the PyInterpreterState
  PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;



  // create a thread state object for this thread
  PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
  PyThreadState_Swap(myThreadState);
  
  Py_InitModule4("fqterm", fqterm_methods,
    NULL,(PyObject*)NULL,PYTHON_API_VERSION);
    
	PyImport_AddModule("fqterm");

	if(!file.isEmpty() )
	{
    QFileInfo info(file);
    PyRun_SimpleString("import sys"); 
    PyRun_SimpleString(QString("sys.path.append(\"%1/\")").arg(info.absolutePath()).toUtf8().data()); 
		PyObject *pName = PyString_FromString(info.baseName().toUtf8().data());
		pModule = PyImport_Import(pName);
    //PyErr_Print();
		Py_DECREF(pName);
		if (pModule != NULL) 
			  pDict = PyModule_GetDict(pModule);
		else
		{
			//printf("Failed to PyImport_Import\n");
		}

		if(pDict != NULL )
			pythonScriptLoaded_ = true;
		else
		{
			//printf("Failed to PyModule_GetDict\n");
		}
	}
	
    //Clean up this thread's python interpreter reference
    PyThreadState_Swap(NULL);
    PyThreadState_Clear(myThreadState);
    PyThreadState_Delete(myThreadState);
    PyEval_ReleaseLock();
    
}

void FQTermWindow::finalizePython()
{
  // get the global python thread lock
	PyEval_AcquireLock();

	// get a reference to the PyInterpreterState
	PyInterpreterState * mainInterpreterState = frame_->getPythonHelper()->getPyThreadState()->interp;
      
	// create a thread state object for this thread
	PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
	PyThreadState_Swap(myThreadState);
      
	//Displose of current python module so we can reload in constructor.
	if(pModule!=NULL)
		Py_DECREF(pModule);

	//Clean up this thread's python interpreter reference
	PyThreadState_Swap(NULL);
	PyThreadState_Clear(myThreadState);
	PyThreadState_Delete(myThreadState);
	PyEval_ReleaseLock();
}

#endif //HAVE_PYTHON

/* ------------------------------------------------------------------------ */
/*                                                                         */
/*                           HTTP Func                                      */
/*                                                                          */
/* ------------------------------------------------------------------------ */

void FQTermWindow::openLink() {
  QString url = session_->getUrl();
  if (!url.isEmpty()) {
	openUrlImpl(url);
  }
}

void FQTermWindow::saveLink() {
  QString url = session_->getUrl();
  if (!url.isEmpty())
    getHttpHelper(url, false);
}

void FQTermWindow::openUrl(QString url)
{
  QString caption = tr("URL Dialog");
  QString strUrl = url;
  QTextEdit *textEdit = new QTextEdit();
  textEdit->setPlainText(strUrl);
  //textEdit->setFocus();
  textEdit->setTabChangesFocus(true);
  textEdit->moveCursor(QTextCursor::End);
  

  QPushButton *okButton = new QPushButton(tr("&Open URL"));
  QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
//  okButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
//  cancelButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
  

  QGridLayout *layout = new QGridLayout;
  layout->addWidget(textEdit, 0, 0, 1, -1);
  layout->addWidget(okButton, 1, 3);
  layout->addWidget(cancelButton, 1, 4);

  QDialog dlg(this);

  QFontMetrics fm(qApp->font());
  

  connect(okButton, SIGNAL(pressed()), &dlg, SLOT(accept()));
  connect(cancelButton, SIGNAL(pressed()), &dlg, SLOT(reject()));

  
  dlg.setWindowTitle(caption);
  dlg.setLayout(layout);
  dlg.resize(fm.width(QString(30, 'W')), fm.height() * (fm.width(strUrl) / fm.width(QString(30, 'W')) + 5));
  textEdit->clearFocus();
  okButton->setFocus(Qt::TabFocusReason);
  if (dlg.exec()) {
	  openUrlImpl(textEdit->toPlainText());
  }
}


void FQTermWindow::openUrlImpl(QString url)
{
    const QString &httpBrowser = FQTermPref::getInstance()->httpBrowser_;
    if (httpBrowser.isNull() || httpBrowser.isEmpty()) {
#if QT_VERSION >= 0x040400
	    QDesktopServices::openUrl(QUrl::fromEncoded(url.toLocal8Bit()));
#else
      QDesktopServices::openUrl(url);
#endif
    } else {
	    runProgram(httpBrowser, url);
    }
}


void FQTermWindow::previewLink() {
  getHttpHelper(session_->getUrl(), true);
}

void FQTermWindow::copyLink() {
  QString strUrl;
  strUrl = session_->bbs2unicode(session_->getUrl().toLatin1());

  QClipboard *clipboard = QApplication::clipboard();

  clipboard->setText(strUrl, QClipboard::Selection);
  clipboard->setText(strUrl, QClipboard::Clipboard);
}

void FQTermWindow::getHttpHelper(const QString &url, bool preview) {

  const QString &strPool = FQTermPref::getInstance()->poolDir_;

  FQTermHttp *http = new FQTermHttp(config_, this, strPool, session_->param().serverEncodingID_);
  if (getSession()->param().proxyType_ != 0) {
    QString host = getSession()->param().proxyHostName_;
    int port = getSession()->param().proxyPort_;
    bool auth = getSession()->param().isAuthentation_;
    QString user = auth ? getSession()->param().proxyUserName_ : QString();
    QString pass = auth ? getSession()->param().proxyPassword_ : QString();
    switch (getSession()->param().proxyType_)
    {
    case 1:
    case 2:
      //no support in qt.
      FQ_TRACE("network", 0) << "proxy type not supported by qt, download will not use proxy.";
      break;
    case 3:
      http->setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, host, port, user, pass));
      break;
    case 4:
      http->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port, user, pass));
      break;
    }
  }
  FQTerm::StatusBar::instance()->newProgressOperation(http).setDescription(tr("Waiting header...")).setAbortSlot(http, SLOT(cancel())).setMaximum(100);
  FQTerm::StatusBar::instance()->resetMainText();
  FQTerm::StatusBar::instance()->setProgress(http, 0);

  FQ_VERIFY(connect(http, SIGNAL(headerReceived(FQTermHttp *, const QString &)),
                    this, SLOT(startHttpDownload(FQTermHttp*,const QString&))));

  FQ_VERIFY(connect(http,SIGNAL(done(QObject*)),this,SLOT(httpDone(QObject*))));

  FQ_VERIFY(connect(http, SIGNAL(message(const QString &)),
                    pageViewMessage_, SLOT(showText(const QString &))));

  FQ_VERIFY(connect(http, SIGNAL(done(QObject*)),
                    FQTerm::StatusBar::instance(),
                    SLOT(endProgressOperation(QObject*))));

  FQ_VERIFY(connect(http, SIGNAL(percent(int)),
                    FQTerm::StatusBar::instance(), SLOT(setProgress(int))));

  FQ_VERIFY(connect(http, SIGNAL(previewImage(const QString &, bool, bool)),
                    this, SLOT(httpPreviewImage(const QString &, bool, bool))));


  http->getLink(url, preview);
}

void FQTermWindow::startHttpDownload(
    FQTermHttp *pHttp, const QString &filedesp) {
  FQTerm::StatusBar::instance()->newProgressOperation(pHttp).setDescription
      (filedesp).setAbortSlot(pHttp, SLOT(cancel())).setMaximum(100);

  FQTerm::StatusBar::instance()->resetMainText();
}

void FQTermWindow::httpDone(QObject *pHttp) {
  pHttp->deleteLater();
}

void FQTermWindow::httpPreviewImage(const QString &filename, bool raiseViewer, bool done) 
{
  if (config_->getItemValue("preference", "image").isEmpty() || done) {
    previewImage(filename, raiseViewer);
  }
}

void FQTermWindow::previewImage(const QString &filename, bool raiseViewer) {
//  QString strViewer = frame_->config()->getItemValue("preference", "image");
  QString strViewer = config_->getItemValue("preference", "image");

  if (strViewer.isEmpty()) {
    frame_->viewImages(filename, raiseViewer);
  } else if(raiseViewer) {
    runProgram(strViewer, filename);
  }

}

void FQTermWindow::beep() {
  if (session_->param().isBeep_) {
    if (session_->param().isBuzz_) {
      frame_->buzz(this);
    }
    if (FQTermPref::getInstance()->beepSoundFileName_.isEmpty() ||
      FQTermPref::getInstance()->openBeep_ == 3) {
      qApp->beep();
    }
    else {
      sound_ = NULL;

      switch (FQTermPref::getInstance()->beepMethodID_) {
        case 0:
          sound_ =
              new FQTermSystemSound(FQTermPref::getInstance()->beepSoundFileName_);
          break;
        case 1:
          sound_ =
              new FQTermExternalSound(FQTermPref::getInstance()->beepPlayerName_,
                FQTermPref::getInstance()->beepSoundFileName_);
          break;
      }

      if (sound_) {
        sound_->start();
      }
    }
  }

  QString strMsg = session_->getMessage();
  if (!strMsg.isEmpty()) {
    allMessages_ += strMsg + "\n\n";
  }

  session_->setSendingMessage();
}


void FQTermWindow::onTitleSet(const QString& title) {
  setWindowTitle(title);
}


void FQTermWindow::startBlink() {
  if (FQTermPref::getInstance()->openTabBlinking_) {
    if (!tabBlinkTimer_->isActive()) {
      tabBlinkTimer_->start(500);
    }
  }
}

void FQTermWindow::stopBlink() {
  if (tabBlinkTimer_->isActive()) {
    tabBlinkTimer_->stop();
    emit blinkTheTab(this, true);
  }
}

void FQTermWindow::setCursorType(const QPoint& mousePosition) {
  QRect rcOld;
  bool isUrl = false;
  if (FQTermPref::getInstance()->openUrlCheck_) {
    showIP(session_->isIP(ipRectangle_, rcOld));
    if (session_->isUrl(urlRectangle_, rcOld)) {
      setCursor(Qt::PointingHandCursor);
      isUrl = true;
    }
  }
  if (!isUrl) {
    FQTermSession::CursorType cursorType =
        session_->getCursorType(screen_->mapToChar(mousePosition));
    setCursor(cursors_[cursorType]);
  }
}

void FQTermWindow::setCursorPosition(const QPoint& mousePosition) {
  // set cursor pos, repaint if state changed
  QRect rc;
  if (session_->setCursorPos(screen_->mapToChar(mousePosition), rc)) {
    screen_->repaint(screen_->mapToRect(rc));

    //re-check whether the cursor is still on the selected rectangle -- dp
    QPoint localPos = mapFromGlobal(QCursor::pos()); //local.
    if (!session_->getMenuRect().contains(screen_->mapToChar(localPos)))
    {
      QRect dummyRect;
      rc = session_->getMenuRect();
      session_->setCursorPos(screen_->mapToChar(localPos), dummyRect);
      screen_->repaint(screen_->mapToRect(rc));
      //restore the state.
      session_->setCursorPos(screen_->mapToChar(mousePosition), dummyRect); 
    }
  }
}

void FQTermWindow::refreshScreen() {
  screen_->setPaintState(FQTermScreen::NewData);
  screen_->update();
}

void FQTermWindow::repaintScreen() {

  screen_->setPaintState(FQTermScreen::Repaint);
  if (clientRect_ != QRect()) {
    screen_->repaint(screen_->mapToRect(clientRect_));
  } else {
    screen_->repaint();
  }
  clientRect_ = QRect();
//  screen_->update(clientRect_);
}

void FQTermWindow::enterMenuItem() {
  char cr = CHAR_CR;
  QRect rc = session_->getMenuRect();
  FQTermSession::PageState ps = session_->getPageState();
  switch (ps) {
    case FQTermSession::Menu:
    case FQTermSession::MailMenu:
    case FQTermSession::TOP10:
      if (!rc.isEmpty()) {
        char cMenu = session_->getMenuChar();
        session_->write(&cMenu, 1);
        session_->write(&cr, 1);
      }
      break;
    case FQTermSession::ArticleList:
    case FQTermSession::BoardList:
    case FQTermSession::FriendMailList:
    case FQTermSession::EliteArticleList:
      if (!rc.isEmpty()) {
        int n = rc.y() - session_->getBuffer()->getCaretLine();
        // scroll lines
        int cursorType = n>0?FQTermSession::kDown:FQTermSession::kUp;
        n = qAbs(n);
        while (n) {
          session_->write(directions_[cursorType], 4);
          n--;
        }
      }
      session_->write(&cr, 1);
      break;
    default:
      // TODO: process other PageState.
      break;
  }
}

void FQTermWindow::processLClick(const QPoint& cellClicked) {
  int cursorType = session_->getCursorType(cellClicked);
  switch(cursorType)
  {
    case FQTermSession::kHome:
    case FQTermSession::kEnd:
    case FQTermSession::kPageUp:
    case FQTermSession::kPageDown:
      session_->writeStr(directions_[cursorType]);
      break;
    case FQTermSession::kUp:
    case FQTermSession::kDown:
    case FQTermSession::kLeft:
      session_->writeStr(directions_[cursorType]);
      break;
    case FQTermSession::kRight:  //Hand
      enterMenuItem();
      break;
    case FQTermSession::kNormal:
      char cr = CHAR_CR;
      session_->write(&cr, 1);
      break;
  }
}

void FQTermWindow::startSelecting(const QPoint& mousePosition) {
  // clear the selected before
  session_->clearSelect();
  sessionUpdated();

  // set the selecting flag
  isSelecting_ = true;
  lastMouseCell_ = screen_->mapToChar(mousePosition);
}

void FQTermWindow::onSelecting(const QPoint& mousePosition) {
  if (mousePosition.y() < childrenRect().top()) {
    screen_->scrollLine(-1);
  }
  if (mousePosition.y() > childrenRect().bottom()) {
    screen_->scrollLine(1);
  }

  QPoint curMouseCell = screen_->mapToChar(mousePosition);
  session_->setSelect(lastMouseCell_, curMouseCell);
  sessionUpdated();
}

void FQTermWindow::finishSelecting(const QPoint& mousePosition) {
  QPoint currentMouseCell = screen_->mapToChar(mousePosition);
  session_->setSelect(currentMouseCell, lastMouseCell_);
  refreshScreen();
  if (session_->param().isAutoCopy_) {
    copy();
  }
  isSelecting_ = false;
}

void FQTermWindow::sendKey(const int keyCode, const Qt::KeyboardModifiers modifier,
                           const QString &t) {
  if (keyCode == Qt::Key_Shift || keyCode == Qt::Key_Control
      || keyCode == Qt::Key_Meta || keyCode == Qt::Key_Alt) {
    return;
  } 
  
  int key = keyCode;
  QString text = t;
  
#ifdef __APPLE__
  // On Mac, pressing Command Key (Apple Key) will set the ControlModifier flag.
  bool alt_on = ((modifier & Qt::ControlModifier) || (modifier & Qt::AltModifier));
  bool ctrl_on = (modifier & Qt::MetaModifier);
#else
  bool alt_on = ((modifier & Qt::MetaModifier) || (modifier & Qt::AltModifier));
  bool ctrl_on = (modifier & Qt::ControlModifier);
#endif
  bool shift_on = (modifier & Qt::ShiftModifier);
  
#ifdef __APPLE__
  // Bullshit! Qt4.4.0 on Mac generates weird key evnets.
  // fix them here.

  if (ctrl_on) {
    static const char char_map[][2] = {
      {'@', '\x00'}, {'A', '\x01'}, {'B', '\x02'}, {'C', '\x03'}, {'D', '\x04'},
      {'E', '\x05'}, {'F', '\x06'}, {'G', '\x07'}, {'H', '\x08'}, {'J', '\x0a'},
      {'K', '\x0b'}, {'L', '\x0c'}, {'N', '\x0e'}, {'O', '\x0f'}, {'P', '\x10'},
      {'Q', '\x11'}, {'R', '\x12'}, {'S', '\x13'}, {'T', '\x14'}, {'U', '\x15'},
      {'V', '\x16'}, {'W', '\x17'}, {'X', '\x18'}, {'Y', '\x19'}, {'Z', '\x1a'},
      {'\\', '\x1c'}, {']', '\x1d'}, {'^', '\x1e'}, {'_', '\x1f'}
    };

    const int num = sizeof(char_map)/2;

    int expected_key = key;
        
    for (int i = 0; i < num; ++i) {
      const char *c = char_map[i];
      if (c[0] == key) {
        expected_key = c[1];
        break;
      }
    }
   
    if (expected_key != key) {
      FQ_TRACE("sendkey", 5) << "translate the key code from "
                             << (int)key << "(" << (char)key << ")"
                             << " to "
                             << (int)expected_key
                             << "(" << (char)expected_key << ")"
                             << ", and change the text appropriately." ;
      key = expected_key;
      text.clear();
      text.append((char) expected_key);
    }
  }

  if (!ctrl_on && alt_on && !shift_on) {
    if (text.size() == 0 && 0 <= key && key <= 128) {
      FQ_TRACE("sendkey", 5)
          << "add appropriate text according to the key code.";
      text.append((char)key);
    }
  }
  
  if (!ctrl_on && alt_on && shift_on) {
    // fix the key code when both shift key and meta/alt key are pressed.
    static const char char_map[][2] = {
      {'`', '~'}, {'1', '!'}, {'2', '@'}, {'3', '#'}, {'4', '$'},
      {'5', '%'}, {'6', '^'}, {'7', '&'}, {'8', '*'}, {'9', '('}, 
      {'0', ')'}, {'-', '_'}, {'=', '+'}, {'[', '{'}, {']', '}'}, 
      {'\\', '|'}, {';', ':'}, {'\'', '"'}, {',', '<'},{'.', '>'},
      {'/', '?'}
    };
    const int num = sizeof(char_map)/2;

    int expected_key = key;
        
    for (int i = 0; i < num; ++i) {
      const char *c = char_map[i];
      if (c[0] == key) {
        expected_key = c[1];
        break;
      }
    }

    if (expected_key != key) {
      FQ_TRACE("sendkey", 5) << "translate the key code from "
                             << (int)key << "(" << (char)key << ")"
                             << " to "
                             << (int)expected_key
                             << "(" << (char)expected_key << ")";
      key = expected_key;
    }
        
    if (text.size() == 0) {
      text.append(key);
    } else {
      text[0] = key;
    }
    FQ_TRACE("sendkey", 5)
        << "set the text for this key event as the keycode.";
  }      
#endif

  FQ_TRACE("sendkey", 5) << "(alt " << (alt_on? " on": "off")
                         << ", ctrl " << (ctrl_on? " on": "off")
                         << ", shift " << (shift_on? " on": "off") << ")"
                         << " key: " << key
                         << " text: " << (text.size() == 0 ? "NULL" : text)
                         << " (in hex: "
                         << dumpHexString << text.toStdString()
                         << dumpNormalString << ")";
  
  if (session_->getBuffer()->isAnsiMode()) {
    if (session_->getBuffer()->isCursorMode()) {
      switch(key) {
        case Qt::Key_Up:
          session_->writeStr("\x1bOA");
          return;
        case Qt::Key_Down:
          session_->writeStr("\x1bOB");
          return;
        case Qt::Key_Left:
          session_->writeStr("\x1bOD");
          return;
        case Qt::Key_Right:
          session_->writeStr("\x1bOC");
          return;
      }
    } else {
      switch(key) {
        case Qt::Key_Up:
          session_->writeStr("\x1b[A");
          return;
        case Qt::Key_Down:
          session_->writeStr("\x1b[B");
          return;
        case Qt::Key_Left:
          session_->writeStr("\x1b[D");
          return;
        case Qt::Key_Right:
          session_->writeStr("\x1b[C");
          return;
      }
    }
  } else {
    // VT52 mode
    switch(key) {
      case Qt::Key_Up:
        // Do NOT change the parameter to "\x1bA".
        session_->writeStr("\x1b" "A");
        return;
      case Qt::Key_Down:
        // Do NOT change the parameter to "\x1bB".        
        session_->writeStr("\x1b" "B");
        return;
      case Qt::Key_Left:
        // Do NOT change the parameter to "\x1bD".        
        session_->writeStr("\x1b" "D");
        return;
      case Qt::Key_Right:
        // Do NOT change the parameter to "\x1bC".        
        session_->writeStr("\x1b" "C");
        return;
    }
  }

  if (!session_->getBuffer()->isNumericMode() &&
      (modifier & Qt::KeypadModifier)) {
    switch(key) {
      case Qt::Key_0:
      case Qt::Key_1:
      case Qt::Key_2:
      case Qt::Key_3:
      case Qt::Key_4:
      case Qt::Key_5:
      case Qt::Key_6:
      case Qt::Key_7:
      case Qt::Key_8:
      case Qt::Key_9:
        {
          char tmp[] = "\x1bOp";
          tmp[2] = 'p' + key - Qt::Key_0;
          session_->writeStr(tmp);
        }
        return;
      case Qt::Key_Plus:
        session_->writeStr("\x1bOl");
        return;
      case Qt::Key_Minus:
        session_->writeStr("\x1bOm");
        return;
      case Qt::Key_Period:
        session_->writeStr("\x1bOn");
        return;
      case Qt::Key_Asterisk:
        session_->writeStr("\x1bOM");
        return;
    }
  }

  switch (key) {
    case Qt::Key_Home:
      session_->writeStr(directions_[0]);
      return;
    case Qt::Key_End:
      session_->writeStr(directions_[1]);
      return;
    case Qt::Key_PageUp:
      session_->writeStr(directions_[2]);
      return;
    case Qt::Key_PageDown:
      session_->writeStr(directions_[3]);
      return;
    case Qt::Key_Up:
      session_->writeStr(directions_[4]);
      return;
    case Qt::Key_Down:
      session_->writeStr(directions_[5]);
      return;
    case Qt::Key_Left:
      session_->writeStr(directions_[6]);
      return;
    case Qt::Key_Right:
      session_->writeStr(directions_[7]);
      return;
    case Qt::Key_F1:
      session_->writeStr("\x1b[11~");
      return;
    case Qt::Key_F2:
      session_->writeStr("\x1b[12~");
      return;
    case Qt::Key_F3:
      session_->writeStr("\x1b[13~");
      return;
    case Qt::Key_F4:
      session_->writeStr("\x1b[14~");
      return;
    case Qt::Key_F5:
      session_->writeStr("\x1b[15~");
      return;
    case Qt::Key_F6:
      session_->writeStr("\x1b[17~");
      return;
    case Qt::Key_F7:
      session_->writeStr("\x1b[18~");
      return;
    case Qt::Key_F8:
      session_->writeStr("\x1b[19~");
      return;
    case Qt::Key_F9:
      session_->writeStr("\x1b[20~");
      return;
    case Qt::Key_F10:
      session_->writeStr("\x1b[21~");
      return;
    case Qt::Key_F11:
      session_->writeStr("\x1b[23~");
      return;
    case Qt::Key_F12:
      session_->writeStr("\x1b[24~");
      return;
  }
  
  if (text.length() > 0) {
    if (alt_on) {
      // Use ESC to emulate Alt-XX key.
      session_->writeStr("\x1b");
    }

    if (ctrl_on) {
      switch(key) {
        case Qt::Key_At:     // Ctrl-@
        case Qt::Key_Space:  // Ctrl-SPACE
          // send NULL
          session_->write("\x0", 1);
          return;
        case Qt::Key_AsciiCircum:  // Ctrl-^
        case Qt::Key_AsciiTilde:   // Ctrl-~
        case Qt::Key_QuoteLeft:    // Ctrl-`
          // send RS
          session_->write("\x1e", 1);
          return;
        case Qt::Key_Underscore: // Ctrl-_
        case Qt::Key_Question:   // Ctrl-?
          // send US
          session_->write("\x1f", 1);            
          return;
      }
    }
    
    if (key == Qt::Key_Backspace) {
      if (shift_on) {
        session_->writeStr("\x1b[3~");
      } else {
        if (session_->param().backspaceType_ ==  0)
          session_->writeStr("\x08");
        else
          session_->writeStr("\x7f");
      }
      return;
    }
    
    if (key == Qt::Key_Delete) {
      if (shift_on) {
        if (session_->param().backspaceType_ ==  0)
          session_->writeStr("\x08");
        else
          session_->writeStr("\x7f");
      } else {
        session_->writeStr("\x1b[3~");
      }
      return;
    }

    if (key == Qt::Key_Return) {
      if (session_->getBuffer()->isNewLineMode()) {
        session_->write("\r\n", 2);
      } else {
        session_->write("\r", 1);
      }
      return;
    }
    
    QByteArray cstrTmp = session_->unicode2bbs_smart(text);
    session_->write(cstrTmp, cstrTmp.length());
  }
  return;
}

void FQTermWindow::forcedRepaintScreen() {
  QResizeEvent *re = new QResizeEvent(screen_->size(), screen_->size());
  QApplication::postEvent(screen_, re);
}

void FQTermWindow::updateSetting(const FQTermParam& param) {
  session_->updateSetting(param);
  screen_->setTermFont(true,
                       QFont(param.englishFontName_,
                             param.englishFontSize_));
  screen_->setTermFont(false,
                       QFont(param.otherFontName_,
                             param.otherFontSize_));
  screen_->setSchema();

  setWindowTitle(param.name_);

  forcedRepaintScreen();
}

void FQTermWindow::setFont()
{
  bool isEnglish =
       ((QAction*)(sender()))->data().toBool();
  setFont(isEnglish);
}

void FQTermWindow::setFont(bool isEnglish) {
  bool ok;
  QString& fontName = isEnglish?session_->param().englishFontName_:session_->param().otherFontName_;
  int& fontSize = isEnglish?session_->param().englishFontSize_:session_->param().otherFontSize_;
  QFont font(fontName, fontSize);
  font = QFontDialog::getFont(&ok, font, this, tr("Font Selector")
#ifdef __APPLE__
, QFontDialog::DontUseNativeDialog
#endif
  );
  if (ok == true) {
    if (FQTermPref::getInstance()->openAntiAlias_) {
      font.setStyleStrategy(QFont::PreferAntialias);
    }
    screen_->setTermFont(isEnglish, font);
    fontName = font.family();
    fontSize = font.pointSize();
    forcedRepaintScreen();
  }
}


void FQTermWindow::runScript(const QString &filename) {
  script_engine_->runScript(filename);
}

//-----------
static int translateQtModifiers(Qt::KeyboardModifiers modifiers) {
  int state = 0;
  if (modifiers &Qt::AltModifier) {
    state |= SBS_ALT;
  }
  if (modifiers &Qt::ControlModifier) {
    state |= SBS_CTRL;
  }
  if (modifiers &Qt::ShiftModifier) {
    state |= SBS_SHIFT;
  }
  return state;
}

static int translateQtButton(Qt::MouseButtons btnstate) {
  int state = 0;
  if(btnstate&Qt::LeftButton)
    state |= SBS_LEFT_BUTTON;
  if(btnstate&Qt::RightButton)
    state |= SBS_RIGHT_BUTTON;
  if(btnstate&Qt::MidButton)
    state |= SBS_MID_BUTTON;
  return state;
}

bool FQTermWindow::scriptKeyEvent( QKeyEvent *keyevent ){
  bool res = false;
  int state = 0;
  state |= translateQtModifiers(keyevent->modifiers());

  res = postScriptCallback(SFN_KEY_EVENT
#ifdef HAVE_PYTHON
                           ,Py_BuildValue("liii", this, SKET_KEY_PRESS, state, keyevent->key())
#endif //HAVE_PYTHON
                           ,QScriptValueList() << SKET_KEY_PRESS << state << keyevent->key());
  return res;
}

bool FQTermWindow::scriptMouseEvent(QMouseEvent *mouseevent){
  int type = SMET_UNKOWN;
  switch (mouseevent->type())
  {
  case QEvent::MouseButtonPress:
    type = SMET_MOUSE_PRESS;
    break;
  case QEvent::MouseButtonRelease:
    type = SMET_MOUSE_RELEASE;
    break;
  case QEvent::MouseButtonDblClick:
    type = SMET_MOUSE_DBCLICK;
    break; 
  case QEvent::MouseMove:
    type = SMET_MOUSE_MOVE;
    break;
  }

  int delta = 0;
  int state=0;

  state |= translateQtButton(mouseevent->button());
  state |= translateQtModifiers(mouseevent->modifiers());

  QPoint ptc = screen_->mapToChar(mouseevent->pos());
  ptc.setY(ptc.y() - screen_->getBufferStart());
  int res = postScriptCallback(SFN_MOUSE_EVENT
#ifdef HAVE_PYTHON
                               ,Py_BuildValue("liiiii", this, type, state, ptc.x(), ptc.y(),delta)
#endif //HAVE_PYTHON
                               ,QScriptValueList() << type << state << ptc.x() << ptc.y() << delta);
  return res;
}

bool FQTermWindow::scriptWheelEvent( QWheelEvent *wheelevent ) {
  int type = SMET_WHEEL;
  int delta = 0;
  int state=0;
  state |= translateQtModifiers(wheelevent->modifiers());
  QPoint ptc = screen_->mapToChar(wheelevent->pos());
  ptc.setY(ptc.y() - screen_->getBufferStart());
  int res = postScriptCallback(SFN_MOUSE_EVENT
#ifdef HAVE_PYTHON
                               ,Py_BuildValue("liiiii", this, type, state, ptc.x(), ptc.y(),wheelevent->delta())
#endif //HAVE_PYTHON
                               ,QScriptValueList() << type << state << ptc.x() << ptc.y() << wheelevent->delta());
  return res;
}

const QString FQTermExternalEditor::textEditName_ =  "external editor input";

FQTermExternalEditor::FQTermExternalEditor(QWidget* parent) 
: QObject(parent),
  editorProcess_(NULL),
  started_(false) {
  editorProcess_ = new QProcess(this);
  FQ_VERIFY(connect(editorProcess_, SIGNAL(stateChanged(QProcess::ProcessState)), 
                    this, SLOT(stateChanged(QProcess::ProcessState))));
  clearTempFileContent();
}  

FQTermExternalEditor::~FQTermExternalEditor() {
  editorProcess_->kill();
  QFile::remove(getTempFilename());
  delete editorProcess_;
}

void FQTermExternalEditor::execDialog() {
  QDialog *dialog = new QDialog((QWidget *)parent());
  QGridLayout *layout = new QGridLayout(dialog);
  QTextEdit *text = new QTextEdit(dialog);
  text->setObjectName(textEditName_);
  layout->addWidget(text);
  QBoxLayout  *rowLayout = new QBoxLayout(QBoxLayout::LeftToRight, dialog);
  layout->addLayout(rowLayout, 1, 0, Qt::AlignHCenter);
  QPushButton *ok = new QPushButton(tr("OK"), dialog);
  QPushButton *cancel = new QPushButton(tr("Cancel"), dialog);
  rowLayout->addWidget(ok);
  rowLayout->addWidget(cancel);
  FQ_VERIFY(connect(ok, SIGNAL(clicked()),
    dialog, SLOT(accept())));
  FQ_VERIFY(connect(cancel, SIGNAL(clicked()),
    dialog, SLOT(reject())));
  FQ_VERIFY(connect(dialog, SIGNAL(accepted()),
    this, SLOT(readDialogData())));
  FQ_VERIFY(connect(dialog, SIGNAL(rejected()),
    this, SLOT(closeDialog())));
  dialog->setModal(true);
  dialog->exec();
}

void FQTermExternalEditor::readDialogData() {
  QDialog *dialog = (QDialog*)sender();
  QTextEdit* edit = (QTextEdit*)dialog->findChild<QTextEdit*>(textEditName_);
  if (edit) {
    emit done(edit->toPlainText());
  }
  closeDialog();
}

void FQTermExternalEditor::closeDialog() {
  sender()->deleteLater();
}

void FQTermExternalEditor::start() {
  if (FQTermPref::getInstance()->externalEditor_.isEmpty()) {
    execDialog();
    return;
  }
  if (started_)
    return;
  started_ = true;
  if (FQTermPref::getInstance()->externalEditorArg_.isEmpty()) {
    editorProcess_->start(FQTermPref::getInstance()->externalEditor_, QStringList() << getTempFilename());
  } else {
    FQ_TRACE("editor", 5) << FQTermPref::getInstance()->externalEditorArg_.arg(getTempFilename());
    editorProcess_->start(FQTermPref::getInstance()->externalEditor_ + " " + FQTermPref::getInstance()->externalEditorArg_.arg(getTempFilename()));
  }
}


QString FQTermExternalEditor::getTempFilename() {
    #ifndef WIN32
    return "/tmp/.fqterm_tmp.txt";
    #else
    return getPath(USER_CONFIG) + "tmp_do_not_use.txt";
    #endif
}

void FQTermExternalEditor::clearTempFileContent() {
  QFile file(getTempFilename());
  file.open(QIODevice::Truncate | QIODevice::WriteOnly);
  file.write("\xEF\xBB\xBF");
  file.flush();
  file.close();
}

void FQTermExternalEditor::stateChanged( QProcess::ProcessState state ) {
  //FQ_TRACE("editor", 0) << "stateChanged: " << state << editorProcess_->readAllStandardOutput() << editorProcess_->readAllStandardError();
  if (!started_ || state != QProcess::NotRunning)
    return;
  QFile file(getTempFilename());
  file.open(QIODevice::ReadOnly);
  QString result = U82U(file.readAll());
  file.close();
#if !defined(WIN32) && !defined(__APPLE__) 
  if (result.endsWith(OS_NEW_LINE)) {
    result.resize(result.size() - 1);
  }
#endif
  emit done(result);
  clearTempFileContent();
  started_ = false;
}
} // namespace FQTerm
#include "fqterm_window.moc"
