/***************************************************************************
                          dcchat.cpp  -  description
                             -------------------
    begin                : Don Mär 28 2002
    copyright            : (C) 2002 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dcchat.h"

#include <QDateTime>
#include <QLineEdit>
#include <QPushButton>
#include <QClipboard>
#include <QApplication>
#include <QMessageBox>
#include <QCursor>
#include <QLayout>
#include <QProcess>
#include <QStatusBar>
#include <QFile>
#include <QFileDialog>
#include <QSplitter>
#include <QTabWidget>
#include <QLabel>
#include <QPixmap>
#include <QGridLayout>
#include <QKeyEvent>
#include <QTextDocumentFragment>
#include <QMdiArea>
#include <QUrl>
#include <QPoint>
#include <QScrollBar>

//#include <QtDebug>

#include "dcevent.h"
#include "dcclient.h"
#include "dcmenuhandler.h"
#include "dcconfig.h"
#include "dctranslator.h"
#include "cdialogpicturemap.h"
#include "dcfiletool.h"
#include "dchublistmanager.h"
#include "dcconnectionmanager.h"
#include "dcuserslist.h"
#include "dciconloader.h"
#include "dctransferview.h"
#include "dchubsearch.h"
#include "dcshellcommandrunner.h"
#include "dcguiutils.h"
#include "dcqtextedit.h"
#include "dcgui.h"
#include "dcantispam.h"

#include "ui/DCDialogMagnet.h"

#include <dclib/cfilemanager.h>
#include <dclib/dcos.h>
#include <dclib/cutils.h>
#include <dclib/core/cbase64.h>
#include <dclib/core/cdir.h>
#include <dclib/dclib.h>
#include <dclib/clistenmanager.h>
#include <dclib/core/cbytearray.h>

/* to enabled the /pretend command */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define MAX_HISTORY_COUNT 25

DCChat *DCChat::pActiveChat = NULL;

/** */
DCChat::DCChat(QWidget * parent, DCClient * client, bool bprivate) : QWidget(parent) {
    // ASSERT(client);

    setupUi(this);

    // set default icon
    setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiGLOBE));

    m_pParent = parent;
    m_bPrivateChat = bprivate;
    m_pClient = client;
    m_bSendAway = true;
    m_eSecureState = esecsNONE;
    m_nCurrentHistoryIndex = -1;

    m_sTimeStamp = QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss");

    m_nTabStart = -1;
    m_nTabEnd = -1;
    m_nTabPressed = 0;
    m_sTabSaved = "";
    m_sTabNick = "";

    m_pShellRunners = new QList<DCShellCommandRunner*>();

    bChatOff = false;
    bRawChat = false;
    bAutoScroll = true;

    InitDocument();
}

/** */
DCChat::~DCChat() {
    /*
     * Call delete on all running shell commands.
     * The DCShellCommand destructor will wait
     * for the thread to finish.
     */
    for (int i = 0; i < m_pShellRunners->count(); i++) {
        delete m_pShellRunners->at(i);
    }

    m_pShellRunners->clear();
    delete m_pShellRunners;
}

/** */
void DCChat::QT3TranslationSync() {
    QString qt31 = tr("Shell command event not found in list");
    QString qt32 = tr("Failed to start shell command.");
}

/** */
void DCChat::InitDocument() {
    //int i;

    m_pStatusBar = new QStatusBar(this);
    m_pStatusBar->setSizeGripEnabled(false);
    layout()->addWidget(m_pStatusBar);
    m_pStatusCrypt = new QLabel(m_pStatusBar);
    m_pStatusBar->addWidget(m_pStatusCrypt);

    SetCrypt(esecsNONE);

    if (!m_bPrivateChat) {
        m_pStatusBar->hide();
    } else {
        m_pStatusBar->show();
    }

    /*	if ( DCConfig::Instance()->ShowChatStatusBar() == false )
            {
                    m_pStatusBar->hide();
            }
     */
    if (DCConfig::Instance()->GetShowChatSendButton()) {
        connect(PushButton_SEND, SIGNAL(clicked()), this, SLOT(slotSendChat()));
        slotTextChangedChatInput();
    } else {
        // remove send button and QT will notice
        delete PushButton_SEND;
        PushButton_SEND = NULL;
    }

    m_pTextEdit_CHATOUTPUT = new DCQTextEdit(Frame_OUTPUT);

    // QSizePolicy::Preferred is the default anyway...
    QSizePolicy policy = m_pTextEdit_CHATOUTPUT->sizePolicy();
    policy.setHorizontalPolicy(QSizePolicy::Preferred);
    policy.setVerticalPolicy(QSizePolicy::Preferred);
    policy.setHorizontalStretch(0);
    policy.setVerticalStretch(0);
    m_pTextEdit_CHATOUTPUT->setSizePolicy(policy);

    m_pTextEdit_CHATOUTPUT->setMinimumSize(QSize(0, 60));
    //m_pTextEdit_CHATOUTPUT->setTextFormat( Qt::RichText );
    m_pTextEdit_CHATOUTPUT->setLineWrapMode(QTextEdit::WidgetWidth);
    m_pTextEdit_CHATOUTPUT->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    m_pTextEdit_CHATOUTPUT->setReadOnly(true);
    m_pTextEdit_CHATOUTPUT->setContextMenuPolicy(Qt::CustomContextMenu);

    if (DCConfig::Instance()->GetChatMaxParagraph() != 0) {
        m_pTextEdit_CHATOUTPUT->document()->setMaximumBlockCount(DCConfig::Instance()->GetChatMaxParagraph());
    }

    slotLoadEmoticons();

    Frame_OUTPUT->layout()->addWidget(m_pTextEdit_CHATOUTPUT);

    connect(m_pTextEdit_CHATOUTPUT, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slotRightButtonClickedChatOutput(const QPoint&)));

    connect(m_pTextEdit_CHATOUTPUT, SIGNAL(clicked()), this, SLOT(slotClickedChatOutput()));
    connect(m_pTextEdit_CHATOUTPUT, SIGNAL(doubleClicked()), this, SLOT(slotDoubleClickedChatOutput()));

    connect(DCConfig::Instance(), SIGNAL(emoticonThemeChanged()), this, SLOT(slotLoadEmoticons()));
    connect(DCConfig::Instance(), SIGNAL(chatBackgroundColorChanged()), this, SLOT(slotBGColorChanged()));
    connect(DCConfig::Instance(), SIGNAL(chatForegroundColorChanged()), this, SLOT(slotFGColorChanged()));
    connect(m_pTextEdit_CHATOUTPUT->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotScrollChat(int)));

    if (DCConfig::Instance()->GetChatBackgroundColorEnabled()) {
        /* using QTextCharFormat or style sheets did not work */
        QPalette palette = m_pTextEdit_CHATOUTPUT->palette();
        palette.setColor(QPalette::Base, DCConfig::Instance()->GetChatBackgroundColor());
        m_pTextEdit_CHATOUTPUT->setAutoFillBackground(true);
        m_pTextEdit_CHATOUTPUT->setPalette(palette);

        palette = TextEdit_CHATINPUT->palette();
        palette.setColor(QPalette::Base, DCConfig::Instance()->GetChatBackgroundColor());
        TextEdit_CHATINPUT->setAutoFillBackground(true);
        TextEdit_CHATINPUT->setPalette(palette);
    }

    /* Set foreground color for chat input */
    QPalette ChatInPalette = TextEdit_CHATINPUT->palette();
    ChatInPalette.setColor(QPalette::Text, DCConfig::Instance()->GetChatForegroundColor());
    TextEdit_CHATINPUT->setPalette(ChatInPalette);

    if (PushButton_SEND) {
        connect(TextEdit_CHATINPUT, SIGNAL(textChanged()), this, SLOT(slotTextChangedChatInput()));
    }

    Frame_SEND->setFixedHeight(35 + 2 + 2);

    m_pTextEdit_CHATOUTPUT->installEventFilter(this);
    TextEdit_CHATINPUT->installEventFilter(this);
    TextEdit_CHATINPUT->setFixedHeight(35);
    TextEdit_CHATINPUT->setFocus();

    pushButton_SMYLE->setIcon(DCIconLoader::Instance()->GetPixmap(eiEMOTICON));

    connect(pushButton_SMYLE, SIGNAL(clicked()), this, SLOT(slotSmyle()));
}

/**
 * FIXME Old icons are not unloaded, using clear() or even setPlainText("")
 * seems to remove the icons, so now setHtml("<html></html>") is used to
 * preserve them when clearing the chat.
 */
void DCChat::slotLoadEmoticons() {
    if (DCConfig::Instance()->GetEnableEmoticons()) {
        DCConfig::Instance()->AddEmoticons(m_pTextEdit_CHATOUTPUT->document());
    }
}

/** event filter */
bool DCChat::eventFilter(QObject * object, QEvent * event) {
    if ((event->type() == QEvent::KeyPress) && ((QTextEdit*) object == TextEdit_CHATINPUT)) {
        QKeyEvent * e = (QKeyEvent*) event;

        bool send = false;
        switch (DCConfig::Instance()->GetSendChat()) {
            case eControlEnter:
                if ((e->modifiers() == Qt::ControlModifier) && ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Return))) {
                    send = true;
                }
                break;
            case eEnter:
                if ((e->modifiers() != Qt::ControlModifier) && ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Return))) {
                    send = true;
                }
                break;
            case eAltEnter:
                if ((e->modifiers() == Qt::AltModifier) && ((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Return))) {
                    send = true;
                }
                break;
            case eAltS:
                if ((e->modifiers() == Qt::AltModifier) && (e->key() == Qt::Key_S)) {
                    send = true;
                }
                break;
            default:
                break;
        }

        if (send) {
            SendChat();
            e->accept();
            return true;
        } else if (e->modifiers() == Qt::ControlModifier) {
            int histindex = -1;

            // check for history
            if ((e->key() == Qt::Key_Down) && (m_nCurrentHistoryIndex != -1)) {
                histindex = m_nCurrentHistoryIndex - 1;

            } else if (e->key() == Qt::Key_Up) {
                if (m_nCurrentHistoryIndex == -1) {
                    histindex = 0;

                    // save current text
                    m_sHistoryTempString = TextEdit_CHATINPUT->toPlainText();
                    // save current cursor position
                    // QT3 TextEdit_CHATINPUT->getCursorPosition( &m_nHistoryTempPara, &m_nHistoryTempIndex );
                    m_nHistoryTempIndex = TextEdit_CHATINPUT->textCursor().position();
                } else {
                    histindex = m_nCurrentHistoryIndex + 1;
                    if (histindex >= m_lHistory.size()) {
                        histindex = -1;
                    }
                }
            }

            if ((histindex >= 0) && (histindex < m_lHistory.size())) {
                m_nCurrentHistoryIndex = histindex;
                TextEdit_CHATINPUT->setPlainText(m_lHistory[histindex]);
            } else if (e->key() == Qt::Key_Down) {
                m_nCurrentHistoryIndex = -1;
                TextEdit_CHATINPUT->setPlainText(m_sHistoryTempString);
                // QT3 TextEdit_CHATINPUT->setCursorPosition( m_nHistoryTempPara, m_nHistoryTempIndex );
                QTextCursor cursor = TextEdit_CHATINPUT->textCursor();
                cursor.setPosition(m_nHistoryTempIndex);
                TextEdit_CHATINPUT->setTextCursor(cursor);
            }
        }

        if (e->key() == Qt::Key_Tab) {
            NickCompletion();
            e->accept();
            return true;
        } else if (m_nTabPressed != 0) {
            // reset tab settings
            m_nTabStart = -1;
            m_nTabEnd = -1;
            m_nTabPressed = 0;
            m_sTabSaved = "";
            //m_sTabNick    = "";
        }
    } else if ((event->type() == QEvent::Resize) && (object == m_pTextEdit_CHATOUTPUT)) {
        bool bscroll, b;

        if (m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maximum() == m_pTextEdit_CHATOUTPUT->verticalScrollBar()->value()) {
            bscroll = true;
        } else {
            bscroll = false;
        }

        b = QWidget::eventFilter(object, event);

        if (bscroll) {
            // QT3 m_pTextEdit_CHATOUTPUT->scrollToBottom();
            // QT3 m_pTextEdit_CHATOUTPUT->moveCursor( Q3TextEdit::MoveEnd, false );
            m_pTextEdit_CHATOUTPUT->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
        }

        return b;
    }

    return QWidget::eventFilter(object, event); // standard event processing
}



