/***************************************************************************
                          dcclient.cpp  -  description
                             -------------------
    begin                : Thu Oct 4 2001
    copyright            : (C) 2001-2005 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 "dcclient.h"

#include <stdlib.h>

#include <QApplication>
#include <QLineEdit>
#include <QCheckBox>
#include <QRegExp>
#include <QComboBox>
#include <QCursor>
#include <QFile>
#include <QMessageBox>
#include <QLabel>
#include <QPushButton>
#include <QListWidgetItem>
#include <QTabWidget>
#include <QInputDialog>
#include <QClipboard>
#include <QLayout>
#include <QSplitter>
#include <QToolButton>
#include <QShowEvent>
#include <QPixmap>
#include <QMouseEvent>
#include <QCloseEvent>
#include <QKeyEvent>
#include <QEvent>
#include <QHeaderView>
#include <QDateTime>
#include <QMdiArea>

#include "dcevent.h"
#include "dcspy.h"
#include "dctransferview.h"
#include "dcconfig.h"
#include "dcchat.h"
#include "dcmenuhandler.h"
#include "dcuserslist.h"
#include "dcfriendobject.h"
#include "dciconloader.h"
#include "dcguiutils.h"
#include "dcconnectionmanager.h"
#include "dcantispam.h"

#include "userlistmodel.h"
#include "filteronlyproxy.h"

#include <dclib/cfilemanager.h>
#include <dclib/dcos.h>
#include <dclib/dcobject.h>

#include "ui/DCDialogForceMove.h"
#include "ui/DCDialogUserCommandLines.h"

/** */
DCClient::DCClient( QWidget * parent, CString remoteEncoding ) : QWidget( parent ), CClient( remoteEncoding ), m_bInitSizesDone( false )
{
    setupUi(this);

    if ( g_pConnectionManager->GetMdiArea() )
    {
        m_pContainerWindow = g_pConnectionManager->GetMdiArea()->addSubWindow(this);
    }
    else
    {
        m_pContainerWindow = 0;
    }

    // set default icon
    setWindowIcon( g_pIconLoader->GetPixmap(eiNOTCONNECTED) );
    if ( m_pContainerWindow != 0 )
    {
        m_pContainerWindow->setWindowIcon( g_pIconLoader->GetPixmap(eiNOTCONNECTED) );
    }

    /* default to sorting A-Z */
    TreeView_USERLIST->sortByColumn( COLUMN_NICK, Qt::AscendingOrder );

    m_pProxyModel = 0;
    m_pUserListModel = new UserListModel( this );

    for ( int i = 0; i < m_pUserListModel->columnCount(); ++i )
    {
        ComboBox_FILTER->addItem( m_pUserListModel->headerData( i, Qt::Horizontal ).toString() );
    }
    ComboBox_FILTER->addItem( tr("Any") );
    ComboBox_FILTER->setCurrentIndex( 0 );

    TreeView_USERLIST->setModel( m_pUserListModel );

    m_pMessageList = new QList<CDCMessage*>();
    m_pMyInfoHash  = new QHash<QString, CMessageMyInfo*>();

    m_bUseTabWidget = g_pConfig->GetShowChatInTab();

    m_nChatFloodCounter = 0;

    allow_show_joins = false;

    InitDocument();
}

/** */
DCClient::~DCClient()
{
    m_Timer.stop();

    m_pHubChat->removeEventFilter(this);
    delete m_pHubChat;

    if ( g_pUsersList )
    {
        // force recheck all friend state
        DCFriendObject m_pFriendObject;
        QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
    }

    Disconnect();

    m_Mutex.lock();

    if ( m_pMessageList )
    {
        QList<CDCMessage*> * tmp = m_pMessageList;
        m_pMessageList = 0;

        for ( QList<CDCMessage*>::const_iterator it = tmp->constBegin(); it != tmp->constEnd(); ++it )
        {
            delete *it;
        }

        delete tmp;
    }

    m_Mutex.unlock();

    m_ChatMap.clear();

    clearMyInfoCache();
    delete m_pMyInfoHash;
    m_pMyInfoHash = 0;

    /* model(s) deleted by QObject auto child deletion */

    qDeleteAll(hubcommands);
    hubcommands.clear();
}

/** */
void DCClient::InitDocument()
{
    SetCrypt(false);

    // add hubchat widget
    m_pHubChat = new DCChat( TabWidget_CHAT, this, false );
    m_pHubChat->setObjectName("HUB-Chat");
    m_pHubChat->setAttribute(Qt::WA_DeleteOnClose);
    m_pHubChat->installEventFilter(this);
    m_pHubChat->SetNick(QString(),tr("Hub"));

    TabWidget_CHAT->addTab( m_pHubChat, tr("Hub") );

    // remove chat list tab
    if ( m_bUseTabWidget )
    {
        TabWidget_CHAT->removeTab(0);
        delete Tab_CHATUSERLIST;
        Tab_CHATUSERLIST = 0;
        ListWidget_CHATUSERLIST = 0; /* deleted with Tab_CHATUSERLIST */

        TabWidget_CHAT->setCurrentIndex(0);

                //TabWidget_CHAT->setCornerWidget( new QPushButton( QIcon( g_pIconLoader->GetPixmap(eiFILECLOSE) ), QString(), TabWidget_CHAT ) );
                //TabWidget_CHAT->cornerWidget()->setToolTip(tr("Close chat tab"));
                //TabWidget_CHAT->cornerWidget()->setEnabled(false);
    }
    else
    {
        connect( ListWidget_CHATUSERLIST,SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotDoubleClickedChatUserList(QListWidgetItem*)) );

        TabWidget_CHAT->setTabText( 0, tr("Chat List")+" ("+QString().setNum(ListWidget_CHATUSERLIST->count())+")" );

        TabWidget_CHAT->setCurrentIndex(1);
    }

    connect( TabWidget_CHAT, SIGNAL(customContextMenuRequested( const QPoint & )), this, SLOT(slotContextMenuTabWidgetChat( const QPoint & )) );
    connect( TreeView_USERLIST,SIGNAL(doubleClicked( const QModelIndex & )), this, SLOT(slotDoubleClickedUserList( const QModelIndex & )) );
    connect( TreeView_USERLIST,SIGNAL(customContextMenuRequested( const QPoint & )), this, SLOT(slotContextMenuUserList( const QPoint & )) );
    connect( TabWidget_CHAT,SIGNAL(currentChanged(QWidget*)), this, SLOT(slotTabWidgetChatCurrentChange(QWidget*)) );
    connect( ToolButton_CONNECT,SIGNAL(clicked()), this, SLOT(slotHubConnect()) );
    connect( ToolButton_SSL,SIGNAL(clicked()), this, SLOT(slotSSLInfo()) );

        /*if (m_bUseTabWidget)
    {
        connect( TabWidget_CHAT->cornerWidget(), SIGNAL(clicked()), this, SLOT(slotTabCornerCloseWidgetClicked()) );
        }*/

    /* filter control widgets */
    connect( CheckBox_FILTER, SIGNAL(toggled(bool)), this, SLOT(slotFilterToggled(bool)) );
    connect( ComboBox_FILTER, SIGNAL(currentIndexChanged(int)), this, SLOT(slotFilterColumn(int)) );
    connect( LineEdit_FILTER, SIGNAL(textChanged(const QString &)), this, SLOT(slotFilterString(const QString &)) );

    /* context menu for user list header */
    TreeView_USERLIST->header()->setContextMenuPolicy( Qt::CustomContextMenu );
    connect( TreeView_USERLIST->header(), SIGNAL(customContextMenuRequested( const QPoint & )), this, SLOT(slotContextMenuHeader( const QPoint & )) );

    // set connect disconnect button pixmap
    SetConnection(false);

    TreeView_USERLIST->setAlternatingRowColors( g_pConfig->GetAlternatingRowColors() );
    connect( g_pConfig, SIGNAL( alternatingRowColorsChanged( bool ) ), this, SLOT( slotAltRowColors( bool ) ) );

    TreeView_USERLIST->installEventFilter(this);

    connect( &m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()) );

    m_Timer.setSingleShot( true );
    m_Timer.start( 600 );

    if (m_bUseTabWidget){
#ifdef QT_NEW
	    //TabWidget_CHAT->setTabsClosable(true);
	    //connect(TabWidget_CHAT, SIGNAL(tabCloseRequested(int)), this, SLOT(slotCloseTabButtonClicked(int)));
#endif
    }
}

/** */
void DCClient::InitSizes()
{
    StringMap * map;

    // restore settings
    if ( g_pConfig->GetMap("CLIENTVIEW",map) )
    {
        if ( ((*map)["WIDTH"].toInt() > 0) && ((*map)["HEIGHT"].toInt() > 0) )
        {
            if ( m_pContainerWindow && !isMinimized() && !isMaximized() )
            {
                m_pContainerWindow->resize( (*map)["WIDTH"].toInt(), (*map)["HEIGHT"].toInt() );
            }
        }

        TreeView_USERLIST->sortByColumn( (*map)["SORTCOLUMN"].toInt(), DCGuiUtils::SortOrderFromName((*map)["SORTORDER"]) );
    }

    if ( g_pConfig->GetUserListRightAlignment() )
    {
        // insertWidget moves the widget if it is already in the splitter
        Splitter_CLIENT->insertWidget(0,Frame_CHATLIST);

        Frame_CHATLIST->adjustSize();
        Frame_USERLIST->adjustSize();
    }

    if ( g_pConfig->GetMap("USERLISTVIEW",map) )
    {
        if ( (map->value("WIDTH").toInt() > 0) && !isMinimized() )
        {
            QList<int> frames = Splitter_CLIENT->sizes();

            if ( frames.size() == 2 )
            {
                int total = frames[0] + frames[1];
                if ( g_pConfig->GetUserListRightAlignment() )
                {
                    frames[0] = total - map->value("WIDTH").toInt();
                    frames[1] = map->value("WIDTH").toInt();
                }
                else
                {
                    frames[0] = map->value("WIDTH").toInt();
                    frames[1] = total - map->value("WIDTH").toInt();
                }

                Splitter_CLIENT->setSizes(frames);
            }
        }
    }

    // restore column order, widths and visibility or set defaults
    bool restored = false;
    if ( g_pConfig->GetMap("USERLISTCOLUMNORDER",map) )
    {
        restored = TreeView_USERLIST->header()->restoreState(
            QByteArray::fromBase64(map->value(QString("USERLISTHEADERSTATE")).toAscii())
        );
    }

    if ( !restored )
    {
        /* TODO initialize default column width ratios if necessary */

        TreeView_USERLIST->header()->hideSection( COLUMN_LOCKPK );
        TreeView_USERLIST->header()->hideSection( COLUMN_SUPPORTS );
    }
}

