Fix a bug in Kademlia LAN mode detection which effectively disabled it
[amule.git] / src / GenericClientListCtrl.cpp
blob7bb520446060f699db6a8d458a11fa089efcb085
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 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "GenericClientListCtrl.h"
26 #include <protocol/ed2k/ClientSoftware.h>
27 #include <common/MenuIDs.h>
29 #include <common/Format.h> // Needed for CFormat
30 #include "amule.h" // Needed for theApp
31 #include "amuleDlg.h" // Needed for CamuleDlg
32 #include "BarShader.h" // Needed for CBarShader
33 #include "ClientDetailDialog.h" // Needed for CClientDetailDialog
34 #include "ChatWnd.h" // Needed for CChatWnd
35 #include "CommentDialogLst.h" // Needed for CCommentDialogLst
36 #include "DataToText.h" // Needed for PriorityToStr
37 #include "FileDetailDialog.h" // Needed for CFileDetailDialog
38 #include "GuiEvents.h" // Needed for CoreNotify_*
39 #ifdef ENABLE_IP2COUNTRY
40 #include "IP2Country.h" // Needed for IP2Country
41 #endif
42 #include "Logger.h"
43 #include "muuli_wdr.h" // Needed for ID_DLOADLIST
44 #include "PartFile.h" // Needed for CPartFile
45 #include "Preferences.h"
46 #include "SharedFileList.h" // Needed for CSharedFileList
47 #include "TerminationProcess.h" // Needed for CTerminationProcess
48 #include "updownclient.h" // Needed for CUpDownClient
50 struct ClientCtrlItem_Struct
52 ClientCtrlItem_Struct()
53 : dwUpdated(0),
54 status(NULL),
55 m_owner(NULL),
56 m_sourceValue(NULL),
57 m_type(UNAVAILABLE_SOURCE)
58 { }
60 ~ClientCtrlItem_Struct() {
61 delete status;
64 SourceItemType GetType() const {
65 return m_type;
68 CKnownFile* GetOwner() const {
69 return m_owner;
73 CUpDownClient* GetSource() const {
74 return m_sourceValue;
77 void SetContents(CKnownFile* owner, CUpDownClient* source, SourceItemType type) {
78 m_owner = owner;
79 m_sourceValue = source;
80 m_type = type;
83 void SetType(SourceItemType type) { m_type = type; }
85 uint32 dwUpdated;
86 wxBitmap* status;
88 private:
89 CKnownFile* m_owner;
90 CUpDownClient* m_sourceValue;
91 SourceItemType m_type;
94 #define m_ImageList theApp->amuledlg->m_imagelist
96 BEGIN_EVENT_TABLE(CGenericClientListCtrl, CMuleListCtrl)
97 EVT_LIST_ITEM_ACTIVATED(ID_CLIENTLIST, CGenericClientListCtrl::OnItemActivated)
98 EVT_LIST_ITEM_RIGHT_CLICK(ID_CLIENTLIST, CGenericClientListCtrl::OnMouseRightClick)
99 EVT_LIST_ITEM_MIDDLE_CLICK(ID_CLIENTLIST, CGenericClientListCtrl::OnMouseMiddleClick)
101 EVT_CHAR( CGenericClientListCtrl::OnKeyPressed )
103 EVT_MENU( MP_CHANGE2FILE, CGenericClientListCtrl::OnSwapSource )
104 EVT_MENU( MP_SHOWLIST, CGenericClientListCtrl::OnViewFiles )
105 EVT_MENU( MP_ADDFRIEND, CGenericClientListCtrl::OnAddFriend )
106 EVT_MENU( MP_SENDMESSAGE, CGenericClientListCtrl::OnSendMessage )
107 EVT_MENU( MP_DETAIL, CGenericClientListCtrl::OnViewClientInfo )
108 END_EVENT_TABLE()
110 //! This listtype is used when gathering the selected items.
111 typedef std::list<ClientCtrlItem_Struct*> ItemList;
113 CGenericClientListCtrl::CGenericClientListCtrl(
114 const wxString& tablename,
115 wxWindow *parent, wxWindowID winid, const wxPoint& pos, const wxSize& size,
116 long style, const wxValidator& validator, const wxString& name )
118 CMuleListCtrl( parent, winid, pos, size, style | wxLC_OWNERDRAW | wxLC_VRULES | wxLC_HRULES, validator, name ),
119 m_columndata(0, NULL)
121 // Setting the sorter function must be done in the derived class, to use the translation function correctly.
122 // SetSortFunc( SortProc );
124 // Set the table-name (for loading and saving preferences).
125 SetTableName( tablename );
127 m_menu = NULL;
128 m_showing = false;
130 m_hilightBrush = CMuleColour(wxSYS_COLOUR_HIGHLIGHT).Blend(125).GetBrush();
132 m_hilightUnfocusBrush = CMuleColour(wxSYS_COLOUR_BTNSHADOW).Blend(125).GetBrush();
134 m_clientcount = 0;
137 void CGenericClientListCtrl::InitColumnData()
139 if (!m_columndata.n_columns) {
140 throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
143 for (int i = 0; i < m_columndata.n_columns; ++i) {
144 InsertColumn( i, wxGetTranslation(m_columndata.columns[i].name), wxLIST_FORMAT_LEFT, m_columndata.columns[i].width);
147 LoadSettings();
150 CGenericClientListCtrl::~CGenericClientListCtrl()
152 while ( !m_ListItems.empty() ) {
153 delete m_ListItems.begin()->second;
154 m_ListItems.erase( m_ListItems.begin() );
157 void CGenericClientListCtrl::RawAddSource(CKnownFile* owner, CUpDownClient* source, SourceItemType type)
159 ClientCtrlItem_Struct* newitem = new ClientCtrlItem_Struct;
160 newitem->SetContents(owner, source, type);
162 m_ListItems.insert( ListItemsPair(source, newitem) );
164 long item = InsertItem( GetItemCount(), wxEmptyString );
165 SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
166 SetItemBackgroundColour( item, GetBackgroundColour() );
169 void CGenericClientListCtrl::AddSource(CKnownFile* owner, CUpDownClient* source, SourceItemType type)
171 wxCHECK_RET(owner, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
172 wxCHECK_RET(source, wxT("NULL source in CGenericClientListCtrl::AddSource"));
174 // Update the other instances of this source
175 bool bFound = false;
176 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
177 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
178 ClientCtrlItem_Struct* cur_item = it->second;
180 // Check if this source has been already added to this file => to be sure
181 if ( cur_item->GetOwner() == owner ) {
182 // Update this instance with its new setting
183 if ((type == A4AF_SOURCE) &&
184 cur_item->GetSource()->GetRequestFile()
185 && std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), cur_item->GetSource()->GetRequestFile())) {
186 cur_item->SetContents(owner, source, AVAILABLE_SOURCE);
187 } else {
188 cur_item->SetContents(owner, source, type);
190 cur_item->dwUpdated = 0;
191 bFound = true;
192 } else if ( type == AVAILABLE_SOURCE ) {
193 // The state 'Available' is exclusive
194 cur_item->SetContents(cur_item->GetOwner(), source, A4AF_SOURCE);
195 cur_item->dwUpdated = 0;
199 if ( bFound ) {
200 return;
203 if ( std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), owner) ) {
204 RawAddSource(owner, source, type);
206 ShowSourcesCount( 1 );
210 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator& it)
212 ClientCtrlItem_Struct* item = it->second;
214 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
216 if ( index > -1 ) {
217 DeleteItem( index );
220 delete item;
222 // Remove it from the m_ListItems
223 m_ListItems.erase( it );
226 void CGenericClientListCtrl::RemoveSource( const CUpDownClient* source, const CKnownFile* owner )
228 // A NULL owner means remove it no matter what.
229 wxCHECK_RET(source, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
231 // Retrieve all entries matching the source
232 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
234 int removedItems = 0;
236 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; /* no ++, it happens later */) {
237 ListItems::iterator tmp = it++;
239 if ( owner == NULL || owner == tmp->second->GetOwner() ) {
241 RawRemoveSource(tmp);
243 ++removedItems;
247 ShowSourcesCount((-1) * removedItems);
250 void CGenericClientListCtrl::UpdateItem(const void* toupdate, SourceItemType type)
252 // Retrieve all entries matching the source
253 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
255 if ( rangeIt.first != rangeIt.second ) {
256 // Visible lines, default to all because not all platforms
257 // support the GetVisibleLines function
258 long first = 0, last = GetItemCount();
260 #ifndef __WXMSW__
261 // Get visible lines if we need them
262 GetVisibleLines( &first, &last );
263 #endif
265 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
266 ClientCtrlItem_Struct* item = it->second;
268 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
270 if ((type == A4AF_SOURCE) &&
271 item->GetSource()->GetRequestFile()
272 && std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), item->GetSource()->GetRequestFile())) {
274 item->SetType(AVAILABLE_SOURCE);
275 } else {
276 item->SetType(type);
279 item->dwUpdated = 0;
281 // Only update visible lines
282 if ( index >= first && index <= last) {
283 RefreshItem( index );
289 void CGenericClientListCtrl::ShowSources( const CKnownFileVector& files )
291 Freeze();
293 // The stored vector is sorted, as it the received one, so we can use binary_search
295 for (unsigned i = 0; i < m_knownfiles.size(); ++i) {
296 // Files that are not in the new list must have show set to false.
297 if (!std::binary_search(files.begin(), files.end(), m_knownfiles[i])) {
298 m_knownfiles[i]->SetShowSources( false );
302 // This will call again SetShowSources in files that were in both vectors. Right now
303 // that function is just a inline setter, so any way to prevent it would be wasteful,
304 // but this must be reviewed if that fact changes.
306 for (unsigned i = 0; i < files.size(); ++i) {
307 files[i]->SetShowSources( true );
310 // We must cleanup sources that are not in the received files.
312 int itemDiff = 0;
314 for (ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); /* no ++, it happens later */) {
315 ListItems::iterator tmp = it++;
316 ClientCtrlItem_Struct* item = tmp->second;
317 if (!std::binary_search(files.begin(), files.end(), item->GetOwner())) {
318 // Remove it from the m_ListItems
319 RawRemoveSource(tmp);
320 --itemDiff;
324 for (unsigned i = 0; i < files.size(); ++i) {
326 // Only those that weren't showing already
327 if (!std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), files[i])) {
329 CKnownFile* file = files[i];
331 wxASSERT_MSG(file, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
333 if (file) {
335 CKnownFile::SourceSet::const_iterator it;
337 if (IsShowingDownloadSources()) {
338 const CKnownFile::SourceSet& normSources = (dynamic_cast<CPartFile*>( file ))->GetSourceList();
339 const CKnownFile::SourceSet& a4afSources = (dynamic_cast<CPartFile*>( file ))->GetA4AFList();
341 // Adding normal sources
342 for ( it = normSources.begin(); it != normSources.end(); ++it ) {
343 switch ((*it)->GetDownloadState()) {
344 case DS_DOWNLOADING:
345 case DS_ONQUEUE:
346 RawAddSource( file, *it, AVAILABLE_SOURCE );
347 ++itemDiff;
348 break;
349 default:
350 // Any other state
351 RawAddSource( file, *it, UNAVAILABLE_SOURCE );
352 ++itemDiff;
356 // Adding A4AF sources
357 for ( it = a4afSources.begin(); it != a4afSources.end(); ++it ) {
358 // Only add if the A4AF file is not in the shown list.
359 if (!std::binary_search(files.begin(), files.end(), (*it)->GetRequestFile())) {
360 RawAddSource( file, *it, A4AF_SOURCE );
361 ++itemDiff;
364 } else {
365 // Known file
366 const CKnownFile::SourceSet& sources = file->m_ClientUploadList;
367 for ( it = sources.begin(); it != sources.end(); ++it ) {
368 switch ((*it)->GetUploadState()) {
369 case US_UPLOADING:
370 case US_ONUPLOADQUEUE:
371 RawAddSource( file, *it, AVAILABLE_SOURCE );
372 ++itemDiff;
373 break;
374 default:
375 // Any other state
376 RawAddSource( file, *it, UNAVAILABLE_SOURCE );
377 ++itemDiff;
385 m_knownfiles = files;
387 ShowSourcesCount( itemDiff );
389 SortList();
391 Thaw();
395 * Helper-function: This function is used to gather selected items.
397 * @param list A pointer to the list to gather items from.
398 * @return A list containing the selected items of the choosen types.
400 ItemList GetSelectedItems( CGenericClientListCtrl* list )
402 ItemList results;
404 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
406 while ( index > -1 ) {
407 ClientCtrlItem_Struct* item = (ClientCtrlItem_Struct*)list->GetItemData( index );
409 results.push_back( item );
411 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
414 return results;
417 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
420 for (unsigned i = 0; i < m_knownfiles.size(); ++i) {
421 wxCHECK_RET(m_knownfiles[i]->IsPartFile(), wxT("File is not a partfile when swapping sources"));
424 ItemList sources = ::GetSelectedItems( this );
426 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
427 (*it)->GetSource()->SwapToAnotherFile( true, false, false, dynamic_cast<CPartFile*>((*it)->GetOwner()));
432 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
434 ItemList sources = ::GetSelectedItems( this );
436 if ( sources.size() == 1 ) {
437 sources.front()->GetSource()->RequestSharedFileList();
442 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
444 ItemList sources = ::GetSelectedItems( this );
446 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
447 CUpDownClient* client = (*it)->GetSource();
448 if (client->IsFriend()) {
449 theApp->amuledlg->m_chatwnd->RemoveFriend(client->GetUserHash(), client->GetIP(), client->GetUserPort());
450 } else {
451 theApp->amuledlg->m_chatwnd->AddFriend( client );
457 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
459 ItemList sources = ::GetSelectedItems( this );
461 if ( sources.size() == 1 ) {
462 CUpDownClient* source = (sources.front())->GetSource();
464 // These values are cached, since calling wxGetTextFromUser will
465 // start an event-loop, in which the client may be deleted.
466 wxString userName = source->GetUserName();
467 uint64 userID = GUI_ID(source->GetIP(), source->GetUserPort());
469 wxString message = ::wxGetTextFromUser(_("Send message to user"),
470 _("Message to send:"));
471 if ( !message.IsEmpty() ) {
472 theApp->amuledlg->m_chatwnd->SendMessage(message, userName, userID);
478 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
480 ItemList sources = ::GetSelectedItems( this );
482 if ( sources.size() == 1 ) {
483 CClientDetailDialog( this, sources.front()->GetSource() ).ShowModal();
488 void CGenericClientListCtrl::OnItemActivated( wxListEvent& evt )
490 CClientDetailDialog( this, ((ClientCtrlItem_Struct*)GetItemData( evt.GetIndex()))->GetSource()).ShowModal();
494 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent& evt)
496 long index = CheckSelection(evt);
497 if (index < 0) {
498 return;
501 delete m_menu;
502 m_menu = NULL;
504 ClientCtrlItem_Struct* item = (ClientCtrlItem_Struct*)GetItemData( index );
505 CUpDownClient* client = item->GetSource();
507 m_menu = new wxMenu(wxT("Clients"));
508 m_menu->Append(MP_DETAIL, _("Show &Details"));
509 m_menu->Append(MP_ADDFRIEND, client->IsFriend() ? _("Remove from friends") : _("Add to Friends"));
510 m_menu->Append(MP_SHOWLIST, _("View Files"));
511 m_menu->Append(MP_SENDMESSAGE, _("Send message"));
513 m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
515 // Only enable the Swap option for A4AF sources
516 m_menu->Enable(MP_CHANGE2FILE, (item->GetType() == A4AF_SOURCE));
517 // We need a valid IP if we are to message the client
518 m_menu->Enable(MP_SENDMESSAGE, (client->GetIP() != 0));
520 m_menu->Enable(MP_SHOWLIST, !client->HasDisabledSharedFiles());
522 PopupMenu(m_menu, evt.GetPoint());
524 delete m_menu;
525 m_menu = NULL;
530 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent& evt)
532 // Check if clicked item is selected. If not, unselect all and select it.
533 long index = CheckSelection(evt);
534 if ( index < 0 ) {
535 return;
538 CClientDetailDialog(this, ((ClientCtrlItem_Struct*)GetItemData( index ))->GetSource()).ShowModal();
542 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent& event )
544 // No actions right now.
545 //switch (event.GetKeyCode()) {
546 // default:
547 event.Skip();
552 void CGenericClientListCtrl::OnDrawItem(
553 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
555 // Don't do any drawing if there's nobody to see it.
556 if ( !theApp->amuledlg->IsDialogVisible( GetParentDialog() ) ) {
557 return;
560 ClientCtrlItem_Struct* content = (ClientCtrlItem_Struct *)GetItemData(item);
562 // Define text-color and background
563 // and the border of the drawn area
564 if (highlighted) {
565 CMuleColour colour;
566 if (GetFocus()) {
567 dc->SetBackground(m_hilightBrush);
568 colour = m_hilightBrush.GetColour();
569 } else {
570 dc->SetBackground(m_hilightUnfocusBrush);
571 colour = m_hilightUnfocusBrush.GetColour();
573 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
574 dc->SetPen( colour.Blend(65).GetPen() );
575 } else {
576 dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
577 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
578 dc->SetPen(*wxTRANSPARENT_PEN);
580 dc->SetBrush( dc->GetBackground() );
582 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
584 dc->SetPen(*wxTRANSPARENT_PEN);
586 // Various constant values we use
587 const int iTextOffset = ( rect.GetHeight() - dc->GetCharHeight() ) / 2;
588 const int iOffset = 4;
590 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
592 for (int i = 0; i < GetColumnCount(); ++i) {
594 int columnwidth = GetColumnWidth(i);
596 if (columnwidth > 2*iOffset) {
597 // Make a copy of the current rectangle so we can apply specific tweaks
598 wxRect target_rec = cur_rec;
599 target_rec.width = columnwidth - 2*iOffset;
601 GenericColumnEnum cid = m_columndata.columns[i].cid;
603 if ( cid != ColumnUserProgress ) {
604 // Text column
605 // will ensure that text is about in the middle ;)
606 target_rec.y += iTextOffset;
609 // Draw the item
610 DrawClientItem(dc, cid, target_rec, content);
613 // Increment to the next column
614 cur_rec.x += columnwidth;
618 void CGenericClientListCtrl::DrawClientItem(
619 wxDC* dc, int nColumn, const wxRect& rect, ClientCtrlItem_Struct* item ) const
621 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
622 wxString buffer;
624 const CUpDownClient* client = item->GetSource();
626 switch (nColumn) {
627 // Client name + various icons
628 case ColumnUserName: {
629 wxRect cur_rec = rect;
630 // +3 is added by OnDrawItem()... so take it off
631 // Kry - eMule says +1, so I'm trusting it
632 wxPoint point( cur_rec.GetX(), cur_rec.GetY()+1 );
634 if (item->GetType() != A4AF_SOURCE) {
635 uint8 image = 0;
637 switch (client->GetDownloadState()) {
638 case DS_CONNECTING:
639 case DS_CONNECTED:
640 case DS_WAITCALLBACK:
641 case DS_TOOMANYCONNS:
642 image = Client_Red_Smiley;
643 break;
644 case DS_ONQUEUE:
645 if (client->IsRemoteQueueFull()) {
646 image = Client_Grey_Smiley;
647 } else {
648 image = Client_Yellow_Smiley;
650 break;
651 case DS_DOWNLOADING:
652 case DS_REQHASHSET:
653 image = Client_Green_Smiley;
654 break;
655 case DS_NONEEDEDPARTS:
656 case DS_LOWTOLOWIP:
657 image = Client_Grey_Smiley;
658 break;
659 default: // DS_NONE i.e.
660 image = Client_White_Smiley;
663 m_ImageList.Draw(image, *dc, point.x, point.y,
664 wxIMAGELIST_DRAW_TRANSPARENT);
665 } else {
666 m_ImageList.Draw(Client_Grey_Smiley, *dc, point.x, point.y,
667 wxIMAGELIST_DRAW_TRANSPARENT);
670 cur_rec.x += 20;
671 wxPoint point2( cur_rec.GetX(), cur_rec.GetY() + 1 );
673 uint8 clientImage;
675 if ( client->IsFriend() ) {
676 clientImage = Client_Friend_Smiley;
677 } else {
678 switch ( client->GetClientSoft() ) {
679 case SO_AMULE:
680 clientImage = Client_aMule_Smiley;
681 break;
682 case SO_MLDONKEY:
683 case SO_NEW_MLDONKEY:
684 case SO_NEW2_MLDONKEY:
685 clientImage = Client_mlDonkey_Smiley;
686 break;
687 case SO_EDONKEY:
688 case SO_EDONKEYHYBRID:
689 clientImage = Client_eDonkeyHybrid_Smiley;
690 break;
691 case SO_EMULE:
692 clientImage = Client_eMule_Smiley;
693 break;
694 case SO_LPHANT:
695 clientImage = Client_lphant_Smiley;
696 break;
697 case SO_SHAREAZA:
698 case SO_NEW_SHAREAZA:
699 case SO_NEW2_SHAREAZA:
700 clientImage = Client_Shareaza_Smiley;
701 break;
702 case SO_LXMULE:
703 clientImage = Client_xMule_Smiley;
704 break;
705 default:
706 // cDonkey, Compatible, Unknown
707 // No icon for those yet.
708 // Using the eMule one + '?'
709 clientImage = Client_Unknown;
710 break;
714 m_ImageList.Draw(clientImage, *dc, point2.x, point.y,
715 wxIMAGELIST_DRAW_TRANSPARENT);
717 if (client->GetScoreRatio() > 1) {
718 // Has credits, draw the gold star
719 m_ImageList.Draw(Client_CreditsYellow_Smiley, *dc, point2.x, point.y,
720 wxIMAGELIST_DRAW_TRANSPARENT );
721 } else if ( !client->ExtProtocolAvailable() ) {
722 // No Ext protocol -> Draw the '-'
723 m_ImageList.Draw(Client_ExtendedProtocol_Smiley, *dc, point2.x, point.y,
724 wxIMAGELIST_DRAW_TRANSPARENT);
727 if (client->IsIdentified()) {
728 // the 'v'
729 m_ImageList.Draw(Client_SecIdent_Smiley, *dc, point2.x, point.y,
730 wxIMAGELIST_DRAW_TRANSPARENT);
731 } else if (client->IsBadGuy()) {
732 // the 'X'
733 m_ImageList.Draw(Client_BadGuy_Smiley, *dc, point2.x, point.y,
734 wxIMAGELIST_DRAW_TRANSPARENT);
737 if (client->GetObfuscationStatus() == OBST_ENABLED) {
738 // the "¿" except it's a key
739 m_ImageList.Draw(Client_Encryption_Smiley, *dc, point2.x, point.y,
740 wxIMAGELIST_DRAW_TRANSPARENT);
743 wxString userName;
744 #ifdef ENABLE_IP2COUNTRY
745 // Draw the flag
746 const CountryData& countrydata = theApp->amuledlg->m_IP2Country->GetCountryData(client->GetFullIP());
747 dc->DrawBitmap(countrydata.Flag,
748 rect.x + 40, rect.y + 5,
749 wxIMAGELIST_DRAW_TRANSPARENT != 0);
751 userName << countrydata.Name;
753 userName << wxT(" - ");
754 #endif // ENABLE_IP2COUNTRY
755 if (client->GetUserName().IsEmpty()) {
756 userName << wxT("?");
757 } else {
758 userName << client->GetUserName();
760 dc->DrawText(userName, rect.GetX() + 60, rect.GetY());
762 break;
764 case ColumnUserDownloaded:
765 if (item->GetType() != A4AF_SOURCE && client->GetTransferredDown()) {
766 buffer = CastItoXBytes(client->GetTransferredDown());
767 dc->DrawText(buffer, rect.GetX(), rect.GetY());
769 break;
770 case ColumnUserUploaded:
771 if (item->GetType() != A4AF_SOURCE && client->GetTransferredUp()) {
772 buffer = CastItoXBytes(client->GetTransferredUp());
773 dc->DrawText(buffer, rect.GetX(), rect.GetY());
775 break;
776 case ColumnUserSpeedDown:
777 if (item->GetType() != A4AF_SOURCE && client->GetKBpsDown() > 0.001) {
778 buffer = wxString::Format(wxT("%.1f "),
779 client->GetKBpsDown()) + _("kB/s");
780 dc->DrawText(buffer, rect.GetX(), rect.GetY());
782 break;
783 case ColumnUserSpeedUp:
784 // Datarate is in bytes.
785 if (item->GetType() != A4AF_SOURCE && client->GetUploadDatarate() > 1024) {
786 buffer = wxString::Format(wxT("%.1f "),
787 client->GetUploadDatarate() / 1024.0f) + _("kB/s");
788 dc->DrawText(buffer, rect.GetX(), rect.GetY());
790 break;
791 case ColumnUserProgress:
792 if ( thePrefs::ShowProgBar() ) {
793 int iWidth = rect.GetWidth() - 2;
794 int iHeight = rect.GetHeight() - 2;
796 // don't draw Text beyond the bar
797 dc->SetClippingRegion(rect.GetX(), rect.GetY() + 1, iWidth, iHeight);
799 if ( item->GetType() != A4AF_SOURCE ) {
800 uint32 dwTicks = GetTickCount();
802 wxMemoryDC cdcStatus;
804 if ( item->dwUpdated < dwTicks || !item->status ||
805 iWidth != item->status->GetWidth() ) {
807 if (item->status == NULL) {
808 item->status = new wxBitmap(iWidth, iHeight);
809 } else if ( item->status->GetWidth() != iWidth ) {
810 // Only recreate if size has changed
811 item->status->Create(iWidth, iHeight);
814 cdcStatus.SelectObject(*(item->status));
816 if ( thePrefs::UseFlatBar() ) {
817 DrawSourceStatusBar( client, &cdcStatus,
818 wxRect(0, 0, iWidth, iHeight), true);
819 } else {
820 DrawSourceStatusBar( client, &cdcStatus,
821 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
823 // Draw black border
824 cdcStatus.SetPen( *wxBLACK_PEN );
825 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
826 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
829 // Plus ten seconds
830 item->dwUpdated = dwTicks + 10000;
831 } else {
832 cdcStatus.SelectObject(*(item->status));
835 dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
836 } else {
837 wxString a4af;
838 CPartFile* p = client->GetRequestFile();
839 if (p) {
840 a4af = p->GetFileName().GetPrintable();
841 } else {
842 a4af = wxT("?");
844 buffer = CFormat(wxT("%s: %s")) % _("A4AF") % a4af;
846 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
847 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
849 wxCoord txtwidth, txtheight;
851 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
853 dc->SetTextForeground(*wxBLACK);
854 dc->DrawText(buffer, wxMax(rect.GetX() + 2, midx - (txtwidth >> 1)), midy - (txtheight >> 1));
856 // Draw black border
857 dc->SetPen( *wxBLACK_PEN );
858 dc->SetBrush( *wxTRANSPARENT_BRUSH );
859 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
862 break;
864 case ColumnUserVersion: {
865 dc->DrawText(client->GetClientVerString(), rect.GetX(), rect.GetY());
866 break;
869 case ColumnUserQueueRankRemote: {
870 // We only show the queue rank for sources actually queued for that file
871 if ( item->GetType() != A4AF_SOURCE && client->GetDownloadState() == DS_ONQUEUE ) {
872 sint16 qrDiff = 0;
873 wxColour savedColour = dc->GetTextForeground();
874 if (client->IsRemoteQueueFull()) {
875 buffer = _("Queue Full");
876 } else {
877 if (client->GetRemoteQueueRank()) {
878 qrDiff = client->GetRemoteQueueRank() -
879 client->GetOldRemoteQueueRank();
880 if (qrDiff == client->GetRemoteQueueRank() ) {
881 qrDiff = 0;
883 if ( qrDiff < 0 ) {
884 dc->SetTextForeground(*wxBLUE);
886 if ( qrDiff > 0 ) {
887 dc->SetTextForeground(*wxRED);
889 buffer = wxString::Format(_("QR: %u (%i)"), client->GetRemoteQueueRank(), qrDiff);
890 } else {
891 buffer = _("QR: ???");
894 dc->DrawText(buffer, rect.GetX(), rect.GetY());
895 if (qrDiff) {
896 dc->SetTextForeground(savedColour);
899 break;
901 case ColumnUserQueueRankLocal:
902 if (item->GetType() != A4AF_SOURCE) {
903 if (client->GetUploadState() == US_ONUPLOADQUEUE ) {
904 uint16 nRank = client->GetUploadQueueWaitingPosition();
905 if (nRank == 0) {
906 buffer = _("Waiting for upload slot");
907 } else {
908 buffer = wxString::Format(_("QR: %u"), nRank);
910 } else if (client->GetUploadState() == US_UPLOADING) {
911 buffer = _("Uploading");
912 } else {
913 buffer = _("None");
915 dc->DrawText(buffer, rect.GetX(), rect.GetY());
917 break;
918 case ColumnUserStatus:
919 if (item->GetType() != A4AF_SOURCE) {
920 buffer = DownloadStateToStr( client->GetDownloadState(),
921 client->IsRemoteQueueFull() );
922 } else {
923 buffer = _("Asked for another file");
924 if ( client->GetRequestFile() &&
925 client->GetRequestFile()->GetFileName().IsOk()) {
926 buffer += CFormat(wxT(" (%s)"))
927 % client->GetRequestFile()->GetFileName();
930 dc->DrawText(buffer, rect.GetX(), rect.GetY());
931 break;
932 // Source comes from?
933 case ColumnUserOrigin: {
934 buffer = wxGetTranslation(OriginToText(client->GetSourceFrom()));
935 dc->DrawText(buffer, rect.GetX(), rect.GetY());
936 break;
938 // Local file name to identify on multi select
939 case ColumnUserFileNameDownload: {
940 const CPartFile * pf = client->GetRequestFile();
941 if (pf) {
942 buffer = pf->GetFileName().GetPrintable();
943 } else {
944 buffer = wxT("???");
946 dc->DrawText(buffer, rect.GetX(), rect.GetY());
947 break;
949 case ColumnUserFileNameUpload: {
950 const CKnownFile * kf = client->GetUploadFile();
951 if (kf) {
952 buffer = kf->GetFileName().GetPrintable();
953 } else {
954 buffer = wxT("???");
956 dc->DrawText(buffer, rect.GetX(), rect.GetY());
957 break;
962 int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
964 ClientCtrlItem_Struct* item1 = (ClientCtrlItem_Struct*)param1;
965 ClientCtrlItem_Struct* item2 = (ClientCtrlItem_Struct*)param2;
967 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
968 sortData &= CMuleListCtrl::COLUMN_MASK;
969 int comp = 0;
971 // Two sources, some different possibilites
972 // Avilable sources first, if we have both an
973 // available and an unavailable
974 comp = ( item2->GetType() - item1->GetType() );
976 if (comp) {
977 // unavailable and available. The order is fixed regardless of sort-order.
978 return comp;
979 } else {
980 comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
983 // We modify the result so that it matches with ascending or decending
984 return sortMod * comp;
987 int CGenericClientListCtrl::Compare(
988 const CUpDownClient* client1, const CUpDownClient* client2, long lParamSort)
990 switch (lParamSort) {
991 // Sort by name
992 case ColumnUserName:
993 return CmpAny( client1->GetUserName(), client2->GetUserName() );
995 // Sort by transferred in the following fields
996 case ColumnUserDownloaded:
997 return CmpAny( client1->GetTransferredDown(), client2->GetTransferredDown() );
999 // Sort by transferred in the following fields
1000 case ColumnUserUploaded:
1001 return CmpAny( client1->GetTransferredUp(), client2->GetTransferredUp() );
1003 // Sort by speed
1004 case ColumnUserSpeedDown:
1005 return CmpAny( client1->GetKBpsDown(), client2->GetKBpsDown() );
1007 // Sort by speed
1008 case ColumnUserSpeedUp:
1009 return CmpAny( client1->GetUploadDatarate(), client2->GetUploadDatarate() );
1011 // Sort by parts offered
1012 case ColumnUserProgress:
1013 return CmpAny(
1014 client1->GetAvailablePartCount(),
1015 client2->GetAvailablePartCount() );
1017 // Sort by client version
1018 case ColumnUserVersion: {
1019 if (client1->GetClientSoft() != client2->GetClientSoft()) {
1020 return client1->GetSoftStr().Cmp(client2->GetSoftStr());
1023 if (client1->GetVersion() != client2->GetVersion()) {
1024 return CmpAny(client1->GetVersion(), client2->GetVersion());
1027 return client1->GetClientModString().Cmp(client2->GetClientModString());
1030 // Sort by Queue-Rank
1031 case ColumnUserQueueRankRemote: {
1032 // This will sort by download state: Downloading, OnQueue, Connecting ...
1033 // However, Asked For Another will always be placed last, due to
1034 // sorting in SortProc
1035 if ( client1->GetDownloadState() != client2->GetDownloadState() ) {
1036 return client1->GetDownloadState() - client2->GetDownloadState();
1039 // Placing items on queue before items on full queues
1040 if ( client1->IsRemoteQueueFull() ) {
1041 if ( client2->IsRemoteQueueFull() ) {
1042 return 0;
1043 } else {
1044 return 1;
1046 } else if ( client2->IsRemoteQueueFull() ) {
1047 return -1;
1048 } else {
1049 if ( client1->GetRemoteQueueRank() ) {
1050 if ( client2->GetRemoteQueueRank() ) {
1051 return CmpAny(
1052 client1->GetRemoteQueueRank(),
1053 client2->GetRemoteQueueRank() );
1054 } else {
1055 return -1;
1057 } else {
1058 if ( client2->GetRemoteQueueRank() ) {
1059 return 1;
1060 } else {
1061 return 0;
1067 // Sort by Queue-Rank
1068 case ColumnUserQueueRankLocal: {
1069 // This will sort by download state: Downloading, OnQueue, Connecting ...
1070 // However, Asked For Another will always be placed last, due to
1071 // sorting in SortProc
1072 if ( client1->GetUploadState() != client2->GetUploadState() ) {
1073 return client1->GetUploadState() - client2->GetUploadState();
1076 uint16 rank1 = client1->GetUploadQueueWaitingPosition();
1077 uint16 rank2 = client2->GetUploadQueueWaitingPosition();
1078 // Placing items on queue before items on full queues
1079 if ( !rank1 ) {
1080 if ( !rank2 ) {
1081 return 0;
1082 } else {
1083 return 1;
1085 } else if ( !rank2 ) {
1086 return -1;
1087 } else {
1088 if ( rank1 ) {
1089 if ( rank2 ) {
1090 return CmpAny(
1091 rank1,
1092 rank2 );
1093 } else {
1094 return -1;
1096 } else {
1097 if ( rank2 ) {
1098 return 1;
1099 } else {
1100 return 0;
1106 // Sort by state
1107 case ColumnUserStatus: {
1108 if (client1->GetDownloadState() == client2->GetDownloadState()) {
1109 return CmpAny(
1110 client1->IsRemoteQueueFull(),
1111 client2->IsRemoteQueueFull() );
1112 } else {
1113 return CmpAny(
1114 client1->GetDownloadState(),
1115 client2->GetDownloadState() );
1119 // Source of source ;)
1120 case ColumnUserOrigin:
1121 return CmpAny(client1->GetSourceFrom(), client2->GetSourceFrom());
1123 // Sort by local filename (download)
1124 case ColumnUserFileNameDownload: {
1125 wxString buffer1, buffer2;
1126 const CPartFile * pf1 = client1->GetRequestFile();
1127 if (pf1) {
1128 buffer1 = pf1->GetFileName().GetPrintable();
1130 const CPartFile * pf2 = client2->GetRequestFile();
1131 if (pf2) {
1132 buffer2 = pf2->GetFileName().GetPrintable();
1134 return CmpAny(buffer1, buffer2);
1137 // Sort by local filename (upload)
1138 case ColumnUserFileNameUpload: {
1139 wxString buffer1, buffer2;
1140 const CKnownFile * kf1 = client1->GetUploadFile();
1141 if (kf1) {
1142 buffer1 = kf1->GetFileName().GetPrintable();
1144 const CKnownFile * kf2 = client2->GetUploadFile();
1145 if (kf2) {
1146 buffer2 = kf2->GetFileName().GetPrintable();
1148 return CmpAny(buffer1, buffer2);
1151 default:
1152 return 0;
1157 void CGenericClientListCtrl::ShowSourcesCount( int diff )
1159 m_clientcount += diff;
1160 wxStaticText* label = CastByID( ID_CLIENTCOUNT, GetParent(), wxStaticText );
1162 if (label) {
1163 wxString str = wxString::Format(wxT("%i"), m_clientcount );
1165 label->SetLabel( str );
1166 label->GetParent()->Layout();
1170 static const CMuleColour crBoth(0, 192, 0);
1171 static const CMuleColour crFlatBoth(0, 150, 0);
1173 static const CMuleColour crNeither(240, 240, 240);
1174 static const CMuleColour crFlatNeither(224, 224, 224);
1176 static const CMuleColour crClientOnly(104, 104, 104);
1177 static const CMuleColour crFlatClientOnly(0, 0, 0);
1179 static const CMuleColour crPending(255, 208, 0);
1180 static const CMuleColour crNextPending(255, 255, 100);
1182 void CGenericClientListCtrl::DrawSourceStatusBar(
1183 const CUpDownClient* source, wxDC* dc, const wxRect& rect, bool bFlat) const
1185 static CBarShader s_StatusBar(16);
1187 CPartFile* reqfile = source->GetRequestFile();
1189 s_StatusBar.SetHeight(rect.height);
1190 s_StatusBar.SetWidth(rect.width);
1191 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1192 const BitVector& partStatus = source->GetPartStatus();
1194 if (reqfile && reqfile->GetPartCount() == partStatus.size()) {
1195 s_StatusBar.SetFileSize(reqfile->GetFileSize());
1196 uint16 lastDownloadingPart = source->GetDownloadState() == DS_DOWNLOADING
1197 ? source->GetLastDownloadingPart() : 0xffff;
1198 uint16 nextRequestedPart = source->GetNextRequestedPart();
1200 for ( uint16 i = 0; i < partStatus.size(); i++ ) {
1201 uint64 uStart = PARTSIZE * i;
1202 uint64 uEnd = uStart + reqfile->GetPartSize(i) - 1;
1204 CMuleColour colour;
1205 if (!partStatus.get(i)) {
1206 // client does not have this part
1207 // light grey
1208 colour = bFlat ? crFlatNeither : crNeither;
1209 } else if ( reqfile->IsComplete(i)) {
1210 // completed part
1211 // green
1212 colour = bFlat ? crFlatBoth : crBoth;
1213 } else if (lastDownloadingPart == i) {
1214 // downloading part
1215 // yellow
1216 colour = crPending;
1217 } else if (nextRequestedPart == i) {
1218 // requested part
1219 // light yellow
1220 colour = crNextPending;
1221 } else {
1222 // client has this part, we need it
1223 // black
1224 colour = bFlat ? crFlatClientOnly : crClientOnly;
1227 if ( source->GetRequestFile()->IsStopped() ) {
1228 colour.Blend(50);
1231 s_StatusBar.FillRange(uStart, uEnd, colour);
1233 } else {
1234 s_StatusBar.SetFileSize(1);
1235 s_StatusBar.FillRange(0, 1, bFlat ? crFlatNeither : crNeither);
1238 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
1241 // File_checked_for_headers