//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//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.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// DownloadListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "emule.h"
#include "DownloadListCtrl.h"
#include "otherfunctions.h" 
#include "updownclient.h"
#include "opcodes.h"
#include "ClientDetailDialog.h"
#include "FileDetailDialog.h"
#include "commentdialoglst.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


// CDownloadListCtrl

#define DLC_DT_TEXT (DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS)
#define DLC_BARUPDATE 512


IMPLEMENT_DYNAMIC(CDownloadListCtrl, CListBox)
CDownloadListCtrl::CDownloadListCtrl() {
}

CDownloadListCtrl::~CDownloadListCtrl(){
	if (m_PrioMenu) VERIFY( m_PrioMenu.DestroyMenu() );
	if (m_FileMenu) VERIFY( m_FileMenu.DestroyMenu() );
	while(m_ListItems.empty() == false){
		delete m_ListItems.begin()->second; // second = CtrlItem_Struct*
		m_ListItems.erase(m_ListItems.begin());
	}
}

void CDownloadListCtrl::Init(){
	CImageList ilDummyImageList; //dummy list for getting the proper height of listview entries
	ilDummyImageList.Create(1, 17,theApp.m_iDfltImageListColorFlags|ILC_MASK, 1, 1); 
	SetImageList(&ilDummyImageList, LVSIL_SMALL);
	ASSERT( (GetStyle() & LVS_SHAREIMAGELISTS) == 0 );
	ilDummyImageList.Detach();

	SetStyle();
	SetColors();

	ModifyStyle(LVS_SINGLESEL,0);
	
	InsertColumn(0,GetResString(IDS_DL_FILENAME),LVCFMT_LEFT, 260);
	InsertColumn(1,GetResString(IDS_DL_SIZE),LVCFMT_LEFT, 60);
	InsertColumn(2,GetResString(IDS_DL_TRANSF),LVCFMT_LEFT, 65);
	InsertColumn(3,GetResString(IDS_DL_TRANSFCOMPL),LVCFMT_LEFT, 65);
	InsertColumn(4,GetResString(IDS_DL_SPEED),LVCFMT_LEFT, 65);
	InsertColumn(5,GetResString(IDS_DL_PROGRESS),LVCFMT_LEFT, 170);
	InsertColumn(6,GetResString(IDS_DL_SOURCES),LVCFMT_LEFT, 50);
	InsertColumn(7,GetResString(IDS_PRIORITY),LVCFMT_LEFT, 55);
	InsertColumn(8,GetResString(IDS_STATUS),LVCFMT_LEFT, 70);
	InsertColumn(9,GetResString(IDS_DL_REMAINS),LVCFMT_LEFT, 110);

	CString lsctitle=GetResString(IDS_LASTSEENCOMPL);
	lsctitle.Remove(':');
	InsertColumn(10, lsctitle,LVCFMT_LEFT, 220);
	lsctitle=GetResString(IDS_FD_LASTCHANGE);
	lsctitle.Remove(':');
	InsertColumn(11, lsctitle,LVCFMT_LEFT, 220);
	InsertColumn(12, GetResString(IDS_CAT) ,LVCFMT_LEFT, 100);

	m_ImageList.Create(16,16,theApp.m_iDfltImageListColorFlags|ILC_MASK,0,10);
	m_ImageList.SetBkColor(CLR_NONE);
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS1));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS2));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS3));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS4));
	m_ImageList.Add(theApp.LoadIcon(IDI_DCS5));
	m_ImageList.Add(theApp.LoadIcon(IDI_COMPPROT));
	m_ImageList.Add(theApp.LoadIcon(IDI_FRIEND));
	m_ImageList.Add(theApp.LoadIcon(IDI_USER0));
	m_ImageList.Add(theApp.LoadIcon(IDI_MLDONK));
	m_ImageList.Add(theApp.LoadIcon(IDI_RATING));
	m_ImageList.Add(theApp.LoadIcon(IDI_RATINGBAD));
	m_ImageList.Add(theApp.LoadIcon(IDI_EDONKEYHYBRID));
	m_ImageList.Add(theApp.LoadIcon(IDI_SHAREAZA));

	CreateMenues();
	LoadSettings(CPreferences::tableDownload);
	curTab=0;
	// Barry - Use preferred sort order from preferences
	int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableDownload);
	bool sortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableDownload);
	SetSortArrow(sortItem, sortAscending);
	SortItems(SortProc, sortItem + (sortAscending ? 0:10));
}

void CDownloadListCtrl::Localize() {
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	HDITEM hdi;
	hdi.mask = HDI_TEXT;
	CString strRes;

	strRes = GetResString(IDS_DL_FILENAME);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(0, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SIZE);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(1, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_TRANSF);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(2, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_TRANSFCOMPL);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(3, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SPEED);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(4, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_PROGRESS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(5, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_SOURCES);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(6, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_PRIORITY);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(7, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_STATUS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(8, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_DL_REMAINS);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(9, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_LASTSEENCOMPL);
	strRes.Remove(':');
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(10, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_FD_LASTCHANGE);
	strRes.Remove(':');
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(11, &hdi);
	strRes.ReleaseBuffer();

	strRes = GetResString(IDS_CAT);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(12, &hdi);
	strRes.ReleaseBuffer();

	CreateMenues();

	ShowFilesCount();
}

void CDownloadListCtrl::AddFile(CPartFile* toadd){
		// Create new Item
        CtrlItem_Struct* newitem = new CtrlItem_Struct;
        uint16 itemnr = GetItemCount();
        newitem->owner = NULL;
        newitem->type = FILE_TYPE;
        newitem->value = toadd;
        newitem->parent = NULL;
		newitem->dwUpdated = 0; 

		// The same file shall be added only once
		ASSERT(m_ListItems.find(toadd) == m_ListItems.end());
		m_ListItems.insert(ListItemsPair(toadd, newitem));

		if (CheckShowItemInGivenCat(toadd, curTab ))
			InsertItem(LVIF_PARAM,itemnr,0,0,0,0,(LPARAM)newitem);

		ShowFilesCount();
}

void CDownloadListCtrl::AddSource(CPartFile* owner,CUpDownClient* source,bool notavailable){
		// Create new Item
        CtrlItem_Struct* newitem = new CtrlItem_Struct;
        newitem->owner = owner;
        newitem->type = (notavailable) ? UNAVAILABLE_SOURCE : AVAILABLE_SOURCE;
        newitem->value = source;
		newitem->dwUpdated = 0; 

		// Update cross link to the owner
		ListItems::const_iterator ownerIt = m_ListItems.find(owner);
		ASSERT(ownerIt != m_ListItems.end());
		CtrlItem_Struct* ownerItem = ownerIt->second;
		ASSERT(ownerItem->value == owner);
		newitem->parent = ownerItem;

		// The same source could be added a few time but only one time per file 
		{
			// Update the other instances of this source
			bool bFound = false;
			std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(source);
			for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
				CtrlItem_Struct* cur_item = it->second;

				// Check if this source has been already added to this file => to be sure
				if(cur_item->owner == owner){
					// Update this instance with its new setting
					cur_item->type = newitem->type;
					cur_item->dwUpdated = 0;
					bFound = true;
				}
				else if(notavailable == false){
					// The state 'Available' is exclusive
					cur_item->type = UNAVAILABLE_SOURCE;
					cur_item->dwUpdated = 0;
				}
			}

			if(bFound == true){
				delete newitem; 
				return;
			}
		}
		m_ListItems.insert(ListItemsPair(source, newitem));
}


void CDownloadListCtrl::RemoveSource(CUpDownClient* source,CPartFile* owner){
	if (!theApp.emuledlg->IsRunning()) return;
	// Retrieve all entries matching the source
	std::pair<ListItems::iterator, ListItems::iterator> rangeIt = m_ListItems.equal_range(source);
	for(ListItems::iterator it = rangeIt.first; it != rangeIt.second; ){
		CtrlItem_Struct* delItem  = it->second;
		if(owner == NULL || owner == delItem->owner){
			// Remove it from the m_ListItems			
			it = m_ListItems.erase(it);

			// Remove it from the CListCtrl
 			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)delItem;
			sint16 result = FindItem(&find);
			if(result != (-1)){
				DeleteItem(result);
			}

			// finally it could be delete
			delete delItem;
		}
		else{
			it++;
		}
	}
}

void CDownloadListCtrl::RemoveFile(CPartFile* toremove){
	if (!theApp.emuledlg->IsRunning()) return;
	// Retrieve all entries matching the File or linked to the file
	// Remark: The 'asked another files' clients must be removed from here
	ASSERT(toremove != NULL);
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* delItem = it->second;
		if(delItem->owner == toremove || delItem->value == toremove){
			// Remove it from the m_ListItems
			it = m_ListItems.erase(it);

			// Remove it from the CListCtrl
			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)delItem;
			sint16 result = FindItem(&find);
			if(result != (-1)){
				DeleteItem(result);
			}

			// finally it could be delete
			delete delItem;
		}
		else {
			it++;
		}
	}
	ShowFilesCount();
}