/** */
void DCClient::DeInitDocument()
{
    StringMap * map;

    // save client view settings
    g_pConfig->GetMap("CLIENTVIEW",map);

    if ( m_pContainerWindow != 0 )
    {
        (*map)["WIDTH"]      = QString().setNum(m_pContainerWindow->width());
        (*map)["HEIGHT"]     = QString().setNum(m_pContainerWindow->height());
    }

    (*map)["SORTCOLUMN"] = QString::number( m_pUserListModel->getSortColumn() );
    (*map)["SORTORDER"]  = DCGuiUtils::SortOrderName( m_pUserListModel->getSortOrder() );

    g_pConfig->GetMap("USERLISTVIEW",map);

    QList<int> sizes = Splitter_CLIENT->sizes();
    int index = Splitter_CLIENT->indexOf( Frame_USERLIST );
    if ( index < sizes.size() )
    {
        (*map)["WIDTH"] = QString().setNum(sizes[index]);
    }

    saveColumns();

    if ( !m_ChatMap.isEmpty() )
    {
        QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();
        while ( it != m_ChatMap.constEnd() )
        {
            it.value()->removeEventFilter(this);
            it.value()->close();
            ++it;
        }
    }
}

/** */
void DCClient::SetCrypt( bool b )
{
    ToolButton_SSL->setEnabled(b);

    if ( b )
    {
        ToolButton_SSL->setToolTip(tr("Line is encrypted."));
        ToolButton_SSL->setIcon( QIcon(g_pIconLoader->GetPixmap(eiSSL_YES)) );
    }
    else
    {
        ToolButton_SSL->setToolTip(tr("Line is not encrypted."));
        ToolButton_SSL->setIcon( QIcon(g_pIconLoader->GetPixmap(eiSSL_NO)) );
    }
}

/** */
void DCClient::SetConnection( bool b )
{
    if ( b )
    {
        ToolButton_CONNECT->setToolTip(tr("Disconnect."));
        ToolButton_CONNECT->setIcon( QIcon(g_pIconLoader->GetPixmap(eiCONNECT_NO)) );
    }
    else
    {
        ToolButton_CONNECT->setToolTip(tr("Connect."));
        ToolButton_CONNECT->setIcon( QIcon(g_pIconLoader->GetPixmap(eiCONNECT_CREATING)) );
    }
}

/** */
void DCClient::slotSSLInfo()
{
    /* a non blocking info dialog, deleted when closed or when the hub is closed */
    QMessageBox * mb = new QMessageBox( this );
    mb->setAttribute( Qt::WA_DeleteOnClose );
    mb->setIcon( QMessageBox::Information );
    mb->setWindowTitle( QString::fromAscii(GetHubName().Data()) );
    mb->setText( QString::fromAscii((GetSSLVersion() + '\n' + GetSSLCipher()).Data()) );
    mb->setInformativeText( QString::fromAscii(VerifyPeerCertificate().Data()) );
    mb->setModal( false );
    mb->show();
}

