/***************************************************************************
                          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>
#include <qtooltip.h>
//Added by qt3to4:
#include <QResizeEvent>
#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 <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 )
{
	int idx;
	
	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) );
	}

	m_pUserListModel = new UserListModel();
	TreeView_USERLIST->setModel( m_pUserListModel );

	// QT3 set the ColumnWidthMode to manual, we will take care of this task
	// in QT4, QHeaderView::Interactive is default anyway
	// QHeaderView::Fixed would disable user resizing
	for( idx = 0; idx < TreeView_USERLIST->header()->count(); idx++ )
	{
		TreeView_USERLIST->header()->setResizeMode( idx, QHeaderView::Interactive );
	}

	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;
	
	delete m_pUserListModel;
	m_pUserListModel = 0;
	
	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( TreeView_USERLIST->header(),SIGNAL(sectionResized(int, int, int)), this, SLOT(slotSaveColumnWidth(int, int, int)) );
	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()) );
	}

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

	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 != 0) && !isMinimized() && !isMaximized() )
			{
				m_pContainerWindow->setGeometry( m_pContainerWindow->x(), m_pContainerWindow->y(), (*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)[QString().setNum(eclcNICK)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcNICK, (*map)[QString().setNum(eclcNICK)].toInt() );
		if ( (*map)[QString().setNum(eclcCOMMENT)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcCOMMENT, (*map)[QString().setNum(eclcCOMMENT)].toInt() );
		if ( (*map)[QString().setNum(eclcTAG)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcTAG, (*map)[QString().setNum(eclcTAG)].toInt() );
		if ( (*map)[QString().setNum(eclcEMAIL)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcEMAIL, (*map)[QString().setNum(eclcEMAIL)].toInt() );
		if ( (*map)[QString().setNum(eclcSPEED)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcSPEED, (*map)[QString().setNum(eclcSPEED)].toInt() );
		if ( (*map)[QString().setNum(eclcSHARE)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcSHARE, (*map)[QString().setNum(eclcSHARE)].toInt() );
		if ( (*map)[QString().setNum(eclcIP)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcIP, (*map)[QString().setNum(eclcIP)].toInt() );
		if ( (*map)[QString().setNum(eclcLOCKPK)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcLOCKPK, (*map)[QString().setNum(eclcLOCKPK)].toInt() );
		if ( (*map)[QString().setNum(eclcSUPPORTS)].toInt() > 0 )
			g_pConfig->SetClientColumnWidth( eclcSUPPORTS, (*map)[QString().setNum(eclcSUPPORTS)].toInt() );
		
		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);
			}
		}
	}

	// set viewable columns
	ResizeListViewColumn();

	// load column order from config
	if ( g_pConfig->GetMap("USERLISTCOLUMNORDER",map) )
	{
		QByteArray ba = QByteArray::fromBase64(map->value(QString("USERLISTHEADERSTATE")).toAscii());
		TreeView_USERLIST->header()->restoreState(ba);
		
		/* for ( int i = 0; i < TreeView_USERLIST->header()->count(); i++ )
		{
			int newpos = (*map)[QString().setNum(i)].toInt();
			printf( "column %d is at %d\n", i, newpos );
			
			if ( newpos >= 0 )
			{
				TreeView_USERLIST->header()->moveSection( i, newpos );
			}
		} */
	}
}

