/***************************************************************************
                          dchubsearch.cpp  -  description
                             -------------------
    begin                : Fri Mar 15 2002
    copyright            : (C) 2002-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 "dchubsearch.h"

/** Qt (TM) includes */
#include <qapplication.h>
#include <qlineedit.h>
#include <QTextEdit>
#include <qradiobutton.h>
#include <q3listview.h>
#include <qcheckbox.h>
#include <qlcdnumber.h>
#include <qcombobox.h>
#include <qspinbox.h>
#include <qpushbutton.h>
#include <qmessagebox.h>
#include <qstringlist.h>
#include <qtabwidget.h>
#include <qcursor.h>
#include <QFileDialog>
#include <QUrl>
#include <qclipboard.h>
#include <QProgressBar>
//#include <qdragobject.h>
//Added by qt3to4:
#include <QKeyEvent>
#include <QResizeEvent>
#include <QShowEvent>
#include <QEvent>
#include <QHeaderView>
#include <QFileInfo>
#include <QDateTime>
#include <QMdiArea>
#include <QByteArray>

/** */
#include <dclib/dcos.h>
#include <dclib/cutils.h>
#include <dclib/core/cstringlist.h>

#ifndef WIN32
#include <stdlib.h>
#endif

#include "dcconfig.h"
#include "dcclient.h"
#include "dcconnectionmanager.h"
#include "dchublistmanager.h"
#include "dcfiletool.h"
#include "dctransferview.h"
#include "dcmenuhandler.h"
#include "dcwidget.h"
#include "dciconloader.h"
#include "dcguiutils.h"

#include <dclib/core/cdir.h>
#include <dclib/core/cxml.h>
#include <dclib/chttp.h>

// for file type icons
#include <dclib/cfilemanager.h>

// for saving search results with correct encoding
#include <dclib/core/ciconv.h>

// for loading saved search results
#include <dclib/cmessagehandler.h>

// not in dchubsearch.h any more
#include <dclib/csearchmanager.h>

// all for checking a TTH is valid and so not send out useless searches
#include <dclib/core/cbytearray.h>
#include <dclib/core/cbase32.h>
#include <dclib/cfilehasher.h>

#include "search-result-columns.h"

/** */
DCHubSearch::DCHubSearch( QWidget * parent ) : QWidget( parent )
{
	int idx;

	lastFileResultsWidth = -1;
	lastUserResultsWidth = -1;

	m_eCurrentGrouping = egsrtNONE;
	m_nFilteredResults = 0;

	setupUi(this);

	m_pEmptyGroup = 0;

	// set default icon
	setWindowIcon( g_pIconLoader->GetPixmap(eiFIND) );

	setAttribute(Qt::WA_DeleteOnClose);
	if ( g_pConnectionManager->GetMdiArea() )
	{
		m_pContainerWindow = g_pConnectionManager->GetMdiArea()->addSubWindow(this);
		m_pContainerWindow->setWindowIcon( g_pIconLoader->GetPixmap(eiFIND) );
	}
	else
	{
		m_pContainerWindow = 0;
	}

	// set the ColumnWidthMode to manual, we have to do this
	// because using the Q3ListView resizeMode AllColumns means that it is
	// impossible to change the size of the last column
	// and QTreeView only resizes the last column
	for( idx = 0; idx < ListView_SEARCHRESULT->columns(); idx++ )
	{
		ListView_SEARCHRESULT->setColumnWidthMode( idx, Q3ListView::Manual );
	}

	/* disable this, we are adjusting the column widths */
	TreeWidget_SEARCHRESULTUSER->header()->setStretchLastSection(false);

	TreeWidget_SEARCHRESULTUSER->sortByColumn( 0, Qt::AscendingOrder );
	TreeWidget_SEARCH->sortByColumn( 1, Qt::AscendingOrder );

	// right align numeric columns
	ListView_SEARCHRESULT->setColumnAlignment( RESULTS_COLUMN_SIZE, Qt::AlignRight );
	ListView_SEARCHRESULT->setColumnAlignment( RESULTS_COLUMN_FREESLOTS, Qt::AlignRight );
	/* not total slots otherwise it's too close to the hub IP address and it's hard to read */

	// select at least
	ComboBox_SEARCHLIMIT->setCurrentIndex(1);

	// count column is hidden unless results have been grouped
	ListView_SEARCHRESULT->hideColumn(RESULTS_COLUMN_COUNT);

	m_pMessageList      = new CList<CDCMessage>();
	m_pSearchResultList = new CList<CMessageSearchResult>();
	m_pSearchQueryList  = 0;
	m_pSearchHistory    = new CList<CDCMessage>();

	InitDocument();

	/*
	 * CSearchManager does not delete the callback function anymore
	 * because multiple search windows will never work with that.
	 */
	m_pOurCallback = new CCallback1<DCHubSearch, CDCMessage*>( this, &DCHubSearch::DC_CallBack );
	
	
	/* figure out global search state */
	switch ( CSearchManager::Instance()->SearchType() )
	{
		case estyNONE:
			m_eSearchState = egssREADY;
			Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
			SetSearchView(true);
			break;
		case estySINGLE:
		case estyMULTI:
			m_eSearchState = egssOTHER;
			Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
			SetSearchView(false);
			break;
		case estyEXTERNAL:
			m_eSearchState = egssAUTO;
			Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
			SetSearchView(false);
			break;
	}
	
	if ( CSearchManager::Instance()->GetCallBackFunction() == 0 )
	{
		CSearchManager::Instance()->SetCallBackFunction( m_pOurCallback );
	}

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

/** */
DCHubSearch::~DCHubSearch()
{
	DeInitDocument();
	
	if ( CSearchManager::Instance()->GetCallBackFunction() == m_pOurCallback )
	{
		if ( (m_eSearchState == egssSEARCH) && ((CSearchManager::Instance()->SearchType() == estySINGLE) || (CSearchManager::Instance()->SearchType() == estyMULTI)) )
		{
			CSearchManager::Instance()->StopSearch();
		}
		CSearchManager::Instance()->SetCallBackFunction( 0 );
	}
	
	delete m_pOurCallback;
	m_pOurCallback = 0;

	ClearSearchResults();

	TreeWidget_SEARCH->removeEventFilter(this);
	Combobox_SEARCH->removeEventFilter(this);

	m_Timer.stop();

	SocketCallbackMutex.lock();

	// cleanup all lists
	delete m_pMessageList;
	m_pMessageList = 0;

	delete m_pSearchResultList;
	m_pSearchResultList = 0;

	delete m_pSearchQueryList;
	m_pSearchQueryList = 0;

	delete m_pSearchHistory;
	m_pSearchHistory = 0;
	
	SocketCallbackMutex.unlock();
}

/** */
void DCHubSearch::InitDocument()
{
	StringMap * map;

	// restore settings
	if ( g_pConfig->GetMap("SEARCHVIEW",map) )
	{
		if ( ((*map)["WIDTH"].toInt() > 0) && ((*map)["HEIGHT"].toInt() > 0) )
		{
			if ( m_pContainerWindow != 0 )
			{
				m_pContainerWindow->resize( (*map)["WIDTH"].toInt(), (*map)["HEIGHT"].toInt() );
			}
		}
		
		if ( !((*map)["MAXTHREADS"].isEmpty()) )
		{
			SpinBox_MAXTHREADS->setValue( (*map)["MAXTHREADS"].toInt() );
		}
		
		if ( !((*map)["GROUPING"].isEmpty()) )
		{
			m_eCurrentGrouping = (eGroupSearchResultType) (*map)["GROUPING"].toInt();
		}
		
		if ( m_eCurrentGrouping != egsrtNONE )
		{
			ListView_SEARCHRESULT->setRootIsDecorated(true);
			ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_COUNT, ListView_SEARCHRESULT->columnWidth( RESULTS_COLUMN_FREESLOTS ) );
			ListView_SEARCHRESULT->setSorting( RESULTS_COLUMN_COUNT );
		}
		
		if ( map->contains("SORTCOLUMN") )
		{
			ListView_SEARCHRESULT->setSortColumn( map->value("SORTCOLUMN").toInt() );
		}
		
		if ( map->contains("SORTORDER") )
		{
			ListView_SEARCHRESULT->setSortOrder( DCGuiUtils::SortOrderFromName(map->value("SORTORDER")) );
		}
	}

	connect( LineEdit_INCLUDE, SIGNAL(returnPressed()), this, SLOT(slotTextFilterResults()) );
	connect( LineEdit_EXCLUDE, SIGNAL(returnPressed()), this, SLOT(slotTextFilterResults()) );
	connect( PushButton_SEARCH, SIGNAL(clicked()), this, SLOT(slotSearchReturnPressed()) );
	connect( PushButton_ADDQUEUE, SIGNAL(clicked()), this, SLOT(slotAddSearchQueue()) );
	connect( PushButton_REFRESHCONNECTEDHUBS, SIGNAL(clicked()), this, SLOT(slotRefreshConnectedHubs()) );
	connect( PushButton_APPLYTEXTFILTER, SIGNAL(clicked()), this, SLOT(slotTextFilterResults()) );
	connect( PushButton_RESETTEXTFILTER, SIGNAL(clicked()), this, SLOT(slotTextFilterReset()) );
 	connect( PushButton_RESETPARAMS, SIGNAL(clicked()), this, SLOT(slotReset()) );
	connect( SpinBox_FREESLOTS, SIGNAL(valueChanged(int)), this, SLOT(slotChangedFreeSlots(int)) );
	connect( CheckBox_SEARCHFILEONLY, SIGNAL(toggled(bool)), this, SLOT(slotToggledSearchFileOnly(bool)) );
	connect( TabWidget_HUBSEARCH,SIGNAL(currentChanged(QWidget*)), this, SLOT(slotTabWidgetCurrentChange(QWidget*)) );
	connect( ListView_SEARCHRESULT,SIGNAL(doubleClicked(Q3ListViewItem*)), this, SLOT(slotDoubleClickedSearchResult(Q3ListViewItem*)) );
	connect( ListView_SEARCHRESULT,SIGNAL(contextMenuRequested( Q3ListViewItem *, const QPoint &, int )), this, SLOT(slotRightButtonClickedSearchResult(Q3ListViewItem*, const QPoint &, int )) );
	connect( TreeWidget_SEARCH,SIGNAL(customContextMenuRequested( const QPoint & )), this, SLOT(slotContextMenuSearch( const QPoint & )) );
	
	connect( Combobox_SEARCH, SIGNAL(activated(int)), this, SLOT(slotSearchSelected(int)) );
	
	connect( PushButton_PURGEHISTORY, SIGNAL(clicked()), this, SLOT(slotPurgeHistory()) );
	
	connect( ComboBox_SEARCHLIMIT, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSizeLimitChanged(int)) );

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

	TreeWidget_SEARCH->installEventFilter(this);
	Combobox_SEARCH->installEventFilter(this);
}

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

	// save search view settings
	g_pConfig->GetMap("SEARCHVIEW",map);

	if ( m_pContainerWindow != 0 )
	{
		(*map)["WIDTH"]     = QString().setNum(m_pContainerWindow->width());
		(*map)["HEIGHT"]    = QString().setNum(m_pContainerWindow->height());
	}
	(*map)["MAXTHREADS"] = QString().setNum(SpinBox_MAXTHREADS->value());
	(*map)["GROUPING"]   = QString().setNum(m_eCurrentGrouping);
	
	(*map)["SORTCOLUMN"] = QString::number( ListView_SEARCHRESULT->sortColumn() );
	(*map)["SORTORDER"]  = DCGuiUtils::SortOrderName( ListView_SEARCHRESULT->sortOrder() );
}