/** event filter */
bool DCClient::eventFilter(QObject* object, QEvent* event)
{
    if((event->type() == QEvent::Close)&&((DCClient*)object!=this))
    {
        if ( m_pHubChat == object )
        {
            event->ignore();
            return true;
        }

        CloseChat( (DCChat*)object );
    }
    else if ( event->type() == QEvent::KeyPress )
    {
        QKeyEvent * e = (QKeyEvent*)event;

        // reconnect
        if ( (e->modifiers() == Qt::AltModifier) && (e->key() == Qt::Key_R) )
        {
            slotHubConnect();
        }

        if ( TreeView_USERLIST->hasFocus() )
        {
            // TODO: add userlist shortcuts
        }
    }

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

/** current tab widget change slot */
void DCClient::slotTabWidgetChatCurrentChange( QWidget * w )
{
    DCChat * chat = (DCChat*)w;

    int index = TabWidget_CHAT->indexOf(w);

    if ( ! TabWidget_CHAT->tabIcon(index).isNull() )
    {
        TabWidget_CHAT->setTabIcon( index, QIcon() );

        // reset nick color
        m_pUserListModel->setHighlight( chat->GetNick(), false );
    }

        /*if (m_bUseTabWidget)
    {
        if (TabWidget_CHAT->currentWidget() == m_pHubChat)
        {
            TabWidget_CHAT->cornerWidget()->setEnabled(false);
        }
        else
        {
            TabWidget_CHAT->cornerWidget()->setEnabled(true);
        }
        }*/
}

/** overridden so that sizes are initialized on first show */
void DCClient::showEvent( QShowEvent * event )
{
    QWidget::showEvent( event );

    if ( (m_bInitSizesDone == false) && isVisible() )
    {
        InitSizes();
        m_bInitSizesDone = true;
    }
}

/** close event - remove client from connection manager before accepting */
void DCClient::closeEvent( QCloseEvent * event )
{
        g_pActiveChat = NULL;

        emit onDie(this->GetMdiSubWindow());

    g_pConnectionManager->HubClosing( this );

    DeInitDocument();

    event->accept();
}

/** */
void DCClient::UpdateCaption()
{
    QString s;

    switch(GetConnectionState())
    {
        case estCONNECTED:
                s = "[+]";
                break;
        default:
                s = "[-]";
                break;
    }

    s += " ";
    s += QString::fromAscii(GetHubName().Data());

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

    if (s.length() > 50)
    {
        s = s.left(50) + "...";
    }

    setWindowTitle(s);
    g_pConnectionManager->CaptionChanged( this );
}

/** */
void DCClient::UpdateStatusBar()
{
    if ( m_pProxyModel && m_pProxyModel->rowCount() != m_pUserListModel->rowCount() )
    {
        TextLabel_USERCOUNT->setText( QString("%1/%2").arg(m_pProxyModel->rowCount()).arg(m_pUserListModel->rowCount()) );
    }
    else
    {
        TextLabel_USERCOUNT->setText( QString::number( m_pUserListModel->rowCount() ) );
    }
    TextLabel_SHARESIZE->setText( DCGuiUtils::GetSizeString(UserList()->ShareSize()) );
}

/** */
void DCClient::timerDone()
{
    CDCMessage *DCMsg;
    int i;

        //for(i=0;i<200;i++)
        while (m_pMessageList  && !m_pMessageList->isEmpty())
    {
        if ( m_Mutex.tryLock() == false )
        {
            break;
        }

                DCMsg = m_pMessageList->takeFirst();

        m_Mutex.unlock();

        if ( DCMsg == 0 )
        {
            break;
        }

        switch ( DCMsg->m_eType )
        {
            case DC_MESSAGE_CONNECTION_STATE:
            {
                CMessageConnectionState *msg = (CMessageConnectionState*) DCMsg;

                if ( msg->m_eState == estCONNECTED )
                {
                    m_nChatFloodCounter = 0;
                    m_bUserPassword     = false;

                    UpdateCaption();
                    setWindowIcon( g_pIconLoader->GetPixmap(eiCONNECTED) );
                    if ( m_pContainerWindow )
                        m_pContainerWindow->setWindowIcon( g_pIconLoader->GetPixmap(eiCONNECTED) );
                    // repaint new icon in minimized mode
                    if ( isMinimized() )
                        showMinimized();

                    m_pHubChat->AddStatus(tr("Connected"));

                    // init userlist
                    DC_NickList(0);

                    g_pConfig->PlaySound(eusCONNECT);

                    SetConnection(true);

                    SetCrypt(false);
                }
                if ( msg->m_eState == estSSLCONNECTED )
                {
                    SetCrypt(true);
                }
                else if ( msg->m_eState == estCONNECTIONTIMEOUT )
                {
                    m_pHubChat->AddStatus(tr("Connection timeout"));

                    SetConnection(false);

                    SetCrypt(false);
                }
                else if ( msg->m_eState == estSOCKETERROR )
                {
                    QString s;
                    s  = tr("Error: '");
                    s += QString::fromAscii(msg->m_sMessage.Data());
                    s += QString("'");
                    m_pHubChat->AddStatus(s);

                    SetConnection(false);

                    SetCrypt(false);
                }
                else if ( msg->m_eState == estDISCONNECTED )
                {
                    m_nChatFloodCounter = 0;

                    UpdateCaption();
                    setWindowIcon( g_pIconLoader->GetPixmap(eiNOTCONNECTED) );
                    if ( m_pContainerWindow )
                        m_pContainerWindow->setWindowIcon( g_pIconLoader->GetPixmap(eiNOTCONNECTED) );

                    // repaint new icon in minimized mode
                    if ( isMinimized() )
                        showMinimized();

                    m_pHubChat->AddStatus(tr("Disconnected"));
                    g_pConfig->PlaySound(eusDISCONNECT);

                                        QString hub_name = QString::fromAscii(GetHubName().Data());

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

                                        g_pMainWin->showNotification(hub_name, tr("Connection lost"), eTrayMessageTypeDISC);

                    if ( g_pUsersList )
                    {
                        // force recheck all friend state
                        DCFriendObject m_pFriendObject;
                        QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
                    }

                    // reset all chats
                    QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();
                    while ( it != m_ChatMap.constEnd() )
                    {
                        it.value()->SetCrypt(esecsNONE);
                        it.value()->AddStatus(tr("We left the hub."));
                        ++it;
                        }

                    SetConnection(false);

                    SetCrypt(false);
                }

                break;
            }

            case DC_MESSAGE_VALIDATEDENIDE:
            {
                m_pHubChat->AddStatus(tr("Your Nick is already in use."));
                Disconnect();

                                QString hub_name = QString::fromAscii(GetHubName().Data());

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

                                g_pMainWin->showNotification(hub_name,
                                                             tr("Connection lost")+": "+tr("Your Nick is already in use."),
                                                             eTrayMessageTypeDISC);

                break;
            }

            case DC_MESSAGE_NICKLIST:
            {
                DC_NickList( (CMessageNickList *) DCMsg );
                                delete DCMsg;
                                m_Timer.setSingleShot( true );
                                m_Timer.start( 150 ); //fast retrieving list of users
                                return;
            }

            case DC_MESSAGE_OPLIST:
            {
                DC_OpList( (CMessageOpList *) DCMsg );
                break;
            }

            case DC_MESSAGE_REVCONNECTTOME:
            {
                if ( GetMode() == ecmPASSIVE )
                {
                    if ( g_pConfig->GetSendMessageOnActiveModeRequest() )
                    {
                        CMessageRevConnectToMe * msg = (CMessageRevConnectToMe*) DCMsg;
                        SendPrivateMessage( GetNick(), msg->m_sDstNick, tr("Sorry, you will not be able to download from me, because we are both in passive mode. (automated message)").toAscii().constData() );
                    }
                }

                break;
            }

            case DC_MESSAGE_CHAT:
            {
                CMessageChat * msg = (CMessageChat *) DCMsg;

                if ( m_ChatFloodMessage.m_sMessage == msg->m_sMessage && m_ChatFloodMessage.m_sNick == msg->m_sNick )
                {
                    // increment flood counter
                    m_nChatFloodCounter++;
                }
                else
                {
                    // update message and reset counter
                    m_ChatFloodMessage.m_sMessage = msg->m_sMessage;
                    m_ChatFloodMessage.m_sNick    = msg->m_sNick;
                    m_nChatFloodCounter = 0;
                }

                // if flood detect enabled ?
                if ( g_pConfig->GetFloodCount() > 0 )
                {
                    if ( m_nChatFloodCounter >= g_pConfig->GetFloodCount() )
                    {
                        // only one flood message
                        if ( m_nChatFloodCounter == g_pConfig->GetFloodCount() )
                        {
                            m_pHubChat->AddStatus(tr("Flood Detected") + " (" + QString().setNum(m_nChatFloodCounter) + ")");

                            // now the admin kick the user if enabled
                            if ( (g_pConfig->GetFloodOpKick()) && (!UserList()->IsAdmin(msg->m_sNick)) && (UserList()->IsAdmin(GetNick())) )
                            {
                                //if OP kick the user for Flooding
                                CString kickmessage=g_pConfig->GetFloodOpKickMessage();
                                SendPrivateMessage( GetNick(), msg->m_sNick, ("You are being kicked because: " + kickmessage));
                                SendChat( GetNick(), GetNick() + " is kicking " + msg->m_sNick + " because: " + kickmessage );
                                SendKick(msg->m_sNick);
                            }
                        }

                        // don't show the flood messages
                        msg = 0;
                    }
                }

                if ( msg )
                {
                    DC_Chat( msg );
                }

                break;
            }

            case DC_MESSAGE_MYINFO:
            {
                DC_MyInfo( (CMessageMyInfo *) DCMsg );
                break;
            }

            case DC_MESSAGE_QUIT:
            {
                DC_Quit( (CMessageQuit *) DCMsg );
                break;
            }

            case DC_MESSAGE_SEARCH_FILE:
            {
                if ( g_pSpy )
                    g_pSpy->DC_Search( (CMessageSearchFile *) DCMsg );
                break;
            }

            case DC_MESSAGE_HELLO:
            {
                DC_Hello( (CMessageHello *) DCMsg );

                break;
            }

            case DC_MESSAGE_FORCEMOVE:
            {
                CMessageForceMove * msg = (CMessageForceMove*) DCMsg;

                if ( g_pConfig->GetForceMoveEnabled() )
                {
                    UpdateCaption();

                    m_pHubChat->AddStatus( tr("Redirect to ")+ QString::fromAscii(msg->m_sHost.Data()) + QString(":") + QString().setNum(msg->m_nPort) );
                }
                else
                {
                    m_pHubChat->AddStatus( tr("Redirect disabled ")+ QString::fromAscii(msg->m_sHost.Data()) + QString(":") + QString().setNum(msg->m_nPort) );
                }

                break;
            }

            case DC_MESSAGE_HUBNAME:
            {
                CMessageHubName * msg = (CMessageHubName*) DCMsg;
                QString qhubname = QString::fromAscii( msg->m_sHubName.Data() );

                if ( (msg->m_sOldHubName.NotEmpty()) && (msg->m_sOldHubName != msg->m_sHubName) )
                {
                    m_pHubChat->AddStatus( tr("Hubname change:")+QString(" '")+QString::fromAscii(msg->m_sOldHubName.Data())+QString("' -> '")+qhubname+QString("'") );
                }

                UpdateCaption();

                // update caption from the main chat
                m_pHubChat->SetNick(QString(),qhubname);

                // update caption from all private chats
                if ( !m_ChatMap.isEmpty() )
                {
                    QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();

                    while( it != m_ChatMap.constEnd() )
                    {
                        it.value()->SetNick( it.value()->GetNick(), qhubname );
                        ++it;
                    }
                }

                break;
            }

            case DC_MESSAGE_HUB_TOPIC:
            {
                UpdateCaption();
                break;
            }

            case DC_MESSAGE_PRIVATECHAT:
            {
                DC_PrivateChat( (CMessagePrivateChat*) DCMsg );

                break;
            }

            case DC_MESSAGE_GETPASS:
            {
                bool ok = false;
                DCConfigHubProfile pConfigHubProfile;

                if ( m_bUserPassword == false )
                {
                    // get password from profile
                    if ( g_pConfig->GetBookmarkHubProfile( GetHubName(), GetHost(), &pConfigHubProfile ) )
                    {
                        if ( pConfigHubProfile.m_sPassword.NotEmpty() )
                        {
                            m_pHubChat->AddStatus(tr("Use password from profile"));
                            SendPass(pConfigHubProfile.m_sPassword);
                            SetUsedPassword(true);
                            m_bUserPassword = true;
                            break;
                        }
                        else
                        {
                            m_pHubChat->AddStatus(tr("Password from profile is empty"));
                        }
                    }
                    else
                    {
                        m_pHubChat->AddStatus(tr("No profile found for this hub"));
                    }
                }

                QString text = QInputDialog::getText(
                    this,
                    tr( "Password - ")+QString(GetHubName().Data()),
                    tr( "Please enter your password" ),
                    QLineEdit::Password, QString::null, &ok );

                if ( ok && !text.isEmpty() )
                {
                    SendPass(text.toAscii().constData());
                    SetUsedPassword(true);
                }
                else
                {
                    Disconnect();
                }

                break;
            }

            case DC_MESSAGE_BADPASS:
            {
                m_pHubChat->AddStatus(tr("Wrong password"));
                SetUsedPassword(false);
                break;
            }

            case DC_MESSAGE_USERIP:
            {
                CMessageUserIP * msg = (CMessageUserIP*) DCMsg;

                std::list<CString>::const_iterator nick_it = msg->m_lNicks.begin();
                std::list<CString>::const_iterator ip_it = msg->m_lIPs.begin();
                QString qnick, qip;
                CMessageMyInfo * myinfo = 0;

                while ( (nick_it != msg->m_lNicks.end()) && (ip_it != msg->m_lIPs.end()) )
                {
                    qnick = QString::fromAscii( nick_it->Data() );
                    qip = QString::fromAscii( ip_it->Data() );

                    // fill in the IP address column for the given nick
                    m_pUserListModel->updateIP( qnick, qip );

                    // update this useless thing
                    myinfo = m_pMyInfoHash->value( qnick );
                    if ( myinfo )
                    {
                        myinfo->m_sTransferHost = *ip_it;
                    }

                    // notify when given own IP
                    if ( *nick_it == GetNick() )
                    {
                        m_pHubChat->AddStatus(tr("Hub gave us our IP: ") + qip);

			allow_show_joins = true;

                        if ( g_pConfig->GetUserIP2Enabled() == false )
                        {
                            m_pHubChat->AddStatus(tr("Enable") + " \"" + tr("Use IP address from the hub") + "\" " + tr("in Options -> Connection -> Mode to use this value."));
                        }
                    }

                    ++nick_it;
                    ++ip_it;
                }

                break;
            }

            case DC_MESSAGE_USER_COMMAND:
            {
                DC_UserCommand( (CMessageUserCommand*)DCMsg );

                break;
            }

            case DC_MESSAGE_LOG:
            {
                m_pHubChat->AddStatus(QString::fromAscii(((CMessageLog*)DCMsg)->sMessage.Data()));
            }

            default:
            {
                break;
            }
        }

        if ( DCMsg )
            delete DCMsg;
    }

    m_Timer.setSingleShot( true );
    m_Timer.start( 500 );
}

/** */
int DCClient::DC_CallBack( CDCMessage * DCMessage )
{
    int err = -1;

    m_Mutex.lock();

    if ( DCMessage && m_pMessageList )
    {
        m_pMessageList->append(DCMessage);
        err = 0;
    }

    m_Mutex.unlock();

    return err;
}

/** chat */
void DCClient::DC_Chat( CMessageChat * MessageChat )
{
    QString qnick = QString::fromAscii(MessageChat->m_sNick.Data());
    QString qmessage = QString::fromAscii(MessageChat->m_sMessage.Data());

    // check for ignored nick
    if ( g_pUsersList->ignoreNick( qnick ) )
    {
        //printf("Ignoring \"%s\" from \"%s\"\n", MessageChat->m_sMessage.Data(), MessageChat->m_sNick.Data());
        return;
    }

    m_pHubChat->AddMessage(qnick,qmessage,true);

    if ( !m_pHubChat->isVisible() )
    {
        TabWidget_CHAT->setTabIcon( TabWidget_CHAT->indexOf(m_pHubChat), QIcon( g_pIconLoader->GetPixmap(eiMESSAGE) ) );
    }

    // send chat event
    g_pConnectionManager->HubEvent(this);

    // check for auto responses
    if ( g_pConfig->GetAutoResponderEnabled() )
    {
        doAutoResponse( qnick, qmessage, m_pHubChat );
    }
}

/** myinfo */
void DCClient::DC_MyInfo( CMessageMyInfo * MessageMyInfo )
{
    if ( MessageMyInfo->m_sNick.IsEmpty() )
    {
        m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
        return;
    }

    QPixmap pm;
    QString nick = QString::fromAscii(MessageMyInfo->m_sNick.Data());

    CMessageMyInfo * myinfo = m_pMyInfoHash->value( nick );

    if ( myinfo == 0 )
    {
        myinfo = new CMessageMyInfo();
        *myinfo = *MessageMyInfo;
        m_pMyInfoHash->insert( nick, myinfo );

        pm = *g_pConfig->GetUserIcon(MessageMyInfo);
        m_pUserListModel->addUser(
            nick,
            pm,
            MessageMyInfo->m_sComment.Data(),
            QString::fromAscii(MessageMyInfo->m_sVerComment.Data()),
            MessageMyInfo->m_sUserSpeed.Data(),
            QString::fromAscii(MessageMyInfo->m_sEMail.Data()),
            MessageMyInfo->m_nShared,
            MessageMyInfo->m_sTransferHost.Data(),
            MessageMyInfo->m_MessageLock.m_sData.Data(),
            MessageMyInfo->m_MessageSupports.m_sContent.Data()
        );

        // user not in the list, add it
        addUser( nick );
    }
    else
    {
        if ( (myinfo->m_eAwayMode != MessageMyInfo->m_eAwayMode) ||
             (myinfo->m_eUserSpeed != MessageMyInfo->m_eUserSpeed) ||
             (myinfo->m_bOperator != MessageMyInfo->m_bOperator) ||
             (myinfo->m_eClientMode != MessageMyInfo->m_eClientMode) ||
             (myinfo->m_eClientVersion != MessageMyInfo->m_eClientVersion) ||
             (myinfo->m_bFireballFlag != MessageMyInfo->m_bFireballFlag) ||
             (myinfo->m_bServerFlag != MessageMyInfo->m_bServerFlag) ||
             (myinfo->m_bTLSFlag != MessageMyInfo->m_bTLSFlag) )
        {
            pm = *g_pConfig->GetUserIcon(MessageMyInfo);
            m_pUserListModel->updateIcon( nick, pm);

            myinfo->m_eAwayMode      = MessageMyInfo->m_eAwayMode;
            myinfo->m_eUserSpeed     = MessageMyInfo->m_eUserSpeed;
            myinfo->m_bOperator      = MessageMyInfo->m_bOperator;
            myinfo->m_eClientMode    = MessageMyInfo->m_eClientMode;
            myinfo->m_eClientVersion = MessageMyInfo->m_eClientVersion;
            myinfo->m_bFireballFlag  = MessageMyInfo->m_bFireballFlag;
            myinfo->m_bServerFlag    = MessageMyInfo->m_bServerFlag;
            myinfo->m_bTLSFlag       = MessageMyInfo->m_bTLSFlag;
        }

        if ( myinfo->m_sComment != MessageMyInfo->m_sComment )
        {
            myinfo->m_sComment = MessageMyInfo->m_sComment;
            m_pUserListModel->updateComment( nick, QString::fromAscii(MessageMyInfo->m_sComment.Data()) );
        }

        if ( myinfo->m_sVerComment != MessageMyInfo->m_sVerComment )
        {
            myinfo->m_sVerComment = MessageMyInfo->m_sVerComment;
            m_pUserListModel->updateTag( nick, MessageMyInfo->m_sVerComment.Data() );
        }

        if ( myinfo->m_sUserSpeed != MessageMyInfo->m_sUserSpeed )
        {
            myinfo->m_sUserSpeed = MessageMyInfo->m_sUserSpeed;
            m_pUserListModel->updateSpeed( nick, MessageMyInfo->m_sUserSpeed.Data() );
        }

        if ( myinfo->m_sEMail != MessageMyInfo->m_sUserSpeed )
        {
            myinfo->m_sEMail = MessageMyInfo->m_sUserSpeed;
            m_pUserListModel->updateEmail( nick, QString::fromAscii(MessageMyInfo->m_sEMail.Data()) );
        }

        if ( myinfo->m_nShared != MessageMyInfo->m_nShared )
        {
            myinfo->m_nShared = MessageMyInfo->m_nShared;
            m_pUserListModel->updateShare( nick, MessageMyInfo->m_nShared );
        }

        if ( myinfo->m_sTransferHost != MessageMyInfo->m_sTransferHost )
        {
            myinfo->m_sTransferHost = MessageMyInfo->m_sTransferHost;
            m_pUserListModel->updateIP( nick, MessageMyInfo->m_sTransferHost.Data() );
        }

        if ( myinfo->m_MessageLock.m_sData != MessageMyInfo->m_MessageLock.m_sData )
        {
            myinfo->m_MessageLock.m_sData = MessageMyInfo->m_MessageLock.m_sData;
            m_pUserListModel->updateLockPK( nick, MessageMyInfo->m_MessageLock.m_sData.Data() );
        }

        if ( myinfo->m_MessageSupports.m_sContent != MessageMyInfo->m_MessageSupports.m_sContent )
        {
            myinfo->m_MessageSupports.m_sContent = MessageMyInfo->m_MessageSupports.m_sContent;
            m_pUserListModel->updateSupports( nick, MessageMyInfo->m_MessageSupports.m_sContent.Data() );
        }
    }

    // send friend event
    if ( g_pUsersList )
    {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName        = nick;
        m_pFriendObject.m_sDescription = QString::fromAscii(MessageMyInfo->m_sComment.Data());
        m_pFriendObject.m_eAwayMode    = MessageMyInfo->m_eAwayMode;
        m_pFriendObject.m_sHubName     = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost     = QString::fromAscii(GetHost().Data());
        QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
    }

    UpdateStatusBar();
}

/** hello */
void DCClient::DC_Hello( CMessageHello * MessageHello )
{
    if ( MessageHello->m_sNick.IsEmpty() )
    {
        m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
        return;
    }

    QPixmap pm;
    QString nick = QString::fromAscii(MessageHello->m_sNick.Data());

    if ( m_pMyInfoHash->contains( nick ) == false )
    {
        CMessageMyInfo * myinfo = new CMessageMyInfo();
        myinfo->m_sNick = MessageHello->m_sNick;
        myinfo->m_eAwayMode = euamNORMAL;

        m_pMyInfoHash->insert( nick, myinfo );

        pm = *g_pConfig->GetUserIcon(myinfo);

        m_pUserListModel->addUser(
            nick,
            pm
        );

        addUser( nick );
    }

    UpdateStatusBar();
}

/** quit */
void DCClient::DC_Quit( CMessageQuit * MessageQuit )
{
    if ( MessageQuit->m_sNick.IsEmpty() )
    {
        return;
    }

    QString nick = QString::fromAscii(MessageQuit->m_sNick.Data());
    DCChat * chat = m_ChatMap.value(nick);

    QHash<QString, CMessageMyInfo*>::iterator it = m_pMyInfoHash->find( nick );
    if ( it != m_pMyInfoHash->end() )
    {
        m_pUserListModel->removeUser( nick );
        delete it.value();
        m_pMyInfoHash->erase( it );
    }

    if ( chat != 0 )
    {
        chat->SetCrypt(esecsNONE);
        chat->AddStatus(tr("User left the hub."));
        }

    // send friend event
    if ( g_pUsersList )
    {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName     = nick;
        m_pFriendObject.m_eAwayMode = euamOFFLINE;
        m_pFriendObject.m_sHubName  = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost  = QString::fromAscii(GetHost().Data());
        QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
    }

    if ( g_pConfig->GetChatShowJoinsAndParts() )
    {
        if ( g_pConfig->GetChatShowJoinsAndPartsOnlyFav() )
        {
            if ( g_pUsersList->isNickInList( nick ) )
            {
                m_pHubChat->AddStatus( nick  + tr(" parted the chat.") );
            }
        }
        else
        {
            m_pHubChat->AddStatus( nick  + tr(" parted the chat.") );
        }
    }

    UpdateStatusBar();
}

/** nicklist */
void DCClient::DC_NickList( CMessageNickList * MessageNickList )
{
    CString * Nick = 0;
    QPixmap pm;
    QString nick;

    TreeView_USERLIST->setUpdatesEnabled(false);
    m_pUserListModel->clear();

    clearMyInfoCache();

    if ( MessageNickList )
    {
        while ( (Nick=MessageNickList->m_NickList.Next(Nick)) != 0 )
        {
            if ( Nick->IsEmpty() )
            {
                m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
                continue;
            }

            nick = QString::fromAscii(Nick->Data());

            if ( m_pMyInfoHash->contains( nick ) == false )
            {
                CMessageMyInfo * myinfo = new CMessageMyInfo();
                myinfo->m_sNick = (*Nick);
                myinfo->m_eAwayMode = euamNORMAL;

                m_pMyInfoHash->insert( nick, myinfo );

                pm = *g_pConfig->GetUserIcon(myinfo);

                m_pUserListModel->addUser(
                    nick,
                    pm
                );
            }

            // send friend event
            if ( g_pUsersList )
            {
                DCFriendObject m_pFriendObject;

                m_pFriendObject.m_sName     = nick;
                m_pFriendObject.m_eAwayMode = euamONLINE;
                m_pFriendObject.m_sHubName  = QString::fromAscii(GetHubName().Data());
                m_pFriendObject.m_sHubHost  = QString::fromAscii(GetHost().Data());
                QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
            }
        }
    }

    TreeView_USERLIST->setUpdatesEnabled(true);

    UpdateStatusBar();
}

/** oplist */
void DCClient::DC_OpList( CMessageOpList * MessageOpList )
{
    CString * Nick = 0;
    QPixmap pm;
    QString nick;

    TreeView_USERLIST->setUpdatesEnabled(false);

    while ( (Nick=MessageOpList->m_NickList.Next(Nick)) != 0 )
    {
        if ( Nick->IsEmpty() )
        {
            m_pHubChat->AddStatus(tr("Cannot display operator with empty nick."));
            continue;
        }

        nick = QString::fromAscii(Nick->Data());

        CMessageMyInfo * myinfo = m_pMyInfoHash->value( nick );

        if ( myinfo == 0 )
        {
            myinfo = new CMessageMyInfo();
            myinfo->m_sNick = (*Nick);
            myinfo->m_bOperator = true;
            myinfo->m_eAwayMode = euamNORMAL;

            m_pMyInfoHash->insert( nick, myinfo );

            // user not in the list, add it
            addUser( nick );

            pm = *g_pConfig->GetUserIcon(myinfo);

            m_pUserListModel->addUser(
                nick,
                pm
            );

            m_pUserListModel->setSortTop( nick, true );
        }
        else
        {
            if ( myinfo->m_bOperator == false )
            {
                myinfo->m_bOperator = true;

                pm = *g_pConfig->GetUserIcon( myinfo );

                m_pUserListModel->updateIcon( nick, pm );

                m_pUserListModel->setSortTop( nick, true );
            }
        }
    }

    TreeView_USERLIST->setUpdatesEnabled(true);

    UpdateStatusBar();
}

void DCClient::DC_PrivateChat( QString nick, QString fromnick, QString message, bool bLocal )
{
    CMessagePrivateChat msg;

    msg.m_sSrcNick      = nick.toAscii().constData();
    msg.m_sMultiSrcNick = fromnick.toAscii().constData();
    msg.m_sMessage      = message.toAscii().constData();

    DC_PrivateChat( &msg, bLocal );
}

/** */
void DCClient::DC_PrivateChat( CMessagePrivateChat * msg, bool bLocal )
{
    QWidget * focuswidget = 0;
    bool bShowChat = true;

    if ( msg->m_sSrcNick.IsEmpty() )
    {
        return;
    }

    QString qnick = QString::fromAscii(msg->m_sSrcNick.Data());
    QString qmessage = QString::fromAscii(msg->m_sMessage.Data());

    // also check for ignored nick for PMs

    if ( g_pUsersList->ignoreNick( qnick ) )
    {
        //printf("Ignoring PM \"%s\" from \"%s\"\n", msg->m_sMessage.Data(), msg->m_sSrcNick.Data() );
        return;
    }

        bool in_gray  = false;
        bool in_white = false;
        bool just_added = false;

        if (g_pConfig->GetAntiSpamEnabled()){
            if (!g_pAntiSpam){

                new DCAntiSpam();

                g_pAntiSpam->loadLists();
                g_pAntiSpam->loadSettings();

            }

            if (g_pAntiSpam->isInBlack(qnick)){
                return;
            }

            if (g_pAntiSpam->isInGray(qnick)){
                in_gray = true;
            }
            else if (g_pAntiSpam->isInWhite(qnick))
                in_white = true;

        }
        else{
            in_gray = true;
            in_white = true;
        }

        if (bLocal)//don't check outgoing messages
            in_gray = (in_white = true);

    DCChat * chat = m_ChatMap.value(qnick);

    // create a new chat window
    if ( chat == 0 )
    {
        focuswidget = qApp->focusWidget();

        if ( m_bUseTabWidget )
        {
            chat = new DCChat( TabWidget_CHAT, this );
            chat->setObjectName("chat");
            chat->setAttribute(Qt::WA_DeleteOnClose);
        }
        else
        {
            chat = new DCChat( g_pConnectionManager->GetMdiArea(), this );
            chat->setObjectName("chat");
            chat->setAttribute(Qt::WA_DeleteOnClose);
            new QListWidgetItem( qnick, ListWidget_CHATUSERLIST );
            TabWidget_CHAT->setTabText( 0, tr("Chat List")+" ("+QString().setNum(ListWidget_CHATUSERLIST->count())+")" );
        }

        // fix hide if chat maximized
                if (in_gray || in_white){
                    chat->show();
                    chat->hide();
                }

        chat->installEventFilter(this);
        chat->SetNick(qnick,QString::fromAscii(GetHubName().Data()));

        m_ChatMap.insert( qnick, chat );

        if ( (g_pConfig->GetOpenPrivateChatWindow() == false) && (bLocal == false) )
        {
            bShowChat = false;
        }
        // check for max lines and set chat invisible
        else if ( (g_pConfig->GetChatMessageMaxLines() > 0) &&
              (QString(msg->m_sMessage.Data()).count("\n") > g_pConfig->GetChatMessageMaxLines()) )
        {
            bShowChat = false;
        }

        // check for open chat windows nick exceptions
        DCConfigHubProfile pConfigHubProfile;
        QString nickregexp;

        if ( g_pConfig->GetBookmarkHubProfile( GetHubName(), GetHost(), &pConfigHubProfile ) )
        {
            nickregexp = pConfigHubProfile.m_sSuppressedNicks.Data();
        }

        if ( nickregexp.isEmpty() )
        {
            nickregexp = g_pConfig->GetSuppressedNicks();
        }

        if ( !nickregexp.isEmpty() )
        {
            QRegExp rx(nickregexp);
            rx.setCaseSensitivity(Qt::CaseInsensitive);
            if ( qnick.contains(rx) )
            {
                bShowChat = (!g_pConfig->GetOpenPrivateChatWindow()) || bLocal;
            }
        }

                if ( m_bUseTabWidget && (in_gray || in_white))
        {
            TabWidget_CHAT->addTab( chat, qnick );
        }

                if ( (bShowChat || bLocal) && (in_gray || in_white))
        {
            if ( m_bUseTabWidget )
            {
                TabWidget_CHAT->setCurrentWidget(chat);
            }
            else
            {
                g_pConnectionManager->GetMdiArea()->addSubWindow(chat);
                chat->setEnabled(true);
                chat->show();
            }
        }
        else
        {
            if ( !m_bUseTabWidget )
            {
                chat->setEnabled(false);
                chat->hide();
            }

            // restore focus
            if ( focuswidget )
                focuswidget->setFocus();
        }

                if (!in_gray && !in_white && g_pConfig->GetAntiSpamEnabled()){
                    sandbox << chat;

                    just_added = true;

                    chat->TextEdit_CHATINPUT->setPlainText(
                            QString("|AntiSpam Bot|: ") + tr("Your message was not delivered. "
                            "Answer the question and send message again, please. In the"
                            " case of incorrect answer you will be added in the black "
                            "list and your message will never arrive. So, the question is: ")
                            + g_pAntiSpam->getPhrase()
                            );
                    chat->SendChat();
                }

        g_pConfig->PlaySound(eusFIRSTRECEIVE);
    }
        else if ( bLocal ) // enable all local (user input)
    {
        if ( chat->isVisible() == false )
        {
            if ( m_bUseTabWidget )
            {
                TabWidget_CHAT->setCurrentWidget(chat);
            }
            else
            {
                g_pConnectionManager->GetMdiArea()->addSubWindow(chat);
                chat->setEnabled(true);
                chat->show();
            }
        }

        m_pUserListModel->setHighlight( qnick, false );
    }

    if ( msg->m_sMessage.NotEmpty() )
    {
                if (!just_added && !AntiSpamOK(chat, qnick, qmessage))
                    return;

        chat->AddMessage( msg );

        if ( chat->isVisible() == false )
        {
            m_pUserListModel->setHighlight( qnick, true );

            if ( m_bUseTabWidget )
                TabWidget_CHAT->setTabIcon( TabWidget_CHAT->indexOf(chat), QIcon( g_pIconLoader->GetPixmap(eiMESSAGE) ) );
        }
        else
        {
            m_pUserListModel->setHighlight( qnick, false );
        }
    }

        if (!just_added && !AntiSpamOK(chat, qnick, qmessage))
            return;

    if ( (chat->isVisible() == false) &&
             (g_pConfig->GetSendHidePrivateChatToPublicChat()) && (in_gray || in_white) )
    {
        QString nick;

        // send private chat to public chat
        if ( (msg->m_sSrcNick != msg->m_sMultiSrcNick) && (msg->m_sMultiSrcNick.NotEmpty()) )
            nick = qnick + "::" + QString::fromAscii(msg->m_sMultiSrcNick.Data());
        else
            nick = qnick;
        m_pHubChat->AddMessage( nick, qmessage, true, true );
    }

    // send chat event
    g_pConnectionManager->HubEvent(this);

        QString hub_name = QString::fromAscii(GetHubName().Data());

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

        g_pMainWin->showNotification(hub_name+" "+tr(": Private chat"),
                                     qnick+": " + qmessage,
                                     eTrayMessageTypePRIV);

    if ( g_pConfig->GetAutoResponderEnabledForPM() )
    {
        doAutoResponse( qnick, qmessage, chat );
    }
}

/** */
void DCClient::DC_UserCommand( CMessageUserCommand * msg )
{
    DC_UserMenuCommand * umc = 0;

    QString qname = QString::fromAscii(msg->name.Data());
    QString qcommand = QString::fromAscii(msg->command.Data());

    if ( msg->type == euctClear )
    {
        if ( msg->context == euccMask ) // clear all
        {
            qDeleteAll(hubcommands);
            hubcommands.clear();
            return;
        }

        for ( int i = 0; i < hubcommands.size(); i++ )
        {
            umc = hubcommands.at(i);

            umc->m_nContext -= (umc->m_nContext & msg->context);

            if ( umc->m_nContext == 0 )
            {
                delete umc;
                hubcommands.removeAt(i);
                i--;
            }
        }
    }
    else if ( msg->type == euctRemove )
    {
        for ( int i = 0; i < hubcommands.size(); i++ )
        {
            umc = hubcommands.at(i);

            if ( umc->m_sName == qname )
            {
                umc->m_nContext -= (umc->m_nContext & msg->context);

                if ( umc->m_nContext == 0 )
                {
                    delete umc;
                    hubcommands.removeAt(i);
                }

                return; // only remove one command
            }
        }
    }
    else if ( msg->type == euctSeparator ) // separators might not have names
    {
        umc = new DC_UserMenuCommand();

        umc->m_nType    = msg->type;
        umc->m_nContext = msg->context;
        umc->m_sName    = qname;
        umc->m_sCommand = qcommand;

        hubcommands.append(umc);
    }
    else if ( (msg->type == euctRaw) || (msg->type == euctRawOnce) )
    {
        // update existing command
        for ( int i = 0; i < hubcommands.size(); i++ )
        {
            umc = hubcommands.at(i);

            if ( umc->m_sName == qname )
            {
                umc->m_nType    = msg->type;
                umc->m_nContext = umc->m_nContext | msg->context;
                umc->m_sCommand = qcommand;
                return;
            }
        }

        umc = new DC_UserMenuCommand();
        umc->m_nType    = msg->type;
        umc->m_nContext = msg->context;
        umc->m_sName    = qname;
        umc->m_sCommand = qcommand;

        hubcommands.append(umc);
    }
    else
    {
        printf("Unknown type %d for command '%s'\n", msg->type, msg->name.Data());
    }
}

/** handling if user join the hub */
void DCClient::addUser( QString nick )
{
    DCChat * chat = m_ChatMap.value(nick);

    // show joins
    if ( g_pConfig->GetChatShowJoinsAndParts() && allow_show_joins)
    {
        if ( g_pConfig->GetChatShowJoinsAndPartsOnlyFav() )
        {
            if ( g_pUsersList->isNickInList( nick ) )
            {
                m_pHubChat->AddStatus( nick  + tr(" joined the chat.") );
            }
        }
        else
        {
            m_pHubChat->AddStatus( nick  + tr(" joined the chat.") );
        }
    }

    // show rejoin on private chat
    if ( chat != 0 && allow_show_joins)
    {
        chat->AddStatus(tr("User rejoin the hub."));
    }

    // send event to friendlist
    if ( g_pUsersList )
    {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName     = nick;
        m_pFriendObject.m_eAwayMode = euamONLINE;
        m_pFriendObject.m_sHubName  = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost  = QString::fromAscii(GetHost().Data());
        QApplication::postEvent( g_pUsersList, new DC_FriendEvent( &m_pFriendObject ) );
    }
}

/** */
void DCClient::CloseChat( DCChat * chat )
{
    chat->removeEventFilter(this);

    m_ChatMap.remove(chat->GetNick());

    if ( m_bUseTabWidget )
    {
        TabWidget_CHAT->removeTab( TabWidget_CHAT->indexOf(chat) );
    }
    else
    {
        QList<QListWidgetItem*> list = ListWidget_CHATUSERLIST->findItems( chat->GetNick(), Qt::MatchExactly );
        for ( int i = 0; i < list.size(); i++ )
        {
            ListWidget_CHATUSERLIST->removeItemWidget( list.at(i) );
            delete list.at(i);
            TabWidget_CHAT->setTabText( 0, tr("Chat List")+" ("+QString().setNum(ListWidget_CHATUSERLIST->count())+")" );
        }
    }

    chat->close();
}

/** */
void DCClient::CloseAllChats( bool onlyOffline )
{
    if ( onlyOffline )
    {
        QList<DCChat*> toclose;

        for ( QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin(); it != m_ChatMap.constEnd(); ++it )
        {
            if ( m_pMyInfoHash->contains( it.key() ) == false )
            {
                toclose.append( it.value() );
            }
        }

        for ( int i = 0; i < toclose.size(); ++i )
        {
            CloseChat( toclose.at(i) );
        }
    }
    else
    {
        while( !m_ChatMap.isEmpty() )
        {
            CloseChat(m_ChatMap.begin().value());
            }
    }
}

/** */
void DCClient::slotDoubleClickedUserList( const QModelIndex & index )
{
    if ( !index.isValid() )
    {
        return;
    }

    QModelIndex nickindex = TreeView_USERLIST->model()->index( index.row(), COLUMN_NICK, QModelIndex() );
    QString nick = TreeView_USERLIST->model()->data( nickindex ).toString();

    if ( g_pConfig->GetDoubleClickAction() == eDownloadList )
    {
        CString empty;
        g_pTransferView->DLM_QueueAdd(
            nick.toAscii().constData(),
            GetHubName(),
            GetHost(),
            DC_USER_FILELIST,
            DC_USER_FILELIST,
            empty,
            empty,
            eltBUFFER,
            0,
            0,
            0,
            empty
        );
    }
    else
    {
        DC_PrivateChat( nick, QString(), QString(), true );
    }
}

/** */
void DCClient::slotDoubleClickedChatUserList( QListWidgetItem * item )
{
    QString nick;

    if ( item == 0 )
    {
        return;
    }

    nick = item->text();

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

    DCChat * chat = m_ChatMap.value( nick );

    if ( chat != 0 )
    {
        if ( !m_bUseTabWidget )
            chat->setEnabled(true);

        // take widget to top
        if ( chat->isMinimized() )
        {
            chat->showNormal();
        }
        else if ( chat->isVisible() )
        {
            // TODO: dont work fix it
            chat->show();
            chat->raise();
        }
        else
        {
            g_pConnectionManager->GetMdiArea()->addSubWindow(chat);
            chat->show();
        }

        m_pUserListModel->setHighlight( nick, false );
    }
}

/** */
void DCClient::slotHubConnect()
{
    if ( GetConnectionState() == estNONE )
    {
        SetConnection(true);
        Connect();
        UpdateReconnect( ersNONE, 0 );
    }
    else if ( GetConnectionState() == estCONNECTED )
    {
        SetConnection(false);
        Disconnect();
    }
}

/** */
void DCClient::slotContextMenuUserList( const QPoint & )
{
    QMap<QAction*, DC_UserMenuCommand*> addedcommands;

    QStringList selectedNicks;

    QModelIndexList milist = TreeView_USERLIST->selectionModel()->selectedRows( COLUMN_NICK );

    for ( QModelIndexList::const_iterator it = milist.constBegin(); it != milist.constEnd(); ++it )
    {
        selectedNicks.append( TreeView_USERLIST->model()->data( *it ).toString() );
    }

    const int numSelected = selectedNicks.size();

    QMenu * m = new QMenu(this);

    if ( numSelected == 1 )
    {
        DCMenuHandler::addAction( m, emiUSER_CAPTION, false, tr("User: ") + selectedNicks.first() );
    }
    else
    {
        DCMenuHandler::addAction( m, emiUSER_CAPTION, false, QString::number(numSelected) + tr(" users") );
    }

    DCMenuHandler::addAction( m, emiSEPARATOR );
    QAction * privchat = DCMenuHandler::addAction( m, emiPRIVATE_CHAT, (numSelected > 0) );
    QAction * to_chat = DCMenuHandler::addAction( m, emiTO_CHAT, (numSelected > 0) );
    QAction * addfriend = DCMenuHandler::addAction( m, emiADD_FRIEND, (numSelected > 0) );
    QAction * browse = DCMenuHandler::addAction( m, emiBROWSE_USER_FILES, (numSelected > 0) );
    DCMenuHandler::addAction( m, emiSEPARATOR );

    QMenu * mslot = DCMenuHandler::addMenu( m, emiUPLOAD_SLOT, true );
    QAction * addslot = DCMenuHandler::addAction( mslot, emiADD, (numSelected > 0) );
    QAction * addperm = DCMenuHandler::addAction( mslot, emiADD_PERMANENT, (numSelected > 0) );
    QAction * remslot = DCMenuHandler::addAction( mslot, emiREMOVE, (numSelected > 0) );

    DCMenuHandler::addAction( m, emiSEPARATOR );
    QAction * upduser = 0;
    if ( GetSupportsNoGetInfo() == false )
    {
        upduser = DCMenuHandler::addAction( m, emiUPDATE_USER, (numSelected > 0) );
    }
    QAction * reloadlist = DCMenuHandler::addAction( m, emiRELOAD_USERLIST, (GetConnectionState() == estCONNECTED) );
    DCMenuHandler::addAction( m, emiSEPARATOR );
    QAction * copycol = DCMenuHandler::addAction( m, emiCOPY_COLUMN_TO_CLIPBOARD, (numSelected == 1) );
    QAction * copyrow = DCMenuHandler::addAction( m, emiCOPY_ROW_TO_CLIPBOARD, (numSelected > 0) );

    DCMenuHandler::addAction( m, emiSEPARATOR );
    QAction * checkver = DCMenuHandler::addAction( m, emiCHECK_CLIENT_VERSION, numSelected > 0 );

    QAction * kick = 0;
    QAction * forcemove = 0;

    if ( UserList()->IsAdmin(GetNick()) )
    {
        kick = DCMenuHandler::addAction( m, emiKICK, (numSelected > 0) );
        forcemove = DCMenuHandler::addAction( m, emiFORCE_MOVE, (numSelected > 0) );
    }

    addedcommands = AddMenuCommands( m, euccChat );

    QList<QAction*> addedactions = addedcommands.keys();

    for ( int i = 0; i < addedactions.size(); i++ )
    {
        addedactions[i]->setEnabled( numSelected > 0 );
    }

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

    delete m;

    if ( chosen == 0 )
    {
        return;
    }
    else if ( chosen == privchat )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            DC_PrivateChat( *it, QString::null, QString::null, true );
        }
    }
    else if (chosen == to_chat )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            g_pActiveChat->addStringToChat(it->toAscii().constData());
        }
    }
    else if ( chosen == addfriend )
    {
        QString qhubname = QString::fromAscii(GetHubName().Data());
        QString qhubhost = QString::fromAscii(GetHost().Data());
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            g_pUsersList->AddFriend( *it, qhubname, qhubhost, QString::null );
        }
    }
    else if ( chosen == browse )
    {
        if ( GetMode() == ecmNONE )
        {
            return;
        }

        CString empty;
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            /** add transfer to the waitlist */
            g_pTransferView->DLM_QueueAdd( it->toAscii().constData(), GetHubName(), GetHost(),
                        DC_USER_FILELIST, DC_USER_FILELIST, empty, empty, eltBUFFER,
                        0, 0, 0, empty );
        }
    }
    else if ( chosen == checkver )
    {
        if ( GetMode() == ecmNONE )
        {
            return;
        }

        CString empty;
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            /** add transfer to the waitlist */
            g_pTransferView->DLM_QueueAdd( it->toAscii().constData(), GetHubName(), GetHost(),
                        "client check", "client check", empty, empty, eltCLIENTVERSION,
                        0, 0, 0, empty );
        }
    }
    else if ( chosen == upduser )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            SendGetInfo( it->toAscii().constData(), GetNick() );
        }
    }
    else if ( chosen == copycol )
    {
        QModelIndex index = TreeView_USERLIST->selectionModel()->currentIndex();

        QApplication::clipboard()->setText( TreeView_USERLIST->model()->data( index ).toString() );
    }
    else if ( chosen == copyrow )
    {
        QString s;

        QModelIndexList indexes = TreeView_USERLIST->selectionModel()->selectedRows( COLUMN_NICK );

        for ( QModelIndexList::const_iterator it = indexes.constBegin(); it != indexes.constEnd(); ++it )
        {
            for ( int j = 0; j < TreeView_USERLIST->model()->columnCount(); j++ )
            {
                int col = TreeView_USERLIST->header()->logicalIndex(j);
                if ( TreeView_USERLIST->header()->isSectionHidden(col) == false )
                {
                    QModelIndex index = TreeView_USERLIST->model()->index( it->row(), col, QModelIndex() );
                    s += TreeView_USERLIST->model()->data( index ).toString();
                    s += " ";
                }
            }

            s += "\n";
        }

        s = s.trimmed();
        QApplication::clipboard()->setText(s);
    }
    else if ( chosen == addslot )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            g_pTransferView->DLM_AddUserSlot( it->toAscii().constData(), GetHubName(), 1 );
        }
    }
    else if ( chosen == addperm )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            g_pTransferView->DLM_AddUserSlot( it->toAscii().constData(), GetHubName(), 0, true );
        }
    }
    else if ( chosen == remslot )
    {
        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            g_pTransferView->DLM_AddUserSlot( it->toAscii().constData(), GetHubName(), 0 );
        }
    }
    else if ( chosen == kick )
    {
        QString kickmessage;

        if ( !GetOPKickMessage(kickmessage,this) )
        {
            return;
        }

        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            OPKick( *it, kickmessage );
        }
    }
    else if ( chosen == forcemove )
    {
        QString host,message;

        if ( !GetOPForceMoveMessage(message,host,this) )
        {
            return;
        }

        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            OPForceMove( *it, message, host );
        }
    }
    else if ( chosen == reloadlist )
    {
        RequestNickList();
    }
    else if ( addedcommands.contains( chosen ) )
    {
        DC_UserMenuCommand * umc = addedcommands[chosen];

        QString origUserCommand = umc->m_sCommand;

        QString usercommand;

        for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
        {
            usercommand = replaceCommandTags( origUserCommand, *it );

            if ( !usercommand.isEmpty() )
            {
                m_pHubChat->AddStatus(usercommand);
                SendString(usercommand.toAscii().constData());
            }
        }
    }
}

