2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 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 "DownloadListCtrl.h" // Interface declarations
28 #include <protocol/ed2k/ClientSoftware.h>
29 #include <common/MenuIDs.h>
31 #include <common/Format.h> // Needed for CFormat
32 #include "amule.h" // Needed for theApp
33 #include "amuleDlg.h" // Needed for CamuleDlg
34 #include "BarShader.h" // Needed for CBarShader
35 #include "CommentDialogLst.h" // Needed for CCommentDialogLst
36 #include "DataToText.h" // Needed for PriorityToStr
37 #include "DownloadQueue.h"
38 #include "FileDetailDialog.h" // Needed for CFileDetailDialog
39 #include "GuiEvents.h" // Needed for CoreNotify_*
41 #include "muuli_wdr.h" // Needed for ID_DLOADLIST
42 #include "PartFile.h" // Needed for CPartFile
43 #include "Preferences.h"
44 #include "SharedFileList.h" // Needed for CSharedFileList
45 #include "TerminationProcess.h" // Needed for CTerminationProcess
46 #include "TransferWnd.h"
47 #include "SourceListCtrl.h"
52 struct FileCtrlItem_Struct
60 ~FileCtrlItem_Struct() {
64 CPartFile
* GetFile() const {
68 void SetContents(CPartFile
* file
) {
76 CPartFile
* m_fileValue
;
79 #define m_ImageList theApp->amuledlg->m_imagelist
93 ColumnLastSeenComplete
,
98 BEGIN_EVENT_TABLE(CDownloadListCtrl
, CMuleListCtrl
)
99 EVT_LIST_ITEM_ACTIVATED(ID_DLOADLIST
, CDownloadListCtrl::OnItemActivated
)
100 EVT_LIST_ITEM_RIGHT_CLICK(ID_DLOADLIST
, CDownloadListCtrl::OnMouseRightClick
)
101 EVT_LIST_ITEM_MIDDLE_CLICK(ID_DLOADLIST
, CDownloadListCtrl::OnMouseMiddleClick
)
102 EVT_LIST_ITEM_SELECTED(ID_DLOADLIST
, CDownloadListCtrl::OnItemSelectionChanged
)
103 EVT_LIST_ITEM_DESELECTED(ID_DLOADLIST
, CDownloadListCtrl::OnItemSelectionChanged
)
105 EVT_CHAR( CDownloadListCtrl::OnKeyPressed
)
107 EVT_MENU( MP_CANCEL
, CDownloadListCtrl::OnCancelFile
)
109 EVT_MENU( MP_PAUSE
, CDownloadListCtrl::OnSetStatus
)
110 EVT_MENU( MP_STOP
, CDownloadListCtrl::OnSetStatus
)
111 EVT_MENU( MP_RESUME
, CDownloadListCtrl::OnSetStatus
)
113 EVT_MENU( MP_PRIOLOW
, CDownloadListCtrl::OnSetPriority
)
114 EVT_MENU( MP_PRIONORMAL
, CDownloadListCtrl::OnSetPriority
)
115 EVT_MENU( MP_PRIOHIGH
, CDownloadListCtrl::OnSetPriority
)
116 EVT_MENU( MP_PRIOAUTO
, CDownloadListCtrl::OnSetPriority
)
118 EVT_MENU( MP_SWAP_A4AF_TO_THIS
, CDownloadListCtrl::OnSwapSources
)
119 EVT_MENU( MP_SWAP_A4AF_TO_THIS_AUTO
, CDownloadListCtrl::OnSwapSources
)
120 EVT_MENU( MP_SWAP_A4AF_TO_ANY_OTHER
, CDownloadListCtrl::OnSwapSources
)
122 EVT_MENU_RANGE( MP_ASSIGNCAT
, MP_ASSIGNCAT
+ 99, CDownloadListCtrl::OnSetCategory
)
124 EVT_MENU( MP_CLEARCOMPLETED
, CDownloadListCtrl::OnClearCompleted
)
126 EVT_MENU( MP_GETMAGNETLINK
, CDownloadListCtrl::OnGetLink
)
127 EVT_MENU( MP_GETED2KLINK
, CDownloadListCtrl::OnGetLink
)
129 EVT_MENU( MP_METINFO
, CDownloadListCtrl::OnViewFileInfo
)
130 EVT_MENU( MP_VIEW
, CDownloadListCtrl::OnPreviewFile
)
131 EVT_MENU( MP_VIEWFILECOMMENTS
, CDownloadListCtrl::OnViewFileComments
)
133 EVT_MENU( MP_WS
, CDownloadListCtrl::OnGetFeedback
)
137 //! This listtype is used when gathering the selected items.
138 typedef std::list
<FileCtrlItem_Struct
*> ItemList
;
140 CDownloadListCtrl::CDownloadListCtrl(
141 wxWindow
*parent
, wxWindowID winid
, const wxPoint
& pos
, const wxSize
& size
,
142 long style
, const wxValidator
& validator
, const wxString
& name
)
144 CMuleListCtrl( parent
, winid
, pos
, size
, style
| wxLC_OWNERDRAW
, validator
, name
)
146 // Setting the sorter function.
147 SetSortFunc( SortProc
);
149 // Set the table-name (for loading and saving preferences).
150 SetTableName( wxT("Download") );
154 m_hilightBrush
= CMuleColour(wxSYS_COLOUR_HIGHLIGHT
).Blend(125).GetBrush();
156 m_hilightUnfocusBrush
= CMuleColour(wxSYS_COLOUR_BTNSHADOW
).Blend(125).GetBrush();
158 InsertColumn( ColumnPart
, _("Part"), wxLIST_FORMAT_LEFT
, 30, wxT("a") );
159 InsertColumn( ColumnFileName
, _("File Name"), wxLIST_FORMAT_LEFT
, 260, wxT("N") );
160 InsertColumn( ColumnSize
, _("Size"), wxLIST_FORMAT_LEFT
, 60, wxT("Z") );
161 InsertColumn( ColumnTransferred
, _("Transferred"), wxLIST_FORMAT_LEFT
, 65, wxT("T") );
162 InsertColumn( ColumnCompleted
, _("Completed"), wxLIST_FORMAT_LEFT
, 65, wxT("C") );
163 InsertColumn( ColumnSpeed
, _("Speed"), wxLIST_FORMAT_LEFT
, 65, wxT("S") );
164 InsertColumn( ColumnProgress
, _("Progress"), wxLIST_FORMAT_LEFT
, 170, wxT("P") );
165 InsertColumn( ColumnSources
, _("Sources"), wxLIST_FORMAT_LEFT
, 50, wxT("u") );
166 InsertColumn( ColumnPriority
, _("Priority"), wxLIST_FORMAT_LEFT
, 55, wxT("p") );
167 InsertColumn( ColumnStatus
, _("Status"), wxLIST_FORMAT_LEFT
, 70, wxT("s") );
168 InsertColumn( ColumnTimeRemaining
, _("Time Remaining"), wxLIST_FORMAT_LEFT
, 110, wxT("r") );
169 InsertColumn( ColumnLastSeenComplete
, _("Last Seen Complete"), wxLIST_FORMAT_LEFT
, 220, wxT("c") );
170 InsertColumn( ColumnLastReception
, _("Last Reception"), wxLIST_FORMAT_LEFT
, 220, wxT("R") );
174 m_ItemSelectionChangePending
= false;
180 // This is the order the columns had before extendable list-control settings save/load code was introduced.
181 // Don't touch when inserting new columns!
182 wxString
CDownloadListCtrl::GetOldColumnOrder() const
184 return wxT("N,Z,T,C,S,P,u,p,s,r,c,R");
187 CDownloadListCtrl::~CDownloadListCtrl()
189 while ( !m_ListItems
.empty() ) {
190 delete m_ListItems
.begin()->second
;
191 m_ListItems
.erase( m_ListItems
.begin() );
195 void CDownloadListCtrl::AddFile( CPartFile
* file
)
199 // Avoid duplicate entries of files
200 if ( m_ListItems
.find( file
) == m_ListItems
.end() ) {
201 FileCtrlItem_Struct
* newitem
= new FileCtrlItem_Struct
;
202 newitem
->SetContents(file
);
204 m_ListItems
.insert( ListItemsPair( file
, newitem
) );
206 // Check if the new file is visible in the current category
207 if ( file
->CheckShowItemInGivenCat( m_category
) ) {
208 ShowFile( file
, true );
209 if (file
->IsCompleted()) {
210 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(true);
217 void CDownloadListCtrl::RemoveFile( CPartFile
* file
)
221 // Ensure that any list-entries are removed
222 ShowFile( file
, false );
224 // Find the assosiated list-item
225 ListItems::iterator it
= m_ListItems
.find( file
);
227 if ( it
!= m_ListItems
.end() ) {
230 m_ListItems
.erase( it
);
235 void CDownloadListCtrl::UpdateItem(const void* toupdate
)
237 // Retrieve all entries matching the source
238 ListIteratorPair rangeIt
= m_ListItems
.equal_range( toupdate
);
240 // Visible lines, default to all because not all platforms
241 // support the GetVisibleLines function
242 long first
= 0, last
= GetItemCount();
245 // Get visible lines if we need them
246 if ( rangeIt
.first
!= rangeIt
.second
) {
247 GetVisibleLines( &first
, &last
);
251 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
252 FileCtrlItem_Struct
* item
= it
->second
;
254 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
256 // Determine if the file should be shown in the current category
258 CPartFile
* file
= item
->GetFile();
260 bool show
= file
->CheckShowItemInGivenCat( m_category
);
266 // Only update visible lines
267 if ( index
>= first
&& index
<= last
) {
268 RefreshItem( index
);
271 // Item should no longer be shown in
272 // the current category
273 ShowFile( file
, false );
276 // Item has been hidden but new status means
277 // that it should it should be shown in the
279 ShowFile( file
, true );
282 if (file
->IsCompleted() && show
) {
283 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(true);
289 void CDownloadListCtrl::ShowFile( CPartFile
* file
, bool show
)
293 ListItems::iterator it
= m_ListItems
.find( file
);
295 if ( it
!= m_ListItems
.end() ) {
296 FileCtrlItem_Struct
* item
= it
->second
;
299 // Check if the file is already being displayed
300 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
302 long newitem
= InsertItem( GetItemCount(), wxEmptyString
);
304 SetItemPtrData( newitem
, reinterpret_cast<wxUIntPtr
>(item
) );
307 myitem
.m_itemId
= newitem
;
308 myitem
.SetBackgroundColour( GetBackgroundColour() );
312 RefreshItem( newitem
);
317 // Try to find the file and remove it
318 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
321 ShowFilesCount( -1 );
327 void CDownloadListCtrl::ChangeCategory( int newCategory
)
331 bool hasCompletedDownloads
= false;
333 // remove all displayed files with a different cat and show the correct ones
334 for (ListItems::const_iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); it
++) {
336 CPartFile
* file
= it
->second
->GetFile();
338 bool curVisibility
= file
->CheckShowItemInGivenCat( m_category
);
339 bool newVisibility
= file
->CheckShowItemInGivenCat( newCategory
);
341 if (newVisibility
&& file
->IsCompleted()) {
342 hasCompletedDownloads
= true;
345 // Check if the visibility of the file has changed. However, if the
346 // current category is the default (0) category, then we can't use
347 // curVisiblity to see if the visibility has changed but instead
348 // have to let ShowFile() check if the file is or isn't on the list.
349 if ( curVisibility
!= newVisibility
|| !newCategory
) {
350 ShowFile( file
, newVisibility
);
354 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(hasCompletedDownloads
);
358 m_category
= newCategory
;
362 uint8
CDownloadListCtrl::GetCategory() const
369 * Helper-function: This function is used to gather selected items.
371 * @param list A pointer to the list to gather items from.
372 * @return A list containing the selected items.
374 ItemList
GetSelectedItems( CDownloadListCtrl
* list
)
378 long index
= list
->GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
380 while ( index
> -1 ) {
381 FileCtrlItem_Struct
* item
= (FileCtrlItem_Struct
*)list
->GetItemData( index
);
382 results
.push_back( item
);
384 index
= list
->GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
391 void CDownloadListCtrl::OnCancelFile(wxCommandEvent
& WXUNUSED(event
))
393 ItemList files
= ::GetSelectedItems(this);
394 for (ItemList::iterator it
= files
.begin(); it
!= files
.end(); ) {
395 ItemList::iterator it1
= it
++;
396 CPartFile
* file
= (*it1
)->GetFile();
398 switch (file
->GetStatus()) {
399 case PS_WAITINGFORHASH
:
410 if (files
.size() == 1) {
411 question
= _("Are you sure that you wish to delete the selected file?");
413 question
= _("Are you sure that you wish to delete the selected files?");
415 if (wxMessageBox( question
, _("Cancel"), wxICON_QUESTION
| wxYES_NO
, this) == wxYES
) {
416 for (ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
417 CPartFile
* file
= (*it
)->GetFile();
419 CoreNotify_PartFile_Delete(file
);
427 void CDownloadListCtrl::OnSetPriority( wxCommandEvent
& event
)
430 switch ( event
.GetId() ) {
431 case MP_PRIOLOW
: priority
= PR_LOW
; break;
432 case MP_PRIONORMAL
: priority
= PR_NORMAL
; break;
433 case MP_PRIOHIGH
: priority
= PR_HIGH
; break;
434 case MP_PRIOAUTO
: priority
= PR_AUTO
; break;
439 ItemList files
= ::GetSelectedItems( this );
441 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
442 CPartFile
* file
= (*it
)->GetFile();
444 if ( priority
== PR_AUTO
) {
445 CoreNotify_PartFile_PrioAuto( file
, true );
447 CoreNotify_PartFile_PrioAuto( file
, false );
449 CoreNotify_PartFile_PrioSet( file
, priority
, true );
455 void CDownloadListCtrl::OnSwapSources( wxCommandEvent
& event
)
457 ItemList files
= ::GetSelectedItems( this );
459 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
460 CPartFile
* file
= (*it
)->GetFile();
462 switch ( event
.GetId() ) {
463 case MP_SWAP_A4AF_TO_THIS
:
464 CoreNotify_PartFile_Swap_A4AF( file
);
467 case MP_SWAP_A4AF_TO_THIS_AUTO
:
468 CoreNotify_PartFile_Swap_A4AF_Auto( file
);
471 case MP_SWAP_A4AF_TO_ANY_OTHER
:
472 CoreNotify_PartFile_Swap_A4AF_Others( file
);
479 void CDownloadListCtrl::OnSetCategory( wxCommandEvent
& event
)
481 ItemList files
= ::GetSelectedItems( this );
483 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
484 CoreNotify_PartFile_SetCat( (*it
)->GetFile(), event
.GetId() - MP_ASSIGNCAT
);
485 ShowFile((*it
)->GetFile(), false);
488 OnItemSelectionChanged(ev
); // clear clients that may have been shown
490 ChangeCategory( m_category
); // This only updates the visibility of the clear completed button
491 theApp
->amuledlg
->m_transferwnd
->UpdateCatTabTitles();
495 void CDownloadListCtrl::OnSetStatus( wxCommandEvent
& event
)
497 ItemList files
= ::GetSelectedItems( this );
499 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
500 CPartFile
* file
= (*it
)->GetFile();
502 switch ( event
.GetId() ) {
504 CoreNotify_PartFile_Pause( file
);
508 CoreNotify_PartFile_Resume( file
);
512 CoreNotify_PartFile_Stop( file
);
519 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent
& WXUNUSED(event
) )
525 void CDownloadListCtrl::OnGetLink(wxCommandEvent
& event
)
527 ItemList files
= ::GetSelectedItems( this );
531 for ( ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
532 CPartFile
* file
= (*it
)->GetFile();
534 if ( event
.GetId() == MP_GETED2KLINK
) {
535 URIs
+= theApp
->CreateED2kLink( file
) + wxT("\n");
537 URIs
+= theApp
->CreateMagnetLink( file
) + wxT("\n");
541 if ( !URIs
.IsEmpty() ) {
542 theApp
->CopyTextToClipboard( URIs
.BeforeLast(wxT('\n')) );
547 void CDownloadListCtrl::OnGetFeedback(wxCommandEvent
& WXUNUSED(event
))
550 ItemList files
= ::GetSelectedItems( this );
552 for (ItemList::iterator it
= files
.begin(); it
!= files
.end(); ++it
) {
553 if (feed
.IsEmpty()) {
554 feed
= CFormat(_("Feedback from: %s (%s)\n\n")) % thePrefs::GetUserNick() % theApp
->GetFullMuleVersion();
558 feed
+= (*it
)->GetFile()->GetFeedback();
561 if (!feed
.IsEmpty()) {
562 theApp
->CopyTextToClipboard(feed
);
566 void CDownloadListCtrl::OnViewFileInfo( wxCommandEvent
& WXUNUSED(event
) )
568 long index
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
571 ShowFileDetailDialog(index
);
576 void CDownloadListCtrl::OnViewFileComments( wxCommandEvent
& WXUNUSED(event
) )
578 ItemList files
= ::GetSelectedItems( this );
580 if ( files
.size() == 1 ) {
581 CCommentDialogLst
dialog( this, files
.front()->GetFile() );
586 void CDownloadListCtrl::OnPreviewFile( wxCommandEvent
& WXUNUSED(event
) )
588 ItemList files
= ::GetSelectedItems( this );
590 if ( files
.size() == 1 ) {
591 PreviewFile(files
.front()->GetFile());
595 void CDownloadListCtrl::OnItemActivated( wxListEvent
& evt
)
597 CPartFile
* file
= ((FileCtrlItem_Struct
*)GetItemData( evt
.GetIndex()))->GetFile();
599 if ((!file
->IsPartFile() || file
->IsCompleted()) && file
->PreviewAvailable()) {
604 void CDownloadListCtrl::OnItemSelectionChanged( wxListEvent
& )
606 if (!m_ItemSelectionChangePending
&& !IsSorting()) {
607 m_ItemSelectionChangePending
= true;
608 Notify_DownloadCtrlDoItemSelectionChanged();
612 void CDownloadListCtrl::DoItemSelectionChanged()
614 m_ItemSelectionChangePending
= false;
615 CKnownFileVector filesVector
;
616 filesVector
.reserve(GetSelectedItemCount());
618 long index
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
620 while ( index
> -1 ) {
621 CPartFile
* file
= ((FileCtrlItem_Struct
*)GetItemData( index
))->GetFile();
622 if (file
->IsPartFile()) {
623 filesVector
.push_back(file
);
625 index
= GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
628 std::sort(filesVector
.begin(), filesVector
.end());
629 theApp
->amuledlg
->m_transferwnd
->clientlistctrl
->ShowSources(filesVector
);
632 void CDownloadListCtrl::OnMouseRightClick(wxListEvent
& evt
)
634 long index
= CheckSelection(evt
);
642 FileCtrlItem_Struct
* item
= (FileCtrlItem_Struct
*)GetItemData( index
);
643 m_menu
= new wxMenu( _("Downloads") );
645 wxMenu
* priomenu
= new wxMenu();
646 priomenu
->AppendCheckItem(MP_PRIOLOW
, _("Low"));
647 priomenu
->AppendCheckItem(MP_PRIONORMAL
, _("Normal"));
648 priomenu
->AppendCheckItem(MP_PRIOHIGH
, _("High"));
649 priomenu
->AppendCheckItem(MP_PRIOAUTO
, _("Auto"));
651 m_menu
->Append(MP_MENU_PRIO
, _("Priority"), priomenu
);
652 m_menu
->Append(MP_CANCEL
, _("Cancel"));
653 m_menu
->Append(MP_STOP
, _("&Stop"));
654 m_menu
->Append(MP_PAUSE
, _("&Pause"));
655 m_menu
->Append(MP_RESUME
, _("&Resume"));
656 m_menu
->Append(MP_CLEARCOMPLETED
, _("C&lear completed"));
657 //-----------------------------------------------------
658 m_menu
->AppendSeparator();
659 //-----------------------------------------------------
660 wxMenu
* extendedmenu
= new wxMenu();
661 extendedmenu
->Append(MP_SWAP_A4AF_TO_THIS
,
662 _("Swap every A4AF to this file now"));
663 extendedmenu
->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO
,
664 _("Swap every A4AF to this file (Auto)"));
665 //-----------------------------------------------------
666 extendedmenu
->AppendSeparator();
667 //-----------------------------------------------------
668 extendedmenu
->Append(MP_SWAP_A4AF_TO_ANY_OTHER
,
669 _("Swap every A4AF to any other file now"));
670 //-----------------------------------------------------
671 m_menu
->Append(MP_MENU_EXTD
,
672 _("Extended Options"), extendedmenu
);
673 //-----------------------------------------------------
674 m_menu
->AppendSeparator();
675 //-----------------------------------------------------
677 m_menu
->Append(MP_VIEW
, _("Preview"));
678 m_menu
->Append(MP_METINFO
, _("Show file &details"));
679 m_menu
->Append(MP_VIEWFILECOMMENTS
, _("Show all comments"));
680 //-----------------------------------------------------
681 m_menu
->AppendSeparator();
682 //-----------------------------------------------------
683 m_menu
->Append(MP_GETMAGNETLINK
,
684 _("Copy magnet URI to clipboard"));
685 m_menu
->Append(MP_GETED2KLINK
,
686 _("Copy eD2k &link to clipboard"));
687 m_menu
->Append(MP_WS
,
688 _("Copy feedback to clipboard"));
689 //-----------------------------------------------------
690 m_menu
->AppendSeparator();
691 //-----------------------------------------------------
692 // Add dynamic entries
693 wxMenu
*cats
= new wxMenu(_("Category"));
694 if (theApp
->glob_prefs
->GetCatCount() > 1) {
695 for (uint32 i
= 0; i
< theApp
->glob_prefs
->GetCatCount(); i
++) {
697 cats
->Append( MP_ASSIGNCAT
, _("unassign") );
699 cats
->Append( MP_ASSIGNCAT
+ i
,
700 theApp
->glob_prefs
->GetCategory(i
)->title
);
704 m_menu
->Append(MP_MENU_CATS
, _("Assign to category"), cats
);
705 m_menu
->Enable(MP_MENU_CATS
, (theApp
->glob_prefs
->GetCatCount() > 1) );
707 CPartFile
* file
= item
->GetFile();
713 if (file
->GetStatus(true) != PS_ALLOCATING
) {
714 const uint8_t fileStatus
= file
->GetStatus();
716 (fileStatus
!= PS_ERROR
) &&
717 (fileStatus
!= PS_COMPLETE
) &&
718 (file
->IsStopped() != true);
719 canPause
= (file
->GetStatus() != PS_PAUSED
) && canStop
;
721 (fileStatus
== PS_PAUSED
) ||
722 (fileStatus
== PS_ERROR
) ||
723 (fileStatus
== PS_INSUFFICIENT
);
724 canCancel
= fileStatus
!= PS_COMPLETE
;
726 canStop
= canPause
= canCancel
= fileResumable
= false;
729 m_menu
->Enable( MP_CANCEL
, canCancel
);
730 m_menu
->Enable( MP_PAUSE
, canPause
);
731 m_menu
->Enable( MP_STOP
, canStop
);
732 m_menu
->Enable( MP_RESUME
, fileResumable
);
733 m_menu
->Enable( MP_CLEARCOMPLETED
, CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->IsEnabled() );
736 if (file
->IsPartFile() && !file
->IsCompleted()) {
737 view
= CFormat(wxT("%s [%s]")) % _("Preview")
738 % file
->GetPartMetFileName().RemoveExt();
739 } else if ( file
->IsCompleted() ) {
740 view
= _("&Open the file");
742 m_menu
->SetLabel(MP_VIEW
, view
);
743 m_menu
->Enable(MP_VIEW
, file
->PreviewAvailable());
745 FileRatingList ratingList
;
746 item
->GetFile()->GetRatingAndComments(ratingList
);
747 m_menu
->Enable(MP_VIEWFILECOMMENTS
, !ratingList
.empty());
749 m_menu
->Check( MP_SWAP_A4AF_TO_THIS_AUTO
, file
->IsA4AFAuto() );
751 int priority
= file
->IsAutoDownPriority() ? PR_AUTO
: file
->GetDownPriority();
753 priomenu
->Check( MP_PRIOHIGH
, priority
== PR_HIGH
);
754 priomenu
->Check( MP_PRIONORMAL
, priority
== PR_NORMAL
);
755 priomenu
->Check( MP_PRIOLOW
, priority
== PR_LOW
);
756 priomenu
->Check( MP_PRIOAUTO
, priority
== PR_AUTO
);
758 m_menu
->Enable( MP_MENU_EXTD
, canPause
);
760 bool autosort
= thePrefs::AutoSortDownload(false);
761 PopupMenu(m_menu
, evt
.GetPoint());
762 thePrefs::AutoSortDownload(autosort
);
769 void CDownloadListCtrl::OnMouseMiddleClick(wxListEvent
& evt
)
771 // Check if clicked item is selected. If not, unselect all and select it.
772 long index
= CheckSelection(evt
);
774 ShowFileDetailDialog(index
);
779 void CDownloadListCtrl::ShowFileDetailDialog(long index
)
781 // Make list of part files in control
782 std::vector
<CPartFile
*> files
;
783 int nrItems
= GetItemCount();
784 files
.reserve(nrItems
);
785 for (int i
= 0; i
< nrItems
; i
++) {
786 files
.push_back(((FileCtrlItem_Struct
*)GetItemData(i
))->GetFile());
788 bool autosort
= thePrefs::AutoSortDownload(false);
789 CFileDetailDialog(this, files
, index
).ShowModal();
790 thePrefs::AutoSortDownload(autosort
);
794 void CDownloadListCtrl::OnKeyPressed( wxKeyEvent
& event
)
796 // Check if delete was pressed
797 switch (event
.GetKeyCode()) {
798 case WXK_NUMPAD_DELETE
:
805 ItemList files
= ::GetSelectedItems( this );
806 if (files
.size() == 1) {
807 CPartFile
* file
= files
.front()->GetFile();
809 // Currently renaming of completed files causes problem with kad
810 if (file
->IsPartFile()) {
811 wxString strNewName
= ::wxGetTextFromUser(
812 _("Enter new name for this file:"),
813 _("File rename"), file
->GetFileName().GetPrintable());
815 CPath newName
= CPath(strNewName
);
816 if (newName
.IsOk() && (newName
!= file
->GetFileName())) {
817 theApp
->sharedfiles
->RenameFile(file
, newName
);
829 void CDownloadListCtrl::OnDrawItem(
830 int item
, wxDC
* dc
, const wxRect
& rect
, const wxRect
& rectHL
, bool highlighted
)
832 // Don't do any drawing if there's nobody to see it.
833 if ( !theApp
->amuledlg
->IsDialogVisible( CamuleDlg::DT_TRANSFER_WND
) ) {
837 FileCtrlItem_Struct
* content
= (FileCtrlItem_Struct
*)GetItemData(item
);
839 // Define text-color and background
840 // and the border of the drawn area
844 dc
->SetBackground(m_hilightBrush
);
845 colour
= m_hilightBrush
.GetColour();
847 dc
->SetBackground(m_hilightUnfocusBrush
);
848 colour
= m_hilightUnfocusBrush
.GetColour();
850 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
851 dc
->SetPen( colour
.Blend(65).GetPen() );
853 dc
->SetBackground(*(wxTheBrushList
->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX
), wxSOLID
)));
854 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
855 dc
->SetPen(*wxTRANSPARENT_PEN
);
857 dc
->SetBrush( dc
->GetBackground() );
859 dc
->DrawRectangle( rectHL
.x
, rectHL
.y
, rectHL
.width
, rectHL
.height
);
861 dc
->SetPen(*wxTRANSPARENT_PEN
);
863 if (!highlighted
|| !GetFocus() ) {
864 // If we have category, override textforeground with what category tells us.
865 CPartFile
*file
= content
->GetFile();
866 if ( file
->GetCategory() ) {
867 dc
->SetTextForeground(CMuleColour(theApp
->glob_prefs
->GetCatColor(file
->GetCategory())) );
871 // Various constant values we use
872 const int iTextOffset
= ( rect
.GetHeight() - dc
->GetCharHeight() ) / 2;
873 const int iOffset
= 4;
875 wxRect
cur_rec( iOffset
, rect
.y
, 0, rect
.height
);
876 for (int i
= 0; i
< GetColumnCount(); i
++) {
878 GetColumn(i
, listitem
);
880 if (listitem
.GetWidth() > 2*iOffset
) {
881 cur_rec
.width
= listitem
.GetWidth() - 2*iOffset
;
883 // Make a copy of the current rectangle so we can apply specific tweaks
884 wxRect target_rec
= cur_rec
;
885 if ( i
== ColumnProgress
) {
886 // Double the offset to make room for the cirle-marker
887 target_rec
.x
+= iOffset
;
888 target_rec
.width
-= iOffset
;
890 // will ensure that text is about in the middle ;)
891 target_rec
.y
+= iTextOffset
;
895 DrawFileItem(dc
, i
, target_rec
, content
);
898 // Increment to the next column
899 cur_rec
.x
+= listitem
.GetWidth();
904 void CDownloadListCtrl::DrawFileItem( wxDC
* dc
, int nColumn
, const wxRect
& rect
, FileCtrlItem_Struct
* item
) const
906 wxDCClipper
clipper( *dc
, rect
.GetX(), rect
.GetY(), rect
.GetWidth(), rect
.GetHeight() );
908 const CPartFile
* file
= item
->GetFile();
910 // Used to contain the contenst of cells that dont need any fancy drawing, just text.
916 if (file
->IsPartFile() && !file
->IsCompleted()) {
917 text
= CFormat(wxT("%03d")) % file
->GetPartMetNumber();
922 case ColumnFileName
: {
923 wxString filename
= file
->GetFileName().GetPrintable();
925 if (file
->HasRating() || file
->HasComment()) {
926 int image
= Client_CommentOnly_Smiley
;
927 if (file
->HasRating()) {
928 image
= Client_InvalidRating_Smiley
+ file
->UserRating() - 1;
931 wxASSERT(image
>= Client_InvalidRating_Smiley
);
932 wxASSERT(image
<= Client_CommentOnly_Smiley
);
936 // it's already centered by OnDrawItem() ...
937 m_ImageList
.Draw(image
, *dc
, rect
.GetX(), rect
.GetY() - 1,
938 wxIMAGELIST_DRAW_TRANSPARENT
);
939 dc
->DrawText(filename
, rect
.GetX() + imgWidth
+ 4, rect
.GetY());
941 dc
->DrawText(filename
, rect
.GetX(), rect
.GetY());
948 text
= CastItoXBytes( file
->GetFileSize() );
952 case ColumnTransferred
:
953 text
= CastItoXBytes( file
->GetTransferred() );
957 case ColumnCompleted
:
958 text
= CastItoXBytes( file
->GetCompletedSize() );
963 if ( file
->GetTransferingSrcCount() ) {
964 text
= CFormat(_("%.1f kB/s")) % file
->GetKBpsDown();
969 case ColumnProgress
:{
970 if (thePrefs::ShowProgBar()) {
971 int iWidth
= rect
.GetWidth() - 2;
972 int iHeight
= rect
.GetHeight() - 2;
974 // DO NOT DRAW IT ALL THE TIME
975 uint32 dwTicks
= GetTickCount();
977 wxMemoryDC cdcStatus
;
979 if (item
->dwUpdated
< dwTicks
|| file
->GetHashingProgress() > 0
980 || !item
->status
|| iWidth
!= item
->status
->GetWidth()) {
981 if ( item
->status
== NULL
) {
982 item
->status
= new wxBitmap(iWidth
, iHeight
);
983 } else if ( item
->status
->GetWidth() != iWidth
) {
984 // Only recreate if the size has changed
985 item
->status
->Create(iWidth
, iHeight
);
988 cdcStatus
.SelectObject( *item
->status
);
990 if ( thePrefs::UseFlatBar() ) {
991 DrawFileStatusBar( file
, &cdcStatus
,
992 wxRect(0, 0, iWidth
, iHeight
), true);
994 DrawFileStatusBar( file
, &cdcStatus
,
995 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
998 cdcStatus
.SetPen( *wxBLACK_PEN
);
999 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
1000 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
1003 item
->dwUpdated
= dwTicks
+ 5000; // Plus five seconds
1005 cdcStatus
.SelectObject( *item
->status
);
1008 dc
->Blit( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
1010 if (thePrefs::ShowPercent()) {
1011 // Percentage of completing or hashing
1012 uint16 hashingProgress
= file
->GetHashingProgress();
1013 double percent
= hashingProgress
== 0 ? file
->GetPercentCompleted()
1014 : 100.0 * hashingProgress
* PARTSIZE
/ file
->GetFileSize();
1015 if (percent
> 100.0) {
1018 wxString buffer
= CFormat(wxT("%.1f%%")) % percent
;
1019 int middlex
= (2*rect
.GetX() + rect
.GetWidth()) >> 1;
1020 int middley
= (2*rect
.GetY() + rect
.GetHeight()) >> 1;
1022 wxCoord textwidth
, textheight
;
1024 dc
->GetTextExtent(buffer
, &textwidth
, &textheight
);
1025 wxColour AktColor
= dc
->GetTextForeground();
1026 // Ordinary progress bar: white percentage
1027 // Hashing progressbar (green/yellow): black percentage
1028 if (thePrefs::ShowProgBar() && hashingProgress
== 0) {
1029 dc
->SetTextForeground(*wxWHITE
);
1031 dc
->SetTextForeground(*wxBLACK
);
1033 dc
->DrawText(buffer
, middlex
- (textwidth
>> 1), middley
- (textheight
>> 1));
1034 dc
->SetTextForeground(AktColor
);
1042 case ColumnSources
: {
1043 uint16 sc
= file
->GetSourceCount();
1044 uint16 ncsc
= file
->GetNotCurrentSourcesCount();
1046 text
= CFormat(wxT("%i/%i")) % (sc
- ncsc
) % sc
;
1048 text
= CFormat(wxT("%i")) % sc
;
1051 if ( file
->GetSrcA4AFCount() ) {
1052 text
+= CFormat(wxT("+%i")) % file
->GetSrcA4AFCount();
1055 if ( file
->GetTransferingSrcCount() ) {
1056 text
+= CFormat(wxT(" (%i)")) % file
->GetTransferingSrcCount();
1063 case ColumnPriority
:
1064 text
= PriorityToStr( file
->GetDownPriority(), file
->IsAutoDownPriority() );
1069 text
= file
->getPartfileStatus();
1073 case ColumnTimeRemaining
: {
1074 if ((file
->GetStatus() != PS_COMPLETING
) && file
->IsPartFile()) {
1075 uint64 remainSize
= file
->GetFileSize() - file
->GetCompletedSize();
1076 sint32 remainTime
= file
->getTimeRemaining();
1078 if (remainTime
>= 0) {
1079 text
= CastSecondsToHM(remainTime
);
1081 text
= _("Unknown");
1084 text
+= wxT(" (") + CastItoXBytes(remainSize
) + wxT(")");
1089 // Last seen completed
1090 case ColumnLastSeenComplete
: {
1091 if ( file
->lastseencomplete
) {
1092 text
= wxDateTime( file
->lastseencomplete
).Format( _("%y/%m/%d %H:%M:%S") );
1094 text
= _("Unknown");
1100 case ColumnLastReception
: {
1101 const time_t lastReceived
= file
->GetLastChangeDatetime();
1103 text
= wxDateTime(lastReceived
).Format( _("%y/%m/%d %H:%M:%S") );
1105 text
= _("Unknown");
1110 if ( !text
.IsEmpty() ) {
1111 dc
->DrawText( text
, rect
.GetX(), rect
.GetY() );
1115 wxString
CDownloadListCtrl::GetTTSText(unsigned item
) const
1117 return ((FileCtrlItem_Struct
*)GetItemData(item
))->GetFile()->GetFileName().GetPrintable();
1121 int CDownloadListCtrl::SortProc(wxUIntPtr param1
, wxUIntPtr param2
, long sortData
)
1123 FileCtrlItem_Struct
* item1
= (FileCtrlItem_Struct
*)param1
;
1124 FileCtrlItem_Struct
* item2
= (FileCtrlItem_Struct
*)param2
;
1126 int sortMod
= (sortData
& CMuleListCtrl::SORT_DES
) ? -1 : 1;
1127 sortData
&= CMuleListCtrl::COLUMN_MASK
;
1129 // We modify the result so that it matches with ascending or decending
1130 return sortMod
* Compare( item1
->GetFile(), item2
->GetFile(), sortData
);
1134 int CDownloadListCtrl::Compare( const CPartFile
* file1
, const CPartFile
* file2
, long lParamSort
)
1138 switch (lParamSort
) {
1139 // Sort by part number
1142 file1
->GetPartMetNumber(),
1143 file2
->GetPartMetNumber() );
1147 case ColumnFileName
:
1149 file1
->GetFileName(),
1150 file2
->GetFileName() );
1156 file1
->GetFileSize(),
1157 file2
->GetFileSize() );
1160 // Sort by transferred
1161 case ColumnTransferred
:
1163 file1
->GetTransferred(),
1164 file2
->GetTransferred() );
1167 // Sort by completed
1168 case ColumnCompleted
:
1170 file1
->GetCompletedSize(),
1171 file2
->GetCompletedSize() );
1177 file1
->GetKBpsDown() * 1024,
1178 file2
->GetKBpsDown() * 1024 );
1181 // Sort by percentage completed
1182 case ColumnProgress
:
1184 file1
->GetPercentCompleted(),
1185 file2
->GetPercentCompleted() );
1188 // Sort by number of sources
1191 file1
->GetSourceCount(),
1192 file2
->GetSourceCount() );
1196 case ColumnPriority
:
1198 file1
->GetDownPriority(),
1199 file2
->GetDownPriority() );
1205 file1
->getPartfileStatusRang(),
1206 file2
->getPartfileStatusRang() );
1209 // Sort by remaining time
1210 case ColumnTimeRemaining
:
1211 if (file1
->getTimeRemaining() == -1) {
1212 if (file2
->getTimeRemaining() == -1) {
1218 if (file2
->getTimeRemaining() == -1) {
1222 file1
->getTimeRemaining(),
1223 file2
->getTimeRemaining() );
1228 // Sort by last seen complete
1229 case ColumnLastSeenComplete
:
1231 file1
->lastseencomplete
,
1232 file2
->lastseencomplete
);
1235 // Sort by last reception
1236 case ColumnLastReception
:
1238 file1
->GetLastChangeDatetime(),
1239 file2
->GetLastChangeDatetime() );
1246 void CDownloadListCtrl::ClearCompleted()
1248 CastByID(ID_BTNCLRCOMPL
, GetParent(), wxButton
)->Enable(false);
1250 // Search for completed files
1251 ListOfUInts32 toClear
;
1252 for ( ListItems::iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); ) {
1253 FileCtrlItem_Struct
* item
= it
->second
; ++it
;
1255 CPartFile
* file
= item
->GetFile();
1257 if (file
->IsCompleted() && file
->CheckShowItemInGivenCat(m_category
)) {
1258 toClear
.push_back(file
->ECID());
1261 if (!toClear
.empty()) {
1262 theApp
->downloadqueue
->ClearCompleted(toClear
);
1267 void CDownloadListCtrl::ShowFilesCount( int diff
)
1269 m_filecount
+= diff
;
1271 wxStaticText
* label
= CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText
);
1273 label
->SetLabel(CFormat(_("Downloads (%i)")) % m_filecount
);
1274 label
->GetParent()->Layout();
1278 static const CMuleColour
crHave(104, 104, 104);
1279 static const CMuleColour
crFlatHave(0, 0, 0);
1281 static const CMuleColour
crPending(255, 208, 0);
1282 static const CMuleColour
crFlatPending(255, 255, 100);
1284 static const CMuleColour
crProgress(0, 224, 0);
1285 static const CMuleColour
crFlatProgress(0, 150, 0);
1287 static const CMuleColour
crMissing(255, 0, 0);
1289 void CDownloadListCtrl::DrawFileStatusBar(
1290 const CPartFile
* file
, wxDC
* dc
, const wxRect
& rect
, bool bFlat
) const
1292 static CBarShader
s_ChunkBar(16);
1294 s_ChunkBar
.SetHeight(rect
.height
);
1295 s_ChunkBar
.SetWidth(rect
.width
);
1296 s_ChunkBar
.SetFileSize( file
->GetFileSize() );
1297 s_ChunkBar
.Set3dDepth( thePrefs::Get3DDepth() );
1299 if ( file
->IsCompleted() || file
->GetStatus() == PS_COMPLETING
) {
1300 s_ChunkBar
.Fill( bFlat
? crFlatProgress
: crProgress
);
1301 s_ChunkBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
1303 } else if (file
->GetHashingProgress() > 0) {
1304 uint64 left
= file
->GetHashingProgress() * PARTSIZE
;
1305 if (left
< file
->GetFileSize() - 1) {
1306 // Fill the amount not yet hashed with yellow
1307 s_ChunkBar
.FillRange(left
+ 1, file
->GetFileSize() - 1, bFlat
? crFlatPending
: crPending
);
1309 left
= file
->GetFileSize() - 1;
1311 // Fill the amount already hashed with green
1312 s_ChunkBar
.FillRange(0, left
, bFlat
? crFlatProgress
: crProgress
);
1313 s_ChunkBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
1316 // Part availability ( of missing parts )
1317 const CGapList
& gaplist
= file
->GetGapList();
1318 CGapList::const_iterator it
= gaplist
.begin();
1319 uint64 lastGapEnd
= 0;
1322 for (; it
!= gaplist
.end(); ++it
) {
1325 uint32 start
= ( it
.start() / PARTSIZE
);
1326 // fill the Have-Part (between this gap and the last)
1328 s_ChunkBar
.FillRange(lastGapEnd
+ 1, it
.start() - 1, bFlat
? crFlatHave
: crHave
);
1330 lastGapEnd
= it
.end();
1332 uint32 end
= ( it
.end() / PARTSIZE
) + 1;
1334 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
1335 if ( end
> file
->GetPartCount() ) {
1336 end
= file
->GetPartCount();
1339 // Place each gap, one PART at a time
1340 for ( uint64 i
= start
; i
< end
; ++i
) {
1341 if ( i
< file
->m_SrcpartFrequency
.size() && file
->m_SrcpartFrequency
[i
]) {
1342 int blue
= 210 - ( 22 * ( file
->m_SrcpartFrequency
[i
] - 1 ) );
1343 colour
.Set(0, ( blue
< 0 ? 0 : blue
), 255 );
1348 if ( file
->IsStopped() ) {
1352 uint64 gap_begin
= ( i
== start
? it
.start() : PARTSIZE
* i
);
1353 uint64 gap_end
= ( i
== end
- 1 ? it
.end() : PARTSIZE
* ( i
+ 1 ) - 1 );
1355 s_ChunkBar
.FillRange( gap_begin
, gap_end
, colour
);
1359 // fill the last Have-Part (between this gap and the last)
1360 s_ChunkBar
.FillRange(lastGapEnd
+ 1, file
->GetFileSize() - 1, bFlat
? crFlatHave
: crHave
);
1363 const CPartFile::CReqBlockPtrList
& requestedblocks_list
= file
->GetRequestedBlockList();
1364 CPartFile::CReqBlockPtrList::const_iterator it2
= requestedblocks_list
.begin();
1365 // adjacing pending parts must be joined to avoid bright lines between them
1366 uint64 lastStartOffset
= 0;
1367 uint64 lastEndOffset
= 0;
1369 colour
= bFlat
? crFlatPending
: crPending
;
1371 if ( file
->IsStopped() ) {
1375 for (; it2
!= requestedblocks_list
.end(); ++it2
) {
1377 if ((*it2
)->StartOffset
> lastEndOffset
+ 1) {
1378 // not adjacing, draw last block
1379 s_ChunkBar
.FillRange(lastStartOffset
, lastEndOffset
, colour
);
1380 lastStartOffset
= (*it2
)->StartOffset
;
1381 lastEndOffset
= (*it2
)->EndOffset
;
1383 // adjacing, grow block
1384 lastEndOffset
= (*it2
)->EndOffset
;
1388 s_ChunkBar
.FillRange(lastStartOffset
, lastEndOffset
, colour
);
1391 // Draw the progress-bar
1392 s_ChunkBar
.Draw( dc
, rect
.x
, rect
.y
, bFlat
);
1395 // Green progressbar width
1396 int width
= (int)(( (float)rect
.width
/ (float)file
->GetFileSize() ) *
1397 file
->GetCompletedSize() );
1400 dc
->SetBrush( crFlatProgress
.GetBrush() );
1402 dc
->DrawRectangle( rect
.x
, rect
.y
, width
, 3 );
1404 // Draw the two black lines for 3d-effect
1405 dc
->SetPen( *wxBLACK_PEN
);
1406 dc
->DrawLine( rect
.x
, rect
.y
+ 0, rect
.x
+ width
, rect
.y
+ 0 );
1407 dc
->DrawLine( rect
.x
, rect
.y
+ 2, rect
.x
+ width
, rect
.y
+ 2 );
1409 // Draw the green line
1410 dc
->SetPen( *(wxThePenList
->FindOrCreatePen( crProgress
, 1, wxSOLID
) ));
1411 dc
->DrawLine( rect
.x
, rect
.y
+ 1, rect
.x
+ width
, rect
.y
+ 1 );
1416 # define QUOTE wxT("\"")
1418 # define QUOTE wxT("\'")
1421 void CDownloadListCtrl::PreviewFile(CPartFile
* file
)
1424 // If no player set in preferences, use mplayer.
1425 // And please, do a warning also :P
1426 if (thePrefs::GetVideoPlayer().IsEmpty()) {
1428 "To prevent this warning to show up in every preview,\nset your preferred video player in preferences (default is mplayer)."),
1429 _("File preview"), wxOK
, this);
1430 // Since newer versions for some reason mplayer does not automatically
1431 // select video output device and needs a parameter, go figure...
1432 command
= wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE
wxT("$file") QUOTE
;
1434 command
= thePrefs::GetVideoPlayer();
1437 wxString partFile
; // File name with full path
1438 wxString partName
; // File name only, without path
1440 // Check if we are (pre)viewing a completed file or not
1441 if (!file
->IsCompleted()) {
1442 // Remove the .met and see if out video player specifiation uses the magic string
1443 partName
= file
->GetPartMetFileName().RemoveExt().GetRaw();
1444 partFile
= thePrefs::GetTempDir().JoinPaths(file
->GetPartMetFileName().RemoveExt()).GetRaw();
1446 // This is a complete file
1447 // FIXME: This is probably not going to work if the filenames are mangled ...
1448 partName
= file
->GetFileName().GetRaw();
1449 partFile
= file
->GetFullName().GetRaw();
1452 // Compatibility with old behaviour
1453 if (!command
.Replace(wxT("$file"), wxT("%PARTFILE"))) {
1454 if ((command
.Find(wxT("%PARTFILE")) == wxNOT_FOUND
) && (command
.Find(wxT("%PARTNAME")) == wxNOT_FOUND
)) {
1455 // No magic string, so we just append the filename to the player command
1456 // Need to use quotes in case filename contains spaces
1457 command
<< wxT(" ") << QUOTE
<< wxT("%PARTFILE") << QUOTE
;
1462 // We have to escape quote characters in the file name, otherwise arbitrary
1463 // options could be passed to the player.
1464 partFile
.Replace(QUOTE
, wxT("\\") QUOTE
);
1465 partName
.Replace(QUOTE
, wxT("\\") QUOTE
);
1468 command
.Replace(wxT("%PARTFILE"), partFile
);
1469 command
.Replace(wxT("%PARTNAME"), partName
);
1471 // We can't use wxShell here, it blocks the app
1472 CTerminationProcess
*p
= new CTerminationProcess(command
);
1473 int ret
= wxExecute(command
, wxEXEC_ASYNC
, p
);
1477 AddLogLineC(CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
1481 // File_checked_for_headers