Upstream tarball 20080630
[amule.git] / src / DownloadListCtrl.cpp
blob00a8abb13153f9a0dffe189ceaf2a8bbffe8769b
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <protocol/ed2k/ClientSoftware.h>
27 #include <common/MenuIDs.h>
29 #include <common/Format.h> // Needed for CFormat
30 #include "amule.h" // Needed for theApp
31 #include "amuleDlg.h" // Needed for CamuleDlg
32 #include "BarShader.h" // Needed for CBarShader
33 #include "ClientDetailDialog.h" // Needed for CClientDetailDialog
34 #include "ChatWnd.h" // Needed for CChatWnd
35 #include "CommentDialogLst.h" // Needed for CCommentDialogLst
36 #include "DownloadListCtrl.h" // Interface declarations
37 #include "DataToText.h" // Needed for PriorityToStr
38 #include "FileDetailDialog.h" // Needed for CFileDetailDialog
39 #include "GuiEvents.h" // Needed for CoreNotify_*
40 #ifdef ENABLE_IP2COUNTRY
41 #include "IP2Country.h" // Needed for IP2Country
42 #endif
43 #include "Logger.h"
44 #include "muuli_wdr.h" // Needed for ID_DLOADLIST
45 #include "PartFile.h" // Needed for CPartFile
46 #include "Preferences.h"
47 #include "SharedFileList.h" // Needed for CSharedFileList
48 #include "TerminationProcess.h" // Needed for CTerminationProcess
49 #include "updownclient.h" // Needed for CUpDownClient
52 class CPartFile;
55 struct CtrlItem_Struct
57 CtrlItem_Struct()
58 : dwUpdated(0),
59 status(NULL),
60 m_owner(NULL),
61 m_fileValue(NULL),
62 m_sourceValue(NULL),
63 m_type(FILE_TYPE)
64 { }
66 ~CtrlItem_Struct() {
67 delete status;
70 DownloadItemType GetType() const {
71 return m_type;
74 CPartFile* GetOwner() const {
75 return m_owner;
78 CPartFile* GetFile() const {
79 return m_fileValue;
82 CUpDownClient* GetSource() const {
83 return m_sourceValue;
86 void SetContents(CPartFile* file) {
87 m_owner = NULL;
88 m_fileValue = file;
89 m_sourceValue = NULL;
90 m_owner = NULL;
91 m_type = FILE_TYPE;
94 void SetContents(CPartFile* owner, CUpDownClient* source, DownloadItemType type) {
95 wxCHECK_RET(type != FILE_TYPE, wxT("Invalid type, not a source"));
97 m_owner = owner;
98 m_fileValue = NULL;
99 m_sourceValue = source;
100 m_type = type;
104 uint32 dwUpdated;
105 wxBitmap* status;
107 private:
108 CPartFile* m_owner;
109 CPartFile* m_fileValue;
110 CUpDownClient* m_sourceValue;
111 DownloadItemType m_type;
116 #define m_ImageList theApp->amuledlg->m_imagelist
119 BEGIN_EVENT_TABLE(CDownloadListCtrl, CMuleListCtrl)
120 EVT_LIST_ITEM_ACTIVATED(ID_DLOADLIST, CDownloadListCtrl::OnItemActivated)
121 EVT_LIST_ITEM_RIGHT_CLICK(ID_DLOADLIST, CDownloadListCtrl::OnMouseRightClick)
122 EVT_LIST_ITEM_MIDDLE_CLICK(ID_DLOADLIST, CDownloadListCtrl::OnMouseMiddleClick)
124 EVT_CHAR( CDownloadListCtrl::OnKeyPressed )
126 EVT_MENU( MP_CANCEL, CDownloadListCtrl::OnCancelFile )
128 EVT_MENU( MP_PAUSE, CDownloadListCtrl::OnSetStatus )
129 EVT_MENU( MP_STOP, CDownloadListCtrl::OnSetStatus )
130 EVT_MENU( MP_RESUME, CDownloadListCtrl::OnSetStatus )
132 EVT_MENU( MP_PRIOLOW, CDownloadListCtrl::OnSetPriority )
133 EVT_MENU( MP_PRIONORMAL, CDownloadListCtrl::OnSetPriority )
134 EVT_MENU( MP_PRIOHIGH, CDownloadListCtrl::OnSetPriority )
135 EVT_MENU( MP_PRIOAUTO, CDownloadListCtrl::OnSetPriority )
137 EVT_MENU( MP_SWAP_A4AF_TO_THIS, CDownloadListCtrl::OnSwapSources )
138 EVT_MENU( MP_SWAP_A4AF_TO_THIS_AUTO, CDownloadListCtrl::OnSwapSources )
139 EVT_MENU( MP_SWAP_A4AF_TO_ANY_OTHER, CDownloadListCtrl::OnSwapSources )
141 EVT_MENU_RANGE( MP_ASSIGNCAT, MP_ASSIGNCAT + 99, CDownloadListCtrl::OnSetCategory )
143 EVT_MENU( MP_CLEARCOMPLETED, CDownloadListCtrl::OnClearCompleted )
145 EVT_MENU( MP_GETMAGNETLINK, CDownloadListCtrl::OnGetLink )
146 EVT_MENU( MP_GETED2KLINK, CDownloadListCtrl::OnGetLink )
148 EVT_MENU( MP_METINFO, CDownloadListCtrl::OnViewFileInfo )
149 EVT_MENU( MP_VIEW, CDownloadListCtrl::OnPreviewFile )
150 EVT_MENU( MP_VIEWFILECOMMENTS, CDownloadListCtrl::OnViewFileComments )
152 EVT_MENU( MP_WS, CDownloadListCtrl::OnGetFeedback )
153 EVT_MENU( MP_RAZORSTATS, CDownloadListCtrl::OnGetRazorStats )
155 EVT_MENU( MP_CHANGE2FILE, CDownloadListCtrl::OnSwapSource )
156 EVT_MENU( MP_SHOWLIST, CDownloadListCtrl::OnViewFiles )
157 EVT_MENU( MP_ADDFRIEND, CDownloadListCtrl::OnAddFriend )
158 EVT_MENU( MP_SENDMESSAGE, CDownloadListCtrl::OnSendMessage )
159 EVT_MENU( MP_DETAIL, CDownloadListCtrl::OnViewClientInfo )
160 END_EVENT_TABLE()
164 //! This listtype is used when gathering the selected items.
165 typedef std::list<CtrlItem_Struct*> ItemList;
169 CDownloadListCtrl::CDownloadListCtrl(
170 wxWindow *parent, wxWindowID winid, const wxPoint& pos, const wxSize& size,
171 long style, const wxValidator& validator, const wxString& name )
173 CMuleListCtrl( parent, winid, pos, size, style | wxLC_OWNERDRAW, validator, name )
175 // Setting the sorter function.
176 SetSortFunc( SortProc );
178 // Set the table-name (for loading and saving preferences).
179 SetTableName( wxT("Download") );
181 m_menu = NULL;
183 m_hilightBrush = *(wxTheBrushList->FindOrCreateBrush(CMuleColour(wxSYS_COLOUR_HIGHLIGHT).Blend(125), wxSOLID ));
185 m_hilightUnfocusBrush = *(wxTheBrushList->FindOrCreateBrush(CMuleColour(wxSYS_COLOUR_BTNSHADOW).Blend(125), wxSOLID ));
187 InsertColumn( 0, _("File Name"), wxLIST_FORMAT_LEFT, 260 );
188 InsertColumn( 1, _("Size"), wxLIST_FORMAT_LEFT, 60 );
189 InsertColumn( 2, _("Transferred"), wxLIST_FORMAT_LEFT, 65 );
190 InsertColumn( 3, _("Completed"), wxLIST_FORMAT_LEFT, 65 );
191 InsertColumn( 4, _("Speed"), wxLIST_FORMAT_LEFT, 65 );
192 InsertColumn( 5, _("Progress"), wxLIST_FORMAT_LEFT, 170 );
193 InsertColumn( 6, _("Sources"), wxLIST_FORMAT_LEFT, 50 );
194 InsertColumn( 7, _("Priority"), wxLIST_FORMAT_LEFT, 55 );
195 InsertColumn( 8, _("Status"), wxLIST_FORMAT_LEFT, 70 );
196 InsertColumn( 9, _("Time Remaining"), wxLIST_FORMAT_LEFT, 110 );
197 InsertColumn( 10, _("Last Seen Complete"), wxLIST_FORMAT_LEFT, 220 );
198 InsertColumn( 11, _("Last Reception"), wxLIST_FORMAT_LEFT, 220 );
200 m_category = 0;
201 m_completedFiles = 0;
202 m_filecount = 0;
203 LoadSettings();
207 CDownloadListCtrl::~CDownloadListCtrl()
209 while ( !m_ListItems.empty() ) {
210 delete m_ListItems.begin()->second;
211 m_ListItems.erase( m_ListItems.begin() );
216 void CDownloadListCtrl::AddFile( CPartFile* file )
218 wxASSERT( file );
220 // Avoid duplicate entries of files
221 if ( m_ListItems.find( file ) == m_ListItems.end() ) {
222 CtrlItem_Struct* newitem = new CtrlItem_Struct;
223 newitem->SetContents(file);
225 m_ListItems.insert( ListItemsPair( file, newitem ) );
227 // Check if the new file is visible in the current category
228 if ( file->CheckShowItemInGivenCat( m_category ) ) {
229 ShowFile( file, true );
230 SortList();
236 void CDownloadListCtrl::AddSource(CPartFile* owner, CUpDownClient* source, DownloadItemType type)
238 wxCHECK_RET(owner, wxT("NULL owner in CDownloadListCtrl::AddSource"));
239 wxCHECK_RET(source, wxT("NULL source in CDownloadListCtrl::AddSource"));
240 wxCHECK_RET(type != FILE_TYPE, wxT("Invalid type, not a source"));
242 // Update the other instances of this source
243 bool bFound = false;
244 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
245 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
246 CtrlItem_Struct* cur_item = it->second;
248 // Check if this source has been already added to this file => to be sure
249 if ( cur_item->GetOwner() == owner ) {
250 // Update this instance with its new setting
251 cur_item->SetContents(owner, source, type);
252 cur_item->dwUpdated = 0;
253 bFound = true;
254 } else if ( type == AVAILABLE_SOURCE ) {
255 // The state 'Available' is exclusive
256 cur_item->SetContents(cur_item->GetOwner(), source, A4AF_SOURCE);
257 cur_item->dwUpdated = 0;
261 if ( bFound ) {
262 return;
265 if ( owner->ShowSources() ) {
266 CtrlItem_Struct* newitem = new CtrlItem_Struct;
267 newitem->SetContents(owner, source, type);
269 m_ListItems.insert( ListItemsPair(source, newitem) );
271 // Find the owner-object
272 ListItems::iterator it = m_ListItems.find( owner );
274 if ( it != m_ListItems.end() ) {
275 long item = FindItem( -1, reinterpret_cast<wxUIntPtr>(it->second) );
277 if ( item > -1 ) {
278 item = InsertItem( item + 1, wxEmptyString );
280 SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
282 // background.. this should be in a function
283 wxListItem listitem;
284 listitem.m_itemId = item;
286 listitem.SetBackgroundColour( GetBackgroundColour() );
288 SetItem( listitem );
295 void CDownloadListCtrl::RemoveSource( const CUpDownClient* source, const CPartFile* owner )
297 wxASSERT( source );
299 // Retrieve all entries matching the source
300 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
302 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ) {
303 ListItems::iterator tmp = it++;
305 CtrlItem_Struct* item = tmp->second;
306 if ( owner == NULL || owner == item->GetOwner() ) {
307 // Remove it from the m_ListItems
308 m_ListItems.erase( tmp );
310 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
312 if ( index > -1 ) {
313 DeleteItem( index );
316 delete item;
322 void CDownloadListCtrl::RemoveFile( CPartFile* file )
324 wxASSERT( file );
326 // Ensure that any assosiated sources and list-entries are removed
327 ShowFile( file, false );
329 // Find the assosiated list-item
330 ListItems::iterator it = m_ListItems.find( file );
332 if ( it != m_ListItems.end() ) {
333 delete it->second;
335 m_ListItems.erase( it );
340 void CDownloadListCtrl::UpdateItem(const void* toupdate)
342 // Retrieve all entries matching the source
343 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
345 // Visible lines, default to all because not all platforms
346 // support the GetVisibleLines function
347 long first = 0, last = GetItemCount();
349 #ifndef __WXMSW__
350 // Get visible lines if we need them
351 if ( rangeIt.first != rangeIt.second ) {
352 GetVisibleLines( &first, &last );
354 #endif
356 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
357 CtrlItem_Struct* item = it->second;
359 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
361 // Determine if the file should be shown in the current category
362 if ( item->GetType() == FILE_TYPE ) {
363 CPartFile* file = item->GetFile();
365 bool show = file->CheckShowItemInGivenCat( m_category );
367 if ( index > -1 ) {
368 if ( show ) {
369 item->dwUpdated = 0;
371 // Only update visible lines
372 if ( index >= first && index <= last) {
373 RefreshItem( index );
375 } else {
376 // Item should no longer be shown in
377 // the current category
378 ShowFile( file, false );
380 } else if ( show ) {
381 // Item has been hidden but new status means
382 // that it should it should be shown in the
383 // current category
384 ShowFile( file, true );
387 if (file->GetStatus() == PS_COMPLETE) {
388 m_completedFiles = true;
390 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(true);
392 } else {
393 item->dwUpdated = 0;
395 // Only update visible lines
396 if ( index >= first && index <= last) {
397 RefreshItem( index );
404 void CDownloadListCtrl::ShowFile( CPartFile* file, bool show )
406 wxASSERT( file );
408 ListItems::iterator it = m_ListItems.find( file );
410 if ( it != m_ListItems.end() ) {
411 CtrlItem_Struct* item = it->second;
413 if ( show ) {
414 // Check if the file is already being displayed
415 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
416 if ( index == -1 ) {
417 long newitem = InsertItem( GetItemCount(), wxEmptyString );
419 SetItemPtrData( newitem, reinterpret_cast<wxUIntPtr>(item) );
421 wxListItem myitem;
422 myitem.m_itemId = newitem;
423 myitem.SetBackgroundColour( GetBackgroundColour() );
425 SetItem(myitem);
427 RefreshItem( newitem );
429 ShowFilesCount( 1 );
431 } else {
432 // Ensure sources are hidden
433 ShowSources( file, false );
435 // Try to find the file and remove it
436 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
437 if ( index > -1 ) {
438 DeleteItem( index );
439 ShowFilesCount( -1 );
446 void CDownloadListCtrl::ShowSources( CPartFile* file, bool show )
448 // Check if the current state is the same as the new state
449 if ( file->ShowSources() == show ) {
450 return;
453 Freeze();
455 file->SetShowSources( show );
457 if ( show ) {
458 const CPartFile::SourceSet& normSources = file->GetSourceList();
459 const CPartFile::SourceSet& a4afSources = file->GetA4AFList();
461 // Adding normal sources
462 CPartFile::SourceSet::const_iterator it;
463 for ( it = normSources.begin(); it != normSources.end(); ++it ) {
464 switch ((*it)->GetDownloadState()) {
465 case DS_DOWNLOADING:
466 case DS_ONQUEUE:
467 AddSource( file, *it, AVAILABLE_SOURCE );
468 default:
469 // Any other state
470 AddSource( file, *it, UNAVAILABLE_SOURCE );
475 // Adding A4AF sources
476 for ( it = a4afSources.begin(); it != a4afSources.end(); ++it ) {
477 AddSource( file, *it, A4AF_SOURCE );
479 } else {
480 for ( int i = GetItemCount() - 1; i >= 0; --i ) {
481 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData(i);
483 if ( item->GetType() != FILE_TYPE && item->GetOwner() == file ) {
484 // Remove from the grand list, this call doesn't remove the source
485 // from the listctrl, because ShowSources is now false. This also
486 // deletes the item.
487 RemoveSource(item->GetSource(), file);
492 Thaw();
496 void CDownloadListCtrl::ChangeCategory( int newCategory )
498 Freeze();
500 // remove all displayed files with a different cat and show the correct ones
501 for (ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++) {
502 const CtrlItem_Struct *cur_item = it->second;
504 if ( cur_item->GetType() == FILE_TYPE ) {
505 CPartFile* file = cur_item->GetFile();
507 bool curVisibility = file->CheckShowItemInGivenCat( m_category );
508 bool newVisibility = file->CheckShowItemInGivenCat( newCategory );
510 // Check if the visibility of the file has changed. However, if the
511 // current category is the default (0) category, then we can't use
512 // curVisiblity to see if the visibility has changed but instead
513 // have to let ShowFile() check if the file is or isn't on the list.
514 if ( curVisibility != newVisibility || !newCategory ) {
515 ShowFile( file, newVisibility );
520 Thaw();
522 m_category = newCategory;
526 uint8 CDownloadListCtrl::GetCategory() const
528 return m_category;
535 const int itFILES = 1;
536 const int itSOURCES = 2;
539 * Helper-function: This function is used to gather selected items.
541 * @param list A pointer to the list to gather items from.
542 * @param types The desired types OR'd together.
543 * @return A list containing the selected items of the choosen types.
545 ItemList GetSelectedItems( CDownloadListCtrl* list, int types )
547 ItemList results;
549 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
551 while ( index > -1 ) {
552 CtrlItem_Struct* item = (CtrlItem_Struct*)list->GetItemData( index );
554 bool add = false;
555 add |= ( item->GetType() == FILE_TYPE ) && ( types & itFILES );
556 add |= ( item->GetType() != FILE_TYPE ) && ( types & itSOURCES );
558 if ( add ) {
559 results.push_back( item );
562 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
565 return results;
569 void CDownloadListCtrl::OnCancelFile(wxCommandEvent& WXUNUSED(event))
571 ItemList files = ::GetSelectedItems(this, itFILES);
572 if (files.size()) {
573 wxString question;
574 if (files.size() == 1) {
575 question = _("Are you sure that you wish to delete the selected file?");
576 } else {
577 question = _("Are you sure that you wish to delete the selected files?");
579 if (wxMessageBox( question, _("Cancel"), wxICON_QUESTION | wxYES_NO, this) == wxYES) {
580 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
581 CPartFile* file = (*it)->GetFile();
582 if (file) {
583 switch (file->GetStatus()) {
584 case PS_WAITINGFORHASH:
585 case PS_HASHING:
586 case PS_COMPLETING:
587 case PS_COMPLETE:
588 break;
589 default:
590 CoreNotify_PartFile_Delete(file);
599 void CDownloadListCtrl::OnSetPriority( wxCommandEvent& event )
601 int priority = 0;
602 switch ( event.GetId() ) {
603 case MP_PRIOLOW: priority = PR_LOW; break;
604 case MP_PRIONORMAL: priority = PR_NORMAL; break;
605 case MP_PRIOHIGH: priority = PR_HIGH; break;
606 case MP_PRIOAUTO: priority = PR_AUTO; break;
607 default:
608 wxASSERT( false );
611 ItemList files = ::GetSelectedItems( this, itFILES );
613 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
614 CPartFile* file = (*it)->GetFile();
616 if ( priority == PR_AUTO ) {
617 CoreNotify_PartFile_PrioAuto( file, true );
618 } else {
619 CoreNotify_PartFile_PrioAuto( file, false );
621 CoreNotify_PartFile_PrioSet( file, priority, true );
627 void CDownloadListCtrl::OnSwapSources( wxCommandEvent& event )
629 ItemList files = ::GetSelectedItems( this, itFILES );
631 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
632 CPartFile* file = (*it)->GetFile();
634 switch ( event.GetId() ) {
635 case MP_SWAP_A4AF_TO_THIS:
636 CoreNotify_PartFile_Swap_A4AF( file );
637 break;
639 case MP_SWAP_A4AF_TO_THIS_AUTO:
640 CoreNotify_PartFile_Swap_A4AF_Auto( file );
641 break;
643 case MP_SWAP_A4AF_TO_ANY_OTHER:
644 CoreNotify_PartFile_Swap_A4AF_Others( file );
645 break;
651 void CDownloadListCtrl::OnSetCategory( wxCommandEvent& event )
653 ItemList files = ::GetSelectedItems( this, itFILES );
655 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
656 CPartFile* file = (*it)->GetFile();
658 CoreNotify_PartFile_SetCat( file, event.GetId() - MP_ASSIGNCAT );
661 ChangeCategory( m_category );
665 void CDownloadListCtrl::OnSetStatus( wxCommandEvent& event )
667 ItemList files = ::GetSelectedItems( this, itFILES );
669 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
670 CPartFile* file = (*it)->GetFile();
672 switch ( event.GetId() ) {
673 case MP_PAUSE:
674 CoreNotify_PartFile_Pause( file );
675 break;
677 case MP_RESUME:
678 CoreNotify_PartFile_Resume( file );
679 break;
681 case MP_STOP:
682 ShowSources(file, false);
683 CoreNotify_PartFile_Stop( file );
684 break;
690 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent& WXUNUSED(event) )
692 ClearCompleted();
696 void CDownloadListCtrl::OnGetLink(wxCommandEvent& event)
698 ItemList files = ::GetSelectedItems( this, itFILES );
700 wxString URIs;
702 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
703 CPartFile* file = (*it)->GetFile();
705 if ( event.GetId() == MP_GETED2KLINK ) {
706 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
707 } else {
708 URIs += theApp->CreateMagnetLink( file ) + wxT("\n");
712 if ( !URIs.IsEmpty() ) {
713 theApp->CopyTextToClipboard( URIs.BeforeLast(wxT('\n')) );
718 void CDownloadListCtrl::OnGetFeedback(wxCommandEvent& WXUNUSED(event))
720 wxString feed;
721 ItemList files = ::GetSelectedItems(this, itFILES);
723 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
724 if (feed.IsEmpty()) {
725 feed = CFormat(_("Feedback from: %s (%s)\n\n")) % thePrefs::GetUserNick() % GetFullMuleVersion();
726 } else {
727 feed += wxT("\n");
729 feed += (*it)->GetFile()->GetFeedback();
732 if (!feed.IsEmpty()) {
733 theApp->CopyTextToClipboard(feed);
738 void CDownloadListCtrl::OnGetRazorStats( wxCommandEvent& WXUNUSED(event) )
740 ItemList files = ::GetSelectedItems( this, itFILES );
742 if ( files.size() == 1 ) {
743 CPartFile* file = files.front()->GetFile();
745 theApp->amuledlg->LaunchUrl(
746 wxT("http://stats.razorback2.com/ed2khistory?ed2k=") +
747 file->GetFileHash().Encode());
752 void CDownloadListCtrl::OnViewFileInfo( wxCommandEvent& WXUNUSED(event) )
754 ItemList files = ::GetSelectedItems( this, itFILES );
756 if ( files.size() == 1 ) {
757 CPartFile* file = files.front()->GetFile();
759 CFileDetailDialog dialog( this, file );
760 dialog.ShowModal();
765 void CDownloadListCtrl::OnViewFileComments( wxCommandEvent& WXUNUSED(event) )
767 ItemList files = ::GetSelectedItems( this, itFILES );
769 if ( files.size() == 1 ) {
770 CPartFile* file = files.front()->GetFile();
772 CCommentDialogLst dialog( this, file );
773 dialog.ShowModal();
778 void CDownloadListCtrl::OnPreviewFile( wxCommandEvent& WXUNUSED(event) )
780 ItemList files = ::GetSelectedItems( this, itFILES );
782 if ( files.size() == 1 ) {
783 PreviewFile(files.front()->GetFile());
787 void CDownloadListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
789 ItemList sources = ::GetSelectedItems( this, itSOURCES );
791 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
792 CPartFile* file = (*it)->GetOwner();
793 CUpDownClient* source = (*it)->GetSource();
795 source->SwapToAnotherFile( true, false, false, file );
800 void CDownloadListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
802 ItemList sources = ::GetSelectedItems( this, itSOURCES );
804 if ( sources.size() == 1 ) {
805 CUpDownClient* source = sources.front()->GetSource();
807 source->RequestSharedFileList();
812 void CDownloadListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
814 ItemList sources = ::GetSelectedItems( this, itSOURCES );
816 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
817 CUpDownClient* client = (*it)->GetSource();
818 if (client->IsFriend()) {
819 theApp->amuledlg->m_chatwnd->RemoveFriend(client->GetUserHash(), client->GetIP(), client->GetUserPort());
820 } else {
821 theApp->amuledlg->m_chatwnd->AddFriend( client );
827 void CDownloadListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
829 ItemList sources = ::GetSelectedItems( this, itSOURCES );
831 if ( sources.size() == 1 ) {
832 CUpDownClient* source = (sources.front())->GetSource();
834 // These values are cached, since calling wxGetTextFromUser will
835 // start an event-loop, in which the client may be deleted.
836 wxString userName = source->GetUserName();
837 uint64 userID = GUI_ID(source->GetIP(), source->GetUserPort());
839 wxString message = ::wxGetTextFromUser(_("Send message to user"),
840 _("Message to send:"));
841 if ( !message.IsEmpty() ) {
842 theApp->amuledlg->m_chatwnd->SendMessage(message, userName, userID);
848 void CDownloadListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
850 ItemList sources = ::GetSelectedItems( this, itSOURCES );
852 if ( sources.size() == 1 ) {
853 CUpDownClient* source = (sources.front())->GetSource();
855 CClientDetailDialog dialog( this, source );
856 dialog.ShowModal();
861 void CDownloadListCtrl::OnItemActivated( wxListEvent& evt )
863 CtrlItem_Struct* content = (CtrlItem_Struct*)GetItemData( evt.GetIndex() );
865 if ( content->GetType() == FILE_TYPE ) {
866 CPartFile* file = content->GetFile();
868 if ((!file->IsPartFile() || file->GetStatus() == PS_COMPLETE) && file->PreviewAvailable()) {
869 PreviewFile( file );
870 } else {
871 ShowSources( file, !file->ShowSources() );
878 void CDownloadListCtrl::OnMouseRightClick(wxListEvent& evt)
880 long index = CheckSelection(evt);
881 if (index < 0) {
882 return;
885 delete m_menu;
886 m_menu = NULL;
888 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData( index );
889 if (item->GetType() == FILE_TYPE) {
890 m_menu = new wxMenu( _("Downloads") );
892 wxMenu* priomenu = new wxMenu();
893 priomenu->AppendCheckItem(MP_PRIOLOW, _("Low"));
894 priomenu->AppendCheckItem(MP_PRIONORMAL, _("Normal"));
895 priomenu->AppendCheckItem(MP_PRIOHIGH, _("High"));
896 priomenu->AppendCheckItem(MP_PRIOAUTO, _("Auto"));
898 m_menu->Append(MP_MENU_PRIO, _("Priority"), priomenu);
899 m_menu->Append(MP_CANCEL, _("Cancel"));
900 m_menu->Append(MP_STOP, _("&Stop"));
901 m_menu->Append(MP_PAUSE, _("&Pause"));
902 m_menu->Append(MP_RESUME, _("&Resume"));
903 m_menu->Append(MP_CLEARCOMPLETED, _("C&lear completed"));
904 //-----------------------------------------------------
905 m_menu->AppendSeparator();
906 //-----------------------------------------------------
907 wxMenu* extendedmenu = new wxMenu();
908 extendedmenu->Append(MP_SWAP_A4AF_TO_THIS,
909 _("Swap every A4AF to this file now"));
910 extendedmenu->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO,
911 _("Swap every A4AF to this file (Auto)"));
912 //-----------------------------------------------------
913 extendedmenu->AppendSeparator();
914 //-----------------------------------------------------
915 extendedmenu->Append(MP_SWAP_A4AF_TO_ANY_OTHER,
916 _("Swap every A4AF to any other file now"));
917 //-----------------------------------------------------
918 m_menu->Append(MP_MENU_EXTD,
919 _("Extended Options"), extendedmenu);
920 //-----------------------------------------------------
921 m_menu->AppendSeparator();
922 //-----------------------------------------------------
923 /* Commented out till RB2 is back
924 m_menu->Append( MP_RAZORSTATS,
925 _("Get Razorback 2's stats for this file"));
926 //-----------------------------------------------------
927 m_menu->AppendSeparator();
928 //-----------------------------------------------------
930 m_menu->Append(MP_VIEW, _("Preview"));
931 m_menu->Append(MP_METINFO, _("Show file &details"));
932 m_menu->Append(MP_VIEWFILECOMMENTS,
933 _("Show all comments"));
934 //-----------------------------------------------------
935 m_menu->AppendSeparator();
936 //-----------------------------------------------------
937 m_menu->Append(MP_GETMAGNETLINK,
938 _("Copy magnet URI to clipboard"));
939 m_menu->Append(MP_GETED2KLINK,
940 _("Copy eD2k &link to clipboard"));
941 m_menu->Append(MP_WS,
942 _("Copy feedback to clipboard"));
943 //-----------------------------------------------------
944 m_menu->AppendSeparator();
945 //-----------------------------------------------------
946 // Add dinamic entries
947 wxMenu *cats = new wxMenu(_("Category"));
948 if (theApp->glob_prefs->GetCatCount() > 1) {
949 for (uint32 i = 0; i < theApp->glob_prefs->GetCatCount(); i++) {
950 if ( i == 0 ) {
951 cats->Append( MP_ASSIGNCAT, _("unassign") );
952 } else {
953 cats->Append( MP_ASSIGNCAT + i,
954 theApp->glob_prefs->GetCategory(i)->title );
958 m_menu->Append(MP_MENU_CATS, _("Assign to category"), cats);
959 m_menu->Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1) );
961 CPartFile* file = item->GetFile();
962 // then set state
963 bool canStop;
964 bool canPause;
965 bool canCancel;
966 bool fileResumable;
967 if (file->GetStatus(true) != PS_ALLOCATING) {
968 const uint8_t fileStatus = file->GetStatus();
969 canStop =
970 (fileStatus != PS_ERROR) &&
971 (fileStatus != PS_COMPLETE) &&
972 (file->IsStopped() != true);
973 canPause = (file->GetStatus() != PS_PAUSED) && canStop;
974 fileResumable =
975 (fileStatus == PS_PAUSED) ||
976 (fileStatus == PS_ERROR) ||
977 (fileStatus == PS_INSUFFICIENT);
978 canCancel = fileStatus != PS_COMPLETE;
979 } else {
980 canStop = canPause = canCancel = fileResumable = false;
983 wxMenu* menu = m_menu;
984 menu->Enable( MP_CANCEL, canCancel );
985 menu->Enable( MP_PAUSE, canPause );
986 menu->Enable( MP_STOP, canStop );
987 menu->Enable( MP_RESUME, fileResumable );
988 menu->Enable( MP_CLEARCOMPLETED, m_completedFiles );
990 wxString view;
991 if (file->IsPartFile() && (file->GetStatus() != PS_COMPLETE)) {
992 view << CFormat(wxT("%s [%s]")) % _("Preview")
993 % file->GetPartMetFileName().RemoveExt();
994 } else if ( file->GetStatus() == PS_COMPLETE ) {
995 view << _("&Open the file");
997 menu->SetLabel(MP_VIEW, view);
998 menu->Enable(MP_VIEW, file->PreviewAvailable() );
1000 menu->Check( MP_SWAP_A4AF_TO_THIS_AUTO, file->IsA4AFAuto() );
1002 int priority = file->IsAutoDownPriority() ?
1003 PR_AUTO : file->GetDownPriority();
1005 priomenu->Check( MP_PRIOHIGH, priority == PR_HIGH );
1006 priomenu->Check( MP_PRIONORMAL, priority == PR_NORMAL );
1007 priomenu->Check( MP_PRIOLOW, priority == PR_LOW );
1008 priomenu->Check( MP_PRIOAUTO, priority == PR_AUTO );
1010 menu->Enable( MP_MENU_EXTD, canPause );
1012 PopupMenu(m_menu, evt.GetPoint());
1014 } else {
1015 CUpDownClient* client = item->GetSource();
1017 m_menu = new wxMenu(wxT("Clients"));
1018 m_menu->Append(MP_DETAIL, _("Show &Details"));
1019 m_menu->Append(MP_ADDFRIEND, client->IsFriend() ? _("Remove from friends") : _("Add to Friends"));
1020 m_menu->Append(MP_SHOWLIST, _("View Files"));
1021 m_menu->Append(MP_SENDMESSAGE, _("Send message"));
1022 m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
1024 // Only enable the Swap option for A4AF sources
1025 m_menu->Enable(MP_CHANGE2FILE, (item->GetType() == A4AF_SOURCE));
1026 // We need a valid IP if we are to message the client
1027 m_menu->Enable(MP_SENDMESSAGE, (client->GetIP() != 0));
1029 m_menu->Enable(MP_SHOWLIST, !client->HasDisabledSharedFiles());
1031 PopupMenu(m_menu, evt.GetPoint());
1035 delete m_menu;
1036 m_menu = NULL;
1041 void CDownloadListCtrl::OnMouseMiddleClick(wxListEvent& evt)
1043 // Check if clicked item is selected. If not, unselect all and select it.
1044 long index = CheckSelection(evt);
1045 if ( index < 0 ) {
1046 return;
1049 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData( index );
1051 if ( item->GetType() == FILE_TYPE ) {
1052 CFileDetailDialog(this, item->GetFile()).ShowModal();
1053 } else {
1054 CClientDetailDialog(this, item->GetSource()).ShowModal();
1059 void CDownloadListCtrl::OnKeyPressed( wxKeyEvent& event )
1061 // Check if delete was pressed
1062 switch (event.GetKeyCode()) {
1063 case WXK_NUMPAD_DELETE:
1064 case WXK_DELETE: {
1065 wxCommandEvent evt;
1066 OnCancelFile( evt );
1067 break;
1069 case WXK_F2: {
1070 ItemList files = ::GetSelectedItems( this, itFILES );
1071 if (files.size() == 1) {
1072 CPartFile* file = files.front()->GetFile();
1074 // Currently renaming of completed files causes problem with kad
1075 if (file->IsPartFile()) {
1076 wxString strNewName = ::wxGetTextFromUser(
1077 _("Enter new name for this file:"),
1078 _("File rename"), file->GetFileName().GetPrintable());
1080 CPath newName = CPath(strNewName);
1081 if (newName.IsOk() && (newName != file->GetFileName())) {
1082 theApp->sharedfiles->RenameFile(file, newName);
1086 break;
1088 default:
1089 event.Skip();
1094 void CDownloadListCtrl::OnDrawItem(
1095 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
1097 // Don't do any drawing if there's nobody to see it.
1098 if ( !theApp->amuledlg->IsDialogVisible( CamuleDlg::DT_TRANSFER_WND ) ) {
1099 return;
1102 CtrlItem_Struct* content = (CtrlItem_Struct *)GetItemData(item);
1104 // Define text-color and background
1105 if ((content->GetType() == FILE_TYPE) && (highlighted)) {
1106 if (GetFocus()) {
1107 dc->SetBackground(m_hilightBrush);
1108 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1109 } else {
1110 dc->SetBackground(m_hilightUnfocusBrush);
1111 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1113 } else {
1114 dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(
1115 wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
1116 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
1120 // Define the border of the drawn area
1121 if ( highlighted ) {
1122 CMuleColour colour;
1123 if ( ( content->GetType() == FILE_TYPE ) && !GetFocus() ) {
1124 colour = m_hilightUnfocusBrush.GetColour();
1125 } else {
1126 colour = m_hilightBrush.GetColour();
1129 dc->SetPen( *(wxThePenList->FindOrCreatePen(colour.Blend(65), 1, wxSOLID) ));
1130 } else {
1131 dc->SetPen(*wxTRANSPARENT_PEN);
1135 dc->SetBrush( dc->GetBackground() );
1136 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
1138 dc->SetPen(*wxTRANSPARENT_PEN);
1140 if ( content->GetType() == FILE_TYPE && ( !highlighted || !GetFocus() ) ) {
1141 // If we have category, override textforeground with what category tells us.
1142 CPartFile *file = content->GetFile();
1143 if ( file->GetCategory() ) {
1144 dc->SetTextForeground(
1145 CMuleColour(theApp->glob_prefs->GetCatColor(file->GetCategory())) );
1149 // Various constant values we use
1150 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
1151 const int iOffset = 4;
1153 // The starting end ending position of the tree
1154 bool tree_show = false;
1155 int tree_start = 0;
1156 int tree_end = 0;
1158 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
1159 for (int i = 0; i < GetColumnCount(); i++) {
1160 wxListItem listitem;
1161 GetColumn(i, listitem);
1163 if (listitem.GetWidth() > 2*iOffset) {
1164 cur_rec.width = listitem.GetWidth() - 2*iOffset;
1166 // Make a copy of the current rectangle so we can apply specific tweaks
1167 wxRect target_rec = cur_rec;
1168 if ( i == 5 ) {
1169 tree_show = ( listitem.GetWidth() > 0 );
1171 tree_start = cur_rec.x - iOffset;
1172 tree_end = cur_rec.x + iOffset;
1174 // Double the offset to make room for the cirle-marker
1175 target_rec.x += iOffset;
1176 target_rec.width -= iOffset;
1177 } else {
1178 // will ensure that text is about in the middle ;)
1179 target_rec.y += iTextOffset;
1182 // Draw the item
1183 if ( content->GetType() == FILE_TYPE ) {
1184 DrawFileItem(dc, i, target_rec, content);
1185 } else {
1186 DrawSourceItem(dc, i, target_rec, content);
1190 // Increment to the next column
1191 cur_rec.x += listitem.GetWidth();
1194 // Draw tree last so it draws over selected and focus (looks better)
1195 if ( tree_show ) {
1196 // Gather some information
1197 const bool notLast = item + 1 != GetItemCount();
1198 const bool notFirst = item != 0;
1199 const bool hasNext = notLast &&
1200 ((CtrlItem_Struct*)GetItemData(item + 1))->GetType() != FILE_TYPE;
1201 const bool isOpenRoot = content->GetType() == FILE_TYPE &&
1202 (content->GetFile())->ShowSources() &&
1203 ((content->GetFile())->GetStatus() != PS_COMPLETE);
1204 const bool isChild = content->GetType() != FILE_TYPE;
1206 // Might as well calculate these now
1207 const int treeCenter = tree_start + 3;
1208 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
1210 // Set up a new pen for drawing the tree
1211 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
1213 if (isChild) {
1214 // Draw the line to the status bar
1215 dc->DrawLine(tree_end, middle, tree_start + 3, middle);
1217 // Draw the line to the child node
1218 if (hasNext) {
1219 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
1222 // Draw the line back up to parent node
1223 if (notFirst) {
1224 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
1226 } else if ( isOpenRoot ) {
1227 // Draw empty circle
1228 dc->SetBrush(*wxTRANSPARENT_BRUSH);
1230 dc->DrawCircle( treeCenter, middle, 3 );
1232 // Draw the line to the child node if there are any children
1233 if (hasNext) {
1234 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
1242 void CDownloadListCtrl::DrawFileItem( wxDC* dc, int nColumn, const wxRect& rect, CtrlItem_Struct* item ) const
1244 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
1246 const CPartFile* file = item->GetFile();
1248 // Used to contain the contenst of cells that dont need any fancy drawing, just text.
1249 wxString text;
1251 switch (nColumn) {
1252 // Filename
1253 case 0: {
1254 // show no. of partfile in filename column
1255 wxString filename;
1256 if (thePrefs::ShowPartFileNumber()) {
1257 if (file->IsPartFile() && !(file->GetStatus() == PS_COMPLETE)) {
1258 filename = CFormat(wxT("[%s] ")) % file->GetPartMetFileName().RemoveAllExt();
1261 filename += file->GetFileName().GetPrintable();
1263 if (file->HasRating() || file->HasComment()) {
1264 int image = Client_CommentOnly_Smiley;
1265 if (file->HasRating()) {
1266 image = Client_InvalidRating_Smiley + file->UserRating() - 1;
1269 wxASSERT(image >= Client_InvalidRating_Smiley);
1270 wxASSERT(image <= Client_CommentOnly_Smiley);
1272 int imgWidth = 16;
1274 // it's already centered by OnDrawItem() ...
1275 m_ImageList.Draw(image, *dc, rect.GetX(), rect.GetY() - 1,
1276 wxIMAGELIST_DRAW_TRANSPARENT);
1277 dc->DrawText(filename, rect.GetX() + imgWidth + 4, rect.GetY());
1278 } else {
1279 dc->DrawText(filename, rect.GetX(), rect.GetY());
1282 break;
1284 // Filesize
1285 case 1:
1286 text = CastItoXBytes( file->GetFileSize() );
1287 break;
1289 // Transferred
1290 case 2:
1291 text = CastItoXBytes( file->GetTransferred() );
1292 break;
1294 // Completed
1295 case 3:
1296 text = CastItoXBytes( file->GetCompletedSize() );
1297 break;
1299 // Speed
1300 case 4: // speed
1301 if ( file->GetTransferingSrcCount() ) {
1302 text = wxString::Format( wxT("%.1f "), file->GetKBpsDown() ) +
1303 _("kB/s");
1305 break;
1307 case 5: // progress
1309 if (thePrefs::ShowProgBar())
1311 int iWidth = rect.GetWidth() - 2;
1312 int iHeight = rect.GetHeight() - 2;
1314 // DO NOT DRAW IT ALL THE TIME
1315 uint32 dwTicks = GetTickCount();
1317 wxMemoryDC cdcStatus;
1319 if ( item->dwUpdated < dwTicks || !item->status || iWidth != item->status->GetWidth() ) {
1320 if ( item->status == NULL) {
1321 item->status = new wxBitmap(iWidth, iHeight);
1322 } else if ( item->status->GetWidth() != iWidth ) {
1323 // Only recreate if the size has changed
1324 item->status->Create(iWidth, iHeight);
1327 cdcStatus.SelectObject( *item->status );
1329 if ( thePrefs::UseFlatBar() ) {
1330 DrawFileStatusBar( file, &cdcStatus,
1331 wxRect(0, 0, iWidth, iHeight), true);
1332 } else {
1333 DrawFileStatusBar( file, &cdcStatus,
1334 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
1336 // Draw black border
1337 cdcStatus.SetPen( *wxBLACK_PEN );
1338 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
1339 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
1342 item->dwUpdated = dwTicks + 5000; // Plus five seconds
1343 } else {
1344 cdcStatus.SelectObject( *item->status );
1347 dc->Blit( rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
1350 if (thePrefs::ShowPercent()) {
1351 // Percentage of completing
1352 // We strip anything below the first decimal point,
1353 // to avoid Format doing roundings
1354 float percent = floor( file->GetPercentCompleted() * 10.0f ) / 10.0f;
1356 wxString buffer = wxString::Format( wxT("%.1f%%"), percent );
1357 int middlex = (2*rect.GetX() + rect.GetWidth()) >> 1;
1358 int middley = (2*rect.GetY() + rect.GetHeight()) >> 1;
1360 wxCoord textwidth, textheight;
1362 dc->GetTextExtent(buffer, &textwidth, &textheight);
1363 wxColour AktColor = dc->GetTextForeground();
1364 if (thePrefs::ShowProgBar()) {
1365 dc->SetTextForeground(*wxWHITE);
1366 } else {
1367 dc->SetTextForeground(*wxBLACK);
1369 dc->DrawText(buffer, middlex - (textwidth >> 1), middley - (textheight >> 1));
1370 dc->SetTextForeground(AktColor);
1373 break;
1375 // Sources
1376 case 6: {
1377 uint16 sc = file->GetSourceCount();
1378 uint16 ncsc = file->GetNotCurrentSourcesCount();
1379 if ( ncsc ) {
1380 text = wxString::Format( wxT("%i/%i" ), sc - ncsc, sc );
1381 } else {
1382 text = wxString::Format( wxT("%i"), sc );
1385 if ( file->GetSrcA4AFCount() ) {
1386 text += wxString::Format( wxT("+%i"), file->GetSrcA4AFCount() );
1389 if ( file->GetTransferingSrcCount() ) {
1390 text += wxString::Format( wxT(" (%i)"), file->GetTransferingSrcCount() );
1393 break;
1396 // Priority
1397 case 7:
1398 text = PriorityToStr( file->GetDownPriority(), file->IsAutoDownPriority() );
1399 break;
1401 // File-status
1402 case 8:
1403 text = file->getPartfileStatus();
1404 break;
1406 // Remaining
1407 case 9: {
1408 if ((file->GetStatus() != PS_COMPLETING) && file->IsPartFile()) {
1409 uint64 remainSize = file->GetFileSize() - file->GetCompletedSize();
1410 sint32 remainTime = file->getTimeRemaining();
1412 if (remainTime >= 0) {
1413 text = CastSecondsToHM(remainTime);
1414 } else {
1415 text = _("Unknown");
1418 text += wxT(" (") + CastItoXBytes(remainSize) + wxT(")");
1420 break;
1423 // Last seen completed
1424 case 10: {
1425 if ( file->lastseencomplete ) {
1426 text = wxDateTime( file->lastseencomplete ).Format( _("%y/%m/%d %H:%M:%S") );
1427 } else {
1428 text = _("Unknown");
1430 break;
1433 // Laste received
1434 case 11: {
1435 const time_t lastReceived = file->GetLastChangeDatetime();
1436 if (lastReceived) {
1437 text = wxDateTime(lastReceived).Format( _("%y/%m/%d %H:%M:%S") );
1438 } else {
1439 text = _("Unknown");
1442 } // switch
1444 if ( !text.IsEmpty() ) {
1445 dc->DrawText( text, rect.GetX(), rect.GetY() );
1450 void CDownloadListCtrl::DrawSourceItem(
1451 wxDC* dc, int nColumn, const wxRect& rect, CtrlItem_Struct* item ) const
1453 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
1454 wxString buffer;
1456 const CUpDownClient* client = item->GetSource();
1458 switch (nColumn) {
1459 // Client name + various icons
1460 case 0: {
1461 wxRect cur_rec = rect;
1462 // +3 is added by OnDrawItem()... so take it off
1463 // Kry - eMule says +1, so I'm trusting it
1464 wxPoint point( cur_rec.GetX(), cur_rec.GetY()+1 );
1466 if (item->GetType() != A4AF_SOURCE) {
1467 uint8 image = 0;
1469 switch (client->GetDownloadState()) {
1470 case DS_CONNECTING:
1471 case DS_CONNECTED:
1472 case DS_WAITCALLBACK:
1473 case DS_TOOMANYCONNS:
1474 image = Client_Red_Smiley;
1475 break;
1476 case DS_ONQUEUE:
1477 if (client->IsRemoteQueueFull()) {
1478 image = Client_Grey_Smiley;
1479 } else {
1480 image = Client_Yellow_Smiley;
1482 break;
1483 case DS_DOWNLOADING:
1484 case DS_REQHASHSET:
1485 image = Client_Green_Smiley;
1486 break;
1487 case DS_NONEEDEDPARTS:
1488 case DS_LOWTOLOWIP:
1489 image = Client_Grey_Smiley;
1490 break;
1491 default: // DS_NONE i.e.
1492 image = Client_White_Smiley;
1495 m_ImageList.Draw(image, *dc, point.x, point.y,
1496 wxIMAGELIST_DRAW_TRANSPARENT);
1497 } else {
1498 m_ImageList.Draw(Client_Grey_Smiley, *dc, point.x, point.y,
1499 wxIMAGELIST_DRAW_TRANSPARENT);
1502 cur_rec.x += 20;
1503 wxPoint point2( cur_rec.GetX(), cur_rec.GetY() + 1 );
1505 uint8 clientImage;
1507 if ( client->IsFriend() ) {
1508 clientImage = Client_Friend_Smiley;
1509 } else {
1510 switch ( client->GetClientSoft() ) {
1511 case SO_AMULE:
1512 clientImage = Client_aMule_Smiley;
1513 break;
1514 case SO_MLDONKEY:
1515 case SO_NEW_MLDONKEY:
1516 case SO_NEW2_MLDONKEY:
1517 clientImage = Client_mlDonkey_Smiley;
1518 break;
1519 case SO_EDONKEY:
1520 case SO_EDONKEYHYBRID:
1521 clientImage = Client_eDonkeyHybrid_Smiley;
1522 break;
1523 case SO_EMULE:
1524 clientImage = Client_eMule_Smiley;
1525 break;
1526 case SO_LPHANT:
1527 clientImage = Client_lphant_Smiley;
1528 break;
1529 case SO_SHAREAZA:
1530 case SO_NEW_SHAREAZA:
1531 case SO_NEW2_SHAREAZA:
1532 clientImage = Client_Shareaza_Smiley;
1533 break;
1534 case SO_LXMULE:
1535 clientImage = Client_xMule_Smiley;
1536 break;
1537 default:
1538 // cDonkey, Compatible, Unknown
1539 // No icon for those yet.
1540 // Using the eMule one + '?'
1541 clientImage = Client_Unknown;
1542 break;
1546 m_ImageList.Draw(clientImage, *dc, point2.x, point.y,
1547 wxIMAGELIST_DRAW_TRANSPARENT);
1549 if (client->GetScoreRatio() > 1) {
1550 // Has credits, draw the gold star
1551 m_ImageList.Draw(Client_CreditsYellow_Smiley, *dc, point2.x, point.y,
1552 wxIMAGELIST_DRAW_TRANSPARENT );
1553 } else if ( client->ExtProtocolAvailable() ) {
1554 // Ext protocol -> Draw the '+'
1555 m_ImageList.Draw(Client_ExtendedProtocol_Smiley, *dc, point2.x, point.y,
1556 wxIMAGELIST_DRAW_TRANSPARENT);
1559 if (client->IsIdentified()) {
1560 // the 'v'
1561 m_ImageList.Draw(Client_SecIdent_Smiley, *dc, point2.x, point.y,
1562 wxIMAGELIST_DRAW_TRANSPARENT);
1563 } else if (client->IsBadGuy()) {
1564 // the 'X'
1565 m_ImageList.Draw(Client_BadGuy_Smiley, *dc, point2.x, point.y,
1566 wxIMAGELIST_DRAW_TRANSPARENT);
1569 if (client->HasObfuscatedConnectionBeenEstablished()) {
1570 // the "¿" except it's a key
1571 m_ImageList.Draw(Client_Encryption_Smiley, *dc, point2.x, point.y,
1572 wxIMAGELIST_DRAW_TRANSPARENT);
1575 wxString userName;
1576 #ifdef ENABLE_IP2COUNTRY
1577 // Draw the flag
1578 const CountryData& countrydata = theApp->amuledlg->m_IP2Country->GetCountryData(client->GetFullIP());
1579 dc->DrawBitmap(countrydata.Flag,
1580 rect.x + 40, rect.y + 5,
1581 wxIMAGELIST_DRAW_TRANSPARENT);
1583 userName << countrydata.Name;
1585 userName << wxT(" - ");
1586 #endif // ENABLE_IP2COUNTRY
1587 if (client->GetUserName().IsEmpty()) {
1588 userName << wxT("?");
1589 } else {
1590 userName << client->GetUserName();
1592 dc->DrawText(userName, rect.GetX() + 60, rect.GetY());
1594 break;
1596 case 3: // completed
1597 if (item->GetType() != A4AF_SOURCE && client->GetTransferredDown()) {
1598 buffer = CastItoXBytes(client->GetTransferredDown());
1599 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1601 break;
1603 case 4: // speed
1604 if (item->GetType() != A4AF_SOURCE && client->GetKBpsDown() > 0.001) {
1605 buffer = wxString::Format(wxT("%.1f "),
1606 client->GetKBpsDown()) + _("kB/s");
1607 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1609 break;
1611 case 5: // file info
1612 if ( thePrefs::ShowProgBar() ) {
1613 int iWidth = rect.GetWidth() - 2;
1614 int iHeight = rect.GetHeight() - 2;
1616 if ( item->GetType() != A4AF_SOURCE ) {
1617 uint32 dwTicks = GetTickCount();
1619 wxMemoryDC cdcStatus;
1621 if ( item->dwUpdated < dwTicks || !item->status ||
1622 iWidth != item->status->GetWidth() ) {
1624 if (item->status == NULL) {
1625 item->status = new wxBitmap(iWidth, iHeight);
1626 } else if ( item->status->GetWidth() != iWidth ) {
1627 // Only recreate if size has changed
1628 item->status->Create(iWidth, iHeight);
1631 cdcStatus.SelectObject(*(item->status));
1633 if ( thePrefs::UseFlatBar() ) {
1634 DrawSourceStatusBar( client, &cdcStatus,
1635 wxRect(0, 0, iWidth, iHeight), true);
1636 } else {
1637 DrawSourceStatusBar( client, &cdcStatus,
1638 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
1640 // Draw black border
1641 cdcStatus.SetPen( *wxBLACK_PEN );
1642 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
1643 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
1646 // Plus ten seconds
1647 item->dwUpdated = dwTicks + 10000;
1648 } else {
1649 cdcStatus.SelectObject(*(item->status));
1652 dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
1653 } else {
1654 buffer = _("A4AF");
1656 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
1657 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
1659 wxCoord txtwidth, txtheight;
1661 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
1663 dc->SetTextForeground(*wxBLACK);
1664 dc->DrawText(buffer, midx - (txtwidth >> 1), midy - (txtheight >> 1));
1666 // Draw black border
1667 dc->SetPen( *wxBLACK_PEN );
1668 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1669 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
1672 break;
1674 case 6: {
1675 // Version
1676 dc->DrawText(client->GetClientVerString(), rect.GetX(), rect.GetY());
1677 break;
1680 case 7: // prio
1681 // We only show priority for sources actually queued for that file
1682 if ( item->GetType() != A4AF_SOURCE &&
1683 client->GetDownloadState() == DS_ONQUEUE ) {
1684 if (client->IsRemoteQueueFull()) {
1685 buffer = _("Queue Full");
1686 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1687 } else {
1688 if (client->GetRemoteQueueRank()) {
1689 sint16 qrDiff = client->GetRemoteQueueRank() -
1690 client->GetOldRemoteQueueRank();
1691 if(qrDiff == client->GetRemoteQueueRank() ) {
1692 qrDiff = 0;
1694 wxColour savedColour = dc->GetTextForeground();
1695 if( qrDiff < 0 ) {
1696 dc->SetTextForeground(*wxBLUE);
1698 if( qrDiff > 0 ) {
1699 dc->SetTextForeground(*wxRED);
1701 //if( qrDiff == 0 ) {
1702 // dc->SetTextForeground(*wxLIGHT_GREY);
1704 buffer = wxString::Format(_("QR: %u (%i)"),
1705 client->GetRemoteQueueRank(), qrDiff);
1706 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1707 dc->SetTextForeground(savedColour);
1711 break;
1713 case 8: // status
1714 if (item->GetType() != A4AF_SOURCE) {
1715 buffer = DownloadStateToStr( client->GetDownloadState(),
1716 client->IsRemoteQueueFull() );
1717 } else {
1718 buffer = _("Asked for another file");
1719 if ( client->GetRequestFile() &&
1720 client->GetRequestFile()->GetFileName().IsOk()) {
1721 buffer += CFormat(wxT(" (%s)"))
1722 % client->GetRequestFile()->GetFileName();
1725 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1726 break;
1727 // Source comes from?
1728 case 9: {
1729 buffer = wxGetTranslation(OriginToText(client->GetSourceFrom()));
1730 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1731 break;
1737 wxString CDownloadListCtrl::GetTTSText(unsigned item) const
1739 CtrlItem_Struct* content = (CtrlItem_Struct*)GetItemData(item);
1741 if (content->GetType() == FILE_TYPE) {
1742 CPartFile* file = content->GetFile();
1744 return file->GetFileName().GetPrintable();
1747 return wxEmptyString;
1751 int CDownloadListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1753 CtrlItem_Struct* item1 = (CtrlItem_Struct*)param1;
1754 CtrlItem_Struct* item2 = (CtrlItem_Struct*)param2;
1756 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1757 sortData &= CMuleListCtrl::COLUMN_MASK;
1758 int comp = 0;
1760 if ( item1->GetType() == FILE_TYPE ) {
1761 if ( item2->GetType() == FILE_TYPE ) {
1762 // Both are files, so we just compare them
1763 comp = Compare( item1->GetFile(), item2->GetFile(), sortData);
1764 } else {
1765 // A file and a source, checking if they belong to each other
1766 if ( item1->GetFile() == item2->GetOwner() ) {
1767 // A file should always be above its sources
1768 // Returning directly to avoid the modifier
1769 return -1;
1770 } else {
1771 // Source belongs to anther file, so we compare the files instead
1772 comp = Compare( item1->GetFile(), item2->GetOwner(), sortData);
1775 } else {
1776 if ( item2->GetType() == FILE_TYPE ) {
1777 // A source and a file, checking if they belong to each other
1778 if ( item1->GetOwner() == item2->GetFile() ) {
1779 // A source should always be below its file
1780 // Returning directly to avoid the modifier
1781 return 1;
1782 } else {
1783 // Source belongs to anther file, so we compare the files instead
1784 comp = Compare( item1->GetOwner(), item2->GetFile(), sortData);
1786 } else {
1787 // Two sources, some different possibilites
1788 if ( item1->GetOwner() == item2->GetOwner() ) {
1789 // Avilable sources first, if we have both an
1790 // available and an unavailable
1791 comp = ( item2->GetType() - item1->GetType() );
1793 if (comp) {
1794 // A4AF and non-A4AF. The order is fixed regardless of sort-order.
1795 return comp;
1796 } else {
1797 comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
1799 } else {
1800 // Belongs to different files, so we compare the files
1801 comp = Compare( item1->GetOwner(), item2->GetOwner(), sortData);
1806 // We modify the result so that it matches with ascending or decending
1807 return sortMod * comp;
1811 int CDownloadListCtrl::Compare( const CPartFile* file1, const CPartFile* file2, long lParamSort)
1813 int result = 0;
1815 switch (lParamSort) {
1816 // Sort by filename
1817 case 0:
1818 result = CmpAny(
1819 file1->GetFileName(),
1820 file2->GetFileName() );
1821 break;
1823 // Sort by size
1824 case 1:
1825 result = CmpAny(
1826 file1->GetFileSize(),
1827 file2->GetFileSize() );
1828 break;
1830 // Sort by transferred
1831 case 2:
1832 result = CmpAny(
1833 file1->GetTransferred(),
1834 file2->GetTransferred() );
1835 break;
1837 // Sort by completed
1838 case 3:
1839 result = CmpAny(
1840 file1->GetCompletedSize(),
1841 file2->GetCompletedSize() );
1842 break;
1844 // Sort by speed
1845 case 4:
1846 result = CmpAny(
1847 file1->GetKBpsDown() * 1024,
1848 file2->GetKBpsDown() * 1024 );
1849 break;
1851 // Sort by percentage completed
1852 case 5:
1853 result = CmpAny(
1854 file1->GetPercentCompleted(),
1855 file2->GetPercentCompleted() );
1856 break;
1858 // Sort by number of sources
1859 case 6:
1860 result = CmpAny(
1861 file1->GetSourceCount(),
1862 file2->GetSourceCount() );
1863 break;
1865 // Sort by priority
1866 case 7:
1867 result = CmpAny(
1868 file1->GetDownPriority(),
1869 file2->GetDownPriority() );
1870 break;
1872 // Sort by status
1873 case 8:
1874 result = CmpAny(
1875 file1->getPartfileStatusRang(),
1876 file2->getPartfileStatusRang() );
1877 break;
1879 // Sort by remaining time
1880 case 9:
1881 if (file1->getTimeRemaining() == -1) {
1882 if (file2->getTimeRemaining() == -1) {
1883 result = 0;
1884 } else {
1885 result = -1;
1887 } else {
1888 if (file2->getTimeRemaining() == -1) {
1889 result = 1;
1890 } else {
1891 result = CmpAny(
1892 file1->getTimeRemaining(),
1893 file2->getTimeRemaining() );
1896 break;
1898 // Sort by last seen complete
1899 case 10:
1900 result = CmpAny(
1901 file1->lastseencomplete,
1902 file2->lastseencomplete );
1903 break;
1905 // Sort by last reception
1906 case 11:
1907 result = CmpAny(
1908 file1->GetLastChangeDatetime(),
1909 file2->GetLastChangeDatetime() );
1910 break;
1913 return result;
1917 int CDownloadListCtrl::Compare(
1918 const CUpDownClient* client1, const CUpDownClient* client2, long lParamSort)
1920 switch (lParamSort) {
1921 // Sort by name
1922 case 0:
1923 return CmpAny( client1->GetUserName(), client2->GetUserName() );
1925 // Sort by status (size field)
1926 case 1:
1927 return CmpAny( client1->GetDownloadState(), client2->GetDownloadState() );
1929 // Sort by transferred in the following fields
1930 case 2:
1931 case 3:
1932 return CmpAny( client1->GetTransferredDown(), client2->GetTransferredDown() );
1934 // Sort by speed
1935 case 4:
1936 return CmpAny( client1->GetKBpsDown(), client2->GetKBpsDown() );
1938 // Sort by parts offered (Progress field)
1939 case 5:
1940 return CmpAny(
1941 client1->GetAvailablePartCount(),
1942 client2->GetAvailablePartCount() );
1944 // Sort by client version
1945 case 6: {
1946 if (client1->GetClientSoft() != client2->GetClientSoft()) {
1947 return client1->GetSoftStr().Cmp(client2->GetSoftStr());
1950 if (client1->GetVersion() != client2->GetVersion()) {
1951 return CmpAny(client1->GetVersion(), client2->GetVersion());
1954 return client1->GetClientModString().Cmp(client2->GetClientModString());
1957 // Sort by Queue-Rank
1958 case 7: {
1959 // This will sort by download state: Downloading, OnQueue, Connecting ...
1960 // However, Asked For Another will always be placed last, due to
1961 // sorting in SortProc
1962 if ( client1->GetDownloadState() != client2->GetDownloadState() ) {
1963 return client1->GetDownloadState() - client2->GetDownloadState();
1966 // Placing items on queue before items on full queues
1967 if ( client1->IsRemoteQueueFull() ) {
1968 if ( client2->IsRemoteQueueFull() ) {
1969 return 0;
1970 } else {
1971 return 1;
1973 } else if ( client2->IsRemoteQueueFull() ) {
1974 return -1;
1975 } else {
1976 if ( client1->GetRemoteQueueRank() ) {
1977 if ( client2->GetRemoteQueueRank() ) {
1978 return CmpAny(
1979 client1->GetRemoteQueueRank(),
1980 client2->GetRemoteQueueRank() );
1981 } else {
1982 return -1;
1984 } else {
1985 if ( client2->GetRemoteQueueRank() ) {
1986 return 1;
1987 } else {
1988 return 0;
1994 // Sort by state
1995 case 8: {
1996 if (client1->GetDownloadState() == client2->GetDownloadState()) {
1997 return CmpAny(
1998 client1->IsRemoteQueueFull(),
1999 client2->IsRemoteQueueFull() );
2000 } else {
2001 return CmpAny(
2002 client1->GetDownloadState(),
2003 client2->GetDownloadState() );
2007 // Source of source ;)
2008 case 9:
2009 return CmpAny(client1->GetSourceFrom(), client2->GetSourceFrom());
2011 default:
2012 return 0;
2017 void CDownloadListCtrl::ClearCompleted()
2019 m_completedFiles = false;
2020 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(false);
2022 // Search for completed files
2023 for ( ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ) {
2024 CtrlItem_Struct* item = it->second; ++it;
2026 if ( item->GetType() == FILE_TYPE ) {
2027 CPartFile* file = item->GetFile();
2029 if ( file->IsPartFile() == false ) {
2030 RemoveFile(file);
2037 void CDownloadListCtrl::ShowFilesCount( int diff )
2039 m_filecount += diff;
2041 wxString str = wxString::Format( _("Downloads (%i)"), m_filecount );
2042 wxStaticText* label = CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText );
2044 label->SetLabel( str );
2045 label->GetParent()->Layout();
2051 bool CDownloadListCtrl::ShowItemInCurrentCat(
2052 const CPartFile* file, int newsel ) const
2054 return
2055 ((newsel == 0 && !thePrefs::ShowAllNotCats()) ||
2056 (newsel == 0 && thePrefs::ShowAllNotCats() && file->GetCategory() == 0)) ||
2057 (newsel > 0 && newsel == file->GetCategory());
2060 static const CMuleColour crHave(104, 104, 104);
2061 static const CMuleColour crFlatHave(0, 0, 0);
2063 static const CMuleColour crPending(255, 208, 0);
2064 static const CMuleColour crFlatPending(255, 255, 100);
2066 static const CMuleColour crProgress(0, 224, 0);
2067 static const CMuleColour crFlatProgress(0, 150, 0);
2069 static const CMuleColour crMissing(255, 0, 0);
2071 void CDownloadListCtrl::DrawFileStatusBar(
2072 const CPartFile* file, wxDC* dc, const wxRect& rect, bool bFlat ) const
2074 static CBarShader s_ChunkBar(16);
2076 s_ChunkBar.SetHeight(rect.height);
2077 s_ChunkBar.SetWidth(rect.width);
2078 s_ChunkBar.SetFileSize( file->GetFileSize() );
2079 s_ChunkBar.Set3dDepth( thePrefs::Get3DDepth() );
2081 if ( file->GetStatus() == PS_COMPLETE || file->GetStatus() == PS_COMPLETING ) {
2082 s_ChunkBar.Fill( bFlat ? crFlatProgress : crProgress );
2083 s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
2084 return;
2087 // Part availability ( of missing parts )
2088 const CPartFile::CGapPtrList& gaplist = file->GetGapList();
2089 CPartFile::CGapPtrList::const_iterator it = gaplist.begin();
2090 uint64 lastGapEnd = 0;
2091 CMuleColour colour(crMissing);
2093 for (; it != gaplist.end(); ++it) {
2094 Gap_Struct* gap = *it;
2096 // Start position
2097 uint32 start = ( gap->start / PARTSIZE );
2098 // fill the Have-Part (between this gap and the last)
2099 if (gap->start) {
2100 s_ChunkBar.FillRange(lastGapEnd + 1, gap->start - 1, bFlat ? crFlatHave : crHave);
2102 lastGapEnd = gap->end;
2103 // End position
2104 uint32 end = ( gap->end / PARTSIZE ) + 1;
2106 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
2107 if ( end > file->GetPartCount() ) {
2108 end = file->GetPartCount();
2111 // Place each gap, one PART at a time
2112 for ( uint64 i = start; i < end; ++i ) {
2113 if ( i < file->m_SrcpartFrequency.size() && file->m_SrcpartFrequency[i]) {
2114 int blue = 210 - ( 22 * ( file->m_SrcpartFrequency[i] - 1 ) );
2115 colour.Set(0, ( blue < 0 ? 0 : blue ), 255 );
2118 if ( file->IsStopped() ) {
2119 colour.Blend(50);
2122 uint64 gap_begin = ( i == start ? gap->start : PARTSIZE * i );
2123 uint64 gap_end = ( i == end - 1 ? gap->end : PARTSIZE * ( i + 1 ) - 1 );
2125 s_ChunkBar.FillRange( gap_begin, gap_end, colour);
2129 // fill the last Have-Part (between this gap and the last)
2130 s_ChunkBar.FillRange(lastGapEnd + 1, file->GetFileSize() - 1, bFlat ? crFlatHave : crHave);
2132 // Pending parts
2133 const CPartFile::CReqBlockPtrList& requestedblocks_list = file->GetRequestedBlockList();
2134 CPartFile::CReqBlockPtrList::const_iterator it2 = requestedblocks_list.begin();
2135 // adjacing pending parts must be joined to avoid bright lines between them
2136 uint64 lastStartOffset = 0;
2137 uint64 lastEndOffset = 0;
2139 colour = bFlat ? crFlatPending : crPending;
2141 if ( file->IsStopped() ) {
2142 colour.Blend(50);
2145 for (; it2 != requestedblocks_list.end(); ++it2) {
2147 if ((*it2)->StartOffset > lastEndOffset + 1) {
2148 // not adjacing, draw last block
2149 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
2150 lastStartOffset = (*it2)->StartOffset;
2151 lastEndOffset = (*it2)->EndOffset;
2152 } else {
2153 // adjacing, grow block
2154 lastEndOffset = (*it2)->EndOffset;
2158 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
2161 // Draw the progress-bar
2162 s_ChunkBar.Draw( dc, rect.x, rect.y, bFlat );
2165 // Green progressbar width
2166 int width = (int)(( (float)rect.width / (float)file->GetFileSize() ) *
2167 file->GetCompletedSize() );
2169 if ( bFlat ) {
2170 dc->SetBrush( *(wxTheBrushList->FindOrCreateBrush(crFlatProgress , wxSOLID ) ));
2172 dc->DrawRectangle( rect.x, rect.y, width, 3 );
2173 } else {
2174 // Draw the two black lines for 3d-effect
2175 dc->SetPen( *wxBLACK_PEN );
2176 dc->DrawLine( rect.x, rect.y + 0, rect.x + width, rect.y + 0 );
2177 dc->DrawLine( rect.x, rect.y + 2, rect.x + width, rect.y + 2 );
2179 // Draw the green line
2180 dc->SetPen( *(wxThePenList->FindOrCreatePen( crProgress , 1, wxSOLID ) ));
2181 dc->DrawLine( rect.x, rect.y + 1, rect.x + width, rect.y + 1 );
2185 static const CMuleColour crBoth(0, 192, 0);
2186 static const CMuleColour crFlatBoth(0, 150, 0);
2188 static const CMuleColour crNeither(240, 240, 240);
2189 static const CMuleColour crFlatNeither(224, 224, 224);
2191 #define crClientOnly crHave
2192 #define crFlatClientOnly crFlatHave
2193 #define crNextPending crFlatPending
2195 void CDownloadListCtrl::DrawSourceStatusBar(
2196 const CUpDownClient* source, wxDC* dc, const wxRect& rect, bool bFlat) const
2198 static CBarShader s_StatusBar(16);
2200 CPartFile* reqfile = source->GetRequestFile();
2202 s_StatusBar.SetFileSize( reqfile->GetFileSize() );
2203 s_StatusBar.SetHeight(rect.height);
2204 s_StatusBar.SetWidth(rect.width);
2205 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
2207 // Barry - was only showing one part from client, even when reserved bits from 2 parts
2208 wxString gettingParts = source->ShowDownloadingParts();
2210 const BitVector& partStatus = source->GetPartStatus();
2212 uint64 uEnd = 0;
2213 for ( uint64 i = 0; i < partStatus.size(); i++ ) {
2214 uint64 uStart = PARTSIZE * i;
2215 uEnd = wxMin(reqfile->GetFileSize(), uStart + PARTSIZE) - 1;
2217 CMuleColour colour;
2218 if (!partStatus[i]) {
2219 colour = bFlat ? crFlatNeither : crNeither;
2220 } else if ( reqfile->IsComplete(uStart, uEnd)) {
2221 colour = bFlat ? crFlatBoth : crBoth;
2222 } else if ( source->GetDownloadState() == DS_DOWNLOADING &&
2223 source->GetLastBlockOffset() <= uEnd &&
2224 source->GetLastBlockOffset() >= uStart) {
2225 colour = crPending;
2226 } else if (gettingParts.GetChar((uint16)i) == 'Y') {
2227 colour = crNextPending;
2228 } else {
2229 colour = bFlat ? crFlatClientOnly : crClientOnly;
2232 if ( source->GetRequestFile()->IsStopped() ) {
2233 colour.Blend(50);
2236 s_StatusBar.FillRange(uStart, uEnd, colour);
2238 // fill the rest (if partStatus is empty)
2239 s_StatusBar.FillRange(uEnd + 1, reqfile->GetFileSize() - 1, bFlat ? crFlatNeither : crNeither);
2241 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
2244 #ifdef __WXMSW__
2245 # define QUOTE wxT("\"")
2246 #else
2247 # define QUOTE wxT("\'")
2248 #endif
2250 void CDownloadListCtrl::PreviewFile(CPartFile* file)
2252 wxString command;
2253 // If no player set in preferences, use mplayer.
2254 // And please, do a warning also :P
2255 if (thePrefs::GetVideoPlayer().IsEmpty()) {
2256 wxMessageBox(_(
2257 "To prevent this warning to show up in every preview,\nset your preferred video player in preferences (default is mplayer)."),
2258 _("File preview"), wxOK, this);
2259 // Since newer versions for some reason mplayer does not automatically
2260 // select video output device and needs a parameter, go figure...
2261 command = wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE wxT("$file") QUOTE;
2262 } else {
2263 command = thePrefs::GetVideoPlayer();
2266 // Check if we are (pre)viewing a completed file or not
2267 if (file->GetStatus() != PS_COMPLETE) {
2268 // Remove the .met and see if out video player specifiation uses the magic string
2269 wxString fileWithoutMet = thePrefs::GetTempDir().JoinPaths(
2270 file->GetPartMetFileName().RemoveExt()).GetRaw();
2271 if (!command.Replace(wxT("$file"), fileWithoutMet)) {
2272 // No magic string, so we just append the filename to the player command
2273 // Need to use quotes in case filename contains spaces
2274 command << wxT(" ") << QUOTE << fileWithoutMet << QUOTE;
2276 } else {
2277 // This is a complete file
2278 // FIXME: This is probably not going to work if the filenames are mangled ...
2279 wxString rawFileName = file->GetFullName().GetRaw();
2280 if (!command.Replace(wxT("$file"), rawFileName)) {
2281 // No magic string, so we just append the filename to the player command
2282 // Need to use quotes in case filename contains spaces
2283 command << wxT(" ") << QUOTE << rawFileName << QUOTE;
2287 // We can't use wxShell here, it blocks the app
2288 CTerminationProcess *p = new CTerminationProcess(command);
2289 int ret = wxExecute(command, wxEXEC_ASYNC, p);
2290 bool ok = ret > 0;
2291 if (!ok) {
2292 delete p;
2293 AddLogLineM( true,
2294 CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
2295 command );
2298 // File_checked_for_headers