/** handle translation event */
void DCChat::customEvent(QEvent * event) {
    if (event->type() == EVENT_TRANSLATION) {
        DC_TranslationEvent *e = (DC_TranslationEvent*) event;

        if (e->m_bTranslate) {
            AddStatus(tr("Translation: ") + "'" + e->m_sOriginal + "' -> '" + e->m_sTranslation + "'");
        } else {
            AddStatus(tr("Translation failed: ") + "'" + e->m_sOriginal + "'" + " ( " + tr("Error was: ") + "'" + e->m_sTranslation + "' )");
        }

        event->accept();
    } else {
        event->ignore();
    }
}

void DCChat::showEvent(QShowEvent *e) {
    e->accept();

    if (isVisible()) {
        DCChat::SetActiveChat(this);
        DCGuiApp::Instance()->SetChatOff(bChatOff);
    }
}

/** */
void DCChat::AddHistory(QString message) {
    if (m_lHistory.size() == MAX_HISTORY_COUNT) {
        m_lHistory.pop_back();
    }

    m_nCurrentHistoryIndex = -1;
    m_lHistory.prepend(message);
}

/** */
void DCChat::SetCrypt(eSecureState e) {
    if (e == esecsENCRYPTED) {
        m_pStatusCrypt->setToolTip(tr("Line is encrypted."));
        m_pStatusCrypt->setPixmap(DCIconLoader::Instance()->GetPixmap(eiSSL_YES));
    } else {
        m_pStatusCrypt->setToolTip(tr("Line is not encrypted."));
        m_pStatusCrypt->setPixmap(DCIconLoader::Instance()->GetPixmap(eiSSL_NO));
    }

    m_eSecureState = e;
}

/** */
void DCChat::slotTextChangedChatInput() {
    if (PushButton_SEND) {
        if (TextEdit_CHATINPUT->toPlainText().isEmpty()) {
            PushButton_SEND->setEnabled(false);
        } else {
            PushButton_SEND->setEnabled(true);
        }
    }
}

/** */
void DCChat::slotClickedChatOutput() {
    if (m_pTextEdit_CHATOUTPUT->getMBNick() != "") {

        QString selected = m_pTextEdit_CHATOUTPUT->getMBNick();
        CString cPressedNick;
        bool isNickOnline;

        cPressedNick = selected.toAscii().constData();
        isNickOnline = m_pClient->UserList()->IsUserOnline(cPressedNick);

        if (!isNickOnline)
            return;

        eChatDoubleClickAction chat_action;

        if (DCConfig::Instance()->GetChatDoubleClickAction() == ecdcaNONE || DCConfig::Instance()->GetChatDoubleClickAction() == ecdcaASK) {
            QMenu p_menu(this);

            QAction *browse = DCMenuHandler::addAction(&p_menu, emiBROWSE_USER_FILES, isNickOnline);
            QAction to_chat(tr("To chat"), this);
            QAction *to_priv = DCMenuHandler::addAction(&p_menu, emiPRIVATE_CHAT, isNickOnline);

            p_menu.addAction(browse);
            p_menu.addAction(&to_chat);
            p_menu.addAction(to_priv);

            QAction *choosen = p_menu.exec(QCursor::pos());

            if (choosen == browse)
                chat_action = ecdcaBROWSE;
            else if (choosen == &to_chat)
                chat_action = ecdcaCHAT;
            else if (choosen == to_priv)
                chat_action = ecdcaPRIVATE;
            else
                return;

        }

        if (DCConfig::Instance()->GetChatDoubleClickAction() == ecdcaNONE) {
            QMessageBox msg_box;
            msg_box.setText(tr("Save changes"));
            msg_box.setInformativeText(tr("Do you want to save your choise?"));
            msg_box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            msg_box.setDefaultButton(QMessageBox::Yes);

            if (QMessageBox::Yes == msg_box.exec())
                DCConfig::Instance()->SetChatDoubleClickAction(chat_action);
            else
                DCConfig::Instance()->SetChatDoubleClickAction(ecdcaASK);
        }

        switch (chat_action) {
            case ecdcaBROWSE:
                DCTransferView::Instance()->DLM_QueueAdd(cPressedNick, m_pClient->GetHubName(),
                        m_pClient->GetHost(),
                        DC_USER_FILELIST, DC_USER_FILELIST, CString(), CString(), eltBUFFER,
                        0, 0, 0, CString());

                break;
            case ecdcaCHAT:
                selected += ": ";

                if (TextEdit_CHATINPUT->toPlainText().isEmpty()) {
                    TextEdit_CHATINPUT->insertPlainText(selected);
                } else {
                    if (TextEdit_CHATINPUT->toPlainText().right(1) != " ")
                        selected.prepend(" ");

                    // not using TextEdit_CHATINPUT->append(), that would add a new line
                    TextEdit_CHATINPUT->insertPlainText(selected);
                }

                TextEdit_CHATINPUT->setFocus();

                break;
            case ecdcaPRIVATE:
                m_pClient->DC_PrivateChat(selected, QString(), QString(), true);

                break;

            default:
                break;
        }

        return;

    }

    QPoint p = m_pTextEdit_CHATOUTPUT->mapFromGlobal(QCursor::pos());
    QString pressedLink = m_pTextEdit_CHATOUTPUT->anchorAt(p);

    //printf("pressedLink=\"%s\"\n",pressedLink.Data());

    if (pressedLink.isEmpty()) /* no link at clicked pos */
        return;

    if (pressedLink.startsWith("dcfile://", Qt::CaseInsensitive)) {
        // DC-File-Link
        CString hubhost, hubname, nick, file, tth;
        ulonglong size;

        if (!CUtils::ConvertDCLink(pressedLink.toAscii().constData(), hubhost, hubname, nick, size, file, tth))
            return;

        QMenu m(this);

        QAction * dl = DCMenuHandler::addAction(&m, emiDOWNLOAD);
        QAction * dl_to = DCMenuHandler::addAction(&m, emiDOWNLOAD_TO);
        QAction * dl_as = DCMenuHandler::addAction(&m, emiDOWNLOAD_AS);
        QAction * dl_in = DCMenuHandler::addAction(&m, emiDOWNLOAD_IN);

        QAction * chosen = m.exec(QCursor::pos());

        if ((chosen == dl) || (chosen == dl_as) || (chosen == dl_to)) {
            QString localrootpath;
            QString localname;

            // select downloadfolder for all selected files
            if (chosen == dl_to) {
                localrootpath = QFileDialog::getExistingDirectory(this, tr("Select download folder"), DCConfig::Instance()->GetDownloadFolder().Data(), QFileDialog::ShowDirsOnly);

                if (localrootpath.isEmpty())
                    return;
            }

            CDir d;
            d.SetPath(file);
            QFileInfo fi(d.DirName().Data());
            localname = fi.fileName();

            if (chosen == dl_as) {
                localrootpath = QFileDialog::getSaveFileName(this, tr("Select file for") + " " + localname, localname);

                if (localrootpath.isEmpty())
                    return;

                QFileInfo fi(localrootpath);
                localrootpath = fi.path();
                localname = fi.fileName();

                if ((localrootpath.isEmpty()) || (localname.isEmpty()))
                    return;
            }

            // add transfer to the waitlist
            DCFileTool::CheckFile(this, nick, hubname, hubhost,
                    file, localname.toAscii().constData(), CString(), localrootpath.toAscii().constData(), eltFILE,
                    size, tth);
        } else if (chosen == dl_in) {
            QString localrootpath;
            QString localname;
            QString localpath;

            if (!DCFileTool::SelectFileSource(this, size, QString(tth.Data()), localname, localrootpath, localpath))
                return;

            // add transfer to the waitlist
            DCFileTool::CheckFile(this, nick, hubname, hubhost,
                    file, localname.toAscii().constData(), localpath.toAscii().constData(), localrootpath.toAscii().constData(), eltFILE,
                    size, tth, true);
        }
    } else if (pressedLink.startsWith("dchub://", Qt::CaseInsensitive) || pressedLink.startsWith("nmdc://", Qt::CaseInsensitive)) {
        if (pressedLink.startsWith("dchub://", Qt::CaseInsensitive))
            pressedLink = pressedLink.right(pressedLink.length() - 8);
        else
            pressedLink = pressedLink.right(pressedLink.length() - 7);

        // remove all '/' chars
        pressedLink.remove('/');

        DCConnectionManager::Instance()->Connect(pressedLink.toAscii().constData(), pressedLink.toAscii().constData());
    } else if (pressedLink.startsWith("dchubs://", Qt::CaseInsensitive) || pressedLink.startsWith("nmdcs://", Qt::CaseInsensitive)) {
        if (pressedLink.startsWith("dchubs://", Qt::CaseInsensitive))
            pressedLink = pressedLink.right(pressedLink.length() - 9);
        else
            pressedLink = pressedLink.right(pressedLink.length() - 8);

        // remove all '/' chars
        pressedLink.remove('/');

        DCConnectionManager::Instance()->Connect(pressedLink.toAscii().constData(), pressedLink.toAscii().constData(), true);
    } else if (pressedLink.startsWith("magnet:", Qt::CaseInsensitive)) {
        //magnet:?xt=urn:tree:tiger:EOSA5AGTL5SD3VWCF3R2OH2WMGXV3S3R7SYN4YA&xl=708780032&dn=FC-6-i386-disc1.iso

        QString key = QString::fromAscii("xt=urn:tree:tiger:");
        int i = pressedLink.indexOf(key);
        if (i == -1) {
            key = QString::fromAscii("xt.1=urn:tree:tiger:");
            i = pressedLink.indexOf(key);
        }

        int end = pressedLink.indexOf("&", i + 1);

        QString tth;
        if (i != -1) {
            i += key.length();
            if (end == -1)
                tth = pressedLink.mid(i);
            else
                tth = pressedLink.mid(i, end - i);
        }

        switch (DCConfig::Instance()->GetMagnetAction(eChatMagnet)) {
            case eMagnetPrompt:
            {
                QDialog dialog(this);
                Ui::DCDialogMagnet ui;
                ui.setupUi(&dialog);

                ui.LineEdit_LINK->setText(pressedLink);
                ui.LineEdit_TTH->setText(tth);

                key = QString::fromAscii("dn=");
                i = pressedLink.indexOf(key);
                if (i != -1) {
                    end = pressedLink.indexOf("&", i + 1);
                    i += key.length();
                    QString dispname;
                    if (end == -1)
                        dispname = pressedLink.mid(i);
                    else
                        dispname = pressedLink.mid(i, end - i);

                    dispname.replace("+", "%20");
                    dispname = QUrl::fromPercentEncoding(dispname.toUtf8());

                    ui.LineEdit_NAME->setText(dispname);
                }

                key = QString::fromAscii("xl=");
                i = pressedLink.indexOf(key);
                if (i != -1) {
                    end = pressedLink.indexOf("&", i + 1);
                    i += key.length();
                    ulonglong size;
                    if (end == -1)
                        size = pressedLink.mid(i).toULongLong();
                    else
                        size = pressedLink.mid(i, end - i).toULongLong();

                    ui.LineEdit_SIZE->setText(DCGuiUtils::GetSizeString(size));
                    ui.LineEdit_EXACT_SIZE->setText(QString::number(size));
                }

                if (dialog.exec() == QDialog::Accepted) {
                    if (ui.CheckBox_SET_DEFAULT->isChecked()) {
                        if (ui.RadioButton_SEARCH->isChecked())
                            DCConfig::Instance()->SetMagnetAction(eMagnetSearch, eChatMagnet);
                        else if (ui.RadioButton_DO_NOTHING->isChecked())
                            DCConfig::Instance()->SetMagnetAction(eMagnetNothing, eChatMagnet);
                    }

                    if (ui.RadioButton_SEARCH->isChecked()) {
                        DCHubSearch * hubsearch = new DCHubSearch(DCConnectionManager::Instance()->GetMdiArea());
                        hubsearch->SetSearchForFile(tth, eftHASH);
                        hubsearch->show();

                        connect(hubsearch, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));
                        DCConnectionManager::Instance()->addTab(hubsearch, tr("Hub search"));

                        hubsearch->StartSearchWithPrompt();
                    }
                }

                break;
            }
            case eMagnetSearch:
            {
                DCHubSearch * hubsearch = new DCHubSearch(DCConnectionManager::Instance()->GetMdiArea());
                hubsearch->SetSearchForFile(tth, eftHASH);
                hubsearch->show();

                connect(hubsearch, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));
                DCConnectionManager::Instance()->addTab(hubsearch, tr("Hub search"));

                hubsearch->StartSearchWithPrompt();
                break;
            }
            case eMagnetDownload:
                /* not supported */
                break;
            case eMagnetNothing:
                break;
            default:
                break;
        }
    } else
        DCConfig::Instance()->OpenURL(pressedLink);
}