/** */
bool DCClient::GetOPKickMessage( QString & message, QWidget * parent )
{
    bool ok = false;

    message = QInputDialog::getText(
        parent,
        tr("OP Kick"),
        tr("Please enter a reason"),
        QLineEdit::Normal, QString::null, &ok );

    return ok;
}

/** */
bool DCClient::GetOPForceMoveMessage( QString & message, QString & host, QWidget * parent )
{
    bool ret = false;

    QDialog * dialog = new QDialog(parent);
    Ui::DCDialogForceMove ui;
    ui.setupUi(dialog);

    if ( dialog->exec() == QDialog::Accepted )
    {
        host = ui.LineEdit_HOST->text();
        message = ui.LineEdit_MESSAGE->text();
        ret = true;
    }

    delete dialog;

    return ret;
}

/** */
bool DCClient::OPKick( QString nick, QString message )
{
    bool res = false;

    SendPrivateMessage( GetNick(), nick.toAscii().constData(), ("You are being kicked because: " + message).toAscii().constData());
    SendChat( GetNick(), GetNick() + " is kicking " + nick.toAscii().constData() + " because: " + message.toAscii().constData() );
    SendKick(nick.toAscii().constData());

    return res;
}

/** */
bool DCClient::OPForceMove( QString nick, QString message, QString host )
{
    bool res = false;

    SendOpForceMove( nick.toAscii().constData(), host.toAscii().constData(), message.toAscii().constData() );

    return res;
}