void CDownloadListCtrl::UpdateItem(void* toupdate){
	if (theApp.emuledlg->IsRunning()) {

		// Retrieve all entries matching the source
		std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(toupdate);
		for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
			CtrlItem_Struct* updateItem  = it->second;

			// Find entry in CListCtrl and update object
 			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)updateItem;
			sint16 result = FindItem(&find);
			if(result != (-1)){
				updateItem->dwUpdated = 0;
				Update(result);
			}
		}
	}
}

void CDownloadListCtrl::DrawFileItem(CDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem) {
	if(lpRect->left < lpRect->right) {
		CString buffer;
		CPartFile *lpPartFile = (CPartFile*)lpCtrlItem->value;
		switch(nColumn) {
		
		case 0:{		// file name
			dc->SetTextColor( (theApp.glob_prefs->GetCatColor(lpPartFile->GetCategory())>0)?theApp.glob_prefs->GetCatColor(lpPartFile->GetCategory()):(::GetSysColor(COLOR_WINDOWTEXT)) );
			if ( theApp.glob_prefs->ShowRatingIndicator() && ( lpPartFile->HasComment() || lpPartFile->HasRating() )){
				POINT point= {lpRect->left-4,lpRect->top+2};
				m_ImageList.Draw(dc,
					(lpPartFile->HasRating() && lpPartFile->HasBadRating()) ?10:9,
					point,
					ILD_NORMAL);

				lpRect->left+=9;
				dc->DrawText(lpPartFile->GetFileName(), (int)strlen(lpPartFile->GetFileName()),lpRect, DLC_DT_TEXT);
				lpRect->left-=9;
			} else
				dc->DrawText(lpPartFile->GetFileName(), (int)strlen(lpPartFile->GetFileName()),lpRect, DLC_DT_TEXT);
			}
			break;

		case 1:		// size
			buffer = CastItoXBytes(lpPartFile->GetFileSize());
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;

		case 2:		// transfered
			buffer = CastItoXBytes(lpPartFile->GetTransfered());
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;

		case 3:		// transfered complete
			buffer = CastItoXBytes(lpPartFile->GetCompletedSize());
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);	
			break;
		case 4:		// speed
			if (lpPartFile->GetTransferingSrcCount())
				buffer.Format("%.1f %s", lpPartFile->GetDatarate() / 1024.0f,GetResString(IDS_KBYTESEC));

			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;

		case 5:		// progress
			{
				lpRect->bottom --;
				lpRect->top ++;
				// added
				int  iWidth = lpRect->right - lpRect->left; 
				int iHeight = lpRect->bottom - lpRect->top;
				if (lpCtrlItem->status == (HBITMAP)NULL)
					VERIFY(lpCtrlItem->status.CreateBitmap(1, 1, 1, 8, NULL)); 
				CDC cdcStatus;
				HGDIOBJ hOldBitmap;
				cdcStatus.CreateCompatibleDC(dc);
				int cx = lpCtrlItem->status.GetBitmapDimension().cx; 
				DWORD dwTicks = GetTickCount();
				if(lpCtrlItem->dwUpdated + DLC_BARUPDATE < dwTicks || cx !=  iWidth || !lpCtrlItem->dwUpdated) {
					lpCtrlItem->status.DeleteObject(); 
					lpCtrlItem->status.CreateCompatibleBitmap(dc,  iWidth, iHeight); 
					lpCtrlItem->status.SetBitmapDimension(iWidth,  iHeight); 
					hOldBitmap = cdcStatus.SelectObject(lpCtrlItem->status); 

					RECT rec_status; 
					rec_status.left = 0; 
					rec_status.top = 0; 
					rec_status.bottom = iHeight; 
					rec_status.right = iWidth; 
					lpPartFile->DrawStatusBar(&cdcStatus,  &rec_status, theApp.glob_prefs->UseFlatBar()); 

					lpCtrlItem->dwUpdated = dwTicks + (rand() % 128); 
				} else 
					hOldBitmap = cdcStatus.SelectObject(lpCtrlItem->status); 

				dc->BitBlt(lpRect->left, lpRect->top, iWidth, iHeight,  &cdcStatus, 0, 0, SRCCOPY); 
				cdcStatus.SelectObject(hOldBitmap);
				//added end
				lpRect->bottom ++;
				lpRect->top --;
			}
			break;

		case 6:		// sources
			{
				uint16 sc = lpPartFile->GetSourceCount();
				uint16 ncsc = lpPartFile->GetNotCurrentSourcesCount();				
				if(ncsc>0){
					buffer.Format("%i/%i (%i)",
								  sc-ncsc, sc, lpPartFile->GetTransferingSrcCount()); 
				}
				else {
					buffer.Format("%i (%i)", 
								  sc, lpPartFile->GetTransferingSrcCount());
				}
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;

		case 7:		// prio
			switch(lpPartFile->GetDownPriority()) {
			case 0:
				if( lpPartFile->IsAutoDownPriority() )
					dc->DrawText(GetResString(IDS_PRIOAUTOLOW),GetResString(IDS_PRIOAUTOLOW).GetLength(),lpRect, DLC_DT_TEXT);
				else
					dc->DrawText(GetResString(IDS_PRIOLOW),GetResString(IDS_PRIOLOW).GetLength(),lpRect, DLC_DT_TEXT);
				break;
			case 1:
				if( lpPartFile->IsAutoDownPriority() )
					dc->DrawText(GetResString(IDS_PRIOAUTONORMAL),GetResString(IDS_PRIOAUTONORMAL).GetLength(),lpRect, DLC_DT_TEXT);
				else
					dc->DrawText(GetResString(IDS_PRIONORMAL),GetResString(IDS_PRIONORMAL).GetLength(),lpRect, DLC_DT_TEXT);
				break;
			case 2:
				if( lpPartFile->IsAutoDownPriority() )
					dc->DrawText(GetResString(IDS_PRIOAUTOHIGH),GetResString(IDS_PRIOAUTOHIGH).GetLength(),lpRect, DLC_DT_TEXT);
				else
					dc->DrawText(GetResString(IDS_PRIOHIGH),GetResString(IDS_PRIOHIGH).GetLength(),lpRect, DLC_DT_TEXT);
				break;
			}
			break;

		case 8:		// <<--9/21/02
			buffer = lpPartFile->getPartfileStatus();
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;

		case 9:		// remaining time & size
			{
				if (lpPartFile->GetStatus()!=PS_COMPLETING && lpPartFile->GetStatus()!=PS_COMPLETE ){
					//size 
					uint32 remains;
					remains=lpPartFile->GetFileSize()-lpPartFile->GetCompletedSize();		//<<-- 09/27/2002, CML

					// time 
					sint32 restTime=lpPartFile->getTimeRemaining();
					buffer.Format("%s (%s)", CastSecondsToHM(restTime),CastItoXBytes(remains));

				}
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;
		case 10: // last seen complete
			{
				if (lpPartFile->lastseencomplete==NULL)
					buffer=GetResString(IDS_UNKNOWN);
				else
					buffer=lpPartFile->lastseencomplete.Format( theApp.glob_prefs->GetDateTimeFormat());
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;
		case 11: // last receive
			if (!IsColumnHidden(11)) {
				if(lpPartFile->GetFileDate()!=NULL)
					buffer=lpPartFile->GetCFileDate().Format( theApp.glob_prefs->GetDateTimeFormat());
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;
		case 12: // cat
			if (!IsColumnHidden(12)) {
				buffer=(lpPartFile->GetCategory()!=0)?
					theApp.glob_prefs->GetCategory(lpPartFile->GetCategory())->title:GetResString(IDS_ALL);
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;
		}
	}
}

void CDownloadListCtrl::DrawSourceItem(CDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem) {
	if(lpRect->left < lpRect->right) { 

		CString buffer;
		CUpDownClient *lpUpDownClient = (CUpDownClient*)lpCtrlItem->value;
		switch(nColumn) {

		case 0:		// icon, name, status
			{
				RECT cur_rec;
				memcpy(&cur_rec, lpRect, sizeof(RECT));
				POINT point = {cur_rec.left, cur_rec.top+1};
				if (lpCtrlItem->type == AVAILABLE_SOURCE){
					switch (lpUpDownClient->GetDownloadState()) {
					case DS_CONNECTING:
						m_ImageList.Draw(dc, 2, point, ILD_NORMAL);
						break;
					case DS_CONNECTED:
						m_ImageList.Draw(dc, 2, point, ILD_NORMAL);
						break;
					case DS_WAITCALLBACK:
						m_ImageList.Draw(dc, 2, point, ILD_NORMAL);
						break;
					case DS_ONQUEUE:
						if(lpUpDownClient->IsRemoteQueueFull())
							m_ImageList.Draw(dc, 3, point, ILD_NORMAL);
						else
							m_ImageList.Draw(dc, 1, point, ILD_NORMAL);
						break;
					case DS_DOWNLOADING:
						m_ImageList.Draw(dc, 0, point, ILD_NORMAL);
						break;
					case DS_REQHASHSET:
						m_ImageList.Draw(dc, 0, point, ILD_NORMAL);
						break;
					case DS_NONEEDEDPARTS:
						m_ImageList.Draw(dc, 3, point, ILD_NORMAL);
						break;
					case DS_LOWTOLOWIP:
						m_ImageList.Draw(dc, 3, point, ILD_NORMAL);
						break;
					case DS_TOOMANYCONNS:
						m_ImageList.Draw(dc, 2, point, ILD_NORMAL);
						break;
					default:
						m_ImageList.Draw(dc, 4, point, ILD_NORMAL);
					}
				}
				else {

					m_ImageList.Draw(dc, 3, point, ILD_NORMAL);
				}
				cur_rec.left += 20;
				if (lpUpDownClient->IsFriend()){
					POINT point2= {cur_rec.left,cur_rec.top+1};
					m_ImageList.Draw(dc, 6, point2, ILD_NORMAL);
					cur_rec.left += 20;
				}
				else if (lpUpDownClient->ExtProtocolAvailable()) {
					POINT point2= {cur_rec.left,cur_rec.top+1};
					m_ImageList.Draw(dc, 5, point2, ILD_NORMAL);
					cur_rec.left += 20;
				}
				else{
					POINT point2= {cur_rec.left,cur_rec.top+1};
					if( lpUpDownClient->GetClientSoft() == SO_MLDONKEY )
						m_ImageList.Draw(dc, 8, point2, ILD_NORMAL);
					else if ( lpUpDownClient->GetClientSoft() == SO_EDONKEYHYBRID )
						m_ImageList.Draw(dc, 11, point2, ILD_NORMAL);
					else if ( lpUpDownClient->GetClientSoft() == SO_SHAREAZA )
						m_ImageList.Draw(dc, 12, point2, ILD_NORMAL);
					else
						m_ImageList.Draw(dc, 7, point2, ILD_NORMAL);
					cur_rec.left += 20;
				}

				if (!lpUpDownClient->GetUserName()) {
					buffer = "?";
				}
				else {
					buffer = lpUpDownClient->GetUserName();
				}
				dc->DrawText(buffer,buffer.GetLength(),&cur_rec, DLC_DT_TEXT);
			}
			break;

		case 1:		// size
			break;

		case 2:// transfered
			if ( !IsColumnHidden(3)) {
				dc->DrawText("",0,lpRect, DLC_DT_TEXT);
				break;
			}
		case 3:// completed
			if (lpCtrlItem->type == AVAILABLE_SOURCE && lpUpDownClient->GetTransferedDown()) {
				buffer = CastItoXBytes(lpUpDownClient->GetTransferedDown());
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;

		case 4:		// speed
			if (lpCtrlItem->type == AVAILABLE_SOURCE && lpUpDownClient->GetDownloadDatarate()){
				if (lpUpDownClient->GetDownloadDatarate())
					buffer.Format("%.1f %s", lpUpDownClient->GetDownloadDatarate()/1024.0f,GetResString(IDS_KBYTESEC));
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;

		case 5:		// file info
			{
				lpRect->bottom--; 
				lpRect->top++; 

				int iWidth = lpRect->right - lpRect->left; 
				int iHeight = lpRect->bottom - lpRect->top; 
				if (lpCtrlItem->status == (HBITMAP)NULL)
					VERIFY(lpCtrlItem->status.CreateBitmap(1, 1, 1, 8, NULL)); 
				CDC cdcStatus;
				HGDIOBJ hOldBitmap;
				cdcStatus.CreateCompatibleDC(dc);
				int cx = lpCtrlItem->status.GetBitmapDimension().cx;
				DWORD dwTicks = GetTickCount();
				if(lpCtrlItem->dwUpdated + DLC_BARUPDATE < dwTicks || cx !=  iWidth  || !lpCtrlItem->dwUpdated) { 
					lpCtrlItem->status.DeleteObject(); 
					lpCtrlItem->status.CreateCompatibleBitmap(dc,  iWidth, iHeight); 
					lpCtrlItem->status.SetBitmapDimension(iWidth,  iHeight); 
					hOldBitmap = cdcStatus.SelectObject(lpCtrlItem->status); 

					RECT rec_status; 
					rec_status.left = 0; 
					rec_status.top = 0; 
					rec_status.bottom = iHeight; 
					rec_status.right = iWidth; 
					lpUpDownClient->DrawStatusBar(&cdcStatus,  &rec_status,(lpCtrlItem->type == UNAVAILABLE_SOURCE), theApp.glob_prefs->UseFlatBar()); 

					lpCtrlItem->dwUpdated = dwTicks + (rand() % 128); 
				} else 
					hOldBitmap = cdcStatus.SelectObject(lpCtrlItem->status); 

				dc->BitBlt(lpRect->left, lpRect->top, iWidth, iHeight,  &cdcStatus, 0, 0, SRCCOPY); 
				cdcStatus.SelectObject(hOldBitmap);

				lpRect->bottom++; 
				lpRect->top--; 
			}
			break;

		case 6:{		// sources
			switch(lpUpDownClient->GetClientSoft()){
				case SO_EDONKEY:
					buffer.Format("eDonkey v%.1f",(float)lpUpDownClient->GetVersion()/10.0f);
					break;
				case SO_EDONKEYHYBRID:
					buffer.Format( "eDonkeyHybrid v%.1f",(float)lpUpDownClient->GetVersion()/10.0f);
					break;
				case SO_EMULE:
				case SO_OLDEMULE:
					buffer.Format("eMule v%.2f",(float)lpUpDownClient->GetVersion()/1000.0f);
					break;
				case SO_CDONKEY:
					buffer.Format("cDonkey v%.2f",(float)lpUpDownClient->GetVersion()/1000.0f);
					break;
				case SO_LMULE:
					buffer.Format("lMule v%.2f",(float)lpUpDownClient->GetVersion()/1000.0f);
					break;
				case SO_MLDONKEY:
					buffer.Format("MLdonkey v%.2f",(float)lpUpDownClient->GetVersion()/1000.0f);
					break;
				case SO_SHAREAZA:
					buffer.Format("Shareaza v%.2f",(float)lpUpDownClient->GetVersion()/1000.0f);
					break;
				default:
					buffer = GetResString(IDS_UNKNOWN);
			}
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;
		}

		case 7:		// prio
			if (lpUpDownClient->GetDownloadState()==DS_ONQUEUE){
				if( lpUpDownClient->IsRemoteQueueFull() ){
					buffer = GetResString(IDS_QUEUEFULL);
					dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
				}
				else{
					if ( lpUpDownClient->GetRemoteQueueRank()){
						buffer.Format("QR: %u",lpUpDownClient->GetRemoteQueueRank());
						dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
					}
					else{
						dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
					}
				}
			} else {
				dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			}
			break;

		case 8:	{	// status
			if (lpCtrlItem->type == AVAILABLE_SOURCE){
				switch (lpUpDownClient->GetDownloadState()) {
					case DS_CONNECTING:
						buffer = GetResString(IDS_CONNECTING);
						break;
					case DS_CONNECTED:
						buffer = GetResString(IDS_ASKING);
						break;
					case DS_WAITCALLBACK:
						buffer = GetResString(IDS_CONNVIASERVER);
						break;
					case DS_ONQUEUE:
						if( lpUpDownClient->IsRemoteQueueFull() )
							buffer = GetResString(IDS_QUEUEFULL);
						else
							buffer = GetResString(IDS_ONQUEUE);
						break;
					case DS_DOWNLOADING:
						buffer = GetResString(IDS_TRANSFERRING);
						break;
					case DS_REQHASHSET:
						buffer = GetResString(IDS_RECHASHSET);
						break;
					case DS_NONEEDEDPARTS:
						buffer = GetResString(IDS_NONEEDEDPARTS);
						break;
					case DS_LOWTOLOWIP:
						buffer = GetResString(IDS_NOCONNECTLOW2LOW);
						break;
					case DS_TOOMANYCONNS:
						buffer = GetResString(IDS_TOOMANYCONNS);
						break;
					default:
						buffer = GetResString(IDS_UNKNOWN);
				}
			}
			else {
				buffer = GetResString(IDS_ASKED4ANOTHERFILE);
			}
			dc->DrawText(buffer,buffer.GetLength(),lpRect, DLC_DT_TEXT);
			break;
				}
		case 9:		// remaining time & size
			break;
		}
	}
}

void CDownloadListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){
	if (!lpDrawItemStruct->itemData)
		return;

	CDC* odc = CDC::FromHandle(lpDrawItemStruct->hDC);
	CtrlItem_Struct* content = (CtrlItem_Struct*)lpDrawItemStruct->itemData;
	BOOL bCtrlFocused = ((GetFocus() == this) || (GetStyle() & LVS_SHOWSELALWAYS));

	if ((content->type == FILE_TYPE) && (lpDrawItemStruct->itemAction | ODA_SELECT) &&
	    (lpDrawItemStruct->itemState & ODS_SELECTED)) {

		if(bCtrlFocused)
			odc->SetBkColor(m_crHighlight);
		else
			odc->SetBkColor(m_crNoHighlight);

	} /*else if (content->type != FILE_TYPE) {
		COLORREF crHighlight = m_crWindowText;
		COLORREF crWindow = m_crWindow;
		COLORREF crHalf = DLC_RGBBLEND(crHighlight, crWindow, 16);
		odc->SetBkColor(crHalf);
	}*/ else
		odc->SetBkColor(GetBkColor());

	CMemDC dc(odc,&CRect(lpDrawItemStruct->rcItem));
	CFont *pOldFont = dc->SelectObject(GetFont());
	COLORREF crOldTextColor = dc->SetTextColor(m_crWindowText);

	BOOL notLast = lpDrawItemStruct->itemID + 1 != GetItemCount();
	BOOL notFirst = lpDrawItemStruct->itemID != 0;
	int tree_start=0;
	int tree_end=0;

	RECT cur_rec;
	memcpy(&cur_rec,&lpDrawItemStruct->rcItem,sizeof(RECT));

	//offset was 4, now it's the standard 2 spaces
	int iOffset = dc->GetTextExtent(_T(" "), 1 ).cx*2;
	CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
	int iCount = pHeaderCtrl->GetItemCount();
	cur_rec.right = cur_rec.left;
	cur_rec.right -= iOffset;
	cur_rec.left += iOffset;

	if (content->type == FILE_TYPE){
		for(int iCurrent = 0; iCurrent < iCount; iCurrent++) {

			int iColumn = pHeaderCtrl->OrderToIndex(iCurrent);
			int cx = CListCtrl::GetColumnWidth(iColumn);
			if(iColumn == 5) {
				int iNextLeft = cur_rec.left + cx;
				//set up tree vars
				cur_rec.left = cur_rec.right + iOffset;
				cur_rec.right = cur_rec.left + min(8, cx);
				tree_start = cur_rec.left + 1;
				tree_end = cur_rec.right;
				//normal column stuff
				cur_rec.left = cur_rec.right + 1;
				cur_rec.right = tree_start + cx - iOffset;
				DrawFileItem(dc, 5, &cur_rec, content);
				cur_rec.left = iNextLeft;
			} else {
				cur_rec.right += cx;
				DrawFileItem(dc, iColumn, &cur_rec, content);
				cur_rec.left += cx;
			}

		}

	}
	else if (content->type == UNAVAILABLE_SOURCE || content->type == AVAILABLE_SOURCE){

		for(int iCurrent = 0; iCurrent < iCount; iCurrent++) {

			int iColumn = pHeaderCtrl->OrderToIndex(iCurrent);
			int cx = CListCtrl::GetColumnWidth(iColumn);

			if(iColumn == 5) {
				int iNextLeft = cur_rec.left + cx;
				//set up tree vars
				cur_rec.left = cur_rec.right + iOffset;
				cur_rec.right = cur_rec.left + min(8, cx);
				tree_start = cur_rec.left + 1;
				tree_end = cur_rec.right;
				//normal column stuff
				cur_rec.left = cur_rec.right + 1;
				cur_rec.right = tree_start + cx - iOffset;
				DrawSourceItem(dc, 5, &cur_rec, content);
				cur_rec.left = iNextLeft;
			} else {
				while(iCurrent < iCount) {
					int iNext = pHeaderCtrl->OrderToIndex(iCurrent + 1);
					if(iNext == 1 /*|| iNext == 5 || iNext == 7 || iNext == 8*/)
						cx += CListCtrl::GetColumnWidth(iNext);
					else
						break;
					iCurrent++;
				}
				cur_rec.right += cx;
				DrawSourceItem(dc, iColumn, &cur_rec, content);
				cur_rec.left += cx;
			}
		}
	}

	//draw rectangle around selected item(s)
	if ((lpDrawItemStruct->itemAction | ODA_SELECT) &&
		(lpDrawItemStruct->itemState & ODS_SELECTED) &&
		(content->type == FILE_TYPE)) {
			RECT outline_rec;
			memcpy(&outline_rec,&lpDrawItemStruct->rcItem,sizeof(RECT));

		outline_rec.top--;
		outline_rec.bottom++;
		dc->FrameRect(&outline_rec, &CBrush(GetBkColor()));
		outline_rec.top++;
		outline_rec.bottom--;
		outline_rec.left++;
		outline_rec.right--;

		if(notFirst && (GetItemState(lpDrawItemStruct->itemID - 1, LVIS_SELECTED))) {

			CtrlItem_Struct* prev = (CtrlItem_Struct*)this->GetItemData(lpDrawItemStruct->itemID - 1);
			if(prev->type == FILE_TYPE)
				outline_rec.top--;
		} 

		if(notLast && (GetItemState(lpDrawItemStruct->itemID + 1, LVIS_SELECTED))) {

			CtrlItem_Struct* next = (CtrlItem_Struct*)this->GetItemData(lpDrawItemStruct->itemID + 1);
			if(next->type == FILE_TYPE)
				outline_rec.bottom++;
		} 

		if(bCtrlFocused)
			dc->FrameRect(&outline_rec, &CBrush(m_crFocusLine));
		else
			dc->FrameRect(&outline_rec, &CBrush(m_crNoFocusLine));
	}

	//draw focus rectangle around non-highlightable items when they have the focus
	else if (((lpDrawItemStruct->itemState & ODS_FOCUS) == ODS_FOCUS) &&
		(GetFocus() == this)) {

		RECT focus_rec;
		focus_rec.top    = lpDrawItemStruct->rcItem.top;
		focus_rec.bottom = lpDrawItemStruct->rcItem.bottom;
		focus_rec.left   = lpDrawItemStruct->rcItem.left + 1;
		focus_rec.right  = lpDrawItemStruct->rcItem.right - 1;
		dc->FrameRect(&focus_rec, &CBrush(m_crNoFocusLine));
	}

	//draw tree last so it draws over selected and focus (looks better)
	if(tree_start < tree_end) {
		//set new bounds
		RECT tree_rect;
		tree_rect.top    = lpDrawItemStruct->rcItem.top;
		tree_rect.bottom = lpDrawItemStruct->rcItem.bottom;
		tree_rect.left   = tree_start;
		tree_rect.right  = tree_end;
		dc->SetBoundsRect(&tree_rect, DCB_DISABLE);

		//gather some information
		BOOL hasNext = notLast &&
			((CtrlItem_Struct*)this->GetItemData(lpDrawItemStruct->itemID + 1))->type != FILE_TYPE;
		BOOL isOpenRoot = hasNext && content->type == FILE_TYPE;
		BOOL isChild = content->type != FILE_TYPE;
		//BOOL isExpandable = !isChild && ((CPartFile*)content->value)->GetSourceCount() > 0;
		//might as well calculate these now
		int treeCenter = tree_start + 3;
		int middle = (cur_rec.top + cur_rec.bottom + 1) / 2;

		//set up a new pen for drawing the tree
		CPen pn, *oldpn;
		pn.CreatePen(PS_SOLID, 1, dc->GetTextColor());
		oldpn = dc->SelectObject(&pn);

		if(isChild) {
			//draw the line to the status bar
			dc->MoveTo(tree_end, middle);
			dc->LineTo(tree_start + 3, middle);

			//draw the line to the child node
			if(hasNext) {
				dc->MoveTo(treeCenter, middle);
				dc->LineTo(treeCenter, cur_rec.bottom + 1);
			}
		} else if(isOpenRoot) {
			//draw circle
			RECT circle_rec;
			COLORREF crBk = dc->GetBkColor();
			circle_rec.top    = middle - 2;
			circle_rec.bottom = middle + 3;
			circle_rec.left   = treeCenter - 2;
			circle_rec.right  = treeCenter + 3;
			dc->FrameRect(&circle_rec, &CBrush(dc->GetTextColor()));
			dc->SetPixelV(circle_rec.left,      circle_rec.top,    crBk);
			dc->SetPixelV(circle_rec.right - 1, circle_rec.top,    crBk);
			dc->SetPixelV(circle_rec.left,      circle_rec.bottom - 1, crBk);
			dc->SetPixelV(circle_rec.right - 1, circle_rec.bottom - 1, crBk);
			//draw the line to the child node
			if(hasNext) {
				dc->MoveTo(treeCenter, middle + 3);
				dc->LineTo(treeCenter, cur_rec.bottom + 1);
			}
		} /*else if(isExpandable) {
			//draw a + sign
			dc->MoveTo(treeCenter, middle - 2);
			dc->LineTo(treeCenter, middle + 3);
			dc->MoveTo(treeCenter - 2, middle);
			dc->LineTo(treeCenter + 3, middle);
		}*/

		//draw the line back up to parent node
		if(notFirst && isChild) {
			dc->MoveTo(treeCenter, middle);
			dc->LineTo(treeCenter, cur_rec.top - 1);
		}

		//put the old pen back
		dc->SelectObject(oldpn);
		pn.DeleteObject();
	}

	//put the original objects back
	dc->SelectObject(pOldFont);
	dc->SetTextColor(crOldTextColor);
}

// modifier-keys -view filtering [Ese Juani+xrmb]
void CDownloadListCtrl::HideSources(CPartFile* toCollapse,bool isShift,bool isCtrl,bool isAlt) {
	SetRedraw(false);
	int pre,post;
	pre = post = 0;
	for(int i = 0; i < GetItemCount(); i++) {
		CtrlItem_Struct* item = (CtrlItem_Struct*)this->GetItemData(i);
		if(item->owner == toCollapse) {
			pre++;
			if(isShift || isCtrl || isAlt){
				uint8 ds=((CUpDownClient*)item->value)->GetDownloadState();
				if((isShift && ds==DS_DOWNLOADING) ||
					(isCtrl && ((CUpDownClient*)item->value)->GetRemoteQueueRank()> 0) ||
					(isAlt && ds!=DS_NONEEDEDPARTS)) continue;
			}
			item->dwUpdated = 0;
			item->status.DeleteObject();
			DeleteItem(i--);
			post++;
		}
	}
	if (pre-post==0) toCollapse->srcarevisible = false;
	SetRedraw(true);
}

BEGIN_MESSAGE_MAP(CDownloadListCtrl, CMuleListCtrl)
	ON_NOTIFY_REFLECT(LVN_ITEMACTIVATE, OnItemActivate)
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnListModified)
	ON_NOTIFY_REFLECT(LVN_INSERTITEM, OnListModified)
	ON_NOTIFY_REFLECT(LVN_DELETEITEM, OnListModified)
	ON_WM_CONTEXTMENU()
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkDownloadlist)
END_MESSAGE_MAP()

void CDownloadListCtrl::ExpandCollapseItem(int item,uint8 expand,bool collapsesource){
	if (item==-1) return;

	CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(item);

	// modifier-keys -view filtering [Ese Juani+xrmb]
	bool isShift=GetAsyncKeyState(VK_SHIFT) < 0;
	bool isCtrl=GetAsyncKeyState(VK_CONTROL) < 0;
	bool isAlt=GetAsyncKeyState(VK_MENU) < 0;

	if (collapsesource && content->parent!=NULL) {// to collapse/expand files when one of its source is selected
		content=content->parent;
		
 		LVFINDINFO find;
		find.flags = LVFI_PARAM;
		find.lParam = (LPARAM)content;
		item = FindItem(&find);
		if (item==-1) return;
	}

	if (!content || content->type != FILE_TYPE) return;
	
	CPartFile* partfile = reinterpret_cast<CPartFile*>(content->value);
	if (!partfile) return;

	if (partfile->GetStatus()==PS_COMPLETE) {
		char* buffer = new char[MAX_PATH];
		sprintf(buffer,"%s",partfile->GetFullName());
		ShellOpenFile(buffer, NULL);
		delete buffer;
		return;
	}

	// Check if the source branch is disable
	if(partfile->srcarevisible == false ) {
		if (expand>COLLAPSE_ONLY){
			SetRedraw(false);
			
			// Go throught the whole list to finf out the sources for this file
			// Remark: don't use GetSourceCount() => UNAVAILABLE_SOURCE
			for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
				const CtrlItem_Struct* cur_item = it->second;
				if(cur_item->owner == partfile){
					if(isShift || isCtrl || isAlt) {
						ASSERT(cur_item->type != FILE_TYPE);
						uint8 ds=((CUpDownClient*)cur_item->value)->GetDownloadState();
						if(!(isShift && ds==DS_DOWNLOADING ||
							isCtrl && ((CUpDownClient*)cur_item->value)->GetRemoteQueueRank()>0 ||
							isAlt && ds!=DS_NONEEDEDPARTS))
							continue; // skip this source
					}
					partfile->srcarevisible = true;
					InsertItem(LVIF_PARAM,item+1,0,0,0,0,(LPARAM)cur_item);
				}
			}

			SetRedraw(true);
		}
	}
	else {
		if (expand==EXPAND_COLLAPSE || expand==COLLAPSE_ONLY) HideSources(partfile,isShift,isCtrl,isAlt);
	}
}

// CDownloadListCtrl message handlers
void CDownloadListCtrl::OnItemActivate(NMHDR *pNMHDR, LRESULT *pResult){
	LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

	if (theApp.glob_prefs->IsDoubleClickEnabled() || pNMIA->iSubItem > 0)
		ExpandCollapseItem(pNMIA->iItem,2);
	*pResult = 0;
}

void CDownloadListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)
{
	if (GetSelectionMark() != (-1)){
		CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(GetSelectionMark());
		if (content->type == FILE_TYPE){
			CPartFile* file = (CPartFile*)content->value;
			bool filedone=(file->GetStatus()==PS_COMPLETE || file->GetStatus()==PS_COMPLETING);

			m_FileMenu.EnableMenuItem(MP_PAUSE,((file->GetStatus() != PS_PAUSED && file->GetStatus() != PS_ERROR && !filedone) ? MF_ENABLED:MF_GRAYED));
			m_FileMenu.EnableMenuItem(MP_STOP,((!file->IsStopped() && file->GetStatus() != PS_ERROR && !filedone ) ? MF_ENABLED:MF_GRAYED));
			m_FileMenu.EnableMenuItem(MP_RESUME,((file->GetStatus() == PS_PAUSED) ? MF_ENABLED:MF_GRAYED));

	        m_FileMenu.EnableMenuItem(MP_OPEN,((file->GetStatus() == PS_COMPLETE) ? MF_ENABLED:MF_GRAYED)); //<<--9/21/02
			m_FileMenu.EnableMenuItem(MP_PREVIEW,((file->PreviewAvailable()) ? MF_ENABLED:MF_GRAYED));

			m_FileMenu.EnableMenuItem(MP_CANCEL,((!filedone) ? MF_ENABLED:MF_GRAYED));

			m_FileMenu.EnableMenuItem((UINT_PTR)m_PrioMenu.m_hMenu,((!filedone) ? MF_ENABLED:MF_GRAYED));


			m_PrioMenu.CheckMenuItem(MP_PRIOAUTO, ((file->IsAutoDownPriority()) ? MF_CHECKED:MF_UNCHECKED));
			m_PrioMenu.CheckMenuItem(MP_PRIOHIGH, ((file->GetDownPriority() == PR_HIGH && !file->IsAutoDownPriority()) ? MF_CHECKED:MF_UNCHECKED));
			m_PrioMenu.CheckMenuItem(MP_PRIONORMAL, ((file->GetDownPriority() == PR_NORMAL && !file->IsAutoDownPriority()) ? MF_CHECKED:MF_UNCHECKED));
			m_PrioMenu.CheckMenuItem(MP_PRIOLOW, ((file->GetDownPriority() == PR_LOW && !file->IsAutoDownPriority()) ? MF_CHECKED:MF_UNCHECKED));

			int counter;
			m_Web.CreateMenu();
			UpdateURLMenu(m_Web,counter);
			UINT flag;
			flag=(counter==0) ? MF_GRAYED:MF_STRING;
			
			m_FileMenu.AppendMenu(flag|MF_POPUP,(UINT_PTR)m_Web.m_hMenu, GetResString(IDS_WEBSERVICES) );

			// create cat-submenue
			CTitleMenu cats;
			cats.CreatePopupMenu();
			cats.AddMenuTitle(GetResString(IDS_CAT));
			flag=(theApp.glob_prefs->GetCatCount()==1) ? MF_GRAYED:MF_STRING;
			if (theApp.glob_prefs->GetCatCount()>1) {
				for (int i=0;i<theApp.glob_prefs->GetCatCount();i++) {
					cats.AppendMenu(MF_STRING,MP_ASSIGNCAT+i, (i==0)?GetResString(IDS_CAT_UNASSIGN):theApp.glob_prefs->GetCategory(i)->title);
				}
			}
			m_FileMenu.AppendMenu(flag|MF_POPUP,(UINT_PTR)cats.m_hMenu, GetResString(IDS_TOCAT) );

			m_FileMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y, this);
			m_FileMenu.RemoveMenu(m_FileMenu.GetMenuItemCount()-1,MF_BYPOSITION);
			m_FileMenu.RemoveMenu(m_FileMenu.GetMenuItemCount()-1,MF_BYPOSITION);
			VERIFY( m_Web.DestroyMenu() );
			VERIFY( cats.DestroyMenu() );
		}else {
			m_ClientMenu.CreatePopupMenu();
			m_ClientMenu.AddMenuTitle(GetResString(IDS_CLIENTS));
			m_ClientMenu.AppendMenu(MF_STRING,MP_DETAIL, GetResString(IDS_SHOWDETAILS));
			m_ClientMenu.AppendMenu(MF_STRING,MP_ADDFRIEND, GetResString(IDS_ADDFRIEND));
			m_ClientMenu.AppendMenu(MF_STRING,MP_MESSAGE, GetResString(IDS_SEND_MSG));
			m_ClientMenu.AppendMenu(MF_STRING,MP_SHOWLIST, GetResString(IDS_VIEWFILES));
			m_ClientMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y, this);
			VERIFY( m_ClientMenu.DestroyMenu() );
		}
	}
	else{
		m_FileMenu.EnableMenuItem(MP_CANCEL,MF_GRAYED);
		m_FileMenu.EnableMenuItem(MP_PAUSE,MF_GRAYED);
		m_FileMenu.EnableMenuItem(MP_STOP,MF_GRAYED);
		m_FileMenu.EnableMenuItem(MP_RESUME,MF_GRAYED);
		m_FileMenu.EnableMenuItem(MP_PREVIEW,MF_GRAYED);
		m_FileMenu.EnableMenuItem(MP_OPEN,MF_GRAYED); //<<--9/21/02
		m_FileMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y, this);
	}
}