/** */
void DCChat::slotRightButtonClickedChatOutput(const QPoint&) {
    //int para;
    QString pressedPara, pressedNick, userCommand;
    CString cPressedNick;
    QPoint p;
    //int x,y
    int nickStart, nickLen;
    QAction * chosen;
    bool isNickOnline;
    QMenu *m, *muser, *mslot;
    QString s;
    QMap<QAction*, DC_UserMenuCommand*> addedcommands;
    DC_UserMenuCommand * umc = 0;

    // get the paragraph of text where the right click was
    // Qt3 m_pTextEdit_CHATOUTPUT->viewportToContents(p.x(),p.y(),x,y);
    // QT3 p.setX(x);
    // QT3 p.setY(y);
    // QT3 para = m_pTextEdit_CHATOUTPUT->paragraphAt(p);
    // QT3 pressedPara = m_pTextEdit_CHATOUTPUT->text(para);
    p = m_pTextEdit_CHATOUTPUT->mapFromGlobal(QCursor::pos());
    QTextCursor cursor = m_pTextEdit_CHATOUTPUT->cursorForPosition(p);
    pressedPara = cursor.block().text();

    //printf("pressedPara=\"%s\"\n", pressedPara.toAscii().constData());

    // cut out the nick
    nickStart = 1 + pressedPara.indexOf("<");
    nickLen = pressedPara.indexOf(">") - nickStart;

    // sanity check
    if ((nickStart == 0) || (nickLen < 0)) {
        if (m_bPrivateChat) {
            pressedNick = m_sNick;
        } else {
            // assume a /me line
            if (DCConfig::Instance()->GetTimeStamp(etsHUBCHAT)) {
                nickStart = QString("[hh.mm.ss] ").length();
            } else {
                nickStart = 0;
            }

            nickLen = pressedPara.indexOf(" ", nickStart) - nickStart;

            pressedNick = pressedPara.mid(nickStart, nickLen);

            /* [10:57:15] * somenick does something */
            if (pressedNick == "*") {
                nickStart = pressedPara.indexOf(" ", nickStart + 1) + 1;
                if (nickStart > 0) {
                    nickLen = pressedPara.indexOf(" ", nickStart) - nickStart;
                    pressedNick = pressedPara.mid(nickStart, nickLen);
                }
            }
        }
    } else {
        pressedNick = pressedPara.mid(nickStart, nickLen);
    }

    // check for joins / parts message
    if (pressedNick == "VALKNUT") {
        int lenJoin = tr("Joins: ").length();
        int lenPart = tr("Parts: ").length();
        int joinPos = pressedPara.indexOf(tr("Joins: "));
        int partPos = pressedPara.indexOf(tr("Parts: "));

        if (joinPos == -1) {
            nickStart = lenPart + partPos;
        } else {
            nickStart = lenJoin + joinPos;
        }

        if ((joinPos != -1) || (partPos != -1)) {
            nickLen = pressedPara.length() - nickStart;
            pressedNick = pressedPara.mid(nickStart, nickLen);
        }
    }

    // check for private chat
    if ((pressedNick == "VALKNUT") && m_bPrivateChat) {
        pressedNick = m_sNick;
    }

    //printf("Debug pressedNick: %s\n",pressedNick.toAscii().constData());

    cPressedNick = pressedNick.toAscii().constData();
    isNickOnline = m_pClient->UserList()->IsUserOnline(cPressedNick);

    // select the user in the userlist
    if (isNickOnline) {
        m_pClient->jumpToNick(pressedNick);
    }

    m = new QMenu(this);

    muser = DCMenuHandler::addMenu(m, emisCHAT_RIGHTCLICK_USER, true, pressedNick);
    QAction * privchat = DCMenuHandler::addAction(muser, emiPRIVATE_CHAT, isNickOnline);
    QAction * addfriend = DCMenuHandler::addAction(muser, emiADD_FRIEND, isNickOnline);
    QAction * browseuser = DCMenuHandler::addAction(muser, emiBROWSE_USER_FILES, isNickOnline);
    DCMenuHandler::addAction(muser, emiSEPARATOR);

    mslot = DCMenuHandler::addMenu(muser, emiUPLOAD_SLOT, true);
    QAction * addslot = DCMenuHandler::addAction(mslot, emiADD, true);
    QAction * addpermslot = DCMenuHandler::addAction(mslot, emiADD_PERMANENT, true);
    QAction * removeslot = DCMenuHandler::addAction(mslot, emiREMOVE, true);

    DCMenuHandler::addAction(muser, emiSEPARATOR);
    QAction * kick = 0;
    QAction * forcemove = 0;

    if (m_pClient->UserList()->IsAdmin(m_pClient->GetNick())) {
        kick = DCMenuHandler::addAction(muser, emiKICK, isNickOnline);
        forcemove = DCMenuHandler::addAction(muser, emiFORCE_MOVE, isNickOnline);
    }

    addedcommands = m_pClient->AddMenuCommands(muser, euccChat);

    DCMenuHandler::addAction(m, emiSEPARATOR);

    QAction * smiley = DCMenuHandler::addAction(m, emiINSERTSMILEY, DCConfig::Instance()->GetEnableEmoticons());
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * copy = DCMenuHandler::addAction(m, emiCOPY, m_pTextEdit_CHATOUTPUT->textCursor().hasSelection());
    QAction * clear = DCMenuHandler::addAction(m, emiCLEAR, true);
    QAction * selectall = DCMenuHandler::addAction(m, emiSELECT_ALL, true);
    DCMenuHandler::addAction(m, emiSEPARATOR);

    QAction * zoomin = DCMenuHandler::addAction(m, emiZOOM_IN);
    QAction * zoomout = DCMenuHandler::addAction(m, emiZOOM_OUT);

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * translate = DCMenuHandler::addAction(m, emiTRANSLATE, m_pTextEdit_CHATOUTPUT->textCursor().hasSelection());
    QAction * translator = DCMenuHandler::addAction(m, emiTRANSLATOR);

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * refresh = DCMenuHandler::addAction(m, emiREFRESH);
    DCMenuHandler::addAction(m, emiSEPARATOR);

    QAction * reqsecchat = 0;
    QAction * closesecchat = 0;

    if (m_bPrivateChat) {
        // insert request/close secure chat
        if (m_eSecureState == esecsNONE)
            reqsecchat = DCMenuHandler::addAction(m, emiREQUEST_SECURE_CHAT, dclibSupportsSSL());
        else
            closesecchat = DCMenuHandler::addAction(m, emiCLOSE_SECURE_CHAT);
    }

    QAction * save = DCMenuHandler::addAction(m, emiSAVE);

    QAction * dock = 0;

    if (parentWidget() == 0)
        dock = DCMenuHandler::addAction(m, emiDOCK);
    else
        dock = DCMenuHandler::addAction(m, emiUNDOCK);

    QAction * hidepriv = 0;
    QAction * closepriv = 0;

    if (m_bPrivateChat) {
        if (!DCConfig::Instance()->GetShowChatInTab()) {
            hidepriv = DCMenuHandler::addAction(m, emiHIDE);
        }

        closepriv = DCMenuHandler::addAction(m, emiCLOSE);
    } else {
        addedcommands.unite(m_pClient->AddMenuCommands(m, euccHub));
    }

    QAction *chat_off = 0;
    if (bChatOff) {
        chat_off = new QAction(tr("Enable chat"), m);
        chat_off->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiWINDOWS_LISTS)));
    } else {
        chat_off = new QAction(tr("Disable chat"), m);
        chat_off->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiWINDOWS_CL_LISTS)));
    }

    m->addAction(chat_off);

    chosen = m->exec(QCursor::pos());

    delete m;

    if (chosen == 0) {
        return;
    } else if (chosen == smiley) {
        slotSmyle();
    } else if (chosen == translate) {
        QString sc;
        QClipboard * cb = QApplication::clipboard();

        // save old text
        sc = cb->text();
        // copy to cb
        m_pTextEdit_CHATOUTPUT->copy();
        // get new text
        s = cb->text();
        // save old text
        cb->setText(sc);

        s.remove("<!--StartFragment-->");
        DCTranslator::Instance()->Translate(this, m_sLanguage, s);
    } else if (chosen == translator) {
        m_sLanguage = DCTranslator::Instance()->SelectLanguage(m_sLanguage, this);
    } else if (chosen == copy) {
        m_pTextEdit_CHATOUTPUT->copy();
    } else if (chosen == clear) {
        ClearChat();
    } else if (chosen == selectall) {
        m_pTextEdit_CHATOUTPUT->selectAll();
    } else if (chosen == reqsecchat) {
        m_pClient->SendPrivateMessage(m_pClient->GetNick(), m_sNick.toAscii().constData(), "<request secchannel>");
    } else if (chosen == closesecchat) {
        m_pClient->SendPrivateMessage(m_pClient->GetNick(), m_sNick.toAscii().constData(), "<close secchannel>");
    } else if (chosen == dock) {
        if (parentWidget() == 0) {
            setParent(m_pParent);

            if (!m_bPrivateChat || DCConfig::Instance()->GetShowChatInTab()) {
                QTabWidget * p = (QTabWidget*) m_pParent;

                int index = p->addTab(this, m_sLabel);
                p->setCurrentIndex(index);
            } else {
                DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(this);
            }

            show();
        } else {
            if (!m_bPrivateChat || DCConfig::Instance()->GetShowChatInTab()) {
                QTabWidget * p = (QTabWidget*) m_pParent;

                int index = p->indexOf(this);
                m_sLabel = p->tabText(index);
                p->removeTab(index);
            } else {
                /* doesn't seem to work */
                /* DCConnectionManager::Instance()->GetMdiArea()->removeSubWindow(this); */

#if QT_VERSION >= 0x040400
                QList<QMdiSubWindow*> subwinlist = DCConnectionManager::Instance()->GetMdiArea()->subWindowList(QMdiArea::ActivationHistoryOrder);
#else
                QList<QMdiSubWindow*> subwinlist = DCConnectionManager::Instance()->GetMdiArea()->subWindowList(QMdiArea::StackingOrder);
#endif

                for (QList<QMdiSubWindow*>::const_iterator subwin_it = subwinlist.constBegin(); subwin_it != subwinlist.constEnd(); ++subwin_it) {
                    if ((*subwin_it)->widget() == this) {
                        (*subwin_it)->setWidget(0);
                        delete *subwin_it;
                        break;
                    }
                }
            }

            setParent(0);
            show();

            // set default icon
            setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiGLOBE));
        }
    } else if (chosen == save) {
        QString s = QFileDialog::getSaveFileName(
                this,
                tr("Choose a filename to save under"),
                QString(),
                "HTML (*.html *.htm)");

        if (!s.isEmpty()) {
            QFile f(s);

            if (f.open(QIODevice::WriteOnly)) {
                f.write(m_pTextEdit_CHATOUTPUT->toHtml().toAscii());
                f.close();
            }
        }
    } else if (chosen == refresh) {
        if (CFileManager::Instance()->CreateShareList())
            s = tr("Refresh share in progress.");
        else
            s = tr("Refresh share already in progress.");
        AddStatus(s);
    } else if (chosen == closepriv) {
        close();
    } else if (chosen == hidepriv) {
        setEnabled(false);
        hide();

        /* there must be a better way... */
#if QT_VERSION >= 0x040400
        QList<QMdiSubWindow*> subwinlist = DCConnectionManager::Instance()->GetMdiArea()->subWindowList(QMdiArea::ActivationHistoryOrder);
#else
        QList<QMdiSubWindow*> subwinlist = DCConnectionManager::Instance()->GetMdiArea()->subWindowList(QMdiArea::StackingOrder);
#endif

        for (QList<QMdiSubWindow*>::const_iterator subwin_it = subwinlist.constBegin(); subwin_it != subwinlist.constEnd(); ++subwin_it) {
            if ((*subwin_it)->widget() == this) {
                (*subwin_it)->setWidget(0);
                delete *subwin_it;
                break;
            }
        }
    } else if (chosen == privchat) {
        m_pClient->DC_PrivateChat(pressedNick, QString(), QString(), true);
    } else if (chosen == addfriend) {
        DCUsersList::Instance()->AddFriend(
                pressedNick,
                QString::fromAscii(m_pClient->GetHubName().Data()),
                QString::fromAscii(m_pClient->GetHost().Data()),
                QString()
                );
    } else if (chosen == browseuser) {
        CString empty;
        DCTransferView::Instance()->DLM_QueueAdd(cPressedNick, m_pClient->GetHubName(),
                m_pClient->GetHost(),
                DC_USER_FILELIST, DC_USER_FILELIST, empty, empty, eltBUFFER,
                0, 0, 0, empty);
    } else if (chosen == kick) {
        QString kickMessage;

        if (!m_pClient->GetOPKickMessage(kickMessage, this)) {
            return;
        }
        m_pClient->OPKick(pressedNick, kickMessage);

    } else if (chosen == forcemove) {
        QString host, message;
        if (!m_pClient->GetOPForceMoveMessage(message, host, this)) {
            return;
        }
        m_pClient->OPForceMove(pressedNick, message, host);
    } else if (chosen == addslot) {
        DCTransferView::Instance()->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 1);
    } else if (chosen == addpermslot) {
        DCTransferView::Instance()->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 0, true);
    } else if (chosen == removeslot) {
        DCTransferView::Instance()->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 0);
    } else if (chosen == zoomin) {
        ZoomIn();
    } else if (chosen == zoomout) {
        ZoomOut();
    } else if (chosen == chat_off) {
        SetChatOff();
    } else if (addedcommands.contains(chosen)) {
        umc = addedcommands[chosen];

        QString origcommand = umc->m_sCommand;
        userCommand = m_pClient->replaceCommandTags(origcommand, pressedNick);

        if (!userCommand.isEmpty()) {
            AddStatus(userCommand);
            m_pClient->SendString(userCommand.toAscii().constData());
        }
    }
}

