Upstream tarball 10143
[amule.git] / src / DownloadListCtrl.cpp
blob1c473696548ef084df9a65a27ebca86b65520e9e
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "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 "FileDetailDialog.h" // Needed for CFileDetailDialog
38 #include "GuiEvents.h" // Needed for CoreNotify_*
39 #include "Logger.h"
40 #include "muuli_wdr.h" // Needed for ID_DLOADLIST
41 #include "PartFile.h" // Needed for CPartFile
42 #include "Preferences.h"
43 #include "SharedFileList.h" // Needed for CSharedFileList
44 #include "TerminationProcess.h" // Needed for CTerminationProcess
45 #include "updownclient.h" // Needed for CUpDownClient
46 #include "TransferWnd.h"
47 #include "SourceListCtrl.h"
49 class CPartFile;
52 struct FileCtrlItem_Struct
54 FileCtrlItem_Struct()
55 : dwUpdated(0),
56 status(NULL),
57 m_fileValue(NULL)
58 { }
60 ~FileCtrlItem_Struct() {
61 delete status;
64 CPartFile* GetFile() const {
65 return m_fileValue;
68 void SetContents(CPartFile* file) {
69 m_fileValue = file;
72 uint32 dwUpdated;
73 wxBitmap* status;
75 private:
76 CPartFile* m_fileValue;
79 #define m_ImageList theApp->amuledlg->m_imagelist
81 enum ColumnEnum {
82 ColumnPart = 0,
83 ColumnFileName,
84 ColumnSize,
85 ColumnTransferred,
86 ColumnCompleted,
87 ColumnSpeed,
88 ColumnProgress,
89 ColumnSources,
90 ColumnPriority,
91 ColumnStatus,
92 ColumnTimeRemaining,
93 ColumnLastSeenComplete,
94 ColumnLastReception,
95 ColumnNumberOfColumns
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 )
135 END_EVENT_TABLE()
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") );
152 m_menu = NULL;
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") );
172 m_category = 0;
173 m_completedFiles = 0;
174 m_filecount = 0;
175 LoadSettings();
177 //m_ready = true;
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 )
197 wxASSERT( 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 SortList();
214 void CDownloadListCtrl::RemoveFile( CPartFile* file )
216 wxASSERT( file );
218 // Ensure that any list-entries are removed
219 ShowFile( file, false );
221 // Find the assosiated list-item
222 ListItems::iterator it = m_ListItems.find( file );
224 if ( it != m_ListItems.end() ) {
225 delete it->second;
227 m_ListItems.erase( it );
232 void CDownloadListCtrl::UpdateItem(const void* toupdate)
234 // Retrieve all entries matching the source
235 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
237 // Visible lines, default to all because not all platforms
238 // support the GetVisibleLines function
239 long first = 0, last = GetItemCount();
241 #ifndef __WXMSW__
242 // Get visible lines if we need them
243 if ( rangeIt.first != rangeIt.second ) {
244 GetVisibleLines( &first, &last );
246 #endif
248 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
249 FileCtrlItem_Struct* item = it->second;
251 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
253 // Determine if the file should be shown in the current category
255 CPartFile* file = item->GetFile();
257 bool show = file->CheckShowItemInGivenCat( m_category );
259 if ( index > -1 ) {
260 if ( show ) {
261 item->dwUpdated = 0;
263 // Only update visible lines
264 if ( index >= first && index <= last) {
265 RefreshItem( index );
267 } else {
268 // Item should no longer be shown in
269 // the current category
270 ShowFile( file, false );
272 } else if ( show ) {
273 // Item has been hidden but new status means
274 // that it should it should be shown in the
275 // current category
276 ShowFile( file, true );
279 if (file->GetStatus() == PS_COMPLETE) {
280 m_completedFiles = true;
282 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(true);
288 void CDownloadListCtrl::ShowFile( CPartFile* file, bool show )
290 wxASSERT( file );
292 ListItems::iterator it = m_ListItems.find( file );
294 if ( it != m_ListItems.end() ) {
295 FileCtrlItem_Struct* item = it->second;
297 if ( show ) {
298 // Check if the file is already being displayed
299 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
300 if ( index == -1 ) {
301 long newitem = InsertItem( GetItemCount(), wxEmptyString );
303 SetItemPtrData( newitem, reinterpret_cast<wxUIntPtr>(item) );
305 wxListItem myitem;
306 myitem.m_itemId = newitem;
307 myitem.SetBackgroundColour( GetBackgroundColour() );
309 SetItem(myitem);
311 RefreshItem( newitem );
313 ShowFilesCount( 1 );
315 } else {
316 // Try to find the file and remove it
317 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
318 if ( index > -1 ) {
319 DeleteItem( index );
320 ShowFilesCount( -1 );
326 void CDownloadListCtrl::ChangeCategory( int newCategory )
328 Freeze();
330 // remove all displayed files with a different cat and show the correct ones
331 for (ListItems::const_iterator it = m_ListItems.begin(); it != m_ListItems.end(); it++) {
333 CPartFile* file = it->second->GetFile();
335 bool curVisibility = file->CheckShowItemInGivenCat( m_category );
336 bool newVisibility = file->CheckShowItemInGivenCat( newCategory );
338 // Check if the visibility of the file has changed. However, if the
339 // current category is the default (0) category, then we can't use
340 // curVisiblity to see if the visibility has changed but instead
341 // have to let ShowFile() check if the file is or isn't on the list.
342 if ( curVisibility != newVisibility || !newCategory ) {
343 ShowFile( file, newVisibility );
347 Thaw();
349 m_category = newCategory;
353 uint8 CDownloadListCtrl::GetCategory() const
355 return m_category;
360 * Helper-function: This function is used to gather selected items.
362 * @param list A pointer to the list to gather items from.
363 * @return A list containing the selected items.
365 ItemList GetSelectedItems( CDownloadListCtrl* list)
367 ItemList results;
369 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
371 while ( index > -1 ) {
372 FileCtrlItem_Struct* item = (FileCtrlItem_Struct*)list->GetItemData( index );
373 results.push_back( item );
375 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
378 return results;
382 void CDownloadListCtrl::OnCancelFile(wxCommandEvent& WXUNUSED(event))
384 ItemList files = ::GetSelectedItems(this);
385 if (files.size()) {
386 wxString question;
387 if (files.size() == 1) {
388 question = _("Are you sure that you wish to delete the selected file?");
389 } else {
390 question = _("Are you sure that you wish to delete the selected files?");
392 if (wxMessageBox( question, _("Cancel"), wxICON_QUESTION | wxYES_NO, this) == wxYES) {
393 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
394 CPartFile* file = (*it)->GetFile();
395 if (file) {
396 switch (file->GetStatus()) {
397 case PS_WAITINGFORHASH:
398 case PS_HASHING:
399 case PS_COMPLETING:
400 case PS_COMPLETE:
401 break;
402 default:
403 CoreNotify_PartFile_Delete(file);
412 void CDownloadListCtrl::OnSetPriority( wxCommandEvent& event )
414 int priority = 0;
415 switch ( event.GetId() ) {
416 case MP_PRIOLOW: priority = PR_LOW; break;
417 case MP_PRIONORMAL: priority = PR_NORMAL; break;
418 case MP_PRIOHIGH: priority = PR_HIGH; break;
419 case MP_PRIOAUTO: priority = PR_AUTO; break;
420 default:
421 wxASSERT( false );
424 ItemList files = ::GetSelectedItems( this );
426 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
427 CPartFile* file = (*it)->GetFile();
429 if ( priority == PR_AUTO ) {
430 CoreNotify_PartFile_PrioAuto( file, true );
431 } else {
432 CoreNotify_PartFile_PrioAuto( file, false );
434 CoreNotify_PartFile_PrioSet( file, priority, true );
440 void CDownloadListCtrl::OnSwapSources( wxCommandEvent& event )
442 ItemList files = ::GetSelectedItems( this );
444 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
445 CPartFile* file = (*it)->GetFile();
447 switch ( event.GetId() ) {
448 case MP_SWAP_A4AF_TO_THIS:
449 CoreNotify_PartFile_Swap_A4AF( file );
450 break;
452 case MP_SWAP_A4AF_TO_THIS_AUTO:
453 CoreNotify_PartFile_Swap_A4AF_Auto( file );
454 break;
456 case MP_SWAP_A4AF_TO_ANY_OTHER:
457 CoreNotify_PartFile_Swap_A4AF_Others( file );
458 break;
464 void CDownloadListCtrl::OnSetCategory( wxCommandEvent& event )
466 ItemList files = ::GetSelectedItems( this );
468 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
469 CoreNotify_PartFile_SetCat( (*it)->GetFile(), event.GetId() - MP_ASSIGNCAT );
472 ChangeCategory( m_category );
476 void CDownloadListCtrl::OnSetStatus( wxCommandEvent& event )
478 ItemList files = ::GetSelectedItems( this );
480 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
481 CPartFile* file = (*it)->GetFile();
483 switch ( event.GetId() ) {
484 case MP_PAUSE:
485 CoreNotify_PartFile_Pause( file );
486 break;
488 case MP_RESUME:
489 CoreNotify_PartFile_Resume( file );
490 break;
492 case MP_STOP:
493 CoreNotify_PartFile_Stop( file );
494 break;
500 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent& WXUNUSED(event) )
502 ClearCompleted();
506 void CDownloadListCtrl::OnGetLink(wxCommandEvent& event)
508 ItemList files = ::GetSelectedItems( this );
510 wxString URIs;
512 for ( ItemList::iterator it = files.begin(); it != files.end(); ++it ) {
513 CPartFile* file = (*it)->GetFile();
515 if ( event.GetId() == MP_GETED2KLINK ) {
516 URIs += theApp->CreateED2kLink( file ) + wxT("\n");
517 } else {
518 URIs += theApp->CreateMagnetLink( file ) + wxT("\n");
522 if ( !URIs.IsEmpty() ) {
523 theApp->CopyTextToClipboard( URIs.BeforeLast(wxT('\n')) );
528 void CDownloadListCtrl::OnGetFeedback(wxCommandEvent& WXUNUSED(event))
530 wxString feed;
531 ItemList files = ::GetSelectedItems( this );
533 for (ItemList::iterator it = files.begin(); it != files.end(); ++it) {
534 if (feed.IsEmpty()) {
535 feed = CFormat(_("Feedback from: %s (%s)\n\n")) % thePrefs::GetUserNick() % GetFullMuleVersion();
536 } else {
537 feed += wxT("\n");
539 feed += (*it)->GetFile()->GetFeedback();
542 if (!feed.IsEmpty()) {
543 theApp->CopyTextToClipboard(feed);
547 void CDownloadListCtrl::OnViewFileInfo( wxCommandEvent& WXUNUSED(event) )
549 ItemList files = ::GetSelectedItems( this );
551 if ( files.size() == 1 ) {
552 CFileDetailDialog dialog( this, files.front()->GetFile() );
553 dialog.ShowModal();
558 void CDownloadListCtrl::OnViewFileComments( wxCommandEvent& WXUNUSED(event) )
560 ItemList files = ::GetSelectedItems( this );
562 if ( files.size() == 1 ) {
563 CCommentDialogLst dialog( this, files.front()->GetFile() );
564 dialog.ShowModal();
568 void CDownloadListCtrl::OnPreviewFile( wxCommandEvent& WXUNUSED(event) )
570 ItemList files = ::GetSelectedItems( this );
572 if ( files.size() == 1 ) {
573 PreviewFile(files.front()->GetFile());
577 void CDownloadListCtrl::OnItemActivated( wxListEvent& evt )
579 CPartFile* file = ((FileCtrlItem_Struct*)GetItemData( evt.GetIndex()))->GetFile();
581 if ((!file->IsPartFile() || file->GetStatus() == PS_COMPLETE) && file->PreviewAvailable()) {
582 PreviewFile( file );
586 void CDownloadListCtrl::OnItemSelectionChanged( wxListEvent& evt )
588 if (!IsSorting()) {
589 CKnownFileVector filesVector;
590 filesVector.reserve(GetSelectedItemCount());
592 long index = GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
594 while ( index > -1 ) {
595 CPartFile* file = ((FileCtrlItem_Struct*)GetItemData( index ))->GetFile();
596 if (file->IsPartFile()) {
597 filesVector.push_back(file);
599 index = GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
602 std::sort(filesVector.begin(), filesVector.end());
603 theApp->amuledlg->m_transferwnd->clientlistctrl->ShowSources(filesVector);
607 void CDownloadListCtrl::OnMouseRightClick(wxListEvent& evt)
609 long index = CheckSelection(evt);
610 if (index < 0) {
611 return;
614 delete m_menu;
615 m_menu = NULL;
617 FileCtrlItem_Struct* item = (FileCtrlItem_Struct*)GetItemData( index );
618 m_menu = new wxMenu( _("Downloads") );
620 wxMenu* priomenu = new wxMenu();
621 priomenu->AppendCheckItem(MP_PRIOLOW, _("Low"));
622 priomenu->AppendCheckItem(MP_PRIONORMAL, _("Normal"));
623 priomenu->AppendCheckItem(MP_PRIOHIGH, _("High"));
624 priomenu->AppendCheckItem(MP_PRIOAUTO, _("Auto"));
626 m_menu->Append(MP_MENU_PRIO, _("Priority"), priomenu);
627 m_menu->Append(MP_CANCEL, _("Cancel"));
628 m_menu->Append(MP_STOP, _("&Stop"));
629 m_menu->Append(MP_PAUSE, _("&Pause"));
630 m_menu->Append(MP_RESUME, _("&Resume"));
631 m_menu->Append(MP_CLEARCOMPLETED, _("C&lear completed"));
632 //-----------------------------------------------------
633 m_menu->AppendSeparator();
634 //-----------------------------------------------------
635 wxMenu* extendedmenu = new wxMenu();
636 extendedmenu->Append(MP_SWAP_A4AF_TO_THIS,
637 _("Swap every A4AF to this file now"));
638 extendedmenu->AppendCheckItem(MP_SWAP_A4AF_TO_THIS_AUTO,
639 _("Swap every A4AF to this file (Auto)"));
640 //-----------------------------------------------------
641 extendedmenu->AppendSeparator();
642 //-----------------------------------------------------
643 extendedmenu->Append(MP_SWAP_A4AF_TO_ANY_OTHER,
644 _("Swap every A4AF to any other file now"));
645 //-----------------------------------------------------
646 m_menu->Append(MP_MENU_EXTD,
647 _("Extended Options"), extendedmenu);
648 //-----------------------------------------------------
649 m_menu->AppendSeparator();
650 //-----------------------------------------------------
652 m_menu->Append(MP_VIEW, _("Preview"));
653 m_menu->Append(MP_METINFO, _("Show file &details"));
654 m_menu->Append(MP_VIEWFILECOMMENTS,
655 _("Show all comments"));
656 //-----------------------------------------------------
657 m_menu->AppendSeparator();
658 //-----------------------------------------------------
659 m_menu->Append(MP_GETMAGNETLINK,
660 _("Copy magnet URI to clipboard"));
661 m_menu->Append(MP_GETED2KLINK,
662 _("Copy eD2k &link to clipboard"));
663 m_menu->Append(MP_WS,
664 _("Copy feedback to clipboard"));
665 //-----------------------------------------------------
666 m_menu->AppendSeparator();
667 //-----------------------------------------------------
668 // Add dinamic entries
669 wxMenu *cats = new wxMenu(_("Category"));
670 if (theApp->glob_prefs->GetCatCount() > 1) {
671 for (uint32 i = 0; i < theApp->glob_prefs->GetCatCount(); i++) {
672 if ( i == 0 ) {
673 cats->Append( MP_ASSIGNCAT, _("unassign") );
674 } else {
675 cats->Append( MP_ASSIGNCAT + i,
676 theApp->glob_prefs->GetCategory(i)->title );
680 m_menu->Append(MP_MENU_CATS, _("Assign to category"), cats);
681 m_menu->Enable(MP_MENU_CATS, (theApp->glob_prefs->GetCatCount() > 1) );
683 CPartFile* file = item->GetFile();
684 // then set state
685 bool canStop;
686 bool canPause;
687 bool canCancel;
688 bool fileResumable;
689 if (file->GetStatus(true) != PS_ALLOCATING) {
690 const uint8_t fileStatus = file->GetStatus();
691 canStop =
692 (fileStatus != PS_ERROR) &&
693 (fileStatus != PS_COMPLETE) &&
694 (file->IsStopped() != true);
695 canPause = (file->GetStatus() != PS_PAUSED) && canStop;
696 fileResumable =
697 (fileStatus == PS_PAUSED) ||
698 (fileStatus == PS_ERROR) ||
699 (fileStatus == PS_INSUFFICIENT);
700 canCancel = fileStatus != PS_COMPLETE;
701 } else {
702 canStop = canPause = canCancel = fileResumable = false;
705 wxMenu* menu = m_menu;
706 menu->Enable( MP_CANCEL, canCancel );
707 menu->Enable( MP_PAUSE, canPause );
708 menu->Enable( MP_STOP, canStop );
709 menu->Enable( MP_RESUME, fileResumable );
710 menu->Enable( MP_CLEARCOMPLETED, m_completedFiles );
712 wxString view;
713 if (file->IsPartFile() && (file->GetStatus() != PS_COMPLETE)) {
714 view = CFormat(wxT("%s [%s]")) % _("Preview")
715 % file->GetPartMetFileName().RemoveExt();
716 } else if ( file->GetStatus() == PS_COMPLETE ) {
717 view = _("&Open the file");
719 menu->SetLabel(MP_VIEW, view);
720 menu->Enable(MP_VIEW, file->PreviewAvailable() );
722 menu->Check( MP_SWAP_A4AF_TO_THIS_AUTO, file->IsA4AFAuto() );
724 int priority = file->IsAutoDownPriority() ? PR_AUTO : file->GetDownPriority();
726 priomenu->Check( MP_PRIOHIGH, priority == PR_HIGH );
727 priomenu->Check( MP_PRIONORMAL, priority == PR_NORMAL );
728 priomenu->Check( MP_PRIOLOW, priority == PR_LOW );
729 priomenu->Check( MP_PRIOAUTO, priority == PR_AUTO );
731 menu->Enable( MP_MENU_EXTD, canPause );
733 PopupMenu(m_menu, evt.GetPoint());
734 delete m_menu;
735 m_menu = NULL;
739 void CDownloadListCtrl::OnMouseMiddleClick(wxListEvent& evt)
741 // Check if clicked item is selected. If not, unselect all and select it.
742 long index = CheckSelection(evt);
743 if ( index < 0 ) {
744 return;
747 CFileDetailDialog(this, ((FileCtrlItem_Struct*)GetItemData( index ))->GetFile()).ShowModal();
751 void CDownloadListCtrl::OnKeyPressed( wxKeyEvent& event )
753 // Check if delete was pressed
754 switch (event.GetKeyCode()) {
755 case WXK_NUMPAD_DELETE:
756 case WXK_DELETE: {
757 wxCommandEvent evt;
758 OnCancelFile( evt );
759 break;
761 case WXK_F2: {
762 ItemList files = ::GetSelectedItems( this );
763 if (files.size() == 1) {
764 CPartFile* file = files.front()->GetFile();
766 // Currently renaming of completed files causes problem with kad
767 if (file->IsPartFile()) {
768 wxString strNewName = ::wxGetTextFromUser(
769 _("Enter new name for this file:"),
770 _("File rename"), file->GetFileName().GetPrintable());
772 CPath newName = CPath(strNewName);
773 if (newName.IsOk() && (newName != file->GetFileName())) {
774 theApp->sharedfiles->RenameFile(file, newName);
778 break;
780 default:
781 event.Skip();
786 void CDownloadListCtrl::OnDrawItem(
787 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
789 // Don't do any drawing if there's nobody to see it.
790 if ( !theApp->amuledlg->IsDialogVisible( CamuleDlg::DT_TRANSFER_WND ) ) {
791 return;
794 FileCtrlItem_Struct* content = (FileCtrlItem_Struct *)GetItemData(item);
796 // Define text-color and background
797 // and the border of the drawn area
798 if (highlighted) {
799 CMuleColour colour;
800 if (GetFocus()) {
801 dc->SetBackground(m_hilightBrush);
802 colour = m_hilightBrush.GetColour();
803 } else {
804 dc->SetBackground(m_hilightUnfocusBrush);
805 colour = m_hilightUnfocusBrush.GetColour();
807 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
808 dc->SetPen( colour.Blend(65).GetPen() );
809 } else {
810 dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
811 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
812 dc->SetPen(*wxTRANSPARENT_PEN);
814 dc->SetBrush( dc->GetBackground() );
816 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
818 dc->SetPen(*wxTRANSPARENT_PEN);
820 if (!highlighted || !GetFocus() ) {
821 // If we have category, override textforeground with what category tells us.
822 CPartFile *file = content->GetFile();
823 if ( file->GetCategory() ) {
824 dc->SetTextForeground(CMuleColour(theApp->glob_prefs->GetCatColor(file->GetCategory())) );
828 // Various constant values we use
829 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
830 const int iOffset = 4;
832 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
833 for (int i = 0; i < GetColumnCount(); i++) {
834 wxListItem listitem;
835 GetColumn(i, listitem);
837 if (listitem.GetWidth() > 2*iOffset) {
838 cur_rec.width = listitem.GetWidth() - 2*iOffset;
840 // Make a copy of the current rectangle so we can apply specific tweaks
841 wxRect target_rec = cur_rec;
842 if ( i == ColumnProgress ) {
843 // Double the offset to make room for the cirle-marker
844 target_rec.x += iOffset;
845 target_rec.width -= iOffset;
846 } else {
847 // will ensure that text is about in the middle ;)
848 target_rec.y += iTextOffset;
851 // Draw the item
852 DrawFileItem(dc, i, target_rec, content);
855 // Increment to the next column
856 cur_rec.x += listitem.GetWidth();
861 void CDownloadListCtrl::DrawFileItem( wxDC* dc, int nColumn, const wxRect& rect, FileCtrlItem_Struct* item ) const
863 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
865 const CPartFile* file = item->GetFile();
867 // Used to contain the contenst of cells that dont need any fancy drawing, just text.
868 wxString text;
870 switch (nColumn) {
871 // Part Number
872 case ColumnPart: {
873 if (file->IsPartFile() && !(file->GetStatus() == PS_COMPLETE)) {
874 text = file->GetPartMetFileName().RemoveAllExt().GetPrintable();
876 break;
878 // Filename
879 case ColumnFileName: {
880 wxString filename = file->GetFileName().GetPrintable();
882 if (file->HasRating() || file->HasComment()) {
883 int image = Client_CommentOnly_Smiley;
884 if (file->HasRating()) {
885 image = Client_InvalidRating_Smiley + file->UserRating() - 1;
888 wxASSERT(image >= Client_InvalidRating_Smiley);
889 wxASSERT(image <= Client_CommentOnly_Smiley);
891 int imgWidth = 16;
893 // it's already centered by OnDrawItem() ...
894 m_ImageList.Draw(image, *dc, rect.GetX(), rect.GetY() - 1,
895 wxIMAGELIST_DRAW_TRANSPARENT);
896 dc->DrawText(filename, rect.GetX() + imgWidth + 4, rect.GetY());
897 } else {
898 dc->DrawText(filename, rect.GetX(), rect.GetY());
900 break;
903 // Filesize
904 case ColumnSize:
905 text = CastItoXBytes( file->GetFileSize() );
906 break;
908 // Transferred
909 case ColumnTransferred:
910 text = CastItoXBytes( file->GetTransferred() );
911 break;
913 // Completed
914 case ColumnCompleted:
915 text = CastItoXBytes( file->GetCompletedSize() );
916 break;
918 // Speed
919 case ColumnSpeed:
920 if ( file->GetTransferingSrcCount() ) {
921 text = wxString::Format( wxT("%.1f "), file->GetKBpsDown() ) +
922 _("kB/s");
924 break;
926 // Progress
927 case ColumnProgress:{
928 if (thePrefs::ShowProgBar()) {
929 int iWidth = rect.GetWidth() - 2;
930 int iHeight = rect.GetHeight() - 2;
932 // DO NOT DRAW IT ALL THE TIME
933 uint32 dwTicks = GetTickCount();
935 wxMemoryDC cdcStatus;
937 if ( item->dwUpdated < dwTicks || !item->status || iWidth != item->status->GetWidth() ) {
938 if ( item->status == NULL) {
939 item->status = new wxBitmap(iWidth, iHeight);
940 } else if ( item->status->GetWidth() != iWidth ) {
941 // Only recreate if the size has changed
942 item->status->Create(iWidth, iHeight);
945 cdcStatus.SelectObject( *item->status );
947 if ( thePrefs::UseFlatBar() ) {
948 DrawFileStatusBar( file, &cdcStatus,
949 wxRect(0, 0, iWidth, iHeight), true);
950 } else {
951 DrawFileStatusBar( file, &cdcStatus,
952 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
954 // Draw black border
955 cdcStatus.SetPen( *wxBLACK_PEN );
956 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
957 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
960 item->dwUpdated = dwTicks + 5000; // Plus five seconds
961 } else {
962 cdcStatus.SelectObject( *item->status );
965 dc->Blit( rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
967 if (thePrefs::ShowPercent()) {
968 // Percentage of completing
969 // We strip anything below the first decimal point,
970 // to avoid Format doing roundings
971 float percent = floor( file->GetPercentCompleted() * 10.0f ) / 10.0f;
973 wxString buffer = wxString::Format( wxT("%.1f%%"), percent );
974 int middlex = (2*rect.GetX() + rect.GetWidth()) >> 1;
975 int middley = (2*rect.GetY() + rect.GetHeight()) >> 1;
977 wxCoord textwidth, textheight;
979 dc->GetTextExtent(buffer, &textwidth, &textheight);
980 wxColour AktColor = dc->GetTextForeground();
981 if (thePrefs::ShowProgBar()) {
982 dc->SetTextForeground(*wxWHITE);
983 } else {
984 dc->SetTextForeground(*wxBLACK);
986 dc->DrawText(buffer, middlex - (textwidth >> 1), middley - (textheight >> 1));
987 dc->SetTextForeground(AktColor);
991 break;
994 // Sources
995 case ColumnSources: {
996 uint16 sc = file->GetSourceCount();
997 uint16 ncsc = file->GetNotCurrentSourcesCount();
998 if ( ncsc ) {
999 text = wxString::Format( wxT("%i/%i" ), sc - ncsc, sc );
1000 } else {
1001 text = wxString::Format( wxT("%i"), sc );
1004 if ( file->GetSrcA4AFCount() ) {
1005 text += wxString::Format( wxT("+%i"), file->GetSrcA4AFCount() );
1008 if ( file->GetTransferingSrcCount() ) {
1009 text += wxString::Format( wxT(" (%i)"), file->GetTransferingSrcCount() );
1012 break;
1015 // Priority
1016 case ColumnPriority:
1017 text = PriorityToStr( file->GetDownPriority(), file->IsAutoDownPriority() );
1018 break;
1020 // File-status
1021 case ColumnStatus:
1022 text = file->getPartfileStatus();
1023 break;
1025 // Remaining
1026 case ColumnTimeRemaining: {
1027 if ((file->GetStatus() != PS_COMPLETING) && file->IsPartFile()) {
1028 uint64 remainSize = file->GetFileSize() - file->GetCompletedSize();
1029 sint32 remainTime = file->getTimeRemaining();
1031 if (remainTime >= 0) {
1032 text = CastSecondsToHM(remainTime);
1033 } else {
1034 text = _("Unknown");
1037 text += wxT(" (") + CastItoXBytes(remainSize) + wxT(")");
1039 break;
1042 // Last seen completed
1043 case ColumnLastSeenComplete: {
1044 if ( file->lastseencomplete ) {
1045 text = wxDateTime( file->lastseencomplete ).Format( _("%y/%m/%d %H:%M:%S") );
1046 } else {
1047 text = _("Unknown");
1049 break;
1052 // Last received
1053 case ColumnLastReception: {
1054 const time_t lastReceived = file->GetLastChangeDatetime();
1055 if (lastReceived) {
1056 text = wxDateTime(lastReceived).Format( _("%y/%m/%d %H:%M:%S") );
1057 } else {
1058 text = _("Unknown");
1063 if ( !text.IsEmpty() ) {
1064 dc->DrawText( text, rect.GetX(), rect.GetY() );
1068 wxString CDownloadListCtrl::GetTTSText(unsigned item) const
1070 return ((FileCtrlItem_Struct*)GetItemData(item))->GetFile()->GetFileName().GetPrintable();
1074 int CDownloadListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1076 FileCtrlItem_Struct* item1 = (FileCtrlItem_Struct*)param1;
1077 FileCtrlItem_Struct* item2 = (FileCtrlItem_Struct*)param2;
1079 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1080 sortData &= CMuleListCtrl::COLUMN_MASK;
1082 // We modify the result so that it matches with ascending or decending
1083 return sortMod * Compare( item1->GetFile(), item2->GetFile(), sortData);
1087 int CDownloadListCtrl::Compare( const CPartFile* file1, const CPartFile* file2, long lParamSort)
1089 int result = 0;
1091 switch (lParamSort) {
1092 // Sort by part number
1093 case ColumnPart:
1094 result = CmpAny(
1095 file1->GetPartMetFileName().RemoveAllExt(),
1096 file2->GetPartMetFileName().RemoveAllExt() );
1097 break;
1099 // Sort by filename
1100 case ColumnFileName:
1101 result = CmpAny(
1102 file1->GetFileName(),
1103 file2->GetFileName() );
1104 break;
1106 // Sort by size
1107 case ColumnSize:
1108 result = CmpAny(
1109 file1->GetFileSize(),
1110 file2->GetFileSize() );
1111 break;
1113 // Sort by transferred
1114 case ColumnTransferred:
1115 result = CmpAny(
1116 file1->GetTransferred(),
1117 file2->GetTransferred() );
1118 break;
1120 // Sort by completed
1121 case ColumnCompleted:
1122 result = CmpAny(
1123 file1->GetCompletedSize(),
1124 file2->GetCompletedSize() );
1125 break;
1127 // Sort by speed
1128 case ColumnSpeed:
1129 result = CmpAny(
1130 file1->GetKBpsDown() * 1024,
1131 file2->GetKBpsDown() * 1024 );
1132 break;
1134 // Sort by percentage completed
1135 case ColumnProgress:
1136 result = CmpAny(
1137 file1->GetPercentCompleted(),
1138 file2->GetPercentCompleted() );
1139 break;
1141 // Sort by number of sources
1142 case ColumnSources:
1143 result = CmpAny(
1144 file1->GetSourceCount(),
1145 file2->GetSourceCount() );
1146 break;
1148 // Sort by priority
1149 case ColumnPriority:
1150 result = CmpAny(
1151 file1->GetDownPriority(),
1152 file2->GetDownPriority() );
1153 break;
1155 // Sort by status
1156 case ColumnStatus:
1157 result = CmpAny(
1158 file1->getPartfileStatusRang(),
1159 file2->getPartfileStatusRang() );
1160 break;
1162 // Sort by remaining time
1163 case ColumnTimeRemaining:
1164 if (file1->getTimeRemaining() == -1) {
1165 if (file2->getTimeRemaining() == -1) {
1166 result = 0;
1167 } else {
1168 result = -1;
1170 } else {
1171 if (file2->getTimeRemaining() == -1) {
1172 result = 1;
1173 } else {
1174 result = CmpAny(
1175 file1->getTimeRemaining(),
1176 file2->getTimeRemaining() );
1179 break;
1181 // Sort by last seen complete
1182 case ColumnLastSeenComplete:
1183 result = CmpAny(
1184 file1->lastseencomplete,
1185 file2->lastseencomplete );
1186 break;
1188 // Sort by last reception
1189 case ColumnLastReception:
1190 result = CmpAny(
1191 file1->GetLastChangeDatetime(),
1192 file2->GetLastChangeDatetime() );
1193 break;
1196 return result;
1199 void CDownloadListCtrl::ClearCompleted()
1201 m_completedFiles = false;
1202 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(false);
1204 // Search for completed files
1205 for ( ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ) {
1206 FileCtrlItem_Struct* item = it->second; ++it;
1208 CPartFile* file = item->GetFile();
1210 if ( file->IsPartFile() == false ) {
1211 RemoveFile(file);
1217 void CDownloadListCtrl::ShowFilesCount( int diff )
1219 m_filecount += diff;
1221 wxStaticText* label = CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText );
1223 label->SetLabel( wxString::Format( _("Downloads (%i)"), m_filecount ) );
1224 label->GetParent()->Layout();
1227 bool CDownloadListCtrl::ShowItemInCurrentCat(
1228 const CPartFile* file, int newsel ) const
1230 return
1231 ((newsel == 0 && !thePrefs::ShowAllNotCats()) ||
1232 (newsel == 0 && thePrefs::ShowAllNotCats() && file->GetCategory() == 0)) ||
1233 (newsel > 0 && newsel == file->GetCategory());
1236 static const CMuleColour crHave(104, 104, 104);
1237 static const CMuleColour crFlatHave(0, 0, 0);
1239 static const CMuleColour crPending(255, 208, 0);
1240 static const CMuleColour crFlatPending(255, 255, 100);
1242 static const CMuleColour crProgress(0, 224, 0);
1243 static const CMuleColour crFlatProgress(0, 150, 0);
1245 static const CMuleColour crMissing(255, 0, 0);
1247 void CDownloadListCtrl::DrawFileStatusBar(
1248 const CPartFile* file, wxDC* dc, const wxRect& rect, bool bFlat ) const
1250 static CBarShader s_ChunkBar(16);
1252 s_ChunkBar.SetHeight(rect.height);
1253 s_ChunkBar.SetWidth(rect.width);
1254 s_ChunkBar.SetFileSize( file->GetFileSize() );
1255 s_ChunkBar.Set3dDepth( thePrefs::Get3DDepth() );
1257 if ( file->GetStatus() == PS_COMPLETE || file->GetStatus() == PS_COMPLETING ) {
1258 s_ChunkBar.Fill( bFlat ? crFlatProgress : crProgress );
1259 s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
1260 return;
1263 // Part availability ( of missing parts )
1264 const CPartFile::CGapPtrList& gaplist = file->GetGapList();
1265 CPartFile::CGapPtrList::const_iterator it = gaplist.begin();
1266 uint64 lastGapEnd = 0;
1267 CMuleColour colour;
1269 for (; it != gaplist.end(); ++it) {
1270 Gap_Struct* gap = *it;
1272 // Start position
1273 uint32 start = ( gap->start / PARTSIZE );
1274 // fill the Have-Part (between this gap and the last)
1275 if (gap->start) {
1276 s_ChunkBar.FillRange(lastGapEnd + 1, gap->start - 1, bFlat ? crFlatHave : crHave);
1278 lastGapEnd = gap->end;
1279 // End position
1280 uint32 end = ( gap->end / PARTSIZE ) + 1;
1282 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
1283 if ( end > file->GetPartCount() ) {
1284 end = file->GetPartCount();
1287 // Place each gap, one PART at a time
1288 for ( uint64 i = start; i < end; ++i ) {
1289 if ( i < file->m_SrcpartFrequency.size() && file->m_SrcpartFrequency[i]) {
1290 int blue = 210 - ( 22 * ( file->m_SrcpartFrequency[i] - 1 ) );
1291 colour.Set(0, ( blue < 0 ? 0 : blue ), 255 );
1292 } else {
1293 colour = crMissing;
1296 if ( file->IsStopped() ) {
1297 colour.Blend(50);
1300 uint64 gap_begin = ( i == start ? gap->start : PARTSIZE * i );
1301 uint64 gap_end = ( i == end - 1 ? gap->end : PARTSIZE * ( i + 1 ) - 1 );
1303 s_ChunkBar.FillRange( gap_begin, gap_end, colour);
1307 // fill the last Have-Part (between this gap and the last)
1308 s_ChunkBar.FillRange(lastGapEnd + 1, file->GetFileSize() - 1, bFlat ? crFlatHave : crHave);
1310 // Pending parts
1311 const CPartFile::CReqBlockPtrList& requestedblocks_list = file->GetRequestedBlockList();
1312 CPartFile::CReqBlockPtrList::const_iterator it2 = requestedblocks_list.begin();
1313 // adjacing pending parts must be joined to avoid bright lines between them
1314 uint64 lastStartOffset = 0;
1315 uint64 lastEndOffset = 0;
1317 colour = bFlat ? crFlatPending : crPending;
1319 if ( file->IsStopped() ) {
1320 colour.Blend(50);
1323 for (; it2 != requestedblocks_list.end(); ++it2) {
1325 if ((*it2)->StartOffset > lastEndOffset + 1) {
1326 // not adjacing, draw last block
1327 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
1328 lastStartOffset = (*it2)->StartOffset;
1329 lastEndOffset = (*it2)->EndOffset;
1330 } else {
1331 // adjacing, grow block
1332 lastEndOffset = (*it2)->EndOffset;
1336 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
1339 // Draw the progress-bar
1340 s_ChunkBar.Draw( dc, rect.x, rect.y, bFlat );
1343 // Green progressbar width
1344 int width = (int)(( (float)rect.width / (float)file->GetFileSize() ) *
1345 file->GetCompletedSize() );
1347 if ( bFlat ) {
1348 dc->SetBrush( crFlatProgress.GetBrush() );
1350 dc->DrawRectangle( rect.x, rect.y, width, 3 );
1351 } else {
1352 // Draw the two black lines for 3d-effect
1353 dc->SetPen( *wxBLACK_PEN );
1354 dc->DrawLine( rect.x, rect.y + 0, rect.x + width, rect.y + 0 );
1355 dc->DrawLine( rect.x, rect.y + 2, rect.x + width, rect.y + 2 );
1357 // Draw the green line
1358 dc->SetPen( *(wxThePenList->FindOrCreatePen( crProgress , 1, wxSOLID ) ));
1359 dc->DrawLine( rect.x, rect.y + 1, rect.x + width, rect.y + 1 );
1363 #ifdef __WXMSW__
1364 # define QUOTE wxT("\"")
1365 #else
1366 # define QUOTE wxT("\'")
1367 #endif
1369 void CDownloadListCtrl::PreviewFile(CPartFile* file)
1371 wxString command;
1372 // If no player set in preferences, use mplayer.
1373 // And please, do a warning also :P
1374 if (thePrefs::GetVideoPlayer().IsEmpty()) {
1375 wxMessageBox(_(
1376 "To prevent this warning to show up in every preview,\nset your preferred video player in preferences (default is mplayer)."),
1377 _("File preview"), wxOK, this);
1378 // Since newer versions for some reason mplayer does not automatically
1379 // select video output device and needs a parameter, go figure...
1380 command = wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE wxT("$file") QUOTE;
1381 } else {
1382 command = thePrefs::GetVideoPlayer();
1385 // Check if we are (pre)viewing a completed file or not
1386 if (file->GetStatus() != PS_COMPLETE) {
1387 // Remove the .met and see if out video player specifiation uses the magic string
1388 wxString fileWithoutMet = thePrefs::GetTempDir().JoinPaths(
1389 file->GetPartMetFileName().RemoveExt()).GetRaw();
1390 if (!command.Replace(wxT("$file"), fileWithoutMet)) {
1391 // No magic string, so we just append the filename to the player command
1392 // Need to use quotes in case filename contains spaces
1393 command << wxT(" ") << QUOTE << fileWithoutMet << QUOTE;
1395 } else {
1396 // This is a complete file
1397 // FIXME: This is probably not going to work if the filenames are mangled ...
1398 wxString rawFileName = file->GetFullName().GetRaw();
1399 if (!command.Replace(wxT("$file"), rawFileName)) {
1400 // No magic string, so we just append the filename to the player command
1401 // Need to use quotes in case filename contains spaces
1402 command << wxT(" ") << QUOTE << rawFileName << QUOTE;
1406 // We can't use wxShell here, it blocks the app
1407 CTerminationProcess *p = new CTerminationProcess(command);
1408 int ret = wxExecute(command, wxEXEC_ASYNC, p);
1409 bool ok = ret > 0;
1410 if (!ok) {
1411 delete p;
1412 AddLogLineM( true,
1413 CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
1414 command );
1417 // File_checked_for_headers