BOOL CDownloadListCtrl::OnCommand(WPARAM wParam,LPARAM lParam ){
	if (GetSelectionMark() != (-1)){
		CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(GetSelectionMark());
		
		///////////////////////////////////////////////////////////// 
		//for multiple selections 
		UINT selectedCount = this->GetSelectedCount(); 
		CTypedPtrList<CPtrList, CPartFile*> selectedList; 
		int index = -1; 
		POSITION pos = this->GetFirstSelectedItemPosition(); 
		while(pos != NULL) 
		{ 
			index = this->GetNextSelectedItem(pos); 
			if(index > -1) 
			{
				if (((CtrlItem_Struct*)this->GetItemData(index))->type == FILE_TYPE) 
					selectedList.AddTail((CPartFile*)((CtrlItem_Struct*)this->GetItemData(index))->value); 
			} 
		} 

		if (content->type == FILE_TYPE){
			CPartFile* file = (CPartFile*)content->value;
			
			if (wParam>=MP_WEBURL && wParam<=MP_WEBURL+99) {
				RunURL(file, theApp.webservices.GetAt(wParam-MP_WEBURL) );
			}
			if (wParam>=MP_ASSIGNCAT && wParam<=MP_ASSIGNCAT+99) {
				while(!selectedList.IsEmpty()) { 
					CPartFile *selected = selectedList.GetHead();
					selected->SetCategory(wParam-MP_ASSIGNCAT);
					selectedList.RemoveHead(); 
				}				
				ChangeCategory(curTab);
			}

			switch (wParam)
			{
				case MPG_DELETE:
				case MP_CANCEL:
				{
					//for multiple selections 
					if(selectedCount > 0)
					{
						SetRedraw(false);
						CString fileList; 
						bool validdelete = false;
						for (pos = selectedList.GetHeadPosition() ; pos != 0 ; selectedList.GetNext(pos))
						{
							if(selectedList.GetAt(pos)->GetStatus() != PS_COMPLETING && selectedList.GetAt(pos)->GetStatus() != PS_COMPLETE){
								validdelete = true;
							fileList += "\n" ; 
							fileList += selectedList.GetAt(pos)->GetFileName(); 
						} 
						} 
						CString quest;
						if (selectedCount==1)
							quest=GetResString(IDS_Q_CANCELDL2);
						else
							quest=GetResString(IDS_Q_CANCELDL);
						if (validdelete && AfxMessageBox(quest + fileList,MB_ICONQUESTION|MB_YESNO) == IDYES)
						{ 
							while(!selectedList.IsEmpty())
							{
								HideSources(selectedList.GetHead());
								switch(selectedList.GetHead()->GetStatus()) { 
									case PS_WAITINGFORHASH: 
									case PS_HASHING: 
									case PS_COMPLETING: 
									case PS_COMPLETE: 
										selectedList.RemoveHead(); 
										break;
									case PS_PAUSED:
										selectedList.GetHead()->DeleteFile(); 
										selectedList.RemoveHead();
										break;
									default:
										if (theApp.glob_prefs->StartNextFile()) theApp.downloadqueue->StartNextFile();
										selectedList.GetHead()->DeleteFile(); 
										selectedList.RemoveHead(); 
								}
							}  
						}
						SetRedraw(true);
					}
					break;
				}
				case MP_PRIOHIGH:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->SetAutoDownPriority(false); 
							selectedList.GetHead()->SetDownPriority(PR_HIGH); 
							selectedList.RemoveHead(); 
						} 
						SetRedraw(true);
						break; 
					} 
					file->SetAutoDownPriority(false);
					file->SetDownPriority(PR_HIGH);
					break;
				case MP_PRIOLOW:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->SetAutoDownPriority(false); 
							selectedList.GetHead()->SetDownPriority(PR_LOW); 
							selectedList.RemoveHead(); 
						}
						SetRedraw(true);
						break; 
					} 
					file->SetAutoDownPriority(false);
					file->SetDownPriority(PR_LOW);
					break;
				case MP_PRIONORMAL:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) {
							selectedList.GetHead()->SetAutoDownPriority(false);
							selectedList.GetHead()->SetDownPriority(PR_NORMAL);
							selectedList.RemoveHead();
						}
						SetRedraw(true);
						break;
					} 
					file->SetAutoDownPriority(false);
					file->SetDownPriority(PR_NORMAL);
					break;
				case MP_PRIOAUTO:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) {
							selectedList.GetHead()->SetAutoDownPriority(true);
							selectedList.GetHead()->SetDownPriority(PR_HIGH);
							selectedList.RemoveHead();
						}
						SetRedraw(true);
						break;
					} 
					file->SetAutoDownPriority(true);
					file->SetDownPriority(PR_HIGH);
					break;
				case MP_PAUSE:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->PauseFile(); 
							selectedList.RemoveHead(); 
						} 
						SetRedraw(true);
						break; 
					} 
					file->PauseFile();
					break;
				case MP_RESUME:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->ResumeFile(); 
							selectedList.RemoveHead(); 
						}
						SetRedraw(true);
						break; 
					} 
					file->ResumeFile();
					break;
				case MP_STOP:
					if(selectedCount > 1)
					{
						SetRedraw(false);
						while(!selectedList.IsEmpty()) { 
							CPartFile *selected = selectedList.GetHead();
							HideSources(selected);
							selected->StopFile(); 
							selectedList.RemoveHead(); 
						}
						SetRedraw(true);
						break; 
					} 
					HideSources(file);
					file->StopFile();
					break;
				case MP_CLEARCOMPLETED:
					SetRedraw(false);
					ClearCompleted();
					SetRedraw(true);
					break;
				case MP_METINFO:
					{
						CFileDetailDialog dialog(file);
						dialog.DoModal();
						break;
					}
				case MP_GETED2KLINK:
					if(selectedCount > 1)
					{
						CString str;
						while(!selectedList.IsEmpty()) { 
							str += theApp.CreateED2kLink(selectedList.GetHead()) + "\n"; 
							selectedList.RemoveHead(); 
						}
						theApp.CopyTextToClipboard(str);
						//AfxMessageBox(GetResString(IDS_COPIED2CB) + str);
						break; 
					} 
					theApp.CopyTextToClipboard(theApp.CreateED2kLink(file));
					break;
				case MP_GETHTMLED2KLINK:
					if(selectedCount > 1)
					{
						CString str;
						while(!selectedList.IsEmpty()) { 
							str += theApp.CreateHTMLED2kLink(selectedList.GetHead()) + "\n"; 
							selectedList.RemoveHead(); 
						}
						theApp.CopyTextToClipboard(str);
						//AfxMessageBox(GetResString(IDS_COPIED2CB) + str);
						break; 
					} 
					theApp.CopyTextToClipboard(theApp.CreateHTMLED2kLink(file));
					break;
				case MP_OPEN:{
					if(selectedCount > 1)
						break;
					char* buffer = new char[MAX_PATH];
					_snprintf(buffer,MAX_PATH,"%s",file->GetFullName());
					ShellOpenFile(buffer, NULL);
					delete buffer;
					break; 
				}
				case MP_PREVIEW:{
					if(selectedCount > 1)
						break;
					file->PreviewFile();
					break;
				}
				case MP_VIEWFILECOMMENTS:{
					CCommentDialogLst dialog(file); 
					dialog.DoModal(); 
					break;
				}
			}
		}
		else{
			CUpDownClient* client = (CUpDownClient*)content->value;
			switch (wParam){
				case MP_SHOWLIST:
					client->RequestSharedFileList();
					break;
				case MP_MESSAGE:
					theApp.emuledlg->chatwnd.StartSession(client);
					break;
				case MP_ADDFRIEND:{
					theApp.friendlist->AddFriend(client);
					break;
				}
				case MP_DETAIL:
					CClientDetailDialog dialog(client);
					dialog.DoModal();
					break;
			}
		}
		// cleanup multiselection
		selectedList.RemoveAll(); 

	}
	else /*nothing selected*/
	{
		switch (wParam){
			case MP_CLEARCOMPLETED:
				ClearCompleted();
				break;
		}

	}

	return true;
}