/** */
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().setNum(m_pUserListModel->getSortColumn());
	(*map)["SORTORDER"]  = DCGuiUtils::SortOrderName( m_pUserListModel->getSortOrder() );

	SaveClientColumn();

	// save column order to config
	//for (int i = 0; i < TreeView_USERLIST->header()->count(); i++ )
	//{
	//	printf("Column %d is at position %d\n", i, TreeView_USERLIST->header()->visualIndex(i) );
	//}
	
	g_pConfig->GetMap("USERLISTCOLUMNORDER",map);
	//printf("TreeView_USERLIST->header()->saveState().size()=%d\n", TreeView_USERLIST->header()->saveState().size());
	//printf("TreeView_USERLIST->header()->saveState().toBase64().data()=%s\n", TreeView_USERLIST->header()->saveState().toBase64().data());
	(*map)[QString("USERLISTHEADERSTATE")] = QString(TreeView_USERLIST->header()->saveState().toBase64().data());
	
	// kept for backwards compatibility with QT3 client
	for ( int i = 0; i < TreeView_USERLIST->header()->count(); i++ )
	{
		(*map)[QString().setNum(i)]     = QString().setNum(TreeView_USERLIST->header()->visualIndex(i));
	}
	
	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::SaveClientColumn()
{
	StringMap * map;

	g_pConfig->GetMap("USERLISTVIEW",map);
	(*map)[QString().setNum(eclcNICK)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcNICK ));
	(*map)[QString().setNum(eclcCOMMENT)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcCOMMENT ));
	(*map)[QString().setNum(eclcTAG)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcTAG ));
	(*map)[QString().setNum(eclcEMAIL)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcEMAIL ));
	(*map)[QString().setNum(eclcSPEED)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcSPEED ));
	(*map)[QString().setNum(eclcSHARE)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcSHARE ));
	(*map)[QString().setNum(eclcIP)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcIP ));
	(*map)[QString().setNum(eclcLOCKPK)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcLOCKPK ));
	(*map)[QString().setNum(eclcSUPPORTS)] = QString().setNum(g_pConfig->GetClientColumnWidth( eclcSUPPORTS ));
	
	QList<int> sizes = Splitter_CLIENT->sizes();
	int index = Splitter_CLIENT->indexOf( Frame_USERLIST );
	if ( index < sizes.size() )
	{
		(*map)["WIDTH"] = QString().setNum(sizes[index]);
	}
}

/** */
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()
{
} */

