// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-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.

#ifdef PRECOMP
#	include "xmule-headers.h"
#else
#	include "DownloadListCtrl.h"

#	include "ChatWnd.h"
#	include "ClientDetailDialog.h"
#	include "CommentDialogLst.h"
#	include "config.h"
#	include "DownloadQueue.h"
#	include "FileDetailDialog.h"
#	include "FriendList.h"
#	include "muuli_wdr.h"
#	include "opcodes.h"
#	include "otherfunctions.h"
#	include "PartFile.h"
#include "Preferences.h"                    // CPreferences::List Settings
#	include "TransferWnd.h"
#	include "updownclient.h"
#	include "wintypes.h"
#	include "xmule.h"
#	include "xmuleDlg.h"
#endif

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <arpa/inet.h>
#include <cmath>                            // std::min
#include <iostream>
#include <wx/msgdlg.h>                      // wxMessageBox
#include <wx/textdlg.h>                     // wxTextEntryDialog

using std::cout;
using std::endl;
using std::min;

#define SYSCOLOR(x) (wxSystemSettings::GetColour(x))

// bitmaps
#include "pixmaps/client0.ICO.xpm"
#include "pixmaps/client1.ICO.xpm"
#include "pixmaps/client2.ICO.xpm"
#include "pixmaps/client3.ICO.xpm"
#include "pixmaps/client4.ICO.xpm"
#include "pixmaps/compatible.ICO.xpm"
#include "pixmaps/Friend.ico.xpm"
#include "pixmaps/neutral.ico.xpm"
#include "pixmaps/mldonk.ico.xpm"
#include "pixmaps/Rating.ico.xpm"
#include "pixmaps/Rating_bad.ico.xpm"
#include "pixmaps/eDonkeyHybrid.ico.xpm"

// lagloose
#include <wx/font.h>
// end lagloose
#include <wx/dcmemory.h>

//#define DLC_DT_TEXT (DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_END_ELLIPSIS)

#define DLC_BARUPDATE 512
#define strcmpi strcasecmp

// hmm some parts here might be overkill ;) ...
// oh well at least I can do/draw/paint everything I want in this window now ;)
IMPLEMENT_DYNAMIC_CLASS(CDownloadListCtrl, CMuleListCtrl)

BEGIN_EVENT_TABLE(CDownloadListCtrl, CMuleListCtrl)
    EVT_LIST_COL_END_DRAG(ID_DLOADLIST, CDownloadListCtrl::OnColResize)
    EVT_LIST_COL_CLICK(ID_DLOADLIST, CDownloadListCtrl::OnColumnClick)
    EVT_LIST_ITEM_ACTIVATED(ID_DLOADLIST, CDownloadListCtrl::OnLvnItemActivate)
    EVT_LIST_ITEM_RIGHT_CLICK(ID_DLOADLIST, CDownloadListCtrl::OnNMRclick)
    // begin lagloose
    EVT_KEY_UP(CDownloadListCtrl::OnKeyUp)
    EVT_KEY_DOWN(CDownloadListCtrl::OnKeyDown)
    // end lagloose
    EVT_MENU(MP_CANCEL, CDownloadListCtrl::OnMCancel)
END_EVENT_TABLE()
// not like this
 /*
 EVT_MENU(MP_PRIOHIGH,CDownloadListCtrl::OnPriHigh)
 EVT_MENU(MP_PRIOLOW,CDownloadListCtrl::OnPriLow)
 EVT_MENU(MP_PRIONORMAL,CDownloadListCtrl::OnPriNormal)
 
    */

void preloadImages(wxImageList *imgs)
{
    imgs->Add(wxBitmap(client0_ICO));
    imgs->Add(wxBitmap(client1_ICO));
    imgs->Add(wxBitmap(client2_ICO));
    imgs->Add(wxBitmap(client3_ICO));
    imgs->Add(wxBitmap(client4_ICO));
    imgs->Add(wxBitmap(compatible_ICO));
    imgs->Add(wxBitmap(Friend_ico));
    imgs->Add(wxBitmap(neutral_ico));
    imgs->Add(wxBitmap(mldonk_ico));
    imgs->Add(wxBitmap(Rating_ico));
    imgs->Add(wxBitmap(Rating_bad_ico));
    imgs->Add(wxBitmap(eDonkeyHybrid_ico));
}

namespace
{
    enum ItemType_t
    {
        FILE_TYPE=1, AVAILABLE_SOURCE=2, UNAVAILABLE_SOURCE=3
    };
}

struct CtrlItem_Struct
{
    ItemType_t       type;
    CPartFile*       owner;
    void*            value;
    CtrlItem_Struct* parent;
    DWORD            dwUpdated;
    wxBitmap*        status;

    ~CtrlItem_Struct()
    {
        delete status;
    }
};

//IMPLEMENT_DYNAMIC(CDownloadListCtrl, CListBox)
CDownloadListCtrl::CDownloadListCtrl(): m_ImageList(170, 16)
{
    //m_ImageList=new wxImageList(colWidth,16);
    SetImageList( &m_ImageList, wxIMAGE_LIST_SMALL);
    preloadImages( &m_ImageList);
}

CDownloadListCtrl::CDownloadListCtrl(wxWindow *&parent, int id, const wxPoint &pos, wxSize siz, int flags): CMuleListCtrl(parent, id, pos, siz, flags | wxLC_OWNERDRAW), m_ImageList(170, 16)
{
    //m_ImageList=new wxImageList(colWidth,16);
    m_ClientMenu = NULL;
    m_PrioMenu = NULL;
    m_FileMenu = NULL;
    wxColour col = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
    wxColour newcol = wxColour(G_BLEND(col.Red(), 125), G_BLEND(col.Green(), 125), G_BLEND(col.Blue(), 125));
    m_hilightBrush = new wxBrush(newcol, wxSOLID);
    col = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW);
    newcol = wxColour(G_BLEND(col.Red(), 125), G_BLEND(col.Green(), 125), G_BLEND(col.Blue(), 125));
    m_hilightUnfocusBrush = new wxBrush(newcol, wxSOLID);
    SetImageList( &m_ImageList, wxIMAGE_LIST_SMALL);
    preloadImages( &m_ImageList);
    Init();
}

void CDownloadListCtrl::InitSort()
{
 /* Only load the settings when doing startup time sorting. All other sortings
 must manage on their own.
    */
    if (!theApp.xmuledlg->IsRunning())
    {
        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: 100));
}

CDownloadListCtrl::~CDownloadListCtrl()
{
    while (m_ListItems.empty() == false)
    {
        // second = CtrlItem_Struct*:
        delete m_ListItems.begin()->second;
        m_ListItems.erase(m_ListItems.begin());
    }
    delete m_hilightBrush;
    delete m_hilightUnfocusBrush;
}

void CDownloadListCtrl::HideSources(CPartFile *toCollapse, bool isShift, bool isCtrl, bool isAlt)
{
    int pre, post;
    pre = post = 0;
    Freeze();
    for (int i = 0 ; i < GetItemCount() ; i++)
    {
        CtrlItem_Struct *item = (CtrlItem_Struct *) this->GetItemData(i);
        if (item->owner == toCollapse)
        {
            pre++;
            CUpDownClient *cl = (CUpDownClient *) item->value;
            if ((isShift || isCtrl || isAlt) && (cl->GetDownloadFile() == toCollapse))
            {
                uint8_t ds = cl->GetDownloadState();
                if ((isShift &&ds == DS_DOWNLOADING) || (isCtrl &&cl->GetRemoteQueueRank() > 0) || (isAlt &&ds != DS_NONEEDEDPARTS))
                {
                    continue;
                }
            }
            item->dwUpdated = 0;
            //item->status.DeleteObject();
            if (item->status)
            {
                delete item->status;
            }
            // clear it!!!!:
            item->status = NULL;
            DeleteItem(i--);
            post++;
        }
    }
    toCollapse->m_bShowOnlyDownloading = isShift;
    if (pre - post == 0)
    {
        toCollapse->srcarevisible = false;
    }
    Thaw();
}

void CDownloadListCtrl::collectSelections(std::list<CPartFile *>* selections)
{
    long item = -1;
    for ( ; ;)
    {
        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

        if (item == -1)
        {
            break;
        }

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

        if (content->type == 1)
        {
            selections->push_back(reinterpret_cast<CPartFile *>(content->value));
        }
    }
}

void CDownloadListCtrl::setPri(int newpri)
{
    std::list<CPartFile *> selections;
    collectSelections(&selections);
    long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

    if (item == -1)
    {
        return;
    }

    CtrlItem_Struct* content = reinterpret_cast<CtrlItem_Struct *>(this->GetItemData(item));
    unsigned int count = this->GetSelectedItemCount();

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

        if (count > 1)
        {
            while (!selections.empty())
            {
                selections.front()->SetDownPriority(newpri);
                selections.pop_front();
            }

            return;
        }

        file->SetDownPriority(newpri);
    }
}