/** current tab widget change slot */
void DCHubSearch::slotTabWidgetCurrentChange(QWidget*)
{
	SizeColumnsPreservingRatios();
}

/** overridden so that the column widths are initialized on first show() */
void DCHubSearch::showEvent( QShowEvent * event )
{
	QWidget::showEvent( event );

	if ( isVisible() )
	{
		SizeColumnsPreservingRatios();
	}
}

/** resize event handler */
void DCHubSearch::resizeEvent( QResizeEvent * )
{
	SizeColumnsPreservingRatios();
}

/** Initialize or adjust widths of the ListView columns */
void DCHubSearch::SizeColumnsPreservingRatios()
{
	int width;
	
	if ( ListView_SEARCHRESULT->isVisible() )
	{
		width = ListView_SEARCHRESULT->width();
		if ( width > 0 )
		{
			if ( lastFileResultsWidth == -1 )
			{
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_FILE, ((width*7)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_SIZE, ((width*3)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_TTH, ((width*4)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_PATH, ((width*6)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_NICK, ((width*3)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_FREESLOTS, ((width*3)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_TOTALSLOTS, ((width*3)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_IP, ((width*4)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_HUB, ((width*4)/30) );
				ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_HOST, ((width*4)/30) );
				
				lastFileResultsWidth = ListView_SEARCHRESULT->width();
			}
			else if ( lastFileResultsWidth != width )
			{
				DCGuiUtils::AdjustColumnWidths( ListView_SEARCHRESULT, lastFileResultsWidth );
				lastFileResultsWidth = ListView_SEARCHRESULT->width();
			}
		}
	}
	else if ( TreeWidget_SEARCHRESULTUSER->isVisible() )
	{
		width = TreeWidget_SEARCHRESULTUSER->width();
		if ( width > 0 )
		{
			if ( lastUserResultsWidth == -1 )
			{
				TreeWidget_SEARCHRESULTUSER->setColumnWidth( 0, width/2 );
				TreeWidget_SEARCHRESULTUSER->setColumnWidth( 1, width/2 );
				
				lastUserResultsWidth = TreeWidget_SEARCHRESULTUSER->width();
			}
			else if ( lastUserResultsWidth != width )
			{
				DCGuiUtils::AdjustColumnWidths( TreeWidget_SEARCHRESULTUSER, lastUserResultsWidth );
				lastUserResultsWidth = TreeWidget_SEARCHRESULTUSER->width();
			}
		}
	}
}

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

		if ( e->key() == Qt::Key_Delete )
		{
			RemoveSelectedSearch(NULL);
		}
	}
	else if ((event->type() == QEvent::KeyPress)&&((QComboBox*)object==Combobox_SEARCH))
	{
		QKeyEvent * e = (QKeyEvent*)event;
		if((e->key() == Qt::Key_Enter) || (e->key() == Qt::Key_Return ))
		{
			slotSearchReturnPressed();
		}
	}

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

/** */
void DCHubSearch::slotSearchSelected(int sel)
{
	CMessageSearchUser *msu;
	CMessageSearchFile *msf;
	int i;
	CDCMessage *hmsg=0;

	i = 0;

	while ( (hmsg=(CDCMessage *)m_pSearchHistory->Next((CDCMessage*)hmsg)) != 0 )
	{
		if ( i == sel )
			break;
		i++;
	}

	if ( hmsg == 0 )
		return;

	if ( hmsg->m_eType == DC_MESSAGE_SEARCH_USER )
	{
		msu = (CMessageSearchUser *)hmsg;

		LineEdit_SEARCHSIZE->setText("0");
		ComboBox_SEARCHUNIT->setCurrentIndex(0);
		ComboBox_SEARCHLIMIT->setCurrentIndex(1);
		ComboBox_SEARCHTYPE->setCurrentIndex(0);
	}
	else
	{
		msf = (CMessageSearchFile *)hmsg;
		
		LineEdit_SEARCHSIZE->setText( QString().setNum(msf->m_nSize) );
		ComboBox_SEARCHUNIT->setCurrentIndex(0);
		
		if ( msf->m_bSizeLimit )
		{
			switch(msf->m_eSizeType)
			{
				case esstATLEAST:
					ComboBox_SEARCHLIMIT->setCurrentIndex(1);
					break;
				case esstATMOST:
					ComboBox_SEARCHLIMIT->setCurrentIndex(2);
					break;
				default:
					break;
			}
		}
		else
		{
			ComboBox_SEARCHLIMIT->setCurrentIndex(0);
		}
		
		ComboBox_SEARCHTYPE->setCurrentIndex(msf->m_eFileType);
	}
}

/** */
void DCHubSearch::addHistory( CDCMessage * msg )
{
	CMessageSearchUser *msu1,*msu2;
	CMessageSearchFile *msf1,*msf2;
	CString s1,s2;
	CDCMessage *hmsg = 0;
	int i = 0;
	bool found = false;
	
	if ( !msg )
		msg = GetSearchObject();
	if ( !msg)
		return;

	if ( msg->m_eType == DC_MESSAGE_SEARCH_USER )
	{
		msu1 = (CMessageSearchUser *)msg;
		s1 = msu1->m_sNick;
	}
	else
	{
		msf1 = (CMessageSearchFile *)msg;
		s1 = msf1->m_sString;
	}

	while ( (hmsg=(CDCMessage *)m_pSearchHistory->Next((CDCMessage*)hmsg)) != 0 )
	{
		if ( msg->m_eType == hmsg->m_eType )
		{
			if ( msg->m_eType == DC_MESSAGE_SEARCH_USER )
			{
				msu2 = (CMessageSearchUser *)hmsg;
				s2   = msu2->m_sNick;
			}
			else
			{
				msf2 = (CMessageSearchFile *)hmsg;
				s2   = msf2->m_sString;
			}

			if ( s1 == s2 )
			{
				Combobox_SEARCH->removeItem(i);
				Combobox_SEARCH->addItem(QString::fromAscii(s1.Data()));
				m_pSearchHistory->Del(hmsg);
				m_pSearchHistory->Add(msg);
				found = true;
				break;
			}
		}
		
		i++;
	}
	
	if ( !found )
	{
		m_pSearchHistory->Add(msg);
		Combobox_SEARCH->addItem(QString::fromAscii(s1.Data()));
	}
	
	Combobox_SEARCH->setCurrentIndex(Combobox_SEARCH->count()-1);
}

/** */
void DCHubSearch::SetSearchView( bool enabled )
{
	if ( enabled )
	{
		PushButton_SEARCH->setText(tr("Start"));
		PushButton_SEARCH->setEnabled(true);
	}
	else
	{
		PushButton_SEARCH->setText(tr("Stop"));
	}

	//LineEdit_SEARCH->setEnabled(enabled);
	Combobox_SEARCH->setEnabled(enabled);
	ButtonGroup_HUBS->setEnabled(enabled);
	SpinBox_MAXTHREADS->setEnabled(enabled);
	PushButton_ADDQUEUE->setEnabled(enabled);
	CheckBox_ENABLETAG->setEnabled(enabled);
	CheckBox_MULTISEARCH->setEnabled(enabled);
	PushButton_RESETPARAMS->setEnabled(enabled);
	PushButton_PURGEHISTORY->setEnabled(enabled);
	TextLabel_TYPE->setEnabled(enabled);
	ComboBox_SEARCHTYPE->setEnabled(enabled);
	ComboBox_SEARCHLIMIT->setEnabled(enabled);
	LineEdit_SEARCHSIZE->setEnabled(enabled);
	ComboBox_SEARCHUNIT->setEnabled(enabled);
	Label_MAX_RESULTS->setEnabled(enabled);
	SpinBox_MAXRESULT->setEnabled(enabled);
}

/** */
int DCHubSearch::DC_CallBack( CDCMessage * DCMsg )
{
	SocketCallbackMutex.lock();

	int err = -1;

	if ( (DCMsg != 0) && (m_pMessageList != 0) )
	{
		m_pMessageList->Add( DCMsg );
		err = 0;
	}

	SocketCallbackMutex.unlock();

	return err;
}

/** */
void DCHubSearch::timerDone()
{
	int i,t;

	if ( m_eSearchState == egssREADY )
	{
		switch ( CSearchManager::Instance()->SearchType() )
		{
			case estyNONE:
				break;
			case estySINGLE:
			case estyMULTI:
				if ( CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback )
				{
					m_eSearchState = egssOTHER;
					Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
					SetSearchView(false);
				}
				else
				{
					AddLogMessage("Bug: someone else started our search (state=ready type=single/multi)\n");
					Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));
					m_eSearchState = egssSEARCH;
					SetSearchView(false);
				}
				break;
			case estyEXTERNAL:
				m_eSearchState = egssAUTO;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
				SetSearchView(false);
				
				/* show log message */
				ShowResults(false);
				
				break;
		}
	}
	else if ( m_eSearchState == egssOTHER )
	{
		switch ( CSearchManager::Instance()->SearchType() )
		{
			case estyNONE:
				m_eSearchState = egssREADY;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
				SetSearchView(true);
				break;
			case estySINGLE:
			case estyMULTI:
				/* other search is still running */
				break;
			case estyEXTERNAL:
				m_eSearchState = egssAUTO;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
				SetSearchView(false);
				break;
		}
	}
	else if ( m_eSearchState == egssAUTO )
	{
		switch ( CSearchManager::Instance()->SearchType() )
		{
			case estyNONE:
				/* show any more log messages */
				ShowResults(false);
				
				AddLogMessage(tr("Auto search finished"));
				
				m_eSearchState = egssREADY;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
				SetSearchView(true);
				break;
			case estySINGLE:
			case estyMULTI:
				if ( CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback )
				{
					m_eSearchState = egssOTHER;
					Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
					SetSearchView(false);
				}
				else
				{
					AddLogMessage("Bug: someone else started our search (state=auto type=single/multi)\n");
					Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));
					m_eSearchState = egssSEARCH;
					SetSearchView(false);
				}
				break;
			case estyEXTERNAL:
				/* auto search still running */
				/* show any more log messages */
				ShowResults(false);
				break;
		}
	}
	else if ( m_eSearchState == egssSEARCH )
	{
		ShowResults(false);
		qApp->processEvents();
		
		switch ( CSearchManager::Instance()->SearchType() )
		{
			case estyNONE:
				m_eSearchState = egssREADY;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Ready"));
				SetSearchView( true );
				setWindowTitle( tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()) );
				AddLogMessage( tr("Search ended with %1 results").arg(m_pSearchResultList->Count()) );
				
				if ( m_nFilteredResults > 0 )
				{
					AddLogMessage( tr("%1 results were filtered").arg(m_nFilteredResults) );
				}
				break;
			case estySINGLE:
			case estyMULTI:
				if ( CSearchManager::Instance()->GetCallBackFunction() == m_pOurCallback )
				{
					LineEdit_LOGHUBS->setText( 
						QString().setNum(CSearchManager::Instance()->HubIndex()) +
						"/" +
						QString().setNum(CSearchManager::Instance()->HubCount()) +
						" (" +
						QString().setNum(CSearchManager::Instance()->HubError()) +
						")" );

					i=0;
					if (CSearchManager::Instance()->HubCount()>0)
						i = ((CSearchManager::Instance()->HubIndex()*100)/CSearchManager::Instance()->HubCount());
					ProgressBar_LOGHUBS->setValue(i);

					i = time(0)-CSearchManager::Instance()->StartTime();

					if ( CSearchManager::Instance()->HubIndex() > 0 )
						t = ((i*CSearchManager::Instance()->HubCount())/CSearchManager::Instance()->HubIndex());
					else
						t = 0;

					LineEdit_LOGTIME->setText( (CUtils::GetTimeString(i) + "/" + CUtils::GetTimeString(t)).Data() );

				}
				else
				{
					m_eSearchState = egssOTHER;
					Label_GLOBAL_SEARCH_STATUS->setText(tr("Other search..."));
					
					setWindowTitle( tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()) );
					AddLogMessage( tr("Search ended with %1 results").arg(m_pSearchResultList->Count()) );
					
					if ( m_nFilteredResults > 0 )
					{
						AddLogMessage( tr("%1 results were filtered").arg(m_nFilteredResults) );
					}
				}
				break;
			case estyEXTERNAL:
				m_eSearchState = egssAUTO;
				Label_GLOBAL_SEARCH_STATUS->setText(tr("Auto search..."));
				
				setWindowTitle( tr("%1 - %2 Results").arg(GetSearchQueryString()).arg(m_pSearchResultList->Count()) );
				AddLogMessage( tr("Search ended with %1 results").arg(m_pSearchResultList->Count()) );
				
				if ( m_nFilteredResults > 0 )
				{
					AddLogMessage( tr("%1 results were filtered").arg(m_nFilteredResults) );
				}
				break;
		}
	}

	// restart timer
	m_Timer.setSingleShot( true );
	m_Timer.start( 500 );
}

