/***************************************************************************
                          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.                                   *
 *                                                                         *
 ***************************************************************************/

/** 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>
#include <qtoolbutton.h>
#include <qinputdialog.h>
//Added by qt3to4:
#include <QKeyEvent>
#include <QResizeEvent>
#include <QEvent>
#include <QHeaderView>

/** */
#include <dclib/dcos.h>
#include <dclib/cutils.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 "dchubsearch.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>

/* column numbers for the search results listview */
#define RESULTS_COLUMN_FILE		0
#define RESULTS_COLUMN_SIZE		1
#define RESULTS_COLUMN_TTH		2
#define RESULTS_COLUMN_NICK		3
#define RESULTS_COLUMN_FREESLOTS	4
#define RESULTS_COLUMN_TOTALSLOTS	5
#define RESULTS_COLUMN_HUB		6
#define RESULTS_COLUMN_PATH		7
#define RESULTS_COLUMN_HOST		8

DCHubSearch * g_pHubSearch = 0;

/** */
DCHubSearch::DCHubSearch(QWidget* parent, const char *name, Qt::WindowFlags wflags) : QWidget(parent, name, wflags)
{
	int idx;

	setupUi(this);

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

	if ( (parent != 0) && (qobject_cast<QMdiArea*>(parent) != 0) )
	{
		m_pContainerWindow = new QMdiSubWindow();
		m_pContainerWindow->setWidget(this);
		(qobject_cast<QMdiArea*>(parent))->addSubWindow(m_pContainerWindow);
		m_pContainerWindow->setIcon( g_pIconLoader->GetPixmap(eiFIND) );
	}
	else
	{
		m_pContainerWindow = 0;
	}

	// set the ColumnWidthMode to manual, we will take care of this task
	for( idx = 0; idx < ListView_SEARCHRESULT->columns(); idx++ )
	{
		ListView_SEARCHRESULT->setColumnWidthMode( idx, Q3ListView::Manual );
	}

	for( idx = 0; idx < TreeWidget_SEARCHRESULTUSER->columnCount(); idx++ )
	{
		TreeWidget_SEARCHRESULTUSER->header()->setResizeMode( idx, QHeaderView::Interactive );
	}

	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 );
	ListView_SEARCHRESULT->setColumnAlignment( RESULTS_COLUMN_TOTALSLOTS, Qt::AlignRight );

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

	InitDocument();

	m_SearchManager.SetCallBackFunction( new CCallback<DCHubSearch>( this, &DCHubSearch::DC_CallBack ) );

	g_pHubSearch = this;
}

/** */
DCHubSearch::~DCHubSearch()
{
	g_pHubSearch = NULL;

	TreeWidget_SEARCH->removeEventFilter(this);

	m_Timer.stop();

	SocketCallbackThread.Lock();

	// cleanup all lists
	if ( m_pMessageList )
	{
		delete m_pMessageList;
		m_pMessageList = 0;
	}

	if ( m_pSearchResultList )
	{
		delete m_pSearchResultList;
		m_pSearchResultList = 0;
	}

	if ( m_pSearchQueryList )
	{
		delete m_pSearchQueryList;
		m_pSearchQueryList = 0;
	}

	if ( m_pSearchHistory )
	{
		delete m_pSearchHistory;
		m_pSearchHistory = 0;
	}
	
	SocketCallbackThread.UnLock();
}

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

	// restore settings
	if ( g_pConfig->GetMap("SEARCHVIEW",map) == TRUE )
	{
		if ( ((*map)["WIDTH"].toInt() > 0) && ((*map)["HEIGHT"].toInt() > 0) )
		{
			if ( m_pContainerWindow != 0 )
			{
				m_pContainerWindow->setGeometry( (*map)["X"].toInt(), (*map)["Y"].toInt(), (*map)["WIDTH"].toInt(), (*map)["HEIGHT"].toInt() );
			}
		}
		
		if ( (*map)["MAXTHREADS"] != "" )
		{
			SpinBox_MAXTHREADS->setValue( (*map)["MAXTHREADS"].toInt() );
		}
	}

	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( ToolButton_RESETFILTER, 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()) );
	
	m_bSearchState = FALSE;

	connect( &m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()) );
	m_Timer.start( 500, TRUE );

	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)["X"]         = QString().setNum(m_pContainerWindow->x());
		(*map)["Y"]         = QString().setNum(m_pContainerWindow->y());
		(*map)["WIDTH"]     = QString().setNum(m_pContainerWindow->width());
		(*map)["HEIGHT"]    = QString().setNum(m_pContainerWindow->height());
		(*map)["VISIBLE"]   = QString().setNum(m_pContainerWindow->isVisible());
		(*map)["MAXIMIZED"] = QString().setNum(m_pContainerWindow->isMaximized());
		(*map)["MINIMIZED"] = QString().setNum(m_pContainerWindow->isMinimized());
		(*map)["ENABLED"]   = QString().setNum(m_pContainerWindow->isEnabled());
	}
	(*map)["MAXTHREADS"] = QString().setNum(SpinBox_MAXTHREADS->value());
}

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

