/***************************************************************************
                          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.h>
#include <qlineedit.h>
#include <qcheckbox.h>
#include <qregexp.h>
#include <qcombobox.h>
#include <qcursor.h>
#include <qfile.h>
#include <qmessagebox.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <QListWidgetItem>
#include <qtabwidget.h>
#include <qinputdialog.h>
#include <qclipboard.h>
#include <qlayout.h>
#include <qsplitter.h>
#include <qtoolbutton.h>
//Added by qt3to4:
#include <QShowEvent>
#include <QStringList>
#include <QPixmap>
#include <QMouseEvent>
#include <QCloseEvent>
#include <QList>
#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 "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;

	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( 500 );
}

/** */
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_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++)
	{
		if ( m_Mutex.tryLock() == false )
		{
			break;
		}

		if ( m_pMessageList  && !m_pMessageList->isEmpty() )
		{
			DCMsg = m_pMessageList->takeFirst();
		}
		else
		{
			DCMsg = 0;
		}

		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);

					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();
				break;
			}

			case DC_MESSAGE_NICKLIST:
			{
				DC_NickList( (CMessageNickList *) DCMsg );
				break;
			}

			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);
						
						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( tr("Parts: ") + nick );
			}
		}
		else
		{
			m_pHubChat->AddStatus( tr("Parts: ") + nick );
		}
	}

	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;
	}
	
	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
		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 )
		{
			TabWidget_CHAT->addTab( chat, qnick );
		}

		if ( bShowChat || bLocal )
		{
			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();
		}

		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() )
	{
		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 ( (chat->isVisible() == false) &&
	     (g_pConfig->GetSendHidePrivateChatToPublicChat()) )
	{
		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);
	
	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() )
	{
		if ( g_pConfig->GetChatShowJoinsAndPartsOnlyFav() )
		{
			if ( g_pUsersList->isNickInList( nick ) )
			{
				m_pHubChat->AddStatus( tr("Joins: ") + nick );
			}
		}
		else
		{
			m_pHubChat->AddStatus( tr("Joins: ") + nick );
		}
	}

	// show rejoin on private chat
	if ( chat != 0 )
	{
		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() == 1 )
	{
		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 * 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 == 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() == 0 )
		{
			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());
}