/** */
void DCHubSearch::ShowResults( bool bClearList )
{
	CDCMessage *DCMsg;
	bool bupdate;

	if ( bClearList )
	{
		ClearSearchResults();
	}

	if ( SocketCallbackMutex.tryLock() == false )
	{
		return;
	}

	bupdate = false;

	if ( m_pMessageList != 0 )
	{
		while( (DCMsg = m_pMessageList->Next(0)) != 0 )
		{
			m_pMessageList->Remove(DCMsg);

			switch ( DCMsg->m_eType )
			{
				case DC_MESSAGE_SEARCHRESULT:
				{
					bool b;
					b = DC_SearchResult( (CMessageSearchResult *) DCMsg );

					if ( b == true )
					{
						DCMsg = 0;
						bupdate = true;
						ListView_SEARCHRESULT->setUpdatesEnabled(false);
					}

					break;
				}

				case DC_MESSAGE_SEARCHRESULT_USER:
				{
					CMessageSearchResultUser * msg = (CMessageSearchResultUser*) DCMsg;

					QTreeWidgetItem * item = new QTreeWidgetItem( TreeWidget_SEARCHRESULTUSER );
					item->setText(0,msg->m_sNick.Data());
					item->setText(1,msg->m_sHubName.Data());
					break;
				}

				case DC_MESSAGE_LOG:
				{
					DC_LogMessage( (CMessageLog *) DCMsg );

					break;
				}

				default:
				{
					break;
				}
			}

			if ( DCMsg )
				delete DCMsg;
		}
	}

	SocketCallbackMutex.unlock();

	if ( bupdate )
	{
		ListView_SEARCHRESULT->setUpdatesEnabled(true);
		ListView_SEARCHRESULT->triggerUpdate();
	}
}

/** search result */
bool DCHubSearch::DC_SearchResult( CMessageSearchResult * MessageSearchResult )
{
	bool res = false;

	if ( CSearchManager::Instance()->SearchType() == estyNONE )
	{
		return res;
	}

	if ( SpinBox_MAXRESULT->value() > 0 )
	{
		if ( m_pSearchResultList->Count() >= SpinBox_MAXRESULT->value() )
		{
			++m_nFilteredResults;
			return res;
		}
	}

	/* SRs will have already been adjusted by CClient if in passive mode */
	if ( g_pConfig->GetAdjustSearchResultHubNames() &&
	     (g_pConfig->GetMode() == ecmACTIVE) )
	{
		if ( g_pConnectionManager->GetConnectedHubCount() == 1 )
		{
			if ( RadioButton_CONNECTEDHUBS->isChecked() ||
			     RadioButton_CONNECTEDSINGLEHUB->isChecked() )
			{
				std::map<CString, CString> * hubmap = g_pConnectionManager->GetConnectedHubServerMap();
				
				if ( hubmap != 0 )
				{
					std::map<CString, CString>::const_iterator it = hubmap->begin();
					if ( it != hubmap->end() )
					{
						MessageSearchResult->m_sHubName = it->first;
						MessageSearchResult->m_sHubHost = it->second;
						// printf( "Adjusted hub name to %s\n", it->first.Data() );
						// printf( "Adjusted hub host to %s\n", it->second.Data() );
					}
					
					delete hubmap;
				}
			}
		}
		else
		{
			if ( RadioButton_CONNECTEDSINGLEHUB->isChecked() )
			{
				QString both = ComboBox_CONNECTEDHUBS->currentText();
				if ( !both.isEmpty() )
				{
					MessageSearchResult->m_sHubName = both.left( both.lastIndexOf('(') ).toAscii().constData();
					int start = both.lastIndexOf('(') + 1;
					MessageSearchResult->m_sHubHost = both.mid( start, both.length() - (start+1) ).toAscii().constData();
				}
			}
		}
	}

	ShowSearchResult( MessageSearchResult, ListView_SEARCHRESULT );

	res = true;

	m_pSearchResultList->Add(MessageSearchResult);

	LCDNumber_RESULTS->display( (int) m_pSearchResultList->Count() );

	return res;
}

/** */
Q3ListViewItem * DCHubSearch::ShowSearchResult( CMessageSearchResult * MessageSearchResult, Q3ListView * parent )
{
	DC_ListResult * item;
	
	if ( m_eCurrentGrouping == egsrtNONE )
	{
		item = new DC_ListResult(parent);
		AddSearchResult( MessageSearchResult, item );
	}
	else
	{
		CDir cdir;
		CString filename, pathname;
		QString key;
		int column;
		DC_ListResult * groupitem;
		
		switch ( m_eCurrentGrouping )
		{
			case egsrtFILE:
				column = RESULTS_COLUMN_FILE;
				cdir.SplitPathFile(MessageSearchResult->m_sFile,pathname,filename);
				key = QString::fromAscii(filename.Data());
				break; 
			case egsrtSIZE:
				column = RESULTS_COLUMN_SIZE;
				key.setNum(MessageSearchResult->m_nSize);
				break;
			case egsrtNICK:
				column = RESULTS_COLUMN_NICK;
				key = QString::fromAscii(MessageSearchResult->m_sNick.Data());
				break;
			case egsrtSLOTS_FREE:
				column = RESULTS_COLUMN_FREESLOTS;
				key.setNum(MessageSearchResult->m_nFreeSlot);
				break;
			case egsrtSLOTS_TOTAL:
				column = RESULTS_COLUMN_TOTALSLOTS;
				key.setNum(MessageSearchResult->m_nMaxSlot);
				break;
			case egsrtHUB:
				column = RESULTS_COLUMN_HUB;
				key = QString::fromAscii(MessageSearchResult->m_sHubName.Data());
				break;
			case egsrtPATH:
				column = RESULTS_COLUMN_PATH;
				cdir.SplitPathFile(MessageSearchResult->m_sFile,pathname,filename);
				key = QString::fromAscii(pathname.Data());
				break;
			case egsrtHOST:
				column = RESULTS_COLUMN_HOST;
				key = QString::fromAscii(MessageSearchResult->m_sHubHost.Data());
				break;
			case egsrtHASH:
				column = RESULTS_COLUMN_TTH;
				key = QString::fromAscii(MessageSearchResult->m_sHash.Data());
				break;
			case egsrtIP:
				column = RESULTS_COLUMN_IP;
				key = QString::fromAscii(MessageSearchResult->m_sSrcIP.Data());
				break;
			default:
				column = RESULTS_COLUMN_FILE;
				cdir.SplitPathFile(MessageSearchResult->m_sFile,pathname,filename);
				key = QString::fromAscii(filename.Data());
				break;
		}
		
		if ( key.isEmpty() )
		{
			if ( m_pEmptyGroup == 0 )
			{
				m_pEmptyGroup = new DC_ListResult( ListView_SEARCHRESULT );
				m_pEmptyGroup->setSelectable( false );
				m_pEmptyGroup->mycol = RESULTS_COLUMN_COUNT;
				m_pEmptyGroup->setOpen( true );
			}
			
			groupitem = m_pEmptyGroup;
		}
		else
		{
			groupitem = m_GroupHash.value(key);
			
			if ( groupitem == 0 )
			{
				groupitem = new DC_ListResult( ListView_SEARCHRESULT );
				groupitem->setSelectable( false );
				groupitem->mycol = RESULTS_COLUMN_COUNT;
				groupitem->setOpen( true );
				
				groupitem->setText( column, key );
				m_GroupHash.insert( key, groupitem );
			}
		}
		
		item = new DC_ListResult( groupitem );
		AddSearchResult( MessageSearchResult, item );
		
		groupitem->myvalue = groupitem->childCount();
		groupitem->setText( RESULTS_COLUMN_COUNT, QString().setNum(groupitem->myvalue) );
	}

	return item;
}