/** */
void DCClient::slotDoubleClickedUserChat( QListWidgetItem * item )
{
    QString nick;

    if ( item == 0 )
    {
        return;
    }

    nick = item->text();

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

    DCChat * chat = m_ChatMap.value( nick );

    if ( chat != 0 )
    {
        if ( !m_bUseTabWidget )
            chat->setEnabled(true);
        chat->show();
        m_pUserListModel->setHighlight( nick, false );
        }
}

/** */
QString DCClient::findNick( QString part, int which )
{
/*
    for(uint i=0; i < nicks->count(); i++)
    {
        if (matches.contains(nicks->text(i)))
            continue;
        if(qstrlen(nicks->text(i)) >= part.length())
        {
            if(qstrnicmp(part, nicks->text(i), part.length()) == 0)
            {
                QString qsNick = ksopts->nick;
                if(qstrcmp(nicks->text(i), qsNick) != 0)
                { // Don't match your own nick
                    matches.append(nicks->text(i));
                }
            }
        }
    }
*/
    QStringList matches;

    QString tlpart = part.trimmed().toLower();

    if ( tlpart == lastPart )
    {
        matches = lastMatches;
    }
    else
    {
        if ( g_pConfig->GetTabCompleteMatchMode() == eTabContains )
        {
            matches = m_pUserListModel->matchNicksContaining(tlpart, g_pConfig->GetTabCompleteStripTags());
        }
        else
        {
            matches = m_pUserListModel->matchNicksStartingWith(tlpart, g_pConfig->GetTabCompleteStripTags());
        }

        lastMatches = matches;
        lastPart = tlpart;
    }

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

    if(matches.count() > 0)
    {
        if(which < matches.count())
            return matches.at(which);
        else
            return QString::null;
    }

    return part;
}