void CDownloadListCtrl::OnColumnClick( NMHDR* pNMHDR, LRESULT* pResult){
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

	// Barry - Store sort order in preferences
	// Determine ascending based on whether already sorted on this column
	int sortItem = theApp.glob_prefs->GetColumnSortItem(CPreferences::tableDownload);
	bool m_oldSortAscending = theApp.glob_prefs->GetColumnSortAscending(CPreferences::tableDownload);
	bool sortAscending = (sortItem != pNMListView->iSubItem) ? true : !m_oldSortAscending;
	// Item is column clicked
	sortItem = pNMListView->iSubItem;
	// Save new preferences
	theApp.glob_prefs->SetColumnSortItem(CPreferences::tableDownload, sortItem);
	theApp.glob_prefs->SetColumnSortAscending(CPreferences::tableDownload, sortAscending);
	// Sort table
	SetSortArrow(sortItem, sortAscending);
	SortItems(SortProc, sortItem + (sortAscending ? 0:100));

	*pResult = 0;
}

int CDownloadListCtrl::SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort){
	CtrlItem_Struct* item1 = (CtrlItem_Struct*)lParam1;
	CtrlItem_Struct* item2 = (CtrlItem_Struct*)lParam2;

	int sortMod = 1;
	if(lParamSort >= 100) {
		sortMod = -1;
		lParamSort -= 100;
	}

	int comp;

	if(item1->type == FILE_TYPE && item2->type != FILE_TYPE) {
		if(item1->value == item2->parent->value)
			return -1;

		comp = Compare((CPartFile*)item1->value, (CPartFile*)(item2->parent->value), lParamSort);

	} else if(item2->type == FILE_TYPE && item1->type != FILE_TYPE) {
		if(item1->parent->value == item2->value)
			return 1;

		comp = Compare((CPartFile*)(item1->parent->value), (CPartFile*)item2->value, lParamSort);

	} else if (item1->type == FILE_TYPE) {
		CPartFile* file1 = (CPartFile*)item1->value;
		CPartFile* file2 = (CPartFile*)item2->value;

		comp = Compare(file1, file2, lParamSort);

	} else {
		CUpDownClient* client1 = (CUpDownClient*)item1->value;
		CUpDownClient* client2 = (CUpDownClient*)item2->value;
		comp = Compare((CPartFile*)(item1->parent->value), (CPartFile*)(item2->parent->value), lParamSort);
		if(comp != 0)
			return sortMod * comp;
		if (item1->type != item2->type)
			return item1->type - item2->type;

		comp = Compare(client1, client2, lParamSort,sortMod);
	}

	return sortMod * comp;
}


