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

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <qdatetime.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qclipboard.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include <qimage.h>
#include <qregexp.h>
#include <qpopupmenu.h>
#include <qcursor.h>
#include <qcombobox.h>
#include <qlayout.h>
#include <qprocess.h>
#include <qglobal.h>
#include <qstatusbar.h>
#include <qtooltip.h>
#include <qfile.h>
#include <qfiledialog.h>
#include <qsplitter.h>
#include <qinputdialog.h>
#include <qtabwidget.h>

#include <dcgui.h>
#include <dcevent.h>
#include <dcclient.h>
#include <dcmenuhandler.h>
#include <dcconfig.h>
#include <dctranslator.h>
#include <cdialogpicturemap.h>
#include <dcfiletool.h>
#include <dchublistmanager.h>
#include <dcconnectionmanager.h>
#include <dcuserslist.h>
#include <dcqtextedit.h>
#include <dciconloader.h>

#include "dcchat.h"
#include "dchubsearch.h"

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

/** the link list*/
const char * p_LinkList[] = {
	"HTTP://",
	"HTTPS://",
	"NEWS://",
	"FILE://",
	"WWW.",
	"FTP://",
	"FTP.",
	"HKP://",
	"LDAP://",
	"DCFILE://",
	"DCHUB://",
	"DCHUBS://",	
	"DCCMD://",
	"MAGNET:",
	NULL
};

#define MAX_HISTORY_COUNT 25

/** */
DCChat::DCChat( QWidget * parent, const char *name, int wflags, DCClient * client, bool bprivate ) : DCDialogChat(parent, name, wflags)
{
	ASSERT(client);

	// set default icon
	setIcon( g_pIconLoader->GetPixmap(eiGLOBE) );

	m_pParent      = parent;
	m_bPrivateChat = bprivate;
	m_pClient      = client;
	m_bSendAway    = TRUE;
	m_eSecureState = esecsNONE;
	m_pCurrentHistory = 0;

	m_sTimeStamp  = QDate::currentDate().toString("yyyyMMdd").ascii();
	m_sTimeStamp += "-";
	m_sTimeStamp += QTime::currentTime().toString("hhmmss").ascii();

	m_nTabStart   = -1;
	m_nTabEnd     = -1;
	m_nTabPressed = 0;
	m_sTabSaved   = "";
	m_sTabNick    = "";
	m_bFirstInit  = TRUE;
	
	InitDocument();
}

/** */
DCChat::~DCChat()
{
}

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

	m_pStatusBar = new QStatusBar(this);
	m_pStatusBar->setSizeGripEnabled(FALSE);
	DCDialogChatLayout->addWidget( m_pStatusBar, 1, 0 );
	m_pStatusCrypt = new QLabel(m_pStatusBar);
	m_pStatusBar->addWidget(m_pStatusCrypt);

	SetCrypt(esecsNONE);

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

/*	if ( g_pConfig->ShowChatStatusBar() == FALSE )
	{
		m_pStatusBar->hide();
	}
*/
	if ( g_pConfig->GetShowChatSendButton() )
	{
		connect( PushButton_SEND, SIGNAL(clicked()), this, SLOT(slotSendMessage()) );
		slotTextChangedChatInput();
	}
	else
	{
		// remove send button and recreate layout
		delete PushButton_SEND;
		PushButton_SEND = NULL;
		delete Frame_SEND->layout();
		QGridLayout * gl = new QGridLayout( Frame_SEND, 1, 1, 1, 1, "");
		gl->addWidget( TextEdit_CHATINPUT, 0, 0 );
	}

	m_pTextEdit_CHATOUTPUT = new DCQTextEdit( Frame_OUTPUT, "TextEdit_CHATOUTPUT" );
	m_pTextEdit_CHATOUTPUT->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)5, 0, 0, m_pTextEdit_CHATOUTPUT->sizePolicy().hasHeightForWidth() ) );
	m_pTextEdit_CHATOUTPUT->setMinimumSize( QSize( 0, 60 ) );
	m_pTextEdit_CHATOUTPUT->setTextFormat( QTextEdit::RichText );
	m_pTextEdit_CHATOUTPUT->setWordWrap( QTextEdit::WidgetWidth );
	m_pTextEdit_CHATOUTPUT->setWrapPolicy( QTextEdit::AtWordOrDocumentBoundary );
    	m_pTextEdit_CHATOUTPUT->setReadOnly( TRUE );
	Frame_OUTPUTLayout->addWidget(m_pTextEdit_CHATOUTPUT,0,0);

	connect( m_pTextEdit_CHATOUTPUT, SIGNAL(rightButtonClicked( const QPoint& )), this, SLOT(slotRightButtonClickedChatOutput( const QPoint& )) );
#if QT_VERSION >= 0x030100
	connect( m_pTextEdit_CHATOUTPUT, SIGNAL(clicked( int, int )), this, SLOT(slotClickedChatOutput( int, int )) );
	connect( m_pTextEdit_CHATOUTPUT, SIGNAL(doubleClicked( int, int )), this, SLOT(slotDoubleClickedChatOutput( int, int )) );
#endif
	if ( PushButton_SEND )
	{
		connect( TextEdit_CHATINPUT, SIGNAL(textChanged()), this, SLOT(slotTextChangedChatInput()) );
	}

	installEventFilter(this);

	m_pTextEdit_CHATOUTPUT->installEventFilter(this);
	TextEdit_CHATINPUT->installEventFilter(this);
	TextEdit_CHATINPUT->setFocus();
}

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

		bool send = FALSE;
		if ( e->state() == AltButton )
		{
			if ( (e->key() == Key_S) && (g_pConfig->GetSendChat() == tr("Alt + S").ascii()) )
			{
				send = TRUE;
			}
			else if( ((e->key() == Key_Enter) || (e->key() == Key_Return )) &&
				   (g_pConfig->GetSendChat() == tr("Alt + Enter").ascii()))
			{
				send = TRUE;
			}
		}
		else if( (g_pConfig->GetSendChat() == tr("Ctrl + Enter").ascii()) &&
			 (e->state() == ControlButton) && ( e->key() == Key_Enter || e->key() == Key_Return) )
		{
			send = TRUE;
		}
		else if( (g_pConfig->GetSendChat() == tr("Enter").ascii() && (e->state() != ControlButton) &&
			 ( e->key() == Key_Enter || e->key() == Key_Return)) )
		{
			send = TRUE;
		}

		if( send == TRUE )
		{
			SendMessage();
			e->accept();
			return true;
		}
		else if ( e->state() == ControlButton )
		{
			CString * ps = 0;

			// check for history
			if ( (e->key() == Key_Down) && (m_pCurrentHistory != 0) )
			{
				ps = m_History.Next(m_pCurrentHistory);

			}
			else if ( e->key() == Key_Up )
			{
				ps = m_History.Forw(m_pCurrentHistory);

				if ( m_pCurrentHistory == 0 )
				{
					// save current text
					m_sHistoryTempString = TextEdit_CHATINPUT->text().ascii();
					// save current cursor position
					TextEdit_CHATINPUT->getCursorPosition( &m_nHistoryTempPara, &m_nHistoryTempIndex );
				}
			}

			if ( ps != 0 )
			{
				m_pCurrentHistory = ps;
				TextEdit_CHATINPUT->setText(m_pCurrentHistory->Data());
			}
			else if (e->key() == Key_Down)
			{
				m_pCurrentHistory = ps;
				TextEdit_CHATINPUT->setText(m_sHistoryTempString.Data());
				TextEdit_CHATINPUT->setCursorPosition( m_nHistoryTempPara, m_nHistoryTempIndex );
			}
		}

		if ( e->key() == Key_Tab )
		{
			NickCompletion();
			e->accept();
			return true;
		}
		else if ( m_nTabPressed != 0 )
		{
			// reset tab settings
			m_nTabStart   = -1;
			m_nTabEnd     = -1;
			m_nTabPressed = 0;
			m_sTabSaved   = "";
			//m_sTabNick    = "";
		}
	}
	else if ( event->type() == EVENT_TRANSLATION )
	{
		DC_TranslationEvent *e = (DC_TranslationEvent*)event;

		if ( e->m_bTranslate == TRUE )
		{
			AddStatus( (tr("Translation: ") + "'" + e->m_sOriginal + "' -> '" + e->m_sTranslation + "'").ascii() );
		}
		else
		{
			AddStatus( (tr("Translation failed: ") + "'" + e->m_sOriginal + "'").ascii() );
		}
	}
	else if ( (event->type() == QEvent::Resize) && (object == m_pTextEdit_CHATOUTPUT) )
	{
		bool bscroll, b;

		if ( m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maxValue() == m_pTextEdit_CHATOUTPUT->verticalScrollBar()->value() )
		{
			bscroll = TRUE;
		}
		else
		{
			bscroll = FALSE;
		}

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

		if ( bscroll )
		{
			m_pTextEdit_CHATOUTPUT->scrollToBottom();
			m_pTextEdit_CHATOUTPUT->moveCursor( QTextEdit::MoveEnd, FALSE );
		}
		
		return b;
	}

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

