2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
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
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.
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
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()
57 m_type(UNAVAILABLE_SOURCE
)
60 ~ClientCtrlItem_Struct() {
64 SourceItemType
GetType() const {
68 CKnownFile
* GetOwner() const {
73 CUpDownClient
* GetSource() const {
77 void SetContents(CKnownFile
* owner
, CUpDownClient
* source
, SourceItemType type
) {
79 m_sourceValue
= source
;
83 void SetType(SourceItemType type
) { m_type
= type
; }
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
)
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
);
130 m_hilightBrush
= CMuleColour(wxSYS_COLOUR_HIGHLIGHT
).Blend(125).GetBrush();
132 m_hilightUnfocusBrush
= CMuleColour(wxSYS_COLOUR_BTNSHADOW
).Blend(125).GetBrush();
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
);
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
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
);
188 cur_item
->SetContents(owner
, source
, type
);
190 cur_item
->dwUpdated
= 0;
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;
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
) );
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
);
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();
261 // Get visible lines if we need them
262 GetVisibleLines( &first
, &last
);
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
);
281 // Only update visible lines
282 if ( index
>= first
&& index
<= last
) {
283 RefreshItem( index
);
289 void CGenericClientListCtrl::ShowSources( const CKnownFileVector
& files
)
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.
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
);
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"));
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()) {
346 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
351 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
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
);
366 const CKnownFile::SourceSet
& sources
= file
->m_ClientUploadList
;
367 for ( it
= sources
.begin(); it
!= sources
.end(); ++it
) {
368 switch ((*it
)->GetUploadState()) {
370 case US_ONUPLOADQUEUE
:
371 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
376 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
385 m_knownfiles
= files
;
387 ShowSourcesCount( itemDiff
);
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
)
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
);
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());
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
);
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());
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
);
538 CClientDetailDialog(this, ((ClientCtrlItem_Struct
*)GetItemData( index
))->GetSource()).ShowModal();
542 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent
& event
)
544 // No actions right now.
545 //switch (event.GetKeyCode()) {
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() ) ) {
560 ClientCtrlItem_Struct
* content
= (ClientCtrlItem_Struct
*)GetItemData(item
);
562 // Define text-color and background
563 // and the border of the drawn area
567 dc
->SetBackground(m_hilightBrush
);
568 colour
= m_hilightBrush
.GetColour();
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() );
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
) {
605 // will ensure that text is about in the middle ;)
606 target_rec
.y
+= iTextOffset
;
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() );
624 const CUpDownClient
* client
= item
->GetSource();
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
) {
637 switch (client
->GetDownloadState()) {
640 case DS_WAITCALLBACK
:
641 case DS_TOOMANYCONNS
:
642 image
= Client_Red_Smiley
;
645 if (client
->IsRemoteQueueFull()) {
646 image
= Client_Grey_Smiley
;
648 image
= Client_Yellow_Smiley
;
653 image
= Client_Green_Smiley
;
655 case DS_NONEEDEDPARTS
:
657 image
= Client_Grey_Smiley
;
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
);
666 m_ImageList
.Draw(Client_Grey_Smiley
, *dc
, point
.x
, point
.y
,
667 wxIMAGELIST_DRAW_TRANSPARENT
);
671 wxPoint
point2( cur_rec
.GetX(), cur_rec
.GetY() + 1 );
675 if ( client
->IsFriend() ) {
676 clientImage
= Client_Friend_Smiley
;
678 switch ( client
->GetClientSoft() ) {
680 clientImage
= Client_aMule_Smiley
;
683 case SO_NEW_MLDONKEY
:
684 case SO_NEW2_MLDONKEY
:
685 clientImage
= Client_mlDonkey_Smiley
;
688 case SO_EDONKEYHYBRID
:
689 clientImage
= Client_eDonkeyHybrid_Smiley
;
692 clientImage
= Client_eMule_Smiley
;
695 clientImage
= Client_lphant_Smiley
;
698 case SO_NEW_SHAREAZA
:
699 case SO_NEW2_SHAREAZA
:
700 clientImage
= Client_Shareaza_Smiley
;
703 clientImage
= Client_xMule_Smiley
;
706 // cDonkey, Compatible, Unknown
707 // No icon for those yet.
708 // Using the eMule one + '?'
709 clientImage
= Client_Unknown
;
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()) {
729 m_ImageList
.Draw(Client_SecIdent_Smiley
, *dc
, point2
.x
, point
.y
,
730 wxIMAGELIST_DRAW_TRANSPARENT
);
731 } else if (client
->IsBadGuy()) {
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
);
744 #ifdef ENABLE_IP2COUNTRY
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("?");
758 userName
<< client
->GetUserName();
760 dc
->DrawText(userName
, rect
.GetX() + 60, rect
.GetY());
764 case ColumnUserDownloaded
:
765 if (item
->GetType() != A4AF_SOURCE
&& client
->GetTransferredDown()) {
766 buffer
= CastItoXBytes(client
->GetTransferredDown());
767 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
770 case ColumnUserUploaded
:
771 if (item
->GetType() != A4AF_SOURCE
&& client
->GetTransferredUp()) {
772 buffer
= CastItoXBytes(client
->GetTransferredUp());
773 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
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());
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());
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);
820 DrawSourceStatusBar( client
, &cdcStatus
,
821 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
824 cdcStatus
.SetPen( *wxBLACK_PEN
);
825 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
826 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
830 item
->dwUpdated
= dwTicks
+ 10000;
832 cdcStatus
.SelectObject(*(item
->status
));
835 dc
->Blit(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
838 CPartFile
* p
= client
->GetRequestFile();
840 a4af
= p
->GetFileName().GetPrintable();
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));
857 dc
->SetPen( *wxBLACK_PEN
);
858 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
859 dc
->DrawRectangle( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
864 case ColumnUserVersion
: {
865 dc
->DrawText(client
->GetClientVerString(), rect
.GetX(), rect
.GetY());
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
) {
873 wxColour savedColour
= dc
->GetTextForeground();
874 if (client
->IsRemoteQueueFull()) {
875 buffer
= _("Queue Full");
877 if (client
->GetRemoteQueueRank()) {
878 qrDiff
= client
->GetRemoteQueueRank() -
879 client
->GetOldRemoteQueueRank();
880 if (qrDiff
== client
->GetRemoteQueueRank() ) {
884 dc
->SetTextForeground(*wxBLUE
);
887 dc
->SetTextForeground(*wxRED
);
889 buffer
= wxString::Format(_("QR: %u (%i)"), client
->GetRemoteQueueRank(), qrDiff
);
891 buffer
= _("QR: ???");
894 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
896 dc
->SetTextForeground(savedColour
);
901 case ColumnUserQueueRankLocal
:
902 if (item
->GetType() != A4AF_SOURCE
) {
903 if (client
->GetUploadState() == US_ONUPLOADQUEUE
) {
904 uint16 nRank
= client
->GetUploadQueueWaitingPosition();
906 buffer
= _("Waiting for upload slot");
908 buffer
= wxString::Format(_("QR: %u"), nRank
);
910 } else if (client
->GetUploadState() == US_UPLOADING
) {
911 buffer
= _("Uploading");
915 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
918 case ColumnUserStatus
:
919 if (item
->GetType() != A4AF_SOURCE
) {
920 buffer
= DownloadStateToStr( client
->GetDownloadState(),
921 client
->IsRemoteQueueFull() );
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());
932 // Source comes from?
933 case ColumnUserOrigin
: {
934 buffer
= wxGetTranslation(OriginToText(client
->GetSourceFrom()));
935 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
938 // Local file name to identify on multi select
939 case ColumnUserFileNameDownload
: {
940 const CPartFile
* pf
= client
->GetRequestFile();
942 buffer
= pf
->GetFileName().GetPrintable();
946 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
949 case ColumnUserFileNameUpload
: {
950 const CKnownFile
* kf
= client
->GetUploadFile();
952 buffer
= kf
->GetFileName().GetPrintable();
956 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY());
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
;
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() );
977 // unavailable and available. The order is fixed regardless of sort-order.
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
) {
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() );
1004 case ColumnUserSpeedDown
:
1005 return CmpAny( client1
->GetKBpsDown(), client2
->GetKBpsDown() );
1008 case ColumnUserSpeedUp
:
1009 return CmpAny( client1
->GetUploadDatarate(), client2
->GetUploadDatarate() );
1011 // Sort by parts offered
1012 case ColumnUserProgress
:
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() ) {
1046 } else if ( client2
->IsRemoteQueueFull() ) {
1049 if ( client1
->GetRemoteQueueRank() ) {
1050 if ( client2
->GetRemoteQueueRank() ) {
1052 client1
->GetRemoteQueueRank(),
1053 client2
->GetRemoteQueueRank() );
1058 if ( client2
->GetRemoteQueueRank() ) {
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
1085 } else if ( !rank2
) {
1107 case ColumnUserStatus
: {
1108 if (client1
->GetDownloadState() == client2
->GetDownloadState()) {
1110 client1
->IsRemoteQueueFull(),
1111 client2
->IsRemoteQueueFull() );
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();
1128 buffer1
= pf1
->GetFileName().GetPrintable();
1130 const CPartFile
* pf2
= client2
->GetRequestFile();
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();
1142 buffer1
= kf1
->GetFileName().GetPrintable();
1144 const CKnownFile
* kf2
= client2
->GetUploadFile();
1146 buffer2
= kf2
->GetFileName().GetPrintable();
1148 return CmpAny(buffer1
, buffer2
);
1157 void CGenericClientListCtrl::ShowSourcesCount( int diff
)
1159 m_clientcount
+= diff
;
1160 wxStaticText
* label
= CastByID( ID_CLIENTCOUNT
, GetParent(), wxStaticText
);
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;
1205 if (!partStatus
.get(i
)) {
1206 // client does not have this part
1208 colour
= bFlat
? crFlatNeither
: crNeither
;
1209 } else if ( reqfile
->IsComplete(i
)) {
1212 colour
= bFlat
? crFlatBoth
: crBoth
;
1213 } else if (lastDownloadingPart
== i
) {
1217 } else if (nextRequestedPart
== i
) {
1220 colour
= crNextPending
;
1222 // client has this part, we need it
1224 colour
= bFlat
? crFlatClientOnly
: crClientOnly
;
1227 if ( source
->GetRequestFile()->IsStopped() ) {
1231 s_StatusBar
.FillRange(uStart
, uEnd
, colour
);
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