void CDownloadListCtrl::ClearCompleted(){
	// Search for completed file(s)
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* cur_item = it->second;
		it++; // Already point to the next iterator. 
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			if(file->IsPartFile() == false && CheckShowItemInGivenCat(file,curTab))
				RemoveFile(file);
		}
	}
}

void CDownloadListCtrl::SetStyle() {
	if (theApp.glob_prefs->IsDoubleClickEnabled())
		SetExtendedStyle(LVS_EX_FULLROWSELECT);
	else
		SetExtendedStyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT);
}

void CDownloadListCtrl::OnListModified(NMHDR *pNMHDR, LRESULT *pResult) {
	NM_LISTVIEW *pNMListView = (NM_LISTVIEW*)pNMHDR;

	//this works because true is equal to 1 and false equal to 0
	BOOL notLast = pNMListView->iItem + 1 != GetItemCount();
	BOOL notFirst = pNMListView->iItem != 0;
	RedrawItems(pNMListView->iItem - notFirst, pNMListView->iItem + notLast);
}

int CDownloadListCtrl::Compare(CPartFile* file1, CPartFile* file2, LPARAM lParamSort) {
	switch(lParamSort){
	case 0: //filename asc
		return _tcsicmp(file1->GetFileName(),file2->GetFileName());
	case 1: //size asc
		return CompareUnsigned(file1->GetFileSize(), file2->GetFileSize());
	case 2: //transfered asc
		return CompareUnsigned(file1->GetTransfered(), file2->GetTransfered());
	case 3: //completed asc
		return CompareUnsigned(file1->GetCompletedSize(), file2->GetCompletedSize());
	case 4: //speed asc
		return CompareUnsigned(file1->GetDatarate(), file2->GetDatarate());
	case 5: //progress asc
		{
			float comp = file1->GetPercentCompleted() - file2->GetPercentCompleted();
			if(comp > 0)
				return 1;
			else if(comp < 0)
				return -1;
			else
				return 0;
		}
	case 6: //sources asc
		return file1->GetSourceCount() - file2->GetSourceCount();
	case 7: //priority asc
		return file1->GetDownPriority() - file2->GetDownPriority();
	case 8: //Status asc 
        {
		    int comp =  file1->getPartfileStatusRang()-file2->getPartfileStatusRang();

            // Second sort order on filename
            if(comp == 0) {
                comp = strcmpi(file1->GetFileName(),file2->GetFileName());
            }

            return comp;
        }
		//return file1->getPartfileStatusRang()-file2->getPartfileStatusRang(); 
	case 9: //Remaining Time asc 
		if (file1->GetDatarate())
			if (file2->GetDatarate())
                return file1->getTimeRemaining()-file2->getTimeRemaining() ;
			else
				return -1;
		else
			if (file2->GetDatarate())
				return 1;
			else
                return 0;
	case 10: //last seen complete asc 
		if (file1->lastseencomplete > file2->lastseencomplete)
			return 1;
		else if(file1->lastseencomplete < file2->lastseencomplete)
			return -1;
		else
			return 0;
	case 11: //last received Time asc 
		if (file1->GetFileDate() > file2->GetFileDate())
			return 1;
		else if(file1->GetFileDate() < file2->GetFileDate())
			return -1;
		else
			return 0;

	case 12:
		return _tcsicmp(
			(file1->GetCategory()!=0)?theApp.glob_prefs->GetCategory(file1->GetCategory())->title:GetResString(IDS_ALL),
			(file2->GetCategory()!=0)?theApp.glob_prefs->GetCategory(file2->GetCategory())->title:GetResString(IDS_ALL)
		);

	default:
		return 0;
	}
}