/** overridden so that the columns are resized on show() */
void DCHubSearch::show()
{
	QWidget::show();

	ResizeListViewColumn();
}

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

/** resize the ListView columns */
void DCHubSearch::ResizeListViewColumn()
{
	int width;

	if ( ListView_SEARCHRESULT->isVisible() )
	{
		width = ListView_SEARCHRESULT->width();

		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_FILE, ((width*7)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_SIZE, ((width*3)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_TTH, ((width*4)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_NICK, ((width*4)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_FREESLOTS, ((width*3)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_TOTALSLOTS, ((width*3)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_HUB, ((width*4)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_PATH, ((width*6)/37) );
		ListView_SEARCHRESULT->setColumnWidth( RESULTS_COLUMN_HOST, ((width*3)/37) );
	}

	if ( TreeWidget_SEARCHRESULTUSER->isVisible() )
	{
		width = TreeWidget_SEARCHRESULTUSER->width();

		TreeWidget_SEARCHRESULTUSER->header()->resizeSection( 0, ((width)/2) );
		TreeWidget_SEARCHRESULTUSER->header()->resizeSection( 1, ((width)/2) );
	}
}

/** 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->setCurrentItem(0);
		ComboBox_SEARCHLIMIT->setCurrentItem(0);
		ComboBox_SEARCHTYPE->setCurrentItem(0);
	}
	else
	{
		msf = (CMessageSearchFile *)hmsg;
		
		LineEdit_SEARCHSIZE->setText( CString().setNum(msf->m_nSize).Data() );
		ComboBox_SEARCHUNIT->setCurrentItem(0);
		
		switch(msf->m_eSizeType)
		{
			case esstATLEAST:
				ComboBox_SEARCHLIMIT->setCurrentItem(0);
				break;
			case esstATMOST:
				ComboBox_SEARCHLIMIT->setCurrentItem(1);
				break;
			case esstEXACT:
				ComboBox_SEARCHLIMIT->setCurrentItem(2);
				break;
			default:
				break;
		}
		
		ComboBox_SEARCHTYPE->setCurrentItem(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->insertItem(s1.Data());
				m_pSearchHistory->Del(hmsg);
				m_pSearchHistory->Add(msg);
				found = TRUE;
				break;
			}
		}
		
		i++;
	}
	
	if ( !found )
	{
		m_pSearchHistory->Add(msg);
		Combobox_SEARCH->insertItem(s1.Data());
	}
	
	Combobox_SEARCH->setCurrentItem(Combobox_SEARCH->count()-1);
}

/** */
int DCHubSearch::selectedItems( Q3ListView * list, QList<Q3ListViewItem*> & lst )
{
	Q3ListViewItemIterator it( (Q3ListView *)list );

	for ( ; it.current(); it++ )
	{
		if ( it.current()->isSelected() )
		{
			lst.append(it.current());
		}
	}

	return lst.count();
}

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

	//LineEdit_SEARCH->setEnabled(enabled);
	Combobox_SEARCH->setEnabled(enabled);
	ButtonGroup_HUBS->setEnabled(enabled);
	SpinBox_MAXTHREADS->setEnabled(enabled);
	PushButton_ADDQUEUE->setEnabled(enabled);
	GroupBox_FILTER->setEnabled(enabled);
	CheckBox_ENABLETAG->setEnabled(enabled);
	CheckBox_MULTISEARCH->setEnabled(enabled);
}

/** */
int DCHubSearch::DC_CallBack( CObject *, CObject * Object )
{
	SocketCallbackThread.Lock();

	int err = -1;

	if ( m_SearchManager.SearchType() != estyNONE )
	{
		if ( (Object != 0) && (m_pMessageList != 0) )
		{
			m_pMessageList->Add(Object);
			err = 0;
		}
        }

	SocketCallbackThread.UnLock();

	return err;
}

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

	if ( (m_bSearchState == FALSE) && (m_SearchManager.SearchType() != estyNONE) )
	{
		SetSearchView( FALSE );
	}
	else if ( (m_bSearchState == TRUE) && (m_SearchManager.SearchType() == estyNONE) )
	{
		SetSearchView( TRUE );
	}

	if ( (m_SearchManager.SearchType() != estyNONE) && (m_SearchManager.SearchType() != estyEXTERNAL) )
	{
		if ( m_SearchManager.SearchType() != estyNONE )
		{
			ShowResults(FALSE);
		}

		qApp->processEvents();
		
		LineEdit_LOGHUBS->setText( 
				(CString().setNum(m_SearchManager.HubIndex()) +
				"/" +
				CString().setNum(m_SearchManager.HubCount()) +
				" (" +
				CString().setNum(m_SearchManager.HubError()) +
				")").Data() );

		i=0;
		if (m_SearchManager.HubCount()>0)
			i = ((m_SearchManager.HubIndex()*100)/m_SearchManager.HubCount());
		ProgressBar_LOGHUBS->setValue(i);

		i = time(0)-m_SearchManager.StartTime();

		if ( m_SearchManager.HubIndex() > 0 )
			t = ((i*m_SearchManager.HubCount())/m_SearchManager.HubIndex());
		else
			t = 0;

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

	// restart timer
	m_Timer.start( 500, TRUE );
}

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

	if ( bClearList == TRUE )
	{
		ListView_SEARCHRESULT->clear();
	}

	if ( SocketCallbackThread.TryLock() == FALSE )
	{
		return;
	}

	bupdate = FALSE;

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

			DCMsg = (CDCMessage*) Object;

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

					if( b == TRUE )
					{
						Object  = 0;
						bupdate = TRUE;
						ListView_SEARCHRESULT->setUpdatesEnabled(FALSE);
					}

					break;
				}

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

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

					break;
				}

				default:
				{
					break;
				}
			}

			if ( Object )
				delete Object;
		}
	}

	SocketCallbackThread.UnLock();

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

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

	if ( m_SearchManager.SearchType() == estyNONE )
	{
		return res;
	}

	if ( SpinBox_MAXRESULT->value() > 0 )
	{
		if ( ListView_SEARCHRESULT->childCount() >= SpinBox_MAXRESULT->value() )
		{
			return res;
		}
	}

	size = CString(LineEdit_SEARCHSIZE->text().toAscii().data()).asULL();

	if ( ComboBox_SEARCHUNIT->currentItem() == 1 )
	{
		size *= 1024;
	}
	else if ( ComboBox_SEARCHUNIT->currentItem() == 2 )
	{
		size *= 1024*1024;
	}
	else if ( ComboBox_SEARCHUNIT->currentItem() == 3 )
	{
		size *= 1024*1024*1024;
	}

	if ( ComboBox_SEARCHLIMIT->currentItem() == 0 )
	{
		if ( MessageSearchResult->m_nSize < size )
		{
			return res;
		}
	}
	else if ( ComboBox_SEARCHLIMIT->currentItem() == 1 )
	{
		if ( MessageSearchResult->m_nSize > size )
		{
			return res;
		}
	}
	else if ( ComboBox_SEARCHLIMIT->currentItem() == 2 )
	{
		if ( MessageSearchResult->m_nSize != size )
		{
			return res;
		}
	}

	ShowSearchResult( MessageSearchResult, ListView_SEARCHRESULT );

	res = TRUE;

	m_pSearchResultList->Add(MessageSearchResult);

	LCDNumber_RESULTS->display(ListView_SEARCHRESULT->childCount());

	return res;
}

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

	AddSearchResult( MessageSearchResult, item );

	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;
	DCFileTool FileTool;

	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.find(search_pattern,0,FALSE) == -1 )
			{
				visible = FALSE;
			}
		}
	}

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

	CDir().SplitPathFile(s.toAscii().data(),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,CUtils::GetSizeString(MessageSearchResult->m_nSize,g_pConfig->GetUnit()).Data());
		
		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());
	
	/* SRs will have already been adjusted by CClient if in passive mode */
	if ( (g_pConfig->GetAdjustSearchResultHubNames() == TRUE) &&
	     (g_pConfig->GetMode() == ecmACTIVE) )
	{
		if ( g_pConnectionManager->GetConnectedHubCount() == 1 )
		{
			if ( (RadioButton_CONNECTEDHUBS->isChecked() == TRUE) ||
			     (RadioButton_CONNECTEDSINGLEHUB->isChecked() == TRUE) )
			{
				CStringList * StringList = g_pConnectionManager->GetConnectedHubServerList();
				CString * ps = 0;
				CString s;
				
				if ( StringList != 0 )
				{
					if ( StringList->Next( s, (CObject*&)ps ) != 0 )
					{
						item->setText( RESULTS_COLUMN_HUB, QString::fromAscii(s.Data()) );
						item->setText( RESULTS_COLUMN_HOST, ps->Data() );
						// printf( "Adjusted hub name to %s\n", item->text(RESULTS_COLUMN_HUB).toAscii().data() );
						// printf( "Adjusted hub host to %s\n", item->text(RESULTS_COLUMN_HOST).toAscii().data() );
					}
					
					delete StringList;
				}
			}
		}
		else
		{
			if ( RadioButton_CONNECTEDSINGLEHUB->isChecked() == TRUE )
			{
				QString both = ComboBox_CONNECTEDHUBS->currentText();
				if ( both != "" )
				{
					item->setText( RESULTS_COLUMN_HUB, both.left( both.findRev('(') ) );
					int start = both.findRev('(') + 1;
					item->setText( RESULTS_COLUMN_HOST, both.mid( start, both.length() - (start+1) ) );
				}
			}
		}
	}

	item->setVisible(visible);
	item->setEnabled(visible);

	return res;
}