/** */
void DCChat::AddHistory( CString message )
{
	CString *ps;

	if ( m_History.Count() == MAX_HISTORY_COUNT )
	{
		ps = m_History.Next(0);
		m_History.Del(ps);
	}

	m_pCurrentHistory = 0;
	m_History.Add(new CString(message));
}

/** */
void DCChat::SetCrypt( eSecureState e )
{
	if ( e == esecsENCRYPTED )
	{
		QToolTip::remove(m_pStatusCrypt);
		QToolTip::add(m_pStatusCrypt, tr("Line is encrypted."));
		m_pStatusCrypt->setPixmap( g_pIconLoader->GetPixmap(eiSSL_YES) );
	}
	else
	{
		QToolTip::remove(m_pStatusCrypt);
		QToolTip::add(m_pStatusCrypt, tr("Line is not encrypted."));
		m_pStatusCrypt->setPixmap( g_pIconLoader->GetPixmap(eiSSL_NO) );
	}

	m_eSecureState = e;
}

/** */
void DCChat::slotTextChangedChatInput()
{
	if ( PushButton_SEND )
	{
		if ( TextEdit_CHATINPUT->text() != "" )
		{
			PushButton_SEND->setEnabled(TRUE);
		}
		else
		{
			PushButton_SEND->setEnabled(FALSE);
		}
	}
}

/** */
void DCChat::slotClickedChatOutput( int, int )
{
	int i;
	CString pressedLink;
	QPoint p;
	int x,y;
	DCFileTool filetool;

	p = m_pTextEdit_CHATOUTPUT->mapFromGlobal(QCursor::pos());
	m_pTextEdit_CHATOUTPUT->viewportToContents(p.x(),p.y(),x,y);
	p.setX(x);
	p.setY(y);
	pressedLink = m_pTextEdit_CHATOUTPUT->anchorAt(p).ascii();

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

	for(i=0;p_LinkList[i]!=0;i++)
	{
		if ( pressedLink.Left(CString(p_LinkList[i]).Length()).ToUpper() == p_LinkList[i] )
		{
			if ( p_LinkList[i] == CString("DCFILE://") )
			{
				// DC-File-Link
				QPopupMenu *m;
				int id;
				CString hubhost,hubname,nick,file;
				ulonglong size;

				if ( CUtils::ConvertDCLink( pressedLink, hubhost, hubname, nick, size, file ) == FALSE )
				{
					return;
				}

				m = new QPopupMenu(this);

				DCMenuHandler::InsertMenu( m, emiDOWNLOAD );
				DCMenuHandler::InsertMenu( m, emiDOWNLOAD_TO );
				DCMenuHandler::InsertMenu( m, emiDOWNLOAD_AS );
				DCMenuHandler::InsertMenu( m, emiDOWNLOAD_IN );

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

				delete m;

				if ( (id == emiDOWNLOAD) || (id == emiDOWNLOAD_AS) || (id == emiDOWNLOAD_TO) )
				{
					QString localrootpath = "";
					QString localname = "";

					// select downloadfolder for all selected files
					if ( id == emiDOWNLOAD_TO )
					{
						localrootpath = QFileDialog::getExistingDirectory( "", this, "bdf", tr("Select download folder"), TRUE );

						if ( localrootpath == "" )
							return;
					}

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

					if ( id == emiDOWNLOAD_AS )
					{
						localrootpath = QFileDialog::getSaveFileName( "", "", this, "bdf", tr("Select file for")+" "+localname );

						if ( localrootpath == "" )
							return;

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

						if ( (localrootpath=="") || (localname=="") )
							return;
					}

					// add transfer to the waitlist
					filetool.CheckFile( nick, hubname, hubhost,
							file, localname.ascii(), "", localrootpath.ascii(), eltFILE,
							size, "" );
				}
				else if ( id == emiDOWNLOAD_IN )
				{
					QString localrootpath;
					QString localname;
					QString localpath;

					if ( filetool.SelectFileSource( size, localname, localrootpath, localpath ) == FALSE )
					{
						return;
					}

					// add transfer to the waitlist
					filetool.CheckFile( nick, hubname, hubhost,
							  file, localname.ascii(), localpath.ascii(), localrootpath.ascii(), eltFILE,
					  		size, "", TRUE);
				}
			}
			else if ( p_LinkList[i] == CString("DCHUB://") )
			{
				pressedLink = pressedLink.Right( pressedLink.Length()-8 );
				// remove all '/' chars
				pressedLink = pressedLink.Replace('/',"");

				g_pConnectionManager->Connect( pressedLink, pressedLink );
			}
			else if ( p_LinkList[i] == CString("DCHUBS://") )
			{
				pressedLink = pressedLink.Right( pressedLink.Length()-8 );
				// remove all '/' chars
				pressedLink = pressedLink.Replace('/',"");

				g_pConnectionManager->Connect( pressedLink, pressedLink, TRUE );
			}
			else if ( p_LinkList[i] == CString("DCCMD://") )
			{
				pressedLink = pressedLink.Right( pressedLink.Length()-8 );
			}
			else if ( p_LinkList[i] == CString("MAGNET:") )
			{
				//magnet:?xt=urn:tree:tiger:EOSA5AGTL5SD3VWCF3R2OH2WMGXV3S3R7SYN4YA&xl=708780032&dn=FC-6-i386-disc1.iso
				// TTHs are always 39 characters long
				QPopupMenu *m = new QPopupMenu(this);
				int id, searchID, copyID, lookupID, i;
				QString tth;
				
				i = pressedLink.Find("xt=urn:tree:tiger:");
				if (i == -1)
				{
					i = pressedLink.Find("xt.1=urn:tree:tiger:");
					tth = pressedLink.Mid(i + 20, 39).Data();
				}
				else
				{
					tth = pressedLink.Mid(i + 18, 39).Data();
				}
				
				searchID = m->insertItem( QIconSet(g_pIconLoader->GetPixmap(eiFILEFIND)), QObject::tr("Search by TTH") );
				m->setItemEnabled(searchID, TRUE);
				copyID = m->insertItem( QIconSet(g_pIconLoader->GetPixmap(eiEDITCOPY)), QObject::tr("Copy TTH to clipboard") );
				m->setItemEnabled(copyID, TRUE);
				lookupID = m->insertItem( QIconSet(g_pIconLoader->GetPixmap(eiFILEFIND)), QObject::tr("Look up TTH at Bitzi.com") );
				m->setItemEnabled(lookupID, TRUE);
				
				id = m->exec(QCursor::pos());
				
				delete m;
				
				if (id == searchID)
				{
					g_pHubSearch->Combobox_SEARCH->setEditText(tth);
					g_pHubSearch->LineEdit_SEARCHSIZE->setText("0");
					g_pHubSearch->ComboBox_SEARCHTYPE->setCurrentItem(9);
					g_pHubSearch->ComboBox_SEARCHUNIT->setCurrentItem(0);
					g_pHubSearch->ComboBox_SEARCHLIMIT->setCurrentItem(0);
		
					if ( g_pHubSearch->isMinimized() )
					{
						g_pHubSearch->showNormal();
					}
					else if ( g_pHubSearch->isVisible()==FALSE )
					{
						g_pHubSearch->setEnabled(TRUE);
						g_pHubSearch->show();
					}

					if ( g_pHubSearch->TabWidget_HUBSEARCH->isVisible()==FALSE )
					{
						g_pHubSearch->TabWidget_HUBSEARCH->show();
					}
					
					// put search dialog above chat
					g_pHubSearch->raise();
					g_pHubSearch->setActiveWindow();
				}
				else if (id == copyID)
				{
					QApplication::clipboard()->setText(tth);
				}
				else if (id == lookupID)
				{
					// http://bitzi.com/lookup/tree:tiger:PKQRUF7BKROIPCX2XWTJ7XUKRXRPUMW5V6REAMY
					if ( g_pConfig->GetBrowser() != "" )
					{
						QProcess * pr = new QProcess();
						QByteArray buf;
						pr->addArgument(g_pConfig->GetBrowser().Data());
						pr->addArgument("http://bitzi.com/lookup/tree:tiger:" + tth);
						pr->launch(buf);
						delete pr;
					}
				}
				
			}
			else if ( g_pConfig->GetBrowser() != "" )
			{
				QProcess * pr = new QProcess();
				QByteArray buf;
				pr->addArgument(g_pConfig->GetBrowser().Data());
				pr->addArgument(pressedLink.Data());
				pr->launch(buf);
				delete pr;
			}
		}
	}
}