/** event filter */
bool DCClient::eventFilter(QObject* object, QEvent* event)
{
	if((event->type() == QEvent::Close)&&((DCClient*)object!=this))
	{
		QCloseEvent* e=(QCloseEvent*)event;

		if ( m_pHubChat == object )
		{
			e->ignore();
			return true;
		}

		DCChat* pView=(DCChat*)object;

		CloseChat(pView);
	}
	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 the columns are resized on show() */
void DCClient::showEvent( QShowEvent * event )
{
	QWidget::showEvent( event );

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

/** resize event handler */
void DCClient::resizeEvent( QResizeEvent * /* event */ )
{
	ResizeListViewColumn();
}

/** close event - remove client from connection manager before accepting */
void DCClient::closeEvent( QCloseEvent * event )
{
	g_pConnectionManager->HubClosing( this );
	
	DeInitDocument();
	
	event->accept();
}

/** */
void DCClient::UpdateClientColumn( bool enabled, int index, int width )
{
	if ( enabled == false )
	{
		// TreeView_USERLIST->setColumnWidth( index, 0 );
		// TreeView_USERLIST->header()->setResizeMode( index, QHeaderView::Fixed );
		TreeView_USERLIST->hideColumn( index );
	}
	else
	{
		if ( width < 10 ) { width = 10; } // otherwise newly-enabled columns remain hidden
		TreeView_USERLIST->showColumn( index );
		TreeView_USERLIST->setColumnWidth( index, width );
	}
}

/** resize the ListView columns */
void DCClient::ResizeListViewColumn()
{
	int width,i;

//	printf("%d\n",g_pConfig->GetClientColumnWidth( eclcNICK ));
	if ( g_pConfig->GetClientColumnWidth( eclcNICK ) < 30 )
	{
		i     = 36;
		width = TreeView_USERLIST->width();

		if ( TreeView_USERLIST->verticalScrollBar()->isVisible() )
			width -= (TreeView_USERLIST->verticalScrollBar()->size().width()+4);

		if ( width > 0 )
		{
			if ( g_pConfig->GetClientColumn( eclcCOMMENT ) == false )
				i -= 5;
			if ( g_pConfig->GetClientColumn( eclcTAG ) == false )
				i -= 4;
			if ( g_pConfig->GetClientColumn( eclcSPEED ) == false )
				i -= 2;
			if ( g_pConfig->GetClientColumn( eclcEMAIL ) == false )
				i -= 3;
			if ( g_pConfig->GetClientColumn( eclcSHARE ) == false )
				i -= 4;
			if ( g_pConfig->GetClientColumn( eclcIP ) == false )
				i -= 4;
			if ( g_pConfig->GetClientColumn( eclcLOCKPK ) == false )
				i -= 4;
			if ( g_pConfig->GetClientColumn( eclcSUPPORTS ) == false )
				i -= 4;

			g_pConfig->SetClientColumnWidth( eclcCOMMENT,  (width*5)/i );
			g_pConfig->SetClientColumnWidth( eclcTAG,      (width*4)/i );
			g_pConfig->SetClientColumnWidth( eclcSPEED,    (width*2)/i );
			g_pConfig->SetClientColumnWidth( eclcEMAIL,    (width*3)/i );
			g_pConfig->SetClientColumnWidth( eclcSHARE,    (width*4)/i );
			g_pConfig->SetClientColumnWidth( eclcIP,       (width*4)/i );
			g_pConfig->SetClientColumnWidth( eclcLOCKPK,   (width*4)/i );
			g_pConfig->SetClientColumnWidth( eclcSUPPORTS, (width*4)/i );
			g_pConfig->SetClientColumnWidth( eclcNICK,     (width*6)/i );
		}
	}

	if ( g_pConfig->GetClientColumnWidth( eclcNICK ) > 0 )
	{
		UpdateClientColumn( true,                                       0, g_pConfig->GetClientColumnWidth( eclcNICK ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcCOMMENT ),  1, g_pConfig->GetClientColumnWidth( eclcCOMMENT ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcTAG ),      2, g_pConfig->GetClientColumnWidth( eclcTAG ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcSPEED ),    3, g_pConfig->GetClientColumnWidth( eclcSPEED ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcEMAIL ),    4, g_pConfig->GetClientColumnWidth( eclcEMAIL ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcSHARE ),    5, g_pConfig->GetClientColumnWidth( eclcSHARE ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcIP ),       6, g_pConfig->GetClientColumnWidth( eclcIP ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcLOCKPK ),   7, g_pConfig->GetClientColumnWidth( eclcLOCKPK ) );
		UpdateClientColumn( g_pConfig->GetClientColumn( eclcSUPPORTS ), 8, g_pConfig->GetClientColumnWidth( eclcSUPPORTS ) );
	}
}

/** */
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()
{
	TextLabel_USERCOUNT->setText( QString().setNum(m_pUserListModel->rowCount( QModelIndex() )) );
	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;
				
				// fill in the IP address column for the given nick
				m_pUserListModel->updateIP( msg->m_sNick.Data(), msg->m_sIP.Data() );
				
				if ( msg->m_sNick == GetNick() )
				{
					m_pHubChat->AddStatus(tr("Hub gave us our IP: ") + QString::fromAscii(msg->m_sIP.Data()));
					
					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."));
					}
				}
				
				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;

	m_Mutex.lock();

	if ( DCMessage != NULL )
	{
		if ( m_pMessageList != NULL )
		{
			m_pMessageList->append(DCMessage);
			err = 0;
		}
		else
		{
			err = -1;
		}
	}
	else
	{
		err = -1;
	}

	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);
			g_pConnectionManager->GetMdiArea()->addSubWindow(chat);
			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 nicks to not open chat windows
		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 = false;
			}
		}
		
		if ( m_bUseTabWidget )
		{
			TabWidget_CHAT->addTab( chat, qnick );
		}

		if ( bShowChat || bLocal )
		{
			if ( m_bUseTabWidget )
				TabWidget_CHAT->setCurrentWidget(chat);
			else
				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
				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);
			
			if ( (msg->context & umc->m_nContext & euccHub) == euccHub )
			{
				umc->m_nContext -= euccHub;
			}
			if ( (msg->context & umc->m_nContext & euccChat) == euccChat )
			{
				umc->m_nContext -= euccChat;
			}
			if ( (msg->context & umc->m_nContext & euccSearch) == euccSearch )
			{
				umc->m_nContext -= euccSearch;
			}
			if ( (msg->context & umc->m_nContext & euccFilelist) == euccFilelist )
			{
				umc->m_nContext -= euccFilelist;
			}
			
			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 )
			{
				if ( (msg->context & umc->m_nContext & euccHub) == euccHub )
				{
					umc->m_nContext -= euccHub;
				}
				if ( (msg->context & umc->m_nContext & euccChat) == euccChat )
				{
					umc->m_nContext -= euccChat;
				}
				if ( (msg->context & umc->m_nContext & euccSearch) == euccSearch )
				{
					umc->m_nContext -= euccSearch;
				}
				if ( (msg->context & umc->m_nContext & euccFilelist) == euccFilelist )
				{
					umc->m_nContext -= euccFilelist;
				}
				
				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 = m_pUserListModel->index( index.row(), COLUMN_NICK, QModelIndex() );
	QString nick = m_pUserListModel->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
		{
			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( m_pUserListModel->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 );
	QMenu * mcols = m->addMenu( tr("Columns") );
	QAction * columns[9];
	
	for ( int i = 0; i < 9; ++i )
	{
		int index = TreeView_USERLIST->header()->logicalIndex(i);
		columns[i] = mcols->addAction( m_pUserListModel->headerData( index, Qt::Horizontal ).toString() );
		columns[i]->setCheckable(true);
		if ( index == 0 )
		{
			columns[i]->setEnabled(false);
		}
		columns[i]->setChecked( TreeView_USERLIST->header()->isSectionHidden( index ) == false );
	}

	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;
	QAction * getip = 0;
	
	if ( UserList()->IsAdmin(GetNick()) )
	{
		kick = DCMenuHandler::addAction( m, emiKICK, (numSelected > 0) );
		forcemove = DCMenuHandler::addAction( m, emiFORCE_MOVE, (numSelected > 0) );
		getip = DCMenuHandler::addAction( m, emiGET_USER_IP, (numSelected > 0) && GetSupportsUserIP2() );
	}
	
	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( m_pUserListModel->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 <= 9; j++ )
			{
				int col = TreeView_USERLIST->header()->logicalIndex(j);
				if ( TreeView_USERLIST->header()->isSectionHidden(col) == false )
				{
					QModelIndex index = m_pUserListModel->index( it->row(), col, QModelIndex() );
					s += m_pUserListModel->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 == getip )
	{
		for ( QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it )
		{
			CString userip = "$UserIP ";
			userip += it->toAscii().constData();
			userip += '|';
			SendString( userip );
		}
	}
	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());
			}
		}
	}
	else
	{
		for ( int i = 0; i < 9; ++i )
		{
			if ( chosen == columns[i] )
			{
				int index = TreeView_USERLIST->header()->logicalIndex(i);
				bool show;
				int width;
				if ( TreeView_USERLIST->header()->isSectionHidden(index) )
				{
					show = true;
					int visible = 0;
					for ( int j = 0; j < 9; ++j )
					{
						if ( TreeView_USERLIST->header()->isSectionHidden(j) == false )
						{
							++visible;
						}
					}
					
					/*
					 * QT4 does not use width == 0 to hide columns, but
					 * both QT3 and QT4 versions are using the same settings.
					 */
					if ( visible > 0 )
					{
						width = TreeView_USERLIST->width() / visible;
					}
					else
					{
						width = 40;
					}
					
					UpdateClientColumn( show, index, width );
				}
				else
				{
					show = false;
					UpdateClientColumn( show, index, TreeView_USERLIST->columnWidth(index) );
				}
				
				/* column widths appear to be saved automatically since a slot is connected to the header */
				eClientColumn mapped_section;
				if ( index == 0 )
				{
					mapped_section = eclcNICK;
				}
				else
				{
					mapped_section = eClientColumn( index - 1 );
				}
				
				/* speed and email are transposed in the enum */
				if ( mapped_section == eclcEMAIL )
				{
					mapped_section = eclcSPEED;
				}
				else if ( mapped_section == eclcSPEED )
				{
					mapped_section = eclcEMAIL;
				}
				
				g_pConfig->SetClientColumn( mapped_section, show );
						
				break;
			}
		}
	}
}