/** */
void DCHubSearch::GroupSearchResults( eGroupSearchResultType type )
{
	CStringList sl;
	DC_ListResult * item;
	CMessageSearchResult * msg = 0;
	CString s,st;

	ListView_SEARCHRESULT->setUpdatesEnabled(FALSE);
	ListView_SEARCHRESULT->clear();

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

	while ( (msg=(CMessageSearchResult *)m_pSearchResultList->Next((CObject*)msg)) != 0 )
	{
		item = 0;

		switch(type)
		{
			case egsrtFILE:
				CDir().SplitPathFile(msg->m_sFile,st,s);
				break;
			case egsrtSIZE:
				s = CString().setNum(msg->m_nSize);
				break;
			case egsrtNICK:
				s = msg->m_sNick;
				break;
			case egsrtHASH:
				s = msg->m_sHash;
				break;
			case egsrtSLOTS_FREE:
				s = CString().setNum(msg->m_nFreeSlot);
				break;
			case egsrtHUB:
				s = msg->m_sHubName;
				break;
			case egsrtPATH:
				CDir().SplitPathFile(msg->m_sFile,s,st);
				break;
			case egsrtHOST:
				s = msg->m_sHubHost;
				break;
			case egsrtNONE:
			default:
				s = "";
				break;
		}

		if ( (s != "") && (sl.Get(s,(CObject*&)item) == 0) )
		{
			ShowSearchResult(msg,item);
		}
		else
		{
			item = (DC_ListResult *)ShowSearchResult(msg,ListView_SEARCHRESULT);

			if ( s != "" )
				sl.Add( s, (CObject*)item );
		}
	}

	item = 0;
	s = "";

	while( sl.Next( s, (CObject*&)item ) != 0 )
	{
		sl.Remove(s);
		item = 0;
	}

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

/** */
void DCHubSearch::DC_LogMessage( CMessageLog * MessageLog )
{
	bool bscroll;

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

	TextEdit_LOG->append(MessageLog->sMessage.Data());

	if ( bscroll )
	{
		// QT3 TextEdit_LOG->scrollToBottom();
		// QT3 TextEdit_LOG->moveCursor( Q3TextEdit::MoveEnd, FALSE );
		
		QTextCursor cursor = TextEdit_LOG->textCursor();
		cursor.movePosition( QTextCursor::End, QTextCursor::MoveAnchor, 1 );
		TextEdit_LOG->setTextCursor( cursor );
	}
}

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

	//if ( LineEdit_SEARCH->text() == "" )
	if ( Combobox_SEARCH->currentText() == "")
	{
		return 0;
	}

	// set searchmode
	if ( ComboBox_SEARCHTYPE->currentItem() == 0 )
	{
		MessageSearchUser = new CMessageSearchUser();
		MessageSearchUser->m_eType = DC_MESSAGE_SEARCH_USER;
		//MessageSearchUser->m_sNick = LineEdit_SEARCH->text().toAscii().data();
		MessageSearchUser->m_sNick = Combobox_SEARCH->currentText().toAscii().data();

		return MessageSearchUser;
	}

	MessageSearchFile = new CMessageSearchFile();
	MessageSearchFile->m_eType = DC_MESSAGE_SEARCH_FILE;

	MessageSearchFile->m_eFileType = eFileTypes(ComboBox_SEARCHTYPE->currentItem());
	
	MessageSearchFile->m_sString = Combobox_SEARCH->currentText().toAscii().data();

	if ( MessageSearchFile->m_eFileType == eftHASH )
	{
		// fix tth
		MessageSearchFile->m_sString = MessageSearchFile->m_sString.ToUpper();

		if ( MessageSearchFile->m_sString.Left(4) != "TTH:" )
		{
			MessageSearchFile->m_sString = "TTH:" + MessageSearchFile->m_sString;
		}
	}
	
	if ( g_pConfig->GetMode() == ecmPASSIVE )
	{
		MessageSearchFile->m_bLocal = TRUE;

		if ( (RadioButton_CONNECTEDSINGLEHUB->isChecked() == TRUE ) ||
		     (RadioButton_CONNECTEDHUBS->isChecked() == TRUE) )
		{ 
			MessageSearchFile->m_sSource = g_pConfig->GetNick();
		}
		else
		{ 
			MessageSearchFile->m_sSource = g_pConfig->GetSearchNick();
		}
	}
	else
	{
		MessageSearchFile->m_bLocal  = FALSE;
		MessageSearchFile->m_sSource = g_pConfig->GetUDPHostString();
	}

	if ( MessageSearchFile->m_sSource == "" )
	{
		delete MessageSearchFile;
		return 0;
	}

	MessageSearchFile->m_nSize = CString(LineEdit_SEARCHSIZE->text().toAscii().data()).asULL();

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

	if ( MessageSearchFile->m_nSize == 0 )
	{
		MessageSearchFile->m_bSizeLimit = FALSE;
	}

	if ( ComboBox_SEARCHLIMIT->currentItem() == 0 )
		MessageSearchFile->m_eSizeType = esstATLEAST;
	else if ( ComboBox_SEARCHLIMIT->currentItem() == 1 )
		MessageSearchFile->m_eSizeType = esstATMOST;
	else if ( ComboBox_SEARCHLIMIT->currentItem() == 2 )
		MessageSearchFile->m_eSizeType = esstEXACT;

	return MessageSearchFile;
}