/** */
void DCChat::slotRightButtonClickedChatOutput( const QPoint& )
{
	int para;
	QString pressedPara, pressedNick, userCommand;
	CString cPressedNick;
	QPoint p;
	int x,y, nickStart, nickLen;
	int id;
	bool isNickOnline;
	QPopupMenu *m,*muser,*mslot;
	QString s;	
	CList<DC_UserMenuCommand> usercommands;
	DC_UserMenuCommand * umc = 0;
	
	// get the paragraph of text where the right click was
	p = m_pTextEdit_CHATOUTPUT->mapFromGlobal(QCursor::pos());
	m_pTextEdit_CHATOUTPUT->viewportToContents(p.x(),p.y(),x,y);
	p.setX(x);
	p.setY(y);
	para = m_pTextEdit_CHATOUTPUT->paragraphAt(p);
	pressedPara = m_pTextEdit_CHATOUTPUT->text(para);
	
	// cut out the nick
	nickStart = 4 + pressedPara.find("&lt;");
	nickLen = pressedPara.find("&gt;") - nickStart;
	
	// sanity check
	if ( (nickStart == 3) || (nickLen < 0) )
	{
		if (m_bPrivateChat)
		{
			pressedNick = QString(m_sNick.Data());
		}
		else
		{
			pressedNick = "Unknown";
		}
	}
	else
	{
		pressedNick = pressedPara.mid(nickStart, nickLen);
	}
	
	// check for joins / parts message
	if ( pressedNick == "VALKNUT" )
	{
		int lenJoin = QString(tr("Joins: ")).length();
		int lenPart = QString(tr("Parts: ")).length();
		int joinPos = pressedPara.find(tr("Joins: "));
		int partPos = pressedPara.find(tr("Parts: "));
		
		if ( joinPos == -1 )
		{
			nickStart = lenPart + partPos;
		}
		else
		{
			nickStart = lenJoin + joinPos;	
		}
		
		if ( (joinPos != -1) || (partPos != -1) )
		{
			nickLen = pressedPara.findRev("</span>") - nickStart;
			pressedNick = pressedPara.mid(nickStart, nickLen);
		}
	}
	
	// check for private chat
	if ( (pressedNick == "VALKNUT") && m_bPrivateChat )
	{
		pressedNick = QString(m_sNick.Data());
	}
	
	//printf("Debug pressedNick: %s\n",pressedNick.ascii());
	
	cPressedNick = CString(pressedNick.ascii());
	isNickOnline = m_pClient->UserList()->IsUserOnline(cPressedNick);
	
	// select the user in the userlist
	if (isNickOnline)
	{
		QListViewItem * item = m_pClient->ListView_USERLIST->findItem(pressedNick, 0);
		m_pClient->ListView_USERLIST->clearSelection();
		m_pClient->ListView_USERLIST->setSelected(item, TRUE);
		m_pClient->ListView_USERLIST->setCurrentItem(item);
		m_pClient->ListView_USERLIST->ensureItemVisible(item);
	}
	
	m = new QPopupMenu(this);
	
	muser = DCMenuHandler::InsertMenu( m , emisCHAT_RIGHTCLICK_USER, TRUE, pressedNick );
	DCMenuHandler::InsertMenu( muser, emiPRIVATE_CHAT, isNickOnline );
	DCMenuHandler::InsertMenu( muser, emiADD_FRIEND,  isNickOnline );
	DCMenuHandler::InsertMenu( muser, emiBROWSE_USER_FILES, isNickOnline );
	DCMenuHandler::InsertMenu( muser, emiSEPARATOR );
	
	mslot = DCMenuHandler::InsertMenu( muser, emiUPLOAD_SLOT, TRUE);
	DCMenuHandler::InsertMenu( mslot, emiADD, TRUE);
	DCMenuHandler::InsertMenu( mslot, emiADD_PERMANENT, TRUE);
	DCMenuHandler::InsertMenu( mslot, emiREMOVE, TRUE);
	
	DCMenuHandler::InsertMenu( muser, emiSEPARATOR );
	DCMenuHandler::InsertMenu( muser, emiKICK, (isNickOnline && m_pClient->UserList()->IsAdmin(m_pClient->GetNick())) );
	DCMenuHandler::InsertMenu( muser, emiFORCE_MOVE, (isNickOnline && m_pClient->UserList()->IsAdmin(m_pClient->GetNick())) );
	
	DCMenuHandler::InsertMenu( muser, emiSEPARATOR );
	
	g_pConfig->GetUserMenuCommands(&usercommands);
	
	umc = 0;
	while ( (umc=usercommands.Next(umc)) != 0 )
	{
		if ( (umc->m_nContext) == 2)
		{
			umc->m_nID = muser->insertItem(umc->m_sName.Data(), -1);
			muser->setItemEnabled( umc->m_nID, isNickOnline );
		}
	}
	
	DCMenuHandler::InsertMenu( m, emiSEPARATOR );
	
	DCMenuHandler::InsertMenu( m, emiINSERTSMILEY, g_pConfig->GetEnableEmoticons() );
	DCMenuHandler::InsertMenu( m, emiSEPARATOR );
	DCMenuHandler::InsertMenu( m, emiCOPY, m_pTextEdit_CHATOUTPUT->hasSelectedText() );
	DCMenuHandler::InsertMenu( m, emiCLEAR, TRUE );
	DCMenuHandler::InsertMenu( m, emiSELECT_ALL, TRUE );
	DCMenuHandler::InsertMenu( m, emiSEPARATOR );
	DCMenuHandler::InsertMenu( m, emiTRANSLATE, m_pTextEdit_CHATOUTPUT->hasSelectedText() );
	DCMenuHandler::InsertMenu( m, emiTRANSLATOR );

	DCMenuHandler::InsertMenu( m, emiSEPARATOR );
	DCMenuHandler::InsertMenu( m, emiREFRESH );
	DCMenuHandler::InsertMenu( m, emiSEPARATOR );

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

	DCMenuHandler::InsertMenu( m, emiSAVE );

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

	if ( m_bPrivateChat )
	{
		DCMenuHandler::InsertMenu( m, emiHIDE, m_bPrivateChat && !g_pConfig->GetShowChatInTab() );
		DCMenuHandler::InsertMenu( m, emiCLOSE, m_bPrivateChat );
	}
	
	if ( !m_bPrivateChat )
	{
		DCMenuHandler::InsertMenu( m, emiSEPARATOR );
		
		umc = 0;
		while ( (umc=usercommands.Next(umc)) != 0 )
		{
			if ( (umc->m_nContext & 1) == 1)
			{
				umc->m_nID = m->insertItem(umc->m_sName.Data(), -1);
			}
		}
	}
	
	id = m->exec(QCursor::pos());

	delete m;

	if ( id == emiINSERTSMILEY )
	{
		int x,y;
		CList<DC_EmoticonObject> * elist;

		elist = g_pConfig->EmoticonList();

		if ( elist )
		{
			CDialogPictureMap * dialog = new CDialogPictureMap(this);
			QPixmap p(g_pConfig->GetEmoticonImage());
			dialog->SetPixmap(p);

			if ( dialog->exec() == QDialog::Accepted )
			{
				dialog->GetXY(x,y);

				DC_EmoticonObject * EmoticonObject = 0;

				while ( (EmoticonObject=elist->Next(EmoticonObject)) != 0 )
				{
					if ( ((EmoticonObject->left<x) && (EmoticonObject->right>x)) &&
					     ((EmoticonObject->top<y)  && (EmoticonObject->bottom>y)) )
					{
						// damn hacks
						QString smiley = EmoticonObject->m_Text.Data();
						smiley = smiley.replace( "&lt;", "<" );
						smiley = smiley.replace( "&gt;", ">" );
						smiley = smiley.replace( "&amp;", "&" );
						smiley = smiley.replace( "&apos;", "\'" );
						smiley = smiley.replace( "&quot;", "\"" );
						
						TextEdit_CHATINPUT->insert(smiley);
						break;
					}
				}
			}

			delete dialog;
		}
	}
	else if ( id == emiTRANSLATE )
	{
		QString sc;
		QClipboard * cb = QApplication::clipboard();
		
		// save old text
		sc = cb->text();
		// copy to cb
		m_pTextEdit_CHATOUTPUT->copy();
		// get new text
		s = cb->text();
		// save old text
		cb->setText(sc);
		
		s = s.replace(QRegExp("<!--StartFragment-->"),"");
		g_pTranslator->Translate( this, m_sLanguage.Data(), s );
	}
	else if ( id == emiTRANSLATOR )
	{
		m_sLanguage = g_pTranslator->SelectLanguage(m_sLanguage);
	}
	else if ( id == emiCOPY )
	{
		m_pTextEdit_CHATOUTPUT->copy();
	}
	else if ( id == emiCLEAR )
	{
		m_pTextEdit_CHATOUTPUT->clear();
		AddStatus( tr("Chat Cleared.").ascii() );
	}
	else if ( id == emiSELECT_ALL )
	{
		m_pTextEdit_CHATOUTPUT->selectAll();
	}
	else if ( id == emiREQUEST_SECURE_CHAT )
	{
		m_pClient->SendPrivateMessage( m_pClient->GetNick(), m_sNick, "<request secchannel>" );
	}
	else if ( id == emiCLOSE_SECURE_CHAT )
	{
		m_pClient->SendPrivateMessage( m_pClient->GetNick(), m_sNick, "<close secchannel>" );
	}
	else if ( (id == emiDOCK) || (id == emiUNDOCK) )
	{
		if ( parentWidget() == 0 )
		{
			DCChat::reparent(m_pParent,QPoint(),TRUE);

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

				p->insertTab(this,m_sLabel);
				p->showPage(this);
			}

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

				m_sLabel = p->tabLabel(this);
				p->removePage(this);
			}

			DCChat::reparent(0,QPoint(),TRUE);

			move(0,0);

			// set default icon
			setIcon( g_pIconLoader->GetPixmap(eiGLOBE) );
		}
	}
	else if ( id == emiSAVE )
	{
		QString s = QFileDialog::getSaveFileName(
			"",
			"HTML (*.html *.htm)",
			this,
			"save file dialog",
			tr("Choose a filename to save under") );

		if ( s != "" )
		{
			QFile f(s);

			if ( f.open(IO_WriteOnly) == TRUE )
			{
				f.writeBlock(m_pTextEdit_CHATOUTPUT->text(),m_pTextEdit_CHATOUTPUT->text().length());
				f.close();
			}
		}
	}
	else if ( id == emiREFRESH )
	{
		if ( CFileManager::Instance()->CreateShareList() )
			s = tr("Refresh share in progress.");
		else
			s = tr("Refresh share already in progress.");
		AddStatus(s.ascii());
	}
	else if ( id == emiCLOSE )
	{
		close();
	}
	else if ( id == emiHIDE )
	{
		setEnabled(FALSE);
		hide();
	}
	else if ( id == emiPRIVATE_CHAT )
	{
		m_pClient->DC_PrivateChat( pressedNick, "", "", TRUE );
	}
	else if ( id == emiADD_FRIEND )
	{
		g_pUsersList->AddFriend( cPressedNick,
				m_pClient->GetHubName(), m_pClient->GetIP(), "" );
	}
	else if ( id == emiBROWSE_USER_FILES )
	{
		g_pTransferView->DLM_QueueAdd( cPressedNick, m_pClient->GetHubName(),
				m_pClient->GetIP()+":"+QString().setNum(m_pClient->GetPort()).ascii(),
				DC_USER_FILELIST_HE3, DC_USER_FILELIST_HE3, "", "", eltBUFFER,
				0, 0, 0, ""  );
	}
	else if ( id == emiKICK )
	{
		QString kickMessage;
		
		if ( !m_pClient->GetOPKickMessage(kickMessage) )
		{
			return;
		}
		m_pClient->OPKick( pressedNick, kickMessage );
		
	}
	else if ( id == emiFORCE_MOVE )
	{
		QString host, message;
		if ( !m_pClient->GetOPForceMoveMessage(message,host) )
		{
			return;
		}
		m_pClient->OPForceMove(pressedNick, message, host);
	}
	else if ( id == emiADD )
	{
		g_pTransferView->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 1);
	}
	else if ( id == emiADD_PERMANENT )
	{
		g_pTransferView->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 0);
		g_pTransferView->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 0, TRUE);
	}
	else if ( id == emiREMOVE )
	{
		g_pTransferView->DLM_AddUserSlot(cPressedNick, m_pClient->GetHubName(), 0);
	}
	else
	{
		umc = 0;
		while ( (umc=usercommands.Next(umc)) != 0 )
		{
			if ( id == umc->m_nID )
			{
				userCommand = m_pClient->replaceCommandTags( umc->m_sCommand.Data(), pressedNick );
				
				AddStatus(userCommand.ascii());
				m_pClient->SendString(userCommand.ascii());
			}
		}
	}
}