/** */
Q3ListViewItem * DCHubSearch::ShowSearchResult( CMessageSearchResult * MessageSearchResult, DC_ListResult * parent )
{
	DC_ListResult *item = new DC_ListResult(parent);

	AddSearchResult( MessageSearchResult, item );

	return item;
}

/** */
bool DCHubSearch::AddSearchResult( CMessageSearchResult * MessageSearchResult, DC_ListResult * item )
{
	bool res = false;
	bool visible = true;
	int i;
	QString s;
	CString spath,sname;

	if ( MessageSearchResult->m_nFreeSlot < (unsigned int)SpinBox_FREESLOTS->value() )
	{
		visible = false;
	}

	s = MessageSearchResult->m_sFile.Data();

	if (CheckBox_SEARCHFILEONLY->isChecked())
	{
	        // Checks if s matches the search pattern
		QString search_pattern;
		//for(i=0;i<=LineEdit_SEARCH->text().contains(" ");i++)
		for(i=0;i<=Combobox_SEARCH->currentText().count(" ");i++)
		{
		        //search_pattern = LineEdit_SEARCH->text().section(" ",i,i);
		        search_pattern = Combobox_SEARCH->currentText().section(" ",i,i);

			if ( !(s.contains(search_pattern,Qt::CaseInsensitive) ) )
			{
				visible = false;
			}
		}
	}

	// split filename and path
	s = MessageSearchResult->m_sFile.Data();

	CDir().SplitPathFile(s.toAscii().constData(),spath,sname);

	item->myvalue = MessageSearchResult->m_nSize;
	item->mycol   = RESULTS_COLUMN_SIZE;

	item->setText(RESULTS_COLUMN_FILE,QString::fromAscii(sname.Data()));
	if (MessageSearchResult->m_bFolder)
	{
		item->setText(RESULTS_COLUMN_SIZE,tr("Folder"));
		
		item->setPixmap(RESULTS_COLUMN_FILE,g_pIconLoader->GetPixmap(eiFOLDER_BLUE));
	}
	else
	{
		item->setText(RESULTS_COLUMN_SIZE,DCGuiUtils::GetSizeString(MessageSearchResult->m_nSize));
		
		eFileTypes type = CFileManager::Instance()->GetFileType(sname);
		QPixmap icon;
		
		switch (type)
		{
			case eftMP3:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_MP3);
				break;
			case eftARCHIVE:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_ARCHIVE);
				break;
			case eftDOCUMENT:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_DOCUMENT);
				break;
			case eftAPPLICATION:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_APPLICATION);
				break;
			case eftPICTURE:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_PICTURE);
				break;
			case eftVIDEO:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_VIDEO);
				break;
			default:
				icon = g_pIconLoader->GetPixmap(eiFILETYPE_UNKNOWN);
				break;
		}
		
		item->setPixmap(RESULTS_COLUMN_FILE,icon);
	}
	item->setText(RESULTS_COLUMN_TTH,MessageSearchResult->m_sHash.Data());
	item->setText(RESULTS_COLUMN_NICK,QString::fromAscii(MessageSearchResult->m_sNick.Data()));
	item->setText(RESULTS_COLUMN_FREESLOTS,QString().setNum(MessageSearchResult->m_nFreeSlot));
	item->setText(RESULTS_COLUMN_TOTALSLOTS,QString().setNum(MessageSearchResult->m_nMaxSlot));
	item->setText(RESULTS_COLUMN_HUB,QString::fromAscii(MessageSearchResult->m_sHubName.Data()));
	item->setText(RESULTS_COLUMN_PATH,QString::fromAscii(spath.Data()));
	item->setText(RESULTS_COLUMN_HOST,MessageSearchResult->m_sHubHost.Data());
	item->setText(RESULTS_COLUMN_IP,MessageSearchResult->m_sSrcIP.Data());
	
	item->setVisible(visible);
	item->setEnabled(visible);

	return res;
}

/** */
void DCHubSearch::GroupSearchResults( eGroupSearchResultType type )
{
	if ( m_eCurrentGrouping == type )
	{
		return;
	}
	
	CMessageSearchResult * msg = 0;

	ListView_SEARCHRESULT->setUpdatesEnabled(false);
	
	ClearSearchResults();

	ListView_SEARCHRESULT->setRootIsDecorated((type != egsrtNONE));

	m_eCurrentGrouping = type;

	if ( type == egsrtNONE )
	{
		ListView_SEARCHRESULT->hideColumn(RESULTS_COLUMN_COUNT);
		while ( (msg = m_pSearchResultList->Next(msg)) != 0 )
		{
			AddSearchResult( msg, new DC_ListResult( ListView_SEARCHRESULT ) );
		}
	}
	else
	{
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_COUNT, ListView_SEARCHRESULT->columnWidth( RESULTS_COLUMN_FREESLOTS ) );
		
		while ( (msg = m_pSearchResultList->Next(msg)) != 0 )
		{
			ShowSearchResult( msg, ListView_SEARCHRESULT );
		}
		
		ListView_SEARCHRESULT->setSorting( RESULTS_COLUMN_COUNT );
		ListView_SEARCHRESULT->sort();
		
	}

	ListView_SEARCHRESULT->setUpdatesEnabled(true);
	ListView_SEARCHRESULT->triggerUpdate();
}

/** */
void DCHubSearch::DC_LogMessage( CMessageLog * MessageLog )
{
	AddLogMessage( QString::fromAscii(MessageLog->sMessage.Data()) );
}

/** */
void DCHubSearch::AddLogMessage( QString message )
{
	bool bscroll;

	if ( TextEdit_LOG->verticalScrollBar()->maximum() == TextEdit_LOG->verticalScrollBar()->value() )
	{
		bscroll = true;
	}
	else
	{
		bscroll = false;
	}
	
	message = QDateTime::currentDateTime().toString( "yyyy-MM-dd hh:mm.ss " ) + message;
	
	TextEdit_LOG->append(message);
	
	if ( bscroll )
	{
		// QT3 TextEdit_LOG->scrollToBottom();
		// QT3 TextEdit_LOG->moveCursor( Q3TextEdit::MoveEnd, false );
		
		TextEdit_LOG->moveCursor( QTextCursor::End, QTextCursor::MoveAnchor );
	}
}

QString DCHubSearch::GetSearchQueryString()
{
	if ( (TreeWidget_SEARCH->topLevelItemCount() == 1) && (TreeWidget_SEARCH->topLevelItem(0) != 0) )
	{
		return TreeWidget_SEARCH->topLevelItem(0)->text(1);
	}
	else
	{
		return tr("%1 items").arg( TreeWidget_SEARCH->topLevelItemCount() );
	}
}

/** */
CDCMessage * DCHubSearch::GetSearchObject()
{
	CMessageSearchUser * MessageSearchUser = 0;
	CMessageSearchFile * MessageSearchFile = 0;

	//if ( LineEdit_SEARCH->text().isEmpty() )
	if ( Combobox_SEARCH->currentText().isEmpty() )
	{
		return 0;
	}

	// set searchmode
	if ( ComboBox_SEARCHTYPE->currentIndex() == 0 )
	{
		MessageSearchUser = new CMessageSearchUser();
		//MessageSearchUser->m_sNick = LineEdit_SEARCH->text().toAscii().constData();
		MessageSearchUser->m_sNick = Combobox_SEARCH->currentText().simplified().toAscii().constData();

		return MessageSearchUser;
	}

	MessageSearchFile = new CMessageSearchFile();

	MessageSearchFile->m_eFileType = eFileTypes(ComboBox_SEARCHTYPE->currentIndex());
	
	MessageSearchFile->m_sString = Combobox_SEARCH->currentText().simplified().toAscii().constData();

	if ( MessageSearchFile->m_eFileType == eftHASH )
	{
		// fix tth
		MessageSearchFile->m_sString = MessageSearchFile->m_sString.ToUpper();
		
		// cannot check TTH valid here because it comes up twice
	}
	
	MessageSearchFile->m_bLocal = (g_pConfig->GetMode() == ecmPASSIVE);
	/* CClient handles setting source to the correct nick or IP */
	
	MessageSearchFile->m_nSize = LineEdit_SEARCHSIZE->text().toULongLong();

	if ( ComboBox_SEARCHUNIT->currentIndex() == 1 )
	{
		MessageSearchFile->m_nSize *= 1024;
	}
	else if ( ComboBox_SEARCHUNIT->currentIndex() == 2 )
	{
		MessageSearchFile->m_nSize *= 1024*1024;
	}
	else if ( ComboBox_SEARCHUNIT->currentIndex() == 3 )
	{
		MessageSearchFile->m_nSize *= 1024*1024*1024;
	}

	if ( ComboBox_SEARCHLIMIT->currentIndex() == 1 )
	{
		/* disable size limit if search is "at least 0 bytes" */
		MessageSearchFile->m_bSizeLimit = (MessageSearchFile->m_nSize > 0);
		
		MessageSearchFile->m_eSizeType = esstATLEAST;
	}
	else if ( ComboBox_SEARCHLIMIT->currentIndex() == 2 )
	{
		MessageSearchFile->m_bSizeLimit = true;
		MessageSearchFile->m_eSizeType = esstATMOST;
	}
	else if ( ComboBox_SEARCHLIMIT->currentIndex() == 0 )
	{
		MessageSearchFile->m_bSizeLimit = false;
		MessageSearchFile->m_eSizeType = esstATLEAST;
	}

	return MessageSearchFile;
}

