No reason to check for Cryptopp < 5.5
[amule.git] / src / DownloadListCtrl.cpp
blob7cb56dbebf49cbe9725172762314d240adfb40d1
1 //
2 // This file is part of the aMule Project.
3 //
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 )
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.
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_*
40 #include "Logger.h"
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"
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_filecount = 0;
174 m_ItemSelectionChangePending = false;
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 if (file->IsCompleted()) {
210 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(true);
212 SortList();
217 void CDownloadListCtrl::RemoveFile( CPartFile* file )
219 wxASSERT( 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() ) {
228 delete it->second;
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();
244 #ifndef __WINDOWS__
245 // Get visible lines if we need them
246 if ( rangeIt.first != rangeIt.second ) {
247 GetVisibleLines( &first, &last );
249 #endif
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 );
262 if ( index > -1 ) {
263 if ( show ) {
264 item->dwUpdated = 0;
266 // Only update visible lines
267 if ( index >= first && index <= last) {
268 RefreshItem( index );
270 } else {
271 // Item should no longer be shown in
272 // the current category
273 ShowFile( file, false );
275 } else if ( show ) {
276 // Item has been hidden but new status means
277 // that it should it should be shown in the
278 // current category
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 )
291 wxASSERT( file );
293 ListItems::iterator it = m_ListItems.find( file );
295 if ( it != m_ListItems.end() ) {
296 FileCtrlItem_Struct* item = it->second;
298 if ( show ) {
299 // Check if the file is already being displayed
300 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
301 if ( index == -1 ) {
302 long newitem = InsertItem( GetItemCount(), wxEmptyString );
304 SetItemPtrData( newitem, reinterpret_cast<wxUIntPtr>(item) );
306 wxListItem myitem;
307 myitem.m_itemId = newitem;
308 myitem.SetBackgroundColour( GetBackgroundColour() );
310 SetItem(myitem);
312 RefreshItem( newitem );
314 ShowFilesCount( 1 );
316 } else {
317 // Try to find the file and remove it
318 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
319 if ( index > -1 ) {
320 DeleteItem( index );
321 ShowFilesCount( -1 );
327 void CDownloadListCtrl::ChangeCategory( int newCategory )
329 Freeze();
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);
356 Thaw();
358 m_category = newCategory;
362 uint8 CDownloadListCtrl::GetCategory() const
364 return m_category;
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 static ItemList GetSelectedItems( CDownloadListCtrl* list)
376 ItemList results;
378 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
380 while ( index > -1 ) {
381 FileCtrlItem_Struct* item = reinterpret_cast<FileCtrlItem_Struct*>(list->GetItemData( index ));
382 results.push_back( item );
384 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
387 return results;
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();
397 if (file) {
398 switch (file->GetStatus()) {
399 case PS_WAITINGFORHASH:
400 case PS_HASHING:
401 case PS_COMPLETING:
402 case PS_COMPLETE:
403 files.erase(it1);
404 break;
408 if (!files.empty()) {
409 wxString question;
410 if (files.size() == 1) {
411 question = _("Are you sure that you wish to delete the selected file?");
412 } else {
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();
418 if (file) {
419 CoreNotify_PartFile_Delete(file);
427 void CDownloadListCtrl::OnSetPriority( wxCommandEvent& event )
429 int priority = 0;
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;
435 default:
436 wxFAIL;
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 );
446 } else {
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 );
465 break;
467 case MP_SWAP_A4AF_TO_THIS_AUTO:
468 CoreNotify_PartFile_Swap_A4AF_Auto( file );
469 break;
471 case MP_SWAP_A4AF_TO_ANY_OTHER:
472 CoreNotify_PartFile_Swap_A4AF_Others( file );
473 break;
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);
487 wxListEvent ev;
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() ) {
503 case MP_PAUSE:
504 CoreNotify_PartFile_Pause( file );
505 break;
507 case MP_RESUME:
508 CoreNotify_PartFile_Resume( file );
509 break;
511 case MP_STOP:
512 CoreNotify_PartFile_Stop( file );
513 break;
519 void CDownloadListCtrl::OnClearCompleted( wxCommandEvent& WXUNUSED(event) )
521 ClearCompleted();
525 void CDownloadListCtrl::OnGetLink(wxCommandEvent& event)
527 ItemList files = ::GetSelectedItems( this );
529 wxString URIs;
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");
536 } else {
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))
549 wxString feed;
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();
555 } else {
556 feed += wxT("\n");
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 );
570 if (index >= 0) {
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() );
582 dialog.ShowModal();
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 = reinterpret_cast<FileCtrlItem_Struct*>(GetItemData(evt.GetIndex()))->GetFile();
599 if ((!file->IsPartFile() || file->IsCompleted()) && file->PreviewAvailable()) {
600 PreviewFile( file );
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 = reinterpret_cast<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);
635 if (index < 0) {
636 return;
639 delete m_menu;
640 m_menu = NULL;
642 FileCtrlItem_Struct* item = reinterpret_cast<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++) {
696 if ( i == 0 ) {
697 cats->Append( MP_ASSIGNCAT, _("unassign") );
698 } else {
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();
708 // then set state
709 bool canStop;
710 bool canPause;
711 bool canCancel;
712 bool fileResumable;
713 if (file->GetStatus(true) != PS_ALLOCATING) {
714 const uint8_t fileStatus = file->GetStatus();
715 canStop =
716 (fileStatus != PS_ERROR) &&
717 (fileStatus != PS_COMPLETE) &&
718 (file->IsStopped() != true);
719 canPause = (file->GetStatus() != PS_PAUSED) && canStop;
720 fileResumable =
721 (fileStatus == PS_PAUSED) ||
722 (fileStatus == PS_ERROR) ||
723 (fileStatus == PS_INSUFFICIENT);
724 canCancel = fileStatus != PS_COMPLETE;
725 } else {
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() );
735 wxString view;
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);
764 delete m_menu;
765 m_menu = NULL;
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);
773 if (index >= 0) {
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(reinterpret_cast<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:
799 case WXK_DELETE: {
800 wxCommandEvent evt;
801 OnCancelFile( evt );
802 break;
804 case WXK_F2: {
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);
821 break;
823 default:
824 event.Skip();
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 ) ) {
834 return;
837 FileCtrlItem_Struct* content = reinterpret_cast<FileCtrlItem_Struct *>(GetItemData(item));
839 // Define text-color and background
840 // and the border of the drawn area
841 if (highlighted) {
842 CMuleColour colour;
843 if (GetFocus()) {
844 dc->SetBackground(m_hilightBrush);
845 colour = m_hilightBrush.GetColour();
846 } else {
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() );
852 } else {
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++) {
877 wxListItem listitem;
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;
889 } else {
890 // will ensure that text is about in the middle ;)
891 target_rec.y += iTextOffset;
894 // Draw the item
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.
911 wxString text;
913 switch (nColumn) {
914 // Part Number
915 case ColumnPart: {
916 if (file->IsPartFile() && !file->IsCompleted()) {
917 text = CFormat(wxT("%03d")) % file->GetPartMetNumber();
919 break;
921 // Filename
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);
934 int imgWidth = 16;
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());
940 } else {
941 dc->DrawText(filename, rect.GetX(), rect.GetY());
943 break;
946 // Filesize
947 case ColumnSize:
948 text = CastItoXBytes( file->GetFileSize() );
949 break;
951 // Transferred
952 case ColumnTransferred:
953 text = CastItoXBytes( file->GetTransferred() );
954 break;
956 // Completed
957 case ColumnCompleted:
958 text = CastItoXBytes( file->GetCompletedSize() );
959 break;
961 // Speed
962 case ColumnSpeed:
963 if ( file->GetTransferingSrcCount() ) {
964 if (file->GetKBpsDown() >= 1024) {
965 text = CFormat(_("%.1f MB/s")) % (file->GetKBpsDown() / 1024.0);
966 } else {
967 text = CFormat(_("%.1f kB/s")) % file->GetKBpsDown();
970 break;
972 // Progress
973 case ColumnProgress:{
974 if (thePrefs::ShowProgBar()) {
975 int iWidth = rect.GetWidth() - 2;
976 int iHeight = rect.GetHeight() - 2;
978 // DO NOT DRAW IT ALL THE TIME
979 uint32 dwTicks = GetTickCount();
981 wxMemoryDC cdcStatus;
983 if (item->dwUpdated < dwTicks || file->GetHashingProgress() > 0
984 || !item->status || iWidth != item->status->GetWidth()) {
985 if ( item->status == NULL) {
986 item->status = new wxBitmap(iWidth, iHeight);
987 } else if ( item->status->GetWidth() != iWidth ) {
988 // Only recreate if the size has changed
989 item->status->Create(iWidth, iHeight);
992 cdcStatus.SelectObject( *item->status );
994 if ( thePrefs::UseFlatBar() ) {
995 DrawFileStatusBar( file, &cdcStatus,
996 wxRect(0, 0, iWidth, iHeight), true);
997 } else {
998 DrawFileStatusBar( file, &cdcStatus,
999 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
1001 // Draw black border
1002 cdcStatus.SetPen( *wxBLACK_PEN );
1003 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
1004 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
1007 item->dwUpdated = dwTicks + 5000; // Plus five seconds
1008 } else {
1009 cdcStatus.SelectObject( *item->status );
1012 dc->Blit( rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
1014 if (thePrefs::ShowPercent()) {
1015 // Percentage of completing or hashing
1016 uint16 hashingProgress = file->GetHashingProgress();
1017 double percent = hashingProgress == 0 ? file->GetPercentCompleted()
1018 : 100.0 * hashingProgress * PARTSIZE / file->GetFileSize();
1019 if (file->IsCompleted()) {
1020 percent = 100.0;
1021 } else if (percent > 99.9) {
1022 percent = 99.9;
1024 wxString buffer = CFormat(wxT("%.1f%%")) % percent;
1025 int middlex = (2*rect.GetX() + rect.GetWidth()) >> 1;
1026 int middley = (2*rect.GetY() + rect.GetHeight()) >> 1;
1028 wxCoord textwidth, textheight;
1030 dc->GetTextExtent(buffer, &textwidth, &textheight);
1031 wxColour AktColor = dc->GetTextForeground();
1032 // Ordinary progress bar: white percentage
1033 // Hashing progressbar (green/yellow): black percentage
1034 if (thePrefs::ShowProgBar() && hashingProgress == 0) {
1035 dc->SetTextForeground(*wxWHITE);
1036 } else {
1037 dc->SetTextForeground(*wxBLACK);
1039 dc->DrawText(buffer, middlex - (textwidth >> 1), middley - (textheight >> 1));
1040 dc->SetTextForeground(AktColor);
1044 break;
1047 // Sources
1048 case ColumnSources: {
1049 uint16 sc = file->GetSourceCount();
1050 uint16 ncsc = file->GetNotCurrentSourcesCount();
1051 if ( ncsc ) {
1052 text = CFormat(wxT("%i/%i")) % (sc - ncsc) % sc;
1053 } else {
1054 text = CFormat(wxT("%i")) % sc;
1057 if ( file->GetSrcA4AFCount() ) {
1058 text += CFormat(wxT("+%i")) % file->GetSrcA4AFCount();
1061 if ( file->GetTransferingSrcCount() ) {
1062 text += CFormat(wxT(" (%i)")) % file->GetTransferingSrcCount();
1065 break;
1068 // Priority
1069 case ColumnPriority:
1070 text = PriorityToStr( file->GetDownPriority(), file->IsAutoDownPriority() );
1071 break;
1073 // File-status
1074 case ColumnStatus:
1075 text = file->getPartfileStatus();
1076 break;
1078 // Remaining
1079 case ColumnTimeRemaining: {
1080 if ((file->GetStatus() != PS_COMPLETING) && file->IsPartFile()) {
1081 uint64 remainSize = file->GetFileSize() - file->GetCompletedSize();
1082 sint32 remainTime = file->getTimeRemaining();
1084 if (remainTime >= 0) {
1085 text = CastSecondsToHM(remainTime);
1086 } else {
1087 text = _("Unknown");
1090 text += wxT(" (") + CastItoXBytes(remainSize) + wxT(")");
1092 break;
1095 // Last seen completed
1096 case ColumnLastSeenComplete: {
1097 if ( file->lastseencomplete ) {
1098 text = wxDateTime( file->lastseencomplete ).Format( _("%y/%m/%d %H:%M:%S") );
1099 } else {
1100 text = _("Unknown");
1102 break;
1105 // Last received
1106 case ColumnLastReception: {
1107 const time_t lastReceived = file->GetLastChangeDatetime();
1108 if (lastReceived) {
1109 text = wxDateTime(lastReceived).Format( _("%y/%m/%d %H:%M:%S") );
1110 } else {
1111 text = _("Unknown");
1116 if ( !text.IsEmpty() ) {
1117 dc->DrawText( text, rect.GetX(), rect.GetY() );
1121 wxString CDownloadListCtrl::GetTTSText(unsigned item) const
1123 return reinterpret_cast<FileCtrlItem_Struct*>(GetItemData(item))->GetFile()->GetFileName().GetPrintable();
1127 int CDownloadListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1129 FileCtrlItem_Struct* item1 = reinterpret_cast<FileCtrlItem_Struct*>(param1);
1130 FileCtrlItem_Struct* item2 = reinterpret_cast<FileCtrlItem_Struct*>(param2);
1132 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1133 sortData &= CMuleListCtrl::COLUMN_MASK;
1135 // We modify the result so that it matches with ascending or decending
1136 return sortMod * Compare( item1->GetFile(), item2->GetFile(), sortData);
1140 int CDownloadListCtrl::Compare( const CPartFile* file1, const CPartFile* file2, long lParamSort)
1142 int result = 0;
1144 switch (lParamSort) {
1145 // Sort by part number
1146 case ColumnPart:
1147 result = CmpAny(
1148 file1->GetPartMetNumber(),
1149 file2->GetPartMetNumber() );
1150 break;
1152 // Sort by filename
1153 case ColumnFileName:
1154 result = CmpAny(
1155 file1->GetFileName(),
1156 file2->GetFileName() );
1157 break;
1159 // Sort by size
1160 case ColumnSize:
1161 result = CmpAny(
1162 file1->GetFileSize(),
1163 file2->GetFileSize() );
1164 break;
1166 // Sort by transferred
1167 case ColumnTransferred:
1168 result = CmpAny(
1169 file1->GetTransferred(),
1170 file2->GetTransferred() );
1171 break;
1173 // Sort by completed
1174 case ColumnCompleted:
1175 result = CmpAny(
1176 file1->GetCompletedSize(),
1177 file2->GetCompletedSize() );
1178 break;
1180 // Sort by speed
1181 case ColumnSpeed:
1182 result = CmpAny(
1183 file1->GetKBpsDown() * 1024,
1184 file2->GetKBpsDown() * 1024 );
1185 break;
1187 // Sort by percentage completed
1188 case ColumnProgress:
1189 result = CmpAny(
1190 file1->GetPercentCompleted(),
1191 file2->GetPercentCompleted() );
1192 break;
1194 // Sort by number of sources
1195 case ColumnSources:
1196 result = CmpAny(
1197 file1->GetSourceCount(),
1198 file2->GetSourceCount() );
1199 break;
1201 // Sort by priority
1202 case ColumnPriority:
1203 result = CmpAny(
1204 file1->GetDownPriority(),
1205 file2->GetDownPriority() );
1206 break;
1208 // Sort by status
1209 case ColumnStatus:
1210 result = CmpAny(
1211 file1->getPartfileStatusRang(),
1212 file2->getPartfileStatusRang() );
1213 break;
1215 // Sort by remaining time
1216 case ColumnTimeRemaining:
1217 if (file1->getTimeRemaining() == -1) {
1218 if (file2->getTimeRemaining() == -1) {
1219 result = 0;
1220 } else {
1221 result = 1;
1223 } else {
1224 if (file2->getTimeRemaining() == -1) {
1225 result = -1;
1226 } else {
1227 result = CmpAny(
1228 file1->getTimeRemaining(),
1229 file2->getTimeRemaining() );
1232 break;
1234 // Sort by last seen complete
1235 case ColumnLastSeenComplete:
1236 result = CmpAny(
1237 file1->lastseencomplete,
1238 file2->lastseencomplete );
1239 break;
1241 // Sort by last reception
1242 case ColumnLastReception:
1243 result = CmpAny(
1244 file1->GetLastChangeDatetime(),
1245 file2->GetLastChangeDatetime() );
1246 break;
1249 return result;
1252 void CDownloadListCtrl::ClearCompleted()
1254 CastByID(ID_BTNCLRCOMPL, GetParent(), wxButton)->Enable(false);
1256 // Search for completed files
1257 ListOfUInts32 toClear;
1258 for ( ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); ) {
1259 FileCtrlItem_Struct* item = it->second; ++it;
1261 CPartFile* file = item->GetFile();
1263 if (file->IsCompleted() && file->CheckShowItemInGivenCat(m_category)) {
1264 toClear.push_back(file->ECID());
1267 if (!toClear.empty()) {
1268 theApp->downloadqueue->ClearCompleted(toClear);
1273 void CDownloadListCtrl::ShowFilesCount( int diff )
1275 m_filecount += diff;
1277 wxStaticText* label = CastByName( wxT("downloadsLabel"), GetParent(), wxStaticText );
1279 label->SetLabel(CFormat(_("Downloads (%i)")) % m_filecount);
1280 label->GetParent()->Layout();
1284 static const CMuleColour crHave(104, 104, 104);
1285 static const CMuleColour crFlatHave(0, 0, 0);
1287 static const CMuleColour crPending(255, 208, 0);
1288 static const CMuleColour crFlatPending(255, 255, 100);
1290 static const CMuleColour crProgress(0, 224, 0);
1291 static const CMuleColour crFlatProgress(0, 150, 0);
1293 static const CMuleColour crMissing(255, 0, 0);
1295 void CDownloadListCtrl::DrawFileStatusBar(
1296 const CPartFile* file, wxDC* dc, const wxRect& rect, bool bFlat ) const
1298 static CBarShader s_ChunkBar(16);
1300 s_ChunkBar.SetHeight(rect.height);
1301 s_ChunkBar.SetWidth(rect.width);
1302 s_ChunkBar.SetFileSize( file->GetFileSize() );
1303 s_ChunkBar.Set3dDepth( thePrefs::Get3DDepth() );
1305 if ( file->IsCompleted() || file->GetStatus() == PS_COMPLETING ) {
1306 s_ChunkBar.Fill( bFlat ? crFlatProgress : crProgress );
1307 s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
1308 return;
1309 } else if (file->GetHashingProgress() > 0) {
1310 uint64 left = file->GetHashingProgress() * PARTSIZE;
1311 if (left < file->GetFileSize() - 1) {
1312 // Fill the amount not yet hashed with yellow
1313 s_ChunkBar.FillRange(left + 1, file->GetFileSize() - 1, bFlat ? crFlatPending : crPending);
1314 } else {
1315 left = file->GetFileSize() - 1;
1317 // Fill the amount already hashed with green
1318 s_ChunkBar.FillRange(0, left, bFlat ? crFlatProgress : crProgress);
1319 s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
1320 return;
1322 // Part availability ( of missing parts )
1323 const CGapList& gaplist = file->GetGapList();
1324 CGapList::const_iterator it = gaplist.begin();
1325 uint64 lastGapEnd = 0;
1326 CMuleColour colour;
1328 for (; it != gaplist.end(); ++it) {
1330 // Start position
1331 uint32 start = ( it.start() / PARTSIZE );
1332 // fill the Have-Part (between this gap and the last)
1333 if (it.start()) {
1334 s_ChunkBar.FillRange(lastGapEnd + 1, it.start() - 1, bFlat ? crFlatHave : crHave);
1336 lastGapEnd = it.end();
1337 // End position
1338 uint32 end = ( it.end() / PARTSIZE ) + 1;
1340 // Avoid going past the filesize. Dunno if this can happen, but the old code did check.
1341 if ( end > file->GetPartCount() ) {
1342 end = file->GetPartCount();
1345 // Place each gap, one PART at a time
1346 for ( uint64 i = start; i < end; ++i ) {
1347 if ( i < file->m_SrcpartFrequency.size() && file->m_SrcpartFrequency[i]) {
1348 int blue = 210 - ( 22 * ( file->m_SrcpartFrequency[i] - 1 ) );
1349 colour.Set(0, ( blue < 0 ? 0 : blue ), 255 );
1350 } else {
1351 colour = crMissing;
1354 if ( file->IsStopped() ) {
1355 colour.Blend(50);
1358 uint64 gap_begin = ( i == start ? it.start() : PARTSIZE * i );
1359 uint64 gap_end = ( i == end - 1 ? it.end() : PARTSIZE * ( i + 1 ) - 1 );
1361 s_ChunkBar.FillRange( gap_begin, gap_end, colour);
1365 // fill the last Have-Part (between this gap and the last)
1366 s_ChunkBar.FillRange(lastGapEnd + 1, file->GetFileSize() - 1, bFlat ? crFlatHave : crHave);
1368 // Pending parts
1369 const CPartFile::CReqBlockPtrList& requestedblocks_list = file->GetRequestedBlockList();
1370 CPartFile::CReqBlockPtrList::const_iterator it2 = requestedblocks_list.begin();
1371 // adjacing pending parts must be joined to avoid bright lines between them
1372 uint64 lastStartOffset = 0;
1373 uint64 lastEndOffset = 0;
1375 colour = bFlat ? crFlatPending : crPending;
1377 if ( file->IsStopped() ) {
1378 colour.Blend(50);
1381 for (; it2 != requestedblocks_list.end(); ++it2) {
1383 if ((*it2)->StartOffset > lastEndOffset + 1) {
1384 // not adjacing, draw last block
1385 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
1386 lastStartOffset = (*it2)->StartOffset;
1387 lastEndOffset = (*it2)->EndOffset;
1388 } else {
1389 // adjacing, grow block
1390 lastEndOffset = (*it2)->EndOffset;
1394 s_ChunkBar.FillRange(lastStartOffset, lastEndOffset, colour);
1397 // Draw the progress-bar
1398 s_ChunkBar.Draw( dc, rect.x, rect.y, bFlat );
1401 // Green progressbar width
1402 int width = (int)(( (float)rect.width / (float)file->GetFileSize() ) *
1403 file->GetCompletedSize() );
1405 if ( bFlat ) {
1406 dc->SetBrush( crFlatProgress.GetBrush() );
1408 dc->DrawRectangle( rect.x, rect.y, width, 3 );
1409 } else {
1410 // Draw the two black lines for 3d-effect
1411 dc->SetPen( *wxBLACK_PEN );
1412 dc->DrawLine( rect.x, rect.y + 0, rect.x + width, rect.y + 0 );
1413 dc->DrawLine( rect.x, rect.y + 2, rect.x + width, rect.y + 2 );
1415 // Draw the green line
1416 dc->SetPen( *(wxThePenList->FindOrCreatePen( crProgress , 1, wxSOLID ) ));
1417 dc->DrawLine( rect.x, rect.y + 1, rect.x + width, rect.y + 1 );
1421 #ifdef __WINDOWS__
1422 # define QUOTE wxT("\"")
1423 #else
1424 # define QUOTE wxT("\'")
1425 #endif
1427 void CDownloadListCtrl::PreviewFile(CPartFile* file)
1429 wxString command;
1430 // If no player set in preferences, use mplayer.
1431 // And please, do a warning also :P
1432 if (thePrefs::GetVideoPlayer().IsEmpty()) {
1433 wxMessageBox(_(
1434 "To prevent this warning to show up in every preview,\nset your preferred video player in preferences (default is mplayer)."),
1435 _("File preview"), wxOK, this);
1436 // Since newer versions for some reason mplayer does not automatically
1437 // select video output device and needs a parameter, go figure...
1438 command = wxT("xterm -T \"aMule Preview\" -iconic -e mplayer ") QUOTE wxT("$file") QUOTE;
1439 } else {
1440 command = thePrefs::GetVideoPlayer();
1443 wxString partFile; // File name with full path
1444 wxString partName; // File name only, without path
1446 // Check if we are (pre)viewing a completed file or not
1447 if (!file->IsCompleted()) {
1448 // Remove the .met and see if out video player specifiation uses the magic string
1449 partName = file->GetPartMetFileName().RemoveExt().GetRaw();
1450 partFile = thePrefs::GetTempDir().JoinPaths(file->GetPartMetFileName().RemoveExt()).GetRaw();
1451 } else {
1452 // This is a complete file
1453 // FIXME: This is probably not going to work if the filenames are mangled ...
1454 partName = file->GetFileName().GetRaw();
1455 partFile = file->GetFullName().GetRaw();
1458 // Compatibility with old behaviour
1459 if (!command.Replace(wxT("$file"), wxT("%PARTFILE"))) {
1460 if ((command.Find(wxT("%PARTFILE")) == wxNOT_FOUND) && (command.Find(wxT("%PARTNAME")) == wxNOT_FOUND)) {
1461 // No magic string, so we just append the filename to the player command
1462 // Need to use quotes in case filename contains spaces
1463 command << wxT(" ") << QUOTE << wxT("%PARTFILE") << QUOTE;
1467 #ifndef __WINDOWS__
1468 // We have to escape quote characters in the file name, otherwise arbitrary
1469 // options could be passed to the player.
1470 partFile.Replace(QUOTE, wxT("\\") QUOTE);
1471 partName.Replace(QUOTE, wxT("\\") QUOTE);
1472 #endif
1474 command.Replace(wxT("%PARTFILE"), partFile);
1475 command.Replace(wxT("%PARTNAME"), partName);
1477 // We can't use wxShell here, it blocks the app
1478 CTerminationProcess *p = new CTerminationProcess(command);
1479 int ret = wxExecute(command, wxEXEC_ASYNC, p);
1480 bool ok = ret > 0;
1481 if (!ok) {
1482 delete p;
1483 AddLogLineC(CFormat( _("ERROR: Failed to execute external media-player! Command: `%s'") ) %
1484 command );
1487 // File_checked_for_headers