/** */
bool DCChat::close( bool alsoDelete )
{
	// re-set the wflags ...
	setWFlags(WDestructiveClose);
	return QWidget::close(alsoDelete);
}

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

	if ( !m_bFirstInit )
		return;
		
	m_bFirstInit = FALSE;

/*	QValueList<int> list = Splitter3->sizes();
	QValueList<int>::Iterator it = list.begin();
	printf("SP: %d\n",*it);
	++it;
	printf("SP: %d\n",*it);*/
	
	// resize splitter
	QValueList<int> list = Splitter3->sizes();
	QValueList<int>::Iterator it = list.begin();
	i = *it;
	++it;
	i += *it;
	i -= TextEdit_CHATINPUT->minimumHeight()*2;
	it = list.begin();
	*it = i;
	++it;
	*it = TextEdit_CHATINPUT->minimumHeight()*2;
	Splitter3->setSizes(list);
	
	// update view
	update();
}

/** */
void DCChat::slotSendMessage()
{
	SendMessage();
}

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

	if ( nick != "" )
	{
		setCaption(tr("Private Chat:")+" "+QString::fromUtf8(nick.Data())+" ["+QString::fromUtf8(hubname.Data())+"]");
	}
	else
	{
		setCaption(tr("Chat:")+" ["+QString::fromUtf8(hubname.Data())+"]");
	}
}

/** */
CString DCChat::GetTimeStamp()
{
	CString s = "";

	if ( ((g_pConfig->GetTimeStamp(etsPRIVATECHAT) == TRUE) && (m_bPrivateChat)) ||
	     ((g_pConfig->GetTimeStamp(etsHUBCHAT) == TRUE) && (!m_bPrivateChat)) )
	{
		s = QTime::currentTime().toString("[hh:mm:ss]").ascii();
	}

	if ( m_bPrivateChat )
		s = "<font color=\"" + g_pConfig->GetChatColor(eccCHATTIMESTAMP) + "\">" + s + "</font> ";
	else
		s = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATTIMESTAMP) + "\">" + s + "</font> ";

	return s;
}

/** */
void DCChat::AddOutput( CString message )
{
	bool bscroll;
	CString s;

	if ( m_pTextEdit_CHATOUTPUT->verticalScrollBar()->maxValue() == m_pTextEdit_CHATOUTPUT->verticalScrollBar()->value() )
	{
		bscroll = TRUE;
	}
	else
	{
		bscroll = FALSE;
	}

	// convert newline
	message = message.Replace( "\xa\\r", "\xa" );
	message = message.Replace( "\xa", "<br />" );
	
	// convert tabs to spaces
	message = message.Replace( "	", "&nbsp;&nbsp;&nbsp;&nbsp;" );

	if ( (g_pConfig->GetChatMaxParagraph()!= 0) && (m_pTextEdit_CHATOUTPUT->paragraphs() > (g_pConfig->GetChatMaxParagraph()+5)) )
	{
		m_pTextEdit_CHATOUTPUT->removeParagraph(0);
		m_pTextEdit_CHATOUTPUT->removeParagraph(0);
		m_pTextEdit_CHATOUTPUT->removeParagraph(0);
		m_pTextEdit_CHATOUTPUT->removeParagraph(0);
		m_pTextEdit_CHATOUTPUT->removeParagraph(0);
	}
	
	m_pTextEdit_CHATOUTPUT->append( QString::fromUtf8(message.Data()) );

	if ( bscroll )
	{
		m_pTextEdit_CHATOUTPUT->scrollToBottom();
		m_pTextEdit_CHATOUTPUT->moveCursor( QTextEdit::MoveEnd, FALSE );
	}

#if QT_VERSION >= 304
	message += "<br />";
#endif

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

				if ( g_pConfig->GetLogChatOption(elcoAPPENDHUBNAME) )
					s += "_" + m_pClient->GetHubName();

				if ( g_pConfig->GetLogChatOption(elcoAPPENDHUBHOST) )
					s += "_" + m_pClient->GetIP();

				if ( g_pConfig->GetLogChatOption(elcoAPPENDDATE) )
 					s += "_" + m_sTimeStamp;

				// damn hacking ;-)
				s = s.Replace('/',"");
				s = s.Replace('\\',"");
				s = s.Replace(':',"");

				s = g_pConfig->GetChatLogPath() + s + ".html";

				QFile f(s.Data());

				if ( f.open(IO_WriteOnly | IO_Append) == TRUE )
				{
					// add newline
					message += "\n";

					f.writeBlock(message.Data(),message.Length());
					f.close();
				}
			}
		}
	}
}