/** */
void DCHubSearch::slotRefreshConnectedHubs()
{
	ComboBox_CONNECTEDHUBS->clear();

	std::map<CString, CString> * hubmap = g_pConnectionManager->GetConnectedHubServerMap();
	if ( hubmap != 0 )
	{
		for ( std::map<CString, CString>::const_iterator it = hubmap->begin(); it != hubmap->end(); ++it )
		{
			// add hubname with hubhost
			ComboBox_CONNECTEDHUBS->addItem( QString::fromAscii((it->first + " (" + it->second +")").Data()) );
		}

		delete hubmap;
	}
}

/** */
void DCHubSearch::UpdateHidden( bool SearchFileOnly, int FreeSlots, bool TextFilter )
{
	Q3ListViewItemIterator iter( ListView_SEARCHRESULT );

	ListView_SEARCHRESULT->setUpdatesEnabled(false);

	for ( ; iter.current(); iter++ )
	{
		bool visible = true;
		QString path_file;

		path_file = iter.current()->text(RESULTS_COLUMN_PATH) + iter.current()->text(RESULTS_COLUMN_FILE);

		if ( SearchFileOnly )
		{
			// Checks if iter.current() path+filename matches the search pattern
			QString search_pattern;
			//for(int j=0; j<=LineEdit_SEARCH->text().contains(" "); j++)
			for(int j=0; j<=Combobox_SEARCH->currentText().count(" "); j++)
                        {
				//search_pattern = LineEdit_SEARCH->text().section(" ",j,j);
				search_pattern = Combobox_SEARCH->currentText().section(" ",j,j);
				if ( !(path_file.contains(search_pattern,Qt::CaseInsensitive)) )
                                {
					visible = false;
                                }
			}
		}

		// filtering with include
		if (TextFilter && (LineEdit_INCLUDE->text().isEmpty() == false))
		{
			/* QString search_pattern;
			for(int i=0; search_pattern = LineEdit_INCLUDE->text().section(" ",i,i,QString::SectionSkipEmpty); i++)
			{
				if ( path_file.find(search_pattern,0,false) == -1 )
				{
					visible = false;
				}
			} */
		}
		// filtering with exclude
		if (TextFilter && (LineEdit_EXCLUDE->text().isEmpty() == false))
		{
			/* QString search_pattern;
			for(int i=0; search_pattern = LineEdit_EXCLUDE->text().section(" ",i,i,QString::SectionSkipEmpty); i++)
			{
				if ( path_file.find(search_pattern,0,false) != -1 )
				{
					visible = false;
				}
			} */
		}
		
		if ( visible )
		{
			if ( iter.current()->text(RESULTS_COLUMN_FREESLOTS).toInt() < FreeSlots )
			{
				visible = false;
			}
		}

		iter.current()->setVisible(visible);
		iter.current()->setEnabled(visible);
	}

	ListView_SEARCHRESULT->setUpdatesEnabled(true);
	ListView_SEARCHRESULT->triggerUpdate();
}

/** */
void DCHubSearch::slotTextFilterResults()
{
	UpdateHidden( CheckBox_SEARCHFILEONLY->isChecked(), SpinBox_FREESLOTS->value(), true );
}

/** */
void DCHubSearch::slotTextFilterReset()
{
	UpdateHidden( CheckBox_SEARCHFILEONLY->isChecked(), SpinBox_FREESLOTS->value(), false );
}

/** */
void DCHubSearch::slotToggledSearchFileOnly( bool chkstate )
{
	UpdateHidden( chkstate, SpinBox_FREESLOTS->value() );
}

/** */
void DCHubSearch::slotChangedFreeSlots( int free_slots )
{
	UpdateHidden( CheckBox_SEARCHFILEONLY->isChecked(), free_slots );
}

/** */
void DCHubSearch::slotSearchReturnPressed()
{
	if ( m_eSearchState == egssREADY )
	{	
		startSearch();
	}
	else
	{
		CSearchManager::Instance()->StopSearch();
		PushButton_SEARCH->setEnabled(false);
	}
}