/*void DCClient::slotTabCornerCloseWidgetClicked()
{
    CloseChat( (DCChat*) TabWidget_CHAT->currentWidget() );
}*/

/** Replaces the %[tag] s in the command with the proper values */
QString DCClient::replaceCommandTags(QString & command, QString remotenick)
{
    QString usercommand = command;

    usercommand.replace( "%[mynick]", GetNick().Data() );
    usercommand.replace( "%[myNI]", GetNick().Data() );

    QString comtag = GetComment().Data();
    QString comment = comtag.mid(0, comtag.indexOf('<'));
    QString tag = comtag.mid(comtag.indexOf('<'));

    usercommand.replace( "%[mytag]", tag );
    usercommand.replace( "%[myTAG]", tag );

    usercommand.replace( "%[mydescription]", comment );
    usercommand.replace( "%[myDE]", comment );

    usercommand.replace( "%[myemail]", GetEMail().Data() );
    usercommand.replace( "%[myEM]", GetEMail().Data() );

    const QString myss = QString::number( GetShareSize() );
    usercommand.replace( "%[myshare]", myss );
    usercommand.replace( "%[mySS]", myss );

    const QString myssshort = DCGuiUtils::GetSizeString(GetShareSize());
    usercommand.replace( "%[myshareshort]", myssshort );
    usercommand.replace( "%[mySSshort]", myssshort );

    const QString myip = GetExternalIP( false ).Data();
    usercommand.replace( "%[myip]", myip );
    usercommand.replace( "%[myI4]", myip );

    usercommand.replace( "%[hub]", GetHubName().Data() );
    usercommand.replace( "%[hubNI]", GetHubName().Data() );

    usercommand.replace( "%[hubURL]", GetHost().Data() );

    usercommand.replace( "%[hubIP]", GetResolvedIP().Data() );
    usercommand.replace( "%[hubI4]", GetResolvedIP().Data() );

    usercommand.replace( "%[nick]", remotenick );
    usercommand.replace( "%[userNI]", remotenick );

    CMessageMyInfo * myinfo = m_pMyInfoHash->value( remotenick );

    if ( myinfo != 0 )
    {
        usercommand.replace( "%[userTAG]", myinfo->m_sVerComment.Data() );
        usercommand.replace( "%[tag]", myinfo->m_sVerComment.Data() );

        usercommand.replace( "%[userDE]", myinfo->m_sComment.Data() );
        usercommand.replace( "%[description]", myinfo->m_sComment.Data() );

        usercommand.replace( "%[userSS]", QString::number(myinfo->m_nShared) );
        usercommand.replace( "%[share]", QString::number(myinfo->m_nShared) );

        QString ss = DCGuiUtils::GetSizeString( myinfo->m_nShared );
        usercommand.replace( "%[userSSshort]", ss );
        usercommand.replace( "%[shareshort]", ss );

        usercommand.replace( "%[userEM]", myinfo->m_sEMail.Data() );
        usercommand.replace( "%[email]", myinfo->m_sEMail.Data() );

        usercommand.replace( "%[userI4]", myinfo->m_sTransferHost.Data() );
        usercommand.replace( "%[ip]", myinfo->m_sTransferHost.Data() );
    }

    if ( usercommand.contains("%[line:") )
    {
        QDialog * dialog = new QDialog(this);
        Ui::DCDialogUserCommandLines ui;
        ui.setupUi(dialog);

        ui.LineEdit_NICK->setText( remotenick );
        ui.LineEdit_HUB->setText( QString::fromAscii(GetHubName().Data()) );

        QMap<QString, QLineEdit*> lineeditmap;
        QWidget * container = new QWidget(dialog);
        QGridLayout * grid = new QGridLayout(container);

        int loc = 0, line = 0, end = 0;

        for ( ; ; )
        {
            loc = usercommand.indexOf( "%[line:", loc );
            end = usercommand.indexOf( "]", loc );

            if ( (loc == -1) || (end == -1) )
            {
                break;
            }

            // 7 is length of "%[line:"
            QString reason = usercommand.mid(loc + 7, end - (loc + 7));

            QString key = "%[line:" + reason + "]";

            if ( !lineeditmap.contains(key) )
            {
                QLabel * label = new QLabel(container);
                label->setText(reason);
                grid->addWidget( label, line, 0 );

                QLineEdit * edit = new QLineEdit(container);
                grid->addWidget( edit, line, 1 );

                lineeditmap.insert( key, edit );
                ++line;
            }

            loc = end + 1;
        }

        container->setLayout(grid);
        ui.ScrollArea->setWidgetResizable(true);
        ui.ScrollArea->setWidget(container);

        if ( dialog->exec() == QDialog::Accepted )
        {
            for ( QMap<QString, QLineEdit*>::const_iterator it = lineeditmap.constBegin(); it != lineeditmap.constEnd(); ++it )
            {
                QString text = it.value()->text();
                text.replace( "$", "&#36;" );
                text.replace( "|", "&#124;" );

                usercommand.replace( it.key(), text  );

                if ( ui.CheckBox_ALL_NICKS->isChecked() )
                {
                    command.replace( it.key(), text );
                }
            }
            delete dialog;
        }
        else
        {
            delete dialog;
            return QString();
        }
    }

    time_t ti = time(0);
    struct tm * t = localtime(&ti);
    QByteArray timebuf;
    timebuf.resize( usercommand.toAscii().size() + 512 );

    /*
     * If strftime returns 0 either the buffer was not big enough
     * or the actual output is zero characters.
     */
    for ( int tries = 0; tries < 10; ++tries )
    {
        if ( strftime( timebuf.data(), timebuf.size(), usercommand.toAscii().constData(), t ) > 0 )
        {
            usercommand = QString::fromAscii( timebuf.constData() );
            break;
        }
        else
        {
            timebuf.resize( timebuf.size() + 128 );
        }
    }

    return usercommand;
}