void CDownloadListCtrl::OnMCancel(wxCommandEvent &evt)
{
    long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
    CtrlItem_Struct* content = reinterpret_cast<CtrlItem_Struct *>(this->GetItemData(item));
    unsigned int count = this->GetSelectedItemCount();
cout << "Selected: " << count << endl;
    std::list<CPartFile *> selections;

    CPartFile* tmp_part;

    //for multiple selections
    if (count > 1)
    {
        collectSelections(&selections);
        Freeze();
        wxString fileList;

        std::list<CPartFile *>::iterator it;
        for (it = selections.begin(); it != selections.end(); ++it)
        {
            fileList += "\n";
            fileList += (*it)->GetFileName();
        }

        if (wxMessageBox((GetResString(IDS_Q_CANCELDL).GetData() + fileList), GetResString(IDS_CANCEL), wxICON_QUESTION | wxYES_NO) == wxYES)
        {
            while (!selections.empty())
            {
                tmp_part = selections.front();
                HideSources(tmp_part);

                switch (tmp_part->GetStatus())
                {
                    case PS_WAITINGFORHASH:
                    case PS_HASHING:
                    case PS_COMPLETING:
                    case PS_COMPLETE:
                        selections.pop_front();
                        break;
                    case PS_PAUSED:
                        tmp_part->DeleteFile();
                        selections.pop_front();
                        break;
                    default:
                        theApp.downloadqueue->StartNextFile();
                        tmp_part->DeleteFile();
                        selections.pop_front();
                }
            }
        }

        Thaw();

        return;
    }

    tmp_part = reinterpret_cast<CPartFile *>(content->value);

    // single selection
    switch (tmp_part->GetStatus())
    {
    case PS_WAITINGFORHASH:
    case PS_HASHING:
    case PS_COMPLETING:
    case PS_COMPLETE:
        break;
    case PS_PAUSED:
        if (wxMessageBox(GetResString(IDS_Q_CANCELDL2), GetResString(IDS_CANCEL), wxICON_QUESTION | wxYES_NO) == wxYES)
        {
            HideSources(tmp_part);
            tmp_part->DeleteFile();
        }
        break;
    default:
        if (wxMessageBox(GetResString(IDS_Q_CANCELDL2), GetResString(IDS_CANCEL), wxICON_QUESTION | wxYES_NO) == wxYES)
        {
            HideSources(tmp_part);
            tmp_part->DeleteFile();
            theApp.downloadqueue->StartNextFile();
        }
        break;
    }
}

void CDownloadListCtrl::OnPriLow(wxCommandEvent &evt)
{
    setPri(PR_LOW);
}

void CDownloadListCtrl::OnPriNormal(wxCommandEvent &evt)
{
    setPri(PR_NORMAL);
}

void CDownloadListCtrl::OnPriHigh(wxCommandEvent &evt)
{
    setPri(PR_HIGH);
}

// laziness strikes
#define MF_CHECKED TRUE
#define MF_UNCHECKED FALSE
#define MF_ENABLED TRUE
#define MF_GRAYED FALSE