/** */
void DCHubSearch::startSearch()
{
	CDCMessage * DCMessage;
	CStringList<CString> * hublist = 0;

	eSearchMode searchmode;
	eSearchType searchtype;
	
	if ( CSearchManager::Instance()->SearchType() != estyNONE )
	{
		QMessageBox::critical( this, tr("Hub Search Error"),
			tr("Another search is already running!") );
		return;
	}

	// clear messagelist
	SocketCallbackMutex.lock();

	/* FIXME why not just clear the list? CList has auto-delete */
	while ( (DCMessage = m_pMessageList->Next(0)) != 0 )
	{
		m_pMessageList->Del(DCMessage);
	}

	m_pSearchResultList->Clear();

	SocketCallbackMutex.unlock();

	// clear searchresults
	ClearSearchResults();
	TreeWidget_SEARCHRESULTUSER->clear();

	// reset counter
	LCDNumber_RESULTS->display(0);
	ProgressBar_LOGHUBS->setMinimum(0);
	ProgressBar_LOGHUBS->setMaximum(100);
	ProgressBar_LOGHUBS->setValue(0);
	m_nFilteredResults = 0;

	// reset
	LineEdit_LOGTIME->clear();
	LineEdit_LOGHUBS->setText("0/0");

	// clear the list on new search if only one search in the list
	if ( (m_pSearchQueryList != 0) && (m_pSearchQueryList->Count() == 1) )
	{
		m_pSearchQueryList->Clear();
		TreeWidget_SEARCH->clear();
	}

	// add a new search into empty list
	if ( (m_pSearchQueryList == 0) || (m_pSearchQueryList->Count() == 0) )
	{
		slotAddSearchQueue();
	}
	
	addHistory();
	
	if ( (m_pSearchQueryList == 0) || (m_pSearchQueryList->Count() == 0) )
	{
		QMessageBox::critical( this, tr("Hub Search Error"),
				       tr("Please add a search!") );
		return;
	}

	// single or multi search
	if ( CheckBox_MULTISEARCH->isChecked() )
	{
		searchtype = estyMULTI;
	}
	else
	{
		searchtype = estySINGLE;
	}

	if ( (g_pConfig->GetMode() == ecmPASSIVE) && (searchtype == estyMULTI) )
	{
		QMessageBox::critical( this, tr("Hub Search Error"),
			tr("Multi Search only work in active mode!") );
		return;
	}

	if ( CSearchManager::Instance()->GetCallBackFunction() != m_pOurCallback )
	{
		CSearchManager::Instance()->SetCallBackFunction( m_pOurCallback );
	}

	// set searchmode
	if ( RadioButton_CONNECTEDSINGLEHUB->isChecked() )
	{
		searchmode = esmCONNECTEDSINGLE;
	}
	else if ( RadioButton_CONNECTEDHUBS->isChecked() )
	{
		searchmode = esmCONNECTEDALL;
	}
	else if ( RadioButton_FILTEREDHUBS->isChecked() )
	{
		hublist = g_pHubListManager->GetFilteredHubList();

		searchmode = esmPUBLIC;
	}
	else if ( RadioButton_AVAILABLEHUBS->isChecked() )
	{
		searchmode = esmPUBLIC;
	}
	else
	{
		searchmode = esmBOOKMARK;
	}

	// set max clients
	CSearchManager::Instance()->MaxClients(SpinBox_MAXTHREADS->value());
	// enable/disable tag
	CSearchManager::Instance()->EnableTag(CheckBox_ENABLETAG->isChecked());

	if ( searchmode == esmCONNECTEDSINGLE )
	{
		CString hubname;

		// get current hubname
		hubname = ComboBox_CONNECTEDHUBS->currentText().toAscii().constData();
		// extract hubhost
		hubname = hubname.Mid( hubname.FindRev('(')+1, hubname.Length()-hubname.FindRev('(')-2 );

		// check for empty hubhost
		if ( hubname.IsEmpty() )
		{
			QMessageBox::critical( this, tr("Hub Search Error"),
				tr("Please select a connected hub.") );

			return;
		}
		else
		{
			// add hubhost to the hublist
			hublist = new CStringList<CString>();
			hublist->Add( hubname, new CString(hubname) );
			
			AddLogMessage( tr("Search for \"%1\" on %2").arg(GetSearchQueryString(),ComboBox_CONNECTEDHUBS->currentText()) );
			
			if ( CSearchManager::Instance()->StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				QMessageBox::critical( this, tr("Hub Search Error"),
					tr("No connected hubs found.") );

				return;
			}
		}
	}
	else if ( searchmode == esmCONNECTEDALL )
	{
		AddLogMessage( tr("Search for \"%1\" on %2").arg(GetSearchQueryString(),tr("all connected hubs")) );
		
		if ( CSearchManager::Instance()->StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
		{
			QMessageBox::critical( this, tr("Hub Search Error"),
				tr("No connected hubs found.") );

			return;
		}
	}
	else
	{
		if ( searchmode == esmPUBLIC )
		{
			QString whichhubs;
			if ( hublist )
			{
				whichhubs = tr("%1 filtered public hubs").arg(hublist->Count());
			}
			else
			{
				whichhubs += tr("all public hubs");
			}
			
			AddLogMessage( tr("Search for \"%1\" on %2").arg(GetSearchQueryString(),whichhubs) );
			
			if ( CSearchManager::Instance()->StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				delete hublist;

				QMessageBox::critical( this, tr("Hub Search Error"),
					tr("No hubs found.") );

				return;
			}
		}
		else
		{
			AddLogMessage( tr("Search for \"%1\" on %2").arg(GetSearchQueryString(),tr("all bookmark hubs")) );
			if ( CSearchManager::Instance()->StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				QMessageBox::critical( this, tr("Hub Search Error"),
					tr("No hubs found.") );

				return;
			}
		}
	}

	// update view
	SetSearchView( false );
	m_eSearchState = egssSEARCH;
	Label_GLOBAL_SEARCH_STATUS->setText(tr("Search..."));
	
	setWindowTitle( tr("%1 - Searching").arg(GetSearchQueryString()) );

	return;
}

/** */
void DCHubSearch::slotAddSearchQueue()
{
	if ( eFileTypes(ComboBox_SEARCHTYPE->currentIndex()) == eftHASH )
	{
		CString tth = Combobox_SEARCH->currentText().simplified().toUpper().toAscii().constData();
		CByteArray dst;
		if ( CBase32::Decode( &dst, &tth ) != CFileHasher::HashSize() )
		{
			if ( QMessageBox::warning(
				this,
				tr("Add search"),
				tr("Invalid TTH"),
				QMessageBox::Ignore | QMessageBox::Cancel,
				QMessageBox::Cancel ) == QMessageBox::Cancel )
			{
				return;
			}
		}
	}
	
	CDCMessage * msg, *msg1;
	
	CMessageSearchUser * MessageSearchUser = 0;
	CMessageSearchFile * MessageSearchFile = 0;

	addHistory();

	if ( m_pSearchQueryList == 0 )
	{
		m_pSearchQueryList = new CList<CDCMessage>();
	}

	if ( (msg = GetSearchObject()) == 0 )
	{
		return;
	}

	if ( msg->m_eType == DC_MESSAGE_SEARCH_USER )
	{
		MessageSearchUser = (CMessageSearchUser *)msg;
	}
	else
	{
		MessageSearchFile = (CMessageSearchFile *)msg;
	}

	msg1 = 0;

	while ( (msg1 = m_pSearchQueryList->Next(msg1)) != 0 )
	{
		if ( msg1->m_eType == msg->m_eType )
		{
			//TODO: compare if search already in the list
			if ( MessageSearchUser )
			{
				if ( ((CMessageSearchUser*)msg1)->m_sNick == MessageSearchUser->m_sNick )
				{
					return;
				}
			}
			else
			{
				if ( ((CMessageSearchFile*)msg1)->m_sString == MessageSearchFile->m_sString )
				{
					return;
				}
			}
		}
	}

	DC_QSearchTreeWidgetItem *item = new DC_QSearchTreeWidgetItem( TreeWidget_SEARCH );

	item->p_msg = msg;

	if ( MessageSearchUser )
	{
		item->setText(0,tr("USER"));
		item->setText(1,MessageSearchUser->m_sNick.Data());
	}
	else
	{
		item->setText(0,tr("FILE"));
		item->setText(1,MessageSearchFile->m_sString.Data());
	}

	m_pSearchQueryList->Add(msg);
}

/** */
void DCHubSearch::slotReset()
{
	Combobox_SEARCH->clearEditText();
	LineEdit_SEARCHSIZE->setText("0");
	ComboBox_SEARCHUNIT->setCurrentIndex(0);
	ComboBox_SEARCHLIMIT->setCurrentIndex(1);
	ComboBox_SEARCHTYPE->setCurrentIndex(1);
	SpinBox_FREESLOTS->setValue(0);
	TreeWidget_SEARCH->clear();

	if ( m_pSearchQueryList != 0 )
	{
		m_pSearchQueryList->Clear();
	}
}

/** */
void DCHubSearch::slotDoubleClickedSearchResult( Q3ListViewItem * /*item*/ )
{
	Q3ListViewItem * curitem = ListView_SEARCHRESULT->currentItem();
	
	/* group parents are not selectable */
	if ( !curitem || (curitem->isSelectable() == false) )
	{
		return;
	}

	if ( curitem->text(RESULTS_COLUMN_SIZE) == tr("Folder") )
	{
		DownloadFolder( curitem );
	}
	else
	{
		// add file as new source
		DCFileTool::AddFileSource( this, curitem->text(RESULTS_COLUMN_NICK).toAscii().constData(),
				curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(),
				curitem->text(RESULTS_COLUMN_HOST).toAscii().constData(),
				(curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().constData(),
				curitem->text(RESULTS_COLUMN_FILE).toAscii().constData(),
				CString(),
				CString(),
				eltFILE,
				((DC_QListViewItem*)curitem)->myvalue,
				curitem->text(RESULTS_COLUMN_TTH).toAscii().constData() );
	}

	return;
/*
	int i;
	CString s;
	CByteArray cba;
	QByteArray qba;

	DC_ListResult *item1 = (DC_ListResult*)item;

	QStoredDrag *sd = new QStoredDrag( "dchubsearch/searchresult", this );

	s = item1->text(0).toAscii().constData();
	if ( s.Data() != 0 )
		cba.Append( s.Data(), s.Length() );
	cba.Append("\0",1);

	s = s.setNum(item1->myvalue);
	if ( s.Data() != 0 )
		cba.Append( s.Data(), s.Length() );
	cba.Append("\0",1);

	for(i=2;i<=6;i++)
	{
		s = item1->text(i).toAscii().constData();
		if ( s.Data() != 0 )
			cba.Append( s.Data(), s.Length() );
		cba.Append("\0",1);
	}

	qba.assign((const char*)cba.Data(),(unsigned int)cba.Size());
	sd->setEncodedData(qba);

	QDragObject *d = sd;
	d->dragCopy();
*/
}

/** */
void DCHubSearch::slotRightButtonClickedSearchResult( Q3ListViewItem * /*item*/, const QPoint &, int column )
{
	QAction * chosen = 0;
	ulonglong size;
        QList<Q3ListViewItem*> selitems;
        Q3ListViewItem * curitem, * item;
	bool isadmin = false, allowDL = true, addUserCommands = true;
	CXml xml;
	DCClient * client = 0;
	QMap<QAction*, DC_UserMenuCommand*> addedcommands;

	// user have select items ?
	DCGuiUtils::SelectedItems( ListView_SEARCHRESULT, selitems );
	
	// cannot download folders yet
	// check only one hub is selected for user commands
	
	QString hubhost;
	if ( selitems.count() > 0 )
	{
		hubhost = selitems.at(0)->text(RESULTS_COLUMN_HOST);
	}
	else
	{
		addUserCommands = false;
	}
	
	for ( int i = 0; i < selitems.size(); i++ )
	{
		curitem = selitems.at(i);
		
		if ( curitem->text(RESULTS_COLUMN_SIZE) == tr("Folder") )
		{
			allowDL = false;
		}
		
		if ( curitem->text(RESULTS_COLUMN_HOST) != hubhost )
		{
			addUserCommands = false;
		}
	}

	// check if admin for admin-menu
	for ( int i = 0; i < selitems.size(); i++ )
	{
		curitem = selitems.at(i);
		
		if ( g_pConnectionManager->IsAdmin(curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(),curitem->text(RESULTS_COLUMN_HOST).toAscii().constData()) == false )
		{
			isadmin = false;
			break;
		}
		else
		{
			isadmin = true;
		}
	}
	
	QMenu *m,*msort;

	m = new QMenu(this);

	QAction * dl = DCMenuHandler::addAction( m, emiDOWNLOAD, (selitems.count() > 0) && allowDL );
	QAction * dl_to = DCMenuHandler::addAction( m, emiDOWNLOAD_TO, (selitems.count() > 0) && allowDL );
	QAction * dl_as = DCMenuHandler::addAction( m, emiDOWNLOAD_AS, (selitems.count() > 0) && allowDL );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * dl_dir = DCMenuHandler::addAction( m, emiDOWNLOAD_FOLDER, (selitems.count() > 0) );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * repair = DCMenuHandler::addAction( m, emiDOWNLOAD_REPAIR, (selitems.count() > 0) && allowDL );
	QAction * binsec = DCMenuHandler::addAction( m, emiDOWNLOAD_BIN_SECTORS, (selitems.count() > 0) && allowDL );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * dl_in = DCMenuHandler::addAction( m, emiDOWNLOAD_IN, (selitems.count() > 0) && allowDL );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * browse = DCMenuHandler::addAction( m, emiBROWSE_USER_FILES, (selitems.count() > 0) );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * copycol = DCMenuHandler::addAction( m, emiCOPY_COLUMN_TO_CLIPBOARD, (selitems.count() > 0) );
	QAction * copyrow = DCMenuHandler::addAction( m, emiCOPY_ROW_TO_CLIPBOARD, (selitems.count() > 0) );
	QAction * dclink = DCMenuHandler::addAction( m, emiCOPYDCFILELINK, (selitems.count() == 1) );
	QAction * magnet = DCMenuHandler::addAction( m, emiCOPYMAGNETLINK, (selitems.count() > 0) );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * connhub = DCMenuHandler::addAction( m, emiCONNECT_TO_HUB, (selitems.count() > 0) );
	QAction * bookmark = DCMenuHandler::addAction( m, emiADD_BOOKMARK, (selitems.count() > 0) );
	DCMenuHandler::addAction( m, emiSEPARATOR );

	// insert submenu
	msort = DCMenuHandler::addMenu( m, emisGROUP, true );
	QAction * nogroup = DCMenuHandler::addAction( msort, emiDISABLE_GROUP, true );
	DCMenuHandler::addAction( msort, emiSEPARATOR );
	QAction * byfile = DCMenuHandler::addAction( msort, emiGROUP_BY_FILE, true );
	QAction * bysize = DCMenuHandler::addAction( msort, emiGROUP_BY_SIZE, true );
	QAction * byhash = DCMenuHandler::addAction( msort, emiGROUP_BY_HASH, true );
	QAction * bypath = DCMenuHandler::addAction( msort, emiGROUP_BY_PATH, true );
	DCMenuHandler::addAction( msort, emiSEPARATOR );
	QAction * bynick = DCMenuHandler::addAction( msort, emiGROUP_BY_NICK, true );
	QAction * byfreeslots = DCMenuHandler::addAction( msort, emiGROUP_BY_SLOTS_FREE, true );
	QAction * bytotalslots = DCMenuHandler::addAction( msort, emiGROUP_BY_SLOTS_TOTAL, true );
	QAction * byip = DCMenuHandler::addAction( msort, emiGROUP_BY_IP, true );
	DCMenuHandler::addAction( msort, emiSEPARATOR );
	QAction * byhub = DCMenuHandler::addAction( msort, emiGROUP_BY_HUB, true );
	QAction * byhost = DCMenuHandler::addAction( msort, emiGROUP_BY_HOST, true );

	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * kick = DCMenuHandler::addAction( m, emiKICK, isadmin && (selitems.count() > 0) );
	QAction * forcemove = DCMenuHandler::addAction( m, emiFORCE_MOVE, isadmin && (selitems.count() > 0) );
	DCMenuHandler::addAction( m, emiSEPARATOR );
	QAction * load = DCMenuHandler::addAction( m, emiLOAD );
	QAction * save = DCMenuHandler::addAction( m, emiSAVE, (m_pSearchResultList->Count() > 0) );

	if ( addUserCommands )
	{
		curitem = selitems.first();
		
		client = g_pConnectionManager->GetClientForHub( curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(), curitem->text(RESULTS_COLUMN_HOST).toAscii().constData() );
		
		if ( client != 0 )
		{
			addedcommands = client->AddMenuCommands( m, euccSearch );
		}
	}

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

	delete m;

	if ( chosen == 0 )
	{
		return;
	}
	else if ( chosen == dclink )
	{
		CString s;

                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			s += "DCFILE://";
			s += xml.ToUTF8(curitem->text(RESULTS_COLUMN_HOST).toAscii().constData());
			s += "?file=";
			s += CHttp::Encode((curitem->text(RESULTS_COLUMN_PATH) + curitem->text(RESULTS_COLUMN_FILE)).toAscii().constData());
			s += "&size=";
			s += CString::number(((DC_QListViewItem*)curitem)->myvalue);
			s += "&nick=";
			s += CHttp::Encode(curitem->text(RESULTS_COLUMN_NICK).toAscii().constData());
			s += "&hub=";
			s += CHttp::Encode(curitem->text(RESULTS_COLUMN_HUB).toAscii().constData());
			
			if ( !(curitem->text(RESULTS_COLUMN_TTH).isEmpty()) )
			{
				s += "&hash=";
				s += CHttp::Encode(curitem->text(RESULTS_COLUMN_TTH).toAscii().constData());
			}
			
			s += "\n";
		}

		QApplication::clipboard()->setText(QString::fromAscii(s.Data()).trimmed());
	}
	else if ( chosen == magnet )
	{
		//magnet:?xt=urn:tree:tiger:EOSA5AGTL5SD3VWCF3R2OH2WMGXV3S3R7SYN4YA&xl=708780032&dn=FC-6-i386-disc1.iso
		QString text;
		
		for ( int i = 0; i < selitems.size(); i++ )
		{
			curitem = selitems.at(i);
			
			text += "magnet:?xt=urn:tree:tiger:";
			text += curitem->text(RESULTS_COLUMN_TTH);
			
			// get size from myvalue used for numeric sorting
			text += "&xl=";
			text += QString().setNum(((DC_QListViewItem*)curitem)->myvalue);
			
			text += "&dn=";
			QString filename = QString(QUrl::toPercentEncoding(curitem->text(RESULTS_COLUMN_FILE)));
			filename.replace("%20","+");
			text += filename;
			
			text += "\n";
		}
		
		// remove trailing "\n"
		text = text.trimmed();
		
		QApplication::clipboard()->setText(text);
	}
	else if ( (chosen == dl) || (chosen == dl_as) || (chosen == dl_to)
		|| (chosen == repair) || (chosen == binsec) )
	{
		QString localrootpath;
		QString localname;

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

			if ( localrootpath.isEmpty() )
				return;
		}

                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			size = ((DC_QListViewItem *)curitem)->myvalue;
			localname = curitem->text(RESULTS_COLUMN_FILE);

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

				if ( localrootpath.isEmpty() )
					return;

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

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

			// add transfer to the waitlist
			eRepairType repairtype;
			if ( chosen == repair )
			{
				repairtype = ertBYTES;
			}
			else if ( chosen == binsec )
			{
				repairtype = ertSECTORS;
			}
			else
			{
				repairtype = ertNONE;
			}

			DCFileTool::CheckFile( this, curitem->text(RESULTS_COLUMN_NICK).toAscii().constData(), curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(), curitem->text(RESULTS_COLUMN_HOST).toAscii().constData(),
				(curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().constData(), localname.toAscii().constData(), CString(), localrootpath.toAscii().constData(), eltFILE,
				size, curitem->text(RESULTS_COLUMN_TTH).toAscii().constData(), false, repairtype );
		}

		// redraw the list
		ListView_SEARCHRESULT->triggerUpdate();
	}
	else if ( chosen == dl_in )
	{
		QString localrootpath;
		QString localname;
		QString localpath;

		curitem = selitems.first();

		size = ((DC_QListViewItem *)curitem)->myvalue;
		QString tth = curitem->text(RESULTS_COLUMN_TTH);

		// all files need equal size and TTH
                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			if ( size != ((DC_QListViewItem *)curitem)->myvalue )
				return;
			if ( tth != curitem->text(RESULTS_COLUMN_TTH) )
				return;
		}

		if ( DCFileTool::SelectFileSource( this, size, tth, localname, localrootpath, localpath ) == false )
		{
			return;
		}

                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			// add transfer to the waitlist
			DCFileTool::CheckFile( this, curitem->text(RESULTS_COLUMN_NICK).toAscii().constData(), curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(), curitem->text(RESULTS_COLUMN_HOST).toAscii().constData(),
					  (curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().constData(), localname.toAscii().constData(), localpath.toAscii().constData(), localrootpath.toAscii().constData(), eltFILE,
					  size, curitem->text(RESULTS_COLUMN_TTH).toAscii().constData(), true);
		}

		// redraw the list
		ListView_SEARCHRESULT->triggerUpdate();
	}
	else if ( chosen == browse )
	{
		/** add transfer to the waitlist */
		CString empty;
                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			CString hubname(curitem->text(RESULTS_COLUMN_HUB).toAscii().constData());
			CString hubhost(curitem->text(RESULTS_COLUMN_HOST).toAscii().constData());

			if ( g_pConnectionManager->IsHubOnline(hubname,hubhost) == ehsNONE )
			{
				switch( QMessageBox::warning( this, tr("Filelist download"),
							      tr("Not connected to required hub!"),
							      tr("Connect"),
							      tr("Cancel"), 0, 0, 1 ) )
				{
					case 0:
						 g_pConnectionManager->Connect(hubname,hubhost);
						 break;
					case 1:
						break;
				}
			}

			g_pTransferView->DLM_QueueAdd( curitem->text(RESULTS_COLUMN_NICK).toAscii().constData(), hubname, hubhost,
						DC_USER_FILELIST, DC_USER_FILELIST,
						(curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().constData(),
						empty,
						eltBUFFER,
						0, 0, 0, empty );
		}
	}
	else if ( chosen == dl_dir )
	{
		for ( int i = 0; i < selitems.size(); i++ )
		{
			DownloadFolder( selitems.at(i) );
		}
	}
	else if ( chosen == copycol )
	{
		QString s;

                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			s += curitem->text(column);
			s += "\n";
		}

		s = s.trimmed();
		QApplication::clipboard()->setText(s);
	}
	else if ( chosen == copyrow )
	{
		int idx;
		QString s;

                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			for( idx = 0; idx < ListView_SEARCHRESULT->columns(); idx++ )
			{
				s += curitem->text(idx);
				s += " ";
			}

			s += "\n";
		}

		s = s.trimmed();
		QApplication::clipboard()->setText(s);
	}
	else if ( chosen == connhub )
	{
		// connect to the hub
                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			g_pConnectionManager->Connect( curitem->text(RESULTS_COLUMN_HUB).toAscii().constData(), curitem->text(RESULTS_COLUMN_HOST).toAscii().constData() );
		}
	}
	else if ( chosen == bookmark )
	{
		// bookmark this hub
                for ( int i = 0; i < selitems.size(); i++ )
                {
			curitem = selitems.at(i);
			
			g_pHubListManager->AddBookmark( curitem->text(RESULTS_COLUMN_HUB), curitem->text(RESULTS_COLUMN_HOST), QString() );
		}
	}
	else if ( chosen == nogroup )
	{
		GroupSearchResults( egsrtNONE );
	}
	else if ( chosen == byfile )
	{
		GroupSearchResults( egsrtFILE );
	}
	else if ( chosen == bysize )
	{
		GroupSearchResults( egsrtSIZE );
	}
	else if ( chosen == byhash )
	{
		GroupSearchResults( egsrtHASH );
	}
	else if ( chosen == bynick )
	{
		GroupSearchResults( egsrtNICK );
	}
	else if ( chosen == byfreeslots )
	{
		GroupSearchResults( egsrtSLOTS_FREE );
	}
	else if ( chosen == bytotalslots )
	{
		GroupSearchResults( egsrtSLOTS_TOTAL );
	}
	else if ( chosen == byhub )
	{
		GroupSearchResults( egsrtHUB );
	}
	else if ( chosen == bypath )
	{
		GroupSearchResults( egsrtPATH );
	}
	else if ( chosen == byhost )
	{
		GroupSearchResults( egsrtHOST );
	}
	else if ( chosen == byip )
	{
		GroupSearchResults( egsrtIP );
	}
	else if ( chosen == kick )
	{
		// TODO: need a patch to kick a user only one time
		QString message;

		if ( ! DCClient::GetOPKickMessage( message, this ) )
		{
			return;
		}

		// kick all user
		while( !selitems.isEmpty() )
		{
			// take and remove this item from the list
			item = selitems.takeFirst();

			// kick this user
			g_pConnectionManager->OPKick( item->text(RESULTS_COLUMN_HUB), item->text(RESULTS_COLUMN_HOST), item->text(RESULTS_COLUMN_NICK), message );

			// search for same entry and remove it
			for ( int i = 0; i < selitems.size(); i++ )
			{
				curitem = selitems.at(i);
				
				if ( (curitem->text(RESULTS_COLUMN_NICK) == item->text(RESULTS_COLUMN_NICK)) &&
				     (curitem->text(RESULTS_COLUMN_HUB) == item->text(RESULTS_COLUMN_HUB)) &&
				     (curitem->text(RESULTS_COLUMN_HOST) == item->text(RESULTS_COLUMN_HOST)) )
				{
					selitems.removeAt(i);
					i--;
				}
			}
		}
	}
	else if ( chosen == forcemove )
	{
		// TODO: need a patch to move a user only one time
		QString message, host;

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

		// force move all user
		while( !selitems.isEmpty() )
		{
			// take and remove this item from the list
			item = selitems.takeFirst();
			
			// force move this user
			g_pConnectionManager->OPForceMove( item->text(RESULTS_COLUMN_HUB), item->text(RESULTS_COLUMN_HOST), item->text(RESULTS_COLUMN_NICK), message, host );

			// search for same entry and remove it
			for ( int i = 0; i < selitems.size(); i++ )
			{
				curitem = selitems.at(i);
				
				if ( (curitem->text(RESULTS_COLUMN_NICK) == item->text(RESULTS_COLUMN_NICK)) &&
				     (curitem->text(RESULTS_COLUMN_HUB) == item->text(RESULTS_COLUMN_HUB)) &&
				     (curitem->text(RESULTS_COLUMN_HOST) == item->text(RESULTS_COLUMN_HOST)) )
				{
					selitems.removeAt(i);
					i--;
				}
			}
		}
	}
	else if ( chosen == save )
	{
		QString filename = QFileDialog::getSaveFileName(
			this,
			tr("Choose a filename to save under") );

		if ( !filename.isEmpty() )
		{
			QFile file(filename);

			if ( file.open( QIODevice::WriteOnly ) )
			{
				CIconv ciconv( g_pConfig->GetLocalEncoding(), g_pConfig->GetRemoteEncoding() );
				CString s;
				CMessageSearchResult * msr = 0;
				
				while ( (msr = m_pSearchResultList->Next(msr)) != 0 )
				{
					s  = "$SR ";
					s += ciconv.encode(msr->m_sNick);
					s += " ";
					s += ciconv.encode(msr->m_sFile);
					if ( msr->m_bFolder == false )
					{
						s += 0x05;
						s += CString::number(msr->m_nSize);
					}
					s += " ";
					s += CString::number(msr->m_nFreeSlot);
					s += "/";
					s += CString::number(msr->m_nMaxSlot);
					s += 0x05;
					if ( msr->m_sHash.IsEmpty() )
					{
						s += ciconv.encode(msr->m_sHubName);
					}
					else
					{
						s += "TTH:";
						s += msr->m_sHash;
					}
					s += " (";
					s += msr->m_sHubHost;
					s += ")|\n";
					file.write(s.Data(),s.Length());
				}

				file.close();
			}
		}
	}
	else if ( chosen == load )
	{
		QString filename = QFileDialog::getOpenFileName(
			this,
			tr("Choose a file to open") );

		if ( !filename.isEmpty() )
		{
			QFile file(filename);
			if ( file.open( QIODevice::ReadOnly ) )
			{
				m_pSearchResultList->Clear();

				ListView_SEARCHRESULT->setUpdatesEnabled(false);
				ClearSearchResults();
				
				QByteArray buffer;
				buffer.resize(4096);

				CString m;
				int p;
				CDCMessage * dcmessage = 0;
				CMessageHandler MessageHandler;

				while( file.readLine(buffer.data(),buffer.size()) != -1 )
				{
					m = buffer.constData();
					p = 0;

					if ( MessageHandler.Parse(&m,p,&dcmessage) == DC_MESSAGE_SEARCHRESULT )
					{
						m_pSearchResultList->Add( (CMessageSearchResult*) dcmessage );
					}
				}

				file.close();

				eGroupSearchResultType current = m_eCurrentGrouping;
				
				// must change m_eCurrentGrouping to anything else
				// otherwise GroupSearchResults will do nothing
				if ( m_eCurrentGrouping == egsrtNONE )
				{
					m_eCurrentGrouping = egsrtFILE;
				}
				else
				{
					m_eCurrentGrouping = egsrtNONE;
				}
				
				GroupSearchResults( current );

				ListView_SEARCHRESULT->setUpdatesEnabled(true);
				ListView_SEARCHRESULT->triggerUpdate();
				LCDNumber_RESULTS->display( (int) m_pSearchResultList->Count() );
			}
		}
	}
	else if ( addedcommands.contains( chosen ) )
	{
		DC_UserMenuCommand * umc = addedcommands[ chosen ];
		
		QString usercommand = umc->m_sCommand;
		QString origUserCommand = usercommand;
		QStringList doneNicks;
		QString fullfilename, filesize, filesizeshort, filetth, filetype;
		
		for ( int i = 0; i < selitems.size(); i++ )
		{
			curitem = selitems.at(i);
			
			// check if already done this nick
			if ( (umc->m_nType == euctRawOnce) && doneNicks.contains(curitem->text(RESULTS_COLUMN_NICK)) )
			{
				continue;
			}
			
			usercommand = client->replaceCommandTags( origUserCommand, curitem->text(RESULTS_COLUMN_NICK) );
			
			if ( usercommand.isEmpty() )
			{
				// had a %[line:reason] but dialog was cancelled
				continue;
			}
			
			fullfilename = curitem->text(RESULTS_COLUMN_PATH);
			if ( (!fullfilename.isEmpty()) && (fullfilename.right(1) != "\\") )
			{
				fullfilename += "\\";
			}
			fullfilename += curitem->text(RESULTS_COLUMN_FILE);
			
			if ( curitem->text(RESULTS_COLUMN_SIZE) == tr("Folder") )
			{
				filesize = tr("Unknown");
				filesizeshort = tr("Unknown");
				filetth = tr("None");
				filetype = tr("Directory");
			}
			else
			{
				filesize.setNum(((DC_QListViewItem*)curitem)->myvalue);
				filesizeshort = curitem->text(RESULTS_COLUMN_SIZE);
				filetth = curitem->text(RESULTS_COLUMN_TTH);
				filetype = tr("File");
			}
			
			usercommand.replace( "%[file]", fullfilename );
			usercommand.replace( "%[fileFN]", fullfilename );
			
			usercommand.replace( "%[filesize]", filesize );
			usercommand.replace( "%[fileSI]", filesize );
			
			usercommand.replace( "%[filesizeshort]", filesizeshort );
			usercommand.replace( "%[fileSIshort]", filesizeshort );
			
			usercommand.replace( "%[tth]", filetth );
			usercommand.replace( "%[fileTR]", filetth );
			
			usercommand.replace( "%[type]", filetype );
			
			client->SendString( usercommand.toAscii().constData() );
			
			doneNicks << curitem->text(RESULTS_COLUMN_NICK);
		}
	}
}