/** */
void DCHubSearch::slotRefreshConnectedHubs()
{
	CString *ps;
	CStringList * StringList;
	CString s;

	ComboBox_CONNECTEDHUBS->clear();

	if ( (StringList = g_pConnectionManager->GetConnectedHubServerList()) != 0 )
	{
		ps = 0;
		while( StringList->Next( s, (CObject*&)ps) )
		{
			// add hubname with hubhost
			ComboBox_CONNECTEDHUBS->insertItem( (s + " (" + *ps +")").Data() );
		}

		delete StringList;
	}
}

/** */
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.find(search_pattern,0,FALSE) == -1 )
                                {
					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 == TRUE )
		{
			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->isOn(), SpinBox_FREESLOTS->value(), TRUE );
}

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

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

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

/** */
void DCHubSearch::slotSearchReturnPressed()
{
	if ( m_SearchManager.SearchType() == estyNONE )
	{	
		startSearch();
	}
	else
	{
		m_SearchManager.StopSearch();
		PushButton_SEARCH->setEnabled(FALSE);
	}
}

/** */
void DCHubSearch::startSearch()
{
	CObject * Object;
	CStringList * hublist = 0;

	eSearchMode searchmode;
	eSearchType searchtype;
	
	if ( m_SearchManager.SearchType() == estyEXTERNAL )
	{
		QMessageBox::critical( this, tr("Hub Search Error"),
			QString( tr("External search is already running!") ));
		return;
	}

	// clear messagelist
	SocketCallbackThread.Lock();

	while( (Object = m_pMessageList->Next(0)) != 0 )
		m_pMessageList->Del(Object);

	m_pSearchResultList->Clear();

	SocketCallbackThread.UnLock();

	// clear searchresults & log
	ListView_SEARCHRESULT->clear();
	TreeWidget_SEARCHRESULTUSER->clear();
	TextEdit_LOG->clear();

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

	// reset
	LineEdit_LOGTIME->setText("");
	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"),
				       QString( tr("Please add a search!") ));
		return;
	}