void CDownloadListCtrl::OnNMRclick(wxListEvent &evt)
{
    // Check if clicked item is selected. If not, unselect all and select it.
    long item =-1;
    if (!GetItemState(evt.GetIndex(), wxLIST_STATE_SELECTED))
    {
        for ( ; ;)
        {
            item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
            if (item == -1)
            {
                break;
            }
            SetItemState(item, 0, wxLIST_STATE_SELECTED);
        }
        SetItemState(evt.GetIndex(), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
    }
    item =-1;
    for ( ; ;)
    {
        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
        if (item == -1)
        {
            break;
        }
        CtrlItem_Struct *content = (CtrlItem_Struct *) this->GetItemData(item);
        if (content->type == 1)
        {
            CPartFile *file = (CPartFile *) content->value;
            if (m_PrioMenu == NULL)
            {
                wxMenu *priomenu = new wxMenu();
                priomenu->AppendCheckItem(MP_PRIOLOW, GetResString(IDS_PRIOLOW));
                priomenu->AppendCheckItem(MP_PRIONORMAL, GetResString(IDS_PRIONORMAL));
                priomenu->AppendCheckItem(MP_PRIOHIGH, GetResString(IDS_PRIOHIGH));
                priomenu->AppendCheckItem(MP_PRIOAUTO, GetResString(IDS_PRIOAUTO));
                m_PrioMenu = priomenu;
                wxMenu *menu = new wxMenu(GetResString(IDS_DOWNLOADMENUTITLE));
                menu->Append(999989, GetResString(IDS_PRIORITY), priomenu);
                menu->Append(MP_CANCEL, GetResString(IDS_MAIN_BTN_CANCEL));
                menu->Append(MP_STOP, GetResString(IDS_DL_STOP));
                menu->Append(MP_PAUSE, GetResString(IDS_DL_PAUSE));
                menu->Append(MP_RESUME, GetResString(IDS_DL_RESUME));
                menu->AppendSeparator();
 /* Razor 1a - Modif by MikaelB
 Menu items for :
 - Drop No Needed Sources now
 - Drop Full Queue Sources now
 - Drop High Queue Rating Sources now
 - Clean Up Sources now ( drop NNS, FQS and HQRS )
 - Swap every A4AF to this file now
 - Swap every A4AF to this file ( AUTO )
    - Swap every A4AF to any other file now   */
                wxMenu *extendedmenu = new wxMenu();
                extendedmenu->Append(MP_SWAP_A4AF_TO_THIS, GetResString(IDS_SWAP_A4AF_TO_THIS));
                extendedmenu->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO, GetResString(IDS_SWAP_A4AF_TO_THIS_AUTO));
                extendedmenu->Append(MP_SWAP_A4AF_TO_ANY_OTHER, GetResString(IDS_SWAP_A4AF_TO_ANY_OTHER));
                extendedmenu->AppendSeparator();
                extendedmenu->Append(MP_DROP_NO_NEEDED_SOURCES, GetResString(IDS_DROP_NO_NEEDED_SOURCES));
                extendedmenu->Append(MP_DROP_FULL_QUEUE_SOURCES, GetResString(IDS_DROP_FULL_QUEUE_SOURCES));
                extendedmenu->Append(MP_DROP_HIGH_QUEUE_RATING_SOURCES, GetResString(IDS_DROP_HIGH_QUEUE_RATING_SOURCES));
                extendedmenu->Append(MP_CLEAN_UP_SOURCES, GetResString(IDS_CLEAN_UP_SOURCES));
                menu->Append(999989, GetResString(IDS_EXTENDED), extendedmenu);
    /* End Modif */
                menu->AppendSeparator();
                menu->Append(MP_OPEN, GetResString(IDS_DL_OPEN));
                menu->Append(MP_PREVIEW, GetResString(IDS_DL_PREVIEW));
                menu->Append(MP_METINFO, GetResString(IDS_DL_INFO));
                menu->Append(MP_VIEWFILECOMMENTS, GetResString(IDS_CMT_SHOWALL));
                menu->AppendSeparator();
                menu->Append(30001, "Add source to downloadfile");
                menu->Append(MP_CLEARCOMPLETED, GetResString(IDS_DL_CLEAR));
                menu->Append(MP_GETED2KLINK, GetResString(IDS_DL_LINK1));
                menu->Append(MP_GETHTMLED2KLINK, GetResString(IDS_DL_LINK2));
                m_FileMenu = menu;
            }
            else
            {
                // Assign category:
                m_FileMenu->Remove(432843);
            }
            wxMenu *cats = new wxMenu(GetResString(IDS_CAT));
            m_cat_count = theApp.glob_prefs->GetCatCount();
            if (m_cat_count > 1)
            {
                for (int i = 0 ; i < m_cat_count; i++)
                {
                    cats->Append(MP_ASSIGNCAT + i, (i == 0) ? GetResString(IDS_CAT_UNASSIGN): theApp.glob_prefs->GetCategory(i)->title);
                    this->Connect(MP_ASSIGNCAT + i, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)(wxCommandEventFunction) &CDownloadListCtrl::HandleCatClick);
                    cout << "At least this happened :-/ | id: " << MP_ASSIGNCAT + i << endl;
                }
            }
            m_FileMenu->Append(432843, GetResString(IDS_TOCAT), cats);
            if (m_cat_count == 1)
            {
                m_FileMenu->Enable(432843, MF_GRAYED);
            }
            else
            {
                m_FileMenu->Enable(432843, MF_ENABLED);
            }
            // then set state
            wxMenu *menu = m_FileMenu;
            menu->Enable(MP_PAUSE, ((file->GetStatus() != PS_PAUSED && file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_STOP, ((file->GetStatus() != PS_PAUSED && file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_RESUME, ((file->GetStatus() == PS_PAUSED) ? MF_ENABLED: MF_GRAYED));
            //<<--9/21/02:
            menu->Enable(MP_OPEN, ((file->GetStatus() == PS_COMPLETE) ? MF_ENABLED: MF_GRAYED));
            menu->SetLabel(MP_PREVIEW, wxString::Format(_("Preview [%s]"), file->GetPartMetFileName()));
            menu->Enable(MP_PREVIEW, ((file->PreviewAvailable()) ? MF_ENABLED: MF_GRAYED));
 /* Razor 1a - Modif by MikaelB
 Set menu items' state for :
 - Drop No Needed Sources now
 - Drop Full Queue Sources now
 - Drop High Queue Rating Sources now
 - Clean Up Sources now ( drop NNS, FQS and HQRS )
 - Swap every A4AF to this file now
 - Swap every A4AF to this file ( AUTO )
    - Swap every A4AF to any other file now   */
            menu->Enable(MP_DROP_NO_NEEDED_SOURCES, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_DROP_FULL_QUEUE_SOURCES, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_DROP_HIGH_QUEUE_RATING_SOURCES, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_CLEAN_UP_SOURCES, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Enable(MP_SWAP_A4AF_TO_THIS_AUTO, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
            menu->Check(MP_SWAP_A4AF_TO_THIS_AUTO, file->IsA4AFAuto() ? MF_CHECKED: MF_UNCHECKED);
            menu->Enable(MP_SWAP_A4AF_TO_ANY_OTHER, ((file->GetStatus() != PS_PAUSED &&file->GetStatus() != PS_ERROR) ? MF_ENABLED: MF_GRAYED));
    /* End modif */
            menu->Enable(MP_CANCEL, MF_ENABLED);
            wxMenu *priomenu = m_PrioMenu;
            priomenu->Check(MP_PRIOHIGH, (!file->IsAutoDownPriority() && (file->GetDownPriority() == PR_HIGH)) ? MF_CHECKED: MF_UNCHECKED);
            priomenu->Check(MP_PRIONORMAL, (!file->IsAutoDownPriority() && (file->GetDownPriority() == PR_NORMAL)) ? MF_CHECKED: MF_UNCHECKED);
            priomenu->Check(MP_PRIOLOW, (!file->IsAutoDownPriority() && (file->GetDownPriority() == PR_LOW)) ? MF_CHECKED: MF_UNCHECKED);
            priomenu->Check(MP_PRIOAUTO, file->IsAutoDownPriority() ? MF_CHECKED: MF_UNCHECKED);
            PopupMenu(m_FileMenu, evt.GetPoint());
        }
        else
        {
            if (m_ClientMenu == NULL)
            {
                wxMenu *menu = new wxMenu(GetResString(IDS_CLIENTS));
                menu->Append(MP_DETAIL, GetResString(IDS_SHOWDETAILS));
                menu->Append(MP_ADDFRIEND, GetResString(IDS_ADDFRIEND));
                menu->Append(MP_MESSAGE, GetResString(IDS_SEND_MSG));
                menu->Append(MP_SHOWLIST, GetResString(IDS_VIEWFILES));
                m_ClientMenu = menu;
            }
            PopupMenu(m_ClientMenu, evt.GetPoint());
        }
        // make sure that we terminate
        break;
    }
    if (item == -1)
    {
        // no selection.. actually this event won't get fired in this case so
        // do nothing..
    }
}

void CDownloadListCtrl::OnColResize(wxListEvent &evt)
{
    return;
}

void CDownloadListCtrl::OnDrawItem(int item, wxDC *dc, const wxRect &rect, const wxRect &rectHL, bool highlighted)
{
    /* Don't do any drawing if there's nobody to see it. */
    if (!theApp.xmuledlg->IsRunning() || (theApp.xmuledlg->GetActiveDialog() != 2))
    {
        return;
    }
    CtrlItem_Struct *content = (CtrlItem_Struct *) GetItemData(item);
    //if ((content->type == 1) && (lpDrawItemStruct->itemAction | ODA_SELECT) &&
    //    (lpDrawItemStruct->itemState & ODS_SELECTED)) {
    if ((content->type == 1) && (highlighted))
    {
        if (GetFocus())
        {
            dc->SetBackground( *m_hilightBrush);
            dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
        }
        else
        {
            dc->SetBackground( *m_hilightUnfocusBrush);
            dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
        }
    }
 /*else if (content->type != 1) {
 COLORREF crHighlight = m_crWindowText;
 COLORREF crWindow = m_crWindow;
 COLORREF crHalf = DLC_RGBBLEND(crHighlight, crWindow, 16);
 odc->SetBkColor(crHalf);
    } */
    else
    {
        //dc->SetBkColor(GetBkColor());
        dc->SetBackground( * (wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
        dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    }
    // If we have category, override textforeground with what category tells us.
    CPartFile *file = (CPartFile *) content->value;
    if (file->GetCategory() > 0)
    {
        dc->SetTextForeground(theApp.glob_prefs->GetCatColor(file->GetCategory()));
    }
    // we must fill the background
    //if(content->type==1) {
    wxPen mypen;
    if (content->type == FILE_TYPE && highlighted)
    {
        // set pen so that we'll get nice border
        wxColour old = GetFocus() ? m_hilightBrush->GetColour(): m_hilightUnfocusBrush->GetColour();
        wxColour newcol = wxColour(((int) old.Red() *65) / 100, ((int) old.Green() *65) / 100, ((int) old.Blue() *65) / 100);
        mypen = wxPen(newcol, 1, wxSOLID);
        dc->SetPen(mypen);
    }
    else
    {
        if (content->type != FILE_TYPE &&highlighted)
        {
            wxColour old = m_hilightBrush->GetColour();
            wxColour newcol = wxColour(((int) old.Red() *65) / 100, ((int) old.Green() *65) / 100, ((int) old.Blue() *65) / 100);
            mypen = wxPen(newcol, 1, wxSOLID);
            dc->SetPen(mypen);
        }
        else
        {
            dc->SetPen( *wxTRANSPARENT_PEN);
        }
    }
    dc->SetBrush(dc->GetBackground());
    // lagloose: removes flicker
    // Madcat: Breaking rectangle lines :( Figure out a way to keep rectangles, and become my
    // personal hero :)
    // if (content->type != FILE_TYPE) {
    dc->DrawRectangle(rectHL);
    // }
    // lagloose
    dc->SetPen( *wxTRANSPARENT_PEN);
    // }
    RECT cur_rec;
    int tree_start = 0, tree_end = 0;
    bool notLast = item + 1 != GetItemCount();
    bool notFirst = item != 0;
    cur_rec.left = rect.x;
    cur_rec.top = rect.y;
    cur_rec.right = rect.x + rect.width;
    cur_rec.bottom = rect.y + rect.height;
    //dc->GetTextExtent(_T(" "), 1 ).cx*2;:
    int iOffset = 4;
    //CHeaderCtrl *pHeaderCtrl = GetHeaderCtrl();
    //pHeaderCtrl->GetItemCount();:
    int iCount = GetColumnCount();
    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++)
        {
            //pHeaderCtrl->OrderToIndex(iCurrent);:
            int iColumn = iCurrent;
            //int cx = GetColumnWidth(iColumn);
            wxListItem listitem;
            GetColumn(iColumn, listitem);
            int cx = listitem.GetWidth();
            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;
                //printf("**** Fileitem:");
                DrawFileItem(dc, 5, &cur_rec, content);
                cur_rec.left = iNextLeft;
            }
            else
            {
                cur_rec.right += cx;
                // will ensure that text is about in the middle ;):
                cur_rec.top += 3;
                DrawFileItem(dc, iColumn, &cur_rec, content);
                cur_rec.top -= 3;
                cur_rec.left += cx;
            }
        }
    }
    else if(content->type == 3 || content->type == 2)
    {
        for (int iCurrent = 0 ; iCurrent < iCount ; iCurrent++)
        {
            //pHeaderCtrl->OrderToIndex(iCurrent);:
            int iColumn = iCurrent;
            //int cx = GetColumnWidth(iColumn);
            wxListItem listitem;
            GetColumn(iColumn, listitem);
            int cx = listitem.GetWidth();
            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)
                {
                    //pHeaderCtrl->OrderToIndex(iCurrent + 1);:
                    int iNext = iCurrent + 1;
    if (iNext == 1 /*|| iNext == 5 || iNext == 7 || iNext == 8 */
                    )
                    {
                        wxListItem newlistitem;
                        GetColumn(iNext, newlistitem);
                        //GetColumnWidth(iNext);:
                        cx += newlistitem.GetWidth();
                    }
                    else
                    {
                        break;
                    }
                    iCurrent++;
                }
                cur_rec.right += cx;
                // will ensure that text is about in the middle ;):
                cur_rec.top += 3;
                DrawSourceItem(dc, iColumn, &cur_rec, content);
                cur_rec.top -= 3;
                cur_rec.left += cx;
            }
        }
    }
    // piirrusteluja puuttuu (selection rectanglet)
    //draw tree last so it draws over selected and focus (looks better)
    if (tree_start < tree_end)
    {
        //set new bounds
        RECT tree_rect;
        //lpDrawItemStruct->rcItem.top;:
        tree_rect.top = rect.y;
        //lpDrawItemStruct->rcItem.bottom;:
        tree_rect.bottom = rect.y + rect.height;
        tree_rect.left = tree_start;
        tree_rect.right = tree_end;
        // TODO:varmaanki clipper?
        //dc->SetBoundsRect(&tree_rect, DCB_DISABLE);
        //gather some information
        bool hasNext = notLast && ((CtrlItem_Struct *) this->GetItemData(item + 1))->type != 1;
        bool isOpenRoot = hasNext &&content->type == 1;
        bool isChild = content->type != 1;
        //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
        wxPen pn;
        //pn.CreatePen(PS_SOLID, 1, dc->GetTextColor());
        pn = * (wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID));
        //oldpn = dc->SelectObject(&pn);
        dc->SetPen(pn);
        if (isChild)
        {
            //draw the line to the status bar
            //dc->MoveTo(tree_end, middle);
            //dc->LineTo(tree_start + 3, middle);
            dc->DrawLine(tree_end, middle, tree_start + 3, middle);
            //draw the line to the child node
            if (hasNext)
            {
                //dc->MoveTo(treeCenter, middle);
                //dc->LineTo(treeCenter, cur_rec.bottom + 1);
                dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.bottom + 1);
            }
        }
        else if(isOpenRoot)
        {
            //draw circle
            RECT circle_rec;
            //COLORREF crBk = dc->GetBkColor();
            wxColour crBk = dc->GetBackground() .GetColour();
            circle_rec.top = middle - 2;
            circle_rec.bottom = middle + 3;
            circle_rec.left = treeCenter - 2;
            circle_rec.right = treeCenter + 3;
            dc->DrawLine(circle_rec.left, circle_rec.top, circle_rec.right, circle_rec.top);
            dc->DrawLine(circle_rec.right, circle_rec.top, circle_rec.right, circle_rec.bottom);
            dc->DrawLine(circle_rec.right, circle_rec.bottom, circle_rec.left, circle_rec.bottom);
            dc->DrawLine(circle_rec.left, circle_rec.bottom, circle_rec.left, circle_rec.top);
            //dc->FrameRect(&circle_rec, &CBrush(dc->GetTextColor()));
            //dc->SetBrush(*(wxTheBrushList->FindOrCreateBrush(dc->GetTextForeground(),wxSOLID)));
            wxPen oldpen = dc->GetPen();
            dc->SetPen( * (wxThePenList->FindOrCreatePen(crBk, 1, wxSOLID)));
            dc->DrawPoint(circle_rec.left, circle_rec.top);
            dc->DrawPoint(circle_rec.right, circle_rec.top);
            dc->DrawPoint(circle_rec.left, circle_rec.bottom);
            dc->DrawPoint(circle_rec.right, circle_rec.bottom);
            dc->SetPen(oldpen);
#if 0
            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);
#endif
            //draw the line to the child node
            if (hasNext)
            {
                //dc->MoveTo(treeCenter, middle + 3);
                //dc->LineTo(treeCenter, cur_rec.bottom + 1);
                dc->DrawLine(treeCenter, middle + 3, 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);
            dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.top - 1);
        }
        //put the old pen back
        //dc->SelectObject(oldpn);
        //pn.DeleteObject();
    }
}