/** */
bool DCChat::close() {
    // re-set the wflags ...
    //QWidget::setWindowFlags(WDestructiveClose);
    QWidget::setAttribute(Qt::WA_DeleteOnClose);
    if (isVisible())
        DCChat::SetActiveChat(NULL);
    return QWidget::close();
}

/** */
void DCChat::slotSendChat() {
    SendChat();
}

/** */
void DCChat::SetNick(QString nick, QString hubname) {
    m_sNick = nick;

    if (nick.isEmpty()) {
        setWindowTitle(tr("Chat:") + QString(" [") + hubname + QString("]"));
    } else {
        setWindowTitle(tr("Private Chat:") + QString(" ") + nick + QString(" [") + hubname + QString("]"));
    }
}

/** */
QString DCChat::GetTimeStamp() {
    QString ts;

    if (m_bPrivateChat) {
        if (DCConfig::Instance()->GetTimeStamp(etsPRIVATECHAT)) {
            ts = "<font color=\"";
            ts += DCConfig::Instance()->GetChatColor(eccPUBLICCHATTIMESTAMP);
            ts += "\">";
            ts += QTime::currentTime().toString("[hh:mm:ss]");
            ts += "</font> ";
        }
    } else if (DCConfig::Instance()->GetTimeStamp(etsHUBCHAT)) {
        ts = "<font color=\"";
        ts += DCConfig::Instance()->GetChatColor(eccCHATTIMESTAMP);
        ts += "\">";
        ts += QTime::currentTime().toString("[hh:mm:ss]");
        ts += "</font> ";
    }

    return ts;
}

/** */
void DCChat::AddOutput(QString message) {
    if (bRawChat) {
        m_pTextEdit_CHATOUTPUT->insertPlainText(message);
        if (bAutoScroll)
            m_pTextEdit_CHATOUTPUT->verticalScrollBar()->setValue(m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maximum());

        return;
    }

    // convert newline
    message.replace("\n", "<br />");

    // convert tabs to spaces
    message.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;");

    // QT3 - replaced with QTextDocument::setMaximumBlockCount in QT4
    /* if ( (DCConfig::Instance()->GetChatMaxParagraph()!= 0) && (m_pTextEdit_CHATOUTPUT->paragraphs() > (DCConfig::Instance()->GetChatMaxParagraph()+5)) )
    {
            m_pTextEdit_CHATOUTPUT->removeParagraph(0);
            m_pTextEdit_CHATOUTPUT->removeParagraph(0);
            m_pTextEdit_CHATOUTPUT->removeParagraph(0);
            m_pTextEdit_CHATOUTPUT->removeParagraph(0);
            m_pTextEdit_CHATOUTPUT->removeParagraph(0);
    } */

    m_pTextEdit_CHATOUTPUT->append(message);

    if (bAutoScroll)
        m_pTextEdit_CHATOUTPUT->verticalScrollBar()->setValue(m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maximum());

    message += "<br />";

    // save to logfile
    if (DCConfig::Instance()->GetLogChatOption(elcoENABLELOGGING)) {
        if (!(DCConfig::Instance()->GetLogChatOption(elcoDISABLEPUBLICCHAT) && !m_bPrivateChat)) {
            if (DCConfig::Instance()->CheckLogChatNickNameFilter(m_sNick)) {
                QString s = m_sNick;

                if (DCConfig::Instance()->GetLogChatOption(elcoAPPENDHUBNAME)) {
                    s += "_";
                    s += QString::fromAscii(m_pClient->GetHubName().Data());
                }

                if (DCConfig::Instance()->GetLogChatOption(elcoAPPENDHUBHOST)) {
                    s += "_";
                    s += QString::fromAscii(m_pClient->GetIP().Data());
                }

                if (DCConfig::Instance()->GetLogChatOption(elcoAPPENDDATE)) {
                    s += "_";
                    s += m_sTimeStamp;
                }

                // damn hacking ;-)
                s.remove('/');
                s.remove('\\');
                s.remove(':');

                s = QString::fromAscii(DCConfig::Instance()->GetChatLogPath().Data()) + s + ".html";

                QFile f(s);

                if (f.open(QIODevice::WriteOnly | QIODevice::Append)) {
                    // add newline
                    message += "\n";

                    f.write(message.toAscii());
                    f.close();
                }
            }
        }
    }
}

/** */
void DCChat::AddStatus(QString message, bool show) {
    if (DCConfig::Instance()->GetShowStatusMessage() || show) {
        // convert special chars ... damn hacking ;-)
        message.replace("<", "&lt;");
        message.replace(">", "&gt;");

        QString html = GetTimeStamp();

        if (m_bPrivateChat) {
            html += "<font color=\"";
            html += DCConfig::Instance()->GetChatColor(eccCHATSTATUSNICK);
            html += "\"><b>&lt;DC-CORE&gt;</b> </font><font color=\"";
            html += DCConfig::Instance()->GetChatColor(eccCHATSTATUSTEXT);
            html += "\">";
            html += message;
            html += "</font>";
        } else {
            html += "<font color=\"";
            html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATSTATUSNICK);
            html += "\"><b>&lt;DC-CORE&gt;</b> </font><font color=\"";
            html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATSTATUSTEXT);
            html += "\">";
            html += message;
            html += "</font>";
        }

        AddOutput(html);
    }
}

