Upstream tarball 20080522
[amule.git] / src / DownloadListCtrl.cpp
blob0fc2d92cb65f83eb767405cd7fe400358904e554
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 wxColour colour = BLEND( SYSCOLOR( wxSYS_COLOUR_HIGHLIGHT ), 125 );
184 m_hilightBrush = new wxBrush( colour, wxSOLID );
186 colour = BLEND( SYSCOLOR( wxSYS_COLOUR_BTNSHADOW), 125 );
187 m_hilightUnfocusBrush = new wxBrush( colour, wxSOLID );
189 InsertColumn( 0, _("File Name"), wxLIST_FORMAT_LEFT, 260 );
190 InsertColumn( 1, _("Size"), wxLIST_FORMAT_LEFT, 60 );
191 InsertColumn( 2, _("Transferred"), wxLIST_FORMAT_LEFT, 65 );
192 InsertColumn( 3, _("Completed"), wxLIST_FORMAT_LEFT, 65 );
193 InsertColumn( 4, _("Speed"), wxLIST_FORMAT_LEFT, 65 );
194 InsertColumn( 5, _("Progress"), wxLIST_FORMAT_LEFT, 170 );
195 InsertColumn( 6, _("Sources"), wxLIST_FORMAT_LEFT, 50 );
196 InsertColumn( 7, _("Priority"), wxLIST_FORMAT_LEFT, 55 );
197 InsertColumn( 8, _("Status"), wxLIST_FORMAT_LEFT, 70 );
198 InsertColumn( 9, _("Time Remaining"), wxLIST_FORMAT_LEFT, 110 );
199 InsertColumn( 10, _("Last Seen Complete"), wxLIST_FORMAT_LEFT, 220 );
200 InsertColumn( 11, _("Last Reception"), wxLIST_FORMAT_LEFT, 220 );
202 m_category = 0;
203 m_completedFiles = 0;
204 m_filecount = 0;
205 LoadSettings();
209 CDownloadListCtrl::~CDownloadListCtrl()
211 while ( !m_ListItems.empty() ) {
212 delete m_ListItems.begin()->second;
213 m_ListItems.erase( m_ListItems.begin() );
215 delete m_hilightBrush;
216 delete m_hilightUnfocusBrush;
220 void CDownloadListCtrl::AddFile( CPartFile* file )
222 wxASSERT( file );
224 // Avoid duplicate entries of files
225 if ( m_ListItems.find( file ) == m_ListItems.end() ) {
226 CtrlItem_Struct* newitem = new CtrlItem_Struct;
227 newitem->SetContents(file);
229 m_ListItems.insert( ListItemsPair( file, newitem ) );
231 // Check if the new file is visible in the current category
232 if ( file->CheckShowItemInGivenCat( m_category ) ) {
233 ShowFile( file, true );
239 void CDownloadListCtrl::AddSource(CPartFile* owner, CUpDownClient* source, DownloadItemType type)
241 wxCHECK_RET(owner, wxT("NULL owner in CDownloadListCtrl::AddSource"));
242 wxCHECK_RET(source, wxT("NULL source in CDownloadListCtrl::AddSource"));
243 wxCHECK_RET(type != FILE_TYPE, wxT("Invalid type, not a source"));
245 // Update the other instances of this source
246 bool bFound = false;
247 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
248 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
249 CtrlItem_Struct* cur_item = it->second;
251 // Check if this source has been already added to this file => to be sure
252 if ( cur_item->GetOwner() == owner ) {
253 // Update this instance with its new setting
254 cur_item->SetContents(owner, source, type);
255 cur_item->dwUpdated = 0;
256 bFound = true;
257 } else if ( type == AVAILABLE_SOURCE ) {
258 // The state 'Available' is exclusive
259 cur_item->SetContents(cur_item->GetOwner(), source, A4AF_SOURCE);
260 cur_item->dwUpdated = 0;
264 if ( bFound ) {
265 return;
268 if ( owner->ShowSources() ) {
269 CtrlItem_Struct* newitem = new CtrlItem_Struct;
270 newitem->SetContents(owner, source, type);
272 m_ListItems.insert( ListItemsPair(source, newitem) );
274 // Find the owner-object
275 ListItems::iterator it = m_ListItems.find( owner );
277 if ( it != m_ListItems.end() ) {
278 long item = FindItem( -1, reinterpret_cast<wxUIntPtr>(it->second) );
280 if ( item > -1 ) {
281 item = InsertItem( item + 1, wxEmptyString );
283 SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
285 // background.. this should be in a function
286 wxListItem listitem;
287 listitem.m_itemId = item;
289 listitem.SetBackgroundColour( GetBackgroundColour() );
291 SetItem( listitem );
298 void CDownloadListCtrl::RemoveSource( const CUpDownClient* source, const CPartFile* owner )
300 wxASSERT( source );
302 // Retrieve all entries matching the source
303 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
305 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ) {
306 ListItems::iterator tmp = it++;
308 CtrlItem_Struct* item = tmp->second;
309 if ( owner == NULL || owner == item->GetOwner() ) {
310 // Remove it from the m_ListItems
311 m_ListItems.erase( tmp );
313 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
315 if ( index > -1 ) {
316 DeleteItem( index );
319 delete item;
325 void CDownloadListCtrl::RemoveFile( CPartFile* file )
327 wxASSERT( file );
329 // Ensure that any assosiated sources and list-entries are removed
330 ShowFile( file, false );
332 // Find the assosiated list-item
333 ListItems::iterator it = m_ListItems.find( file );
335 if ( it != m_ListItems.end() ) {
336 delete it->second;
338 m_ListItems.erase( it );
343 void CDownloadListCtrl::UpdateItem(const void* toupdate)
345 // Retrieve all entries matching the source
346 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
348 // Visible lines, default to all because not all platforms
349 // support the GetVisibleLines function
350 long first = 0, last = GetItemCount();
352 #ifndef __WXMSW__
353 // Get visible lines if we need them
354 if ( rangeIt.first != rangeIt.second ) {
355 GetVisibleLines( &first, &last );
357 #endif
359 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
360 CtrlItem_Struct* item = it->second;
362 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
364 // Determine if the file should be shown in the current category
365 if ( item->GetType() == FILE_TYPE ) {
366 CPartFile* file = item->GetFile();
368 bool show = file->CheckShowItemInGivenCat( m_category );
370 if ( index > -1 ) {
371 if ( show ) {
372 item->dwUpdated = 0;
374 // Only update visible lines
375 if ( index >= first && index <= last) {
376 RefreshItem( index );
378 } else {
379 // Item should no longer be shown in
380 // the current category
381 ShowFile( file, false );
383 } else if ( show ) {
384 // Item has been hidden but new status means
385 // that it should it should be shown in the
386 // current category
387 ShowFile( file, true );
390 if (file->GetStatus() == PS_COMPLETE) {
391 m_completedFiles = true;
393 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(true);
395 } else {
396 item->dwUpdated = 0;
398 // Only update visible lines
399 if ( index >= first && index <= last) {
400 RefreshItem( index );
407 void CDownloadListCtrl::ShowFile( CPartFile* file, bool show )
409 wxASSERT( file );
411 ListItems::iterator it = m_ListItems.find( file );
413 if ( it != m_ListItems.end() ) {
414 CtrlItem_Struct* item = it->second;
416 if ( show ) {
417 // Check if the file is already being displayed
418 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
419 if ( index == -1 ) {
420 long newitem = InsertItem( GetItemCount(), wxEmptyString );
422 SetItemPtrData( newitem, reinterpret_cast<wxUIntPtr>(item) );
424 wxListItem myitem;
425 myitem.m_itemId = newitem;
426 myitem.SetBackgroundColour( GetBackgroundColour() );
428 SetItem(myitem);
430 RefreshItem( newitem );
432 ShowFilesCount( 1 );
434 } else {
435 // Ensure sources are hidden
436 ShowSources( file, false );
438 // Try to find the file and remove it
439 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
440 if ( index > -1 ) {
441 DeleteItem( index );
442 ShowFilesCount( -1 );
449 void CDownloadListCtrl::ShowSources( CPartFile* file, bool show )
451 // Check if the current state is the same as the new state
452 if ( file->ShowSources() == show ) {
453 return;
456 Freeze();
458 file->SetShowSources( show );
460 if ( show ) {
461 const CPartFile::SourceSet& normSources = file->GetSourceList();
462 const CPartFile::SourceSet& a4afSources = file->GetA4AFList();
464 // Adding normal sources
465 CPartFile::SourceSet::const_iterator it;
466 for ( it = normSources.begin(); it != normSources.end(); ++it ) {
467 switch ((*it)->GetDownloadState()) {
468 case DS_DOWNLOADING:
469 case DS_ONQUEUE:
470 AddSource( file, *it, AVAILABLE_SOURCE );
471 default:
472 // Any other state
473 AddSource( file, *it, UNAVAILABLE_SOURCE );
478 // Adding A4AF sources
479 for ( it = a4afSources.begin(); it != a4afSources.end(); ++it ) {
480 AddSource( file, *it, A4AF_SOURCE );
482 } else {
483 for ( int i = GetItemCount() - 1; i >= 0; --i ) {
484 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData(i);
486 if ( item->GetType() != FILE_TYPE && item->GetOwner() == file ) {
487 // Remove from the grand list, this call doesn't remove the source
488 // from the listctrl, because ShowSources is now false. This also
489 // deletes the item.
490 RemoveSource(item->GetSource(), file);
495 Thaw();
499 void CDownloadListCtrl::ChangeCategory( int newCategory )
501 Freeze();
503 // remove all displayed files with a different cat and show the correct ones
504 for (ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++) {
505 const CtrlItem_Struct *cur_item = it->second;
507 if ( cur_item->GetType() == FILE_TYPE ) {
508 CPartFile* file = cur_item->GetFile();
510 bool curVisibility = file->CheckShowItemInGivenCat( m_category );
511 bool newVisibility = file->CheckShowItemInGivenCat( newCategory );
513 // Check if the visibility of the file has changed. However, if the
514 // current category is the default (0) category, then we can't use
515 // curVisiblity to see if the visibility has changed but instead
516 // have to let ShowFile() check if the file is or isn't on the list.
517 if ( curVisibility != newVisibility || !newCategory ) {
518 ShowFile( file, newVisibility );
523 Thaw();
525 m_category = newCategory;
529 uint8 CDownloadListCtrl::GetCategory() const
531 return m_category;
538 const int itFILES = 1;
539 const int itSOURCES = 2;
542 * Helper-function: This function is used to gather selected items.
544 * @param list A pointer to the list to gather items from.
545 * @param types The desired types OR'd together.
546 * @return A list containing the selected items of the choosen types.
548 ItemList GetSelectedItems( CDownloadListCtrl* list, int types )
550 ItemList results;
552 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
554 while ( index > -1 ) {
555 CtrlItem_Struct* item = (CtrlItem_Struct*)list->GetItemData( index );
557 bool add = false;
558 add |= ( item->GetType() == FILE_TYPE ) && ( types & itFILES );
559 add |= ( item->GetType() != FILE_TYPE ) && ( types & itSOURCES );
561 if ( add ) {
562 results.push_back( item );
565 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
568 return results;
572 void CDownloadListCtrl::OnCancelFile(wxCommandEvent& WXUNUSED(event))
574 ItemList files = ::GetSelectedItems(this, itFILES);
575 if (files.size()) {
576 wxString question;
577 if (files.size() == 1) {
578 question = _("Are you sure that you wish to delete the selected file?");
579 } else {
580 question = _("Are you sure that you wish to delete the selected files?");
582 if (wxMessageBox( question, _("Cancel"), wxICON_QUESTION | wxYES_NO, this) == wxYES) {
583 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
584 CPartFile* file = (*it)->GetFile();
585 if (file) {
586 switch (file->GetStatus()) {
587 case PS_WAITINGFORHASH:
588 case PS_HASHING:
589 case PS_COMPLETING:
590 case PS_COMPLETE:
591 break;
592 default:
593 CoreNotify_PartFile_Delete(file);
602 void CDownloadListCtrl::OnSetPriority( wxCommandEvent& event )
604 int priority = 0;
605 switch ( event.GetId() ) {
606 case MP_PRIOLOW: priority = PR_LOW; break;
607 case MP_PRIONORMAL: priority = PR_NORMAL; break;
608 case MP_PRIOHIGH: priority = PR_HIGH; break;
609 case MP_PRIOAUTO: priority = PR_AUTO; break;
610 default:
611 wxASSERT( false );
614 ItemList files = ::GetSelectedItems( this, itFILES );
616 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
617 CPartFile* file = (*it)->GetFile();
619 if ( priority == PR_AUTO ) {
620 CoreNotify_PartFile_PrioAuto( file, true );
621 } else {
622 CoreNotify_PartFile_PrioAuto( file, false );
624 CoreNotify_PartFile_PrioSet( file, priority, true );
630 void CDownloadListCtrl::OnSwapSources( wxCommandEvent& event )
632 ItemList files = ::GetSelectedItems( this, itFILES );
634 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
635 CPartFile* file = (*it)->GetFile();
637 switch ( event.GetId() ) {
638 case MP_SWAP_A4AF_TO_THIS:
639 CoreNotify_PartFile_Swap_A4AF( file );
640 break;
642 case MP_SWAP_A4AF_TO_THIS_AUTO:
643 CoreNotify_PartFile_Swap_A4AF_Auto( file );
644 break;
646 case MP_SWAP_A4AF_TO_ANY_OTHER:
647 CoreNotify_PartFile_Swap_A4AF_Others( file );
648 break;
654 void CDownloadListCtrl::OnSetCategory( wxCommandEvent& event )
656 ItemList files = ::GetSelectedItems( this, itFILES );
658 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
659 CPartFile* file = (*it)->GetFile();
661 CoreNotify_PartFile_SetCat( file, event.GetId() - MP_ASSIGNCAT );
664 ChangeCategory( m_category );
668 void CDownloadListCtrl::OnSetStatus( wxCommandEvent& event )
670 ItemList files = ::GetSelectedItems( this, itFILES );
672 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
673 CPartFile* file = (*it)->GetFile();
675 switch ( event.GetId() ) {
676 case MP_PAUSE:
677 CoreNotify_PartFile_Pause( file );
678 break;
680 case MP_RESUME:
681 CoreNotify_PartFile_Resume( file );
682 break;
684 case MP_STOP:
685 ShowSources(file, false);
686 CoreNotify_PartFile_Stop( file );
687 break;
693 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent& WXUNUSED(event) )
695 ClearCompleted();
699 void CDownloadListCtrl::OnGetLink(wxCommandEvent& event)
701 ItemList files = ::GetSelectedItems( this, itFILES );
703 wxString URIs;
705 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
706 CPartFile* file = (*it)->GetFile();
708 if ( event.GetId() == MP_GETED2KLINK ) {
709 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
710 } else {
711 URIs += theApp->CreateMagnetLink( file ) + wxT("\n");
715 if ( !URIs.IsEmpty() ) {
716 theApp->CopyTextToClipboard( URIs.BeforeLast(wxT('\n')) );
721 void CDownloadListCtrl::OnGetFeedback(wxCommandEvent& WXUNUSED(event))
723 wxString feed;
724 ItemList files = ::GetSelectedItems(this, itFILES);
726 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
727 if (feed.IsEmpty()) {
728 feed = CFormat(_("Feedback from: %s (%s)\n\n")) % thePrefs::GetUserNick() % GetFullMuleVersion();
729 } else {
730 feed += wxT("\n");
732 feed += (*it)->GetFile()->GetFeedback();
735 if (!feed.IsEmpty()) {
736 theApp->CopyTextToClipboard(feed);
741 void CDownloadListCtrl::OnGetRazorStats( wxCommandEvent& WXUNUSED(event) )
743 ItemList files = ::GetSelectedItems( this, itFILES );
745 if ( files.size() == 1 ) {
746 CPartFile* file = files.front()->GetFile();
748 theApp->amuledlg->LaunchUrl(
749 wxT("http://stats.razorback2.com/ed2khistory?ed2k=") +
750 file->GetFileHash().Encode());
755 void CDownloadListCtrl::OnViewFileInfo( wxCommandEvent& WXUNUSED(event) )
757 ItemList files = ::GetSelectedItems( this, itFILES );
759 if ( files.size() == 1 ) {
760 CPartFile* file = files.front()->GetFile();
762 CFileDetailDialog dialog( this, file );
763 dialog.ShowModal();
768 void CDownloadListCtrl::OnViewFileComments( wxCommandEvent& WXUNUSED(event) )
770 ItemList files = ::GetSelectedItems( this, itFILES );
772 if ( files.size() == 1 ) {
773 CPartFile* file = files.front()->GetFile();
775 CCommentDialogLst dialog( this, file );
776 dialog.ShowModal();
781 void CDownloadListCtrl::OnPreviewFile( wxCommandEvent& WXUNUSED(event) )
783 ItemList files = ::GetSelectedItems( this, itFILES );
785 if ( files.size() == 1 ) {
786 PreviewFile(files.front()->GetFile());
790 void CDownloadListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
792 ItemList sources = ::GetSelectedItems( this, itSOURCES );
794 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
795 CPartFile* file = (*it)->GetOwner();
796 CUpDownClient* source = (*it)->GetSource();
798 source->SwapToAnotherFile( true, false, false, file );
803 void CDownloadListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
805 ItemList sources = ::GetSelectedItems( this, itSOURCES );
807 if ( sources.size() == 1 ) {
808 CUpDownClient* source = sources.front()->GetSource();
810 source->RequestSharedFileList();
815 void CDownloadListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
817 ItemList sources = ::GetSelectedItems( this, itSOURCES );
819 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
820 CUpDownClient* client = (*it)->GetSource();
821 if (client->IsFriend()) {
822 theApp->amuledlg->m_chatwnd->RemoveFriend(client->GetUserHash(), client->GetIP(), client->GetUserPort());
823 } else {
824 theApp->amuledlg->m_chatwnd->AddFriend( client );
830 void CDownloadListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
832 ItemList sources = ::GetSelectedItems( this, itSOURCES );
834 if ( sources.size() == 1 ) {
835 CUpDownClient* source = (sources.front())->GetSource();
837 // These values are cached, since calling wxGetTextFromUser will
838 // start an event-loop, in which the client may be deleted.
839 wxString userName = source->GetUserName();
840 uint64 userID = GUI_ID(source->GetIP(), source->GetUserPort());
842 wxString message = ::wxGetTextFromUser(_("Send message to user"),
843 _("Message to send:"));
844 if ( !message.IsEmpty() ) {
845 theApp->amuledlg->m_chatwnd->SendMessage(message, userName, userID);
851 void CDownloadListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
853 ItemList sources = ::GetSelectedItems( this, itSOURCES );
855 if ( sources.size() == 1 ) {
856 CUpDownClient* source = (sources.front())->GetSource();
858 CClientDetailDialog dialog( this, source );
859 dialog.ShowModal();
864 void CDownloadListCtrl::OnItemActivated( wxListEvent& evt )
866 CtrlItem_Struct* content = (CtrlItem_Struct*)GetItemData( evt.GetIndex() );
868 if ( content->GetType() == FILE_TYPE ) {
869 CPartFile* file = content->GetFile();
871 if ((!file->IsPartFile() || file->GetStatus() == PS_COMPLETE) && file->PreviewAvailable()) {
872 PreviewFile( file );
873 } else {
874 ShowSources( file, !file->ShowSources() );
881 void CDownloadListCtrl::OnMouseRightClick(wxListEvent& evt)
883 long index = CheckSelection(evt);
884 if (index < 0) {
885 return;
888 delete m_menu;
889 m_menu = NULL;
891 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData( index );
892 if (item->GetType() == FILE_TYPE) {
893 m_menu = new wxMenu( _("Downloads") );
895 wxMenu* priomenu = new wxMenu();
896 priomenu->AppendCheckItem(MP_PRIOLOW, _("Low"));
897 priomenu->AppendCheckItem(MP_PRIONORMAL, _("Normal"));
898 priomenu->AppendCheckItem(MP_PRIOHIGH, _("High"));
899 priomenu->AppendCheckItem(MP_PRIOAUTO, _("Auto"));
901 m_menu->Append(MP_MENU_PRIO, _("Priority"), priomenu);
902 m_menu->Append(MP_CANCEL, _("Cancel"));
903 m_menu->Append(MP_STOP, _("&Stop"));
904 m_menu->Append(MP_PAUSE, _("&Pause"));
905 m_menu->Append(MP_RESUME, _("&Resume"));
906 m_menu->Append(MP_CLEARCOMPLETED, _("C&lear completed"));
907 //-----------------------------------------------------
908 m_menu->AppendSeparator();
909 //-----------------------------------------------------
910 wxMenu* extendedmenu = new wxMenu();
911 extendedmenu->Append(MP_SWAP_A4AF_TO_THIS,
912 _("Swap every A4AF to this file now"));
913 extendedmenu->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO,
914 _("Swap every A4AF to this file (Auto)"));
915 //-----------------------------------------------------
916 extendedmenu->AppendSeparator();
917 //-----------------------------------------------------
918 extendedmenu->Append(MP_SWAP_A4AF_TO_ANY_OTHER,
919 _("Swap every A4AF to any other file now"));
920 //-----------------------------------------------------
921 m_menu->Append(MP_MENU_EXTD,
922 _("Extended Options"), extendedmenu);
923 //-----------------------------------------------------
924 m_menu->AppendSeparator();
925 //-----------------------------------------------------
926 /* Commented out till RB2 is back
927 m_menu->Append( MP_RAZORSTATS,
928 _("Get Razorback 2's stats for this file"));
929 //-----------------------------------------------------
930 m_menu->AppendSeparator();
931 //-----------------------------------------------------
933 m_menu->Append(MP_VIEW, _("Preview"));
934 m_menu->Append(MP_METINFO, _("Show file &details"));
935 m_menu->Append(MP_VIEWFILECOMMENTS,
936 _("Show all comments"));
937 //-----------------------------------------------------
938 m_menu->AppendSeparator();
939 //-----------------------------------------------------
940 m_menu->Append(MP_GETMAGNETLINK,
941 _("Copy magnet URI to clipboard"));
942 m_menu->Append(MP_GETED2KLINK,
943 _("Copy ED2k &link to clipboard"));
944 m_menu->Append(MP_WS,
945 _("Copy feedback to clipboard"));
946 //-----------------------------------------------------
947 m_menu->AppendSeparator();
948 //-----------------------------------------------------
949 // Add dinamic entries
950 wxMenu *cats = new wxMenu(_("Category"));
951 if (theApp->glob_prefs->GetCatCount() > 1) {
952 for (uint32 i = 0; i < theApp->glob_prefs->GetCatCount(); i++) {
953 if ( i == 0 ) {
954 cats->Append( MP_ASSIGNCAT, _("unassign") );
955 } else {
956 cats->Append( MP_ASSIGNCAT + i,
957 theApp->glob_prefs->GetCategory(i)->title );
961 m_menu->Append(MP_MENU_CATS, _("Assign to category"), cats);
962 m_menu->Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1) );
964 CPartFile* file = item->GetFile();
965 // then set state
966 const bool canStop =
967 (file->GetStatus() != PS_ERROR) &&
968 (file->GetStatus() != PS_COMPLETE) &&
969 (file->IsStopped() != true);
970 const bool canPause =
971 (file->GetStatus() != PS_PAUSED) && canStop;
972 const bool fileResumable =
973 (file->GetStatus() == PS_PAUSED) ||
974 (file->GetStatus() == PS_ERROR) ||
975 (file->GetStatus() == PS_INSUFFICIENT);
977 wxMenu* menu = m_menu;
978 menu->Enable( MP_CANCEL, ( file->GetStatus() != PS_COMPLETE ) );
979 menu->Enable( MP_PAUSE, canPause );
980 menu->Enable( MP_STOP, canStop );
981 menu->Enable( MP_RESUME, fileResumable );
982 menu->Enable( MP_CLEARCOMPLETED, m_completedFiles );
984 wxString view;
985 if (file->IsPartFile() && (file->GetStatus() != PS_COMPLETE)) {
986 view << CFormat(wxT("%s [%s]")) % _("Preview")
987 % file->GetPartMetFileName().RemoveExt();
988 } else if ( file->GetStatus() == PS_COMPLETE ) {
989 view << _("&Open the file");
991 menu->SetLabel(MP_VIEW, view);
992 menu->Enable(MP_VIEW, file->PreviewAvailable() );
994 menu->Check( MP_SWAP_A4AF_TO_THIS_AUTO, file->IsA4AFAuto() );
996 int priority = file->IsAutoDownPriority() ?
997 PR_AUTO : file->GetDownPriority();
999 priomenu->Check( MP_PRIOHIGH, priority == PR_HIGH );
1000 priomenu->Check( MP_PRIONORMAL, priority == PR_NORMAL );
1001 priomenu->Check( MP_PRIOLOW, priority == PR_LOW );
1002 priomenu->Check( MP_PRIOAUTO, priority == PR_AUTO );
1004 menu->Enable( MP_MENU_EXTD, canPause );
1006 PopupMenu(m_menu, evt.GetPoint());
1008 } else {
1009 CUpDownClient* client = item->GetSource();
1011 m_menu = new wxMenu(wxT("Clients"));
1012 m_menu->Append(MP_DETAIL, _("Show &Details"));
1013 m_menu->Append(MP_ADDFRIEND, client->IsFriend() ? _("Remove from friends") : _("Add to Friends"));
1014 m_menu->Append(MP_SHOWLIST, _("View Files"));
1015 m_menu->Append(MP_SENDMESSAGE, _("Send message"));
1016 m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
1018 // Only enable the Swap option for A4AF sources
1019 m_menu->Enable(MP_CHANGE2FILE, (item->GetType() == A4AF_SOURCE));
1020 // We need a valid IP if we are to message the client
1021 m_menu->Enable(MP_SENDMESSAGE, (client->GetIP() != 0));
1023 m_menu->Enable(MP_SHOWLIST, !client->HasDisabledSharedFiles());
1025 PopupMenu(m_menu, evt.GetPoint());
1029 delete m_menu;
1030 m_menu = NULL;
1035 void CDownloadListCtrl::OnMouseMiddleClick(wxListEvent& evt)
1037 // Check if clicked item is selected. If not, unselect all and select it.
1038 long index = CheckSelection(evt);
1039 if ( index < 0 ) {
1040 return;
1043 CtrlItem_Struct* item = (CtrlItem_Struct*)GetItemData( index );
1045 if ( item->GetType() == FILE_TYPE ) {
1046 CFileDetailDialog(this, item->GetFile()).ShowModal();
1047 } else {
1048 CClientDetailDialog(this, item->GetSource()).ShowModal();
1053 void CDownloadListCtrl::OnKeyPressed( wxKeyEvent& event )
1055 // Check if delete was pressed
1056 switch (event.GetKeyCode()) {
1057 case WXK_NUMPAD_DELETE:
1058 case WXK_DELETE: {
1059 wxCommandEvent evt;
1060 OnCancelFile( evt );
1061 break;
1063 case WXK_F2: {
1064 ItemList files = ::GetSelectedItems( this, itFILES );
1065 if (files.size() == 1) {
1066 CPartFile* file = files.front()->GetFile();
1068 // Currently renaming of completed files causes problem with kad
1069 if (file->IsPartFile()) {
1070 wxString strNewName = ::wxGetTextFromUser(
1071 _("Enter new name for this file:"),
1072 _("File rename"), file->GetFileName().GetPrintable());
1074 CPath newName = CPath(strNewName);
1075 if (newName.IsOk() && (newName != file->GetFileName())) {
1076 theApp->sharedfiles->RenameFile(file, newName);
1080 break;
1082 default:
1083 event.Skip();
1088 void CDownloadListCtrl::OnDrawItem(
1089 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
1091 // Don't do any drawing if there's nobody to see it.
1092 if ( !theApp->amuledlg->IsDialogVisible( CamuleDlg::DT_TRANSFER_WND ) ) {
1093 return;
1096 CtrlItem_Struct* content = (CtrlItem_Struct *)GetItemData(item);
1098 // Define text-color and background
1099 if ((content->GetType() == FILE_TYPE) && (highlighted)) {
1100 if (GetFocus()) {
1101 dc->SetBackground(*m_hilightBrush);
1102 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1103 } else {
1104 dc->SetBackground(*m_hilightUnfocusBrush);
1105 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
1107 } else {
1108 dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(
1109 wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
1110 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
1114 // Define the border of the drawn area
1115 if ( highlighted ) {
1116 wxColour old;
1117 if ( ( content->GetType() == FILE_TYPE ) && !GetFocus() ) {
1118 old = m_hilightUnfocusBrush->GetColour();
1119 } else {
1120 old = m_hilightBrush->GetColour();
1123 wxColor newcol(
1124 ((int)old.Red() * 65) / 100,
1125 ((int)old.Green() * 65) / 100,
1126 ((int)old.Blue() * 65) / 100);
1128 dc->SetPen( wxPen(newcol, 1, wxSOLID) );
1129 } else {
1130 dc->SetPen(*wxTRANSPARENT_PEN);
1134 dc->SetBrush( dc->GetBackground() );
1135 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
1137 dc->SetPen(*wxTRANSPARENT_PEN);
1139 if ( content->GetType() == FILE_TYPE && ( !highlighted || !GetFocus() ) ) {
1140 // If we have category, override textforeground with what category tells us.
1141 CPartFile *file = content->GetFile();
1142 if ( file->GetCategory() ) {
1143 dc->SetTextForeground(
1144 WxColourFromCr(theApp->glob_prefs->GetCatColor(file->GetCategory())) );
1148 // Various constant values we use
1149 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
1150 const int iOffset = 4;
1152 // The starting end ending position of the tree
1153 bool tree_show = false;
1154 int tree_start = 0;
1155 int tree_end = 0;
1157 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
1158 for (int i = 0; i < GetColumnCount(); i++) {
1159 wxListItem listitem;
1160 GetColumn(i, listitem);
1162 if (listitem.GetWidth() > 2*iOffset) {
1163 cur_rec.width = listitem.GetWidth() - 2*iOffset;
1165 // Make a copy of the current rectangle so we can apply specific tweaks
1166 wxRect target_rec = cur_rec;
1167 if ( i == 5 ) {
1168 tree_show = ( listitem.GetWidth() > 0 );
1170 tree_start = cur_rec.x - iOffset;
1171 tree_end = cur_rec.x + iOffset;
1173 // Double the offset to make room for the cirle-marker
1174 target_rec.x += iOffset;
1175 target_rec.width -= iOffset;
1176 } else {
1177 // will ensure that text is about in the middle ;)
1178 target_rec.y += iTextOffset;
1181 // Draw the item
1182 if ( content->GetType() == FILE_TYPE ) {
1183 DrawFileItem(dc, i, target_rec, content);
1184 } else {
1185 DrawSourceItem(dc, i, target_rec, content);
1189 // Increment to the next column
1190 cur_rec.x += listitem.GetWidth();
1193 // Draw tree last so it draws over selected and focus (looks better)
1194 if ( tree_show ) {
1195 // Gather some information
1196 const bool notLast = item + 1 != GetItemCount();
1197 const bool notFirst = item != 0;
1198 const bool hasNext = notLast &&
1199 ((CtrlItem_Struct*)GetItemData(item + 1))->GetType() != FILE_TYPE;
1200 const bool isOpenRoot = content->GetType() == FILE_TYPE &&
1201 (content->GetFile())->ShowSources() &&
1202 ((content->GetFile())->GetStatus() != PS_COMPLETE);
1203 const bool isChild = content->GetType() != FILE_TYPE;
1205 // Might as well calculate these now
1206 const int treeCenter = tree_start + 3;
1207 const int middle = cur_rec.y + ( cur_rec.height + 1 ) / 2;
1209 // Set up a new pen for drawing the tree
1210 dc->SetPen( *(wxThePenList->FindOrCreatePen(dc->GetTextForeground(), 1, wxSOLID)) );
1212 if (isChild) {
1213 // Draw the line to the status bar
1214 dc->DrawLine(tree_end, middle, tree_start + 3, middle);
1216 // Draw the line to the child node
1217 if (hasNext) {
1218 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y + cur_rec.height + 1);
1221 // Draw the line back up to parent node
1222 if (notFirst) {
1223 dc->DrawLine(treeCenter, middle, treeCenter, cur_rec.y - 1);
1225 } else if ( isOpenRoot ) {
1226 // Draw empty circle
1227 dc->SetBrush(*wxTRANSPARENT_BRUSH);
1229 dc->DrawCircle( treeCenter, middle, 3 );
1231 // Draw the line to the child node if there are any children
1232 if (hasNext) {
1233 dc->DrawLine(treeCenter, middle + 3, treeCenter, cur_rec.y + cur_rec.height + 1);
1241 void CDownloadListCtrl::DrawFileItem( wxDC* dc, int nColumn, const wxRect& rect, CtrlItem_Struct* item ) const
1243 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
1245 const CPartFile* file = item->GetFile();
1247 // Used to contain the contenst of cells that dont need any fancy drawing, just text.
1248 wxString text;
1250 switch (nColumn) {
1251 // Filename
1252 case 0: {
1253 // show no. of partfile in filename column
1254 wxString filename;
1255 if (thePrefs::ShowPartFileNumber()) {
1256 if (file->IsPartFile() && !(file->GetStatus() == PS_COMPLETE)) {
1257 filename = CFormat(wxT("[%s] ")) % file->GetPartMetFileName().RemoveAllExt();
1260 filename += file->GetFileName().GetPrintable();
1262 if (file->HasRating() || file->HasComment()) {
1263 int image = Client_CommentOnly_Smiley;
1264 if (file->HasRating()) {
1265 image = Client_InvalidRating_Smiley + file->UserRating() - 1;
1268 wxASSERT(image >= Client_InvalidRating_Smiley);
1269 wxASSERT(image <= Client_CommentOnly_Smiley);
1271 int imgWidth = 16;
1273 // it's already centered by OnDrawItem() ...
1274 m_ImageList.Draw(image, *dc, rect.GetX(), rect.GetY() - 1,
1275 wxIMAGELIST_DRAW_TRANSPARENT);
1276 dc->DrawText(filename, rect.GetX() + imgWidth + 4, rect.GetY());
1277 } else {
1278 dc->DrawText(filename, rect.GetX(), rect.GetY());
1281 break;
1283 // Filesize
1284 case 1:
1285 text = CastItoXBytes( file->GetFileSize() );
1286 break;
1288 // Transferred
1289 case 2:
1290 text = CastItoXBytes( file->GetTransferred() );
1291 break;
1293 // Completed
1294 case 3:
1295 text = CastItoXBytes( file->GetCompletedSize() );
1296 break;
1298 // Speed
1299 case 4: // speed
1300 if ( file->GetTransferingSrcCount() ) {
1301 text = wxString::Format( wxT("%.1f "), file->GetKBpsDown() ) +
1302 _("kB/s");
1304 break;
1306 case 5: // progress
1308 if (thePrefs::ShowProgBar())
1310 int iWidth = rect.GetWidth() - 2;
1311 int iHeight = rect.GetHeight() - 2;
1313 // DO NOT DRAW IT ALL THE TIME
1314 uint32 dwTicks = GetTickCount();
1316 wxMemoryDC cdcStatus;
1318 if ( item->dwUpdated < dwTicks || !item->status || iWidth != item->status->GetWidth() ) {
1319 if ( item->status == NULL) {
1320 item->status = new wxBitmap(iWidth, iHeight);
1321 } else if ( item->status->GetWidth() != iWidth ) {
1322 // Only recreate if the size has changed
1323 item->status->Create(iWidth, iHeight);
1326 cdcStatus.SelectObject( *item->status );
1328 if ( thePrefs::UseFlatBar() ) {
1329 DrawFileStatusBar( file, &cdcStatus,
1330 wxRect(0, 0, iWidth, iHeight), true);
1331 } else {
1332 DrawFileStatusBar( file, &cdcStatus,
1333 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
1335 // Draw black border
1336 cdcStatus.SetPen( *wxBLACK_PEN );
1337 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
1338 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
1341 item->dwUpdated = dwTicks + 5000; // Plus five seconds
1342 } else {
1343 cdcStatus.SelectObject( *item->status );
1346 dc->Blit( rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
1349 if (thePrefs::ShowPercent()) {
1350 // Percentage of completing
1351 // We strip anything below the first decimal point,
1352 // to avoid Format doing roundings
1353 float percent = floor( file->GetPercentCompleted() * 10.0f ) / 10.0f;
1355 wxString buffer = wxString::Format( wxT("%.1f%%"), percent );
1356 int middlex = (2*rect.GetX() + rect.GetWidth()) >> 1;
1357 int middley = (2*rect.GetY() + rect.GetHeight()) >> 1;
1359 wxCoord textwidth, textheight;
1361 dc->GetTextExtent(buffer, &textwidth, &textheight);
1362 wxColour AktColor = dc->GetTextForeground();
1363 if (thePrefs::ShowProgBar()) {
1364 dc->SetTextForeground(*wxWHITE);
1365 } else {
1366 dc->SetTextForeground(*wxBLACK);
1368 dc->DrawText(buffer, middlex - (textwidth >> 1), middley - (textheight >> 1));
1369 dc->SetTextForeground(AktColor);
1372 break;
1374 // Sources
1375 case 6: {
1376 uint16 sc = file->GetSourceCount();
1377 uint16 ncsc = file->GetNotCurrentSourcesCount();
1378 if ( ncsc ) {
1379 text = wxString::Format( wxT("%i/%i" ), sc - ncsc, sc );
1380 } else {
1381 text = wxString::Format( wxT("%i"), sc );
1384 if ( file->GetSrcA4AFCount() ) {
1385 text += wxString::Format( wxT("+%i"), file->GetSrcA4AFCount() );
1388 if ( file->GetTransferingSrcCount() ) {
1389 text += wxString::Format( wxT(" (%i)"), file->GetTransferingSrcCount() );
1392 break;
1395 // Priority
1396 case 7:
1397 text = PriorityToStr( file->GetDownPriority(), file->IsAutoDownPriority() );
1398 break;
1400 // File-status
1401 case 8:
1402 text = file->getPartfileStatus();
1403 break;
1405 // Remaining
1406 case 9: {
1407 if ((file->GetStatus() != PS_COMPLETING) && file->IsPartFile()) {
1408 uint64 remainSize = file->GetFileSize() - file->GetCompletedSize();
1409 sint32 remainTime = file->getTimeRemaining();
1411 if (remainTime >= 0) {
1412 text = CastSecondsToHM(remainTime);
1413 } else {
1414 text = _("Unknown");
1417 text += wxT(" (") + CastItoXBytes(remainSize) + wxT(")");
1419 break;
1422 // Last seen completed
1423 case 10: {
1424 if ( file->lastseencomplete ) {
1425 text = wxDateTime( file->lastseencomplete ).Format( _("%y/%m/%d %H:%M:%S") );
1426 } else {
1427 text = _("Unknown");
1429 break;
1432 // Laste received
1433 case 11: {
1434 const time_t lastReceived = file->GetLastChangeDatetime();
1435 if (lastReceived) {
1436 text = wxDateTime(lastReceived).Format( _("%y/%m/%d %H:%M:%S") );
1437 } else {
1438 text = _("Unknown");
1441 } // switch
1443 if ( !text.IsEmpty() ) {
1444 dc->DrawText( text, rect.GetX(), rect.GetY() );
1449 void CDownloadListCtrl::DrawSourceItem(
1450 wxDC* dc, int nColumn, const wxRect& rect, CtrlItem_Struct* item ) const
1452 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
1453 wxString buffer;
1455 const CUpDownClient* client = item->GetSource();
1457 switch (nColumn) {
1458 // Client name + various icons
1459 case 0: {
1460 wxRect cur_rec = rect;
1461 // +3 is added by OnDrawItem()... so take it off
1462 // Kry - eMule says +1, so I'm trusting it
1463 wxPoint point( cur_rec.GetX(), cur_rec.GetY()+1 );
1465 if (item->GetType() != A4AF_SOURCE) {
1466 uint8 image = 0;
1468 switch (client->GetDownloadState()) {
1469 case DS_CONNECTING:
1470 case DS_CONNECTED:
1471 case DS_WAITCALLBACK:
1472 case DS_TOOMANYCONNS:
1473 image = Client_Red_Smiley;
1474 break;
1475 case DS_ONQUEUE:
1476 if (client->IsRemoteQueueFull()) {
1477 image = Client_Grey_Smiley;
1478 } else {
1479 image = Client_Yellow_Smiley;
1481 break;
1482 case DS_DOWNLOADING:
1483 case DS_REQHASHSET:
1484 image = Client_Green_Smiley;
1485 break;
1486 case DS_NONEEDEDPARTS:
1487 case DS_LOWTOLOWIP:
1488 image = Client_Grey_Smiley;
1489 break;
1490 default: // DS_NONE i.e.
1491 image = Client_White_Smiley;
1494 m_ImageList.Draw(image, *dc, point.x, point.y,
1495 wxIMAGELIST_DRAW_TRANSPARENT);
1496 } else {
1497 m_ImageList.Draw(Client_Grey_Smiley, *dc, point.x, point.y,
1498 wxIMAGELIST_DRAW_TRANSPARENT);
1501 cur_rec.x += 20;
1502 wxPoint point2( cur_rec.GetX(), cur_rec.GetY() + 1 );
1504 uint8 clientImage;
1506 if ( client->IsFriend() ) {
1507 clientImage = Client_Friend_Smiley;
1508 } else {
1509 switch ( client->GetClientSoft() ) {
1510 case SO_AMULE:
1511 clientImage = Client_aMule_Smiley;
1512 break;
1513 case SO_MLDONKEY:
1514 case SO_NEW_MLDONKEY:
1515 case SO_NEW2_MLDONKEY:
1516 clientImage = Client_mlDonkey_Smiley;
1517 break;
1518 case SO_EDONKEY:
1519 case SO_EDONKEYHYBRID:
1520 clientImage = Client_eDonkeyHybrid_Smiley;
1521 break;
1522 case SO_EMULE:
1523 clientImage = Client_eMule_Smiley;
1524 break;
1525 case SO_LPHANT:
1526 clientImage = Client_lphant_Smiley;
1527 break;
1528 case SO_SHAREAZA:
1529 case SO_NEW_SHAREAZA:
1530 case SO_NEW2_SHAREAZA:
1531 clientImage = Client_Shareaza_Smiley;
1532 break;
1533 case SO_LXMULE:
1534 clientImage = Client_xMule_Smiley;
1535 break;
1536 default:
1537 // cDonkey, Compatible, Unknown
1538 // No icon for those yet.
1539 // Using the eMule one + '?'
1540 clientImage = Client_Unknown;
1541 break;
1545 m_ImageList.Draw(clientImage, *dc, point2.x, point.y,
1546 wxIMAGELIST_DRAW_TRANSPARENT);
1548 if (client->GetScoreRatio() > 1) {
1549 // Has credits, draw the gold star
1550 m_ImageList.Draw(Client_CreditsYellow_Smiley, *dc, point2.x, point.y,
1551 wxIMAGELIST_DRAW_TRANSPARENT );
1552 } else if ( client->ExtProtocolAvailable() ) {
1553 // Ext protocol -> Draw the '+'
1554 m_ImageList.Draw(Client_ExtendedProtocol_Smiley, *dc, point2.x, point.y,
1555 wxIMAGELIST_DRAW_TRANSPARENT);
1558 if (client->IsIdentified()) {
1559 // the 'v'
1560 m_ImageList.Draw(Client_SecIdent_Smiley, *dc, point2.x, point.y,
1561 wxIMAGELIST_DRAW_TRANSPARENT);
1562 } else if (client->IsBadGuy()) {
1563 // the 'X'
1564 m_ImageList.Draw(Client_BadGuy_Smiley, *dc, point2.x, point.y,
1565 wxIMAGELIST_DRAW_TRANSPARENT);
1568 if (client->HasObfuscatedConnectionBeenEstablished()) {
1569 // the "¿" except it's a key
1570 m_ImageList.Draw(Client_Encryption_Smiley, *dc, point2.x, point.y,
1571 wxIMAGELIST_DRAW_TRANSPARENT);
1574 wxString userName;
1575 #ifdef ENABLE_IP2COUNTRY
1576 // Draw the flag
1577 const CountryData& countrydata = theApp->amuledlg->m_IP2Country->GetCountryData(client->GetFullIP());
1578 dc->DrawBitmap(countrydata.Flag,
1579 rect.x + 40, rect.y + 5,
1580 wxIMAGELIST_DRAW_TRANSPARENT);
1582 userName << countrydata.Name;
1584 userName << wxT(" - ");
1585 #endif // ENABLE_IP2COUNTRY
1586 if (client->GetUserName().IsEmpty()) {
1587 userName << wxT("?");
1588 } else {
1589 userName << client->GetUserName();
1591 dc->DrawText(userName, rect.GetX() + 60, rect.GetY());
1593 break;
1595 case 3: // completed
1596 if (item->GetType() != A4AF_SOURCE && client->GetTransferredDown()) {
1597 buffer = CastItoXBytes(client->GetTransferredDown());
1598 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1600 break;
1602 case 4: // speed
1603 if (item->GetType() != A4AF_SOURCE && client->GetKBpsDown() > 0.001) {
1604 buffer = wxString::Format(wxT("%.1f "),
1605 client->GetKBpsDown()) + _("kB/s");
1606 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1608 break;
1610 case 5: // file info
1611 if ( thePrefs::ShowProgBar() ) {
1612 int iWidth = rect.GetWidth() - 2;
1613 int iHeight = rect.GetHeight() - 2;
1615 if ( item->GetType() != A4AF_SOURCE ) {
1616 uint32 dwTicks = GetTickCount();
1618 wxMemoryDC cdcStatus;
1620 if ( item->dwUpdated < dwTicks || !item->status ||
1621 iWidth != item->status->GetWidth() ) {
1623 if (item->status == NULL) {
1624 item->status = new wxBitmap(iWidth, iHeight);
1625 } else if ( item->status->GetWidth() != iWidth ) {
1626 // Only recreate if size has changed
1627 item->status->Create(iWidth, iHeight);
1630 cdcStatus.SelectObject(*(item->status));
1632 if ( thePrefs::UseFlatBar() ) {
1633 DrawSourceStatusBar( client, &cdcStatus,
1634 wxRect(0, 0, iWidth, iHeight), true);
1635 } else {
1636 DrawSourceStatusBar( client, &cdcStatus,
1637 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
1639 // Draw black border
1640 cdcStatus.SetPen( *wxBLACK_PEN );
1641 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
1642 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
1645 // Plus ten seconds
1646 item->dwUpdated = dwTicks + 10000;
1647 } else {
1648 cdcStatus.SelectObject(*(item->status));
1651 dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
1652 } else {
1653 buffer = _("A4AF");
1655 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
1656 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
1658 wxCoord txtwidth, txtheight;
1660 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
1662 dc->SetTextForeground(*wxBLACK);
1663 dc->DrawText(buffer, midx - (txtwidth >> 1), midy - (txtheight >> 1));
1665 // Draw black border
1666 dc->SetPen( *wxBLACK_PEN );
1667 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1668 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
1671 break;
1673 case 6: {
1674 // Version
1675 dc->DrawText(client->GetClientVerString(), rect.GetX(), rect.GetY());
1676 break;
1679 case 7: // prio
1680 // We only show priority for sources actually queued for that file
1681 if ( item->GetType() != A4AF_SOURCE &&
1682 client->GetDownloadState() == DS_ONQUEUE ) {
1683 if (client->IsRemoteQueueFull()) {
1684 buffer = _("Queue Full");
1685 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1686 } else {
1687 if (client->GetRemoteQueueRank()) {
1688 sint16 qrDiff = client->GetRemoteQueueRank() -
1689 client->GetOldRemoteQueueRank();
1690 if(qrDiff == client->GetRemoteQueueRank() ) {
1691 qrDiff = 0;
1693 wxColour savedColour = dc->GetTextForeground();
1694 if( qrDiff < 0 ) {
1695 dc->SetTextForeground(*wxBLUE);
1697 if( qrDiff > 0 ) {
1698 dc->SetTextForeground(*wxRED);
1700 //if( qrDiff == 0 ) {
1701 // dc->SetTextForeground(*wxLIGHT_GREY);
1703 buffer = wxString::Format(_("QR: %u (%i)"),
1704 client->GetRemoteQueueRank(), qrDiff);
1705 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1706 dc->SetTextForeground(savedColour);
1710 break;
1712 case 8: // status
1713 if (item->GetType() != A4AF_SOURCE) {
1714 buffer = DownloadStateToStr( client->GetDownloadState(),
1715 client->IsRemoteQueueFull() );
1716 } else {
1717 buffer = _("Asked for another file");
1718 if ( client->GetRequestFile() &&
1719 client->GetRequestFile()->GetFileName().IsOk()) {
1720 buffer += CFormat(wxT(" (%s)"))
1721 % client->GetRequestFile()->GetFileName();
1724 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1725 break;
1726 // Source comes from?
1727 case 9: {
1728 buffer = wxGetTranslation(OriginToText(client->GetSourceFrom()));
1729 dc->DrawText(buffer, rect.GetX(), rect.GetY());
1730 break;
1736 wxString CDownloadListCtrl::GetTTSText(unsigned item) const
1738 CtrlItem_Struct* content = (CtrlItem_Struct*)GetItemData(item);
1740 if (content->GetType() == FILE_TYPE) {
1741 CPartFile* file = content->GetFile();
1743 return file->GetFileName().GetPrintable();
1746 return wxEmptyString;
1750 int CDownloadListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1752 CtrlItem_Struct* item1 = (CtrlItem_Struct*)param1;
1753 CtrlItem_Struct* item2 = (CtrlItem_Struct*)param2;
1755 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1756 sortData &= CMuleListCtrl::COLUMN_MASK;
1757 int comp = 0;
1759 if ( item1->GetType() == FILE_TYPE ) {
1760 if ( item2->GetType() == FILE_TYPE ) {
1761 // Both are files, so we just compare them
1762 comp = Compare( item1->GetFile(), item2->GetFile(), sortData);
1763 } else {
1764 // A file and a source, checking if they belong to each other
1765 if ( item1->GetFile() == item2->GetOwner() ) {
1766 // A file should always be above its sources
1767 // Returning directly to avoid the modifier
1768 return -1;
1769 } else {
1770 // Source belongs to anther file, so we compare the files instead
1771 comp = Compare( item1->GetFile(), item2->GetOwner(), sortData);
1774 } else {
1775 if ( item2->GetType() == FILE_TYPE ) {
1776 // A source and a file, checking if they belong to each other
1777 if ( item1->GetOwner() == item2->GetFile() ) {
1778 // A source should always be below its file
1779 // Returning directly to avoid the modifier
1780 return 1;
1781 } else {
1782 // Source belongs to anther file, so we compare the files instead
1783 comp = Compare( item1->GetOwner(), item2->GetFile(), sortData);
1785 } else {
1786 // Two sources, some different possibilites
1787 if ( item1->GetOwner() == item2->GetOwner() ) {
1788 // Avilable sources first, if we have both an
1789 // available and an unavailable
1790 comp = ( item2->GetType() - item1->GetType() );
1792 if (comp) {
1793 // A4AF and non-A4AF. The order is fixed regardless of sort-order.
1794 return comp;
1795 } else {
1796 comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
1798 } else {
1799 // Belongs to different files, so we compare the files
1800 comp = Compare( item1->GetOwner(), item2->GetOwner(), sortData);
1805 // We modify the result so that it matches with ascending or decending
1806 return sortMod * comp;
1810 int CDownloadListCtrl::Compare( const CPartFile* file1, const CPartFile* file2, long lParamSort)
1812 int result = 0;
1814 switch (lParamSort) {
1815 // Sort by filename
1816 case 0:
1817 result = CmpAny(
1818 file1->GetFileName(),
1819 file2->GetFileName() );
1820 break;
1822 // Sort by size
1823 case 1:
1824 result = CmpAny(
1825 file1->GetFileSize(),
1826 file2->GetFileSize() );
1827 break;
1829 // Sort by transferred
1830 case 2:
1831 result = CmpAny(
1832 file1->GetTransferred(),
1833 file2->GetTransferred() );
1834 break;
1836 // Sort by completed
1837 case 3:
1838 result = CmpAny(
1839 file1->GetCompletedSize(),
1840 file2->GetCompletedSize() );
1841 break;
1843 // Sort by speed
1844 case 4:
1845 result = CmpAny(
1846 file1->GetKBpsDown() * 1024,
1847 file2->GetKBpsDown() * 1024 );
1848 break;
1850 // Sort by percentage completed
1851 case 5:
1852 result = CmpAny(
1853 file1->GetPercentCompleted(),
1854 file2->GetPercentCompleted() );
1855 break;
1857 // Sort by number of sources
1858 case 6:
1859 result = CmpAny(
1860 file1->GetSourceCount(),
1861 file2->GetSourceCount() );
1862 break;
1864 // Sort by priority
1865 case 7:
1866 result = CmpAny(
1867 file1->GetDownPriority(),
1868 file2->GetDownPriority() );
1869 break;
1871 // Sort by status
1872 case 8:
1873 result = CmpAny(
1874 file1->getPartfileStatusRang(),
1875 file2->getPartfileStatusRang() );
1876 break;
1878 // Sort by remaining time
1879 case 9:
1880 if (file1->getTimeRemaining() == -1) {
1881 if (file2->getTimeRemaining() == -1) {
1882 result = 0;
1883 } else {
1884 result = -1;
1886 } else {
1887 if (file2->getTimeRemaining() == -1) {
1888 result = 1;
1889 } else {
1890 result = CmpAny(
1891 file1->getTimeRemaining(),
1892 file2->getTimeRemaining() );
1895 break;
1897 // Sort by last seen complete
1898 case 10:
1899 result = CmpAny(
1900 file1->lastseencomplete,
1901 file2->lastseencomplete );
1902 break;
1904 // Sort by last reception
1905 case 11:
1906 result = CmpAny(
1907 file1->GetLastChangeDatetime(),
1908 file2->GetLastChangeDatetime() );
1909 break;
1912 return result;
1916 int CDownloadListCtrl::Compare(
1917 const CUpDownClient* client1, const CUpDownClient* client2, long lParamSort)
1919 switch (lParamSort) {
1920 // Sort by name
1921 case 0:
1922 return CmpAny( client1->GetUserName(), client2->GetUserName() );
1924 // Sort by status (size field)
1925 case 1:
1926 return CmpAny( client1->GetDownloadState(), client2->GetDownloadState() );
1928 // Sort by transferred in the following fields
1929 case 2:
1930 case 3:
1931 return CmpAny( client1->GetTransferredDown(), client2->GetTransferredDown() );
1933 // Sort by speed
1934 case 4:
1935 return CmpAny( client1->GetKBpsDown(), client2->GetKBpsDown() );
1937 // Sort by parts offered (Progress field)
1938 case 5:
1939 return CmpAny(
1940 client1->GetAvailablePartCount(),
1941 client2->GetAvailablePartCount() );
1943 // Sort by client version
1944 case 6: {
1945 if (client1->GetClientSoft() != client2->GetClientSoft()) {
1946 return client1->GetSoftStr().Cmp(client2->GetSoftStr());
1949 if (client1->GetVersion() != client2->GetVersion()) {
1950 return CmpAny(client1->GetVersion(), client2->GetVersion());
1953 return client1->GetClientModString().Cmp(client2->GetClientModString());
1956 // Sort by Queue-Rank
1957 case 7: {
1958 // This will sort by download state: Downloading, OnQueue, Connecting ...
1959 // However, Asked For Another will always be placed last, due to
1960 // sorting in SortProc
1961 if ( client1->GetDownloadState() != client2->GetDownloadState() ) {
1962 return client1->GetDownloadState() - client2->GetDownloadState();
1965 // Placing items on queue before items on full queues
1966 if ( client1->IsRemoteQueueFull() ) {
1967 if ( client2->IsRemoteQueueFull() ) {
1968 return 0;
1969 } else {
1970 return 1;
1972 } else if ( client2->IsRemoteQueueFull() ) {
1973 return -1;
1974 } else {
1975 if ( client1->GetRemoteQueueRank() ) {
1976 if ( client2->GetRemoteQueueRank() ) {
1977 return CmpAny(
1978 client1->GetRemoteQueueRank(),
1979 client2->GetRemoteQueueRank() );
1980 } else {
1981 return -1;
1983 } else {
1984 if ( client2->GetRemoteQueueRank() ) {
1985 return 1;
1986 } else {
1987 return 0;
1993 // Sort by state
1994 case 8: {
1995 if (client1->GetDownloadState() == client2->GetDownloadState()) {
1996 return CmpAny(
1997 client1->IsRemoteQueueFull(),
1998 client2->IsRemoteQueueFull() );
1999 } else {
2000 return CmpAny(
2001 client1->GetDownloadState(),
2002 client2->GetDownloadState() );
2006 // Source of source ;)
2007 case 9:
2008 return CmpAny(client1->GetSourceFrom(), client2->GetSourceFrom());
2010 default:
2011 return 0;
2016 void CDownloadListCtrl::ClearCompleted()
2018 m_completedFiles = false;
2019 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(false);
2021 // Search for completed files
2022 for ( ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ) {
2023 CtrlItem_Struct* item = it->second; ++it;
2025 if ( item->GetType() == FILE_TYPE ) {
2026 CPartFile* file = item->GetFile();
2028 if ( file->IsPartFile() == false ) {
2029 RemoveFile(file);
2036 void CDownloadListCtrl::ShowFilesCount( int diff )
2038 m_filecount += diff;
2040 wxString str = wxString::Format( _("Downloads (%i)"), m_filecount );
2041 wxStaticText* label = CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText );
2043 label->SetLabel( str );
2044 label->GetParent()->Layout();
2050 bool CDownloadListCtrl::ShowItemInCurrentCat(
2051 const CPartFile* file, int newsel ) const
2053 return
2054 ((newsel == 0 && !thePrefs::ShowAllNotCats()) ||
2055 (newsel == 0 && thePrefs::ShowAllNotCats() && file->GetCategory() == 0)) ||
2056 (newsel > 0 && newsel == file->GetCategory());
2061 void CDownloadListCtrl::DrawFileStatusBar(
2062 const CPartFile* file, wxDC* dc, const wxRect& rect, bool bFlat ) const
2064 static CBarShader s_ChunkBar(16);
2066 COLORREF crHave = ( bFlat ? RGB( 0, 0, 0 ) : RGB( 104, 104, 104 ) );
2067 COLORREF crPending = ( bFlat ? RGB( 255, 255, 100 ) : RGB( 255, 208, 0 ) );
2068 COLORREF crProgress = ( bFlat ? RGB( 0, 150, 0 ) : RGB( 0, 224, 0 ) );
2069 COLORREF crMissing = RGB(255, 0, 0);
2071 s_ChunkBar.SetHeight(rect.height);
2072 s_ChunkBar.SetWidth(rect.width);
2073 s_ChunkBar.SetFileSize( file->GetFileSize() );
2074 s_ChunkBar.Set3dDepth( thePrefs::Get3DDepth() );
2077 if ( file->GetStatus() == PS_COMPLETE || file->GetStatus() == PS_COMPLETING ) {
2078 s_ChunkBar.Fill( crProgress );
2079 s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
2080 return;
2084 // Part availability ( of missing parts )
2085 const CPartFile::CGapPtrList& gaplist = file->GetGapList();
2086 CPartFile::CGapPtrList::const_iterator it = gaplist.begin();
2087 uint64 lastGapEnd = 0;
2088 for (; it != gaplist.end(); ++it) {
2089 Gap_Struct* gap = *it;
2091 // Start position
2092 uint32 start = ( gap->start / PARTSIZE );
2093 // fill the Have-Part (between this gap and the last)
2094 if (gap->start) {
2095 s_ChunkBar.FillRange(lastGapEnd + 1, gap->start - 1, crHave);
2097 lastGapEnd = gap->end;
2098 // End position
2099 uint32 end = ( gap->end / PARTSIZE ) + 1;
2101 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
2102 if ( end > file->GetPartCount() ) {
2103 end = file->GetPartCount();
2106 // Place each gap, one PART at a time
2107 for ( uint64 i = start; i < end; ++i ) {
2108 COLORREF color;
2109 if ( i < file->m_SrcpartFrequency.size() && file->m_SrcpartFrequency[i]) {
2110 int blue = 210 - ( 22 * ( file->m_SrcpartFrequency[i] - 1 ) );
2111 color = RGB( 0, ( blue < 0 ? 0 : blue ), 255 );
2112 } else {
2113 color = crMissing;
2116 if ( file->IsStopped() ) {
2117 color = DarkenColour( color, 2 );
2120 uint64 gap_begin = ( i == start ? gap->start : PARTSIZE * i );
2121 uint64 gap_end = ( i == end - 1 ? gap->end : PARTSIZE * ( i + 1 ) - 1 );
2123 s_ChunkBar.FillRange( gap_begin, gap_end, color);
2126 // fill the last Have-Part (between this gap and the last)
2127 s_ChunkBar.FillRange(lastGapEnd + 1, file->GetFileSize() - 1, crHave);
2130 // Pending parts
2131 const CPartFile::CReqBlockPtrList& requestedblocks_list = file->GetRequestedBlockList();
2132 CPartFile::CReqBlockPtrList::const_iterator it2 = requestedblocks_list.begin();
2133 // adjacing pending parts must be joined to avoid bright lines between them
2134 uint64 lastStartOffset = 0;
2135 uint64 lastEndOffset = 0;
2136 COLORREF color = file->IsStopped() ? DarkenColour( crPending, 2 ) : crPending;
2137 for (; it2 != requestedblocks_list.end(); ++it2) {
2139 if ((*it2)->StartOffset > lastEndOffset + 1) {
2140 // not adjacing, draw last block
2141 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, color);
2142 lastStartOffset = (*it2)->StartOffset;
2143 lastEndOffset = (*it2)->EndOffset;
2144 } else {
2145 // adjacing, grow block
2146 lastEndOffset = (*it2)->EndOffset;
2149 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, color);
2152 // Draw the progress-bar
2153 s_ChunkBar.Draw( dc, rect.x, rect.y, bFlat );
2156 // Green progressbar width
2157 int width = (int)(( (float)rect.width / (float)file->GetFileSize() ) *
2158 file->GetCompletedSize() );
2160 if ( bFlat ) {
2161 dc->SetBrush( wxBrush( crProgress, wxSOLID ) );
2163 dc->DrawRectangle( rect.x, rect.y, width, 3 );
2164 } else {
2165 // Draw the two black lines for 3d-effect
2166 dc->SetPen( wxPen( wxColour( 0, 0, 0 ), 1, wxSOLID ) );
2167 dc->DrawLine( rect.x, rect.y + 0, rect.x + width, rect.y + 0 );
2168 dc->DrawLine( rect.x, rect.y + 2, rect.x + width, rect.y + 2 );
2170 // Draw the green line
2171 dc->SetPen( wxPen( crProgress, 1, wxSOLID ) );
2172 dc->DrawLine( rect.x, rect.y + 1, rect.x + width, rect.y + 1 );
2177 void CDownloadListCtrl::DrawSourceStatusBar(
2178 const CUpDownClient* source, wxDC* dc, const wxRect& rect, bool bFlat) const
2180 static CBarShader s_StatusBar(16);
2182 uint32 crBoth = ( bFlat ? RGB( 0, 150, 0 ) : RGB( 0, 192, 0 ) );
2183 uint32 crNeither = ( bFlat ? RGB( 224, 224, 224 ) : RGB( 240, 240, 240 ) );
2184 uint32 crClientOnly = ( bFlat ? RGB( 0, 0, 0 ) : RGB( 104, 104, 104 ) );
2185 uint32 crPending = ( bFlat ? RGB( 255, 208, 0 ) : RGB( 255, 208, 0 ) );
2186 uint32 crNextPending = ( bFlat ? RGB( 255, 255, 100 ) : RGB( 255, 255, 100 ) );
2188 CPartFile* reqfile = source->GetRequestFile();
2190 s_StatusBar.SetFileSize( reqfile->GetFileSize() );
2191 s_StatusBar.SetHeight(rect.height);
2192 s_StatusBar.SetWidth(rect.width);
2193 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
2195 // Barry - was only showing one part from client, even when reserved bits from 2 parts
2196 wxString gettingParts = source->ShowDownloadingParts();
2198 const BitVector& partStatus = source->GetPartStatus();
2200 uint64 uEnd = 0;
2201 for ( uint64 i = 0; i < partStatus.size(); i++ ) {
2202 uint64 uStart = PARTSIZE * i;
2203 uEnd = wxMin(reqfile->GetFileSize(), uStart + PARTSIZE) - 1;
2205 uint32 color = 0;
2206 if (!partStatus[i]) {
2207 color = crNeither;
2208 } else if ( reqfile->IsComplete(uStart, uEnd)) {
2209 color = crBoth;
2210 } else if ( source->GetDownloadState() == DS_DOWNLOADING &&
2211 source->GetLastBlockOffset() <= uEnd &&
2212 source->GetLastBlockOffset() >= uStart) {
2213 color = crPending;
2214 } else if (gettingParts.GetChar((uint16)i) == 'Y') {
2215 color = crNextPending;
2216 } else {
2217 color = crClientOnly;
2220 if ( source->GetRequestFile()->IsStopped() ) {
2221 color = DarkenColour( color, 2 );
2224 s_StatusBar.FillRange(uStart, uEnd, color);
2226 // fill the rest (if partStatus is empty)
2227 s_StatusBar.FillRange(uEnd + 1, reqfile->GetFileSize() - 1, crNeither);
2229 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
2232 #ifdef __WXMSW__
2233 # define QUOTE wxT("\"")
2234 #else
2235 # define QUOTE wxT("\'")
2236 #endif
2238 void CDownloadListCtrl::PreviewFile(CPartFile* file)
2240 wxString command;
2241 // If no player set in preferences, use mplayer.
2242 // And please, do a warning also :P
2243 if (thePrefs::GetVideoPlayer().IsEmpty()) {
2244 wxMessageBox(_(
2245 "Please set your preferred video player on preferences.\nMeanwhile, aMule will attempt to use mplayer and you will get this warning on every preview"),
2246 _("File preview"), wxOK, this);
2247 // Since newer versions for some reason mplayer does not automatically
2248 // select video output device and needs a parameter, go figure...
2249 command = wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE wxT("$file") QUOTE;
2250 } else {
2251 command = thePrefs::GetVideoPlayer();
2254 // Check if we are (pre)viewing a completed file or not
2255 if (file->GetStatus() != PS_COMPLETE) {
2256 // Remove the .met and see if out video player specifiation uses the magic string
2257 wxString fileWithoutMet = thePrefs::GetTempDir().JoinPaths(
2258 file->GetPartMetFileName().RemoveExt()).GetRaw();
2259 if (!command.Replace(wxT("$file"), fileWithoutMet)) {
2260 // No magic string, so we just append the filename to the player command
2261 // Need to use quotes in case filename contains spaces
2262 command << wxT(" ") << QUOTE << fileWithoutMet << QUOTE;
2264 } else {
2265 // This is a complete file
2266 // FIXME: This is probably not going to work if the filenames are mangled ...
2267 wxString rawFileName = file->GetFullName().GetRaw();
2268 if (!command.Replace(wxT("$file"), rawFileName)) {
2269 // No magic string, so we just append the filename to the player command
2270 // Need to use quotes in case filename contains spaces
2271 command << wxT(" ") << QUOTE << rawFileName << QUOTE;
2275 // We can't use wxShell here, it blocks the app
2276 CTerminationProcess *p = new CTerminationProcess(command);
2277 int ret = wxExecute(command, wxEXEC_ASYNC, p);
2278 bool ok = ret > 0;
2279 if (!ok) {
2280 delete p;
2281 AddLogLineM( true,
2282 CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
2283 command );
2286 // File_checked_for_headers