void CDownloadListCtrl::Init()
{
#if 0
    //dummy list:
    CImageList ilDummyImageList;
    ilDummyImageList.Create(1, 17, ILC_COLOR, 1, 1);
    SetImageList( &ilDummyImageList, LVSIL_SMALL);
    SetStyle();
    SetColors();
    ModifyStyle(LVS_SINGLESEL, 0);
#endif
#define LVCFMT_LEFT wxLIST_FORMAT_LEFT
    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);
    wxString lsctitle = GetResString(IDS_LASTSEENCOMPL);
    lsctitle.Replace(":", "");
    InsertColumn(10, lsctitle, LVCFMT_LEFT, 220);
    lsctitle = GetResString(IDS_FD_LASTCHANGE);
    lsctitle.Replace(":", "");
    InsertColumn(11, lsctitle, LVCFMT_LEFT, 220);
    curTab = 0;
#if 0
    m_ImageList.Create(15, 15, ILC_COLOR32 | 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));
    CreateMenues();
#endif
    // sort setting->InitSort
}

void CDownloadListCtrl::Localize()
{
}

void CDownloadListCtrl::AddFile(CPartFile *toadd)
{
    CtrlItem_Struct *newitem = new CtrlItem_Struct;
    uint16_t itemnr = GetItemCount();
    newitem->owner = NULL;
    newitem->type = FILE_TYPE;
    newitem->value = toadd;
    newitem->status = NULL;
    newitem->parent = NULL;
    newitem->dwUpdated = 0;
    //listcontent.Append(newitem);
    m_ListItems.insert(ListItemsPair(toadd, newitem));
    //InsertItem(LVIF_PARAM,itemnr,0,0,0,0,(LPARAM)newitem);
    if (CheckShowItemInGivenCat(toadd, curTab))
    {
        // rip something off from DrawFileItem()
        CPartFile *pf = (CPartFile *) newitem->value;
        uint32_t newid = InsertItem(itemnr, pf->GetFileName());
        SetItemData(newid, (long) newitem);
        wxListItem myitem;
        myitem.m_itemId = newid;
        myitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
        SetItem(myitem);
    }
    ShowFilesCount();
}

void CDownloadListCtrl::AddSource(CPartFile *owner, CUpDownClient *source, bool notavailable)
{
    CtrlItem_Struct *newitem = new CtrlItem_Struct;
    newitem->owner = owner;
    newitem->type = (notavailable) ? UNAVAILABLE_SOURCE: AVAILABLE_SOURCE;
    newitem->value = source;
    newitem->status = NULL;
    newitem->dwUpdated = 0;
    //listcontent.Append(newitem);
    // Update cross link to the owner
    ListItems::const_iterator ownerIt = m_ListItems.find(owner);
    //wxASSERT(ownerIt != m_ListItems.end());
    CtrlItem_Struct *ownerItem = ownerIt->second;
    //wxASSERT(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));
    if (!owner->srcarevisible)
    {
        return;
    }
    if (owner->m_bShowOnlyDownloading &&
    ((source->GetDownloadState() != DS_DOWNLOADING) || (source->GetDownloadFile() != owner)))
    {
        return;
    }
    // insert newitem to the display too!
    // find it
    int itemnr = FindItem(-1, (long) ownerItem);
    while (GetItemCount() > itemnr + 1 && ((CtrlItem_Struct *) GetItemData(itemnr + 1))->type != FILE_TYPE)
    {
        itemnr++;
    }
    int newid = InsertItem(itemnr + 1, "This text is not visible");
    SetItemData(newid, (long) newitem);
    // background.. this should be in a function
    wxListItem newitemL;
    newitemL.m_itemId = newid;
    newitemL.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
    SetItem(newitemL);
}

void CDownloadListCtrl::RemoveSource(CUpDownClient *source, CPartFile *owner)
{
    // 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
            ListItems::iterator tmp = it;
            it++;
    /*it = */
            m_ListItems.erase(tmp++);
            //LVFINDINFO find;
            //find.flags = LVFI_PARAM;
            //find.lParam = (LPARAM)delitem;
            //int16_t result = FindItem(&find);
            int16_t result = FindItem(-1, (long) delItem);
            if (result != (-1))
            {
                DeleteItem(result);
            }
            delete delItem;
        }
        else
        {
            ++it;
        }
    }
}

// argh. wxWin lists. remove these!!!
void CDownloadListCtrl::RemoveFile(CPartFile *toremove)
{
    // Retrieve all entries matching the File or linked to the file
    // Remark: The 'asked another files' clients must be removed from here
    //wxASSERT(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
            ListItems::iterator tmp = it;
            it++;
    /*it = */
            m_ListItems.erase(tmp++);
            // Remove it from the CListCtrl
            //LVFINDINFO find;
            //find.flags = LVFI_PARAM;
            //find.lParam = (LPARAM)delItem;
            int16_t result = FindItem(-1, (long) delItem);
            if (result != (-1))
            {
                DeleteItem(result);
            }
            // finally it could be delete
            delete delItem;
        }
        else
        {
            ++it;
        }
    }
    ShowFilesCount();
}

void CDownloadListCtrl::UpdateItem(void *toupdate)
{
    // 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;
        int16_t result = FindItem(-1, (long) updateItem);
        if (result != (-1))
        {
            updateItem->dwUpdated = 0;
            long first = 0, last = 0;
            GetVisibleLines( &first, &last);
            if (result >= first &&result <= last)
            {
                RefreshItems(result, result);
            }
        }
    }
}