void DCClient::doAutoResponse( QString nick, QString message, DCChat * chatobject )
{
    long delay = g_pConfig->GetAutoResponseDelay();
    long now = QDateTime::currentDateTime().toTime_t();

    if ( (delay > 0) &&
         (nick == lastAutoNick) &&
         (now < delay + lastAutoResponseTime)
       )
    {
        // avoid spamming
        //printf("Auto response spam protection!\n");
        return;
    }

    // null pointer check
    if (chatobject == 0)
    {
        printf("Error: DCClient::doAutoResponse called with NULL chatobject!\n");
        return;
    }

    // do not respond to ourself
    if (nick == QString::fromAscii(GetNick().Data()))
    {
        //printf("Not auto-responding to ourself.\n");
        return;
    }

    // check for ignored nicks
    QString ignores = g_pConfig->GetAutoResponseIgnores();
    if ( !ignores.isEmpty() )
    {
        QRegExp ignore_re;
        ignore_re.setPattern(ignores);
        ignore_re.setCaseSensitivity(Qt::CaseInsensitive);
        if ( nick.contains(ignore_re) )
        {
            //printf("Not auto responding to %s\n", nick.toAscii().constData());
            return;
        }
    }

    QList<DC_AutoResponseObject*> arlist;
    DC_AutoResponseObject * aro = 0;

    g_pConfig->GetAutoResponses(&arlist);

    QRegExp re;

    for ( QList<DC_AutoResponseObject*>::const_iterator it = arlist.constBegin(); it != arlist.constEnd(); ++it )
    {
        aro = *it;
        re.setPattern(aro->m_sTrigger);

        if ( aro->m_bCaseSensitive )
        {
            re.setCaseSensitivity(Qt::CaseSensitive);
        }
        else
        {
            re.setCaseSensitivity(Qt::CaseInsensitive);
        }

        if ( message.contains(re) ) // pattern matches
        {
            QString origResponse = aro->m_sResponse;
            QString chat = replaceCommandTags( origResponse, nick );

            if ( chat.isEmpty() )
            {
                break;
            }

            //SendChat( GetNick(), chat.toAscii().constData() );
            chatobject->TextEdit_CHATINPUT->setPlainText(chat);
            chatobject->SendChat();

            // update last nick and time for anti-spam
            lastAutoNick = nick;
            lastAutoResponseTime = QDateTime::currentDateTime().toTime_t();

            // stop searching list of triggers
            break;
        }
    }

    for ( QList<DC_AutoResponseObject*>::const_iterator it = arlist.constBegin(); it != arlist.constEnd(); ++it )
    {
        delete *it;
    }
}

