//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"

// 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(){
	for (POSITION pos = listcontent.GetHeadPosition();pos != 0;listcontent.GetNext(pos))
		delete listcontent.GetAt(pos);
}

void CDownloadListCtrl::Init(){
	CImageList ilDummyImageList; //dummy list
	ilDummyImageList.Create(1, 17,ILC_COLOR32 | ILC_MASK, 1, 1); 
	SetImageList(&ilDummyImageList, LVSIL_SMALL);
	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_SPEED),LVCFMT_LEFT, 65);
	InsertColumn(4,GetResString(IDS_DL_PROGRESS),LVCFMT_LEFT, 170);
	InsertColumn(5,GetResString(IDS_DL_SOURCES),LVCFMT_LEFT, 50);
	InsertColumn(6,GetResString(IDS_PRIORITY),LVCFMT_LEFT, 55);
	InsertColumn(7,GetResString(IDS_STATUS),LVCFMT_LEFT, 70);
	InsertColumn(8,GetResString(IDS_DL_REMAINS),LVCFMT_LEFT, 110);

	m_ImageList.Create(16,16,ILC_COLOR32|ILC_MASK,0,20);
	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_TAROD)); // Added by Tarod

	CreateMenues();
	LoadSettings(CPreferences::tableDownload);

	// 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_SPEED);
	hdi.pszText = strRes.GetBuffer();
	pHeaderCtrl->SetItem(3, &hdi);
	strRes.ReleaseBuffer();

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

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

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

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

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

void CDownloadListCtrl::AddFile(CPartFile* toadd){
        CtrlItem_Struct* newitem = new CtrlItem_Struct;
        uint16 itemnr = GetItemCount();
        newitem->owner = 0;
        newitem->type = 1;
        newitem->value = toadd;
        newitem->parent = NULL;
		newitem->dwUpdated = false; 
        listcontent.AddTail(newitem);
        InsertItem(LVIF_PARAM,itemnr,0,0,0,0,(LPARAM)newitem);
}

void CDownloadListCtrl::AddSource(CPartFile* owner,CUpDownClient* source,bool notavailable){
        
        CtrlItem_Struct* newitem = new CtrlItem_Struct;
        newitem->owner = owner;
        newitem->type = (notavailable)? 3:2;
        newitem->value = source;
		newitem->dwUpdated = false; 

        listcontent.AddTail(newitem);

        CtrlItem_Struct* search = 0;
        for (POSITION pos = listcontent.GetHeadPosition();pos != 0;listcontent.GetNext(pos)){
                if (listcontent.GetAt(pos)->value == owner){
                        search = listcontent.GetAt(pos);
                        break;
                }
        }
        newitem->parent = search;

        if (!owner->srcarevisible)
                return;

        LVFINDINFO find;
        find.flags = LVFI_PARAM;
        find.lParam = (LPARAM)search;
        uint16 itemnr = FindItem(&find);
        while (GetItemCount() > itemnr+1 && ((CtrlItem_Struct*)GetItemData(itemnr+1))->type != 1)
                itemnr++;
        InsertItem(LVIF_PARAM,itemnr+1,0,0,0,0,(LPARAM)newitem);
}


void CDownloadListCtrl::RemoveSource(CUpDownClient* source,CPartFile* owner){
   POSITION pos1, pos2;
   for( pos1 = listcontent.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
	   listcontent.GetNext(pos1);
	   CtrlItem_Struct* delitem = listcontent.GetAt(pos2);
	   if(delitem->value == source && (delitem->owner == owner || (!owner) ) ){
 			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)delitem;
			sint16 result = FindItem(&find);
			if (result != (-1))
				DeleteItem(result);
			listcontent.RemoveAt(pos2);
			delete delitem;
       }
   }
}

void CDownloadListCtrl::RemoveFile(CPartFile* toremove){
   POSITION pos1, pos2;
   for( pos1 = listcontent.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
	   CtrlItem_Struct* delitem = listcontent.GetNext(pos1);    
	   if(delitem->value == toremove){
 			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)delitem;
			sint16 result = FindItem(&find);
			if (result != -1)
				DeleteItem(result);
			listcontent.RemoveAt(pos2);
			delete delitem;
       }
   }
}