void CDownloadListCtrl::DrawFileItem(wxDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem)
{
    if (lpRect->left < lpRect->right)
    {
        // force clipper (clip 2 px more than the rectangle from the right side)
        wxDCClipper clipper( *dc, lpRect->left, lpRect->top, lpRect->right - lpRect->left - 2, lpRect->bottom - lpRect->top);
        wxString buffer;
        CPartFile *lpPartFile = (CPartFile *) lpCtrlItem->value;
        switch (nColumn)
        {
        case 0:
            {
                // file name
                if (lpPartFile->HasComment() || lpPartFile->HasRating())
                {
                    int image = 9;
                    if (lpPartFile->HasRating())
                    {
                        if (lpPartFile->HasBadRating())
                        {
                            image = 10;
                        }
                    }
                    // it's already centered by OnDrawItem() ...
                    POINT point =
                    {
    lpRect->left - 4, lpRect->top /*+3 */
                                        };
                    //m_ImageList.Draw(dc, image, point, ILD_NORMAL);
                    m_ImageList.Draw(image, *dc, point.x, point.y - 1, wxIMAGELIST_DRAW_TRANSPARENT);
                    lpRect->left += 9;
                    dc->DrawText(lpPartFile->GetFileName(), lpRect->left, lpRect->top);
                    lpRect->left -= 9;
                }
                else
                {
                    dc->DrawText(lpPartFile->GetFileName(), lpRect->left, lpRect->top);
                }
            }
            break;
            // size:
        case 1:
            buffer.Printf("%s", CastItoXBytes(lpPartFile->GetFileSize()) .GetData());
            //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
            dc->DrawText(buffer, lpRect->left, lpRect->top);
            break;
            // transfered:
        case 2:
            buffer.Printf("%s", CastItoXBytes(lpPartFile->GetTransfered()) .GetData());
            //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
            dc->DrawText(buffer, lpRect->left, lpRect->top);
            break;
            // transfered complete:
        case 3:
            buffer.Printf("%s", CastItoXBytes(lpPartFile->GetCompletedSize()) .GetData());
            dc->DrawText(buffer, lpRect->left, lpRect->top);
            break;
            // speed:
        case 4:
            if (lpPartFile->GetTransferingSrcCount() == 0)
            {
                buffer = "";
            }
            else
            {
                buffer.Printf("%.1f %s", lpPartFile->GetDatarate() / 1024.0f, GetResString(IDS_KBYTESEC) .GetData());
            }
            //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
            dc->DrawText(buffer, lpRect->left, lpRect->top);
            break;
#ifndef DISABLE_PROGRESS
            // progress:
        case 5:
            {
                lpRect->bottom--;
                lpRect->top++;
                int iWidth = lpRect->right - lpRect->left;
                int iHeight = lpRect->bottom - lpRect->top;
                // DO NOT DRAW IT ALL THE TIME
                DWORD dwTicks = GetTickCount();
                wxMemoryDC cdcStatus;
                if (lpCtrlItem->dwUpdated + DLC_BARUPDATE < dwTicks || !lpCtrlItem->status || iWidth != lpCtrlItem->status->GetWidth() || !lpCtrlItem->dwUpdated)
                {
                    if (lpCtrlItem->status == NULL)
                    {
                        lpCtrlItem->status = new wxBitmap(iWidth, iHeight);
                    }
                    else
                    {
                        //delete lpCtrlItem->status;
                        //lpCtrlItem->status=NULL;
                        //lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
                        //SetWidth(iWidth);:
                        lpCtrlItem->status->Create(iWidth, iHeight);
                    }
                    //lpCtrlItem->status->Create(iWidth,iHeight);
                    cdcStatus.SelectObject( * (lpCtrlItem->status));
                    lpPartFile->DrawStatusBar(&cdcStatus, wxRect(0, 0, iWidth, iHeight), (theApp.dynprefs->Get<long>("progress-bar-style") == 0));
                    lpCtrlItem->dwUpdated = dwTicks + (rand() % 128);
                }
                else
                {
                    cdcStatus.SelectObject( * (lpCtrlItem->status));
                }
                dc->Blit(lpRect->left, lpRect->top, iWidth, iHeight, &cdcStatus, 0, 0);
                cdcStatus.SelectObject(wxNullBitmap);
                lpRect->bottom++;
                lpRect->top--;
            }
            break;
#endif
            // sources:
        case 6:
            {
                wxString buffer2;
                uint16_t ncsc = lpPartFile->GetNotCurrentSourcesCount();
                if (ncsc > 0)
                {
                    buffer2.Printf("%i/", lpPartFile->GetSourceCount() - ncsc);
                }
                else
                {
                    buffer2 = "";
                }
                buffer.Printf("%s%i (%i)", buffer2.GetData(), lpPartFile->GetSourceCount(), lpPartFile->GetTransferingSrcCount());
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
            // prio:
        case 7:
            switch (lpPartFile->GetDownPriority())
            {
            case PR_LOW:
                if (lpPartFile->IsAutoDownPriority())
                {
                    dc->DrawText(GetResString(IDS_PRIOAUTOLOW), lpRect->left, lpRect->top);
                }
                else
                {
                    dc->DrawText(GetResString(IDS_PRIOLOW), lpRect->left, lpRect->top);
                }

                break;
            case PR_NORMAL:
                if (lpPartFile->IsAutoDownPriority())
                {
                    dc->DrawText(GetResString(IDS_PRIOAUTONORMAL), lpRect->left, lpRect->top);
                }
                else
                {
                    dc->DrawText(GetResString(IDS_PRIONORMAL), lpRect->left, lpRect->top);
                }

                break;
            case PR_HIGH:
                if (lpPartFile->IsAutoDownPriority())
                {
                    dc->DrawText(GetResString(IDS_PRIOAUTOHIGH), lpRect->left, lpRect->top);
                }
                else
                {
                    dc->DrawText(GetResString(IDS_PRIOHIGH), lpRect->left, lpRect->top);
                }

                break;
            }

            break;
            // <<--9/21/02:
        case 8:
            buffer.Printf("%s", lpPartFile->getPartfileStatus() .GetData());
            //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
            dc->DrawText(buffer, lpRect->left, lpRect->top);
            break;
            // remaining time & size:
        case 9:
            {
                //char bufferSize[50];
                //char bufferTime[50];
                //size
                uint32_t remains;
                //<<-- 09/27/2002, CML:
                remains = lpPartFile->GetFileSize() - lpPartFile->GetCompletedSize();
                if (remains < 0)
                {
                    remains = 0;
                }
                // time
                int32_t restTime = lpPartFile->getTimeRemaining();
                buffer.Printf("%s (%s)", CastSecondsToHM(restTime) .GetData(), CastItoXBytes(remains) .GetData());
                if (lpPartFile->GetStatus() == PS_COMPLETING || lpPartFile->GetStatus() == PS_COMPLETE)
                {
                    buffer = "";
                }
                //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
            // last seen complete:
        case 10:
            {
                if (lpPartFile->lastseencomplete == 0)
                {
                    buffer = GetResString(IDS_UNKNOWN);
                }
                else
                {
                    char tmpstr[512];
                    strftime(tmpstr, sizeof(tmpstr), "%A, %x, %X", localtime( &lpPartFile->lastseencomplete));
                    //lpPartFile->lastseencomplete.Printf( "%A, %x, %X");:
                    buffer = tmpstr;
                }
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
            // last receive:
        case 11:
            if (!IsColumnHidden(11))
            {
                if (lpPartFile->GetLastChangeDatetime() != 0)
                {
                    char tmpstr[512];
                    time_t kello = lpPartFile->GetLastChangeDatetime();
                    strftime(tmpstr, sizeof(tmpstr), "%A, %x, %X", localtime( &kello));
                    //lpPartFile->GetLastChangeDatetime().Printf( theApp.glob_prefs->GetDateTimeFormat());:
                    buffer = tmpstr;
                }
                else
                buffer = "";
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
        }
    }
}

#define ILD_NORMAL wxIMAGELIST_DRAW_TRANSPARENT

void CDownloadListCtrl::DrawSourceItem(wxDC *dc, int nColumn, LPRECT lpRect, CtrlItem_Struct *lpCtrlItem)
{
    if (lpRect->left < lpRect->right)
    {
        // force clipper (clip 2 px more than the rectangle from the right side)
        wxDCClipper clipper( *dc, lpRect->left, lpRect->top, lpRect->right - lpRect->left - 2, lpRect->bottom - lpRect->top);
        wxString buffer;
        CUpDownClient *lpUpDownClient = (CUpDownClient *) lpCtrlItem->value;
        switch (nColumn)
        {
            // icon, name, status:
        case 0:
            {
                RECT cur_rec;
                memcpy( &cur_rec, lpRect, sizeof(RECT));
                // +3 is added by OnDrawItem()... so take it off
                POINT point =
                {
    cur_rec.left, cur_rec.top /*+1 */
                    - 2
                                };
                if (lpCtrlItem->type == 2)
                {
                    switch (lpUpDownClient->GetDownloadState())
                    {
                    case DS_CONNECTING:
                        m_ImageList.Draw(2, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_CONNECTED:
                        m_ImageList.Draw(2, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_WAITCALLBACK:
                        m_ImageList.Draw(2, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_ONQUEUE:
                        if (lpUpDownClient->IsRemoteQueueFull())
                        {
                            m_ImageList.Draw(3, *dc, point.x, point.y, ILD_NORMAL);
                        }
                        else
                        {
                            m_ImageList.Draw(1, *dc, point.x, point.y, ILD_NORMAL);
                        }
                        break;
                    case DS_DOWNLOADING:
                        m_ImageList.Draw(0, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_REQHASHSET:
                        m_ImageList.Draw(0, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_NONEEDEDPARTS:
                        m_ImageList.Draw(3, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_LOWTOLOWIP:
                        m_ImageList.Draw(3, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    case DS_TOOMANYCONNS:
                        m_ImageList.Draw(2, *dc, point.x, point.y, ILD_NORMAL);
                        break;
                    default:
                        m_ImageList.Draw(4, *dc, point.x, point.y, ILD_NORMAL);
                    }
                }
                else
                {
                    m_ImageList.Draw(3, *dc, point.x, point.y, ILD_NORMAL);
                }
                cur_rec.left += 20;
                if (lpUpDownClient->IsFriend())
                {
                    POINT point2 =
                    {
                        cur_rec.left, cur_rec.top + 1
                                        };
                    m_ImageList.Draw(6, *dc, point2.x, point.y, ILD_NORMAL);
                    cur_rec.left += 20;
                }
                else if(lpUpDownClient->ExtProtocolAvailable())
                {
                    POINT point2 =
                    {
                        cur_rec.left, cur_rec.top + 1
                                        };
                    m_ImageList.Draw(5, *dc, point2.x, point.y, ILD_NORMAL);
                    cur_rec.left += 20;
                }
                else
                {
                    POINT point2 =
                    {
                        cur_rec.left, cur_rec.top + 1
                                        };
                    if (lpUpDownClient->GetClientSoft() == SO_MLDONKEY || lpUpDownClient->GetClientSoft() == SO_NEW_MLDONKEY)
                    {
                        m_ImageList.Draw(8, *dc, point2.x, point.y, ILD_NORMAL);
                    }
                    else if(lpUpDownClient->GetClientSoft() == SO_EDONKEYHYBRID)
                    {
                        m_ImageList.Draw(11, *dc, point2.x, point.y, ILD_NORMAL);
                    }
                    else
                    {
                        m_ImageList.Draw(7, *dc, point2.x, point.y, ILD_NORMAL);
                    }
                    cur_rec.left += 20;
                }
                if (!lpUpDownClient->GetUserName())
                {
                    buffer = "?";
                }
                else
                {
                    buffer.Printf("%s", lpUpDownClient->GetUserName());
                }
                dc->DrawText(buffer, cur_rec.left, cur_rec.top);
            }
            break;
            // size:
        case 1:
            break;
        case 2:
            if (!IsColumnHidden(3))
            {
                dc->DrawText("", lpRect->left, lpRect->top);
                break;
            }
            // completed:
        case 3:
            if (lpCtrlItem->type == 2 &&lpUpDownClient->GetTransferedDown())
            {
                buffer.Printf("%s", CastItoXBytes(lpUpDownClient->GetTransferedDown()) .GetData());
                //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
            // speed:
        case 4:
            if (lpCtrlItem->type == 2 &&lpUpDownClient->GetDownloadDatarate())
            {
                if (lpUpDownClient->GetDownloadDatarate() == 0)
                {
                    buffer = "";
                }
                else
                {
                    buffer.Printf("%.1f %s", lpUpDownClient->GetDownloadDatarate() / 1024.0f, GetResString(IDS_KBYTESEC) .GetData());
                }
                //dc->DrawText(buffer,(int)strlen(buffer),lpRect, DLC_DT_TEXT);
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
#ifndef DISABLE_PROGRESS
            // file info:
        case 5:
            {
                lpRect->bottom--;
                lpRect->top++;
                int iWidth = lpRect->right - lpRect->left;
                int iHeight = lpRect->bottom - lpRect->top;
                DWORD dwTicks = GetTickCount();
                wxMemoryDC cdcStatus;
                if (lpCtrlItem->dwUpdated + (4 *DLC_BARUPDATE) < dwTicks || !lpCtrlItem->status || iWidth != lpCtrlItem->status->GetWidth() || !lpCtrlItem->dwUpdated)
                {
                    if (lpCtrlItem->status == NULL)
                    {
                        lpCtrlItem->status = new wxBitmap(iWidth, iHeight);
                    }
                    else
                    {
                        //delete lpCtrlItem->status;
                        //lpCtrlItem->status=NULL;
                        //lpCtrlItem->status=new wxBitmap(iWidth,iHeight);
                        lpCtrlItem->status->Create(iWidth, iHeight);
                    }
                    cdcStatus.SelectObject( * (lpCtrlItem->status));
                    lpUpDownClient->DrawStatusBar( &cdcStatus, wxRect(0, 0, iWidth, iHeight), (lpCtrlItem->type == 3), (theApp.dynprefs->Get<long>("progress-bar-style") == 0));
                    lpCtrlItem->dwUpdated = dwTicks + (rand() % 128);
                }
                else
                {
                    cdcStatus.SelectObject( * (lpCtrlItem->status));
                }
                dc->Blit(lpRect->left, lpRect->top, iWidth, iHeight, &cdcStatus, 0, 0);
                cdcStatus.SelectObject(wxNullBitmap);
                lpRect->top--;
                lpRect->bottom++;
            }
            break;
#endif
        case 6:
            dc->DrawText(lpUpDownClient->GetClientVerString(), lpRect->left, lpRect->top);
            break;

        case 7:
            if (lpUpDownClient->GetDownloadState() == DS_ONQUEUE)
            {
                if (lpUpDownClient->IsRemoteQueueFull())
                {
                    buffer.Printf("%s", GetResString(IDS_QUEUEFULL) .GetData());
                    dc->DrawText(buffer, lpRect->left, lpRect->top);
                }
                else
                {
                    if (lpUpDownClient->GetRemoteQueueRank())
                    {
                        buffer.Printf("QR: %u", lpUpDownClient->GetRemoteQueueRank());
                        dc->DrawText(buffer, lpRect->left, lpRect->top);
                    }
                    else
                    {
                        buffer = "";
                        dc->DrawText(buffer, lpRect->left, lpRect->top);
                    }
                }
            }
            else
            {
                if (lpCtrlItem->type == 2)
                {
#if defined(__DEBUG__)
                    switch (lpUpDownClient->GetDownloadState())
                    {
                    case DS_CONNECTED:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_CONNECTED");
                        break;
                    case DS_CONNECTING:
                        buffer.Printf("%ld %s %d %lu", lpUpDownClient->GetDownloadState(),
                        "DS_CONNECTING", lpUpDownClient->m_cFailed,
                        GetTickCount() - lpUpDownClient->GetLastStateChange());
                        break;
                    case DS_DOWNLOADING:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_DOWNLOADING");
                        break;
                    case DS_LOWTOLOWIP:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_LOWTOLOWIP");
                        break;
                    case DS_NONE:
                        if(theApp.serverconnect->IsConnected()) {
				buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_NONE*S");
			} else {
				buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_NONE--");
			}
                        break;
                    case DS_NONEEDEDPARTS:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_NONEEDEDPARTS");
                        break;
                    case DS_ONQUEUE:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_ONQEUE");
                        break;
                    case DS_REQHASHSET:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_REQHASHSET");
                        break;
                    case DS_TOOMANYCONNS:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_TOOMANYCONNS");
                        break;
                    case DS_WAITCALLBACK:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_WAITCALLBACK");
                        break;
                    default:
                        buffer.Printf("%ld %s", lpUpDownClient->GetDownloadState(), "DS_UNKNOWN");
                        break;
                    }
#endif
                }
                else
                {
                    buffer = "";
                }
                dc->DrawText(buffer, lpRect->left, lpRect->top);
            }
            break;
            // status:
        case 8:
            if (lpCtrlItem->type == 2)
            {
                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, lpRect->left, lpRect->top);
            break;
            // remaining time & size:
        case 9:
            break;
        }
    }
}

void CDownloadListCtrl::OnLvnItemActivate(wxListEvent &evt)
{
    //LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
    //pNMIA->iItem);:
    CtrlItem_Struct *content = (CtrlItem_Struct *) this->GetItemData(evt.GetIndex());
    bool added = false;
    if (content->type == FILE_TYPE)
    {
        CPartFile *partfile = (CPartFile *) content->value;
        if (!partfile->srcarevisible)
        {
            Freeze();
            for (ListItems::const_iterator it = m_ListItems.begin() ; it != m_ListItems.end() ; it++)
            {
                CtrlItem_Struct *cur_item = it->second;
                if (cur_item->owner == partfile)
                {
                    //InsertItem(LVIF_PARAM,pNMIA->iItem+1,0,0,0,0,(LPARAM)cur_item);
                    //added = true;
                    CUpDownClient *client = (CUpDownClient *) cur_item->value;
                    wxString textData;
                    wxString status;
                    // lagloose
                    partfile->m_bShowOnlyDownloading = isShift;
                    if (isShift)
                    {
                        uint8_t ds = client->GetDownloadState();
                        if ((client->GetDownloadFile() != partfile) || (ds != DS_DOWNLOADING))
                        {
                            continue;
                        }
                    }
                    // end lagloose
                    int newid = InsertItem(evt.GetIndex() + 1, textData);
                    added = true;
                    wxListItem newitem;
                    newitem.m_itemId = newid;
                    newitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
                    SetItem(newitem);
                    SetItemData(newid, (long) cur_item);
                }
                partfile->srcarevisible = added;
            }
            Thaw();
        }
        else
        {
            HideSources(partfile, false, false, false);
        }
    }
    // lagloose
    isShift = false;
    // end lagloose
    //*pResult = 0;
}

bool CDownloadListCtrl::ProcessEvent(wxEvent &evt)
{
    if ((evt.GetEventType() != wxEVT_COMMAND_MENU_SELECTED) || 
/*->*/  (evt.GetId() == MP_CANCEL) || 
/*->*/  ((evt.GetId() >= MP_ASSIGNCAT) && (evt.GetId() <= MP_ASSIGNCAT + m_cat_count)) || 
/*->*/  ((evt.GetId() >= MP_LISTCOL_1) && (evt.GetId() <= MP_LISTCOL_15)))
    {
        return CMuleListCtrl::ProcessEvent(evt);
    }

    wxCommandEvent &event = (wxCommandEvent &) evt;
    long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
    if (item != (-1))
    {
        CtrlItem_Struct *content = (CtrlItem_Struct *) this->GetItemData(item);
        UINT selectedCount = this->GetSelectedItemCount();
        CTypedPtrList < CPtrList, CPartFile *> selectedList;
        item =-1;
        for ( ; ;)
        {
            item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
            if (item == (-1))
            {
                break;
            }
            if (((CtrlItem_Struct *) this->GetItemData(item))->type == 1)
            {
                selectedList.AddTail((CPartFile *)((CtrlItem_Struct *) this->GetItemData(item))->value);
            }
        }
        if (content->type == 1)
        {
            CPartFile *file = (CPartFile *) content->value;
            switch (event.GetId())
            {
 /* Razor 1a - Modif by MikaelB
    Event for Drop No Needed Sources */
            case MP_DROP_NO_NEEDED_SOURCES:
                // While selected items aren't empty
                while (!selectedList.IsEmpty())
                {
                    // Remove No Needed sources from the current selected item
                    selectedList.GetHead()->RemoveNoNeededSources();
                    // Remove this item from the selected items list
                    selectedList.RemoveHead();
                }
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Drop Full Queue Sources */
            case MP_DROP_FULL_QUEUE_SOURCES:
                // While selected items aren't empty
                while (!selectedList.IsEmpty())
                {
                    // Remove Full Queue sources from the current selected item
                    selectedList.GetHead()->RemoveFullQueueSources();
                    // Remove this item from the selected items list
                    selectedList.RemoveHead();
                }
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Drop High Queue Rating Sources */
            case MP_DROP_HIGH_QUEUE_RATING_SOURCES:
                // While selected items aren't empty
                while (!selectedList.IsEmpty())
                {
                    // Remove High Queue Rating sources from the current selected item
                    selectedList.GetHead()->RemoveHighQueueRatingSources();
                    // Remove this item from the selected items list
                    selectedList.RemoveHead();
                }
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Clean Up Sources */
            case MP_CLEAN_UP_SOURCES:
                // While selected items aren't empty
                while (!selectedList.IsEmpty())
                {
                    // Clean up sources from the current selected item
                    selectedList.GetHead()->CleanUpSources();
                    // Remove this item from the selected items list
                    selectedList.RemoveHead();
                }
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Swap every A4AF to this file now */
            case MP_SWAP_A4AF_TO_THIS:
                // If you have selected only one item and
                // either file status is ready or empty...
                if ((selectedCount == 1) && ((file->GetStatus(false) == PS_READY) || (file->GetStatus(false) == PS_EMPTY)))
                {
                    // Disable any A4AF auto in download queue
                    theApp.downloadqueue->DisableAnyA4AFAuto();
                    // Some positions
                    POSITION position, temp_position;
                    // For all A4AF sources of current file...
                    for (position = file->A4AFSourcesList.GetHeadPosition() ; (temp_position = position) != NULL ;)
                    {
                        // Get next position in position
                        file->A4AFSourcesList.GetNext(position);
                        // Get client at temp_position in A4AF sources list
                        CUpDownClient *A4AFclient = file->A4AFSourcesList.GetAt(temp_position);
                        // If this client download status isn't downloading
                        if (A4AFclient->GetDownloadState() != DS_DOWNLOADING)
                        {
                            // Remove source at temp_position in A4AF sources list
                            file->A4AFSourcesList.RemoveAt(temp_position);
                            // Swap every source of this file to client
                            A4AFclient->SwapToThisFile(file);
                        }
                    }
                }
                // Update item in download list window
                this->UpdateItem(file);
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Swap every A4AF to this file AUTO */
            case MP_SWAP_A4AF_TO_THIS_AUTO:
                // If file is already in A4AF Auto mode...
                if (file->IsA4AFAuto())
                {
                    // Then disable it
                    file->SetA4AFAuto(false);
                }
                else
                {
                    // Disable any A4AF in the download queue
                    theApp.downloadqueue->DisableAnyA4AFAuto();
                    // Enable A4AF Auto mode for this file
                    file->SetA4AFAuto(true);
                }
                break;
    /* End modif */
 /* Razor 1a - Modif by MikaelB
    Event for Swap every A4AF to any other file now */
            case MP_SWAP_A4AF_TO_ANY_OTHER:
                // If you have selected only one item and
                // either file status is ready or empty...
                if ((selectedCount == 1) && ((file->GetStatus(false) == PS_READY) || (file->GetStatus(false) == PS_EMPTY)))
                {
                    // Disable any A4AF Auto in download queue
                    theApp.downloadqueue->DisableAnyA4AFAuto();
                    // Some positions
                    POSITION position, temp_position;
                    // For all position in the file sources list...
                    for (position = file->srclist.GetHeadPosition() ; (temp_position = position) != NULL ;)
                    {
                        // Get next position in position
                        file->srclist.GetNext(position);
                        // Swap every sources of source at temp_position to another file
                        file->srclist.GetAt(temp_position)->SwapToAnotherFile();
                    }
                }
                // Update item in download list window
                this->UpdateItem(file);
                break;
    /* End modif */
            case MP_PRIOHIGH:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        //[Tarod]:
                        selectedList.GetHead()->SetAutoDownPriority(false);
                        selectedList.GetHead()->SetDownPriority(PR_HIGH);
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->SetAutoDownPriority(false);
                file->SetDownPriority(PR_HIGH);
                break;
            case MP_PRIOLOW:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        //[Tarod]:
                        selectedList.GetHead()->SetAutoDownPriority(false);
                        selectedList.GetHead()->SetDownPriority(PR_LOW);
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->SetAutoDownPriority(false);
                file->SetDownPriority(PR_LOW);
                break;
            case MP_PRIONORMAL:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        //[Tarod]:
                        selectedList.GetHead()->SetAutoDownPriority(false);
                        selectedList.GetHead()->SetDownPriority(PR_NORMAL);
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->SetAutoDownPriority(false);
                file->SetDownPriority(PR_NORMAL);
                break;
            case MP_PRIOAUTO:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        selectedList.GetHead()->SetAutoDownPriority(true);
                        selectedList.GetHead()->SetDownPriority(PR_HIGH);
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->SetAutoDownPriority(true);
                file->SetDownPriority(PR_HIGH);
                break;
            case MP_PAUSE:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        selectedList.GetHead()->PauseFile();
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->PauseFile();
                break;
            case MP_RESUME:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        selectedList.GetHead()->ResumeFile();
                        selectedList.GetHead()->SavePartFile();
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                file->ResumeFile();
                file->SavePartFile();
                break;
            case MP_STOP:
                if (selectedCount > 1)
                {
                    Freeze();
                    while (!selectedList.IsEmpty())
                    {
                        CPartFile *selected = selectedList.GetHead();
                        HideSources(selected);
                        selected->StopFile();
                        selectedList.RemoveHead();
                    }
                    Thaw();
                    break;
                }
                HideSources(file);
                file->StopFile();
                break;
            case MP_CLEARCOMPLETED:
                Freeze();
                ClearCompleted();
                Thaw();
                break;
            case 30001:
                {
                    wxTextEntryDialog addsource((wxWindow *) this, _T("(as a sample:) '192.168.3.1:4661'"), _T(":"),
                    _T("127.0.0.1:4661"), wxOK |wxCANCEL);
                    if (addsource.ShowModal() == wxID_OK)
                    {
                        wxString result;
                        wxUint16 i = 0, srcport = 0;
                        wxUint32 srcip = 0;
                        result = addsource.GetValue();
                        while (i < result.Len())
                        {
                            if (((const char *) result) [i] == ':')
                            {
                                srcip = (wxUint32) inet_addr((const char *) result.Mid(0, i));
                                srcport = (wxUint16) atoi((const char *) result.Mid(i + 1, 5));
                                if (srcip &&srcport)
                                {
                                    if (theApp.dynprefs->Get<long>("max-sources") > file->GetSourceCount())
                                    {
                                        CUpDownClient *newsource = new CUpDownClient(srcport, srcip, 0, 0, file);
                                        theApp.downloadqueue->CheckAndAddSource(file, newsource);
                                    }
                                }
                                i = result.Len();
                            }
                            else i++;
                        }
                    }
                    break;
                }
            case MP_METINFO:
                {
                    CFileDetailDialog *dialog = new CFileDetailDialog(this, file);
                    dialog->ShowModal();
                    delete dialog;
                    break;
                }
            case MP_GETED2KLINK:
                if (selectedCount > 1)
                {
                    wxString str;
                    while (!selectedList.IsEmpty())
                    {
                        str += theApp.CreateED2kLink(selectedList.GetHead()) + "\n";
                        selectedList.RemoveHead();
                    }
                    theApp.CopyTextToClipboard(str);
                    break;
                }
                theApp.CopyTextToClipboard(theApp.CreateED2kLink(file));
                break;
            case MP_GETHTMLED2KLINK:
                if (selectedCount > 1)
                {
                    wxString str;
                    while (!selectedList.IsEmpty())
                    {
                        str += theApp.CreateHTMLED2kLink(selectedList.GetHead()) + "\n";
                        selectedList.RemoveHead();
                    }
                    theApp.CopyTextToClipboard(str);
                    break;
                }
                theApp.CopyTextToClipboard(theApp.CreateHTMLED2kLink(file));
                break;
            case MP_OPEN:
                {
                    if (selectedCount > 1)
                    {
                        break;
                    }
                    char *buffer = new char[250];
                    sprintf(buffer, "%s/%s", static_cast<const char *>(theApp.dynprefs->Get<wxString>("incoming-directory").c_str()), file->GetFileName());
                    //ShellOpenFile(buffer);
                    printf("===> open %s\n", buffer);
                    delete buffer;
                    break;
                }
            case MP_PREVIEW:
                {
                    if (selectedCount > 1)
                    {
                        break;
                    }
                    file->PreviewFile();
                    break;
                }
            case MP_VIEWFILECOMMENTS:
                {
                    CCommentDialogLst dialog(this, file);
                    dialog.ShowModal();
                    break;
                }
            }
        }
        else
        {
            CUpDownClient *client = (CUpDownClient *) content->value;
            switch (event.GetId())
            {
            case MP_SHOWLIST:
                client->RequestSharedFileList();
                break;
            case MP_MESSAGE:
                theApp.xmuledlg->chatwnd->StartSession(client);
                break;
            case MP_ADDFRIEND:
                {
                    //        int pos=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
                    //        while (pos!=-1){
                    theApp.friendlist->AddFriend(client);
                    //    pos=GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED);
                    //  }
                    break;
                }
            case MP_DETAIL:
                CClientDetailDialog *dialog = new CClientDetailDialog(this, client);
                dialog->ShowModal();
                delete dialog;
                //dialog.DoModal();
                return TRUE;
                break;
            }
        }
        // cleanup multiselection
        selectedList.RemoveAll();
    }
    else
    {
        // nothing selected
        switch (event.GetId())
        {
        case MP_CLEARCOMPLETED:
            ClearCompleted();
            break;
        }
    }
    // should we call this? (no!)
    evt.Skip();
    return true;
//    return CMuleListCtrl::ProcessEvent(evt);
}

void CDownloadListCtrl::OnColumnClick(wxListEvent &evt)
{
    //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 != evt.GetColumn()) ? true: !m_oldSortAscending;
    // Item is column clicked
    sortItem = evt.GetColumn();
    // 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));
}

int CDownloadListCtrl::SortProc(long lParam1, long lParam2, long 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 == 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;
}

#if 0
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);
}

#endif

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

int CDownloadListCtrl::Compare(CUpDownClient *client1, CUpDownClient *client2, long lParamSort, int sortMod)
{
    switch (lParamSort)
    {
        //name asc:
    case 0:
        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());
        //size but we use status asc:
    case 1:
        return client1->GetDownloadState() - client2->GetDownloadState();
        //transfered asc:
    case 2:
        //completed asc:
    case 3:
        return client1->GetTransferedDown() - client2->GetTransferedDown();
        //speed asc:
    case 4:
        return client1->GetDownloadDatarate() - client2->GetDownloadDatarate();
        //progress asc:
    case 5:
        return client1->GetAvailablePartCount() - client2->GetAvailablePartCount();
    case 6:
        if (client1->GetClientSoft() == client2->GetClientSoft())
        {
            if (client1->IsEmuleClient())
            {
                return client2->GetMuleVersion() - client1->GetMuleVersion();
            }
            else
            {
                return client2->GetVersion() - client1->GetVersion();
            }
        }
        return client1->GetClientSoft() - client2->GetClientSoft();
        //qr asc:
    case 7:
        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())
            {
                return 1;
            }
            if (client2->IsRemoteQueueFull())
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }
        return client1->GetDownloadState() - client2->GetDownloadState();
    default:
        return 0;
    }
}

void CDownloadListCtrl::CreateMenues()
{
}

wxString CDownloadListCtrl::getTextList()
{
    wxString 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);
            if (file->IsPartFile() == false && CheckShowItemInGivenCat(file, curTab))
            {
                RemoveFile(file);
            }
            theApp.xmuledlg->AddLogLine(false, "%s", file->GetFileName());
            char buffer[50 + 1];
            //wxASSERT(file->GetFileName() != NULL);
            strncpy(buffer, file->GetFileName(), 50);
            buffer[50] = '\0';
            wxString temp;
            temp.Printf("\n%s\t [%.1f%%] %i/%i - %s", buffer, file->GetPercentCompleted(), file->GetTransferingSrcCount(), file->GetSourceCount(), file->getPartfileStatus() .GetData());
            out += temp;
        }
    }
    theApp.xmuledlg->AddLogLine(false, "%s", out.c_str());
    return out;
}

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;
        // Already point to the next iterator.:
        it++;
        if (cur_item->type == FILE_TYPE)
        {
            CPartFile *file = reinterpret_cast < CPartFile *> (cur_item->value);
            if (file->IsPartFile() == false)
            {
                RemoveFile(file);
            }
        }
    }
}

void CDownloadListCtrl::ShowFilesCount()
{
    wxString counter;
    //theApp.downloadqueue->GetFileCount();:
    uint16_t count = 0;
    // remove all displayed files with a different cat
    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++;
            }
        }
    }
    wxString fmtstr = wxString::Format(_("Downloads (%i)"), GetItemCount());
    wxStaticCast(FindWindowByName(wxT("downloadsLabel")), wxStaticText)->SetLabel(fmtstr);
}