int CDownloadListCtrl::Compare(CUpDownClient *client1, CUpDownClient *client2, LPARAM lParamSort, int sortMod) {
	switch(lParamSort){
	case 0: //name asc
		if(client1->GetUserName() == client2->GetUserName())
			return 0;
		else if(!client1->GetUserName())
			return 1;
		else if(!client2->GetUserName())
			return -1;
		return _tcsicmp(client1->GetUserName(),client2->GetUserName());
	case 1: //size but we use status asc
		return client1->GetDownloadState() - client2->GetDownloadState();
	case 2://transfered asc
	case 3://completed asc
		return CompareUnsigned(client1->GetTransferedDown(), client2->GetTransferedDown());
	case 4: //speed asc
		return CompareUnsigned(client1->GetDownloadDatarate(), client2->GetDownloadDatarate());
	case 5: //progress asc
		return client1->GetAvailablePartCount() - client2->GetAvailablePartCount();
	case 6:
		if( client1->GetClientSoft() == client2->GetClientSoft() )
			return client2->GetVersion() - client1->GetVersion();
		return client1->GetClientSoft() - client2->GetClientSoft();
	case 7: //qr asc
		if(client1->GetDownloadState() == DS_DOWNLOADING)
			if(client2->GetDownloadState() == DS_DOWNLOADING)
				return CompareUnsigned(client2->GetDownloadDatarate(), client1->GetDownloadDatarate());
			else
				return -1;
		else if (client2->GetDownloadState() == DS_DOWNLOADING)
			return 1;
		if(client1->GetRemoteQueueRank() == 0 && client1->GetDownloadState() == DS_ONQUEUE && client1->IsRemoteQueueFull() == true) return 1;
		if(client2->GetRemoteQueueRank() == 0 && client2->GetDownloadState() == DS_ONQUEUE && client2->IsRemoteQueueFull() == true) return -1;
		if(client1->GetRemoteQueueRank() == 0) return 1;
		if(client2->GetRemoteQueueRank() == 0) return -1;
		return client1->GetRemoteQueueRank() - client2->GetRemoteQueueRank();
	case 8:
		if( client1->GetDownloadState() == client2->GetDownloadState() ){
			if( client1->IsRemoteQueueFull() && client2->IsRemoteQueueFull() )
				return 0;
			else if( client1->IsRemoteQueueFull() )
				return 1;
			else if( client2->IsRemoteQueueFull() )
				return -1;
			else
				return 0;
		}
		return client1->GetDownloadState() - client2->GetDownloadState();
	default:
		return 0;
	}
}

