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 "BitVector.h"
34 #include "ClientDetailDialog.h" // Needed for CClientDetailDialog
35 #include "ChatWnd.h" // Needed for CChatWnd
36 #include "CommentDialogLst.h" // Needed for CCommentDialogLst
37 #include "DataToText.h" // Needed for PriorityToStr
38 #include "FileDetailDialog.h" // Needed for CFileDetailDialog
39 #include "GetTickCount.h" // Needed for GetTickCount
40 #include "GuiEvents.h" // Needed for CoreNotify_*
41 #ifdef ENABLE_IP2COUNTRY
42 #include "IP2Country.h" // Needed for IP2Country
45 #include "muuli_wdr.h" // Needed for ID_DLOADLIST
46 #include "PartFile.h" // Needed for CPartFile
47 #include "Preferences.h"
48 #include "SharedFileList.h" // Needed for CSharedFileList
49 #include "TerminationProcess.h" // Needed for CTerminationProcess
50 #include "ClientRef.h" // Needed for CClientRef
51 #include "FriendList.h"
53 struct ClientCtrlItem_Struct
55 ClientCtrlItem_Struct()
59 m_type(UNAVAILABLE_SOURCE
)
62 ~ClientCtrlItem_Struct() {
66 SourceItemType
GetType() const {
70 CKnownFile
* GetOwner() const {
75 CClientRef
& GetSource() {
79 void SetContents(CKnownFile
* owner
, const CClientRef
& source
, SourceItemType type
) {
81 m_sourceValue
= source
;
85 void SetType(SourceItemType type
) { m_type
= type
; }
92 CClientRef m_sourceValue
;
93 SourceItemType m_type
;
96 #define m_ImageList theApp->amuledlg->m_imagelist
98 BEGIN_EVENT_TABLE(CGenericClientListCtrl
, CMuleListCtrl
)
99 EVT_LIST_ITEM_ACTIVATED(wxID_ANY
, CGenericClientListCtrl::OnItemActivated
)
100 EVT_LIST_ITEM_RIGHT_CLICK(wxID_ANY
, CGenericClientListCtrl::OnMouseRightClick
)
101 EVT_LIST_ITEM_MIDDLE_CLICK(wxID_ANY
, CGenericClientListCtrl::OnMouseMiddleClick
)
103 EVT_CHAR( CGenericClientListCtrl::OnKeyPressed
)
105 EVT_MENU( MP_CHANGE2FILE
, CGenericClientListCtrl::OnSwapSource
)
106 EVT_MENU( MP_SHOWLIST
, CGenericClientListCtrl::OnViewFiles
)
107 EVT_MENU( MP_ADDFRIEND
, CGenericClientListCtrl::OnAddFriend
)
108 EVT_MENU( MP_FRIENDSLOT
, CGenericClientListCtrl::OnSetFriendslot
)
109 EVT_MENU( MP_SENDMESSAGE
, CGenericClientListCtrl::OnSendMessage
)
110 EVT_MENU( MP_DETAIL
, CGenericClientListCtrl::OnViewClientInfo
)
113 //! This listtype is used when gathering the selected items.
114 typedef std::list
<ClientCtrlItem_Struct
*> ItemList
;
116 CGenericClientListCtrl::CGenericClientListCtrl(
117 const wxString
& tablename
,
118 wxWindow
*parent
, wxWindowID winid
, const wxPoint
& pos
, const wxSize
& size
,
119 long style
, const wxValidator
& validator
, const wxString
& name
)
121 CMuleListCtrl( parent
, winid
, pos
, size
, style
| wxLC_OWNERDRAW
| wxLC_VRULES
| wxLC_HRULES
, validator
, name
),
122 m_columndata(0, NULL
)
124 // Setting the sorter function must be done in the derived class, to use the translation function correctly.
125 // SetSortFunc( SortProc );
127 // Set the table-name (for loading and saving preferences).
128 SetTableName( tablename
);
133 m_hilightBrush
= CMuleColour(wxSYS_COLOUR_HIGHLIGHT
).Blend(125).GetBrush();
135 m_hilightUnfocusBrush
= CMuleColour(wxSYS_COLOUR_BTNSHADOW
).Blend(125).GetBrush();
140 wxString
CGenericClientListCtrl::TranslateCIDToName(GenericColumnEnum cid
)
142 wxString name
= wxEmptyString
;
148 case ColumnUserDownloaded
:
151 case ColumnUserUploaded
:
154 case ColumnUserSpeedDown
:
157 case ColumnUserSpeedUp
:
160 case ColumnUserProgress
:
163 case ColumnUserAvailable
:
166 case ColumnUserVersion
:
169 case ColumnUserQueueRankLocal
:
172 case ColumnUserQueueRankRemote
:
175 case ColumnUserOrigin
:
178 case ColumnUserFileNameDownload
:
181 case ColumnUserFileNameUpload
:
184 case ColumnUserFileNameDownloadRemote
:
196 void CGenericClientListCtrl::InitColumnData()
198 if (!m_columndata
.n_columns
) {
199 throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
202 for (int i
= 0; i
< m_columndata
.n_columns
; ++i
) {
203 InsertColumn( i
, wxGetTranslation(m_columndata
.columns
[i
].title
), wxLIST_FORMAT_LEFT
, m_columndata
.columns
[i
].width
, TranslateCIDToName(m_columndata
.columns
[i
].cid
));
209 CGenericClientListCtrl::~CGenericClientListCtrl()
211 while ( !m_ListItems
.empty() ) {
212 delete m_ListItems
.begin()->second
;
213 m_ListItems
.erase( m_ListItems
.begin() );
216 void CGenericClientListCtrl::RawAddSource(CKnownFile
* owner
, CClientRef source
, SourceItemType type
)
218 ClientCtrlItem_Struct
* newitem
= new ClientCtrlItem_Struct
;
219 newitem
->SetContents(owner
, source
, type
);
221 m_ListItems
.insert( ListItemsPair(source
.ECID(), newitem
) );
223 long item
= InsertItem( GetItemCount(), wxEmptyString
);
224 SetItemPtrData( item
, reinterpret_cast<wxUIntPtr
>(newitem
) );
225 SetItemBackgroundColour( item
, GetBackgroundColour() );
228 void CGenericClientListCtrl::AddSource(CKnownFile
* owner
, const CClientRef
& source
, SourceItemType type
)
230 wxCHECK_RET(owner
, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
231 wxCHECK_RET(source
.IsLinked(), wxT("Unlinked source in CGenericClientListCtrl::AddSource"));
233 // Update the other instances of this source
235 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
.ECID());
236 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
237 ClientCtrlItem_Struct
* cur_item
= it
->second
;
239 // Check if this source has been already added to this file => to be sure
240 if ( cur_item
->GetOwner() == owner
) {
241 // Update this instance with its new setting
242 if ((type
== A4AF_SOURCE
) &&
243 cur_item
->GetSource().GetRequestFile()
244 && std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), cur_item
->GetSource().GetRequestFile())) {
245 cur_item
->SetContents(owner
, source
, AVAILABLE_SOURCE
);
247 cur_item
->SetContents(owner
, source
, type
);
249 cur_item
->dwUpdated
= 0;
251 } else if ( type
== AVAILABLE_SOURCE
) {
252 // The state 'Available' is exclusive
253 cur_item
->SetContents(cur_item
->GetOwner(), source
, A4AF_SOURCE
);
254 cur_item
->dwUpdated
= 0;
262 if ( std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), owner
) ) {
263 RawAddSource(owner
, source
, type
);
265 ShowSourcesCount( 1 );
269 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator
& it
)
271 ClientCtrlItem_Struct
* item
= it
->second
;
273 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
281 // Remove it from the m_ListItems
282 m_ListItems
.erase( it
);
285 void CGenericClientListCtrl::RemoveSource(uint32 source
, const CKnownFile
* owner
)
287 // A NULL owner means remove it no matter what.
288 wxCHECK_RET(source
, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
290 // Retrieve all entries matching the source
291 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
);
293 int removedItems
= 0;
295 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; /* no ++, it happens later */) {
296 ListItems::iterator tmp
= it
++;
298 if ( owner
== NULL
|| owner
== tmp
->second
->GetOwner() ) {
300 RawRemoveSource(tmp
);
306 ShowSourcesCount((-1) * removedItems
);
309 void CGenericClientListCtrl::UpdateItem(uint32 toupdate
, SourceItemType type
)
311 // Retrieve all entries matching the source
312 ListIteratorPair rangeIt
= m_ListItems
.equal_range( toupdate
);
314 if ( rangeIt
.first
!= rangeIt
.second
) {
315 // Visible lines, default to all because not all platforms
316 // support the GetVisibleLines function
317 long first
= 0, last
= GetItemCount();
320 // Get visible lines if we need them
321 GetVisibleLines( &first
, &last
);
324 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
325 ClientCtrlItem_Struct
* item
= it
->second
;
327 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
329 if ((type
== A4AF_SOURCE
) &&
330 item
->GetSource().GetRequestFile()
331 && std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), item
->GetSource().GetRequestFile())) {
333 item
->SetType(AVAILABLE_SOURCE
);
340 // Only update visible lines
341 if ( index
>= first
&& index
<= last
) {
342 RefreshItem( index
);
348 void CGenericClientListCtrl::ShowSources( const CKnownFileVector
& files
)
352 // The stored vector is sorted, as is the received one, so we can use binary_search
354 for (unsigned i
= 0; i
< m_knownfiles
.size(); ++i
) {
355 // Files that are not in the new list must have show set to false.
356 if (!std::binary_search(files
.begin(), files
.end(), m_knownfiles
[i
])) {
357 SetShowSources(m_knownfiles
[i
], false);
361 // This will call again SetShowSources in files that were in both vectors. Right now
362 // that function is just a inline setter, so any way to prevent it would be wasteful,
363 // but this must be reviewed if that fact changes.
365 for (unsigned i
= 0; i
< files
.size(); ++i
) {
366 SetShowSources(files
[i
], true);
369 // We must cleanup sources that are not in the received files.
373 for (ListItems::iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); /* no ++, it happens later */) {
374 ListItems::iterator tmp
= it
++;
375 ClientCtrlItem_Struct
* item
= tmp
->second
;
376 if (!std::binary_search(files
.begin(), files
.end(), item
->GetOwner())) {
377 // Remove it from the m_ListItems
378 RawRemoveSource(tmp
);
383 for (unsigned i
= 0; i
< files
.size(); ++i
) {
385 // Only those that weren't showing already
386 if (!std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), files
[i
])) {
388 CKnownFile
* file
= files
[i
];
390 wxASSERT_MSG(file
, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
394 CKnownFile::SourceSet::const_iterator it
;
396 if (IsShowingDownloadSources()) {
397 const CKnownFile::SourceSet
& normSources
= (dynamic_cast<CPartFile
*>( file
))->GetSourceList();
398 const CKnownFile::SourceSet
& a4afSources
= (dynamic_cast<CPartFile
*>( file
))->GetA4AFList();
400 // Adding normal sources
401 for ( it
= normSources
.begin(); it
!= normSources
.end(); ++it
) {
402 switch (it
->GetDownloadState()) {
405 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
410 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
415 // Adding A4AF sources
416 for ( it
= a4afSources
.begin(); it
!= a4afSources
.end(); ++it
) {
417 // Only add if the A4AF file is not in the shown list.
418 if (!std::binary_search(files
.begin(), files
.end(), it
->GetRequestFile())) {
419 RawAddSource( file
, *it
, A4AF_SOURCE
);
425 const CKnownFile::SourceSet
& sources
= file
->m_ClientUploadList
;
426 for ( it
= sources
.begin(); it
!= sources
.end(); ++it
) {
427 switch (it
->GetUploadState()) {
429 case US_ONUPLOADQUEUE
:
430 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
435 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
444 m_knownfiles
= files
;
446 ShowSourcesCount( itemDiff
);
454 * Helper-function: This function is used to gather selected items.
456 * @param list A pointer to the list to gather items from.
457 * @return A list containing the selected items of the choosen types.
459 ItemList
GetSelectedItems( CGenericClientListCtrl
* list
)
463 long index
= list
->GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
465 while ( index
> -1 ) {
466 ClientCtrlItem_Struct
* item
= (ClientCtrlItem_Struct
*)list
->GetItemData( index
);
468 results
.push_back( item
);
470 index
= list
->GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
476 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent
& WXUNUSED(event
) )
478 ItemList sources
= ::GetSelectedItems( this );
480 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
481 CKnownFile
* kf
= (*it
)->GetOwner();
482 if (!kf
->IsPartFile()) {
483 wxFAIL_MSG(wxT("File is not a partfile when swapping sources"));
486 (*it
)->GetSource().SwapToAnotherFile( true, false, false, dynamic_cast<CPartFile
*>(kf
));
491 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent
& WXUNUSED(event
) )
493 ItemList sources
= ::GetSelectedItems( this );
495 if ( sources
.size() == 1 ) {
496 sources
.front()->GetSource().RequestSharedFileList();
501 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent
& WXUNUSED(event
) )
503 ItemList sources
= ::GetSelectedItems( this );
505 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
506 CClientRef
&client
= (*it
)->GetSource();
507 if (client
.IsFriend()) {
508 theApp
->friendlist
->RemoveFriend(client
.GetFriend());
510 theApp
->friendlist
->AddFriend(client
);
516 void CGenericClientListCtrl::OnSetFriendslot(wxCommandEvent
& evt
)
518 ItemList sources
= ::GetSelectedItems( this );
520 ItemList::iterator it
= sources
.begin();
521 if (it
!= sources
.end()) {
522 CClientRef
&client
= (*it
)->GetSource();
523 theApp
->friendlist
->SetFriendSlot(client
.GetFriend(), evt
.IsChecked());
526 if (it
!= sources
.end()) {
527 wxMessageBox(_("You are not allowed to set more than one friendslot.\n Only one slot was assigned."), _("Multiple selection"), wxOK
| wxICON_ERROR
, this);
532 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent
& WXUNUSED(event
) )
534 ItemList sources
= ::GetSelectedItems( this );
536 if ( sources
.size() == 1 ) {
537 CClientRef
& source
= (sources
.front())->GetSource();
539 // These values are cached, since calling wxGetTextFromUser will
540 // start an event-loop, in which the client may be deleted.
541 wxString userName
= source
.GetUserName();
542 uint64 userID
= GUI_ID(source
.GetIP(), source
.GetUserPort());
544 wxString message
= ::wxGetTextFromUser(_("Send message to user"),
545 _("Message to send:"));
546 if ( !message
.IsEmpty() ) {
547 theApp
->amuledlg
->m_chatwnd
->SendMessage(message
, userName
, userID
);
553 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent
& WXUNUSED(event
) )
555 ItemList sources
= ::GetSelectedItems( this );
557 if ( sources
.size() == 1 ) {
558 CClientDetailDialog( this, sources
.front()->GetSource() ).ShowModal();
563 void CGenericClientListCtrl::OnItemActivated( wxListEvent
& evt
)
565 CClientDetailDialog( this, ((ClientCtrlItem_Struct
*)GetItemData( evt
.GetIndex()))->GetSource() ).ShowModal();
569 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent
& evt
)
571 long index
= CheckSelection(evt
);
579 ClientCtrlItem_Struct
* item
= (ClientCtrlItem_Struct
*)GetItemData( index
);
580 CClientRef
& client
= item
->GetSource();
582 m_menu
= new wxMenu(wxT("Clients"));
583 m_menu
->Append(MP_DETAIL
, _("Show &Details"));
584 m_menu
->Append(MP_ADDFRIEND
, client
.IsFriend() ? _("Remove from friends") : _("Add to Friends"));
586 m_menu
->AppendCheckItem(MP_FRIENDSLOT
, _("Establish Friend Slot"));
587 if (client
.IsFriend()) {
588 m_menu
->Enable(MP_FRIENDSLOT
, true);
589 m_menu
->Check(MP_FRIENDSLOT
, client
.GetFriendSlot());
591 m_menu
->Enable(MP_FRIENDSLOT
, false);
594 m_menu
->Append(MP_SHOWLIST
, _("View Files"));
595 m_menu
->Append(MP_SENDMESSAGE
, _("Send message"));
597 m_menu
->Append(MP_CHANGE2FILE
, _("Swap to this file"));
599 // Only enable the Swap option for A4AF sources
600 m_menu
->Enable(MP_CHANGE2FILE
, (item
->GetType() == A4AF_SOURCE
));
601 // We need a valid IP if we are to message the client
602 m_menu
->Enable(MP_SENDMESSAGE
, (client
.GetIP() != 0));
604 m_menu
->Enable(MP_SHOWLIST
, !client
.HasDisabledSharedFiles());
606 PopupMenu(m_menu
, evt
.GetPoint());
614 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent
& evt
)
616 // Check if clicked item is selected. If not, unselect all and select it.
617 long index
= CheckSelection(evt
);
622 CClientDetailDialog(this, ((ClientCtrlItem_Struct
*)GetItemData( index
))->GetSource()).ShowModal();
626 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent
& event
)
628 // No actions right now.
629 //switch (event.GetKeyCode()) {
636 void CGenericClientListCtrl::OnDrawItem(
637 int item
, wxDC
* dc
, const wxRect
& rect
, const wxRect
& rectHL
, bool highlighted
)
639 // Don't do any drawing if there's nobody to see it.
640 if ( !theApp
->amuledlg
->IsDialogVisible( GetParentDialog() ) ) {
644 ClientCtrlItem_Struct
* content
= (ClientCtrlItem_Struct
*)GetItemData(item
);
646 // Define text-color and background
647 // and the border of the drawn area
651 dc
->SetBackground(m_hilightBrush
);
652 colour
= m_hilightBrush
.GetColour();
654 dc
->SetBackground(m_hilightUnfocusBrush
);
655 colour
= m_hilightUnfocusBrush
.GetColour();
657 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
658 dc
->SetPen( colour
.Blend(65).GetPen() );
660 dc
->SetBackground(*(wxTheBrushList
->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX
), wxSOLID
)));
661 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
662 dc
->SetPen(*wxTRANSPARENT_PEN
);
664 dc
->SetBrush( dc
->GetBackground() );
666 dc
->DrawRectangle( rectHL
.x
, rectHL
.y
, rectHL
.width
, rectHL
.height
);
668 dc
->SetPen(*wxTRANSPARENT_PEN
);
670 // Various constant values we use
671 const int iTextOffset
= (( rect
.GetHeight() - dc
->GetCharHeight() ) / 2) + 1 /* Fixes rounding in the centering math, much easier than floor() */;
672 const int iOffset
= 2;
673 wxASSERT(m_ImageList
.GetImageCount() > 0);
674 int imageListBitmapYOffset
= 0;
675 int imageListBitmapXSize
= 0;
676 if (m_ImageList
.GetSize(0, imageListBitmapXSize
, imageListBitmapYOffset
)) {
677 imageListBitmapXSize
+= 2; // Padding.
678 imageListBitmapYOffset
= ((rect
.GetHeight() - imageListBitmapYOffset
) / 2) + 1 /* Fixes rounding like above */;
683 wxRect
cur_rec( iOffset
, rect
.y
, 0, rect
.height
);
685 for (int i
= 0; i
< GetColumnCount(); ++i
) {
687 int columnwidth
= GetColumnWidth(i
);
689 if (columnwidth
> 2*iOffset
) {
690 // Make a copy of the current rectangle so we can apply specific tweaks
691 wxRect target_rec
= cur_rec
;
692 target_rec
.width
= columnwidth
- 2*iOffset
;
694 GenericColumnEnum cid
= m_columndata
.columns
[i
].cid
;
697 DrawClientItem(dc
, cid
, target_rec
, content
, iTextOffset
, imageListBitmapYOffset
, imageListBitmapXSize
);
700 // Increment to the next column
701 cur_rec
.x
+= columnwidth
;
705 void CGenericClientListCtrl::DrawClientItem(wxDC
* dc
, int nColumn
, const wxRect
& rect
, ClientCtrlItem_Struct
* item
, int iTextOffset
, int iBitmapOffset
, int iBitmapXSize
) const
707 wxDCClipper
clipper( *dc
, rect
.GetX(), rect
.GetY(), rect
.GetWidth(), rect
.GetHeight() );
710 const CClientRef
& client
= item
->GetSource();
713 // Client name + various icons
714 case ColumnUserName
: {
715 // Point will get shifted per drawing.
717 wxPoint
point( rect
.GetX(), rect
.GetY() );
719 uint8 image
= Client_Grey_Smiley
;
721 if (item
->GetType() != A4AF_SOURCE
) {
723 switch (client
.GetDownloadState()) {
726 case DS_WAITCALLBACK
:
727 case DS_TOOMANYCONNS
:
728 image
= Client_Red_Smiley
;
731 if (client
.IsRemoteQueueFull()) {
732 image
= Client_Grey_Smiley
;
734 image
= Client_Yellow_Smiley
;
739 image
= Client_Green_Smiley
;
741 case DS_NONEEDEDPARTS
:
743 image
= Client_Grey_Smiley
; // Redundant
745 default: // DS_NONE i.e.
746 image
= Client_White_Smiley
;
750 // Default (Client_Grey_Smiley)
753 m_ImageList
.Draw(image
, *dc
, point
.x
, point
.y
+ iBitmapOffset
, wxIMAGELIST_DRAW_TRANSPARENT
);
757 point
.x
+= iBitmapXSize
;
759 uint8 clientImage
= Client_Unknown
;
761 if ( client
.IsFriend() ) {
762 clientImage
= Client_Friend_Smiley
;
764 switch ( client
.GetClientSoft() ) {
766 clientImage
= Client_aMule_Smiley
;
769 case SO_NEW_MLDONKEY
:
770 case SO_NEW2_MLDONKEY
:
771 clientImage
= Client_mlDonkey_Smiley
;
774 case SO_EDONKEYHYBRID
:
775 clientImage
= Client_eDonkeyHybrid_Smiley
;
778 clientImage
= Client_eMule_Smiley
;
781 clientImage
= Client_lphant_Smiley
;
784 case SO_NEW_SHAREAZA
:
785 case SO_NEW2_SHAREAZA
:
786 clientImage
= Client_Shareaza_Smiley
;
789 clientImage
= Client_xMule_Smiley
;
792 // cDonkey, Compatible, Unknown
793 // No icon for those yet.
794 // Using the eMule one + '?'
795 // Which is a faillback to the default (Client_Unknown)
800 int realY
= point
.y
+ iBitmapOffset
;
801 m_ImageList
.Draw(clientImage
, *dc
, point
.x
, realY
, wxIMAGELIST_DRAW_TRANSPARENT
);
803 if (client
.GetScoreRatio() > 1) {
804 // Has credits, draw the gold star
805 m_ImageList
.Draw(Client_CreditsYellow_Smiley
, *dc
, point
.x
, realY
,
806 wxIMAGELIST_DRAW_TRANSPARENT
);
807 } else if ( !client
.ExtProtocolAvailable() ) {
808 // No Ext protocol -> Draw the '-'
809 m_ImageList
.Draw(Client_ExtendedProtocol_Smiley
, *dc
, point
.x
, realY
,
810 wxIMAGELIST_DRAW_TRANSPARENT
);
813 if (client
.IsIdentified()) {
815 m_ImageList
.Draw(Client_SecIdent_Smiley
, *dc
, point
.x
, realY
,
816 wxIMAGELIST_DRAW_TRANSPARENT
);
817 } else if (client
.IsBadGuy()) {
819 m_ImageList
.Draw(Client_BadGuy_Smiley
, *dc
, point
.x
, realY
,
820 wxIMAGELIST_DRAW_TRANSPARENT
);
823 if (client
.GetObfuscationStatus() == OBST_ENABLED
) {
824 // the "¿" except it's a key
825 m_ImageList
.Draw(Client_Encryption_Smiley
, *dc
, point
.x
, realY
,
826 wxIMAGELIST_DRAW_TRANSPARENT
);
831 point
.x
+= iBitmapXSize
;
834 #ifdef ENABLE_IP2COUNTRY
835 if (theApp
->amuledlg
->m_IP2Country
->IsEnabled() && thePrefs::IsGeoIPEnabled()) {
836 // Draw the flag. Size can't be precached.
837 const CountryData
& countrydata
= theApp
->amuledlg
->m_IP2Country
->GetCountryData(client
.GetFullIP());
839 realY
= point
.y
+ (rect
.GetHeight() - countrydata
.Flag
.GetHeight())/2 + 1 /* floor() */;
841 dc
->DrawBitmap(countrydata
.Flag
,
845 userName
<< countrydata
.Name
;
847 userName
<< wxT(" - ");
849 point
.x
+= countrydata
.Flag
.GetWidth() + 2 /*Padding*/;
851 #endif // ENABLE_IP2COUNTRY
852 if (client
.GetUserName().IsEmpty()) {
853 userName
<< wxT("?");
855 userName
<< client
.GetUserName();
858 dc
->DrawText(userName
, point
.x
, rect
.GetY() + iTextOffset
);
862 case ColumnUserDownloaded
:
863 if (item
->GetType() != A4AF_SOURCE
&& client
.GetTransferredDown()) {
864 buffer
= CastItoXBytes(client
.GetTransferredDown());
865 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
868 case ColumnUserUploaded
:
869 if (item
->GetType() != A4AF_SOURCE
&& client
.GetTransferredUp()) {
870 buffer
= CastItoXBytes(client
.GetTransferredUp());
871 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
874 case ColumnUserSpeedDown
:
875 if (item
->GetType() != A4AF_SOURCE
&& client
.GetKBpsDown() > 0.001) {
876 buffer
= CFormat(_("%.1f kB/s")) % client
.GetKBpsDown();
877 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
880 case ColumnUserSpeedUp
:
881 // Datarate is in bytes.
882 if (item
->GetType() != A4AF_SOURCE
&& client
.GetUploadDatarate() > 1024) {
883 buffer
= CFormat(_("%.1f kB/s")) % (client
.GetUploadDatarate() / 1024.0);
884 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
887 case ColumnUserProgress
:
888 if ( thePrefs::ShowProgBar() ) {
889 int iWidth
= rect
.GetWidth() - 2;
890 int iHeight
= rect
.GetHeight() - 2;
892 // don't draw Text beyond the bar
893 dc
->SetClippingRegion(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
895 if ( item
->GetType() != A4AF_SOURCE
) {
896 uint32 dwTicks
= GetTickCount();
898 wxMemoryDC cdcStatus
;
900 if ( item
->dwUpdated
< dwTicks
|| !item
->status
||
901 iWidth
!= item
->status
->GetWidth() ) {
903 if (item
->status
== NULL
) {
904 item
->status
= new wxBitmap(iWidth
, iHeight
);
905 } else if ( item
->status
->GetWidth() != iWidth
) {
906 // Only recreate if size has changed
907 item
->status
->Create(iWidth
, iHeight
);
910 cdcStatus
.SelectObject(*(item
->status
));
912 if ( thePrefs::UseFlatBar() ) {
913 DrawSourceStatusBar( client
, &cdcStatus
,
914 wxRect(0, 0, iWidth
, iHeight
), true);
916 DrawSourceStatusBar( client
, &cdcStatus
,
917 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
920 cdcStatus
.SetPen( *wxBLACK_PEN
);
921 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
922 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
926 item
->dwUpdated
= dwTicks
+ 10000;
928 cdcStatus
.SelectObject(*(item
->status
));
931 dc
->Blit(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
934 CPartFile
* p
= client
.GetRequestFile();
936 a4af
= p
->GetFileName().GetPrintable();
940 buffer
= CFormat(wxT("%s: %s")) % _("A4AF") % a4af
;
942 int midx
= (2*rect
.GetX() + rect
.GetWidth()) >> 1;
943 int midy
= (2*rect
.GetY() + rect
.GetHeight()) >> 1;
945 wxCoord txtwidth
, txtheight
;
947 dc
->GetTextExtent(buffer
, &txtwidth
, &txtheight
);
949 dc
->SetTextForeground(*wxBLACK
);
950 dc
->DrawText(buffer
, wxMax(rect
.GetX() + 2, midx
- (txtwidth
>> 1)), midy
- (txtheight
>> 1));
953 dc
->SetPen( *wxBLACK_PEN
);
954 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
955 dc
->DrawRectangle( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
960 case ColumnUserAvailable
: {
961 if ( client
.GetUpPartCount() ) {
962 DrawStatusBar( client
, dc
, rect
);
967 case ColumnUserVersion
: {
968 dc
->DrawText(client
.GetClientVerString(), rect
.GetX(), rect
.GetY() + iTextOffset
);
972 case ColumnUserQueueRankRemote
: {
974 wxColour savedColour
= dc
->GetTextForeground();
975 // We only show the queue rank for sources actually queued for that file
976 if ( item
->GetType() != A4AF_SOURCE
&& client
.GetDownloadState() == DS_ONQUEUE
) {
977 if (client
.IsRemoteQueueFull()) {
978 buffer
= _("Queue Full");
980 uint16 rank
= client
.GetRemoteQueueRank();
982 qrDiff
= rank
- client
.GetOldRemoteQueueRank();
983 if (qrDiff
== rank
) {
987 dc
->SetTextForeground(*wxBLUE
);
990 dc
->SetTextForeground(*wxRED
);
992 buffer
= CFormat(_("On Queue: %u (%i)")) % rank
% qrDiff
;
994 buffer
= _("On Queue");
998 if (item
->GetType() != A4AF_SOURCE
) {
999 buffer
= DownloadStateToStr( client
.GetDownloadState(),
1000 client
.IsRemoteQueueFull() );
1002 buffer
= _("Asked for another file");
1003 if ( client
.GetRequestFile() &&
1004 client
.GetRequestFile()->GetFileName().IsOk()) {
1005 buffer
+= CFormat(wxT(" (%s)"))
1006 % client
.GetRequestFile()->GetFileName();
1010 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1012 dc
->SetTextForeground(savedColour
);
1016 case ColumnUserQueueRankLocal
:
1017 if (item
->GetType() != A4AF_SOURCE
) {
1018 if (client
.GetUploadState() == US_ONUPLOADQUEUE
) {
1019 uint16 nRank
= client
.GetUploadQueueWaitingPosition();
1021 buffer
= _("Waiting for upload slot");
1023 buffer
= CFormat(_("On Queue: %u")) % nRank
;
1025 } else if (client
.GetUploadState() == US_UPLOADING
) {
1026 buffer
= _("Uploading");
1031 buffer
= _("Asked for another file");
1033 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1035 // Source comes from?
1036 case ColumnUserOrigin
: {
1037 buffer
= wxGetTranslation(OriginToText(client
.GetSourceFrom()));
1038 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1041 // Local file name to identify on multi select
1042 case ColumnUserFileNameDownload
: {
1043 const CPartFile
* pf
= client
.GetRequestFile();
1045 buffer
= pf
->GetFileName().GetPrintable();
1047 buffer
= _("Unknown");
1048 buffer
= wxT("[") + buffer
+ wxT("]");
1050 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1053 case ColumnUserFileNameUpload
: {
1054 const CKnownFile
* kf
= client
.GetUploadFile();
1056 buffer
= kf
->GetFileName().GetPrintable();
1058 buffer
= _("Unknown");
1059 buffer
= wxT("[") + buffer
+ wxT("]");
1061 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1064 case ColumnUserFileNameDownloadRemote
: {
1065 bool nameMissmatch
= false;
1066 wxColour savedColour
= dc
->GetTextForeground();
1067 if (client
.GetClientFilename().IsEmpty()) {
1068 buffer
= _("Unknown");
1069 buffer
= wxT("[") + buffer
+ wxT("]");
1071 buffer
= client
.GetClientFilename();
1072 const CPartFile
* pf
= client
.GetRequestFile();
1073 if (pf
&& (pf
->GetFileName().GetPrintable().CmpNoCase(buffer
) != 0)) {
1074 nameMissmatch
= true;
1075 dc
->SetTextForeground(*wxRED
);
1078 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1079 if (nameMissmatch
) {
1080 dc
->SetTextForeground(savedColour
);
1087 int CGenericClientListCtrl::SortProc(wxUIntPtr param1
, wxUIntPtr param2
, long sortData
)
1089 ClientCtrlItem_Struct
* item1
= (ClientCtrlItem_Struct
*)param1
;
1090 ClientCtrlItem_Struct
* item2
= (ClientCtrlItem_Struct
*)param2
;
1092 int sortMod
= (sortData
& CMuleListCtrl::SORT_DES
) ? -1 : 1;
1093 sortData
&= CMuleListCtrl::COLUMN_MASK
;
1096 // Two sources, some different possibilites
1097 // Avilable sources first, if we have both an
1098 // available and an unavailable
1099 comp
= ( item2
->GetType() - item1
->GetType() );
1102 // unavailable and available. The order is fixed regardless of sort-order.
1105 comp
= Compare(item1
->GetSource(), item2
->GetSource(), sortData
);
1108 // We modify the result so that it matches with ascending or decending
1109 return sortMod
* comp
;
1112 int CGenericClientListCtrl::Compare(
1113 const CClientRef
& client1
, const CClientRef
& client2
, long lParamSort
)
1115 switch (lParamSort
) {
1117 case ColumnUserName
:
1118 return CmpAny( client1
.GetUserName(), client2
.GetUserName() );
1120 // Sort by transferred in the following fields
1121 case ColumnUserDownloaded
:
1122 return CmpAny( client1
.GetTransferredDown(), client2
.GetTransferredDown() );
1124 // Sort by transferred in the following fields
1125 case ColumnUserUploaded
:
1126 return CmpAny( client1
.GetTransferredUp(), client2
.GetTransferredUp() );
1129 case ColumnUserSpeedDown
:
1130 return CmpAny( client1
.GetKBpsDown(), client2
.GetKBpsDown() );
1133 case ColumnUserSpeedUp
:
1134 return CmpAny( client1
.GetUploadDatarate(), client2
.GetUploadDatarate() );
1136 // Sort by parts offered
1137 case ColumnUserProgress
:
1139 client1
.GetAvailablePartCount(),
1140 client2
.GetAvailablePartCount() );
1142 // Sort by client version
1143 case ColumnUserVersion
: {
1144 int cmp
= client1
.GetSoftStr().Cmp(client2
.GetSoftStr());
1147 cmp
= CmpAny(client1
.GetVersion(), client2
.GetVersion());
1150 cmp
= client1
.GetClientModString().Cmp(client2
.GetClientModString());
1155 // Sort by Queue-Rank
1156 case ColumnUserQueueRankRemote
: {
1157 // This will sort by download state: Downloading, OnQueue, Connecting ...
1158 // However, Asked For Another will always be placed last, due to
1159 // sorting in SortProc
1160 if ( client1
.GetDownloadState() != client2
.GetDownloadState() ) {
1161 return client1
.GetDownloadState() - client2
.GetDownloadState();
1164 // Placing items on queue before items on full queues
1165 if ( client1
.IsRemoteQueueFull() ) {
1166 if ( client2
.IsRemoteQueueFull() ) {
1171 } else if ( client2
.IsRemoteQueueFull() ) {
1174 if ( client1
.GetRemoteQueueRank() ) {
1175 if ( client2
.GetRemoteQueueRank() ) {
1177 client1
.GetRemoteQueueRank(),
1178 client2
.GetRemoteQueueRank() );
1183 if ( client2
.GetRemoteQueueRank() ) {
1192 // Sort by Queue-Rank
1193 case ColumnUserQueueRankLocal
: {
1194 // This will sort by download state: Downloading, OnQueue, Connecting ...
1195 // However, Asked For Another will always be placed last, due to
1196 // sorting in SortProc
1197 if ( client1
.GetUploadState() != client2
.GetUploadState() ) {
1198 return client1
.GetUploadState() - client2
.GetUploadState();
1201 uint16 rank1
= client1
.GetUploadQueueWaitingPosition();
1202 uint16 rank2
= client2
.GetUploadQueueWaitingPosition();
1203 // Placing items on queue before items on full queues
1210 } else if ( !rank2
) {
1231 // Source of source ;)
1232 case ColumnUserOrigin
:
1233 return CmpAny(client1
.GetSourceFrom(), client2
.GetSourceFrom());
1235 // Sort by local filename (download)
1236 case ColumnUserFileNameDownload
: {
1237 wxString buffer1
, buffer2
;
1238 const CPartFile
* pf1
= client1
.GetRequestFile();
1240 buffer1
= pf1
->GetFileName().GetPrintable();
1242 const CPartFile
* pf2
= client2
.GetRequestFile();
1244 buffer2
= pf2
->GetFileName().GetPrintable();
1246 return CmpAny(buffer1
, buffer2
);
1249 // Sort by local filename (upload)
1250 case ColumnUserFileNameUpload
: {
1251 wxString buffer1
, buffer2
;
1252 const CKnownFile
* kf1
= client1
.GetUploadFile();
1254 buffer1
= kf1
->GetFileName().GetPrintable();
1256 const CKnownFile
* kf2
= client2
.GetUploadFile();
1258 buffer2
= kf2
->GetFileName().GetPrintable();
1260 return CmpAny(buffer1
, buffer2
);
1263 case ColumnUserFileNameDownloadRemote
: {
1264 return CmpAny(client1
.GetClientFilename(), client2
.GetClientFilename());
1273 void CGenericClientListCtrl::ShowSourcesCount( int diff
)
1275 m_clientcount
+= diff
;
1276 wxStaticText
* label
= CastByID( ID_CLIENTCOUNT
, GetParent(), wxStaticText
);
1279 label
->SetLabel(CFormat(wxT("%i")) % m_clientcount
);
1280 label
->GetParent()->Layout();
1284 static const CMuleColour
crBoth(0, 192, 0);
1285 static const CMuleColour
crFlatBoth(0, 150, 0);
1287 static const CMuleColour
crNeither(240, 240, 240);
1288 static const CMuleColour
crFlatNeither(224, 224, 224);
1290 static const CMuleColour
crClientOnly(104, 104, 104);
1291 static const CMuleColour
crFlatClientOnly(0, 0, 0);
1293 static const CMuleColour
crPending(255, 208, 0);
1294 static const CMuleColour
crNextPending(255, 255, 100);
1296 void CGenericClientListCtrl::DrawSourceStatusBar(
1297 const CClientRef
& source
, wxDC
* dc
, const wxRect
& rect
, bool bFlat
) const
1299 static CBarShader
s_StatusBar(16);
1301 CPartFile
* reqfile
= source
.GetRequestFile();
1303 s_StatusBar
.SetHeight(rect
.height
);
1304 s_StatusBar
.SetWidth(rect
.width
);
1305 s_StatusBar
.Set3dDepth( thePrefs::Get3DDepth() );
1306 const BitVector
& partStatus
= source
.GetPartStatus();
1308 if (reqfile
&& reqfile
->GetPartCount() == partStatus
.size()) {
1309 s_StatusBar
.SetFileSize(reqfile
->GetFileSize());
1310 uint16 lastDownloadingPart
= source
.GetDownloadState() == DS_DOWNLOADING
1311 ? source
.GetLastDownloadingPart() : 0xffff;
1312 uint16 nextRequestedPart
= source
.GetNextRequestedPart();
1314 for ( uint16 i
= 0; i
< partStatus
.size(); i
++ ) {
1315 uint64 uStart
= PARTSIZE
* i
;
1316 uint64 uEnd
= uStart
+ reqfile
->GetPartSize(i
) - 1;
1319 if (!partStatus
.get(i
)) {
1320 // client does not have this part
1322 colour
= bFlat
? crFlatNeither
: crNeither
;
1323 } else if ( reqfile
->IsComplete(i
)) {
1326 colour
= bFlat
? crFlatBoth
: crBoth
;
1327 } else if (lastDownloadingPart
== i
) {
1331 } else if (nextRequestedPart
== i
) {
1334 colour
= crNextPending
;
1336 // client has this part, we need it
1338 colour
= bFlat
? crFlatClientOnly
: crClientOnly
;
1341 if ( source
.GetRequestFile()->IsStopped() ) {
1345 s_StatusBar
.FillRange(uStart
, uEnd
, colour
);
1348 s_StatusBar
.SetFileSize(1);
1349 s_StatusBar
.FillRange(0, 1, bFlat
? crFlatNeither
: crNeither
);
1352 s_StatusBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
1355 static const CMuleColour
crUnavailable(240, 240, 240);
1356 static const CMuleColour
crFlatUnavailable(224, 224, 224);
1358 static const CMuleColour
crAvailable(104, 104, 104);
1359 static const CMuleColour
crFlatAvailable(0, 0, 0);
1361 void CGenericClientListCtrl::DrawStatusBar( const CClientRef
& client
, wxDC
* dc
, const wxRect
& rect1
) const
1363 wxRect rect
= rect1
;
1367 wxPen old_pen
= dc
->GetPen();
1368 wxBrush old_brush
= dc
->GetBrush();
1369 bool bFlat
= thePrefs::UseFlatBar();
1371 wxRect barRect
= rect
;
1372 if (!bFlat
) { // round bar has a black border, the bar itself is 1 pixel less on each border
1375 barRect
.height
-= 2;
1378 static CBarShader
s_StatusBar(16);
1380 uint32 partCount
= client
.GetUpPartCount();
1382 // Seems the partfile in the client object is not necessarily valid when bar is drawn for the first time.
1383 // Keep it simple and make all parts same size.
1384 s_StatusBar
.SetFileSize(partCount
* PARTSIZE
);
1385 s_StatusBar
.SetHeight(barRect
.height
);
1386 s_StatusBar
.SetWidth(barRect
.width
);
1387 s_StatusBar
.Set3dDepth( thePrefs::Get3DDepth() );
1390 for ( uint64 i
= 0; i
< partCount
; i
++ ) {
1391 uint64 uStart
= PARTSIZE
* i
;
1392 uEnd
= uStart
+ PARTSIZE
- 1;
1394 s_StatusBar
.FillRange(uStart
, uEnd
, client
.IsUpPartAvailable(i
) ? (bFlat
? crFlatAvailable
: crAvailable
) : (bFlat
? crFlatUnavailable
: crUnavailable
));
1396 // fill the rest (if partStatus is empty)
1397 s_StatusBar
.FillRange(uEnd
+ 1, partCount
* PARTSIZE
- 1, bFlat
? crFlatUnavailable
: crUnavailable
);
1398 s_StatusBar
.Draw(dc
, barRect
.x
, barRect
.y
, bFlat
);
1401 // Draw black border
1402 dc
->SetPen( *wxBLACK_PEN
);
1403 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
1404 dc
->DrawRectangle(rect
);
1407 dc
->SetPen( old_pen
);
1408 dc
->SetBrush( old_brush
);
1411 // File_checked_for_headers