void CDownloadListCtrl::ShowSelectedFileDetails()
{
    if (GetSelectedItemCount() == 0)
    {
        return;
    }
    int cursel = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
    CtrlItem_Struct *content = (CtrlItem_Struct *) this->GetItemData(cursel);
    if (content->type == FILE_TYPE)
    {
        CPartFile *file = (CPartFile *) content->value;
        //CFileDetailDialog dialog(file);
        //dialog.DoModal();
    if ((file->HasComment() || file->HasRating()) /*&& p.x<13 */
        )
        {
            //CCommentDialogLst dialog(file);
            //dialog.DoModal();
        }
        else
        {
            //CFileDetailDialog dialog(file);
            //dialog.DoModal();
        }
    }
    else
    {
        //CClientDetailDialog dialog(client);
        //dialog.DoModal();
        printf("Show details me too\n");
    }
}

void CDownloadListCtrl::ChangeCategory(int newsel)
{
    //SetRedraw(FALSE);
    Freeze();
    // 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);
    Thaw();
    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;
        int16_t result = FindItem(-1, (long) updateItem);
        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;
        int16_t result = FindItem(-1, (long) updateItem);
        if (result == (-1))
        {
            //InsertItem(LVIF_PARAM,GetItemCount(),0,0,0,0,(LPARAM)updateItem);
            int newitem = InsertItem(GetItemCount(), "This is not visible");
            SetItemData(newitem, (long) updateItem);
            wxListItem myitem;
            myitem.m_itemId = newitem;
            myitem.SetBackgroundColour(SYSCOLOR(wxSYS_COLOUR_LISTBOX));
            SetItem(myitem);
        }
        return;
    }
}