/** */
bool DCClient::jumpToNick( const QString & nick )
{
    if ( nick.isEmpty() )
    {
        return false;
    }

    QModelIndex index = m_pUserListModel->indexForNick( nick );

    if ( !index.isValid() )
    {
        return false;
    }

    if ( m_pProxyModel )
    {
        index = m_pProxyModel->mapFromSource( index );

        if ( !index.isValid() )
        {
            return false;
        }
    }

    TreeView_USERLIST->selectionModel()->select( index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows );
    TreeView_USERLIST->scrollTo( index, QAbstractItemView::EnsureVisible );
    return true;
}

/** */
void DCClient::disableUserListSorting()
{
    TreeView_USERLIST->setSortingEnabled( false );
    m_pUserListModel->sort( -1 );
}

/** */
void DCClient::enableUserListSorting()
{
    TreeView_USERLIST->setSortingEnabled( true );
    TreeView_USERLIST->sortByColumn( COLUMN_NICK, Qt::AscendingOrder );
}

/** */
void DCClient::centreOnMdiArea()
{
    // centre window within workspace otherwise the top left corner
    // ends up in the centre so most of it is off the screen
    if ( (g_pConnectionManager->GetMdiArea() != 0) &&
         (m_pContainerWindow != 0) &&
         (m_pContainerWindow->isMinimized() == false) &&
         (m_pContainerWindow->isMaximized() == false)
       )
    {
        int newx = (g_pConnectionManager->GetMdiArea()->width() - m_pContainerWindow->width())/2;
        int newy = (g_pConnectionManager->GetMdiArea()->height() - m_pContainerWindow->height())/2;

        if ( newx < 0 )
        {
            newx = 0;
        }

        if ( newy < 0 )
        {
            newy = 0;
        }

        m_pContainerWindow->move(newx,newy);
    }
}

/** */
QMap<QAction*, DC_UserMenuCommand*> DCClient::AddMenuCommands( QMenu * menu, int context )
{
    QMap<QAction*, DC_UserMenuCommand*> added;
    QList<DC_UserMenuCommand*> allcommands;
    QMap<QString, QMenu*> createdmenus;
    QMenu * prevmenu = 0;

    if ( !menu )
    {
        return added;
    }

    if ( g_pConfig->GetEnableUserCommand() )
    {
        allcommands += hubcommands;
    }

    QMap<int, DC_UserMenuCommand*> usercommands;

    g_pConfig->GetUserMenuCommandsDirect(&usercommands);

    allcommands += usercommands.values();

    usercommands.clear();

    if ( g_pConfig->GetUserCommandSubmenu() )
    {
        menu = DCMenuHandler::addMenu(menu, emisUSER_COMMANDS);
    }

    for ( int i = 0; i < allcommands.size(); i++ )
    {
        if ( (allcommands[i]->m_nContext & context) == context )
        {
            if ( !(allcommands[i]->m_sHubIP.isEmpty()) )
            {
                QString hubiplc = allcommands[i]->m_sHubIP.toLower();
                if ( (hubiplc == "op") && (UserList()->IsAdmin(GetNick()) == false) )
                {
                    //printf("Skipping command '%s' because we are not OP here\n",allcommands[i]->m_sName.toAscii().constData());
                    continue;
                }
                else if ( (hubiplc != QString::fromAscii(GetHost().ToLower().Data())) && (hubiplc != QString::fromAscii(GetIP().ToLower().Data())) )
                {
                    //printf("Skipping command '%s' because host/IP does not match (required=%s our host=%s its ip=%s)\n",allcommands[i]->m_sHubIP.toAscii().constData(),GetHost().Data(),GetIP().Data());
                    continue;
                }
            }

            QMenu * submenu = menu;
            QString name = allcommands[i]->m_sName;

            if ( name.contains('\\') )
            {
                QStringList subMenuNames = name.split( '\\', QString::SkipEmptyParts );
                name = subMenuNames.takeLast(); // remove and store command name

                QString subMenuKey;
                QMenu * parentMenu = menu;

                for ( int j = 0; j < subMenuNames.size(); j++ )
                {
                    subMenuKey += subMenuNames[j];

                    if ( createdmenus.contains(subMenuKey) )
                    {
                        parentMenu = createdmenus[subMenuKey];
                        submenu = parentMenu;
                    }
                    else
                    {
                        parentMenu = parentMenu->addMenu( subMenuNames[j] );
                        createdmenus[subMenuKey] = parentMenu;
                        submenu = parentMenu;
                    }

                    subMenuKey += '\\';
                }
            }

            if ( allcommands[i]->m_nType == euctSeparator )
            {
                // no need to keep the QAction returned
                // the protocol was badly designed, separators do not have names
                // and so cannot be removed from menus
                // whatever docs exist on random forum posts etc.
                // also say separators are supposed to be a "vertical bar"
                // valknut currently uses horizontal bars
                if ( allcommands[i]->m_sName.isEmpty() && prevmenu )
                {
                    prevmenu->addSeparator();
                }
                else
                {
                    submenu->addSeparator();
                }
            }
            else if ( (allcommands[i]->m_nType == euctRaw) || (allcommands[i]->m_nType == euctRawOnce) )
            {
                added.insert( submenu->addAction(name), allcommands[i] );
                prevmenu = submenu;
            }
            //else
            //{
            //	printf("AddMenuCommands: wrong command type %d in lists for command '%s'\n", allcommands[i]->m_nType, allcommands[i]->m_sName.Data());
            //}
        }
    }

    if ( g_pConfig->GetUserCommandSubmenu() && (added.size() == 0) )
    {
        menu->setEnabled(false);
    }

    return added;
}

/** */
void DCClient::clearMyInfoCache()
{
    for ( QHash<QString, CMessageMyInfo*>::const_iterator it = m_pMyInfoHash->constBegin(); it != m_pMyInfoHash->constEnd(); ++it )
    {
        delete it.value();
    }

    m_pMyInfoHash->clear();
}

/** */
void DCClient::slotContextMenuTabWidgetChat( const QPoint & pos )
{
    if ( (TabWidget_CHAT->currentWidget() != 0)
         && (TabWidget_CHAT->currentWidget() != Tab_CHATUSERLIST) )
    {
        ((DCChat*)TabWidget_CHAT->currentWidget())->slotRightButtonClickedChatOutput(pos);
    }
}

/** */
void DCClient::slotFilterToggled( bool enabled )
{
    LineEdit_FILTER->setEnabled( enabled );
    ComboBox_FILTER->setEnabled( enabled );

    if ( enabled )
    {
        /* it should be NULL */
        delete m_pProxyModel;

        m_pProxyModel = new FilterOnlyProxy( this );
        m_pProxyModel->setDynamicSortFilter( true );
        m_pProxyModel->setFilterCaseSensitivity( Qt::CaseInsensitive );
        m_pProxyModel->setSourceModel( m_pUserListModel );
        m_pProxyModel->setFilterFixedString( LineEdit_FILTER->text() );

        if ( ComboBox_FILTER->currentIndex() == m_pUserListModel->columnCount() )
        {
            m_pProxyModel->setFilterKeyColumn( -1 );
        }
        else
        {
            m_pProxyModel->setFilterKeyColumn( ComboBox_FILTER->currentIndex() );
        }

        TreeView_USERLIST->setModel( m_pProxyModel );
    }
    else
    {
        TreeView_USERLIST->setModel( m_pUserListModel );
        delete m_pProxyModel;
        m_pProxyModel = 0;
    }

    UpdateStatusBar();
}

/** */
void DCClient::slotFilterColumn( int column )
{
    if ( m_pProxyModel )
    {
        if ( column == m_pUserListModel->columnCount() )
        {
            m_pProxyModel->setFilterKeyColumn( -1 );
        }
        else
        {
            m_pProxyModel->setFilterKeyColumn( column );
        }

        UpdateStatusBar();
    }
}

/** */
void DCClient::slotFilterString( const QString & text )
{
    if ( m_pProxyModel )
    {
        m_pProxyModel->setFilterFixedString( text );
        UpdateStatusBar();
    }
}

/** */
void DCClient::slotContextMenuHeader( const QPoint & /* pos */ )
{
    QMenu * mcols = new QMenu( this );
    QAction * column;
    int index;

    for ( int i = 0; i < m_pUserListModel->columnCount(); ++i )
    {
        index = TreeView_USERLIST->header()->logicalIndex(i);
        column = mcols->addAction( m_pUserListModel->headerData( index, Qt::Horizontal ).toString() );
        column->setCheckable(true);
        if ( index == 0 )
        {
            column->setEnabled(false);
        }
        column->setChecked( TreeView_USERLIST->header()->isSectionHidden( index ) == false );
        column->setData( index );
    }

    mcols->addSeparator();
    QAction * save = DCMenuHandler::addAction( mcols, emiSAVE );
    QAction * chosen = mcols->exec( QCursor::pos() );

    if ( chosen == save )
    {
        saveColumns();
    }
    else if ( chosen )
    {
        index = chosen->data().toInt();
        if ( TreeView_USERLIST->header()->isSectionHidden(index) )
        {
            TreeView_USERLIST->header()->showSection( index );
        }
        else
        {
            TreeView_USERLIST->header()->hideSection( index );
        }
    }

    delete mcols;
}

/** */
void DCClient::slotAltRowColors( bool enable )
{
    TreeView_USERLIST->setAlternatingRowColors( enable );
}

/** */
void DCClient::saveColumns()
{
    StringMap * map;
    g_pConfig->GetMap("USERLISTCOLUMNORDER",map);
    (*map)["USERLISTHEADERSTATE"] = QString(TreeView_USERLIST->header()->saveState().toBase64().constData());
}

#ifdef QT_NEW
/** */
/*
void DCClient::slotCloseTabButtonClicked(int index)
{
    QWidget *wgt = TabWidget_CHAT->widget(index);

    if (wgt){
        wgt->close();
    }
}
*/
#endif

/** */
bool DCClient::AntiSpamOK(DCChat *chat, QString qnick, QString qmessage)
{
    if (!chat || (qnick == ""))
        return false;

    if (g_pAntiSpam && sandbox.contains(chat)){

        if (qmessage != g_pAntiSpam->getKey()){

            g_pAntiSpam->addToBlack(QList<QString>() << qnick);

            chat->TextEdit_CHATINPUT->setPlainText("|AntiSpam Bot|: "
                                                   + tr("Now you blacklisted. Bye."));
            chat->SendChat();
            chat->close();

            int index = sandbox.indexOf(chat);
            sandbox.removeAt(index);

            return false;
        }
       else{
          g_pAntiSpam->addToGray(QList<QString>() << qnick);

          chat->TextEdit_CHATINPUT->setPlainText("|AntiSpam Bot|: "
                                                 + tr("Ok. Now you can write your message."));
          chat->SendChat();
          chat->close();

          int index = sandbox.indexOf(chat);
          sandbox.removeAt(index);

          return true;
        }

       m_ChatMap.remove(qnick);

    }

    return true;

}