/** */
void DCChat::AddStatus( CString message, bool show )
{
	CString timestamp;

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

	if ( (g_pConfig->GetShowStatusMessage() == TRUE) || (show) )
	{
		timestamp = GetTimeStamp();

		// convert newline
		message = message.Replace( "\\r\xa", "\xa" );

#if QT_VERSION < 304
		if ( message.Find('\xa',0) == -1 )
			message += "<br />";
#endif
		message = message.Replace( "\xa", "<br />" );

		if ( m_bPrivateChat )
			message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATSTATUSTEXT) + "\">" + message + "</font>";
		else
			message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATSTATUSTEXT) + "\">" + message + "</font>";

		if ( m_bPrivateChat )
			message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATSTATUSNICK) + "\">" + "<b>&lt;VALKNUT&gt;</b> " + "</font>" + message;
		else
			message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATSTATUSNICK) + "\">" + "<b>&lt;VALKNUT&gt;</b> " + "</font>" + message;

		AddOutput( timestamp + message );
	}
}

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

	if ( msg->m_sSrcNick == "" )
		AddMessage( msg->m_sSrcNick.Data(), msg->m_sMessage.Data(), bremote, forward );
	else
		AddMessage( msg->m_sMultiSrcNick.Data(), msg->m_sMessage.Data(), bremote, forward );
}

/** */
void DCChat::AddMessage( CString nick, CString message, bool bremote, bool forward )
{
	unsigned int len;
	bool b;
	CString msg,s,s1,timestamp;
	int index;
	int i1,i2;
	bool b1,b2;
	bool bSay = FALSE;
	bool bMe = FALSE;
	bool isOP = m_pClient->UserList()->IsAdmin( nick );

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

	if ( message == "" )
	{
		return;
	}

	if ( CheckForData(message) == TRUE )
	{
		return;
	}

	// check for nick - but avoid string searching if not enabled
	if ( !m_bPrivateChat && g_pConfig->GetSoundEnabled(eusNICKMENTIONED) == TRUE )
	{
		if ( message.Find(m_pClient->GetNick()) != -1 )
		{
			g_pConfig->PlaySound(eusNICKMENTIONED);
		}
	}

	// convert newline
	message = message.Replace( "\\r\xa", "\xa" );
	message = message.Replace( "\\r", "\xa" );

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

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

	s1 = m_pClient->GetNick() + ": ";

	if ( ( message.Left(4).ToUpper() == "/ME " ) || ( message.Left(4).ToUpper() == "+ME " ) )
	{
		bMe    = TRUE;
		message = message.Right( message.Length() - 4 );
	}

	if ( message.Left(s1.Length()) == s1 )
	{
		bSay    = TRUE;
		// message = message.Right( message.Length() - s1.Length() );
	}

	msg = message;
	s1 = "";

	b1 = b2 = TRUE;

	while ( msg != "" )
	{
		if (b1)
		{
			if ( (i1 = FindFirstLink(msg)) == -1 )
			{
				b1 = FALSE;
			}
		}
		else
		{
			i1 = -1;
		}

		if (b2)
		{
			if ( (i2 = FindFirstEmoticon(msg)) == -1 )
			{
				b2 = FALSE;
			}
		}
		else
		{
			i2 = -1;
		}

		if ( ((i1 < i2) || (i2 == -1)) && (i1 != -1) )
		{
			// convert link
			s1 += msg.Left(i1);
			msg = msg.Right(msg.Length()-i1);

			index = ConvertLinks( msg, s );
		}
		else if ( ((i2 < i1) || (i1 == -1)) && (i2 != -1) )
		{
			// convert emot
			s1 += msg.Left(i2);
			msg = msg.Right(msg.Length()-i2);

			index = ConvertEmoticons( msg, s );
		}
		else
		{
			// no link & no emot found
			s1 += msg;
			msg = "";
			break;
		}

		if ( index > 1 )
		{
			s1 += s;
			msg = msg.Right(msg.Length()-index);
			continue;
		}

		if ( index == 1 )
		{
			s1 += msg.Left(1);
		}

		msg = msg.Right(msg.Length()-index);
	}

	message = s1;

	// convert spaces
	s = "";
	b = TRUE;

	for(len=0;len<message.Length();len++)
	{
		if ( message.Data()[len] == '\xa' )
			b = TRUE;
		else if ( message.Data()[len] != ' ' )
			b = FALSE;

		if ( message.Data()[len] == ' ' )
		{
			if (b)
				s += "&nbsp;";
			else
				s += " ";
			b = !b;
		}
		else
		{
			s += message.Data()[len];
		}
	}

	message = s;

	// convert newline
#if QT_VERSION < 304
	if ( message.Find('\xa',0) == -1 )
		message += "<br />";
#endif

	message = message.Replace( "\xa", "<br />" );

	// set color
	if ( m_bPrivateChat )
		if ( bremote && (nick != m_pClient->GetNick().Data()) )
			if ( bSay )
				message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATSAY) + "\">" + message + "</font>";
			else
				message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATREMOTETEXT) + "\">" + message + "</font>";
		else
			message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATLOCALTEXT) + "\">" + message + "</font>";
	else
		if ( forward )
			message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICPRIVATECHATTEXT) + "\">" + message + "</font>";
		else if ( bremote && (nick != m_pClient->GetNick().Data()) )
			if ( bSay )
				message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATSAY) + "\">" + message + "</font>";
			else
				message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATREMOTETEXT) + "\">" + message + "</font>";
		else
			message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATLOCALTEXT) + "\">" + message + "</font>";

	// add nick
	if ( m_bPrivateChat )
	{
		if ( nick != m_pClient->GetNick().Data() )
		{
			if ( isOP )
				message = "<font color=\"" + g_pConfig->GetChatColor(eccOPNICK) + "\">" + "<b>" + nick + "</b>: " + message + "</font>";
			else
				message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATREMOTENICK) + "\">" + "<b>" + nick + "</b>: " + message + "</font>";
		}
		else
			message = "<font color=\"" + g_pConfig->GetChatColor(eccCHATLOCALNICK) + "\">" + "<b>" + nick + "</b>: " + message + "</font>";
	}
	else
	{
		if ( forward )
			message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICPRIVATECHATNICK) + "\">" + "<b>&lt;" + nick + "&gt;</b> " + "</font>" + message;
		else if ( nick != m_pClient->GetNick().Data() )
			if ( bMe )
				message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATMENICK) + "\">" + "<b>" + nick + "</b> " + "</font>" + message;
			else
			{
				if ( isOP )
					message = "<font color=\"" + g_pConfig->GetChatColor(eccOPNICK) + "\">" + "<b>&lt;" + nick + "&gt;</b> " + "</font>" + message;
				else
					message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATREMOTENICK) + "\">" + "<b>&lt;" + nick + "&gt;</b> " + "</font>" + message;
			}
		else
			if ( bMe )
				message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATMENICK) + "\">" + "<b>" + nick + "</b> " + "</font>" + message;			
			else
				message = "<font color=\"" + g_pConfig->GetChatColor(eccPUBLICCHATLOCALNICK) + "\">" + "<b>&lt;" + nick + "&gt;</b> " + "</font>" + message;
	}

	timestamp = GetTimeStamp();

	AddOutput( timestamp + message );

	if ( bremote && m_bPrivateChat )
	{
		g_pConfig->PlaySound(eusRECEIVE);

		// send away message
		if ( g_pConfig->GetAwayMode() == euamAWAY )
		{
			s = g_pConfig->GetAwayMessage().Data();

			if ( (s != "") && (m_bSendAway) )
			{
				m_pClient->SendPrivateMessage( m_pClient->GetNick(), m_sNick, s );
				m_bSendAway = FALSE;
				AddStatus("Sent away message: " + s);
			}
		}
		else
		{
			m_bSendAway = TRUE;
		}
	}
}

/** */
void DCChat::SendMessage( CString message )
{
	int err;
	CString s;
	bool local = FALSE;
	bool cmd = FALSE;

	if ( !m_pClient )
	{
		return;
	}

	if ( message == "" )
	{
		message = TextEdit_CHATINPUT->text().ascii();
	}
	else
	{
		local = TRUE;
	}

	if ( message != "" )
	{
		if ( local == FALSE )
		{
			// add history
			AddHistory(message);
			m_sHistoryTempString = "";
			cmd = CheckForCommand();
		}

		if ( cmd == FALSE )
		{
			// convert the newlines
			message = message.Replace( "\\r\xa", "\xa" );
			message = message.Replace( "\xa", "\xd\xa" );

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

			if ( err == 0 )
			{
				if ( m_bPrivateChat )
					AddMessage( m_pClient->GetNick(), message, FALSE );
				g_pConfig->PlaySound(eusSEND);
			}
			else
			{
				AddStatus( tr("Message not sent!").ascii() );
			}
		}

		if ( local == FALSE )
		{
			TextEdit_CHATINPUT->setText("");
		}
	}
}