bool CDownloadListCtrl::ShowItemInCurrentCat(CPartFile *file, int newsel)
{
    return(((newsel == 0 && !theApp.glob_prefs->ShowAllNotCats()) || (newsel == 0 &&theApp.glob_prefs->ShowAllNotCats() &&file->GetCategory() == 0)) || (newsel > 0 &&newsel == file->GetCategory()));
}

//lagloose
void CDownloadListCtrl::OnKeyUp(wxKeyEvent &event)
{
    isShift = false;
    event.Skip();
}

void CDownloadListCtrl::OnKeyDown(wxKeyEvent &event)
{
    if (event.GetKeyCode() == WXK_SHIFT)
    {
        isShift = true;
    }
    else
    {
        isShift = false;
    }
    event.Skip();
}

// end lagloose
wxString CDownloadListCtrl::getListforDaemon ()
{
    wxString out = "";
    wxString temp,tmp2;

    // 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);
            if (file->IsPartFile() == false &&CheckShowItemInGivenCat(file, curTab))
            {
                RemoveFile(file);
            }
            char buffer[60 + 1];
            strncpy(buffer, file->GetFileName(), 60);
            buffer[60] = '\0';

            if (file->GetTransferingSrcCount() == 0)
                tmp2 = "";
            else
                tmp2.Printf ("%.1f %s", file->GetDatarate() / 1024.0f, GetResString(IDS_KBYTESEC) .GetData());

            temp.Printf ("%-60s %10s %9s %11s %6.1f%% %4i|%4i %s\n", buffer, CastItoXBytes(file->GetFileSize()) .GetData(), CastItoXBytes(file->GetCompletedSize()) .GetData(), tmp2.GetData(),file->GetPercentCompleted(), file->GetTransferingSrcCount(), file->GetSourceCount(), file->getPartfileStatus() .GetData());
            out += temp;
        }
    }
    return out;
}

void CDownloadListCtrl::HandleCatClick(wxCommandEvent* event)
{
    cout << "handled cat click!" << endl;
    std::list<CPartFile *> selections;
    collectSelections(&selections);

    while (selections.empty() == false)
    {
        CPartFile* selected = selections.front();
        selected->SetCategory(event->GetId() - MP_ASSIGNCAT);
        selections.erase(selections.begin());
    }

    ChangeCategory(curTab);
}
