2 // This file is part of the aMule Project.
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 )
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
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.
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
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
55 struct CtrlItem_Struct
70 DownloadItemType
GetType() const {
74 CPartFile
* GetOwner() const {
78 CPartFile
* GetFile() const {
82 CUpDownClient
* GetSource() const {
86 void SetContents(CPartFile
* file
) {
94 void SetContents(CPartFile
* owner
, CUpDownClient
* source
, DownloadItemType type
) {
95 wxCHECK_RET(type
!= FILE_TYPE
, wxT("Invalid type, not a source"));
99 m_sourceValue
= source
;
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
)
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") );
183 m_hilightBrush
= CMuleColour(wxSYS_COLOUR_HIGHLIGHT
).Blend(125).GetBrush();
185 m_hilightUnfocusBrush
= CMuleColour(wxSYS_COLOUR_BTNSHADOW
).Blend(125).GetBrush();
187 InsertColumn( ColumnPart
, _("Part"), wxLIST_FORMAT_LEFT
, 30, wxT("a") );
188 InsertColumn( ColumnFileName
, _("File Name"), wxLIST_FORMAT_LEFT
, 260, wxT("N") );
189 InsertColumn( ColumnSize
, _("Size"), wxLIST_FORMAT_LEFT
, 60, wxT("Z") );
190 InsertColumn( ColumnTransferred
, _("Transferred"), wxLIST_FORMAT_LEFT
, 65, wxT("T") );
191 InsertColumn( ColumnCompleted
, _("Completed"), wxLIST_FORMAT_LEFT
, 65, wxT("C") );
192 InsertColumn( ColumnSpeed
, _("Speed"), wxLIST_FORMAT_LEFT
, 65, wxT("S") );
193 InsertColumn( ColumnProgress
, _("Progress"), wxLIST_FORMAT_LEFT
, 170, wxT("P") );
194 InsertColumn( ColumnSources
, _("Sources"), wxLIST_FORMAT_LEFT
, 50, wxT("u") );
195 InsertColumn( ColumnPriority
, _("Priority"), wxLIST_FORMAT_LEFT
, 55, wxT("p") );
196 InsertColumn( ColumnStatus
, _("Status"), wxLIST_FORMAT_LEFT
, 70, wxT("s") );
197 InsertColumn( ColumnTimeRemaining
, _("Time Remaining"), wxLIST_FORMAT_LEFT
, 110, wxT("r") );
198 InsertColumn( ColumnLastSeenComplete
, _("Last Seen Complete"), wxLIST_FORMAT_LEFT
, 220, wxT("c") );
199 InsertColumn( ColumnLastReception
, _("Last Reception"), wxLIST_FORMAT_LEFT
, 220, wxT("R") );
202 m_completedFiles
= 0;
207 // This is the order the columns had before extendable list-control settings save/load code was introduced.
208 // Don't touch when inserting new columns!
209 wxString
CDownloadListCtrl::GetOldColumnOrder() const
211 return wxT("N,Z,T,C,S,P,u,p,s,r,c,R");
214 CDownloadListCtrl::~CDownloadListCtrl()
216 while ( !m_ListItems
.empty() ) {
217 delete m_ListItems
.begin()->second
;
218 m_ListItems
.erase( m_ListItems
.begin() );
223 void CDownloadListCtrl::AddFile( CPartFile
* file
)
227 // Avoid duplicate entries of files
228 if ( m_ListItems
.find( file
) == m_ListItems
.end() ) {
229 CtrlItem_Struct
* newitem
= new CtrlItem_Struct
;
230 newitem
->SetContents(file
);
232 m_ListItems
.insert( ListItemsPair( file
, newitem
) );
234 // Check if the new file is visible in the current category
235 if ( file
->CheckShowItemInGivenCat( m_category
) ) {
236 ShowFile( file
, true );
243 void CDownloadListCtrl::AddSource(CPartFile
* owner
, CUpDownClient
* source
, DownloadItemType type
)
245 wxCHECK_RET(owner
, wxT("NULL owner in CDownloadListCtrl::AddSource"));
246 wxCHECK_RET(source
, wxT("NULL source in CDownloadListCtrl::AddSource"));
247 wxCHECK_RET(type
!= FILE_TYPE
, wxT("Invalid type, not a source"));
249 // Update the other instances of this source
251 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
);
252 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
253 CtrlItem_Struct
* cur_item
= it
->second
;
255 // Check if this source has been already added to this file => to be sure
256 if ( cur_item
->GetOwner() == owner
) {
257 // Update this instance with its new setting
258 cur_item
->SetContents(owner
, source
, type
);
259 cur_item
->dwUpdated
= 0;
261 } else if ( type
== AVAILABLE_SOURCE
) {
262 // The state 'Available' is exclusive
263 cur_item
->SetContents(cur_item
->GetOwner(), source
, A4AF_SOURCE
);
264 cur_item
->dwUpdated
= 0;
272 if ( owner
->ShowSources() ) {
273 CtrlItem_Struct
* newitem
= new CtrlItem_Struct
;
274 newitem
->SetContents(owner
, source
, type
);
276 m_ListItems
.insert( ListItemsPair(source
, newitem
) );
278 // Find the owner-object
279 ListItems::iterator it
= m_ListItems
.find( owner
);
281 if ( it
!= m_ListItems
.end() ) {
282 long item
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(it
->second
) );
285 item
= InsertItem( item
+ 1, wxEmptyString
);
287 SetItemPtrData( item
, reinterpret_cast<wxUIntPtr
>(newitem
) );
289 // background.. this should be in a function
291 listitem
.m_itemId
= item
;
293 listitem
.SetBackgroundColour( GetBackgroundColour() );
302 void CDownloadListCtrl::RemoveSource( const CUpDownClient
* source
, const CPartFile
* owner
)
306 // Retrieve all entries matching the source
307 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
);
309 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ) {
310 ListItems::iterator tmp
= it
++;
312 CtrlItem_Struct
* item
= tmp
->second
;
313 if ( owner
== NULL
|| owner
== item
->GetOwner() ) {
314 // Remove it from the m_ListItems
315 m_ListItems
.erase( tmp
);
317 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
329 void CDownloadListCtrl::RemoveFile( CPartFile
* file
)
333 // Ensure that any assosiated sources and list-entries are removed
334 ShowFile( file
, false );
336 // Find the assosiated list-item
337 ListItems::iterator it
= m_ListItems
.find( file
);
339 if ( it
!= m_ListItems
.end() ) {
342 m_ListItems
.erase( it
);
347 void CDownloadListCtrl::UpdateItem(const void* toupdate
)
349 // Retrieve all entries matching the source
350 ListIteratorPair rangeIt
= m_ListItems
.equal_range( toupdate
);
352 // Visible lines, default to all because not all platforms
353 // support the GetVisibleLines function
354 long first
= 0, last
= GetItemCount();
357 // Get visible lines if we need them
358 if ( rangeIt
.first
!= rangeIt
.second
) {
359 GetVisibleLines( &first
, &last
);
363 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
364 CtrlItem_Struct
* item
= it
->second
;
366 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
368 // Determine if the file should be shown in the current category
369 if ( item
->GetType() == FILE_TYPE
) {
370 CPartFile
* file
= item
->GetFile();
372 bool show
= file
->CheckShowItemInGivenCat( m_category
);
378 // Only update visible lines
379 if ( index
>= first
&& index
<= last
) {
380 RefreshItem( index
);
383 // Item should no longer be shown in
384 // the current category
385 ShowFile( file
, false );
388 // Item has been hidden but new status means
389 // that it should it should be shown in the
391 ShowFile( file
, true );
394 if (file
->GetStatus() == PS_COMPLETE
) {
395 m_completedFiles
= true;
397 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(true);
402 // Only update visible lines
403 if ( index
>= first
&& index
<= last
) {
404 RefreshItem( index
);
411 void CDownloadListCtrl::ShowFile( CPartFile
* file
, bool show
)
415 ListItems::iterator it
= m_ListItems
.find( file
);
417 if ( it
!= m_ListItems
.end() ) {
418 CtrlItem_Struct
* item
= it
->second
;
421 // Check if the file is already being displayed
422 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
424 long newitem
= InsertItem( GetItemCount(), wxEmptyString
);
426 SetItemPtrData( newitem
, reinterpret_cast<wxUIntPtr
>(item
) );
429 myitem
.m_itemId
= newitem
;
430 myitem
.SetBackgroundColour( GetBackgroundColour() );
434 RefreshItem( newitem
);
439 // Ensure sources are hidden
440 ShowSources( file
, false );
442 // Try to find the file and remove it
443 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
446 ShowFilesCount( -1 );
453 void CDownloadListCtrl::ShowSources( CPartFile
* file
, bool show
)
455 // Check if the current state is the same as the new state
456 if ( file
->ShowSources() == show
) {
462 file
->SetShowSources( show
);
465 const CPartFile::SourceSet
& normSources
= file
->GetSourceList();
466 const CPartFile::SourceSet
& a4afSources
= file
->GetA4AFList();
468 // Adding normal sources
469 CPartFile::SourceSet::const_iterator it
;
470 for ( it
= normSources
.begin(); it
!= normSources
.end(); ++it
) {
471 switch ((*it
)->GetDownloadState()) {
474 AddSource( file
, *it
, AVAILABLE_SOURCE
);
477 AddSource( file
, *it
, UNAVAILABLE_SOURCE
);
482 // Adding A4AF sources
483 for ( it
= a4afSources
.begin(); it
!= a4afSources
.end(); ++it
) {
484 AddSource( file
, *it
, A4AF_SOURCE
);
489 for ( int i
= GetItemCount() - 1; i
>= 0; --i
) {
490 CtrlItem_Struct
* item
= (CtrlItem_Struct
*)GetItemData(i
);
492 if ( item
->GetType() != FILE_TYPE
&& item
->GetOwner() == file
) {
493 // Remove from the grand list, this call doesn't remove the source
494 // from the listctrl, because ShowSources is now false. This also
496 RemoveSource(item
->GetSource(), file
);
505 void CDownloadListCtrl::ChangeCategory( int newCategory
)
509 // remove all displayed files with a different cat and show the correct ones
510 for (ListItems::const_iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); it
++) {
511 const CtrlItem_Struct
*cur_item
= it
->second
;
513 if ( cur_item
->GetType() == FILE_TYPE
) {
514 CPartFile
* file
= cur_item
->GetFile();
516 bool curVisibility
= file
->CheckShowItemInGivenCat( m_category
);
517 bool newVisibility
= file
->CheckShowItemInGivenCat( newCategory
);
519 // Check if the visibility of the file has changed. However, if the
520 // current category is the default (0) category, then we can't use
521 // curVisiblity to see if the visibility has changed but instead
522 // have to let ShowFile() check if the file is or isn't on the list.
523 if ( curVisibility
!= newVisibility
|| !newCategory
) {
524 ShowFile( file
, newVisibility
);
531 m_category
= newCategory
;
535 uint8
CDownloadListCtrl::GetCategory() const
544 const int itFILES
= 1;
545 const int itSOURCES
= 2;
548 * Helper-function: This function is used to gather selected items.
550 * @param list A pointer to the list to gather items from.
551 * @param types The desired types OR'd together.
552 * @return A list containing the selected items of the choosen types.
554 ItemList
GetSelectedItems( CDownloadListCtrl
* list
, int types
)
558 long index
= list
->GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
560 while ( index
> -1 ) {
561 CtrlItem_Struct
* item
= (CtrlItem_Struct
*)list
->GetItemData( index
);
564 add
|= ( item
->GetType() == FILE_TYPE
) && ( types
& itFILES
);
565 add
|= ( item
->GetType() != FILE_TYPE
) && ( types
& itSOURCES
);
568 results
.push_back( item
);
571 index
= list
->GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
578 void CDownloadListCtrl::OnCancelFile(wxCommandEvent
& WXUNUSED(event
))
580 ItemList files
= ::GetSelectedItems(this, itFILES
);
583 if (files
.size() == 1) {
584 question
= _("Are you sure that you wish to delete the selected file?");
586 question
= _("Are you sure that you wish to delete the selected files?");
588 if (wxMessageBox( question
, _("Cancel"), wxICON_QUESTION
| wxYES_NO
, this) == wxYES
) {
589 for (ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
590 CPartFile
* file
= (*it
)->GetFile();
592 switch (file
->GetStatus()) {
593 case PS_WAITINGFORHASH
:
599 CoreNotify_PartFile_Delete(file
);
608 void CDownloadListCtrl::OnSetPriority( wxCommandEvent
& event
)
611 switch ( event
.GetId() ) {
612 case MP_PRIOLOW
: priority
= PR_LOW
; break;
613 case MP_PRIONORMAL
: priority
= PR_NORMAL
; break;
614 case MP_PRIOHIGH
: priority
= PR_HIGH
; break;
615 case MP_PRIOAUTO
: priority
= PR_AUTO
; break;
620 ItemList files
= ::GetSelectedItems( this, itFILES
);
622 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
623 CPartFile
* file
= (*it
)->GetFile();
625 if ( priority
== PR_AUTO
) {
626 CoreNotify_PartFile_PrioAuto( file
, true );
628 CoreNotify_PartFile_PrioAuto( file
, false );
630 CoreNotify_PartFile_PrioSet( file
, priority
, true );
636 void CDownloadListCtrl::OnSwapSources( wxCommandEvent
& event
)
638 ItemList files
= ::GetSelectedItems( this, itFILES
);
640 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
641 CPartFile
* file
= (*it
)->GetFile();
643 switch ( event
.GetId() ) {
644 case MP_SWAP_A4AF_TO_THIS
:
645 CoreNotify_PartFile_Swap_A4AF( file
);
648 case MP_SWAP_A4AF_TO_THIS_AUTO
:
649 CoreNotify_PartFile_Swap_A4AF_Auto( file
);
652 case MP_SWAP_A4AF_TO_ANY_OTHER
:
653 CoreNotify_PartFile_Swap_A4AF_Others( file
);
660 void CDownloadListCtrl::OnSetCategory( wxCommandEvent
& event
)
662 ItemList files
= ::GetSelectedItems( this, itFILES
);
664 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
665 CPartFile
* file
= (*it
)->GetFile();
667 CoreNotify_PartFile_SetCat( file
, event
.GetId() - MP_ASSIGNCAT
);
670 ChangeCategory( m_category
);
674 void CDownloadListCtrl::OnSetStatus( wxCommandEvent
& event
)
676 ItemList files
= ::GetSelectedItems( this, itFILES
);
678 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
679 CPartFile
* file
= (*it
)->GetFile();
681 switch ( event
.GetId() ) {
683 CoreNotify_PartFile_Pause( file
);
687 CoreNotify_PartFile_Resume( file
);
691 ShowSources(file
, false);
692 CoreNotify_PartFile_Stop( file
);
699 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent
& WXUNUSED(event
) )
705 void CDownloadListCtrl::OnGetLink(wxCommandEvent
& event
)
707 ItemList files
= ::GetSelectedItems( this, itFILES
);
711 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
712 CPartFile
* file
= (*it
)->GetFile();
714 if ( event
.GetId() == MP_GETED2KLINK
) {
715 URIs
+= theApp
->CreateED2kLink( file
) + wxT("\n");
717 URIs
+= theApp
->CreateMagnetLink( file
) + wxT("\n");
721 if ( !URIs
.IsEmpty() ) {
722 theApp
->CopyTextToClipboard( URIs
.BeforeLast(wxT('\n')) );
727 void CDownloadListCtrl::OnGetFeedback(wxCommandEvent
& WXUNUSED(event
))
730 ItemList files
= ::GetSelectedItems(this, itFILES
);
732 for (ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
733 if (feed
.IsEmpty()) {
734 feed
= CFormat(_("Feedback from: %s (%s)\n\n")) % thePrefs::GetUserNick() % GetFullMuleVersion();
738 feed
+= (*it
)->GetFile()->GetFeedback();
741 if (!feed
.IsEmpty()) {
742 theApp
->CopyTextToClipboard(feed
);
747 void CDownloadListCtrl::OnGetRazorStats( wxCommandEvent
& WXUNUSED(event
) )
749 ItemList files
= ::GetSelectedItems( this, itFILES
);
751 if ( files
.size() == 1 ) {
752 CPartFile
* file
= files
.front()->GetFile();
754 theApp
->amuledlg
->LaunchUrl(
755 wxT("http://stats.razorback2.com/ed2khistory?ed2k=") +
756 file
->GetFileHash().Encode());
761 void CDownloadListCtrl::OnViewFileInfo( wxCommandEvent
& WXUNUSED(event
) )
763 ItemList files
= ::GetSelectedItems( this, itFILES
);
765 if ( files
.size() == 1 ) {
766 CPartFile
* file
= files
.front()->GetFile();
768 CFileDetailDialog
dialog( this, file
);
774 void CDownloadListCtrl::OnViewFileComments( wxCommandEvent
& WXUNUSED(event
) )
776 ItemList files
= ::GetSelectedItems( this, itFILES
);
778 if ( files
.size() == 1 ) {
779 CPartFile
* file
= files
.front()->GetFile();
781 CCommentDialogLst
dialog( this, file
);
787 void CDownloadListCtrl::OnPreviewFile( wxCommandEvent
& WXUNUSED(event
) )
789 ItemList files
= ::GetSelectedItems( this, itFILES
);
791 if ( files
.size() == 1 ) {
792 PreviewFile(files
.front()->GetFile());
796 void CDownloadListCtrl::OnSwapSource( wxCommandEvent
& WXUNUSED(event
) )
798 ItemList sources
= ::GetSelectedItems( this, itSOURCES
);
800 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
801 CPartFile
* file
= (*it
)->GetOwner();
802 CUpDownClient
* source
= (*it
)->GetSource();
804 source
->SwapToAnotherFile( true, false, false, file
);
809 void CDownloadListCtrl::OnViewFiles( wxCommandEvent
& WXUNUSED(event
) )
811 ItemList sources
= ::GetSelectedItems( this, itSOURCES
);
813 if ( sources
.size() == 1 ) {
814 CUpDownClient
* source
= sources
.front()->GetSource();
816 source
->RequestSharedFileList();
821 void CDownloadListCtrl::OnAddFriend( wxCommandEvent
& WXUNUSED(event
) )
823 ItemList sources
= ::GetSelectedItems( this, itSOURCES
);
825 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
826 CUpDownClient
* client
= (*it
)->GetSource();
827 if (client
->IsFriend()) {
828 theApp
->amuledlg
->m_chatwnd
->RemoveFriend(client
->GetUserHash(), client
->GetIP(), client
->GetUserPort());
830 theApp
->amuledlg
->m_chatwnd
->AddFriend( client
);
836 void CDownloadListCtrl::OnSendMessage( wxCommandEvent
& WXUNUSED(event
) )
838 ItemList sources
= ::GetSelectedItems( this, itSOURCES
);
840 if ( sources
.size() == 1 ) {
841 CUpDownClient
* source
= (sources
.front())->GetSource();
843 // These values are cached, since calling wxGetTextFromUser will
844 // start an event-loop, in which the client may be deleted.
845 wxString userName
= source
->GetUserName();
846 uint64 userID
= GUI_ID(source
->GetIP(), source
->GetUserPort());
848 wxString message
= ::wxGetTextFromUser(_("Send message to user"),
849 _("Message to send:"));
850 if ( !message
.IsEmpty() ) {
851 theApp
->amuledlg
->m_chatwnd
->SendMessage(message
, userName
, userID
);
857 void CDownloadListCtrl::OnViewClientInfo( wxCommandEvent
& WXUNUSED(event
) )
859 ItemList sources
= ::GetSelectedItems( this, itSOURCES
);
861 if ( sources
.size() == 1 ) {
862 CUpDownClient
* source
= (sources
.front())->GetSource();
864 CClientDetailDialog
dialog( this, source
);
870 void CDownloadListCtrl::OnItemActivated( wxListEvent
& evt
)
872 CtrlItem_Struct
* content
= (CtrlItem_Struct
*)GetItemData( evt
.GetIndex() );
874 if ( content
->GetType() == FILE_TYPE
) {
875 CPartFile
* file
= content
->GetFile();
877 if ((!file
->IsPartFile() || file
->GetStatus() == PS_COMPLETE
) && file
->PreviewAvailable()) {
880 ShowSources( file
, !file
->ShowSources() );
887 void CDownloadListCtrl::OnMouseRightClick(wxListEvent
& evt
)
889 long index
= CheckSelection(evt
);
897 CtrlItem_Struct
* item
= (CtrlItem_Struct
*)GetItemData( index
);
898 if (item
->GetType() == FILE_TYPE
) {
899 m_menu
= new wxMenu( _("Downloads") );
901 wxMenu
* priomenu
= new wxMenu();
902 priomenu
->AppendCheckItem(MP_PRIOLOW
, _("Low"));
903 priomenu
->AppendCheckItem(MP_PRIONORMAL
, _("Normal"));
904 priomenu
->AppendCheckItem(MP_PRIOHIGH
, _("High"));
905 priomenu
->AppendCheckItem(MP_PRIOAUTO
, _("Auto"));
907 m_menu
->Append(MP_MENU_PRIO
, _("Priority"), priomenu
);
908 m_menu
->Append(MP_CANCEL
, _("Cancel"));
909 m_menu
->Append(MP_STOP
, _("&Stop"));
910 m_menu
->Append(MP_PAUSE
, _("&Pause"));
911 m_menu
->Append(MP_RESUME
, _("&Resume"));
912 m_menu
->Append(MP_CLEARCOMPLETED
, _("C&lear completed"));
913 //-----------------------------------------------------
914 m_menu
->AppendSeparator();
915 //-----------------------------------------------------
916 wxMenu
* extendedmenu
= new wxMenu();
917 extendedmenu
->Append(MP_SWAP_A4AF_TO_THIS
,
918 _("Swap every A4AF to this file now"));
919 extendedmenu
->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO
,
920 _("Swap every A4AF to this file (Auto)"));
921 //-----------------------------------------------------
922 extendedmenu
->AppendSeparator();
923 //-----------------------------------------------------
924 extendedmenu
->Append(MP_SWAP_A4AF_TO_ANY_OTHER
,
925 _("Swap every A4AF to any other file now"));
926 //-----------------------------------------------------
927 m_menu
->Append(MP_MENU_EXTD
,
928 _("Extended Options"), extendedmenu
);
929 //-----------------------------------------------------
930 m_menu
->AppendSeparator();
931 //-----------------------------------------------------
932 /* Commented out till RB2 is back
933 m_menu->Append( MP_RAZORSTATS,
934 _("Get Razorback 2's stats for this file"));
935 //-----------------------------------------------------
936 m_menu->AppendSeparator();
937 //-----------------------------------------------------
939 m_menu
->Append(MP_VIEW
, _("Preview"));
940 m_menu
->Append(MP_METINFO
, _("Show file &details"));
941 m_menu
->Append(MP_VIEWFILECOMMENTS
,
942 _("Show all comments"));
943 //-----------------------------------------------------
944 m_menu
->AppendSeparator();
945 //-----------------------------------------------------
946 m_menu
->Append(MP_GETMAGNETLINK
,
947 _("Copy magnet URI to clipboard"));
948 m_menu
->Append(MP_GETED2KLINK
,
949 _("Copy eD2k &link to clipboard"));
950 m_menu
->Append(MP_WS
,
951 _("Copy feedback to clipboard"));
952 //-----------------------------------------------------
953 m_menu
->AppendSeparator();
954 //-----------------------------------------------------
955 // Add dinamic entries
956 wxMenu
*cats
= new wxMenu(_("Category"));
957 if (theApp
->glob_prefs
->GetCatCount() > 1) {
958 for (uint32 i
= 0; i
< theApp
->glob_prefs
->GetCatCount(); i
++) {
960 cats
->Append( MP_ASSIGNCAT
, _("unassign") );
962 cats
->Append( MP_ASSIGNCAT
+ i
,
963 theApp
->glob_prefs
->GetCategory(i
)->title
);
967 m_menu
->Append(MP_MENU_CATS
, _("Assign to category"), cats
);
968 m_menu
->Enable(MP_MENU_CATS
, (theApp
->glob_prefs
->GetCatCount() > 1) );
970 CPartFile
* file
= item
->GetFile();
976 if (file
->GetStatus(true) != PS_ALLOCATING
) {
977 const uint8_t fileStatus
= file
->GetStatus();
979 (fileStatus
!= PS_ERROR
) &&
980 (fileStatus
!= PS_COMPLETE
) &&
981 (file
->IsStopped() != true);
982 canPause
= (file
->GetStatus() != PS_PAUSED
) && canStop
;
984 (fileStatus
== PS_PAUSED
) ||
985 (fileStatus
== PS_ERROR
) ||
986 (fileStatus
== PS_INSUFFICIENT
);
987 canCancel
= fileStatus
!= PS_COMPLETE
;
989 canStop
= canPause
= canCancel
= fileResumable
= false;
992 wxMenu
* menu
= m_menu
;
993 menu
->Enable( MP_CANCEL
, canCancel
);
994 menu
->Enable( MP_PAUSE
, canPause
);
995 menu
->Enable( MP_STOP
, canStop
);
996 menu
->Enable( MP_RESUME
, fileResumable
);
997 menu
->Enable( MP_CLEARCOMPLETED
, m_completedFiles
);
1000 if (file
->IsPartFile() && (file
->GetStatus() != PS_COMPLETE
)) {
1001 view
= CFormat(wxT("%s [%s]")) % _("Preview")
1002 % file
->GetPartMetFileName().RemoveExt();
1003 } else if ( file
->GetStatus() == PS_COMPLETE
) {
1004 view
= _("&Open the file");
1006 menu
->SetLabel(MP_VIEW
, view
);
1007 menu
->Enable(MP_VIEW
, file
->PreviewAvailable() );
1009 menu
->Check( MP_SWAP_A4AF_TO_THIS_AUTO
, file
->IsA4AFAuto() );
1011 int priority
= file
->IsAutoDownPriority() ?
1012 PR_AUTO
: file
->GetDownPriority();
1014 priomenu
->Check( MP_PRIOHIGH
, priority
== PR_HIGH
);
1015 priomenu
->Check( MP_PRIONORMAL
, priority
== PR_NORMAL
);
1016 priomenu
->Check( MP_PRIOLOW
, priority
== PR_LOW
);
1017 priomenu
->Check( MP_PRIOAUTO
, priority
== PR_AUTO
);
1019 menu
->Enable( MP_MENU_EXTD
, canPause
);
1021 PopupMenu(m_menu
, evt
.GetPoint());
1024 CUpDownClient
* client
= item
->GetSource();
1026 m_menu
= new wxMenu(wxT("Clients"));
1027 m_menu
->Append(MP_DETAIL
, _("Show &Details"));
1028 m_menu
->Append(MP_ADDFRIEND
, client
->IsFriend() ? _("Remove from friends") : _("Add to Friends"));
1029 m_menu
->Append(MP_SHOWLIST
, _("View Files"));
1030 m_menu
->Append(MP_SENDMESSAGE
, _("Send message"));
1031 m_menu
->Append(MP_CHANGE2FILE
, _("Swap to this file"));
1033 // Only enable the Swap option for A4AF sources
1034 m_menu
->Enable(MP_CHANGE2FILE
, (item
->GetType() == A4AF_SOURCE
));
1035 // We need a valid IP if we are to message the client
1036 m_menu
->Enable(MP_SENDMESSAGE
, (client
->GetIP() != 0));
1038 m_menu
->Enable(MP_SHOWLIST
, !client
->HasDisabledSharedFiles());
1040 PopupMenu(m_menu
, evt
.GetPoint());
1050 void CDownloadListCtrl::OnMouseMiddleClick(wxListEvent
& evt
)
1052 // Check if clicked item is selected. If not, unselect all and select it.
1053 long index
= CheckSelection(evt
);
1058 CtrlItem_Struct
* item
= (CtrlItem_Struct
*)GetItemData( index
);
1060 if ( item
->GetType() == FILE_TYPE
) {
1061 CFileDetailDialog(this, item
->GetFile()).ShowModal();
1063 CClientDetailDialog(this, item
->GetSource()).ShowModal();
1068 void CDownloadListCtrl::OnKeyPressed( wxKeyEvent
& event
)
1070 // Check if delete was pressed
1071 switch (event
.GetKeyCode()) {
1072 case WXK_NUMPAD_DELETE
:
1075 OnCancelFile( evt
);
1079 ItemList files
= ::GetSelectedItems( this, itFILES
);
1080 if (files
.size() == 1) {
1081 CPartFile
* file
= files
.front()->GetFile();
1083 // Currently renaming of completed files causes problem with kad
1084 if (file
->IsPartFile()) {
1085 wxString strNewName
= ::wxGetTextFromUser(
1086 _("Enter new name for this file:"),
1087 _("File rename"), file
->GetFileName().GetPrintable());
1089 CPath newName
= CPath(strNewName
);
1090 if (newName
.IsOk() && (newName
!= file
->GetFileName())) {
1091 theApp
->sharedfiles
->RenameFile(file
, newName
);
1103 void CDownloadListCtrl::OnDrawItem(
1104 int item
, wxDC
* dc
, const wxRect
& rect
, const wxRect
& rectHL
, bool highlighted
)
1106 // Don't do any drawing if there's nobody to see it.
1107 if ( !theApp
->amuledlg
->IsDialogVisible( CamuleDlg::DT_TRANSFER_WND
) ) {
1111 CtrlItem_Struct
* content
= (CtrlItem_Struct
*)GetItemData(item
);
1113 // Define text-color and background
1114 if ((content
->GetType() == FILE_TYPE
) && (highlighted
)) {
1116 dc
->SetBackground(m_hilightBrush
);
1117 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
1119 dc
->SetBackground(m_hilightUnfocusBrush
);
1120 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
1123 dc
->SetBackground(*(wxTheBrushList
->FindOrCreateBrush(
1124 wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX
), wxSOLID
)));
1125 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
1129 // Define the border of the drawn area
1130 if ( highlighted
) {
1132 if ( ( content
->GetType() == FILE_TYPE
) && !GetFocus() ) {
1133 colour
= m_hilightUnfocusBrush
.GetColour();
1135 colour
= m_hilightBrush
.GetColour();
1138 dc
->SetPen( colour
.Blend(65).GetPen() );
1140 dc
->SetPen(*wxTRANSPARENT_PEN
);
1144 dc
->SetBrush( dc
->GetBackground() );
1145 dc
->DrawRectangle( rectHL
.x
, rectHL
.y
, rectHL
.width
, rectHL
.height
);
1147 dc
->SetPen(*wxTRANSPARENT_PEN
);
1149 if ( content
->GetType() == FILE_TYPE
&& ( !highlighted
|| !GetFocus() ) ) {
1150 // If we have category, override textforeground with what category tells us.
1151 CPartFile
*file
= content
->GetFile();
1152 if ( file
->GetCategory() ) {
1153 dc
->SetTextForeground(
1154 CMuleColour(theApp
->glob_prefs
->GetCatColor(file
->GetCategory())) );
1158 // Various constant values we use
1159 const int iTextOffset
= ( rect
.GetHeight() - dc
->GetCharHeight() ) / 2;
1160 const int iOffset
= 4;
1162 // The starting end ending position of the tree
1163 bool tree_show
= false;
1167 wxRect
cur_rec( iOffset
, rect
.y
, 0, rect
.height
);
1168 for (int i
= 0; i
< GetColumnCount(); i
++) {
1169 wxListItem listitem
;
1170 GetColumn(i
, listitem
);
1172 if (listitem
.GetWidth() > 2*iOffset
) {
1173 cur_rec
.width
= listitem
.GetWidth() - 2*iOffset
;
1175 // Make a copy of the current rectangle so we can apply specific tweaks
1176 wxRect target_rec
= cur_rec
;
1177 if ( i
== ColumnProgress
) {
1178 tree_show
= ( listitem
.GetWidth() > 0 );
1180 tree_start
= cur_rec
.x
- iOffset
;
1181 tree_end
= cur_rec
.x
+ iOffset
;
1183 // Double the offset to make room for the cirle-marker
1184 target_rec
.x
+= iOffset
;
1185 target_rec
.width
-= iOffset
;
1187 // will ensure that text is about in the middle ;)
1188 target_rec
.y
+= iTextOffset
;
1192 if ( content
->GetType() == FILE_TYPE
) {
1193 DrawFileItem(dc
, i
, target_rec
, content
);
1195 DrawSourceItem(dc
, i
, target_rec
, content
);
1199 // Increment to the next column
1200 cur_rec
.x
+= listitem
.GetWidth();
1203 // Draw tree last so it draws over selected and focus (looks better)
1205 // Gather some information
1206 const bool notLast
= item
+ 1 != GetItemCount();
1207 const bool notFirst
= item
!= 0;
1208 const bool hasNext
= notLast
&&
1209 ((CtrlItem_Struct
*)GetItemData(item
+ 1))->GetType() != FILE_TYPE
;
1210 const bool isOpenRoot
= content
->GetType() == FILE_TYPE
&&
1211 (content
->GetFile())->ShowSources() &&
1212 ((content
->GetFile())->GetStatus() != PS_COMPLETE
);
1213 const bool isChild
= content
->GetType() != FILE_TYPE
;
1215 // Might as well calculate these now
1216 const int treeCenter
= tree_start
+ 3;
1217 const int middle
= cur_rec
.y
+ ( cur_rec
.height
+ 1 ) / 2;
1219 // Set up a new pen for drawing the tree
1220 dc
->SetPen( *(wxThePenList
->FindOrCreatePen(dc
->GetTextForeground(), 1, wxSOLID
)) );
1223 // Draw the line to the status bar
1224 dc
->DrawLine(tree_end
, middle
, tree_start
+ 3, middle
);
1226 // Draw the line to the child node
1228 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
+ cur_rec
.height
+ 1);
1231 // Draw the line back up to parent node
1233 dc
->DrawLine(treeCenter
, middle
, treeCenter
, cur_rec
.y
- 1);
1235 } else if ( isOpenRoot
) {
1236 // Draw empty circle
1237 dc
->SetBrush(*wxTRANSPARENT_BRUSH
);
1239 dc
->DrawCircle( treeCenter
, middle
, 3 );
1241 // Draw the line to the child node if there are any children
1243 dc
->DrawLine(treeCenter
, middle
+ 3, treeCenter
, cur_rec
.y
+ cur_rec
.height
+ 1);
1251 void CDownloadListCtrl::DrawFileItem( wxDC
* dc
, int nColumn
, const wxRect
& rect
, CtrlItem_Struct
* item
) const
1253 wxDCClipper
clipper( *dc
, rect
.GetX(), rect
.GetY(), rect
.GetWidth(), rect
.GetHeight() );
1255 const CPartFile
* file
= item
->GetFile();
1257 // Used to contain the contenst of cells that dont need any fancy drawing, just text.
1266 if (file
->IsPartFile() && !(file
->GetStatus() == PS_COMPLETE
)) {
1267 partno
= CFormat(wxT("%s")) % file
->GetPartMetFileName().RemoveAllExt();
1269 dc
->DrawText(partno
, rect
.GetX(), rect
.GetY());
1273 case ColumnFileName
: {
1274 wxString filename
= file
->GetFileName().GetPrintable();
1276 if (file
->HasRating() || file
->HasComment()) {
1277 int image
= Client_CommentOnly_Smiley
;
1278 if (file
->HasRating()) {
1279 image
= Client_InvalidRating_Smiley
+ file
->UserRating() - 1;
1282 wxASSERT(image
>= Client_InvalidRating_Smiley
);
1283 wxASSERT(image
<= Client_CommentOnly_Smiley
);
1287 // it's already centered by OnDrawItem() ...
1288 m_ImageList
.Draw(image
, *dc
, rect
.GetX(), rect
.GetY() - 1,
1289 wxIMAGELIST_DRAW_TRANSPARENT
);
1290 dc
->DrawText(filename
, rect
.GetX() + imgWidth
+ 4, rect
.GetY());
1292 dc
->DrawText(filename
, rect
.GetX(), rect
.GetY());
1299 text
= CastItoXBytes( file
->GetFileSize() );
1303 case ColumnTransferred
:
1304 text
= CastItoXBytes( file
->GetTransferred() );
1308 case ColumnCompleted
:
1309 text
= CastItoXBytes( file
->GetCompletedSize() );
1314 if ( file
->GetTransferingSrcCount() ) {
1315 text
= wxString::Format( wxT("%.1f "), file
->GetKBpsDown() ) +
1321 case ColumnProgress
:
1323 if (thePrefs::ShowProgBar())
1325 int iWidth
= rect
.GetWidth() - 2;
1326 int iHeight
= rect
.GetHeight() - 2;
1328 // DO NOT DRAW IT ALL THE TIME
1329 uint32 dwTicks
= GetTickCount();
1331 wxMemoryDC cdcStatus
;
1333 if ( item
->dwUpdated
< dwTicks
|| !item
->status
|| iWidth
!= item
->status
->GetWidth() ) {
1334 if ( item
->status
== NULL
) {
1335 item
->status
= new wxBitmap(iWidth
, iHeight
);
1336 } else if ( item
->status
->GetWidth() != iWidth
) {
1337 // Only recreate if the size has changed
1338 item
->status
->Create(iWidth
, iHeight
);
1341 cdcStatus
.SelectObject( *item
->status
);
1343 if ( thePrefs::UseFlatBar() ) {
1344 DrawFileStatusBar( file
, &cdcStatus
,
1345 wxRect(0, 0, iWidth
, iHeight
), true);
1347 DrawFileStatusBar( file
, &cdcStatus
,
1348 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
1350 // Draw black border
1351 cdcStatus
.SetPen( *wxBLACK_PEN
);
1352 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
1353 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
1356 item
->dwUpdated
= dwTicks
+ 5000; // Plus five seconds
1358 cdcStatus
.SelectObject( *item
->status
);
1361 dc
->Blit( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
1364 if (thePrefs::ShowPercent()) {
1365 // Percentage of completing
1366 // We strip anything below the first decimal point,
1367 // to avoid Format doing roundings
1368 float percent
= floor( file
->GetPercentCompleted() * 10.0f
) / 10.0f
;
1370 wxString buffer
= wxString::Format( wxT("%.1f%%"), percent
);
1371 int middlex
= (2*rect
.GetX() + rect
.GetWidth()) >> 1;
1372 int middley
= (2*rect
.GetY() + rect
.GetHeight()) >> 1;
1374 wxCoord textwidth
, textheight
;
1376 dc
->GetTextExtent(buffer
, &textwidth
, &textheight
);
1377 wxColour AktColor
= dc
->GetTextForeground();
1378 if (thePrefs::ShowProgBar()) {
1379 dc
->SetTextForeground(*wxWHITE
);
1381 dc
->SetTextForeground(*wxBLACK
);
1383 dc
->DrawText(buffer
, middlex
- (textwidth
>> 1), middley
- (textheight
>> 1));
1384 dc
->SetTextForeground(AktColor
);
1390 case ColumnSources
: {
1391 uint16 sc
= file
->GetSourceCount();
1392 uint16 ncsc
= file
->GetNotCurrentSourcesCount();
1394 text
= wxString::Format( wxT("%i/%i" ), sc
- ncsc
, sc
);
1396 text
= wxString::Format( wxT("%i"), sc
);
1399 if ( file
->GetSrcA4AFCount() ) {
1400 text
+= wxString::Format( wxT("+%i"), file
->GetSrcA4AFCount() );
1403 if ( file
->GetTransferingSrcCount() ) {
1404 text
+= wxString::Format( wxT(" (%i)"), file
->GetTransferingSrcCount() );
1411 case ColumnPriority
:
1412 text
= PriorityToStr( file
->GetDownPriority(), file
->IsAutoDownPriority() );
1417 text
= file
->getPartfileStatus();
1421 case ColumnTimeRemaining
: {
1422 if ((file
->GetStatus() != PS_COMPLETING
) && file
->IsPartFile()) {
1423 uint64 remainSize
= file
->GetFileSize() - file
->GetCompletedSize();
1424 sint32 remainTime
= file
->getTimeRemaining();
1426 if (remainTime
>= 0) {
1427 text
= CastSecondsToHM(remainTime
);
1429 text
= _("Unknown");
1432 text
+= wxT(" (") + CastItoXBytes(remainSize
) + wxT(")");
1437 // Last seen completed
1438 case ColumnLastSeenComplete
: {
1439 if ( file
->lastseencomplete
) {
1440 text
= wxDateTime( file
->lastseencomplete
).Format( _("%y/%m/%d %H:%M:%S") );
1442 text
= _("Unknown");
1448 case ColumnLastReception
: {
1449 const time_t lastReceived
= file
->GetLastChangeDatetime();
1451 text
= wxDateTime(lastReceived
).Format( _("%y/%m/%d %H:%M:%S") );
1453 text
= _("Unknown");
1458 if ( !text
.IsEmpty() ) {
1459 dc
->DrawText( text
, rect
.GetX(), rect
.GetY() );
1464 void CDownloadListCtrl::DrawSourceItem(
1465 wxDC
* dc
, int nColumn
, const wxRect
& rect
, CtrlItem_Struct
* item
) const
1467 wxDCClipper
clipper( *dc
, rect
.GetX(), rect
.GetY(), rect
.GetWidth(), rect
.GetHeight() );
1470 const CUpDownClient
* client
= item
->GetSource();
1473 // Client name + various icons
1474 case ColumnFileName
: {
1475 wxRect cur_rec
= rect
;
1476 // +3 is added by OnDrawItem()... so take it off
1477 // Kry - eMule says +1, so I'm trusting it
1478 wxPoint
point( cur_rec
.GetX(), cur_rec
.GetY()+1 );
1480 if (item
->GetType() != A4AF_SOURCE
) {
1483 switch (client
->GetDownloadState()) {
1486 case DS_WAITCALLBACK
:
1487 case DS_TOOMANYCONNS
:
1488 image
= Client_Red_Smiley
;
1491 if (client
->IsRemoteQueueFull()) {
1492 image
= Client_Grey_Smiley
;
1494 image
= Client_Yellow_Smiley
;
1497 case DS_DOWNLOADING
:
1499 image
= Client_Green_Smiley
;
1501 case DS_NONEEDEDPARTS
:
1503 image
= Client_Grey_Smiley
;
1505 default: // DS_NONE i.e.
1506 image
= Client_White_Smiley
;
1509 m_ImageList
.Draw(image
, *dc
, point
.x
, point
.y
,
1510 wxIMAGELIST_DRAW_TRANSPARENT
);
1512 m_ImageList
.Draw(Client_Grey_Smiley
, *dc
, point
.x
, point
.y
,
1513 wxIMAGELIST_DRAW_TRANSPARENT
);
1517 wxPoint
point2( cur_rec
.GetX(), cur_rec
.GetY() + 1 );
1521 if ( client
->IsFriend() ) {
1522 clientImage
= Client_Friend_Smiley
;
1524 switch ( client
->GetClientSoft() ) {
1526 clientImage
= Client_aMule_Smiley
;
1529 case SO_NEW_MLDONKEY
:
1530 case SO_NEW2_MLDONKEY
:
1531 clientImage
= Client_mlDonkey_Smiley
;
1534 case SO_EDONKEYHYBRID
:
1535 clientImage
= Client_eDonkeyHybrid_Smiley
;
1538 clientImage
= Client_eMule_Smiley
;
1541 clientImage
= Client_lphant_Smiley
;
1544 case SO_NEW_SHAREAZA
:
1545 case SO_NEW2_SHAREAZA
:
1546 clientImage
= Client_Shareaza_Smiley
;
1549 clientImage
= Client_xMule_Smiley
;
1552 // cDonkey, Compatible, Unknown
1553 // No icon for those yet.
1554 // Using the eMule one + '?'
1555 clientImage
= Client_Unknown
;
1560 m_ImageList
.Draw(clientImage
, *dc
, point2
.x
, point
.y
,
1561 wxIMAGELIST_DRAW_TRANSPARENT
);
1563 if (client
->GetScoreRatio() > 1) {
1564 // Has credits, draw the gold star
1565 m_ImageList
.Draw(Client_CreditsYellow_Smiley
, *dc
, point2
.x
, point
.y
,
1566 wxIMAGELIST_DRAW_TRANSPARENT
);
1567 } else if ( client
->ExtProtocolAvailable() ) {
1568 // Ext protocol -> Draw the '+'
1569 m_ImageList
.Draw(Client_ExtendedProtocol_Smiley
, *dc
, point2
.x
, point
.y
,
1570 wxIMAGELIST_DRAW_TRANSPARENT
);
1573 if (client
->IsIdentified()) {
1575 m_ImageList
.Draw(Client_SecIdent_Smiley
, *dc
, point2
.x
, point
.y
,
1576 wxIMAGELIST_DRAW_TRANSPARENT
);
1577 } else if (client
->IsBadGuy()) {
1579 m_ImageList
.Draw(Client_BadGuy_Smiley
, *dc
, point2
.x
, point
.y
,
1580 wxIMAGELIST_DRAW_TRANSPARENT
);
1583 if (client
->HasObfuscatedConnectionBeenEstablished()) {
1584 // the "¿" except it's a key
1585 m_ImageList
.Draw(Client_Encryption_Smiley
, *dc
, point2
.x
, point
.y
,
1586 wxIMAGELIST_DRAW_TRANSPARENT
);
1590 #ifdef ENABLE_IP2COUNTRY
1592 const CountryData
& countrydata
= theApp
->amuledlg
->m_IP2Country
->GetCountryData(client
->GetFullIP());
1593 dc
->DrawBitmap(countrydata
.Flag
,
1594 rect
.x
+ 40, rect
.y
+ 5,
1595 wxIMAGELIST_DRAW_TRANSPARENT
);
1597 userName
<< countrydata
.Name
;
1599 userName
<< wxT(" - ");
1600 #endif // ENABLE_IP2COUNTRY
1601 if (client
->GetUserName().IsEmpty()) {
1602 userName
<< wxT("?");
1604 userName
<< client
->GetUserName();
1606 dc
->DrawText(userName
, rect
.GetX() + 60, rect
.GetY());
1610 case ColumnCompleted
: // completed
1611 if (item
->GetType() != A4AF_SOURCE
&& client
->GetTransferredDown()) {
1612 buffer
= CastItoXBytes(client
->GetTransferredDown());
1613 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1617 case ColumnSpeed
: // speed
1618 if (item
->GetType() != A4AF_SOURCE
&& client
->GetKBpsDown() > 0.001) {
1619 buffer
= wxString::Format(wxT("%.1f "),
1620 client
->GetKBpsDown()) + _("kB/s");
1621 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1625 case ColumnProgress
: // file info
1626 if ( thePrefs::ShowProgBar() ) {
1627 int iWidth
= rect
.GetWidth() - 2;
1628 int iHeight
= rect
.GetHeight() - 2;
1630 // don't draw Text beyond the bar
1631 dc
->SetClippingRegion(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
1633 if ( item
->GetType() != A4AF_SOURCE
) {
1634 uint32 dwTicks
= GetTickCount();
1636 wxMemoryDC cdcStatus
;
1638 if ( item
->dwUpdated
< dwTicks
|| !item
->status
||
1639 iWidth
!= item
->status
->GetWidth() ) {
1641 if (item
->status
== NULL
) {
1642 item
->status
= new wxBitmap(iWidth
, iHeight
);
1643 } else if ( item
->status
->GetWidth() != iWidth
) {
1644 // Only recreate if size has changed
1645 item
->status
->Create(iWidth
, iHeight
);
1648 cdcStatus
.SelectObject(*(item
->status
));
1650 if ( thePrefs::UseFlatBar() ) {
1651 DrawSourceStatusBar( client
, &cdcStatus
,
1652 wxRect(0, 0, iWidth
, iHeight
), true);
1654 DrawSourceStatusBar( client
, &cdcStatus
,
1655 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
1657 // Draw black border
1658 cdcStatus
.SetPen( *wxBLACK_PEN
);
1659 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
1660 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
1664 item
->dwUpdated
= dwTicks
+ 10000;
1666 cdcStatus
.SelectObject(*(item
->status
));
1669 dc
->Blit(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
1672 CPartFile
* p
= client
->GetRequestFile();
1674 a4af
= p
->GetFileName().GetPrintable();
1678 buffer
= CFormat(wxT("%s: %s")) % _("A4AF") % a4af
;
1680 int midx
= (2*rect
.GetX() + rect
.GetWidth()) >> 1;
1681 int midy
= (2*rect
.GetY() + rect
.GetHeight()) >> 1;
1683 wxCoord txtwidth
, txtheight
;
1685 dc
->GetTextExtent(buffer
, &txtwidth
, &txtheight
);
1687 dc
->SetTextForeground(*wxBLACK
);
1688 dc
->DrawText(buffer
, wxMax(rect
.GetX() + 2, midx
- (txtwidth
>> 1)), midy
- (txtheight
>> 1));
1690 // Draw black border
1691 dc
->SetPen( *wxBLACK_PEN
);
1692 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
1693 dc
->DrawRectangle( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
1698 case ColumnSources
: {
1700 dc
->DrawText(client
->GetClientVerString(), rect
.GetX(), rect
.GetY());
1704 case ColumnPriority
: // prio
1705 // We only show priority for sources actually queued for that file
1706 if ( item
->GetType() != A4AF_SOURCE
&&
1707 client
->GetDownloadState() == DS_ONQUEUE
) {
1708 if (client
->IsRemoteQueueFull()) {
1709 buffer
= _("Queue Full");
1710 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1712 if (client
->GetRemoteQueueRank()) {
1713 sint16 qrDiff
= client
->GetRemoteQueueRank() -
1714 client
->GetOldRemoteQueueRank();
1715 if(qrDiff
== client
->GetRemoteQueueRank() ) {
1718 wxColour savedColour
= dc
->GetTextForeground();
1720 dc
->SetTextForeground(*wxBLUE
);
1723 dc
->SetTextForeground(*wxRED
);
1725 //if( qrDiff == 0 ) {
1726 // dc->SetTextForeground(*wxLIGHT_GREY);
1728 buffer
= wxString::Format(_("QR: %u (%i)"),
1729 client
->GetRemoteQueueRank(), qrDiff
);
1730 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1731 dc
->SetTextForeground(savedColour
);
1737 case ColumnStatus
: // status
1738 if (item
->GetType() != A4AF_SOURCE
) {
1739 buffer
= DownloadStateToStr( client
->GetDownloadState(),
1740 client
->IsRemoteQueueFull() );
1742 buffer
= _("Asked for another file");
1743 if ( client
->GetRequestFile() &&
1744 client
->GetRequestFile()->GetFileName().IsOk()) {
1745 buffer
+= CFormat(wxT(" (%s)"))
1746 % client
->GetRequestFile()->GetFileName();
1749 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1751 // Source comes from?
1752 case ColumnTimeRemaining
: {
1753 buffer
= wxGetTranslation(OriginToText(client
->GetSourceFrom()));
1754 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
1761 wxString
CDownloadListCtrl::GetTTSText(unsigned item
) const
1763 CtrlItem_Struct
* content
= (CtrlItem_Struct
*)GetItemData(item
);
1765 if (content
->GetType() == FILE_TYPE
) {
1766 CPartFile
* file
= content
->GetFile();
1768 return file
->GetFileName().GetPrintable();
1771 return wxEmptyString
;
1775 int CDownloadListCtrl::SortProc(wxUIntPtr param1
, wxUIntPtr param2
, long sortData
)
1777 CtrlItem_Struct
* item1
= (CtrlItem_Struct
*)param1
;
1778 CtrlItem_Struct
* item2
= (CtrlItem_Struct
*)param2
;
1780 int sortMod
= (sortData
& CMuleListCtrl::SORT_DES
) ? -1 : 1;
1781 sortData
&= CMuleListCtrl::COLUMN_MASK
;
1784 if ( item1
->GetType() == FILE_TYPE
) {
1785 if ( item2
->GetType() == FILE_TYPE
) {
1786 // Both are files, so we just compare them
1787 comp
= Compare( item1
->GetFile(), item2
->GetFile(), sortData
);
1789 // A file and a source, checking if they belong to each other
1790 if ( item1
->GetFile() == item2
->GetOwner() ) {
1791 // A file should always be above its sources
1792 // Returning directly to avoid the modifier
1795 // Source belongs to anther file, so we compare the files instead
1796 comp
= Compare( item1
->GetFile(), item2
->GetOwner(), sortData
);
1800 if ( item2
->GetType() == FILE_TYPE
) {
1801 // A source and a file, checking if they belong to each other
1802 if ( item1
->GetOwner() == item2
->GetFile() ) {
1803 // A source should always be below its file
1804 // Returning directly to avoid the modifier
1807 // Source belongs to anther file, so we compare the files instead
1808 comp
= Compare( item1
->GetOwner(), item2
->GetFile(), sortData
);
1811 // Two sources, some different possibilites
1812 if ( item1
->GetOwner() == item2
->GetOwner() ) {
1813 // Avilable sources first, if we have both an
1814 // available and an unavailable
1815 comp
= ( item2
->GetType() - item1
->GetType() );
1818 // A4AF and non-A4AF. The order is fixed regardless of sort-order.
1821 comp
= Compare(item1
->GetSource(), item2
->GetSource(), sortData
);
1824 // Belongs to different files, so we compare the files
1825 comp
= Compare( item1
->GetOwner(), item2
->GetOwner(), sortData
);
1830 // We modify the result so that it matches with ascending or decending
1831 return sortMod
* comp
;
1835 int CDownloadListCtrl::Compare( const CPartFile
* file1
, const CPartFile
* file2
, long lParamSort
)
1839 switch (lParamSort
) {
1840 // Sort by part number
1843 file1
->GetPartMetFileName().RemoveAllExt(),
1844 file2
->GetPartMetFileName().RemoveAllExt() );
1848 case ColumnFileName
:
1850 file1
->GetFileName(),
1851 file2
->GetFileName() );
1857 file1
->GetFileSize(),
1858 file2
->GetFileSize() );
1861 // Sort by transferred
1862 case ColumnTransferred
:
1864 file1
->GetTransferred(),
1865 file2
->GetTransferred() );
1868 // Sort by completed
1869 case ColumnCompleted
:
1871 file1
->GetCompletedSize(),
1872 file2
->GetCompletedSize() );
1878 file1
->GetKBpsDown() * 1024,
1879 file2
->GetKBpsDown() * 1024 );
1882 // Sort by percentage completed
1883 case ColumnProgress
:
1885 file1
->GetPercentCompleted(),
1886 file2
->GetPercentCompleted() );
1889 // Sort by number of sources
1892 file1
->GetSourceCount(),
1893 file2
->GetSourceCount() );
1897 case ColumnPriority
:
1899 file1
->GetDownPriority(),
1900 file2
->GetDownPriority() );
1906 file1
->getPartfileStatusRang(),
1907 file2
->getPartfileStatusRang() );
1910 // Sort by remaining time
1911 case ColumnTimeRemaining
:
1912 if (file1
->getTimeRemaining() == -1) {
1913 if (file2
->getTimeRemaining() == -1) {
1919 if (file2
->getTimeRemaining() == -1) {
1923 file1
->getTimeRemaining(),
1924 file2
->getTimeRemaining() );
1929 // Sort by last seen complete
1930 case ColumnLastSeenComplete
:
1932 file1
->lastseencomplete
,
1933 file2
->lastseencomplete
);
1936 // Sort by last reception
1937 case ColumnLastReception
:
1939 file1
->GetLastChangeDatetime(),
1940 file2
->GetLastChangeDatetime() );
1948 int CDownloadListCtrl::Compare(
1949 const CUpDownClient
* client1
, const CUpDownClient
* client2
, long lParamSort
)
1951 switch (lParamSort
) {
1954 case ColumnFileName
:
1955 return CmpAny( client1
->GetUserName(), client2
->GetUserName() );
1957 // Sort by status (size field)
1959 return CmpAny( client1
->GetDownloadState(), client2
->GetDownloadState() );
1961 // Sort by transferred in the following fields
1962 case ColumnTransferred
:
1963 case ColumnCompleted
:
1964 return CmpAny( client1
->GetTransferredDown(), client2
->GetTransferredDown() );
1968 return CmpAny( client1
->GetKBpsDown(), client2
->GetKBpsDown() );
1970 // Sort by parts offered (Progress field)
1971 case ColumnProgress
:
1973 client1
->GetAvailablePartCount(),
1974 client2
->GetAvailablePartCount() );
1976 // Sort by client version
1977 case ColumnSources
: {
1978 if (client1
->GetClientSoft() != client2
->GetClientSoft()) {
1979 return client1
->GetSoftStr().Cmp(client2
->GetSoftStr());
1982 if (client1
->GetVersion() != client2
->GetVersion()) {
1983 return CmpAny(client1
->GetVersion(), client2
->GetVersion());
1986 return client1
->GetClientModString().Cmp(client2
->GetClientModString());
1989 // Sort by Queue-Rank
1990 case ColumnPriority
: {
1991 // This will sort by download state: Downloading, OnQueue, Connecting ...
1992 // However, Asked For Another will always be placed last, due to
1993 // sorting in SortProc
1994 if ( client1
->GetDownloadState() != client2
->GetDownloadState() ) {
1995 return client1
->GetDownloadState() - client2
->GetDownloadState();
1998 // Placing items on queue before items on full queues
1999 if ( client1
->IsRemoteQueueFull() ) {
2000 if ( client2
->IsRemoteQueueFull() ) {
2005 } else if ( client2
->IsRemoteQueueFull() ) {
2008 if ( client1
->GetRemoteQueueRank() ) {
2009 if ( client2
->GetRemoteQueueRank() ) {
2011 client1
->GetRemoteQueueRank(),
2012 client2
->GetRemoteQueueRank() );
2017 if ( client2
->GetRemoteQueueRank() ) {
2027 case ColumnStatus
: {
2028 if (client1
->GetDownloadState() == client2
->GetDownloadState()) {
2030 client1
->IsRemoteQueueFull(),
2031 client2
->IsRemoteQueueFull() );
2034 client1
->GetDownloadState(),
2035 client2
->GetDownloadState() );
2039 // Source of source ;)
2040 case ColumnTimeRemaining
:
2041 return CmpAny(client1
->GetSourceFrom(), client2
->GetSourceFrom());
2049 void CDownloadListCtrl::ClearCompleted()
2051 m_completedFiles
= false;
2052 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(false);
2054 // Search for completed files
2055 for ( ListItems::iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); ) {
2056 CtrlItem_Struct
* item
= it
->second
; ++it
;
2058 if ( item
->GetType() == FILE_TYPE
) {
2059 CPartFile
* file
= item
->GetFile();
2061 if ( file
->IsPartFile() == false ) {
2069 void CDownloadListCtrl::ShowFilesCount( int diff
)
2071 m_filecount
+= diff
;
2073 wxString str
= wxString::Format( _("Downloads (%i)"), m_filecount
);
2074 wxStaticText
* label
= CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText
);
2076 label
->SetLabel( str
);
2077 label
->GetParent()->Layout();
2083 bool CDownloadListCtrl::ShowItemInCurrentCat(
2084 const CPartFile
* file
, int newsel
) const
2087 ((newsel
== 0 && !thePrefs::ShowAllNotCats()) ||
2088 (newsel
== 0 && thePrefs::ShowAllNotCats() && file
->GetCategory() == 0)) ||
2089 (newsel
> 0 && newsel
== file
->GetCategory());
2092 static const CMuleColour
crHave(104, 104, 104);
2093 static const CMuleColour
crFlatHave(0, 0, 0);
2095 static const CMuleColour
crPending(255, 208, 0);
2096 static const CMuleColour
crFlatPending(255, 255, 100);
2098 static const CMuleColour
crProgress(0, 224, 0);
2099 static const CMuleColour
crFlatProgress(0, 150, 0);
2101 static const CMuleColour
crMissing(255, 0, 0);
2103 void CDownloadListCtrl::DrawFileStatusBar(
2104 const CPartFile
* file
, wxDC
* dc
, const wxRect
& rect
, bool bFlat
) const
2106 static CBarShader
s_ChunkBar(16);
2108 s_ChunkBar
.SetHeight(rect
.height
);
2109 s_ChunkBar
.SetWidth(rect
.width
);
2110 s_ChunkBar
.SetFileSize( file
->GetFileSize() );
2111 s_ChunkBar
.Set3dDepth( thePrefs::Get3DDepth() );
2113 if ( file
->GetStatus() == PS_COMPLETE
|| file
->GetStatus() == PS_COMPLETING
) {
2114 s_ChunkBar
.Fill( bFlat
? crFlatProgress
: crProgress
);
2115 s_ChunkBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
2119 // Part availability ( of missing parts )
2120 const CPartFile::CGapPtrList
& gaplist
= file
->GetGapList();
2121 CPartFile::CGapPtrList::const_iterator it
= gaplist
.begin();
2122 uint64 lastGapEnd
= 0;
2125 for (; it
!= gaplist
.end(); ++it
) {
2126 Gap_Struct
* gap
= *it
;
2129 uint32 start
= ( gap
->start
/ PARTSIZE
);
2130 // fill the Have-Part (between this gap and the last)
2132 s_ChunkBar
.FillRange(lastGapEnd
+ 1, gap
->start
- 1, bFlat
? crFlatHave
: crHave
);
2134 lastGapEnd
= gap
->end
;
2136 uint32 end
= ( gap
->end
/ PARTSIZE
) + 1;
2138 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
2139 if ( end
> file
->GetPartCount() ) {
2140 end
= file
->GetPartCount();
2143 // Place each gap, one PART at a time
2144 for ( uint64 i
= start
; i
< end
; ++i
) {
2145 if ( i
< file
->m_SrcpartFrequency
.size() && file
->m_SrcpartFrequency
[i
]) {
2146 int blue
= 210 - ( 22 * ( file
->m_SrcpartFrequency
[i
] - 1 ) );
2147 colour
.Set(0, ( blue
< 0 ? 0 : blue
), 255 );
2152 if ( file
->IsStopped() ) {
2156 uint64 gap_begin
= ( i
== start
? gap
->start
: PARTSIZE
* i
);
2157 uint64 gap_end
= ( i
== end
- 1 ? gap
->end
: PARTSIZE
* ( i
+ 1 ) - 1 );
2159 s_ChunkBar
.FillRange( gap_begin
, gap_end
, colour
);
2163 // fill the last Have-Part (between this gap and the last)
2164 s_ChunkBar
.FillRange(lastGapEnd
+ 1, file
->GetFileSize() - 1, bFlat
? crFlatHave
: crHave
);
2167 const CPartFile::CReqBlockPtrList
& requestedblocks_list
= file
->GetRequestedBlockList();
2168 CPartFile::CReqBlockPtrList::const_iterator it2
= requestedblocks_list
.begin();
2169 // adjacing pending parts must be joined to avoid bright lines between them
2170 uint64 lastStartOffset
= 0;
2171 uint64 lastEndOffset
= 0;
2173 colour
= bFlat
? crFlatPending
: crPending
;
2175 if ( file
->IsStopped() ) {
2179 for (; it2
!= requestedblocks_list
.end(); ++it2
) {
2181 if ((*it2
)->StartOffset
> lastEndOffset
+ 1) {
2182 // not adjacing, draw last block
2183 s_ChunkBar
.FillRange(lastStartOffset
, lastEndOffset
, colour
);
2184 lastStartOffset
= (*it2
)->StartOffset
;
2185 lastEndOffset
= (*it2
)->EndOffset
;
2187 // adjacing, grow block
2188 lastEndOffset
= (*it2
)->EndOffset
;
2192 s_ChunkBar
.FillRange(lastStartOffset
, lastEndOffset
, colour
);
2195 // Draw the progress-bar
2196 s_ChunkBar
.Draw( dc
, rect
.x
, rect
.y
, bFlat
);
2199 // Green progressbar width
2200 int width
= (int)(( (float)rect
.width
/ (float)file
->GetFileSize() ) *
2201 file
->GetCompletedSize() );
2204 dc
->SetBrush( crFlatProgress
.GetBrush() );
2206 dc
->DrawRectangle( rect
.x
, rect
.y
, width
, 3 );
2208 // Draw the two black lines for 3d-effect
2209 dc
->SetPen( *wxBLACK_PEN
);
2210 dc
->DrawLine( rect
.x
, rect
.y
+ 0, rect
.x
+ width
, rect
.y
+ 0 );
2211 dc
->DrawLine( rect
.x
, rect
.y
+ 2, rect
.x
+ width
, rect
.y
+ 2 );
2213 // Draw the green line
2214 dc
->SetPen( *(wxThePenList
->FindOrCreatePen( crProgress
, 1, wxSOLID
) ));
2215 dc
->DrawLine( rect
.x
, rect
.y
+ 1, rect
.x
+ width
, rect
.y
+ 1 );
2219 static const CMuleColour
crBoth(0, 192, 0);
2220 static const CMuleColour
crFlatBoth(0, 150, 0);
2222 static const CMuleColour
crNeither(240, 240, 240);
2223 static const CMuleColour
crFlatNeither(224, 224, 224);
2225 #define crClientOnly crHave
2226 #define crFlatClientOnly crFlatHave
2227 #define crNextPending crFlatPending
2229 void CDownloadListCtrl::DrawSourceStatusBar(
2230 const CUpDownClient
* source
, wxDC
* dc
, const wxRect
& rect
, bool bFlat
) const
2232 static CBarShader
s_StatusBar(16);
2234 CPartFile
* reqfile
= source
->GetRequestFile();
2236 s_StatusBar
.SetFileSize( reqfile
->GetFileSize() );
2237 s_StatusBar
.SetHeight(rect
.height
);
2238 s_StatusBar
.SetWidth(rect
.width
);
2239 s_StatusBar
.Set3dDepth( thePrefs::Get3DDepth() );
2241 // Barry - was only showing one part from client, even when reserved bits from 2 parts
2242 wxString gettingParts
= source
->ShowDownloadingParts();
2244 const BitVector
& partStatus
= source
->GetPartStatus();
2247 for ( uint64 i
= 0; i
< partStatus
.size(); i
++ ) {
2248 uint64 uStart
= PARTSIZE
* i
;
2249 uEnd
= wxMin(reqfile
->GetFileSize(), uStart
+ PARTSIZE
) - 1;
2252 if (!partStatus
[i
]) {
2253 colour
= bFlat
? crFlatNeither
: crNeither
;
2254 } else if ( reqfile
->IsComplete(uStart
, uEnd
)) {
2255 colour
= bFlat
? crFlatBoth
: crBoth
;
2256 } else if ( source
->GetDownloadState() == DS_DOWNLOADING
&&
2257 source
->GetLastBlockOffset() <= uEnd
&&
2258 source
->GetLastBlockOffset() >= uStart
) {
2260 } else if (gettingParts
.GetChar((uint16
)i
) == 'Y') {
2261 colour
= crNextPending
;
2263 colour
= bFlat
? crFlatClientOnly
: crClientOnly
;
2266 if ( source
->GetRequestFile()->IsStopped() ) {
2270 s_StatusBar
.FillRange(uStart
, uEnd
, colour
);
2272 // fill the rest (if partStatus is empty)
2273 s_StatusBar
.FillRange(uEnd
+ 1, reqfile
->GetFileSize() - 1, bFlat
? crFlatNeither
: crNeither
);
2275 s_StatusBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
2279 # define QUOTE wxT("\"")
2281 # define QUOTE wxT("\'")
2284 void CDownloadListCtrl::PreviewFile(CPartFile
* file
)
2287 // If no player set in preferences, use mplayer.
2288 // And please, do a warning also :P
2289 if (thePrefs::GetVideoPlayer().IsEmpty()) {
2291 "To prevent this warning to show up in every preview,\nset your preferred video player in preferences (default is mplayer)."),
2292 _("File preview"), wxOK
, this);
2293 // Since newer versions for some reason mplayer does not automatically
2294 // select video output device and needs a parameter, go figure...
2295 command
= wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE
wxT("$file") QUOTE
;
2297 command
= thePrefs::GetVideoPlayer();
2300 // Check if we are (pre)viewing a completed file or not
2301 if (file
->GetStatus() != PS_COMPLETE
) {
2302 // Remove the .met and see if out video player specifiation uses the magic string
2303 wxString fileWithoutMet
= thePrefs::GetTempDir().JoinPaths(
2304 file
->GetPartMetFileName().RemoveExt()).GetRaw();
2305 if (!command
.Replace(wxT("$file"), fileWithoutMet
)) {
2306 // No magic string, so we just append the filename to the player command
2307 // Need to use quotes in case filename contains spaces
2308 command
<< wxT(" ") << QUOTE
<< fileWithoutMet
<< QUOTE
;
2311 // This is a complete file
2312 // FIXME: This is probably not going to work if the filenames are mangled ...
2313 wxString rawFileName
= file
->GetFullName().GetRaw();
2314 if (!command
.Replace(wxT("$file"), rawFileName
)) {
2315 // No magic string, so we just append the filename to the player command
2316 // Need to use quotes in case filename contains spaces
2317 command
<< wxT(" ") << QUOTE
<< rawFileName
<< QUOTE
;
2321 // We can't use wxShell here, it blocks the app
2322 CTerminationProcess
*p
= new CTerminationProcess(command
);
2323 int ret
= wxExecute(command
, wxEXEC_ASYNC
, p
);
2328 CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
2332 // File_checked_for_headers