void CDownloadListCtrl::OnNMDblclkDownloadlist(NMHDR *pNMHDR, LRESULT *pResult) {
	int iSel = GetSelectionMark();
	if (iSel != -1){
		CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(iSel);
		if (content && content->value){
			if (content->type == FILE_TYPE){
				if (!theApp.glob_prefs->IsDoubleClickEnabled()){
					CPoint pt;
					::GetCursorPos(&pt);
					ScreenToClient(&pt);
					LVHITTESTINFO hit;
					hit.pt = pt;
					if (HitTest(&hit) >= 0 && (hit.flags & LVHT_ONITEM)){
						LVHITTESTINFO subhit;
						subhit.pt = pt;
						if (SubItemHitTest(&subhit) >= 0 && subhit.iSubItem == 0){
							CFileDetailDialog dialog((CPartFile*)content->value);
							dialog.DoModal();
						}
					}
				}
			}
			else{
				CClientDetailDialog dialog((CUpDownClient*)content->value);
				dialog.DoModal();
			}
		}
	}
	
	*pResult = 0;
}

void CDownloadListCtrl::CreateMenues() {
	if (m_PrioMenu) VERIFY( m_PrioMenu.DestroyMenu() );
	if (m_FileMenu) VERIFY( m_FileMenu.DestroyMenu() );

	m_PrioMenu.CreateMenu();
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIOLOW,GetResString(IDS_PRIOLOW));
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIONORMAL,GetResString(IDS_PRIONORMAL));
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIOHIGH, GetResString(IDS_PRIOHIGH));
	m_PrioMenu.AppendMenu(MF_STRING,MP_PRIOAUTO, GetResString(IDS_PRIOAUTO));

	m_FileMenu.CreatePopupMenu();
	m_FileMenu.AddMenuTitle(GetResString(IDS_DOWNLOADMENUTITLE));
	m_FileMenu.AppendMenu(MF_STRING|MF_POPUP,(UINT_PTR)m_PrioMenu.m_hMenu, GetResString(IDS_PRIORITY) );

	m_FileMenu.AppendMenu(MF_STRING,MP_CANCEL,GetResString(IDS_MAIN_BTN_CANCEL) );
	m_FileMenu.AppendMenu(MF_STRING,MP_STOP, GetResString(IDS_DL_STOP));
	m_FileMenu.AppendMenu(MF_STRING,MP_PAUSE, GetResString(IDS_DL_PAUSE));
	m_FileMenu.AppendMenu(MF_STRING,MP_RESUME, GetResString(IDS_DL_RESUME));
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING,MP_OPEN, GetResString(IDS_DL_OPEN) );//<--9/21/02
	m_FileMenu.AppendMenu(MF_STRING,MP_PREVIEW, GetResString(IDS_DL_PREVIEW) );
	m_FileMenu.AppendMenu(MF_STRING,MP_METINFO, GetResString(IDS_DL_INFO) );//<--9/21/02
	m_FileMenu.AppendMenu(MF_STRING,MP_VIEWFILECOMMENTS, GetResString(IDS_CMT_SHOWALL) );

	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING,MP_CLEARCOMPLETED, GetResString(IDS_DL_CLEAR));
	m_FileMenu.AppendMenu(MF_STRING,MP_GETED2KLINK, GetResString(IDS_DL_LINK1) );
	m_FileMenu.AppendMenu(MF_STRING,MP_GETHTMLED2KLINK, GetResString(IDS_DL_LINK2));
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	//SetMenu(&m_FileMenu);
}