void DCClient::slotSaveColumnWidth(int section, int /*oldSize*/, int newSize)
{
	eClientColumn mapped_section;

	if (section == 0)
		mapped_section = eclcNICK;
	else
		mapped_section = eClientColumn(section - 1);
	
	/* speed and email are the wrong way around in the enum */
	if ( mapped_section == eclcEMAIL )
	{
		mapped_section = eclcSPEED;
	}
	else if ( mapped_section == eclcSPEED )
	{
		mapped_section = eclcEMAIL;
	}
	
	g_pConfig->SetClientColumnWidth( mapped_section, newSize );
}

/** */
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;
		int line = 0;
		
		for ( ; ; )
		{
			loc = usercommand.indexOf( "%[line:", loc );
			
			if ( loc == -1 )
			{
				break;
			}
			
			int end = usercommand.indexOf( "]", loc );
			
			// 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();
		}
	}
					
	{
		struct tm * t;
		time_t ti;
		char timed[2048];
						
		ti = time(0);
		t  = localtime(&ti);
		strftime(timed, sizeof(timed), usercommand.toAscii().constData(), t);
		usercommand = timed;
	}
	
	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;
			}
			
			//this->SendChat( this->GetNick(), chat.toAscii().constData() );
			chatobject->TextEdit_CHATINPUT->setPlainText(chat);
			chatobject->SendMessage( QString() );
			
			// 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;
	}
}