/** */
void DCChat::AddMessage(CMessagePrivateChat * msg, bool bremote, bool forward) {
    if (msg->m_eSecureState != m_eSecureState) {
        SetCrypt(msg->m_eSecureState);
    }

    if (msg->m_sSrcNick.IsEmpty())
        AddMessage(QString::fromAscii(msg->m_sSrcNick.Data()), QString::fromAscii(msg->m_sMessage.Data()), bremote, forward);
    else
        AddMessage(QString::fromAscii(msg->m_sMultiSrcNick.Data()), QString::fromAscii(msg->m_sMessage.Data()), bremote, forward);
}

/** */
void DCChat::AddMessage(QString nick, QString message, bool bremote, bool forward) {
    QString s1, html;
    bool bSay = false;
    bool bMe = false;
    bool isOP = m_pClient->UserList()->IsAdmin(nick.toAscii().constData());
    QString clientnick = QString::fromAscii(m_pClient->GetNick().Data());

    if (DCConfig::Instance()->GetAntiSpamEnabled()){
        if (!DCAntiSpam::Instance()){
            DCAntiSpam::SetInstance(new DCAntiSpam());

            DCAntiSpam::Instance()->loadSettings();
            DCAntiSpam::Instance()->loadLists();
        }

        if (DCAntiSpam::Instance()->isInBlack(nick))
            return;
    }

    //printf("ADD MESSAGE '%s'\n",message.Data());

    if (message.isEmpty()) {
        return;
    }

    if (CheckForData(message)) {
        return;
    }

    if (bChatOff) {
        return;
    }


    // check for nick - but avoid string searching if not enabled
    if (!m_bPrivateChat && DCConfig::Instance()->GetSoundEnabled(eusNICKMENTIONED)) {
        if (message.indexOf(clientnick) != -1) {
            DCConfig::Instance()->PlaySound(eusNICKMENTIONED);

            QString hub_name = QString::fromAscii(m_pClient->GetHubName().Data());

            if (hub_name.length() > 18)
                hub_name = hub_name.left(15) + "...";

            DCGuiApp::Instance()->showNotification(hub_name +
                    tr(": somebody wishes to talk with you."),
                    nick + ": " + message,
                    eTrayMessageTypeNICK);
        }
    }

    if (bRawChat) {
        QString out = GetTimeStamp();
        out += " <" + nick + ">: ";
        out += message + "\n";

        AddOutput(out);

        return;
    }

    // convert special chars ... damn hacking ;-)
    message.replace("<", "&lt;");
    message.replace(">", "&gt;");

    // convert special chars in nick ... damn hacking ;-)
    nick.replace("<", "&lt;");
    nick.replace(">", "&gt;");

    s1 = clientnick;
    s1 += ": ";

    if ((message.left(4).toUpper() == "/ME ") || (message.left(4).toUpper() == "+ME ")) {
        bMe = true;
        message = message.right(message.length() - 4);
    }

    if (message.startsWith(s1)) {
        bSay = true;
        // message = message.right( message.length() - s1.length() );
    }

    /* convert links and maybe emoticons */
    message = DCConfig::Instance()->ChatToHTML(message);

    html = GetTimeStamp();

    if (!nick.isEmpty()) {
        html += "<font color=\"";

        if (m_bPrivateChat) {
            if (bMe) {
                html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATMENICK);
            } else if (nick != clientnick) {
                if (isOP) {
                    html += DCConfig::Instance()->GetChatColor(eccOPNICK);
                } else {
                    html += DCConfig::Instance()->GetChatColor(eccCHATREMOTENICK);
                }
            } else {
                html += DCConfig::Instance()->GetChatColor(eccCHATLOCALNICK);
            }

            html += "\"><b>";
            html += nick;
            if (bMe) {
                html += "</b> </font>";
            } else {
                html += "</b>: </font>";
            }
        } else {
            if (forward) {
                html += DCConfig::Instance()->GetChatColor(eccPUBLICPRIVATECHATNICK);
                if (bMe) {
                    html += "\"><b>";
                    html += nick;
                    html += "</b> </font>";
                } else {
                    html += "\"><b>&lt;";
                    html += nick;
                    html += "&gt;</b> </font>";
                }
            } else if (bMe) {
                html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATMENICK);
                html += "\"><b>";
                html += nick;
                html += "</b> </font>";
            } else if (nick != clientnick) {
                if (isOP) {
                    html += DCConfig::Instance()->GetChatColor(eccOPNICK);
                } else {
                    html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATREMOTENICK);
                }

                html += "\"><b>&lt;";
                html += nick;
                html += "&gt;</b> </font>";
            } else {
                html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATLOCALNICK);
                html += "\"><b>&lt;";
                html += nick;
                html += "&gt;</b> </font>";
            }
        }
    }

    html += "<font color=\"";

    if (m_bPrivateChat) {
        if (bremote && (nick != clientnick)) {
            if (bSay) {
                html += DCConfig::Instance()->GetChatColor(eccCHATSAY);
            } else {
                html += DCConfig::Instance()->GetChatColor(eccCHATREMOTETEXT);
            }
        } else {
            html += DCConfig::Instance()->GetChatColor(eccCHATLOCALTEXT);
        }
    } else {
        if (forward) {
            html += DCConfig::Instance()->GetChatColor(eccPUBLICPRIVATECHATTEXT);
        } else if (bremote && (nick != clientnick)) {
            if (bSay) {
                html += DCConfig::Instance()->GetChatColor(eccCHATSAY);
            } else {
                html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATREMOTETEXT);
            }
        } else {
            html += DCConfig::Instance()->GetChatColor(eccPUBLICCHATLOCALTEXT);
        }
    }

    html += "\">";
    html += message;
    html += "</font>";

    AddOutput(html);

    if (bremote && m_bPrivateChat) {
        DCConfig::Instance()->PlaySound(eusRECEIVE);

        // send away message
        if (DCConfig::Instance()->GetAwayMode() == euamAWAY) {
            CString awaymsg = DCConfig::Instance()->GetAwayMessage();

            if (!(awaymsg.IsEmpty()) && (m_bSendAway)) {
                m_pClient->SendPrivateMessage(m_pClient->GetNick(), m_sNick.toAscii().constData(), awaymsg);
                m_bSendAway = false;
                AddStatus(tr("Sent away message: ") + QString::fromAscii(awaymsg.Data()));
            }
        } else {
            m_bSendAway = true;
        }
    }
}

/** */
void DCChat::SendChat(QString message) {
    int err;
    QString s;
    bool local = false;
    bool cmd = false;

    if (!m_pClient) {
        return;
    }

    if (message.isEmpty()) {
        message = TextEdit_CHATINPUT->toPlainText();
    } else {
        local = true;
    }

    if (!message.isEmpty()) {
        if (local == false) {
            // add history
            AddHistory(message);
            m_sHistoryTempString = QString();
            if (message.at(0) == '/') {
                cmd = CheckForCommand();
            }
        }

        if (cmd == false) {
            // convert the newlines
            // any windows to unix then all unix to windows
            // so windows do not become double newlines
            message.replace("\r\n", "\n");
            message.replace("\n", "\r\n");

            if (m_bPrivateChat)
                err = m_pClient->SendPrivateMessage(m_pClient->GetNick(), m_sNick.toAscii().constData(), message.toAscii().constData());
            else
                err = m_pClient->SendChat(m_pClient->GetNick(), message.toAscii().constData());

            if (err == 0) {
                if (m_bPrivateChat)
                    AddMessage(QString::fromAscii(m_pClient->GetNick().Data()), message, false);
                DCConfig::Instance()->PlaySound(eusSEND);
            } else {
                AddStatus(tr("Message not sent!"));
            }
        }

        if (local == false) {
            TextEdit_CHATINPUT->clear();
        }
    }
}