//	g_pConfig->AddSearchHistory( LineEdit_SEARCH->text().toAscii().data() );

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

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

	// 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
	m_SearchManager.MaxClients(SpinBox_MAXTHREADS->value());
	// enable/disable tag
	m_SearchManager.EnableTag(CheckBox_ENABLETAG->isChecked());

	if ( searchmode == esmCONNECTEDSINGLE )
	{
		CString hubname;

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

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

			return;
		}
		else
		{
			// add hubhost to the hublist
			hublist = new CStringList();
			hublist->Add( hubname, new CString(hubname) );
			
			if ( m_SearchManager.StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				QMessageBox::critical( this, tr("Hub Search Error"),
					QString(tr("No connected hubs found.")));

				return;
			}
		}
	}
	else if ( searchmode == esmCONNECTEDALL )
	{
		if ( m_SearchManager.StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
		{
			QMessageBox::critical( this, tr("Hub Search Error"),
				QString(tr("No connected hubs found.")));

			return;
		}
	}
	else
	{
		if ( searchmode == esmPUBLIC )
		{
			if ( m_SearchManager.StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				delete hublist;

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

				return;
			}
		}
		else
		{
			if ( m_SearchManager.StartSearch(searchmode,searchtype,m_pSearchQueryList,hublist) != 0 )
			{
				QMessageBox::critical( this, tr("Hub Search Error"),
					QString(tr("No hubs found.")));

				return;
			}
		}
	}

	// update view
	SetSearchView( FALSE );

	return;
}