/** */
void DCClient::jumpToNick( const QString & nick )
{
	if ( nick.isEmpty() )
	{
		return;
	}
	
	QModelIndex topLeft = m_pUserListModel->indexForNick( nick );
	QModelIndex bottomRight = m_pUserListModel->index( topLeft.row(), COLUMN_SUPPORTS, QModelIndex() );
	QItemSelection sel( topLeft, bottomRight );
	TreeView_USERLIST->selectionModel()->select( sel, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current );
	TreeView_USERLIST->scrollTo( topLeft, QAbstractItemView::EnsureVisible );
}

/** */
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;
	
	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 = subMenuNames.first();
				QMenu * parentMenu = menu;
				
				for ( int j = 0; j < subMenuNames.size(); j++ )
				{
					if ( createdmenus.contains(subMenuKey) )
					{
						parentMenu = createdmenus[subMenuKey];
						submenu = parentMenu;
					}
					else
					{
						
						parentMenu = parentMenu->addMenu( subMenuNames[j] );
						createdmenus[subMenuKey] = parentMenu;
						submenu = parentMenu;
					}
					
					subMenuKey += '\\';
					subMenuKey += subMenuNames[j];
				}
			}
			
			if ( allcommands[i]->m_nType == euctSeparator )
			{
				// no need to keep the QAction returned
				submenu->addSeparator();
			}
			else if ( (allcommands[i]->m_nType == euctRaw) || (allcommands[i]->m_nType == euctRawOnce) )
			{
				added.insert( submenu->addAction(name), allcommands[i] );
			}
			//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);
	}
}