/** */
void DCHubSearch::slotContextMenuSearch( const QPoint & )
{
	// TODO: handle delete,edit searches (multiselection support)
	QAction * chosen = 0;
        QList<QTreeWidgetItem*> selitems = TreeWidget_SEARCH->selectedItems();

	QMenu * m = new QMenu(this);

	QAction * remove = DCMenuHandler::addAction( m, emiREMOVE, selitems.count() > 0 );
	QAction * clear = DCMenuHandler::addAction( m, emiCLEAR );

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

	delete m;

	if ( chosen == remove )
	{
		RemoveSelectedSearch(&selitems);
	}
	else if ( chosen == clear )
	{
		if ( m_pSearchQueryList )
			m_pSearchQueryList->Clear();
		TreeWidget_SEARCH->clear();
	}
}

/** */
void DCHubSearch::RemoveSelectedSearch( QList<QTreeWidgetItem*> * list )
{
        QList<QTreeWidgetItem*> selitems;
        DC_QSearchTreeWidgetItem * curitem;

	if ( !list )
	{
		selitems = TreeWidget_SEARCH->selectedItems();
		
		if ( selitems.size() == 0 )
		{
			return;
		}

		list = &selitems;
	}

	for ( int i = 0; i < list->size(); i++ )
	{
		curitem = (DC_QSearchTreeWidgetItem *)list->at(i);
		
		if ( m_pSearchQueryList )
			m_pSearchQueryList->Del(curitem->p_msg);
		
		delete curitem;
	}
}