/** */
int DCChat::FindFirstLink( CString msg )
{
	int i,i1,i2;

	for(i=0,i1=-1,i2=-1;p_LinkList[i]!=0;i++)
	{
		if ( (i1 = msg.Find(p_LinkList[i],0,FALSE)) != -1 )
		{
			if ( (i2 == -1) || (i2 > i1) )
			{
				i2 = i1;
			}
		}
	}

	return i2;
}

/** */
int DCChat::ConvertLinks( CString msg, CString & s )
{
	int ret = 1;
	int i,i1,i2,i3;

	// convert links
	for(i=0;p_LinkList[i]!=0;i++)
	{
		if ( msg.Left(strlen(p_LinkList[i])).ToUpper() == p_LinkList[i] )
		{
			// link end at space or newline
			i1 = msg.Find(' ',0,FALSE);
			i2 = msg.Find('\xa',0,FALSE);
			i3 = msg.Find('\xd',0,FALSE);

			if ( i1 == -1 )
				i1 = msg.Length();
			if ( (i1 > i2) && (i2 != -1) )
				i1 = i2;
			if ( (i1 > i3) && (i3 != -1) )
				i1 = i3;

			s = msg.Left(i1);
			s = "<a title=\""+s+"\" name=\""+s+"\" href=\""+s+"\">"+s+"</a>";

			ret = i1;

			break;
		}
	}

	return ret;
}

/** */
int DCChat::FindFirstEmoticon( CString msg )
{
	int i1,i2;
	CList<DC_EmoticonObject> * elist;

	elist = g_pConfig->EmoticonList();

	i1 = i2 = -1;

	if ( (g_pConfig->GetEnableEmoticons() == TRUE) && (elist != 0) )
	{
		DC_EmoticonObject * EmoticonObject = 0;

		while ( (EmoticonObject=elist->Next(EmoticonObject)) != 0 )
		{
			if ( (i1 = msg.Find( EmoticonObject->m_Text, 0 )) != -1 )
			{
				if ( (i2 == -1) || (i2 > i1) )
				{
					i2 = i1;
				}
			}
		}
	}

	return i2;
}

/** */
int DCChat::ConvertEmoticons( CString msg, CString & s )
{
	int ret = 1;
	CList<DC_EmoticonObject> * elist;

	elist = g_pConfig->EmoticonList();

	// convert emoticons
	if ( (g_pConfig->GetEnableEmoticons() == TRUE) && (elist != 0) )
	{
		DC_EmoticonObject * EmoticonObject = 0;

		while ( (EmoticonObject=elist->Next(EmoticonObject)) != 0 )
		{
			if ( msg.Left(EmoticonObject->m_Text.Length()) == EmoticonObject->m_Text )
			{
				s   = "<img alt=\"\" align=\"center\" source=\"emoticon"+CString().setNum(EmoticonObject->m_nID)+"\" />";
				ret = EmoticonObject->m_Text.Length();
				break;
			}
		}
	}

	return ret;
}

