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(wxID_ANY
, CGenericClientListCtrl::OnItemActivated
)
98 EVT_LIST_ITEM_RIGHT_CLICK(wxID_ANY
, CGenericClientListCtrl::OnMouseRightClick
)
99 EVT_LIST_ITEM_MIDDLE_CLICK(wxID_ANY
, 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 wxString
CGenericClientListCtrl::TranslateCIDToName(GenericColumnEnum cid
)
139 wxString name
= wxEmptyString
;
145 case ColumnUserDownloaded
:
148 case ColumnUserUploaded
:
151 case ColumnUserSpeedDown
:
154 case ColumnUserSpeedUp
:
157 case ColumnUserProgress
:
160 case ColumnUserAvailable
:
163 case ColumnUserVersion
:
166 case ColumnUserQueueRankLocal
:
169 case ColumnUserQueueRankRemote
:
172 case ColumnUserOrigin
:
175 case ColumnUserFileNameDownload
:
178 case ColumnUserFileNameUpload
:
181 case ColumnUserFileNameDownloadRemote
:
193 void CGenericClientListCtrl::InitColumnData()
195 if (!m_columndata
.n_columns
) {
196 throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
199 for (int i
= 0; i
< m_columndata
.n_columns
; ++i
) {
200 InsertColumn( i
, wxGetTranslation(m_columndata
.columns
[i
].title
), wxLIST_FORMAT_LEFT
, m_columndata
.columns
[i
].width
, TranslateCIDToName(m_columndata
.columns
[i
].cid
));
206 CGenericClientListCtrl::~CGenericClientListCtrl()
208 while ( !m_ListItems
.empty() ) {
209 delete m_ListItems
.begin()->second
;
210 m_ListItems
.erase( m_ListItems
.begin() );
213 void CGenericClientListCtrl::RawAddSource(CKnownFile
* owner
, CUpDownClient
* source
, SourceItemType type
)
215 ClientCtrlItem_Struct
* newitem
= new ClientCtrlItem_Struct
;
216 newitem
->SetContents(owner
, source
, type
);
218 m_ListItems
.insert( ListItemsPair(source
, newitem
) );
220 long item
= InsertItem( GetItemCount(), wxEmptyString
);
221 SetItemPtrData( item
, reinterpret_cast<wxUIntPtr
>(newitem
) );
222 SetItemBackgroundColour( item
, GetBackgroundColour() );
225 void CGenericClientListCtrl::AddSource(CKnownFile
* owner
, CUpDownClient
* source
, SourceItemType type
)
227 wxCHECK_RET(owner
, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
228 wxCHECK_RET(source
, wxT("NULL source in CGenericClientListCtrl::AddSource"));
230 // Update the other instances of this source
232 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
);
233 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
234 ClientCtrlItem_Struct
* cur_item
= it
->second
;
236 // Check if this source has been already added to this file => to be sure
237 if ( cur_item
->GetOwner() == owner
) {
238 // Update this instance with its new setting
239 if ((type
== A4AF_SOURCE
) &&
240 cur_item
->GetSource()->GetRequestFile()
241 && std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), cur_item
->GetSource()->GetRequestFile())) {
242 cur_item
->SetContents(owner
, source
, AVAILABLE_SOURCE
);
244 cur_item
->SetContents(owner
, source
, type
);
246 cur_item
->dwUpdated
= 0;
248 } else if ( type
== AVAILABLE_SOURCE
) {
249 // The state 'Available' is exclusive
250 cur_item
->SetContents(cur_item
->GetOwner(), source
, A4AF_SOURCE
);
251 cur_item
->dwUpdated
= 0;
259 if ( std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), owner
) ) {
260 RawAddSource(owner
, source
, type
);
262 ShowSourcesCount( 1 );
266 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator
& it
)
268 ClientCtrlItem_Struct
* item
= it
->second
;
270 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
278 // Remove it from the m_ListItems
279 m_ListItems
.erase( it
);
282 void CGenericClientListCtrl::RemoveSource( const CUpDownClient
* source
, const CKnownFile
* owner
)
284 // A NULL owner means remove it no matter what.
285 wxCHECK_RET(source
, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
287 // Retrieve all entries matching the source
288 ListIteratorPair rangeIt
= m_ListItems
.equal_range(source
);
290 int removedItems
= 0;
292 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; /* no ++, it happens later */) {
293 ListItems::iterator tmp
= it
++;
295 if ( owner
== NULL
|| owner
== tmp
->second
->GetOwner() ) {
297 RawRemoveSource(tmp
);
303 ShowSourcesCount((-1) * removedItems
);
306 void CGenericClientListCtrl::UpdateItem(const void* toupdate
, SourceItemType type
)
308 // Retrieve all entries matching the source
309 ListIteratorPair rangeIt
= m_ListItems
.equal_range( toupdate
);
311 if ( rangeIt
.first
!= rangeIt
.second
) {
312 // Visible lines, default to all because not all platforms
313 // support the GetVisibleLines function
314 long first
= 0, last
= GetItemCount();
317 // Get visible lines if we need them
318 GetVisibleLines( &first
, &last
);
321 for ( ListItems::iterator it
= rangeIt
.first
; it
!= rangeIt
.second
; ++it
) {
322 ClientCtrlItem_Struct
* item
= it
->second
;
324 long index
= FindItem( -1, reinterpret_cast<wxUIntPtr
>(item
) );
326 if ((type
== A4AF_SOURCE
) &&
327 item
->GetSource()->GetRequestFile()
328 && std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), item
->GetSource()->GetRequestFile())) {
330 item
->SetType(AVAILABLE_SOURCE
);
337 // Only update visible lines
338 if ( index
>= first
&& index
<= last
) {
339 RefreshItem( index
);
345 void CGenericClientListCtrl::ShowSources( const CKnownFileVector
& files
)
349 // The stored vector is sorted, as is the received one, so we can use binary_search
351 for (unsigned i
= 0; i
< m_knownfiles
.size(); ++i
) {
352 // Files that are not in the new list must have show set to false.
353 if (!std::binary_search(files
.begin(), files
.end(), m_knownfiles
[i
])) {
354 SetShowSources(m_knownfiles
[i
], false);
358 // This will call again SetShowSources in files that were in both vectors. Right now
359 // that function is just a inline setter, so any way to prevent it would be wasteful,
360 // but this must be reviewed if that fact changes.
362 for (unsigned i
= 0; i
< files
.size(); ++i
) {
363 SetShowSources(files
[i
], true);
366 // We must cleanup sources that are not in the received files.
370 for (ListItems::iterator it
= m_ListItems
.begin(); it
!= m_ListItems
.end(); /* no ++, it happens later */) {
371 ListItems::iterator tmp
= it
++;
372 ClientCtrlItem_Struct
* item
= tmp
->second
;
373 if (!std::binary_search(files
.begin(), files
.end(), item
->GetOwner())) {
374 // Remove it from the m_ListItems
375 RawRemoveSource(tmp
);
380 for (unsigned i
= 0; i
< files
.size(); ++i
) {
382 // Only those that weren't showing already
383 if (!std::binary_search(m_knownfiles
.begin(), m_knownfiles
.end(), files
[i
])) {
385 CKnownFile
* file
= files
[i
];
387 wxASSERT_MSG(file
, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
391 CKnownFile::SourceSet::const_iterator it
;
393 if (IsShowingDownloadSources()) {
394 const CKnownFile::SourceSet
& normSources
= (dynamic_cast<CPartFile
*>( file
))->GetSourceList();
395 const CKnownFile::SourceSet
& a4afSources
= (dynamic_cast<CPartFile
*>( file
))->GetA4AFList();
397 // Adding normal sources
398 for ( it
= normSources
.begin(); it
!= normSources
.end(); ++it
) {
399 switch ((*it
)->GetDownloadState()) {
402 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
407 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
412 // Adding A4AF sources
413 for ( it
= a4afSources
.begin(); it
!= a4afSources
.end(); ++it
) {
414 // Only add if the A4AF file is not in the shown list.
415 if (!std::binary_search(files
.begin(), files
.end(), (*it
)->GetRequestFile())) {
416 RawAddSource( file
, *it
, A4AF_SOURCE
);
422 const CKnownFile::SourceSet
& sources
= file
->m_ClientUploadList
;
423 for ( it
= sources
.begin(); it
!= sources
.end(); ++it
) {
424 switch ((*it
)->GetUploadState()) {
426 case US_ONUPLOADQUEUE
:
427 RawAddSource( file
, *it
, AVAILABLE_SOURCE
);
432 RawAddSource( file
, *it
, UNAVAILABLE_SOURCE
);
441 m_knownfiles
= files
;
443 ShowSourcesCount( itemDiff
);
451 * Helper-function: This function is used to gather selected items.
453 * @param list A pointer to the list to gather items from.
454 * @return A list containing the selected items of the choosen types.
456 ItemList
GetSelectedItems( CGenericClientListCtrl
* list
)
460 long index
= list
->GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
462 while ( index
> -1 ) {
463 ClientCtrlItem_Struct
* item
= (ClientCtrlItem_Struct
*)list
->GetItemData( index
);
465 results
.push_back( item
);
467 index
= list
->GetNextItem( index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
473 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent
& WXUNUSED(event
) )
475 ItemList sources
= ::GetSelectedItems( this );
477 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
478 CKnownFile
* kf
= (*it
)->GetOwner();
479 if (!kf
->IsPartFile()) {
480 wxFAIL_MSG(wxT("File is not a partfile when swapping sources"));
483 (*it
)->GetSource()->SwapToAnotherFile( true, false, false, dynamic_cast<CPartFile
*>(kf
));
488 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent
& WXUNUSED(event
) )
490 ItemList sources
= ::GetSelectedItems( this );
492 if ( sources
.size() == 1 ) {
493 sources
.front()->GetSource()->RequestSharedFileList();
498 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent
& WXUNUSED(event
) )
500 ItemList sources
= ::GetSelectedItems( this );
502 for ( ItemList::iterator it
= sources
.begin(); it
!= sources
.end(); ++it
) {
503 CUpDownClient
* client
= (*it
)->GetSource();
504 if (client
->IsFriend()) {
505 theApp
->amuledlg
->m_chatwnd
->RemoveFriend(client
->GetUserHash(), client
->GetIP(), client
->GetUserPort());
507 theApp
->amuledlg
->m_chatwnd
->AddFriend( client
);
513 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent
& WXUNUSED(event
) )
515 ItemList sources
= ::GetSelectedItems( this );
517 if ( sources
.size() == 1 ) {
518 CUpDownClient
* source
= (sources
.front())->GetSource();
520 // These values are cached, since calling wxGetTextFromUser will
521 // start an event-loop, in which the client may be deleted.
522 wxString userName
= source
->GetUserName();
523 uint64 userID
= GUI_ID(source
->GetIP(), source
->GetUserPort());
525 wxString message
= ::wxGetTextFromUser(_("Send message to user"),
526 _("Message to send:"));
527 if ( !message
.IsEmpty() ) {
528 theApp
->amuledlg
->m_chatwnd
->SendMessage(message
, userName
, userID
);
534 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent
& WXUNUSED(event
) )
536 ItemList sources
= ::GetSelectedItems( this );
538 if ( sources
.size() == 1 ) {
539 CClientDetailDialog( this, sources
.front()->GetSource() ).ShowModal();
544 void CGenericClientListCtrl::OnItemActivated( wxListEvent
& evt
)
546 CClientDetailDialog( this, ((ClientCtrlItem_Struct
*)GetItemData( evt
.GetIndex()))->GetSource()).ShowModal();
550 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent
& evt
)
552 long index
= CheckSelection(evt
);
560 ClientCtrlItem_Struct
* item
= (ClientCtrlItem_Struct
*)GetItemData( index
);
561 CUpDownClient
* client
= item
->GetSource();
563 m_menu
= new wxMenu(wxT("Clients"));
564 m_menu
->Append(MP_DETAIL
, _("Show &Details"));
565 m_menu
->Append(MP_ADDFRIEND
, client
->IsFriend() ? _("Remove from friends") : _("Add to Friends"));
566 m_menu
->Append(MP_SHOWLIST
, _("View Files"));
567 m_menu
->Append(MP_SENDMESSAGE
, _("Send message"));
569 m_menu
->Append(MP_CHANGE2FILE
, _("Swap to this file"));
571 // Only enable the Swap option for A4AF sources
572 m_menu
->Enable(MP_CHANGE2FILE
, (item
->GetType() == A4AF_SOURCE
));
573 // We need a valid IP if we are to message the client
574 m_menu
->Enable(MP_SENDMESSAGE
, (client
->GetIP() != 0));
576 m_menu
->Enable(MP_SHOWLIST
, !client
->HasDisabledSharedFiles());
578 PopupMenu(m_menu
, evt
.GetPoint());
586 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent
& evt
)
588 // Check if clicked item is selected. If not, unselect all and select it.
589 long index
= CheckSelection(evt
);
594 CClientDetailDialog(this, ((ClientCtrlItem_Struct
*)GetItemData( index
))->GetSource()).ShowModal();
598 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent
& event
)
600 // No actions right now.
601 //switch (event.GetKeyCode()) {
608 void CGenericClientListCtrl::OnDrawItem(
609 int item
, wxDC
* dc
, const wxRect
& rect
, const wxRect
& rectHL
, bool highlighted
)
611 // Don't do any drawing if there's nobody to see it.
612 if ( !theApp
->amuledlg
->IsDialogVisible( GetParentDialog() ) ) {
616 ClientCtrlItem_Struct
* content
= (ClientCtrlItem_Struct
*)GetItemData(item
);
618 // Define text-color and background
619 // and the border of the drawn area
623 dc
->SetBackground(m_hilightBrush
);
624 colour
= m_hilightBrush
.GetColour();
626 dc
->SetBackground(m_hilightUnfocusBrush
);
627 colour
= m_hilightUnfocusBrush
.GetColour();
629 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
630 dc
->SetPen( colour
.Blend(65).GetPen() );
632 dc
->SetBackground(*(wxTheBrushList
->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX
), wxSOLID
)));
633 dc
->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT
));
634 dc
->SetPen(*wxTRANSPARENT_PEN
);
636 dc
->SetBrush( dc
->GetBackground() );
638 dc
->DrawRectangle( rectHL
.x
, rectHL
.y
, rectHL
.width
, rectHL
.height
);
640 dc
->SetPen(*wxTRANSPARENT_PEN
);
642 // Various constant values we use
643 const int iTextOffset
= (( rect
.GetHeight() - dc
->GetCharHeight() ) / 2) + 1 /* Fixes rounding in the centering math, much easier than floor() */;
644 const int iOffset
= 2;
645 wxASSERT(m_ImageList
.GetImageCount() > 0);
646 int imageListBitmapYOffset
= 0;
647 int imageListBitmapXSize
= 0;
648 if (m_ImageList
.GetSize(0, imageListBitmapXSize
, imageListBitmapYOffset
)) {
649 imageListBitmapXSize
+= 2; // Padding.
650 imageListBitmapYOffset
= ((rect
.GetHeight() - imageListBitmapYOffset
) / 2) + 1 /* Fixes rounding like above */;
655 wxRect
cur_rec( iOffset
, rect
.y
, 0, rect
.height
);
657 for (int i
= 0; i
< GetColumnCount(); ++i
) {
659 int columnwidth
= GetColumnWidth(i
);
661 if (columnwidth
> 2*iOffset
) {
662 // Make a copy of the current rectangle so we can apply specific tweaks
663 wxRect target_rec
= cur_rec
;
664 target_rec
.width
= columnwidth
- 2*iOffset
;
666 GenericColumnEnum cid
= m_columndata
.columns
[i
].cid
;
669 DrawClientItem(dc
, cid
, target_rec
, content
, iTextOffset
, imageListBitmapYOffset
, imageListBitmapXSize
);
672 // Increment to the next column
673 cur_rec
.x
+= columnwidth
;
677 void CGenericClientListCtrl::DrawClientItem(wxDC
* dc
, int nColumn
, const wxRect
& rect
, ClientCtrlItem_Struct
* item
, int iTextOffset
, int iBitmapOffset
, int iBitmapXSize
) const
679 wxDCClipper
clipper( *dc
, rect
.GetX(), rect
.GetY(), rect
.GetWidth(), rect
.GetHeight() );
682 const CUpDownClient
* client
= item
->GetSource();
685 // Client name + various icons
686 case ColumnUserName
: {
687 // Point will get shifted per drawing.
689 wxPoint
point( rect
.GetX(), rect
.GetY() );
691 uint8 image
= Client_Grey_Smiley
;
693 if (item
->GetType() != A4AF_SOURCE
) {
695 switch (client
->GetDownloadState()) {
698 case DS_WAITCALLBACK
:
699 case DS_TOOMANYCONNS
:
700 image
= Client_Red_Smiley
;
703 if (client
->IsRemoteQueueFull()) {
704 image
= Client_Grey_Smiley
;
706 image
= Client_Yellow_Smiley
;
711 image
= Client_Green_Smiley
;
713 case DS_NONEEDEDPARTS
:
715 image
= Client_Grey_Smiley
; // Redundant
717 default: // DS_NONE i.e.
718 image
= Client_White_Smiley
;
722 // Default (Client_Grey_Smiley)
725 m_ImageList
.Draw(image
, *dc
, point
.x
, point
.y
+ iBitmapOffset
, wxIMAGELIST_DRAW_TRANSPARENT
);
729 point
.x
+= iBitmapXSize
;
731 uint8 clientImage
= Client_Unknown
;
733 if ( client
->IsFriend() ) {
734 clientImage
= Client_Friend_Smiley
;
736 switch ( client
->GetClientSoft() ) {
738 clientImage
= Client_aMule_Smiley
;
741 case SO_NEW_MLDONKEY
:
742 case SO_NEW2_MLDONKEY
:
743 clientImage
= Client_mlDonkey_Smiley
;
746 case SO_EDONKEYHYBRID
:
747 clientImage
= Client_eDonkeyHybrid_Smiley
;
750 clientImage
= Client_eMule_Smiley
;
753 clientImage
= Client_lphant_Smiley
;
756 case SO_NEW_SHAREAZA
:
757 case SO_NEW2_SHAREAZA
:
758 clientImage
= Client_Shareaza_Smiley
;
761 clientImage
= Client_xMule_Smiley
;
764 // cDonkey, Compatible, Unknown
765 // No icon for those yet.
766 // Using the eMule one + '?'
767 // Which is a faillback to the default (Client_Unknown)
772 int realY
= point
.y
+ iBitmapOffset
;
773 m_ImageList
.Draw(clientImage
, *dc
, point
.x
, realY
, wxIMAGELIST_DRAW_TRANSPARENT
);
775 if (client
->GetScoreRatio() > 1) {
776 // Has credits, draw the gold star
777 m_ImageList
.Draw(Client_CreditsYellow_Smiley
, *dc
, point
.x
, realY
,
778 wxIMAGELIST_DRAW_TRANSPARENT
);
779 } else if ( !client
->ExtProtocolAvailable() ) {
780 // No Ext protocol -> Draw the '-'
781 m_ImageList
.Draw(Client_ExtendedProtocol_Smiley
, *dc
, point
.x
, realY
,
782 wxIMAGELIST_DRAW_TRANSPARENT
);
785 if (client
->IsIdentified()) {
787 m_ImageList
.Draw(Client_SecIdent_Smiley
, *dc
, point
.x
, realY
,
788 wxIMAGELIST_DRAW_TRANSPARENT
);
789 } else if (client
->IsBadGuy()) {
791 m_ImageList
.Draw(Client_BadGuy_Smiley
, *dc
, point
.x
, realY
,
792 wxIMAGELIST_DRAW_TRANSPARENT
);
795 if (client
->GetObfuscationStatus() == OBST_ENABLED
) {
796 // the "¿" except it's a key
797 m_ImageList
.Draw(Client_Encryption_Smiley
, *dc
, point
.x
, realY
,
798 wxIMAGELIST_DRAW_TRANSPARENT
);
803 point
.x
+= iBitmapXSize
;
806 #ifdef ENABLE_IP2COUNTRY
807 if (theApp
->amuledlg
->m_IP2Country
->IsEnabled() && thePrefs::IsGeoIPEnabled()) {
808 // Draw the flag. Size can't be precached.
809 const CountryData
& countrydata
= theApp
->amuledlg
->m_IP2Country
->GetCountryData(client
->GetFullIP());
811 realY
= point
.y
+ (rect
.GetHeight() - countrydata
.Flag
.GetHeight())/2 + 1 /* floor() */;
813 dc
->DrawBitmap(countrydata
.Flag
,
817 userName
<< countrydata
.Name
;
819 userName
<< wxT(" - ");
821 point
.x
+= countrydata
.Flag
.GetWidth() + 2 /*Padding*/;
823 #endif // ENABLE_IP2COUNTRY
824 if (client
->GetUserName().IsEmpty()) {
825 userName
<< wxT("?");
827 userName
<< client
->GetUserName();
830 dc
->DrawText(userName
, point
.x
, rect
.GetY() + iTextOffset
);
834 case ColumnUserDownloaded
:
835 if (item
->GetType() != A4AF_SOURCE
&& client
->GetTransferredDown()) {
836 buffer
= CastItoXBytes(client
->GetTransferredDown());
837 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
840 case ColumnUserUploaded
:
841 if (item
->GetType() != A4AF_SOURCE
&& client
->GetTransferredUp()) {
842 buffer
= CastItoXBytes(client
->GetTransferredUp());
843 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
846 case ColumnUserSpeedDown
:
847 if (item
->GetType() != A4AF_SOURCE
&& client
->GetKBpsDown() > 0.001) {
848 buffer
= CFormat(_("%.1f kB/s")) % client
->GetKBpsDown();
849 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
852 case ColumnUserSpeedUp
:
853 // Datarate is in bytes.
854 if (item
->GetType() != A4AF_SOURCE
&& client
->GetUploadDatarate() > 1024) {
855 buffer
= CFormat(_("%.1f kB/s")) % (client
->GetUploadDatarate() / 1024.0);
856 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
859 case ColumnUserProgress
:
860 if ( thePrefs::ShowProgBar() ) {
861 int iWidth
= rect
.GetWidth() - 2;
862 int iHeight
= rect
.GetHeight() - 2;
864 // don't draw Text beyond the bar
865 dc
->SetClippingRegion(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
867 if ( item
->GetType() != A4AF_SOURCE
) {
868 uint32 dwTicks
= GetTickCount();
870 wxMemoryDC cdcStatus
;
872 if ( item
->dwUpdated
< dwTicks
|| !item
->status
||
873 iWidth
!= item
->status
->GetWidth() ) {
875 if (item
->status
== NULL
) {
876 item
->status
= new wxBitmap(iWidth
, iHeight
);
877 } else if ( item
->status
->GetWidth() != iWidth
) {
878 // Only recreate if size has changed
879 item
->status
->Create(iWidth
, iHeight
);
882 cdcStatus
.SelectObject(*(item
->status
));
884 if ( thePrefs::UseFlatBar() ) {
885 DrawSourceStatusBar( client
, &cdcStatus
,
886 wxRect(0, 0, iWidth
, iHeight
), true);
888 DrawSourceStatusBar( client
, &cdcStatus
,
889 wxRect(1, 1, iWidth
- 2, iHeight
- 2), false);
892 cdcStatus
.SetPen( *wxBLACK_PEN
);
893 cdcStatus
.SetBrush( *wxTRANSPARENT_BRUSH
);
894 cdcStatus
.DrawRectangle( 0, 0, iWidth
, iHeight
);
898 item
->dwUpdated
= dwTicks
+ 10000;
900 cdcStatus
.SelectObject(*(item
->status
));
903 dc
->Blit(rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
, &cdcStatus
, 0, 0);
906 CPartFile
* p
= client
->GetRequestFile();
908 a4af
= p
->GetFileName().GetPrintable();
912 buffer
= CFormat(wxT("%s: %s")) % _("A4AF") % a4af
;
914 int midx
= (2*rect
.GetX() + rect
.GetWidth()) >> 1;
915 int midy
= (2*rect
.GetY() + rect
.GetHeight()) >> 1;
917 wxCoord txtwidth
, txtheight
;
919 dc
->GetTextExtent(buffer
, &txtwidth
, &txtheight
);
921 dc
->SetTextForeground(*wxBLACK
);
922 dc
->DrawText(buffer
, wxMax(rect
.GetX() + 2, midx
- (txtwidth
>> 1)), midy
- (txtheight
>> 1));
925 dc
->SetPen( *wxBLACK_PEN
);
926 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
927 dc
->DrawRectangle( rect
.GetX(), rect
.GetY() + 1, iWidth
, iHeight
);
932 case ColumnUserAvailable
: {
933 if ( client
->GetUpPartCount() ) {
934 DrawStatusBar( client
, dc
, rect
);
939 case ColumnUserVersion
: {
940 dc
->DrawText(client
->GetClientVerString(), rect
.GetX(), rect
.GetY() + iTextOffset
);
944 case ColumnUserQueueRankRemote
: {
946 wxColour savedColour
= dc
->GetTextForeground();
947 // We only show the queue rank for sources actually queued for that file
948 if ( item
->GetType() != A4AF_SOURCE
&& client
->GetDownloadState() == DS_ONQUEUE
) {
949 if (client
->IsRemoteQueueFull()) {
950 buffer
= _("Queue Full");
952 if (client
->GetRemoteQueueRank()) {
953 qrDiff
= client
->GetRemoteQueueRank() -
954 client
->GetOldRemoteQueueRank();
955 if (qrDiff
== client
->GetRemoteQueueRank() ) {
959 dc
->SetTextForeground(*wxBLUE
);
962 dc
->SetTextForeground(*wxRED
);
964 buffer
= CFormat(_("On Queue: %u (%i)")) % client
->GetRemoteQueueRank() % qrDiff
;
966 buffer
= _("On Queue");
970 if (item
->GetType() != A4AF_SOURCE
) {
971 buffer
= DownloadStateToStr( client
->GetDownloadState(),
972 client
->IsRemoteQueueFull() );
974 buffer
= _("Asked for another file");
975 if ( client
->GetRequestFile() &&
976 client
->GetRequestFile()->GetFileName().IsOk()) {
977 buffer
+= CFormat(wxT(" (%s)"))
978 % client
->GetRequestFile()->GetFileName();
982 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
984 dc
->SetTextForeground(savedColour
);
988 case ColumnUserQueueRankLocal
:
989 if (item
->GetType() != A4AF_SOURCE
) {
990 if (client
->GetUploadState() == US_ONUPLOADQUEUE
) {
991 uint16 nRank
= client
->GetUploadQueueWaitingPosition();
993 buffer
= _("Waiting for upload slot");
995 buffer
= CFormat(_("On Queue: %u")) % nRank
;
997 } else if (client
->GetUploadState() == US_UPLOADING
) {
998 buffer
= _("Uploading");
1003 buffer
= _("Asked for another file");
1005 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1007 // Source comes from?
1008 case ColumnUserOrigin
: {
1009 buffer
= wxGetTranslation(OriginToText(client
->GetSourceFrom()));
1010 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1013 // Local file name to identify on multi select
1014 case ColumnUserFileNameDownload
: {
1015 const CPartFile
* pf
= client
->GetRequestFile();
1017 buffer
= pf
->GetFileName().GetPrintable();
1019 buffer
= wxT("[Unknown]");
1021 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1024 case ColumnUserFileNameUpload
: {
1025 const CKnownFile
* kf
= client
->GetUploadFile();
1027 buffer
= kf
->GetFileName().GetPrintable();
1029 buffer
= wxT("[Unknown]");
1031 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1034 case ColumnUserFileNameDownloadRemote
: {
1035 bool nameMissmatch
= false;
1036 wxColour savedColour
= dc
->GetTextForeground();
1037 if (client
->GetClientFilename().IsEmpty()) {
1038 buffer
= wxT("[Unknown]");
1040 buffer
= client
->GetClientFilename();
1041 const CPartFile
* pf
= client
->GetRequestFile();
1042 if (pf
&& (pf
->GetFileName().GetPrintable().CmpNoCase(buffer
) != 0)) {
1043 nameMissmatch
= true;
1044 dc
->SetTextForeground(*wxRED
);
1047 dc
->DrawText(buffer
, rect
.GetX(), rect
.GetY() + iTextOffset
);
1048 if (nameMissmatch
) {
1049 dc
->SetTextForeground(savedColour
);
1056 int CGenericClientListCtrl::SortProc(wxUIntPtr param1
, wxUIntPtr param2
, long sortData
)
1058 ClientCtrlItem_Struct
* item1
= (ClientCtrlItem_Struct
*)param1
;
1059 ClientCtrlItem_Struct
* item2
= (ClientCtrlItem_Struct
*)param2
;
1061 int sortMod
= (sortData
& CMuleListCtrl::SORT_DES
) ? -1 : 1;
1062 sortData
&= CMuleListCtrl::COLUMN_MASK
;
1065 // Two sources, some different possibilites
1066 // Avilable sources first, if we have both an
1067 // available and an unavailable
1068 comp
= ( item2
->GetType() - item1
->GetType() );
1071 // unavailable and available. The order is fixed regardless of sort-order.
1074 comp
= Compare(item1
->GetSource(), item2
->GetSource(), sortData
);
1077 // We modify the result so that it matches with ascending or decending
1078 return sortMod
* comp
;
1081 int CGenericClientListCtrl::Compare(
1082 const CUpDownClient
* client1
, const CUpDownClient
* client2
, long lParamSort
)
1084 switch (lParamSort
) {
1086 case ColumnUserName
:
1087 return CmpAny( client1
->GetUserName(), client2
->GetUserName() );
1089 // Sort by transferred in the following fields
1090 case ColumnUserDownloaded
:
1091 return CmpAny( client1
->GetTransferredDown(), client2
->GetTransferredDown() );
1093 // Sort by transferred in the following fields
1094 case ColumnUserUploaded
:
1095 return CmpAny( client1
->GetTransferredUp(), client2
->GetTransferredUp() );
1098 case ColumnUserSpeedDown
:
1099 return CmpAny( client1
->GetKBpsDown(), client2
->GetKBpsDown() );
1102 case ColumnUserSpeedUp
:
1103 return CmpAny( client1
->GetUploadDatarate(), client2
->GetUploadDatarate() );
1105 // Sort by parts offered
1106 case ColumnUserProgress
:
1108 client1
->GetAvailablePartCount(),
1109 client2
->GetAvailablePartCount() );
1111 // Sort by client version
1112 case ColumnUserVersion
: {
1113 if (client1
->GetClientSoft() != client2
->GetClientSoft()) {
1114 return client1
->GetSoftStr().Cmp(client2
->GetSoftStr());
1117 if (client1
->GetVersion() != client2
->GetVersion()) {
1118 return CmpAny(client1
->GetVersion(), client2
->GetVersion());
1121 return client1
->GetClientModString().Cmp(client2
->GetClientModString());
1124 // Sort by Queue-Rank
1125 case ColumnUserQueueRankRemote
: {
1126 // This will sort by download state: Downloading, OnQueue, Connecting ...
1127 // However, Asked For Another will always be placed last, due to
1128 // sorting in SortProc
1129 if ( client1
->GetDownloadState() != client2
->GetDownloadState() ) {
1130 return client1
->GetDownloadState() - client2
->GetDownloadState();
1133 // Placing items on queue before items on full queues
1134 if ( client1
->IsRemoteQueueFull() ) {
1135 if ( client2
->IsRemoteQueueFull() ) {
1140 } else if ( client2
->IsRemoteQueueFull() ) {
1143 if ( client1
->GetRemoteQueueRank() ) {
1144 if ( client2
->GetRemoteQueueRank() ) {
1146 client1
->GetRemoteQueueRank(),
1147 client2
->GetRemoteQueueRank() );
1152 if ( client2
->GetRemoteQueueRank() ) {
1161 // Sort by Queue-Rank
1162 case ColumnUserQueueRankLocal
: {
1163 // This will sort by download state: Downloading, OnQueue, Connecting ...
1164 // However, Asked For Another will always be placed last, due to
1165 // sorting in SortProc
1166 if ( client1
->GetUploadState() != client2
->GetUploadState() ) {
1167 return client1
->GetUploadState() - client2
->GetUploadState();
1170 uint16 rank1
= client1
->GetUploadQueueWaitingPosition();
1171 uint16 rank2
= client2
->GetUploadQueueWaitingPosition();
1172 // Placing items on queue before items on full queues
1179 } else if ( !rank2
) {
1200 // Source of source ;)
1201 case ColumnUserOrigin
:
1202 return CmpAny(client1
->GetSourceFrom(), client2
->GetSourceFrom());
1204 // Sort by local filename (download)
1205 case ColumnUserFileNameDownload
: {
1206 wxString buffer1
, buffer2
;
1207 const CPartFile
* pf1
= client1
->GetRequestFile();
1209 buffer1
= pf1
->GetFileName().GetPrintable();
1211 const CPartFile
* pf2
= client2
->GetRequestFile();
1213 buffer2
= pf2
->GetFileName().GetPrintable();
1215 return CmpAny(buffer1
, buffer2
);
1218 // Sort by local filename (upload)
1219 case ColumnUserFileNameUpload
: {
1220 wxString buffer1
, buffer2
;
1221 const CKnownFile
* kf1
= client1
->GetUploadFile();
1223 buffer1
= kf1
->GetFileName().GetPrintable();
1225 const CKnownFile
* kf2
= client2
->GetUploadFile();
1227 buffer2
= kf2
->GetFileName().GetPrintable();
1229 return CmpAny(buffer1
, buffer2
);
1232 case ColumnUserFileNameDownloadRemote
: {
1233 return CmpAny(client1
->GetClientFilename(), client2
->GetClientFilename());
1242 void CGenericClientListCtrl::ShowSourcesCount( int diff
)
1244 m_clientcount
+= diff
;
1245 wxStaticText
* label
= CastByID( ID_CLIENTCOUNT
, GetParent(), wxStaticText
);
1248 label
->SetLabel(CFormat(wxT("%i")) % m_clientcount
);
1249 label
->GetParent()->Layout();
1253 static const CMuleColour
crBoth(0, 192, 0);
1254 static const CMuleColour
crFlatBoth(0, 150, 0);
1256 static const CMuleColour
crNeither(240, 240, 240);
1257 static const CMuleColour
crFlatNeither(224, 224, 224);
1259 static const CMuleColour
crClientOnly(104, 104, 104);
1260 static const CMuleColour
crFlatClientOnly(0, 0, 0);
1262 static const CMuleColour
crPending(255, 208, 0);
1263 static const CMuleColour
crNextPending(255, 255, 100);
1265 void CGenericClientListCtrl::DrawSourceStatusBar(
1266 const CUpDownClient
* source
, wxDC
* dc
, const wxRect
& rect
, bool bFlat
) const
1268 static CBarShader
s_StatusBar(16);
1270 CPartFile
* reqfile
= source
->GetRequestFile();
1272 s_StatusBar
.SetHeight(rect
.height
);
1273 s_StatusBar
.SetWidth(rect
.width
);
1274 s_StatusBar
.Set3dDepth( thePrefs::Get3DDepth() );
1275 const BitVector
& partStatus
= source
->GetPartStatus();
1277 if (reqfile
&& reqfile
->GetPartCount() == partStatus
.size()) {
1278 s_StatusBar
.SetFileSize(reqfile
->GetFileSize());
1279 uint16 lastDownloadingPart
= source
->GetDownloadState() == DS_DOWNLOADING
1280 ? source
->GetLastDownloadingPart() : 0xffff;
1281 uint16 nextRequestedPart
= source
->GetNextRequestedPart();
1283 for ( uint16 i
= 0; i
< partStatus
.size(); i
++ ) {
1284 uint64 uStart
= PARTSIZE
* i
;
1285 uint64 uEnd
= uStart
+ reqfile
->GetPartSize(i
) - 1;
1288 if (!partStatus
.get(i
)) {
1289 // client does not have this part
1291 colour
= bFlat
? crFlatNeither
: crNeither
;
1292 } else if ( reqfile
->IsComplete(i
)) {
1295 colour
= bFlat
? crFlatBoth
: crBoth
;
1296 } else if (lastDownloadingPart
== i
) {
1300 } else if (nextRequestedPart
== i
) {
1303 colour
= crNextPending
;
1305 // client has this part, we need it
1307 colour
= bFlat
? crFlatClientOnly
: crClientOnly
;
1310 if ( source
->GetRequestFile()->IsStopped() ) {
1314 s_StatusBar
.FillRange(uStart
, uEnd
, colour
);
1317 s_StatusBar
.SetFileSize(1);
1318 s_StatusBar
.FillRange(0, 1, bFlat
? crFlatNeither
: crNeither
);
1321 s_StatusBar
.Draw(dc
, rect
.x
, rect
.y
, bFlat
);
1324 static const CMuleColour
crUnavailable(240, 240, 240);
1325 static const CMuleColour
crFlatUnavailable(224, 224, 224);
1327 static const CMuleColour
crAvailable(104, 104, 104);
1328 static const CMuleColour
crFlatAvailable(0, 0, 0);
1330 void CGenericClientListCtrl::DrawStatusBar( const CUpDownClient
* client
, wxDC
* dc
, const wxRect
& rect1
) const
1332 wxRect rect
= rect1
;
1336 wxPen old_pen
= dc
->GetPen();
1337 wxBrush old_brush
= dc
->GetBrush();
1338 bool bFlat
= thePrefs::UseFlatBar();
1340 wxRect barRect
= rect
;
1341 if (!bFlat
) { // round bar has a black border, the bar itself is 1 pixel less on each border
1344 barRect
.height
-= 2;
1347 static CBarShader
s_StatusBar(16);
1349 uint32 partCount
= client
->GetUpPartCount();
1351 // Seems the partfile in the client object is not necessarily valid when bar is drawn for the first time.
1352 // Keep it simple and make all parts same size.
1353 s_StatusBar
.SetFileSize(partCount
* PARTSIZE
);
1354 s_StatusBar
.SetHeight(barRect
.height
);
1355 s_StatusBar
.SetWidth(barRect
.width
);
1356 s_StatusBar
.Set3dDepth( thePrefs::Get3DDepth() );
1359 for ( uint64 i
= 0; i
< partCount
; i
++ ) {
1360 uint64 uStart
= PARTSIZE
* i
;
1361 uEnd
= uStart
+ PARTSIZE
- 1;
1363 s_StatusBar
.FillRange(uStart
, uEnd
, client
->IsUpPartAvailable(i
) ? (bFlat
? crFlatAvailable
: crAvailable
) : (bFlat
? crFlatUnavailable
: crUnavailable
));
1365 // fill the rest (if partStatus is empty)
1366 s_StatusBar
.FillRange(uEnd
+ 1, partCount
* PARTSIZE
- 1, bFlat
? crFlatUnavailable
: crUnavailable
);
1367 s_StatusBar
.Draw(dc
, barRect
.x
, barRect
.y
, bFlat
);
1370 // Draw black border
1371 dc
->SetPen( *wxBLACK_PEN
);
1372 dc
->SetBrush( *wxTRANSPARENT_BRUSH
);
1373 dc
->DrawRectangle(rect
);
1376 dc
->SetPen( old_pen
);
1377 dc
->SetBrush( old_brush
);
1380 // File_checked_for_headers