/** Clears the history of search strings */
void DCHubSearch::slotPurgeHistory()
{
	Combobox_SEARCH->clear();
	slotReset();
	m_pSearchHistory->Clear();
	TextEdit_LOG->clear();
}

/** Disable size entry if size limit not used */
void DCHubSearch::slotSizeLimitChanged( int index )
{
	if ( index == 0 )
	{
		LineEdit_SEARCHSIZE->setEnabled(false);
		ComboBox_SEARCHUNIT->setEnabled(false);
	}
	else
	{
		LineEdit_SEARCHSIZE->setEnabled(true);
		ComboBox_SEARCHUNIT->setEnabled(true);
	}
}

/** */
void DCHubSearch::SetSearchForFile( QString file, eFileTypes filetype, int sizepos, ulonglong size )
{
	Combobox_SEARCH->setEditText(file);
	
	ComboBox_SEARCHUNIT->setCurrentIndex(0);
	LineEdit_SEARCHSIZE->setText(QString().setNum(size));
	ComboBox_SEARCHLIMIT->setCurrentIndex(sizepos);
	
	ComboBox_SEARCHTYPE->setCurrentIndex(filetype);
	
	TabWidget_SEARCH_PARAMETERS->setCurrentIndex(0);
}

/** */
void DCHubSearch::StartSearchWithPrompt()
{
	if ( (m_eSearchState == egssREADY) ||
	     (QMessageBox::question(
	     	this,
	     	tr("Hub Search"),
		tr("Another search is running.\nStop other search?"),
		QMessageBox::Yes | QMessageBox::No,
		QMessageBox::No
	     ) == QMessageBox::Yes)
	  )
	{
		slotSearchReturnPressed();
	}
}

/** */
void DCHubSearch::ClearSearchResults()
{
	delete m_pEmptyGroup;
	m_pEmptyGroup = 0;
	
	m_GroupHash.clear();
	
	ListView_SEARCHRESULT->clear();
}

/** */
void DCHubSearch::DownloadFolder( Q3ListViewItem * item )
{
	CString hubname(item->text(RESULTS_COLUMN_HUB).toAscii().constData());
	CString hubhost(item->text(RESULTS_COLUMN_HOST).toAscii().constData());
	
	if ( g_pConnectionManager->IsHubOnline(hubname,hubhost) == ehsNONE )
	{
		int ret = QMessageBox::warning(
			this,
			tr("Folder download"),
			tr("Not connected to required hub!"),
			tr("Connect"),
			tr("Cancel"),
			0,
			0,
			1
		);
		
		if ( ret == 0 )
		{
			g_pConnectionManager->Connect(hubname,hubhost);
		}
	}
	
	CString dir(item->text(RESULTS_COLUMN_PATH).toAscii().constData());
	
	if ( item->text(RESULTS_COLUMN_SIZE) == tr("Folder") )
	{
		dir += item->text(RESULTS_COLUMN_FILE).toAscii().constData();
	}
	
	g_pTransferView->DLM_QueueAdd(
		item->text(RESULTS_COLUMN_NICK).toAscii().constData(),
		hubname,
		hubhost,
		DC_USER_FILELIST,
		DC_USER_FILELIST,
		dir,
		dir,
		eltBUFFER,
		0,
		0,
		0,
		CString()
	);
}