/** */
bool DCChat::CheckForCommand()
{
	bool res = FALSE;
	bool addedSpace = FALSE;
	QString timestamp;
	CString cmd,s;
	CList<DC_UserChatCommand> usercommands;
	DC_UserChatCommand * ucc = 0;
	
	cmd = TextEdit_CHATINPUT->text().ascii();
	
	// replace custom commands with their actions
	if ( cmd.Right(1) != " " )
	{
		cmd = cmd + " ";
		addedSpace = TRUE;
	}
	
	g_pConfig->GetUserChatCommands(&usercommands);
	
	while( (ucc=usercommands.Next(ucc)) != 0 )
	{
		if ( cmd.Left(ucc->m_sName.Length() + 1) == (ucc->m_sName + " ") )
		{
			cmd = cmd.Replace( ucc->m_sName, ucc->m_sCommand );
		}
	}
		
	if (addedSpace)
	{
		cmd = cmd.Left(cmd.Length() - 1);
	}
	
	if ( (cmd != "") && (cmd.Left(1) == '/') )
	{
		// help
		if ( cmd == "/dchelp" )
		{
			s  = tr("Help:").ascii();
			s += "\n";
			s += tr("/clear - clears the chat window").ascii();
			s += "\n";
			s += tr("/mode - shows the current mode").ascii();
			s += "\n";
			s += tr("/refresh - refresh share").ascii();
			s += "\n";
			s += tr("/rebuild - remove no longer present files from hash database").ascii();
			s += "\n";
			s += tr("/slots &lt;N&gt; - set number of upload slots to N");
			s += "\n";
			s += tr("/ts - switch time display in chat on/off").ascii();
			s += "\n";
			s += tr("/adv - send an advertisment to the hub").ascii();
			s += "\n";
			s += tr("/join &lt;address&gt; - disconnect from currently connected hub and connect to another hub").ascii();
			s += "\n";
			s += tr("/msg &lt;nick&gt; &lt;message&gt; - send private message").ascii();
			s += "\n";
			s += tr("/away &lt;message&gt; - set automatic response for private messages; if the message is empty, disable it").ascii();
			s += "\n";
			s += tr("/bye &lt;message&gt; - disconnect from channel with an optional message").ascii();
			s += "\n";
			s += tr("/uptime [show] - show valknut uptime [to other users]").ascii();
			s += "\n";
			s += tr("/ls &lt;nick&gt; - get share list from user").ascii();
			s += "\n";
			s += tr("/grant &lt;nick&gt; - grant user a slot").ascii();
			s += "\n";
			s += tr("/grantp &lt;nick&gt; - grant user a permanent slot").ascii();
			s += "\n";
			s += tr("/friend &lt;nick&gt; - add user to friend list").ascii();
			s += "\n";
			s += tr("/fav - add hub to bookmark list").ascii();
			s += "\n";
			s += tr("/info &lt;nick&gt; - show user info").ascii();
			s += "\n";
			s += tr("/now - send current time to the chat").ascii();
			s += "\n";
			s += tr("/sh &lt;command&gt; &lt;args...&gt; - send output of shell command to chat").ascii();
			s += "\n";
			s += tr("/raw - send raw message").ascii();
			s += "\n";
			s += tr("/disablesorting - disable sorting of the user list").ascii();
			s += "\n";
			s += tr("/enablesorting - enable sorting of the user list").ascii();
			s += "\n";
			s += tr("/ignore &lt;nick&gt; - do not show main chat lines from the user").ascii();
			s += "\n";
			s += tr("/unignore &lt;nick&gt - show main chat lines from the user again").ascii();
			s += "\n";
			s += tr("/ratio [show] - show share ratio [to other users]").ascii();
			s += "\n";
			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /clear - clear the chat window
		else if ( cmd == "/clear" )
		{
			m_pTextEdit_CHATOUTPUT->clear();

			AddStatus( tr("Chat Cleared.").ascii(), TRUE );
			res = TRUE;
		}
		// /info - show user info
		else if ( cmd.Left(5) == "/info" )
		{
			CString nick;
			CMessageMyInfo myinfo;
			
			if ( m_bPrivateChat && (cmd.Length() == 5) )
			{
				nick = m_sNick;
			}
			else
			{
				nick = cmd.Mid(6,cmd.Length()-6);	
			}
						
			if (m_pClient->UserList()->GetUserMyInfo( nick, &myinfo ) == FALSE)
			{
				s = tr("Get info failed.");
			}
			else
			{
				s  = tr("Info:") + "\n";
				s += tr("Nick:") + " " + myinfo.m_sNick + "\n";
				s += tr("Operator:") + " ";

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

				s += tr("\n");

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

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

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

				s += "\n";

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

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

				s += "\n";

				s += tr("Tag:") + " " + myinfo.m_sVerComment + "\n";
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /uptime - show valknut uptime
		else if ( cmd.Left(7) == "/uptime" )
		{
			int d,h,m;
			int i = time(0)-dclibUptime();

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

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

			if ( d > 0 )
			{
				s += CString().setNum(d);
				s += " ";
				if ( d == 1 )
					s += tr("day");
				else
					s += tr("days");
				s += ",";
			}
			if ( h > 0 )
			{
				s += CString().setNum(h) + " ";
				if ( h == 1 )
					s += tr("hour");
				else
					s += tr("hours");
				s += ",";
			}
			s += CString().setNum(m) + " ";
			if ( m == 1 )
				s += tr("minute");
			else
				s += tr("minutes");
			
			if ( cmd.Mid(8, 4).ToLower() == "show" )
				SendMessage(s);
			else
				AddStatus( s, TRUE );

			res = TRUE;
		}
		// /mode - show current mode
		else if ( cmd == "/mode" )
		{
			CString lms;
			
			s  = tr("Current mode: ");

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

			// get listen manager status
			if ( CListenManager::Instance() )
			{
				lms = CListenManager::Instance()->GetSocketErrorMsg();
			
				if ( lms != "" )
				{
					s += " (" + lms + ")";
				}	
			}
			
			s += ".";
			
			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /refresh - refresh share 
		else if ( cmd == "/refresh" )
		{
			if ( CFileManager::Instance()->CreateShareList() == TRUE )
			{
				s = tr("Refresh share in progress.").ascii();
			}
			else
			{
				s = tr("Refresh share already in progress.").ascii();
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /rebuild - clean up share databases
		else if ( cmd == "/rebuild" )
		{
			if ( CFileManager::Instance()->RebuildLists() == TRUE )
			{
				s = tr("Rebuild share in progress.").ascii();
			}
			else
			{
				s = tr("Refresh/rebuild already in progress.").ascii();
			}
			
			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /slots - set number of slots
		else if ( cmd.Left(7) == "/slots " )
		{
			int i = (cmd.Mid(7, cmd.Length() - 7)).asINT();
			
			if ( (i >= 0) && (i <= 99) )
			{
				g_pConfig->SetMaxUpload(i);
				AddStatus( tr("Setting upload slots to: " + QString::number(i)).ascii(), TRUE );
			}
			else
			{
				AddStatus( tr("Invalid number of upload slots specified.").ascii(), TRUE );
			}
			
			res = TRUE;
		}
		// /ts - switch time display in chat on/off 
		else if ( cmd == "/ts" )
		{
			bool b;

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

			s = tr("Switch timestamp ").ascii();

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

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

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

			SendMessage(s);

			res = TRUE;
		}
		// /now - send current time to the chat
		else if ( ((cmd.Left(4) == "/now") && cmd.Length() == 4) ||
		          (cmd.Left(5) == "/now ") )
		{
			if ( cmd.Length() > 4 )
			{
				s = " " + cmd.Mid(5,cmd.Length()-5);
			}
			
			s = QTime::currentTime().toString("hh:mm:ss").ascii() + s;

			SendMessage(s);

			res = TRUE;
		}
		// /raw - send raw message
		else if ( cmd.Left(5) == "/raw " )
		{
			CString msg;

			if ( !m_bPrivateChat )
			{
				msg = cmd.Mid(5,cmd.Length()-5);
			
				if ( msg != "" )
				{
					AddStatus( msg, TRUE );
					m_pClient->SendString(msg);
				}
			}

			res = TRUE;
		}
		// /join <address> - disconnect from currently connected hub and connect to another hub 
		else if ( cmd.Left(6) == "/join " )
		{
			CString address;

			address = cmd.Mid(6,cmd.Length()-6);

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

				m_pClient->SetHubName(address);
				m_pClient->setCaption(address.Data());
				m_pClient->Connect(address);
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /ls - get share list from user
		else if ( cmd.Left(3) == "/ls" )
		{
			CString nick;

			if ( m_bPrivateChat && (cmd.Length() == 3) )
			{
				nick = m_sNick;
			}
			else
			{
				nick = cmd.Mid(4,cmd.Length()-4);
			}

			if ( nick != "" )
			{
				/** add transfer to the waitlist */
				g_pTransferView->DLM_QueueAdd( nick, m_pClient->GetHubName(), m_pClient->GetIP()+":"+QString().setNum(m_pClient->GetPort()).ascii(),
							DC_USER_FILELIST_HE3, DC_USER_FILELIST_HE3, "", "", eltBUFFER,
							0, 0, 0, "" );

				s = tr("Download sharelist from") + " '" + nick + "'";
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /grantp - grant perm. slot to user
		else if ( cmd.Left(7) == "/grantp" )
		{
			CString nick;

			if ( m_bPrivateChat && (cmd.Length() == 7) )
			{
				nick = m_sNick;
			}
			else
			{
				nick = cmd.Mid(8,cmd.Length()-8);
			}

			if ( nick != "" )
			{
				g_pTransferView->DLM_AddUserSlot( nick, m_pClient->GetHubName(), 0 );
				g_pTransferView->DLM_AddUserSlot( nick, m_pClient->GetHubName(), 0, TRUE );

				s = tr("Permanent slot added for") + " '" + nick + "'";
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /grant - grant slot to user
		else if ( cmd.Left(6) == "/grant" )
		{
			CString nick;

			if ( m_bPrivateChat && (cmd.Length() == 6) )
			{
				nick = m_sNick;
			}
			else
			{
				nick = cmd.Mid(7,cmd.Length()-7);
			}

			if ( nick != "" )
			{
				g_pTransferView->DLM_AddUserSlot( nick, m_pClient->GetHubName(), 1 );

				s = tr("Slot added for") + " '" + nick + "'";
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /friend - add user to friend list
		else if ( cmd.Left(7) == "/friend" )
		{
			CString nick;

			if ( m_bPrivateChat && (cmd.Length() == 7) )
			{
				nick = m_sNick;
			}
			else
			{
				nick = cmd.Mid(8,cmd.Length()-8);
			}

			if ( nick != "" )
			{
				g_pUsersList->AddFriend( nick, m_pClient->GetHubName(), m_pClient->GetHost(), "" );

				s = tr("Added to friend list ") + " '" + nick + "'";
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /fav - add hub to bookmarks
		else if ( cmd == "/fav" )
		{
			g_pHubListManager->AddBookmark( m_pClient->GetHubName().Data(), m_pClient->GetHost().Data(), "" );

			s = tr("Add bookmark hub") + " '" + m_pClient->GetHubName() + "'";

			AddStatus( s, TRUE );
			res = TRUE;
		}
		// /bye <msg> - disconnect from channel
		else if ( cmd.Left(4) == "/bye" )
		{
			CString message;

			message = cmd.Mid(5,cmd.Length()-5);

			if ( message != "" )
			{
				SendMessage(message);
			}

			m_pClient->Disconnect(TRUE);

			res = TRUE;
		}
		// /msg <nick> <message> - send private message 
		else if ( cmd.Left(5) == "/msg " )
		{
			int i;
			CString nick,message="";
			
			AddStatus( cmd, TRUE );

			cmd = cmd.Mid(5,cmd.Length()-5);

			if ( (i = cmd.Find(' ')) != -1 )
			{
				nick    = cmd.Left(i);
				message = cmd.Mid(i+1,cmd.Length()-i-1);
			}

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

			AddStatus( s, TRUE );
			res = TRUE;

		}
		// /away <message> - set automatic response for private messages; if the message is empty, disable it 
		else if ( cmd.Left(5) == "/away" )
		{
			int i;

			s = "";

			i = cmd.Find(' ',5);

			if ( i > 0 )
				s = cmd.Mid(i+1,cmd.Length()-i-1);

			g_pConfig->SetAwayMessage(s);

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

			AddStatus( s, TRUE );
			res = TRUE;
		}
		else if ( cmd.Left(4) == "/sh " )
		{
			// display the command the user entered
			AddStatus( cmd, TRUE );
			
			// actually this is better - more portable, doesn't depend on /bin/sh
			QStringList shellargs = QStringList::split( QString(" "), QString((cmd.Mid(4, cmd.Length() - 4)).Data()) );
			
			// QStringList shellargs;
			// shellargs.append( QString("/bin/sh") );
			// shellargs.append( QString("-c") );
			// shellargs.append( QString((cmd.Mid(4, cmd.Length() - 4)).Data()) );
			
			// debug code - show the args we are going to run
			//AddStatus( (shellargs.join( QString(",") )).ascii(), TRUE );
			
			QProcess shellproc( shellargs, this );
			shellproc.setCommunication( QProcess::Stdout );
			
			if ( shellproc.start() )
			{
				// wait up to 5 seconds for the process to finish
				int i = 0;
				while ( (shellproc.isRunning()) && (i < 5) )
				{
					sleep(1);
					i++;
				}
				
				shellproc.closeStdin();
				
				// kill the process if it's still running
				if ( shellproc.isRunning() )
				{
					AddStatus( tr("Process still running after 5 seconds, killing process...").ascii(), TRUE );
					shellproc.tryTerminate();
					sleep(1);
					if ( shellproc.isRunning() )
					{
						shellproc.kill();
					}
				}
	
				if ( shellproc.normalExit() )
				{
					int exitcode = shellproc.exitStatus();
					if ( exitcode == 0 )
					{
						s = QString( shellproc.readStdout() ).stripWhiteSpace();
						if ( s == "" )
						{
							AddStatus( tr("Command produced no visible output.").ascii(), TRUE );
						}
						else
						{
							SendMessage(s);
						}
					}
					else
					{
						AddStatus( tr("Process exited with status " + QString::number(exitcode)).ascii(), TRUE);
					}
				}
				else
				{
					AddStatus( tr("Process was killed or crashed.").ascii(), TRUE);
				}
			}
			else
			{
				AddStatus( tr("Failed to start shell command.").ascii(), TRUE );
			}
			
			res = TRUE;
		}
		else if ( cmd == "/sendphoto" )
		{
			if ( !m_bPrivateChat )
			{
				s = tr("Only allowed in private chat.").ascii();
			}
			else if ( m_pClient->UserList()->GetUserClientVersion(m_sNick) != eucvDCGUI )
			{
				s = tr("The user is using an old version or not using Valknut").ascii();
			}
			else
			{
				CString fn = g_pConfig->GetUserPhotoFileName();
				CDir dir;
				CFile f;
				CByteArray bas,bad;
				ulonglong size;
				CBase64 base64;

				size = dir.getFileSize( fn, FALSE );

				if ( (size == 0) || (f.Open(fn,IO_READONLY|IO_RAW) == FALSE) )
				{
					s = tr("Photo not found.").ascii();
				}
				else
				{
					bas.SetSize(size);

					if ( f.Read((char*)bas.Data(),size) != size )
					{
						s = tr("Can't read your photo.").ascii();
					}
					else
					{
						base64.Encode( &bad, &bas );

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

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

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

				f.Close();
			}

			AddStatus( s, TRUE );
			res = TRUE;
		}
		else if ( cmd == "/disablesorting" )
		{
			m_pClient->ListView_USERLIST->setSortColumn(-1);
			s = tr("Disabled user list sorting.").ascii();
			AddStatus( s, TRUE );
			res = TRUE;
		}
		else if ( cmd == "/enablesorting" )
		{
			m_pClient->ListView_USERLIST->setSortColumn(0);
			s = tr("Enabled user list sorting.").ascii();
			AddStatus( s, TRUE );
			res = TRUE;
		}
		else if (cmd.Left(7) == "/ignore")
		{
			CString nick = cmd.Mid(8, cmd.Length()-8);
			if (nick != "")
			{
				if (g_pUsersList->isNickInList(nick) == FALSE)
				{
					g_pUsersList->AddFriend( nick, m_pClient->GetHubName(), m_pClient->GetHost(), "" );
				}
				
				g_pUsersList->setIgnore( nick, TRUE );
				s = tr(QString("Ignoring main chat lines from ") + QString(nick.Data()));
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}
			
			AddStatus( s, TRUE );
			res = TRUE;
			
		}
		else if (cmd.Left(9) == "/unignore")
		{
			CString nick = cmd.Mid(10, cmd.Length()-10);
			if (nick != "")
			{
				if (g_pUsersList->ignoreNick( nick ) == TRUE)
				{
					g_pUsersList->setIgnore( nick, FALSE);
					s = tr(QString("Showing main chat for ") + QString(nick.Data()));
				}
				else
				{
					s = tr(QString(nick.Data()) + QString(" is not in ignore list"));
				}
				
			}
			else
			{
				s = tr("No nick, try /dchelp");
			}
			
			AddStatus( s, TRUE );
			res = TRUE;
		}
		else if ( cmd.Left(6) == "/ratio" )
		{
			ulonglong up = CSocket::m_Traffic.GetTraffic(ettTX);
			ulonglong down = CSocket::m_Traffic.GetTraffic(ettRX);
			
			float ratio = 0;
			
			if ( down > 0 )
			{
				ratio = (double) up / (double) down;
			}
			
			s  = tr("ratio: overall: ").ascii();
			s += QString().setNum( ratio, 'f', 2 ).ascii();
			s += " (";
			s += tr("uploads").ascii();
			s += ": ";
			s += CUtils::GetSizeString( up, g_pConfig->GetUnit() );
			s += " | ";
			s += tr("downloads").ascii();
			s += ": ";
			s += CUtils::GetSizeString( down, g_pConfig->GetUnit() );
			s += ")\n";
			
			up -= g_pConfig->GetStartUploaded();
			down -= g_pConfig->GetStartDownloaded();
			
			if ( down > 0 )
			{
				ratio = (double) up / (double) down;
			}
			else
			{
				ratio = 0;
			}
			
			s += tr("this session: ").ascii();
			s += QString().setNum( ratio, 'f', 2 ).ascii();
			s += " (";
			s += tr("uploads").ascii();
			s += ": ";
			s += CUtils::GetSizeString( up, g_pConfig->GetUnit() );
			s += " | ";
			s += tr("downloads").ascii();
			s += ": ";
			s += CUtils::GetSizeString( down, g_pConfig->GetUnit() );
			s += ")";
			
			if ( cmd.Mid( 7, 4 ).ToLower() == "show" )
			{
				SendMessage( s );
			}
			else
			{
				AddStatus( s, TRUE );
			}
			res = TRUE;
		}
	}
	return res;
}

/** */
bool DCChat::CheckForData( CString message )
{
	bool res = FALSE;

	// now we check for special private messages
	if ( m_bPrivateChat )
	{
		// remote send a photo
		if ( (message.Find("<photo ") == 0) && (message.Find(">",7) > 0) )
		{
			int i1,i2;
			CString s;
			CByteArray ba;
			CBase64 base64;

			if ( (i1 = message.Find("data=\"")) != -1 )
			{
				if ( (i2 = message.Find("\"",i1+6)) != -1 )
				{
					//printf("'%s'\n",message.Data());
					//printf("'%s'\n",message.Mid(i1+6,i2-i1-6).Data());

					s = message.Mid(i1+6,i2-i1-6);

					if ( base64.Decode( &ba, &s ) > 0 )
					{
						g_pUsersList->AddFriendPhoto(m_sNick,&ba);
						AddStatus(tr("Photo received.").ascii(), TRUE );

						res = TRUE;
					}
				}
			}
		}
	}

	return res;
}

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

	if ( m_nTabPressed > 0 )
	{
		s     = m_sTabSaved;
		start = m_nTabStart;
		end   = m_nTabEnd;
	}
	else
	{
		TextEdit_CHATINPUT->getCursorPosition(&p,&i);
		s = TextEdit_CHATINPUT->text();
		m_sTabSaved = s;
		if ( i == 0 )
			end = 0;
		else
			end = i - 1;
		start = s.findRev(" ", end, FALSE);
		m_nTabStart = start;
		m_nTabEnd = end;
	}

	if ( (s.isEmpty() == TRUE) && (m_sTabNick.isEmpty() == FALSE) )
	{
		// use the saved nick
		QString line = m_sTabNick + ": ";
		TextEdit_CHATINPUT->setText(line);
		TextEdit_CHATINPUT->setCursorPosition( 0,line.length() );
	}
	else
	{
		int cpos = 1;

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

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

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

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

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

		if ( m_sTabNick.isEmpty() == FALSE )
		{
			TextEdit_CHATINPUT->setText(s);

			TextEdit_CHATINPUT->setCursorPosition(0,start + m_sTabNick.length() + cpos);

			m_nTabPressed++;
		}
	}
}

/** insert double clicked word into chat input box */
void DCChat::slotDoubleClickedChatOutput( int, int )
{
	// double clicking already selects the text
	QString selected = m_pTextEdit_CHATOUTPUT->selectedText();
	
	// do noting on no selection
	if ( selected == "" )
	{
		return;
	}
	
	// remove all html tags
	QRegExp re;
	re.setMinimal(TRUE);
	re.setPattern("<.*>");
	selected = selected.replace( re, "" );
	
	// "NICK> " gets selected, trim "> " off nick
	if ( selected.right(5) == "&gt; " )
	{
		selected = selected.left( selected.length() - 5 );
	}
	
	// trim white space off ends
	selected = selected.simplifyWhiteSpace();
	
	// add space on end
	selected += " ";
	
	if ( TextEdit_CHATINPUT->text() == "" )
	{
		TextEdit_CHATINPUT->setText(selected);
	}
	else
	{
		if ( TextEdit_CHATINPUT->text().right(1) != " " )
		{
			selected = " " + selected;
		}
		
		// not using TextEdit_CHATINPUT->append(), that would add a new line
		TextEdit_CHATINPUT->setText( TextEdit_CHATINPUT->text() + selected );
	}
}