/** */
bool DCChat::CheckForCommand() {
    bool res = false;
    QString s;

    QString cmd = DCConfig::Instance()->ReplaceUserChatCommands(TextEdit_CHATINPUT->toPlainText());

    if (cmd.isEmpty()) {
        return res;
    }

    if (cmd.at(0) == '/') {
        /*
         * Extract command and remaining arguments.
         * More complicated commands need to
         * extract their arguments themselves but
         * most commands just take a nick.
         *
         * Commands whose first argument is
         * not a nick should not use theRest
         * because it gets set to the nick
         * if private chat.
         */

        int firstspace = cmd.indexOf(' ');

        QString cmdOnly;
        QString theRest;

        if (firstspace == -1) {
            cmdOnly = cmd;
            if (m_bPrivateChat) {
                theRest = m_sNick;
            }
        } else {
            cmdOnly = cmd.left(firstspace);
            theRest = cmd.mid(firstspace + 1);
        }

        // AddStatus( "cmdOnly ='" + cmdOnly + "'", true );
        // AddStatus( "theRest = '" + theRest + "'", true );

        // help
        if (cmdOnly == "/dchelp") {
            s = tr("Help:");
            s += "\n";
            s += tr("/clear - clears the chat window");
            s += "\n";
            s += tr("/mode - shows the current mode");
            s += "\n";
            s += tr("/refresh - refresh share");
            s += "\n";
            s += tr("/rebuild - remove no longer present files from hash database");
            s += "\n";
            s += tr("/validate - check hash database for errors");
            s += "\n";
            s += tr("/slots &lt;N&gt; - set number of upload slots to N");
            s += "\n";
            s += tr("/ts - switch time display in chat on/off");
            s += "\n";
            s += tr("/adv - send an advertisment to the hub");
            s += "\n";
            s += tr("/join &lt;address&gt; - disconnect from currently connected hub and connect to another hub");
            s += "\n";
            s += tr("/msg &lt;nick&gt; &lt;message&gt; - send private message");
            s += "\n";
            s += tr("/away &lt;message&gt; - toggle away mode, optionally set message");
            s += "\n";
            s += tr("/back - disable away mode");
            s += "\n";
            s += tr("/awaymsg &lt;message&gt; - set automatic response for private messages; if the message is empty, disable it");
            s += "\n";
            s += tr("/bye &lt;message&gt; - disconnect from channel with an optional message");
            s += "\n";
            s += tr("/uptime [show] - show EiskaltDC uptime [to other users]");
            s += "\n";
            s += tr("/ls &lt;nick&gt; - get share list from user");
            s += "\n";
            s += tr("/grant &lt;nick&gt; - grant user a slot");
            s += "\n";
            s += tr("/grantp &lt;nick&gt; - grant user a permanent slot");
            s += "\n";
            s += tr("/friend &lt;nick&gt; - add user to friend list");
            s += "\n";
            s += tr("/fav - add hub to bookmark list");
            s += "\n";
            s += tr("/info &lt;nick&gt; - show user info");
            s += "\n";
            s += tr("/now - send current time to the chat");
            s += "\n";
            s += tr("/sh &lt;command&gt; &lt;args...&gt; - send output of shell command to chat");
            s += "\n";
            s += tr("/raw - send raw message");
            s += "\n";
            s += tr("/disablesorting - disable sorting of the user list");
            s += "\n";
            s += tr("/enablesorting - enable sorting of the user list");
            s += "\n";
            s += tr("/ignore &lt;nick&gt; - do not show chat messages from the user");
            s += "\n";
            s += tr("/unignore &lt;nick&gt; - show chat messages from the user again");
            s += "\n";
            s += tr("/ratio [show] - show share ratio [to other users]");
            s += "\n";
            s += tr("/newlog - start a new logfile");
            s += "\n";
            s += tr("/topic - shows the topic hub");
            s += "\n";
            AddStatus(s, true);
            res = true;
        }            // /clear - clear the chat window
        else if (cmdOnly == "/clear") {
            m_pTextEdit_CHATOUTPUT->setHtml("<html></html>");

            AddStatus(tr("Chat Cleared."), true);
            res = true;
        }            // /info - show user info
        else if (cmdOnly == "/topic"){
			QString s = "\n";
			s += tr("Hub Name:");
			s += ' ';
			s += QString::fromAscii(m_pClient->GetHubName().Data());
			s += "\n";
			s += tr("Hub Description:");
			s += ' ';
			s += QString::fromAscii(m_pClient->GetHubTopic().Data());
			AddStatus(s,true);
			res=true;
		}
        else if (cmdOnly == "/info") {
            CMessageMyInfo myinfo;

            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else if (m_pClient->UserList()->GetUserMyInfo(theRest.toAscii().constData(), &myinfo) == false) {
                s = tr("Get info failed.");
            } else {
                s = tr("Info:");
                s += "\n";
                s += tr("Nick:");
                s += ' ';
                s += QString::fromAscii(myinfo.m_sNick.Data());
                s += "\n";
                s += tr("Operator:");
                s += ' ';

                if (myinfo.m_bOperator) {
                    s += tr("yes");
                } else {
                    s += tr("no");
                }

                s += "\n";

                s += tr("Comment:");
                s += ' ';
                s += QString::fromAscii(myinfo.m_sComment.Data());
                s += "\n";
                s += tr("Speed:");
                s += ' ';
                s += QString::fromAscii(myinfo.m_sUserSpeed.Data());
                s += "\n";
                s += tr("EMail:");
                s += ' ';
                s += QString::fromAscii(myinfo.m_sEMail.Data());
                s += "\n";
                s += tr("Shared:");
                s += ' ';
                s += QString().setNum(myinfo.m_nShared);
                s += ' ';
                s += tr("Bytes");
                s += "\n";

                s += tr("Away:");
                s += ' ';

                switch (myinfo.m_eAwayMode) {
                    case euamAWAY:
                        s += tr("on");
                        break;
                    default:
                        s += tr("off");
                        break;
                }

                s += "\n";

                s += tr("Version:");
                s += ' ';

                switch (myinfo.m_eClientVersion) {
                    case eucvDCPP:
                        s += "DC++";
                        break;
                    case eucvDCGUI:
                        s += "EiskaltDC";
                        break;
                    case eucvMICRODC:
                        s += "microdc";
                        break;
                    case eucvSHAKESPEER:
                        s += "ShakesPeer";
                        break;
                    default:
                        s += tr("Unknown");
                        break;
                }

                s += "\n";

                s += tr("Tag:");
                s += ' ';
                s += QString::fromAscii(myinfo.m_sVerComment.Data());
                s += "\n";
                s += tr("Supports encryption:");
                s += ' ';
                if (myinfo.m_bTLSFlag) {
                    s += tr("yes");
                } else {
                    s += tr("no");
                }
                s += "\n";
            }

            AddStatus(s, true);
            res = true;
        }            // /uptime - show valknut uptime
        else if (cmdOnly == "/uptime") {
            int d, h, m;
            int i = time(0) - dclibUptime();

            d = i / (60 * 60 * 24);
            i = i % (60 * 60 * 24);
            h = i / (60 * 60);
            i = i % (60 * 60);
            m = i / 60;

            s = tr("EiskaltDC uptime");
            s += ": ";

            if (d > 0) {
                s += QString().setNum(d);
                s += ' ';
                if (d == 1)
                    s += tr("day");
                else
                    s += tr("days");
                s += ',';
            }
            if (h > 0) {
                s += QString().setNum(h);
                s += ' ';
                if (h == 1)
                    s += tr("hour");
                else
                    s += tr("hours");
                s += ',';
            }
            s += QString().setNum(m);
            s += ' ';
            if (m == 1)
                s += tr("minute");
            else
                s += tr("minutes");

            if (cmd.mid(8).toLower() == "show")
                SendChat(s);
            else
                AddStatus(s, true);

            res = true;
        }            // /mode - show current mode
        else if (cmdOnly == "/mode") {
            s = tr("Current mode: ");

            if (DCConfig::Instance()->GetMode() == ecmACTIVE)
                s += tr("active");
            else
                s += tr("passive");

            // get listen manager status
            if (CListenManager::Instance()) {
                CString lms = CListenManager::Instance()->GetSocketErrorMsg();

                if (lms.NotEmpty()) {
                    s += " (";
                    s += QString::fromAscii(lms.Data());
                    s += ')';
                }
            }

            s += '.';

            AddStatus(s, true);
            res = true;
        }            // /refresh - refresh share
        else if (cmdOnly == "/refresh") {
            if (CFileManager::Instance()->CreateShareList()) {
                s = tr("Refresh share in progress.");
            } else {
                s = tr("A share operation is already in progress.");
            }

            AddStatus(s, true);
            res = true;
        }            // /rebuild - clean up share databases
        else if (cmdOnly == "/rebuild") {
            if (CFileManager::Instance()->RebuildLists()) {
                s = tr("Rebuild share in progress.");
            } else {
                s = tr("A share operation is already in progress.");
            }

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/validate") {
            if (CFileManager::Instance()->ValidateLeaves()) {
                s = tr("Validate hash database in progress.");
            } else {
                s = tr("A share operation is already in progress.");
            }

            AddStatus(s, true);
            res = true;
        }            // /slots - set number of slots
        else if (cmdOnly == "/slots") {
            s = cmd.mid(7);
            if (s.isEmpty()) {
                AddStatus(tr("Upload slots: ") + QString().setNum(DCConfig::Instance()->GetMaxUpload()), true);
            } else {
                bool valid = false;
                int i = s.toInt(&valid);

                if (valid && (i >= 0) && (i <= 99)) {
                    DCConfig::Instance()->SetMaxUpload(i);
                    AddStatus(tr("Setting upload slots to") + " " + QString::number(i), true);
                } else {
                    AddStatus(tr("Invalid number of upload slots specified."), true);
                }
            }

            res = true;
        }            // /ts - switch time display in chat on/off
        else if (cmdOnly == "/ts") {
            bool b;

            if (m_bPrivateChat) {
                b = !DCConfig::Instance()->GetTimeStamp(etsPRIVATECHAT);
                DCConfig::Instance()->SetTimeStamp(etsPRIVATECHAT, b);
            } else {
                b = !DCConfig::Instance()->GetTimeStamp(etsHUBCHAT);
                DCConfig::Instance()->SetTimeStamp(etsHUBCHAT, b);
            }

            s = tr("Switch timestamp ");

            if (b)
                s += tr("on.");
            else
                s += tr("off.");

            AddStatus(s, true);
            res = true;
        }            // /adv - send an advertisment to the hub
        else if (cmdOnly == "/adv") {
            AddStatus(tr("Send advertisment."), true);

            s = tr("\nDownload EiskaltDC from http://eiskaltdc.sourceforge.net\nIt does everything !\nWorks on almost any platform you can think of and makes you coffee.\n");

            SendChat(s);

            res = true;
        }            // /now - send current time to the chat
        else if (cmdOnly == "/now") {
            s = QTime::currentTime().toString("hh:mm:ss");

            if (cmd.length() > 5) {
                s += " ";
                s += cmd.mid(5, cmd.length() - 5);
            }

            SendChat(s);

            res = true;
        }            // /raw - send raw message
        else if (cmdOnly == "/raw") {
            s = cmd.mid(5);

            if (!(s.isEmpty())) {
                AddStatus(cmd, true);
                m_pClient->SendString(s.toAscii().constData());
            } else {
                AddStatus(tr("Wrong parameter for '/raw'"), true);
            }

            res = true;
        } else if (cmdOnly == "/rawchat") {
            s = cmd.mid(9);

            if (s == "on") {
                AddStatus("Raw chat mode ON", true);
                bRawChat = true;
            } else if (s == "off") {
                AddStatus("Raw chat mode OFF", true);
                bRawChat = false;
            } else
                AddStatus(tr("Wrong parameter for '/rawchat'"), true);

            res = true;
        }
#ifdef ENABLE_PRETEND_TESTING_COMMAND
        else if (cmdOnly == "/pretend") {
            AddStatus(cmd, true);

            /* no attempt is made to make encoding correct */
            QByteArray ba = cmd.mid(9).toAscii();
            m_pClient->DataAvailable(ba.constData(), ba.size());

            res = true;
        }
#endif
            // /join <address> - disconnect from currently connected hub and connect to another hub
        else if (cmdOnly == "/join") {
            QString address = cmd.mid(6, cmd.length() - 6);

            if (address.isEmpty()) {
                s = tr("Wrong parameter for '/join'");
            } else {
                s = tr("Join to: ") + address;

                CString caddress = address.toAscii().constData();

                m_pClient->SetHubName(caddress);
                m_pClient->setWindowTitle(address);
                m_pClient->Connect(caddress);
            }

            AddStatus(s, true);
            res = true;
        }            // /ls - get share list from user
        else if (cmdOnly == "/ls") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                /** add transfer to the waitlist */
                CString empty;
                DCTransferView::Instance()->DLM_QueueAdd(theRest.toAscii().constData(), m_pClient->GetHubName(), m_pClient->GetHost(),
                        DC_USER_FILELIST, DC_USER_FILELIST, empty, empty, eltBUFFER,
                        0, 0, 0, empty);

                s = tr("Download sharelist from");
                s += " '";
                s += theRest;
                s += '\'';
            }

            AddStatus(s, true);
            res = true;
        }            // /grantp - grant perm. slot to user
        else if (cmdOnly == "/grantp") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                DCTransferView::Instance()->DLM_AddUserSlot(theRest.toAscii().constData(), m_pClient->GetHubName(), 0, true);

                s = tr("Permanent slot added for");
                s += " '";
                s += theRest;
                s += '\'';
            }

            AddStatus(s, true);
            res = true;
        }            // /grant - grant slot to user
        else if (cmdOnly == "/grant") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                DCTransferView::Instance()->DLM_AddUserSlot(theRest.toAscii().constData(), m_pClient->GetHubName(), 1);

                s = tr("Slot added for");
                s += " '";
                s += theRest;
                s += '\'';
            }

            AddStatus(s, true);
            res = true;
        }            // /friend - add user to friend list
        else if (cmdOnly == "/friend") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                DCUsersList::Instance()->AddFriend(theRest, QString::fromAscii(m_pClient->GetHubName().Data()), QString::fromAscii(m_pClient->GetHost().Data()), QString());

                s = tr("Added to friend list ");
                s += " '";
                s += theRest;
                s + '\'';
            }

            AddStatus(s, true);
            res = true;
        }            // /fav - add hub to bookmarks
        else if (cmdOnly == "/fav") {
            QString qhubname = QString::fromAscii(m_pClient->GetHubName().Data());
            DCHubListManager::Instance()->AddBookmark(qhubname, QString::fromAscii(m_pClient->GetHost().Data()), QString());

            s = tr("Add bookmark hub");
            s += " '";
            s += qhubname;
            s += '\'';

            AddStatus(s, true);
            res = true;
        }            // /bye <msg> - disconnect from channel
        else if (cmdOnly == "/bye") {
            s = cmd.mid(5);
            if (!(s.isEmpty())) {
                SendChat(s);
            }

            m_pClient->Disconnect(true);

            res = true;
        }            // /msg <nick> <message> - send private message
        else if (cmdOnly == "/msg") {
            QString nick, message;

            AddStatus(cmd, true);

            int i = theRest.indexOf(' ');
            if (i != -1) {
                nick = theRest.left(i);
                message = theRest.mid(i + 1);
            }

            if (nick.isEmpty() || message.isEmpty()) {
                s = tr("Wrong parameter for '/msg'");
            } else {
                if (m_pClient->SendPrivateMessage(m_pClient->GetNick(), nick.toAscii().constData(), message.toAscii().constData()) == 0) {
                    s = tr("Private message sent.");
                    DCConfig::Instance()->PlaySound(eusSEND);
                } else {
                    s = tr("Private message not sent!");
                }
            }

            AddStatus(s, true);
            res = true;

        }            // /away <message> - set automatic response for private messages; if the message is empty, disable it
        else if (cmdOnly == "/awaymsg") {
            s = cmd.mid(9);

            DCConfig::Instance()->SetAwayMessage(s.toAscii().constData());

            if (s.isEmpty())
                s = tr("Away message disabled.");
            else
                s = tr("Away message set.");

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/away") {
            if (DCConfig::Instance()->GetAwayMode() == euamAWAY) {
                DCConfig::Instance()->SetAwayMode(euamNORMAL);
                s = tr("Away mode off.");
            } else {
                s = cmd.mid(6);
                if (s.isEmpty()) {
                    s = QString::fromAscii(DCConfig::Instance()->GetAwayMessage().Data());
                } else {
                    DCConfig::Instance()->SetAwayMessage(s.toAscii().constData());
                }

                DCConfig::Instance()->SetAwayMode(euamAWAY);
                s.prepend(tr("Away mode on: "));
            }

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/back") {
            DCConfig::Instance()->SetAwayMode(euamNORMAL);
            s = tr("Away mode off.");

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/sh") {
            // display the command the user entered
            AddStatus(cmd, true);

            // actually this is better - more portable, doesn't depend on /bin/sh
            QString args = cmd.mid(4, cmd.length() - 4);

            if (args.isEmpty()) {
                AddStatus(tr("No command entered."), true);
                return true;
            }

            if (DCConfig::Instance()->GetThreadsForShellCommands()) {
                DCShellCommandRunner * runner = new DCShellCommandRunner(args, this);
                m_pShellRunners->append(runner);
                connect(runner, SIGNAL(finished(bool, QString)), this, SLOT(slotShellCommandFinished(bool, QString)));
                runner->start();
            } else {
                QProcess shellproc(this);
                shellproc.start(args);
                shellproc.closeWriteChannel();
                shellproc.waitForFinished(DCConfig::Instance()->GetShellCommandTimeout() * 1000);

                if ((shellproc.state() == QProcess::NotRunning)) {
                    if (shellproc.exitStatus() == QProcess::NormalExit) {
                        int exitcode = shellproc.exitCode();
                        if (exitcode == 0) {
                            s = QString(shellproc.readAllStandardOutput()).trimmed();
                            if (s.isEmpty()) {
                                AddStatus(tr("Command produced no visible output."), true);
                            } else {
                                SendChat(s);
                            }
                        } else {
                            AddStatus(tr("Process exited with status") + " " + QString::number(exitcode), true);
                        }
                    } else {
                        AddStatus(tr("Process was killed or crashed."), true);
                    }
                } else {
                    AddStatus(tr("Process still running after") + " " + QString().setNum(DCConfig::Instance()->GetShellCommandTimeout()) + " " + tr("seconds, killing process..."), true);
                    shellproc.terminate();
                    shellproc.waitForFinished(1000);
                    if (shellproc.state() != QProcess::NotRunning) {
                        shellproc.kill();
                    }
                }
            }

            res = true;
        } else if (cmdOnly == "/sendphoto") {
            if (!m_bPrivateChat) {
                s = tr("Only allowed in private chat.");
            } else if (m_pClient->UserList()->GetUserClientVersion(m_sNick.toAscii().constData()) != eucvDCGUI) {
                s = tr("The user is using an old version or not using EiskaltDC");
            } else {
                CString fn = DCConfig::Instance()->GetUserPhotoFileName().toAscii().constData();
                CByteArray bas, bad;

                if (CDir().IsFile(fn, false)) {
                    if ((bas.LoadFromFile(fn) == false) || (bas.Size() == 0)) {
                        s = tr("Can't read your photo.");
                    } else {
                        CBase64::Encode(&bad, &bas);

                        if (bad.Size() == 0) {
                            s = tr("Photo encode error.");
                        } else {
                            CString se;

                            se.Set((char*) bad.Data(), bad.Size());
                            se = "<photo data=\"" + se + "\">";

                            if (m_pClient->SendPrivateMessage(m_pClient->GetNick(), m_sNick.toAscii().constData(), se) != 0) {
                                s = tr("Photo not sent.");
                            } else {
                                s = tr("Photo sent.");
                            }
                        }
                    }
                } else {
                    s = tr("Photo not found.");
                }
            }

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/disablesorting") {
            m_pClient->disableUserListSorting();
            s = tr("Disabled user list sorting.");
            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/enablesorting") {
            m_pClient->enableUserListSorting();
            s = tr("Enabled user list sorting.");
            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/ignore") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                if (DCUsersList::Instance()->isNickInList(theRest) == false) {
                    DCUsersList::Instance()->AddFriend(theRest, QString::fromAscii(m_pClient->GetHubName().Data()), QString::fromAscii(m_pClient->GetHost().Data()), QString());
                }

                DCUsersList::Instance()->setIgnore(theRest, true);
                s = tr("Ignoring chat from ");
                s += theRest;
            }

            AddStatus(s, true);
            res = true;

        } else if (cmdOnly == "/unignore") {
            if (theRest.isEmpty()) {
                s = tr("No nick, try /dchelp");
            } else {
                if (DCUsersList::Instance()->ignoreNick(theRest)) {
                    DCUsersList::Instance()->setIgnore(theRest, false);
                    s = tr("Showing chat from ");
                    s += theRest;
                } else {
                    s = theRest;
                    s += tr(" is not in ignore list");
                }

            }

            AddStatus(s, true);
            res = true;
        } else if (cmdOnly == "/ratio") {
            ulonglong up = CSocket::m_Traffic.GetTraffic(ettTX);
            ulonglong down = CSocket::m_Traffic.GetTraffic(ettRX);

            double ratio = 0;

            if (down > 0) {
                ratio = (double) up / (double) down;
            }

            s = tr("ratio: overall: ");
            s += QString().setNum(ratio, 'f', 2);
            s += " (";
            s += tr("uploads");
            s += ": ";
            s += DCGuiUtils::GetSizeString(up);
            s += " | ";
            s += tr("downloads");
            s += ": ";
            s += DCGuiUtils::GetSizeString(down);
            s += ")\n";

            up -= DCConfig::Instance()->GetStartUploaded();
            down -= DCConfig::Instance()->GetStartDownloaded();

            if (down > 0) {
                ratio = (double) up / (double) down;
            } else {
                ratio = 0;
            }

            s += tr("this session: ");
            s += QString().setNum(ratio, 'f', 2);
            s += " (";
            s += tr("uploads");
            s += ": ";
            s += DCGuiUtils::GetSizeString(up);
            s += " | ";
            s += tr("downloads");
            s += ": ";
            s += DCGuiUtils::GetSizeString(down);
            s += ")";

            if (cmd.mid(7).toLower() == "show") {
                SendChat(s);
            } else {
                AddStatus(s, true);
            }
            res = true;
        } else if (cmdOnly == "/newlog") {
            m_sTimeStamp = QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss");

            s = tr("New log timestamp: ");
            s += m_sTimeStamp;

            if (DCConfig::Instance()->GetLogChatOption(elcoAPPENDDATE) == false) {
                s += "\n";
                s += tr("Warning: log timestamp not enabled.");
            }

            AddStatus(s, true);
            res = true;
        } else if (DCConfig::Instance()->GetSendUnknownCommandsAsChat() == false) {
            bool isAllowed = false;

            QString allowedstring = DCConfig::Instance()->GetAllowedUnknownCommands();

            if (!allowedstring.isEmpty()) {
                QStringList allowed = allowedstring.split(",", QString::SkipEmptyParts);

                for (int i = 0; i < allowed.count(); i++) {
                    // e.g. allow "/me" and "/me waves" but not "/mediqjdoh"
                    QString tail = cmd.mid(allowed[i].length(), 1);
                    if (cmd.startsWith(allowed[i]) &&
                            ((tail == " ") || (tail.isEmpty()))) {
                        isAllowed = true;
                        break;
                    }
                }
            }

            if (!isAllowed) {
                AddStatus(cmd);
                AddStatus(tr("Unknown command, try /dchelp"));
                res = true;
            }
        }
    } else {
        /* A custom chat command was replaced with a message, send it */
        AddStatus(TextEdit_CHATINPUT->toPlainText(), true);
        SendChat(cmd);
        res = true;
    }

    return res;
}