/** */
void DCHubSearch::slotAddSearchQueue()
{
	CDCMessage * msg, *msg1;
	
	CMessageSearchUser * MessageSearchUser = 0;
	CMessageSearchFile * MessageSearchFile = 0;

	addHistory();

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

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

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

	msg1 = 0;

	while ( (msg1 = (CDCMessage*)m_pSearchQueryList->Next((CObject*)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_data = 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->clearEdit();
	LineEdit_SEARCHSIZE->setText("0");
	ComboBox_SEARCHUNIT->setCurrentItem(0);
	ComboBox_SEARCHLIMIT->setCurrentItem(0);
	ComboBox_SEARCHTYPE->setCurrentItem(1);
	SpinBox_FREESLOTS->setValue(0);
	TreeWidget_SEARCH->clear();

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

/** */
void DCHubSearch::slotDoubleClickedSearchResult( Q3ListViewItem * /*item*/ )
{
	Q3ListViewItem * curitem;
	ulonglong size;
	QString localname;
	DCFileTool FileTool;

	curitem   = ListView_SEARCHRESULT->currentItem();
	size      = ((DC_QListViewItem*)curitem)->myvalue;
	localname = curitem->text(RESULTS_COLUMN_FILE);

	// add file as new source
	FileTool.AddFileSource( curitem->text(RESULTS_COLUMN_NICK).toAscii().data(),
				curitem->text(RESULTS_COLUMN_HUB).toAscii().data(),
				curitem->text(RESULTS_COLUMN_HOST).toAscii().data(),
				(curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().data(),
				localname.toAscii().data(),
				"",
				"",
				eltFILE,
				size,
				curitem->text(RESULTS_COLUMN_TTH).toAscii().data() );

	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().data();
	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().data();
		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 )
{
	DCFileTool filetool;
	CString s;
	QAction * chosen = 0;
	ulonglong size;
        QList<Q3ListViewItem*> selitems;
        Q3ListViewItem * curitem, * item;
	bool isadmin = FALSE, allowDL;
	CXml xml;

	// user have select items ?
	selectedItems( ListView_SEARCHRESULT, selitems );
	
	// cannot download folders yet
	allowDL = true;
	for ( int i = 0; i < selitems.size(); i++ )
	{
		curitem = selitems.at(i);
		
		if ( curitem->text(RESULTS_COLUMN_SIZE) == tr("Folder") )
		{
			allowDL = 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().data(),curitem->text(RESULTS_COLUMN_HOST).toAscii().data()) == FALSE )
		{
			isadmin = FALSE;
			break;
		}
		else
		{
			isadmin = TRUE;
		}
	}
	
	QMenu *m,*msort;
	DCMenuHandler mhandler;

	m = new QMenu(this);

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

	// insert submenu
	msort = mhandler.addMenu( m, emisGROUP, TRUE );
	QAction * nogroup = mhandler.addAction( msort, emiDISABLE_GROUP, TRUE );
	QAction * byfile = mhandler.addAction( msort, emiGROUP_BY_FILE, TRUE );
	QAction * bysize = mhandler.addAction( msort, emiGROUP_BY_SIZE, TRUE );
	QAction * bynick = mhandler.addAction( msort, emiGROUP_BY_NICK, TRUE );
	QAction * byhash = mhandler.addAction( msort, emiGROUP_BY_HASH, TRUE );
	QAction * byslots = mhandler.addAction( msort, emiGROUP_BY_SLOTS_FREE, TRUE );
	QAction * byhub = mhandler.addAction( msort, emiGROUP_BY_HUB, TRUE );
	QAction * bypath = mhandler.addAction( msort, emiGROUP_BY_PATH, TRUE );
	QAction * byhost = mhandler.addAction( msort, emiGROUP_BY_HOST, TRUE );

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

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

	delete m;

	if ( chosen == dclink )
	{
		s = "";

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

		QApplication::clipboard()->setText(s.Data());
	}
	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(2).mid(4);
			
			// 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(0)));
			filename.replace("%20","+");
			text += filename;
			
			text += "\n";
		}
		
		// remove trailing "\n"
		text = text.stripWhiteSpace();
		
		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 == "" )
				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 == "" )
					return;

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

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

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

			filetool.CheckFile( curitem->text(RESULTS_COLUMN_NICK).toAscii().data(), curitem->text(RESULTS_COLUMN_HUB).toAscii().data(), curitem->text(RESULTS_COLUMN_HOST).toAscii().data(),
				(curitem->text(RESULTS_COLUMN_PATH)+curitem->text(RESULTS_COLUMN_FILE)).toAscii().data(), localname.toAscii().data(), "", localrootpath.toAscii().data(), eltFILE,
				size, curitem->text(RESULTS_COLUMN_TTH).toAscii().data(), FALSE, repairtype );
		}

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

		if ( !(curitem = selitems.first()) )
			return;

		size = ((DC_QListViewItem *)curitem)->myvalue;

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

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

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

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

			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.Data(),hubhost.Data());
						 break;
					case 1:
						break;
				}
			}

			g_pTransferView->DLM_QueueAdd( curitem->text(RESULTS_COLUMN_NICK).toAscii().data(), curitem->text(RESULTS_COLUMN_HUB).toAscii().data(), curitem->text(RESULTS_COLUMN_HOST).toAscii().data(),
						DC_USER_FILELIST_HE3, DC_USER_FILELIST_HE3, "", "", eltBUFFER,
						0, 0, 0, "" );
		}
	}
	else if ( chosen == copycol )
	{
		QString s="";

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

		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 += "\n";
		}

		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().data(), curitem->text(RESULTS_COLUMN_HOST).toAscii().data() );
		}
	}
	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), "" );
		}
	}
	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 == byslots )
	{
		GroupSearchResults( egsrtSLOTS_FREE );
	}
	else if ( chosen == byhub )
	{
		GroupSearchResults( egsrtHUB );
	}
	else if ( chosen == bypath )
	{
		GroupSearchResults( egsrtPATH );
	}
	else if ( chosen == byhost )
	{
		GroupSearchResults( egsrtHOST );
	}
	else if ( chosen == kick )
	{
		// TODO: need a patch to kick a user only one time
		QString message;

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

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

			// 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.remove(curitem);
					i--;
				}
			}
		}
	}
	else if ( chosen == forcemove )
	{
		// TODO: need a patch to move a user only one time
		QString message, host;

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

		// force move all user
		while( !selitems.isEmpty() )
		{
			item = selitems.first();
			
			// remove this item from the list
			selitems.remove(item);

			// 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.remove(curitem);
					i--;
				}
			}
		}
	}
	else if ( chosen == save )
	{
		CString s;
		CMessageSearchResult * msr = 0;

		QString filename = QFileDialog::getSaveFileName(
			this,
			tr("Choose a filename to save under") );

		if ( filename != "" )
		{
			QFile file(filename);

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

				file.close();
			}
		}
	}
	else if ( chosen == load )
	{
		QString s;
		CString m;
		int p;
		CObject * object;
		CMessageHandler MessageHandler;

		QString filename = QFileDialog::getOpenFileName(
			this,
			tr("Choose a file to open") );

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

				ListView_SEARCHRESULT->setUpdatesEnabled(FALSE);
				ListView_SEARCHRESULT->clear();
				
				char buf[4096];

				while( file.readLine(buf,4096) != -1 )
				{
					m = CString(buf);
					p = 0;

					if ( MessageHandler.Parse(&m,p,object=0) == DC_MESSAGE_SEARCHRESULT )
					{
						m_pSearchResultList->Add(object);
					}
				}

				file.close();

				GroupSearchResults( egsrtNONE );

				ListView_SEARCHRESULT->setUpdatesEnabled(TRUE);
				ListView_SEARCHRESULT->triggerUpdate();
				LCDNumber_RESULTS->display(ListView_SEARCHRESULT->childCount());
			}
		}
	}
}

/** */
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);
	DCMenuHandler mhandler;

	QAction * remove = mhandler.addAction( m, emiREMOVE, selitems.count() > 0 );
	QAction * clear = mhandler.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((CObject*)curitem->p_data);
		
		delete curitem;
	}
}

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