CString CDownloadListCtrl::getTextList() {
	CString out="";

	// Search for file(s)
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){ // const is better
		CtrlItem_Struct* cur_item = it->second;
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);

			theApp.emuledlg->AddLogLine(false, file->GetFileName());

			char buffer[50+1];
			ASSERT(file->GetFileName() != NULL);
			strncpy(buffer, file->GetFileName(), 50);
			buffer[50] = '\0';

			CString temp;
			temp.Format("\n%s\t [%.1f%%] %i/%i - %s",
						buffer,
						file->GetPercentCompleted(),
						file->GetTransferingSrcCount(),
						file->GetSourceCount(), 
						file->getPartfileStatus());
			
			out += temp;
		}
	}

	theApp.emuledlg->AddLogLine(false, out);
	return out;
}

void CDownloadListCtrl::ShowFilesCount() {
	CString counter;
	uint16	count=0;//theApp.downloadqueue->GetFileCount();
/*
	for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
		const CtrlItem_Struct* cur_item = it->second;
		if (cur_item->type == FILE_TYPE){
			CPartFile* file=(CPartFile*)cur_item->value;
			if (file->GetCategory()==curTab || 
				(!theApp.glob_prefs->ShowAllNotCats() && file->GetCategory()>0 && curTab==0 ) ) count++;
		}
	}
*/
	count=GetItemCount();

	counter.Format("%s (%u)", GetResString(IDS_TW_DOWNLOADS),count);
	theApp.emuledlg->transferwnd.GetDlgItem(IDC_DOWNLOAD_TEXT)->SetWindowText(counter);
}

void CDownloadListCtrl::ShowSelectedFileDetails() {
	POINT point;
	::GetCursorPos(&point);
	CPoint p = point; 
    ScreenToClient(&p); 
    int it = HitTest(p); 
	SetSelectionMark(it);   // display selection mark correctly! 
    if (it == -1) return;

	CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(GetSelectionMark());

	if (content->type == FILE_TYPE){
		CPartFile* file = (CPartFile*)content->value;

		if ((file->HasComment() || file->HasRating()) && p.x<13 ) {
			CCommentDialogLst dialog(file);
			dialog.DoModal();
		}
		else {
			CFileDetailDialog dialog(file);
			dialog.DoModal();
		}
	}else {
		CUpDownClient* client = (CUpDownClient*)content->value;
		CClientDetailDialog dialog(client);
		dialog.DoModal();
	}
}

void CDownloadListCtrl::ChangeCategory(int newsel){

	SetRedraw(FALSE);

	// remove all displayed files with a different cat and show the correct ones
	for(ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++){
		const CtrlItem_Struct* cur_item = it->second;
		if (cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			
			if ( !CheckShowItemInGivenCat(file,newsel)) HideFile(file);
				else ShowFile(file);

		}
	}

	SetRedraw(TRUE);
	curTab=newsel;
	ShowFilesCount();
}

void CDownloadListCtrl::HideFile(CPartFile* tohide){
	HideSources(tohide);

	// Retrieve all entries matching the source
	std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(tohide);
	for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
		CtrlItem_Struct* updateItem  = it->second;

		// Find entry in CListCtrl and update object
 		LVFINDINFO find;
		find.flags = LVFI_PARAM;
		find.lParam = (LPARAM)updateItem;
		sint16 result = FindItem(&find);
		if(result != (-1)) {
			DeleteItem(result);
			return;
		}
	}
}

void CDownloadListCtrl::ShowFile(CPartFile* toshow){
	// Retrieve all entries matching the source
	std::pair<ListItems::const_iterator, ListItems::const_iterator> rangeIt = m_ListItems.equal_range(toshow);
	for(ListItems::const_iterator it = rangeIt.first; it != rangeIt.second; it++){
		CtrlItem_Struct* updateItem  = it->second;

		// Check if entry is already in the List
 		LVFINDINFO find;
		find.flags = LVFI_PARAM;
		find.lParam = (LPARAM)updateItem;
		sint16 result = FindItem(&find);
		if(result == (-1))
			InsertItem(LVIF_PARAM,GetItemCount(),0,0,0,0,(LPARAM)updateItem);

		return;
	}
}

void CDownloadListCtrl::GetDisplayedFiles(CArray<CPartFile*,CPartFile*> *list){
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* cur_item = it->second;
		it++; // Already point to the next iterator. 
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			list->Add(file);
		}
	}	
}

void CDownloadListCtrl::MoveCompletedfilesCat(uint8 from, uint8 to){
	for(ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ){
		CtrlItem_Struct* cur_item = it->second;
		it++; // Already point to the next iterator.
		if(cur_item->type == FILE_TYPE){
			CPartFile* file = reinterpret_cast<CPartFile*>(cur_item->value);
			if (!file->IsPartFile() && file->GetCategory()==from )
				file->SetCategory(to);
		}
	}
}