/** */
bool DCChat::CheckForData(QString message) {
    bool res = false;

    // now we check for special private messages
    if (m_bPrivateChat) {
        // remote send a photo
        if (message.startsWith("<photo data=\"") && message.endsWith("\">")) {
            CString s;
            CByteArray ba;

            //printf("CheckForData message='%s'\n",message.toAscii().constData());

            s = message.mid(13, message.length() - 15).toAscii().constData();

            //printf("CheckForData s='%s'\n",s.Data());

            if (CBase64::Decode(&ba, &s) > 0) {
                DCUsersList::Instance()->AddFriendPhoto(m_sNick, &ba);
                AddStatus(tr("Photo received."), true);

                res = true;
            }
        }
    }

    return res;
}

/** */
void DCChat::NickCompletion() {
    // shameles stolen from ksirc
    int start, end;
    int i;
    QString s;

    if (m_nTabPressed > 0) {
        s = m_sTabSaved;
        start = m_nTabStart;
        end = m_nTabEnd;
    } else {
        // QT3 TextEdit_CHATINPUT->getCursorPosition(&p,&i);
        i = TextEdit_CHATINPUT->textCursor().position();
        s = TextEdit_CHATINPUT->toPlainText();
        m_sTabSaved = s;
        if (i == 0)
            end = 0;
        else
            end = i - 1;
        start = s.lastIndexOf(QRegExp("\\s"), end);
        m_nTabStart = start;
        m_nTabEnd = end;
    }

    if (s.isEmpty() && (m_sTabNick.isEmpty() == false)) {
        // use the saved nick
        QString line = m_sTabNick + ": ";
        TextEdit_CHATINPUT->setPlainText(line);
        // QT3 TextEdit_CHATINPUT->setCursorPosition( 0,line.length() );
        QTextCursor cursor = TextEdit_CHATINPUT->textCursor();
        cursor.setPosition(line.length());
        TextEdit_CHATINPUT->setTextCursor(cursor);
    } else {
        int cpos = 1;

        if (start == -1) {
            m_sTabNick = m_pClient->findNick(s.mid(0, end + 1), m_nTabPressed);

            if (m_sTabNick.isNull()) {
                m_nTabPressed = 0;
                m_sTabNick = m_pClient->findNick(s.mid(0, end + 1), m_nTabPressed);
            }

            s.replace(0, end + 1, m_sTabNick + ": ");
            cpos += 2;
        } else {
            m_sTabNick = m_pClient->findNick(s.mid(start + 1, end - start), m_nTabPressed);

            if (m_sTabNick.isNull()) {
                m_nTabPressed = 0;
                m_sTabNick = m_pClient->findNick(s.mid(start + 1, end - start), m_nTabPressed);
            }

            s.replace(start + 1, end - start, m_sTabNick);
        }

        if (m_sTabNick.isEmpty() == false) {
            TextEdit_CHATINPUT->setPlainText(s);

            // QT3 TextEdit_CHATINPUT->setCursorPosition(0,start + m_sTabNick.length() + cpos);
            QTextCursor cursor = TextEdit_CHATINPUT->textCursor();
            cursor.setPosition(start + m_sTabNick.length() + cpos);
            TextEdit_CHATINPUT->setTextCursor(cursor);

            m_nTabPressed++;
        }
    }
}