void CDownloadListCtrl::UpdateItem(void* toupdate){
	CtrlItem_Struct* search = 0;
	for (POSITION pos = listcontent.GetHeadPosition();pos !=  0;listcontent.GetNext(pos)){
		if (listcontent.GetAt(pos)->value == toupdate){
			search = listcontent.GetAt(pos);
			LVFINDINFO find;
			find.flags = LVFI_PARAM;
			find.lParam = (LPARAM)search;
			sint16 result = FindItem(&find);
			if (result != -1){
				search->dwUpdated = 0;
				Update(result);
			}
			return;
		}
	}
}

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

		char buffer[200];
		CPartFile *lpPartFile = (CPartFile*)lpCtrlItem->value;
		switch(nColumn) {

		case 0:		// file name
			dc->DrawText(lpPartFile->GetFileName(),
				(int)strlen(lpPartFile->GetFileName()),lpRect, DLC_DT_TEXT);
			break;

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

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

		case 3:		// speed
			if (lpPartFile->GetTransferingSrcCount()==0)
				sprintf(buffer, "");
			else
				sprintf(buffer,"%.1f %s", lpPartFile->GetDatarate() / 1024.0f,GetResString(IDS_KBYTESEC));

			dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			break;

		case 4:		// 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 5:		// sources
			{
				char buffer2[64];
				uint16 ncsc=lpPartFile->GetNotCurrentSourcesCount();
				if(ncsc>0) sprintf(buffer2,"%i/",lpPartFile->GetSourceCount() -ncsc); else sprintf(buffer2,"");
				sprintf(buffer,"%s%i (%i)",buffer2,
					lpPartFile->GetSourceCount(),
					lpPartFile->GetTransferingSrcCount());
					//UAP
					if (((CKnownFile*)lpPartFile)->IsAutoPrioritized())
						((CKnownFile*)lpPartFile)->UpdateUploadAutoPriority() ;
					theApp.sharedfiles->UpdateItem(((CKnownFile*)lpPartFile)) ;
					//end UAP
				dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			}
			break;

		case 6:		// prio
			// auto priority (stumpi) patched and updated by Tarod
			if (lpPartFile->IsAutoPrioritized() && theApp.glob_prefs->IsUAPEnabled()) {
				lpPartFile->UpdateDownloadAutoPriority();

					switch(lpPartFile->GetPriority()) {
						case PR_LOW:
							dc->DrawText(GetResString(IDS_PRIOAUTOLOW),GetResString(IDS_PRIOAUTOLOW).GetLength(),lpRect, DLC_DT_TEXT);
							break;
						case PR_NORMAL:
							dc->DrawText(GetResString(IDS_PRIOAUTONORMAL),GetResString(IDS_PRIOAUTONORMAL).GetLength(),lpRect, DLC_DT_TEXT);
							break;
						case PR_HIGH:
							dc->DrawText(GetResString(IDS_PRIOAUTOHIGH),GetResString(IDS_PRIOAUTOHIGH).GetLength(),lpRect, DLC_DT_TEXT);
							break;
					}
				} else {
			switch(lpPartFile->GetPriority()) {
				case PR_LOW:
				dc->DrawText(GetResString(IDS_PRIOLOW),GetResString(IDS_PRIOLOW).GetLength(),lpRect, DLC_DT_TEXT);
				break;
				case PR_NORMAL:
				dc->DrawText(GetResString(IDS_PRIONORMAL),GetResString(IDS_PRIONORMAL).GetLength(),lpRect, DLC_DT_TEXT);
				break;
				case PR_HIGH:
				dc->DrawText(GetResString(IDS_PRIOHIGH),GetResString(IDS_PRIOHIGH).GetLength(),lpRect, DLC_DT_TEXT);
				break;
			}
				}
			break;

		case 7:		// <<--9/21/02
			sprintf(buffer,"%s", lpPartFile->getPartfileStatus());
			dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			break;

		case 8:		// remaining time & size
			{
				char bufferSize[50];
				char bufferTime[50];

				//size 
				uint32 remains;
				remains=lpPartFile->GetFileSize()-lpPartFile->GetCompletedSize();		//<<-- 09/27/2002, CML

				if( remains < 0 ) remains = 0;
				CastItoXBytes(remains,bufferSize);

				// time 
				sint32 restTime=lpPartFile->getTimeRemaining();
				CastSecondsToHM(restTime,bufferTime);
				sprintf(buffer,"%s (%s)",bufferTime,bufferSize);
				if (lpPartFile->GetStatus()==PS_COMPLETING || lpPartFile->GetStatus()==PS_COMPLETE )
					sprintf(buffer,"");

				// Added by Tarod (modified) [AgentSmith]
				float saved = (float)lpPartFile->GetTransfered() / (float)lpPartFile->GetFileSize(); 
				saved *= 100.0f; 
				if (saved < 100.0f) {
					sprintf(buffer, "%s [%.1f%%]", buffer, (100.0f - saved)); 
				} else if (saved > 100.0f) {
					sprintf(buffer, "%s [-%.1f%%]", buffer, (saved - 100.0f));
				}
				// End Tarod
				dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			}
			break;
		}
	}
}

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

		char buffer[100];
		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 == 2){
					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:
						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};
					if (lpUpDownClient->IsTarodVersion()) { // Modified by Tarod
						m_ImageList.Draw(dc, 9, point2, ILD_NORMAL); // Shows Tarod icon in case that is a Tarod/Plus version
					} else {
						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
						m_ImageList.Draw(dc, 7, point2, ILD_NORMAL);
					cur_rec.left += 20;
				}

				char* buffer1;
				if (!lpUpDownClient->GetUserName()) {
					buffer1 = new char [50];
					sprintf(buffer1,"?");
				}
				else {
					buffer1 = new char [strlen(lpUpDownClient->GetUserName()) + 50];
					sprintf(buffer1,"%s",lpUpDownClient->GetUserName());
				}
				dc->DrawText(buffer1,(int)strlen(buffer1),&cur_rec, DLC_DT_TEXT);
				delete[] buffer1;
			}
			break;

		case 1:		// size
			break;

		case 2:		// transfered
			if (lpCtrlItem->type == 2 && lpUpDownClient->GetTransferedDown()) {
				CastItoXBytes(lpUpDownClient->GetTransferedDown(),buffer);
				dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);	
			}
			break;

		case 3:		// speed
			if (lpCtrlItem->type == 2 && lpUpDownClient->GetDownloadDatarate()){
				if (lpUpDownClient->GetDownloadDatarate()==0) sprintf(buffer,""); else
					sprintf(buffer,"%.1f %s", lpUpDownClient->GetDownloadDatarate()/1024.0f,GetResString(IDS_KBYTESEC));
				dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			}
			break;

		case 4:		// 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 == 3), 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 5:{		// sources
				switch(lpUpDownClient->GetClientSoft()){
					case SO_EDONKEY:
						sprintf(buffer, "eDonkey v%i", lpUpDownClient->GetVersion());
						break;
					case SO_EMULE:
					case SO_OLDEMULE:{
						int temp = lpUpDownClient->GetMuleVersion()/16;
						sprintf(buffer, "eMule v%i", (temp*10)+( lpUpDownClient->GetMuleVersion()-temp*16));

						// Added by Tarod -checks for Plus or Tarod versions
						if (lpUpDownClient->IsTarodVersion()) {
							if (lpUpDownClient->GetTarodVersionNumber() > 0) {
								sprintf(buffer, "%s [Tarod.%03x", buffer, lpUpDownClient->GetTarodVersionNumber());
								// If last version digit is 0 just trim it off
								if (buffer[strlen(buffer) - 1] == '0') {
									buffer[strlen(buffer) - 1] = ']';
								} else {
									strcat(buffer, "]");
								}
							} else {
								sprintf(buffer, "%s [Tarod/Plus]", buffer);
							}
						}
						break;
									 }
					case SO_MLDONKEY:
						sprintf(buffer, "Old MlDonkey");
						break;
					case SO_UNKNOWN:
						sprintf(buffer, GetResString(IDS_UNKNOWN));
				}
				dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
				break;
			   }

		case 6:		// prio
			if (lpUpDownClient->IsEmuleClient() && 
				lpUpDownClient->GetDownloadState()==DS_ONQUEUE
			){
				if( lpUpDownClient->IsRemoteQueueFull()  ){
					sprintf(buffer,GetResString(IDS_QUEUEFULL));
					dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
				}
				else{
					if ( lpUpDownClient->GetRemoteQueueRank()){
						sprintf(buffer,"QR: %u",lpUpDownClient->GetRemoteQueueRank());
						dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
					}
				}
			} else {
						sprintf(buffer,"");
						dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			}
			break;

		case 7:	{	// status
			CString status = "";
			if (lpCtrlItem->type == 2){
				switch (lpUpDownClient->GetDownloadState()) {
					case DS_CONNECTING:
						status = GetResString(IDS_CONNECTING);
						break;
					case DS_CONNECTED:
						status = GetResString(IDS_ASKING);
						break;
					case DS_WAITCALLBACK:
						status = GetResString(IDS_CONNVIASERVER);
						break;
					case DS_ONQUEUE:
						status = GetResString(IDS_ONQUEUE);
						break;
					case DS_DOWNLOADING:
						status = GetResString(IDS_TRANSFERRING);
						break;
					case DS_REQHASHSET:
						status = GetResString(IDS_RECHASHSET);
						break;
					case DS_NONEEDEDPARTS:
						status = GetResString(IDS_NONEEDEDPARTS);
						break;
					case DS_LOWTOLOWIP:
						status = GetResString(IDS_NOCONNECTLOW2LOW);
						break;
					case DS_TOOMANYCONNS:
						status = GetResString(IDS_TOOMANYCONNS);
						break;
					default:
						status = GetResString(IDS_UNKNOWN);
				}
			}
			else {
				status = GetResString(IDS_ASKED4ANOTHERFILE);
			}
			sprintf(buffer,"%s",status);
			dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
			break;
				}
		case 8:		// 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 == 1) && (lpDrawItemStruct->itemAction | ODA_SELECT) &&
	    (lpDrawItemStruct->itemState & ODS_SELECTED)) {

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

	} /*else if (content->type != 1) {
		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;
	int tree_end;

	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 == 1){
		for(int iCurrent = 0; iCurrent < iCount; iCurrent++) {

			int iColumn = pHeaderCtrl->OrderToIndex(iCurrent);
			int cx = CListCtrl::GetColumnWidth(iColumn);
			if(iColumn == 4) {
				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, 4, &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 == 3 || content->type == 2){

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

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

			if(iColumn == 4) {
				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, 4, &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 == 1)) {
			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 == 1)
				outline_rec.top--;
		} 

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

			CtrlItem_Struct* next = (CtrlItem_Struct*)this->GetItemData(lpDrawItemStruct->itemID + 1);
			if(next->type == 1)
				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 != 1;
		BOOL isOpenRoot = hasNext && content->type == 1;
		BOOL isChild = content->type != 1;
		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);
}

void CDownloadListCtrl::HideSources(CPartFile* toCollapse) {
	toCollapse->srcarevisible = false; 
	SetRedraw(false);
	for(int i = 0; i < GetItemCount(); i++) {
		CtrlItem_Struct* item = (CtrlItem_Struct*)this->GetItemData(i);
		if(item->owner == toCollapse) {
			item->dwUpdated = 0;
			item->status.DeleteObject();
			DeleteItem(i--);
		}
	}
	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_NOTIFY_REFLECT(NM_RCLICK, OnRightClick)
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
END_MESSAGE_MAP()



// CDownloadListCtrl message handlers

void CDownloadListCtrl::OnItemActivate(NMHDR *pNMHDR, LRESULT *pResult){
	LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(pNMIA->iItem);

	if (content->type == 1){
		CPartFile* partfile = (CPartFile*)content->value;
		
		if (!partfile->srcarevisible) {
			if(partfile->GetSourceCount() > 0) {
				SetRedraw(false);
				partfile->srcarevisible = true;
				for (POSITION pos = listcontent.GetHeadPosition();pos != 0;listcontent.GetNext(pos)){
					CtrlItem_Struct* cur_item = (CtrlItem_Struct*)listcontent.GetAt(pos);
					if (cur_item->owner == partfile){
						InsertItem(LVIF_PARAM,pNMIA->iItem+1,0,0,0,0,(LPARAM)cur_item);
					}
				}
				SetRedraw(true);
			}
		}
		else {
			HideSources(partfile);
		}
	}
	*pResult = 0;
}

void CDownloadListCtrl::OnRightClick(NMHDR *pNMHDR, LRESULT *pResult){	
	POINT point;
	::GetCursorPos(&point);
	if (GetSelectionMark() != (-1)){
		CtrlItem_Struct* content = (CtrlItem_Struct*)this->GetItemData(GetSelectionMark());
		if (content->type == 1){
			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->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_LOADSOURCES,((file->GetStatus() != PS_COMPLETE && file->GetStatus() != PS_COMPLETING)?MF_ENABLED:MF_GRAYED)); // Add mr.fry
			//m_FileMenu.EnableMenuItem(MP_SAVESOURCES,MF_ENABLED); // Add mr.fry

			//m_FileMenu.EnableMenuItem(MP_DROPNONEEDEDSRCS,MF_ENABLED); // Added by Tarod
			//m_FileMenu.EnableMenuItem(MP_ALL_A4AF_TO_HERE, MF_ENABLED); // Added by Tarod
			m_FileMenu.CheckMenuItem(MP_ALL_A4AF_AUTO, file->IsA4AFAuto()? MF_CHECKED: MF_UNCHECKED); // Added by Tarod

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

			m_PrioMenu.EnableMenuItem(MP_PRIOAUTO, ((theApp.glob_prefs->IsUAPEnabled()) ? MF_ENABLED:MF_GRAYED)); //Hunter
			m_PrioMenu.CheckMenuItem(MP_PRIOAUTO, (file->IsAutoPrioritized())? MF_CHECKED:MF_UNCHECKED); //[Tarod]
			m_PrioMenu.CheckMenuItem(MP_PRIOHIGH,(file->GetPriority() == PR_HIGH)&&(!file->IsAutoPrioritized())? MF_CHECKED:MF_UNCHECKED);
			m_PrioMenu.CheckMenuItem(MP_PRIONORMAL,(file->GetPriority() == PR_NORMAL)&&(!file->IsAutoPrioritized())? MF_CHECKED:MF_UNCHECKED);
			m_PrioMenu.CheckMenuItem(MP_PRIOLOW,(file->GetPriority() == PR_LOW)&&(!file->IsAutoPrioritized())? MF_CHECKED:MF_UNCHECKED);

			m_FileMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y, this);
		}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));
			if (content->type == 3) m_ClientMenu.AppendMenu(MF_STRING,MP_DOWNNOW, IDS_DOWNLOAD_A4AF); // Added by Tarod [Ambdribant]
			m_ClientMenu.TrackPopupMenu(TPM_LEFTALIGN |TPM_RIGHTBUTTON, point.x, point.y, this);
			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);
	}
	*pResult = 0;
}

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 == 1) 
					selectedList.AddTail((CPartFile*)((CtrlItem_Struct*)this->GetItemData(index))->value); 
			} 
		} 

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

			switch (wParam)
			{
				case MP_CANCEL:
				{
					//for multiple selections 
					if(selectedCount > 1)
					{ 
						CString fileList; 
						for (pos = selectedList.GetHeadPosition() ; pos != 0 ; selectedList.GetNext(pos))
						{
							fileList += "\n" ; 
							fileList += selectedList.GetAt(pos)->GetFileName(); 
						} 
						if (MessageBox((GetResString(IDS_Q_CANCELDL) + fileList),GetResString(IDS_CANCEL),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:
										theApp.downloadqueue->StartNextFile();
										selectedList.GetHead()->DeleteFile(); 
										selectedList.RemoveHead(); 
								}
							}  
						}
						break; 
					}
					// single selection
					switch(file->GetStatus())
					{
						case PS_WAITINGFORHASH:
						case PS_HASHING:
						case PS_COMPLETING:
						case PS_COMPLETE:
							break;
						case PS_PAUSED:
							if (MessageBox(GetResString(IDS_Q_CANCELDL2),GetResString(IDS_CANCEL),MB_ICONQUESTION|MB_YESNO) == IDYES) {
								HideSources(file);
								file->DeleteFile();
							}
							break;
						default:
							if (MessageBox(GetResString(IDS_Q_CANCELDL2),GetResString(IDS_CANCEL),MB_ICONQUESTION|MB_YESNO) == IDYES) {
								HideSources(file);
								file->DeleteFile();
								theApp.downloadqueue->StartNextFile();
							}
						break;
					}
					break;
				}
				case MP_PRIOHIGH:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
							selectedList.GetHead()->SetPriority(PR_HIGH); 
							selectedList.RemoveHead(); 
						} 
						break; 
					} 
					file->SetAutoPriority(false);
					file->SetPriority(PR_HIGH);
					break;
				case MP_PRIOLOW:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
							selectedList.GetHead()->SetPriority(PR_LOW); 
							selectedList.RemoveHead(); 
						} 
						break; 
					} 
					file->SetAutoPriority(false);
					file->SetPriority(PR_LOW);
					break;
				case MP_PRIONORMAL:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) {
							selectedList.GetHead()->SetAutoPriority(false); //[Tarod]
							selectedList.GetHead()->SetPriority(PR_NORMAL);
							selectedList.RemoveHead();
						}
						break;
					} 
					file->SetAutoPriority(false);
					file->SetPriority(PR_NORMAL);
					break;
				case MP_PRIOAUTO:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) {
							selectedList.GetHead()->SetPriority(PR_AUTO);
							selectedList.RemoveHead();
						}
						break;
					} 
					file->SetPriority(PR_AUTO);
					break;
				case MP_PAUSE:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->PauseFile(); 
							selectedList.RemoveHead(); 
						} 
						break; 
					} 
					file->PauseFile();
					break;
				case MP_RESUME:
					if(selectedCount > 1)
					{
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->ResumeFile(); 
							selectedList.RemoveHead(); 
						} 
						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:
					ClearCompleted();
					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[250];
					sprintf(buffer,"%s\\%s",theApp.glob_prefs->GetIncomingDir(),file->GetFileName());
					ShellOpenFile(buffer);
					delete buffer;
					break; 
				}
				case MP_LOADSOURCES:	//added by Mr.Fry
					if(selectedCount > 1){
						while(!selectedList.IsEmpty()){
							selectedList.GetHead()->RemoveNoNeededSources(); // Added by Tarod -clean no needed sources
							if(theApp.downloadqueue->KeepStoredSourcesOpen())
								selectedList.GetHead()->AddFromStoredSources(selectedList.GetHead()->GetStoredSources(),true,-1,true);
							else
								selectedList.GetHead()->LoadAndAddSources(false,true,-1,true);
							selectedList.RemoveHead();
						}
						break;
					}
					file->RemoveNoNeededSources(); // Added by Tarod -clean no needed sources
					if(theApp.downloadqueue->KeepStoredSourcesOpen())
						file->AddFromStoredSources(file->GetStoredSources(),true,-1,true);
					else
						file->LoadAndAddSources(false,true,-1,true);
					break;
				case MP_SAVESOURCES:	//added by Mr.Fry
					if(selectedCount > 1){
						while(!selectedList.IsEmpty()){
							selectedList.GetHead()->SaveSources();
							selectedList.RemoveHead();
						}
						break;
					}
					file->SaveSources();
					break;
				case MP_ALL_A4AF_TO_HERE: { // Added by Tarod
					DownloadAllA4AF(file);
					break;
				}
				case MP_ALL_A4AF_AUTO: {
					if (file->IsA4AFAuto()) {
						file->SetA4AFAuto(false);
					} else {
						theApp.downloadqueue->DisableAllA4AFAuto();
						file->SetA4AFAuto(true);
					}
					break;
				}
				case MP_DROPNONEEDEDSRCS: { // Added by Tarod
					if(selectedCount > 1) {
						while(!selectedList.IsEmpty()) { 
							selectedList.GetHead()->RemoveNoNeededSources();
							selectedList.RemoveHead(); 
						} 
						break; 
					} 
					file->RemoveNoNeededSources();
					break;
				}
				case MP_PREVIEW:{
					if(selectedCount > 1)
						break;
					file->PreviewFile();
					break;
				}
				case MP_VIEWFILECOMMENTS:{
					CCommentDialogLst dialog(file); 
					dialog.DoModal(); 
					break;
				}
				case MP_JIGLE_FAKECHECK: { // Added by Tarod [deltaHF] -fixed, wrong arguments
					theApp.Action(NULL, file, NULL);
					break;
				}
			}
		}
		else{
			CUpDownClient* client = (CUpDownClient*)content->value;
			switch (wParam){
				case MP_SHOWLIST:
					client->RequestSharedFileList();
					break;
				case MP_DOWNNOW: // Added by Tarod [Amdribant]
					client->HandoverToAnotherFile((CPartFile*)content->owner); 
					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:10));

	*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 >= 10) {
		sortMod = -1;
		lParamSort -= 10;
	}

	int comp;

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

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

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

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

	} else if (item1->type == 1) {
		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(){
   POSITION pos1, pos2;
   for( pos1 = listcontent.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
	   listcontent.GetNext(pos1);
	   CtrlItem_Struct* delitem = listcontent.GetAt(pos2);
	   if (delitem->type == 1){
		   CPartFile* file = (CPartFile*)delitem->value;
		   if (!file->IsPartFile())
			   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 strcmpi(file1->GetFileName(),file2->GetFileName());
	case 1: //size asc
		return file1->GetFileSize() - file2->GetFileSize();
	case 2: //transfered asc
		return file1->GetTransfered() - file2->GetTransfered();
	case 3: //speed asc
		return file1->GetDatarate() - file2->GetDatarate();
	case 4: //progress asc
		{
			float comp = file1->GetPercentCompleted() - file2->GetPercentCompleted();
			if(comp > 0)
				return 1;
			else if(comp < 0)
				return -1;
			else
				return 0;
		}
	case 5: //sources asc
		return file1->GetSourceCount() - file2->GetSourceCount();
	case 6: //priority asc
		return file1->GetPriority() - file2->GetPriority();
	case 7: //Status asc 
		return file1->getPartfileStatusRang()-file2->getPartfileStatusRang(); 
	case 8: //Remaining Time asc 
		return file1->getTimeRemaining()-file2->getTimeRemaining() ;
	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 strcmpi(client1->GetUserName(),client2->GetUserName());
	case 1: //size but we use status asc
		return client1->GetDownloadState() - client2->GetDownloadState();
	case 2: //transfered asc
		return client1->GetTransferedDown() - client2->GetTransferedDown();
	case 3: //speed asc
		return client1->GetDownloadDatarate() - client2->GetDownloadDatarate();
	case 4: //progress asc
		return client1->GetAvailablePartCount() - client2->GetAvailablePartCount();
	case 5:
		if( client1->GetClientSoft() == SO_UNKNOWN )
			return 1;
		if( client2->GetClientSoft() == SO_UNKNOWN )
			return -1;
		if( client1->GetClientSoft() == client2->GetClientSoft() )
			if( client1->IsEmuleClient() )
				return client2->GetMuleVersion() - client1->GetMuleVersion();
			else
				return client2->GetVersion() - client1->GetVersion();
		return client1->GetClientSoft() - client2->GetClientSoft();
	case 6: //qr asc
		if(client1->GetRemoteQueueRank() == 0 && client2->GetRemoteQueueRank() == 0 ){
			if(client1->IsRemoteQueueFull() == false) return sortMod;
			if(client2->GetRemoteQueueRank() == false) return -1*sortMod;
		}
		if(client1->GetRemoteQueueRank() == 0) return sortMod;
		if(client2->GetRemoteQueueRank() == 0) return -1*sortMod;

		return client1->GetRemoteQueueRank() - client2->GetRemoteQueueRank();
	case 7:
		return client1->GetDownloadState() - client2->GetDownloadState();
	default:
		return 0;
	}
}

void CDownloadListCtrl::CreateMenues() {
	if (m_PrioMenu) m_PrioMenu.DestroyMenu();
	if (m_FileMenu) 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_STRING, MP_JIGLE_FAKECHECK, IDS_JIGLE_FAKECHECK); // Added by Tarod [deltaHF]
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING,MP_LOADSOURCES, IDS_DL_LOADSOURCES); //Added by Mr.Fry
	m_FileMenu.AppendMenu(MF_STRING,MP_SAVESOURCES, IDS_DL_SAVESOURCES); // Added by Mr.Fry
	m_FileMenu.AppendMenu(MF_SEPARATOR);
	m_FileMenu.AppendMenu(MF_STRING, MP_DROPNONEEDEDSRCS, IDS_DROPNONEEDEDSRCS); // Added by Tarod
	m_FileMenu.AppendMenu(MF_STRING, MP_ALL_A4AF_TO_HERE, IDS_ALL_A4AF_TO_HERE); // Added by Tarod
	m_FileMenu.AppendMenu(MF_STRING, MP_ALL_A4AF_AUTO, IDS_ALL_A4AF_AUTO); // Added by Tarod
	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));
	SetMenu(&m_FileMenu);
}

CString CDownloadListCtrl::getTextList() {
	CString out="";
	CString temp,temp2;

	POSITION pos1, pos2;
	for( pos1 = listcontent.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		listcontent.GetNext(pos1);
		CtrlItem_Struct* delitem = listcontent.GetAt(pos2);
		if (delitem->type == 1){
			CPartFile* file = (CPartFile*)delitem->value;
			theApp.emuledlg->AddLogLine(false,file->GetFileName());

			temp2=file->GetFileName(); if (temp2.GetLength()>50) temp2=temp2.Mid(0,50);
			temp.Format("\n%s\t [%.1f%%] %i/%i - %s",
				temp2,file->GetPercentCompleted(),file->GetTransferingSrcCount(),file->GetSourceCount(), file->getPartfileStatus());
			out=out+temp;
		}
	}
	theApp.emuledlg->AddLogLine(false,out);
	return out;
}

// Added by Tarod -download all A4AF sources in this file now
//
// [In]
//   file: Part file that is going to be processed
void CDownloadListCtrl::DownloadAllA4AF(CPartFile* file)
{
	POSITION pos1, pos2;
	for (pos1 = listcontent.GetHeadPosition(); (pos2 = pos1) != NULL;) {
		listcontent.GetNext(pos1);
		CtrlItem_Struct* item = (CtrlItem_Struct*)listcontent.GetAt(pos2);
		if (item != NULL && item->type == 3 
			&& memcmp(((CPartFile*)item->owner)->GetFileHash(), file->GetFileHash(), 16) == 0) {

			CUpDownClient* cur_src = (CUpDownClient*)item->value;
			if (cur_src != NULL) cur_src->HandoverToAnotherFile(file);
		}
	}
}