/** insert double clicked word into chat input box */
void DCChat::slotDoubleClickedChatOutput() {
    // double clicking already selects the text
    QString selected = m_pTextEdit_CHATOUTPUT->textCursor().selectedText();
    CString cPressedNick;

    //printf("selected=%s\n", selected.toAscii().constData());

    // replace all emoticon special characters with the proper text
    // WARNING: the whole thing could break if QT changes its internal HTML
    // Lets hope that just the single emoticon is selected on double
    // clicking it, this appears to be the case, and the tooltip is the
    // actual text that was said, after the emoticon re-write it is the
    // same number for several variations of one emoticon, that show as
    // the same picture.
    if (selected == "\ufffc") {
        selected = m_pTextEdit_CHATOUTPUT->textCursor().charFormat().toolTip();
    } else if (selected.contains("\ufffc") && DCConfig::Instance()->GetEnableEmoticons()) {
        // get the html
        QString html = m_pTextEdit_CHATOUTPUT->textCursor().selection().toHtml();

        QStringList smileys;
        int start = 0;
        QString src = "src=\"" + DCConfig::Instance()->GetEmoticonTheme() + "/emoticon";
        const int len = src.length();

        while (start != -1) {
            // crap, the html doesn't include the title=":-)", only src="theme/emoticon5"
            //printf("html=%s\n", html.toAscii().constData());
            start = html.indexOf(src);
            if (start == -1) {
                break;
            }

            start = start + len;
            int end = html.indexOf("\"", start);
            if (end == -1) {
                break;
            }

            int id = html.mid(start, end - start).toInt();
            html = html.right(html.length() - (end + 1));
            //printf( "start=%d end=%d id=%d\n", start, end, id );

            QString smiley = DCConfig::Instance()->EmoticonTextForID(id);

            if (!smiley.isEmpty()) {
                smiley.replace("&lt;", "<");
                smiley.replace("&gt;", ">");
                smiley.replace("&amp;", "&");
                smiley.replace("&apos;", "\'");
                smiley.replace("&quot;", "\"");
            } else {
                smiley = "???";
            }

            smileys << smiley;
        }

        // finally replace those stupid characters
        for (int i = 0; i < smileys.size(); i++) {
            int pos = selected.indexOf("\ufffc");
            if (pos == -1) {
                break;
            }
            selected.replace(pos, 1, smileys[i]);
        }
        selected.replace("\ufffc", "???");
    }

    // do noting on no selection
    if (selected.isEmpty()) {
        return;
    }

    QPoint p = m_pTextEdit_CHATOUTPUT->mapFromGlobal(QCursor::pos());
    QString pressedPara;
    int nickStart, nickLen;

    QTextCursor cursor = m_pTextEdit_CHATOUTPUT->cursorForPosition(p);

    pressedPara = cursor.block().text();

    nickStart = 1 + pressedPara.indexOf("<");
    nickLen = pressedPara.indexOf(">") - nickStart;

    if (!((nickStart == 0) || (nickLen < 0) || (pressedPara.indexOf(selected) > pressedPara.indexOf(">")))) {
        QString _mbnick = pressedPara.mid(nickStart, nickLen);
        CString nick = _mbnick.toAscii().data();

        if (nick != "" && m_pClient->UserList()->IsUserOnline(nick)) {

            if (TextEdit_CHATINPUT->toPlainText().isEmpty())
                selected = _mbnick + ": ";
            else {
                if (TextEdit_CHATINPUT->toPlainText().right(1) != " ")
                    selected = " " + _mbnick + " ";
                else
                    selected = _mbnick + " ";
            }
        }
    } else
        selected = selected.trimmed();

    if (TextEdit_CHATINPUT->toPlainText().isEmpty()) {
        TextEdit_CHATINPUT->insertPlainText(selected);
    } else {
        if (TextEdit_CHATINPUT->toPlainText().right(1) != " ") {
            selected.prepend(" ");
        }

        // not using TextEdit_CHATINPUT->append(), that would add a new line
        TextEdit_CHATINPUT->insertPlainText(selected);
    }

    TextEdit_CHATINPUT->setFocus();
}

/** */
void DCChat::slotShellCommandFinished(bool ok, QString output) {
    DCShellCommandRunner * runner = qobject_cast<DCShellCommandRunner*>(sender());
    int pos = m_pShellRunners->indexOf(runner);
    if (pos == -1) {
        AddStatus(tr("Shell command event not found in list"));
        AddStatus(output);
    } else {
        if (ok) {
            SendChat(output);
        } else {
            AddStatus(output);
        }

        m_pShellRunners->removeAt(pos);
        disconnect(runner, SIGNAL(finished(bool, QString)), this, SLOT(slotShellCommandFinished(bool, QString)));
        delete runner;
    }
}

/** */
void DCChat::slotBGColorChanged() {
    if (DCConfig::Instance()->GetChatBackgroundColorEnabled()) {
        /*
         * strangely trying to change the text background colour using QTextFormat
         * or style sheets does not appear to work, this method could be applied to
         * any widget
         */
        QPalette palette = m_pTextEdit_CHATOUTPUT->palette();
        palette.setColor(QPalette::Base, DCConfig::Instance()->GetChatBackgroundColor());
        m_pTextEdit_CHATOUTPUT->setAutoFillBackground(true);
        m_pTextEdit_CHATOUTPUT->setPalette(palette);

        palette = TextEdit_CHATINPUT->palette();
        palette.setColor(QPalette::Base, DCConfig::Instance()->GetChatBackgroundColor());
        TextEdit_CHATINPUT->setAutoFillBackground(true);
        TextEdit_CHATINPUT->setPalette(palette);
    } else {
        QTextEdit te;
        m_pTextEdit_CHATOUTPUT->setAutoFillBackground(te.autoFillBackground());
        m_pTextEdit_CHATOUTPUT->setPalette(te.palette());
        TextEdit_CHATINPUT->setAutoFillBackground(te.autoFillBackground());
        TextEdit_CHATINPUT->setPalette(te.palette());
    }
}

void DCChat::slotFGColorChanged() {
    QPalette palette = TextEdit_CHATINPUT->palette();
    palette.setColor(QPalette::Text, DCConfig::Instance()->GetChatForegroundColor());
    TextEdit_CHATINPUT->setPalette(palette);
}

void DCChat::ClearChat() {
    m_pTextEdit_CHATOUTPUT->setHtml("<html></html>");
    AddStatus(tr("Chat Cleared."));
}

void DCChat::slotSmyle() {
    int x, y;
    CDialogPictureMap * dialog = new CDialogPictureMap(this);
    QPixmap p = QPixmap::fromImage(DCConfig::Instance()->GetEmoticonImage());
    dialog->SetPixmap(p);

    if (dialog->exec() == QDialog::Accepted) {

        dialog->GetXY(x, y);

        QString smiley = DCConfig::Instance()->EmoticonTextForPos(x, y);

        if (!smiley.isEmpty()) {

            smiley.replace("&lt;", "<");
            smiley.replace("&gt;", ">");
            smiley.replace("&amp;", "&");
            smiley.replace("&apos;", "\'");
            smiley.replace("&quot;", "\"");

            smiley += " ";

            TextEdit_CHATINPUT->insertPlainText(smiley);
            TextEdit_CHATINPUT->setFocus();

        }
    }

    delete dialog;
}

void DCChat::slotScrollChat(int value){
    if (value == m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maximum())
        bAutoScroll = true;
    else
        bAutoScroll = false;
}

void DCChat::addStringToChat(QString nick) {

    if (TextEdit_CHATINPUT->toPlainText().isEmpty()) {
        TextEdit_CHATINPUT->insertPlainText(nick += ": ");
    } else {
        if (TextEdit_CHATINPUT->toPlainText().right(1) != " ") {
            nick.prepend(" ");
        }
        // not using TextEdit_CHATINPUT->append(), that would add a new line
        TextEdit_CHATINPUT->insertPlainText(nick);

    }

    TextEdit_CHATINPUT->setFocus();

}

void DCChat::ZoomIn() {
    m_pTextEdit_CHATOUTPUT->zoomIn();
}

void DCChat::ZoomOut() {
    m_pTextEdit_CHATOUTPUT->zoomOut();
}

void DCChat::SetChatOff() {
    bChatOff = !bChatOff;

    QString chat_msg = "";

    if (bChatOff) {
        chat_msg = tr("Chat disabled.");
    } else {
        chat_msg = tr("Chat enabled.");
    }

    AddStatus(chat_msg, true);

    DCGuiApp::Instance()->SetChatOff(bChatOff);
}

void DCChat::closeEvent(QCloseEvent * e) {
    if (isVisible())
        DCChat::SetActiveChat(NULL);

    QWidget::setAttribute(Qt::WA_DeleteOnClose);

    e->accept();
}

void DCChat::SetActiveChat(DCChat *chat) {
    pActiveChat = chat;
}

DCChat *DCChat::ActiveChat() {
    return pActiveChat;
}
