1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
23 //
24 #include "GenericClientListCtrl.h"
25 
26 #include <protocol/ed2k/ClientSoftware.h>
27 #include <common/MenuIDs.h>
28 
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
43 #endif
44 #include "Logger.h"
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"
52 
53 struct ClientCtrlItem_Struct
54 {
ClientCtrlItem_StructClientCtrlItem_Struct55 	ClientCtrlItem_Struct()
56 		: dwUpdated(0),
57 		  status(NULL),
58 		  m_owner(NULL),
59 		  m_type(UNAVAILABLE_SOURCE)
60 	{ }
61 
~ClientCtrlItem_StructClientCtrlItem_Struct62 	~ClientCtrlItem_Struct() {
63 		delete status;
64 	}
65 
GetTypeClientCtrlItem_Struct66 	SourceItemType GetType() const {
67 		return m_type;
68 	}
69 
GetOwnerClientCtrlItem_Struct70 	CKnownFile* GetOwner() const {
71 		return m_owner;
72 	}
73 
74 
GetSourceClientCtrlItem_Struct75 	CClientRef& GetSource() {
76 		return m_sourceValue;
77 	}
78 
SetContentsClientCtrlItem_Struct79 	void SetContents(CKnownFile* owner, const CClientRef& source, SourceItemType type) {
80 		m_owner = owner;
81 		m_sourceValue = source;
82 		m_type = type;
83 	}
84 
SetTypeClientCtrlItem_Struct85 	void SetType(SourceItemType type) { m_type = type; }
86 
87 	uint32		dwUpdated;
88 	wxBitmap*	status;
89 
90 private:
91 	CKnownFile*		m_owner;
92 	CClientRef		m_sourceValue;
93 	SourceItemType	m_type;
94 };
95 
96 #define m_ImageList theApp->amuledlg->m_imagelist
97 
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)
102 
103 	EVT_CHAR( CGenericClientListCtrl::OnKeyPressed )
104 
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 )
111 END_EVENT_TABLE()
112 
113 //! This listtype is used when gathering the selected items.
114 typedef std::list<ClientCtrlItem_Struct*>	ItemList;
115 
CGenericClientListCtrl(const wxString & tablename,wxWindow * parent,wxWindowID winid,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)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 )
120 :
121 CMuleListCtrl( parent, winid, pos, size, style | wxLC_OWNERDRAW | wxLC_VRULES | wxLC_HRULES, validator, name ),
122 m_columndata(0, NULL)
123 {
124 	// Setting the sorter function must be done in the derived class, to use the translation function correctly.
125 	// SetSortFunc( SortProc );
126 
127 	// Set the table-name (for loading and saving preferences).
128 	SetTableName( tablename );
129 
130 	m_menu = NULL;
131 	m_showing = false;
132 
133 	m_hilightBrush  = CMuleColour(wxSYS_COLOUR_HIGHLIGHT).Blend(125).GetBrush();
134 
135 	m_hilightUnfocusBrush = CMuleColour(wxSYS_COLOUR_BTNSHADOW).Blend(125).GetBrush();
136 
137 	m_clientcount = 0;
138 }
139 
TranslateCIDToName(GenericColumnEnum cid)140 wxString CGenericClientListCtrl::TranslateCIDToName(GenericColumnEnum cid)
141 {
142 	wxString name;
143 
144 	switch (cid) {
145 		case ColumnUserName:
146 			name = wxT("N");
147 			break;
148 		case ColumnUserDownloaded:
149 			name = wxT("D");
150 			break;
151 		case ColumnUserUploaded:
152 			name = wxT("U");
153 			break;
154 		case ColumnUserSpeedDown:
155 			name = wxT("S");
156 			break;
157 		case ColumnUserSpeedUp:
158 			name = wxT("s");
159 			break;
160 		case ColumnUserProgress:
161 			name = wxT("P");
162 			break;
163 		case ColumnUserAvailable:
164 			name = wxT("A");
165 			break;
166 		case ColumnUserVersion:
167 			name = wxT("V");
168 			break;
169 		case ColumnUserQueueRankLocal:
170 			name = wxT("Q");
171 			break;
172 		case ColumnUserQueueRankRemote:
173 			name = wxT("q");
174 			break;
175 		case ColumnUserOrigin:
176 			name = wxT("O");
177 			break;
178 		case ColumnUserFileNameDownload:
179 			name = wxT("F");
180 			break;
181 		case ColumnUserFileNameUpload:
182 			name = wxT("f");
183 			break;
184 		case ColumnUserFileNameDownloadRemote:
185 			name = wxT("R");
186 			break;
187 		case ColumnUserSharedFiles:
188 			name = wxT("m");
189 			break;
190 		case ColumnInvalid:
191 		default:
192 			wxFAIL;
193 			break;
194 	}
195 
196 	return name;
197 }
198 
InitColumnData()199 void CGenericClientListCtrl::InitColumnData()
200 {
201 	if (!m_columndata.n_columns) {
202 		throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
203 	}
204 
205 	for (int i = 0; i < m_columndata.n_columns; ++i) {
206 		InsertColumn( i, wxGetTranslation(m_columndata.columns[i].title), wxLIST_FORMAT_LEFT, m_columndata.columns[i].width, TranslateCIDToName(m_columndata.columns[i].cid));
207 	}
208 
209 	LoadSettings();
210 }
211 
~CGenericClientListCtrl()212 CGenericClientListCtrl::~CGenericClientListCtrl()
213 {
214 	while ( !m_ListItems.empty() ) {
215 		delete m_ListItems.begin()->second;
216 		m_ListItems.erase( m_ListItems.begin() );
217 	}
218 }
RawAddSource(CKnownFile * owner,CClientRef source,SourceItemType type)219 void CGenericClientListCtrl::RawAddSource(CKnownFile* owner, CClientRef source, SourceItemType type)
220 {
221 	ClientCtrlItem_Struct* newitem = new ClientCtrlItem_Struct;
222 	newitem->SetContents(owner, source, type);
223 
224 	m_ListItems.insert( ListItemsPair(source.ECID(), newitem) );
225 
226 	long item = InsertItem( GetItemCount(), wxEmptyString );
227 	SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
228 	SetItemBackgroundColour( item, GetBackgroundColour() );
229 }
230 
AddSource(CKnownFile * owner,const CClientRef & source,SourceItemType type)231 void CGenericClientListCtrl::AddSource(CKnownFile* owner, const CClientRef& source, SourceItemType type)
232 {
233 	wxCHECK_RET(owner, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
234 	wxCHECK_RET(source.IsLinked(), wxT("Unlinked source in CGenericClientListCtrl::AddSource"));
235 
236 	// Update the other instances of this source
237 	bool bFound = false;
238 	ListIteratorPair rangeIt = m_ListItems.equal_range(source.ECID());
239 	for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
240 		ClientCtrlItem_Struct* cur_item = it->second;
241 
242 		// Check if this source has been already added to this file => to be sure
243 		if ( cur_item->GetOwner() == owner ) {
244 			// Update this instance with its new setting
245 			if ((type == A4AF_SOURCE) &&
246 				cur_item->GetSource().GetRequestFile()
247 				&& std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), cur_item->GetSource().GetRequestFile())) {
248 				cur_item->SetContents(owner, source, AVAILABLE_SOURCE);
249 			} else {
250 				cur_item->SetContents(owner, source, type);
251 			}
252 			cur_item->dwUpdated = 0;
253 			bFound = true;
254 		} else if ( type == AVAILABLE_SOURCE ) {
255 			// The state 'Available' is exclusive
256 			cur_item->SetContents(cur_item->GetOwner(), source, A4AF_SOURCE);
257 			cur_item->dwUpdated = 0;
258 		}
259 	}
260 
261 	if ( bFound ) {
262 		return;
263 	}
264 
265 	if ( std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), owner) ) {
266 		RawAddSource(owner, source, type);
267 
268 		ShowSourcesCount( 1 );
269 	}
270 }
271 
RawRemoveSource(ListItems::iterator & it)272 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator& it)
273 {
274 	ClientCtrlItem_Struct* item = it->second;
275 
276 	long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
277 
278 	if ( index > -1 ) {
279 		DeleteItem( index );
280 	}
281 
282 	delete item;
283 
284 	// Remove it from the m_ListItems
285 	m_ListItems.erase( it );
286 }
287 
RemoveSource(uint32 source,const CKnownFile * owner)288 void CGenericClientListCtrl::RemoveSource(uint32 source, const CKnownFile* owner)
289 {
290 	// A NULL owner means remove it no matter what.
291 	wxCHECK_RET(source, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
292 
293 	// Retrieve all entries matching the source
294 	ListIteratorPair rangeIt = m_ListItems.equal_range(source);
295 
296 	int removedItems = 0;
297 
298 	for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second;  /* no ++, it happens later */) {
299 		ListItems::iterator tmp = it++;
300 
301 		if ( owner == NULL || owner == tmp->second->GetOwner() ) {
302 
303 			RawRemoveSource(tmp);
304 
305 			++removedItems;
306 		}
307 	}
308 
309 	ShowSourcesCount((-1) * removedItems);
310 }
311 
UpdateItem(uint32 toupdate,SourceItemType type)312 void CGenericClientListCtrl::UpdateItem(uint32 toupdate, SourceItemType type)
313 {
314 	// Retrieve all entries matching the source
315 	ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
316 
317 	if ( rangeIt.first != rangeIt.second ) {
318 		// Visible lines, default to all because not all platforms
319 		// support the GetVisibleLines function
320 		long first = 0, last = GetItemCount();
321 
322 	#ifndef __WINDOWS__
323 		// Get visible lines if we need them
324 		GetVisibleLines( &first, &last );
325 	#endif
326 
327 		for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
328 			ClientCtrlItem_Struct* item = it->second;
329 
330 			long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
331 
332 			if ((type == A4AF_SOURCE) &&
333 					item->GetSource().GetRequestFile()
334 					&& std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), item->GetSource().GetRequestFile())) {
335 
336 				item->SetType(AVAILABLE_SOURCE);
337 			} else {
338 				item->SetType(type);
339 			}
340 
341 			item->dwUpdated = 0;
342 
343 			// Only update visible lines
344 			if ( index >= first && index <= last) {
345 				RefreshItem( index );
346 			}
347 		}
348 	}
349 }
350 
ShowSources(const CKnownFileVector & files)351 void CGenericClientListCtrl::ShowSources( const CKnownFileVector& files )
352 {
353 	Freeze();
354 
355 	// The stored vector is sorted, as is the received one, so we can use binary_search
356 
357 	for (unsigned i = 0; i < m_knownfiles.size(); ++i) {
358 		// Files that are not in the new list must have show set to false.
359 		if (!std::binary_search(files.begin(), files.end(), m_knownfiles[i])) {
360 			SetShowSources(m_knownfiles[i], false);
361 		}
362 	}
363 
364 	// This will call again SetShowSources in files that were in both vectors. Right now
365 	// that function is just a inline setter, so any way to prevent it would be wasteful,
366 	// but this must be reviewed if that fact changes.
367 
368 	for (unsigned i = 0; i < files.size(); ++i) {
369 		SetShowSources(files[i], true);
370 	}
371 
372 	// We must cleanup sources that are not in the received files.
373 
374 	int itemDiff = 0;
375 
376 	for (ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); /* no ++, it happens later */) {
377 		ListItems::iterator tmp = it++;
378 		ClientCtrlItem_Struct* item = tmp->second;
379 		if (!std::binary_search(files.begin(), files.end(), item->GetOwner())) {
380 			// Remove it from the m_ListItems
381 			RawRemoveSource(tmp);
382 			--itemDiff;
383 		}
384 	}
385 
386 	for (unsigned i = 0; i < files.size(); ++i) {
387 
388 		// Only those that weren't showing already
389 		if (!std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), files[i])) {
390 
391 			CKnownFile* file = files[i];
392 
393 			wxASSERT_MSG(file, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
394 
395 			if (file) {
396 
397 				CKnownFile::SourceSet::const_iterator it;
398 
399 				if (IsShowingDownloadSources()) {
400 					const CKnownFile::SourceSet& normSources = (dynamic_cast<CPartFile*>( file ))->GetSourceList();
401 					const CKnownFile::SourceSet& a4afSources = (dynamic_cast<CPartFile*>( file ))->GetA4AFList();
402 
403 					// Adding normal sources
404 					for ( it = normSources.begin(); it != normSources.end(); ++it ) {
405 						switch (it->GetDownloadState()) {
406 							case DS_DOWNLOADING:
407 							case DS_ONQUEUE:
408 								RawAddSource( file, *it, AVAILABLE_SOURCE );
409 								++itemDiff;
410 								break;
411 							default:
412 								// Any other state
413 								RawAddSource( file, *it, UNAVAILABLE_SOURCE );
414 								++itemDiff;
415 						}
416 					}
417 
418 					// Adding A4AF sources
419 					for ( it = a4afSources.begin(); it != a4afSources.end(); ++it ) {
420 						// Only add if the A4AF file is not in the shown list.
421 						if (!std::binary_search(files.begin(), files.end(), it->GetRequestFile())) {
422 							RawAddSource( file, *it, A4AF_SOURCE );
423 							++itemDiff;
424 						}
425 					}
426 				} else {
427 					// Known file
428 					const CKnownFile::SourceSet& sources = file->m_ClientUploadList;
429 					for ( it = sources.begin(); it != sources.end(); ++it ) {
430 						switch (it->GetUploadState()) {
431 							case US_UPLOADING:
432 							case US_ONUPLOADQUEUE:
433 								RawAddSource( file, *it, AVAILABLE_SOURCE );
434 								++itemDiff;
435 								break;
436 							default:
437 								// Any other state
438 								RawAddSource( file, *it, UNAVAILABLE_SOURCE );
439 								++itemDiff;
440 						}
441 					}
442 				}
443 			}
444 		}
445 	}
446 
447 	m_knownfiles = files;
448 
449 	ShowSourcesCount( itemDiff );
450 
451 	SortList();
452 
453 	Thaw();
454 }
455 
456 /**
457  * Helper-function: This function is used to gather selected items.
458  *
459  * @param list A pointer to the list to gather items from.
460  * @return A list containing the selected items of the choosen types.
461  */
GetSelectedItems(CGenericClientListCtrl * list)462 static ItemList GetSelectedItems( CGenericClientListCtrl* list )
463 {
464 	ItemList results;
465 
466 	long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
467 
468 	while ( index > -1 ) {
469 		ClientCtrlItem_Struct* item = reinterpret_cast<ClientCtrlItem_Struct*>(list->GetItemData( index ));
470 
471 		results.push_back( item );
472 
473 		index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
474 	}
475 
476 	return results;
477 }
478 
OnSwapSource(wxCommandEvent & WXUNUSED (event))479 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
480 {
481 	ItemList sources = ::GetSelectedItems( this );
482 
483 	for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
484 		CKnownFile * kf = (*it)->GetOwner();
485 		if (!kf->IsPartFile()) {
486 			wxFAIL_MSG(wxT("File is not a partfile when swapping sources"));
487 			continue;
488 		}
489 		(*it)->GetSource().SwapToAnotherFile( true, false, false,  dynamic_cast<CPartFile*>(kf));
490 	}
491 }
492 
493 
OnViewFiles(wxCommandEvent & WXUNUSED (event))494 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
495 {
496 	ItemList sources = ::GetSelectedItems( this );
497 
498 	if ( sources.size() == 1 ) {
499 		sources.front()->GetSource().RequestSharedFileList();
500 	}
501 }
502 
503 
OnAddFriend(wxCommandEvent & WXUNUSED (event))504 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
505 {
506 	ItemList sources = ::GetSelectedItems( this );
507 
508 	for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
509 		CClientRef &client = (*it)->GetSource();
510 		if (client.IsFriend()) {
511 			theApp->friendlist->RemoveFriend(client.GetFriend());
512 		} else {
513 			theApp->friendlist->AddFriend(client);
514 		}
515 	}
516 }
517 
518 
OnSetFriendslot(wxCommandEvent & evt)519 void CGenericClientListCtrl::OnSetFriendslot(wxCommandEvent& evt)
520 {
521 	ItemList sources = ::GetSelectedItems( this );
522 
523 	ItemList::iterator it = sources.begin();
524 	if (it != sources.end()) {
525 		CClientRef &client = (*it)->GetSource();
526 		theApp->friendlist->SetFriendSlot(client.GetFriend(), evt.IsChecked());
527 		++it;
528 	}
529 	if (it != sources.end()) {
530 		wxMessageBox(_("You are not allowed to set more than one friendslot.\n Only one slot was assigned."), _("Multiple selection"), wxOK | wxICON_ERROR, this);
531 	}
532 }
533 
534 
OnSendMessage(wxCommandEvent & WXUNUSED (event))535 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
536 {
537 	ItemList sources = ::GetSelectedItems( this );
538 
539 	if ( sources.size() == 1 ) {
540 		CClientRef & source = (sources.front())->GetSource();
541 
542 		// These values are cached, since calling wxGetTextFromUser will
543 		// start an event-loop, in which the client may be deleted.
544 		wxString userName = source.GetUserName();
545 		uint64 userID = GUI_ID(source.GetIP(), source.GetUserPort());
546 
547 		wxString message = ::wxGetTextFromUser(_("Send message to user"),
548 			_("Message to send:"));
549 		if ( !message.IsEmpty() ) {
550 			theApp->amuledlg->m_chatwnd->SendMessage(message, userName, userID);
551 		}
552 	}
553 }
554 
555 
OnViewClientInfo(wxCommandEvent & WXUNUSED (event))556 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
557 {
558 	ItemList sources = ::GetSelectedItems( this );
559 
560 	if ( sources.size() == 1 ) {
561 		CClientDetailDialog( this, sources.front()->GetSource() ).ShowModal();
562 	}
563 }
564 
565 
OnItemActivated(wxListEvent & evt)566 void CGenericClientListCtrl::OnItemActivated( wxListEvent& evt )
567 {
568 	CClientDetailDialog( this, reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( evt.GetIndex()))->GetSource() ).ShowModal();
569 }
570 
571 
OnMouseRightClick(wxListEvent & evt)572 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent& evt)
573 {
574 	long index = CheckSelection(evt);
575 	if (index < 0) {
576 		return;
577 	}
578 
579 	delete m_menu;
580 	m_menu = NULL;
581 
582 	ClientCtrlItem_Struct* item = reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( index ));
583 	CClientRef& client = item->GetSource();
584 
585 	m_menu = new wxMenu(wxT("Clients"));
586 	m_menu->Append(MP_DETAIL, _("Show &Details"));
587 	m_menu->Append(MP_ADDFRIEND, client.IsFriend() ? _("Remove from friends") : _("Add to Friends"));
588 
589 	m_menu->AppendCheckItem(MP_FRIENDSLOT, _("Establish Friend Slot"));
590 	if (client.IsFriend()) {
591 		m_menu->Enable(MP_FRIENDSLOT, true);
592 		m_menu->Check(MP_FRIENDSLOT, client.GetFriendSlot());
593 	} else {
594 		m_menu->Enable(MP_FRIENDSLOT, false);
595 	}
596 
597 	m_menu->Append(MP_SHOWLIST, _("View Files"));
598 	m_menu->Append(MP_SENDMESSAGE, _("Send message"));
599 
600 	m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
601 
602 	// Only enable the Swap option for A4AF sources
603 	m_menu->Enable(MP_CHANGE2FILE, (item->GetType() == A4AF_SOURCE));
604 	// We need a valid IP if we are to message the client
605 	m_menu->Enable(MP_SENDMESSAGE, (client.GetIP() != 0));
606 
607 	m_menu->Enable(MP_SHOWLIST, !client.HasDisabledSharedFiles());
608 
609 	PopupMenu(m_menu, evt.GetPoint());
610 
611 	delete m_menu;
612 	m_menu = NULL;
613 
614 }
615 
616 
OnMouseMiddleClick(wxListEvent & evt)617 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent& evt)
618 {
619 	// Check if clicked item is selected. If not, unselect all and select it.
620 	long index = CheckSelection(evt);
621 	if ( index < 0 ) {
622 		return;
623 	}
624 
625 	CClientDetailDialog(this, reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( index ))->GetSource()).ShowModal();
626 }
627 
628 
OnKeyPressed(wxKeyEvent & event)629 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent& event )
630 {
631 	// No actions right now.
632 	//switch (event.GetKeyCode()) {
633 	//	default:
634 			event.Skip();
635 	//}
636 }
637 
638 
OnDrawItem(int item,wxDC * dc,const wxRect & rect,const wxRect & rectHL,bool highlighted)639 void CGenericClientListCtrl::OnDrawItem(
640 	int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
641 {
642 	// Don't do any drawing if there's nobody to see it.
643 	if ( !theApp->amuledlg->IsDialogVisible( GetParentDialog() ) ) {
644 		return;
645 	}
646 
647 	ClientCtrlItem_Struct* content = reinterpret_cast<ClientCtrlItem_Struct *>(GetItemData(item));
648 
649 	// Define text-color and background
650 	// and the border of the drawn area
651 	if (highlighted) {
652 		CMuleColour colour;
653 		if (GetFocus()) {
654 			dc->SetBackground(m_hilightBrush);
655 			colour = m_hilightBrush.GetColour();
656 		} else {
657 			dc->SetBackground(m_hilightUnfocusBrush);
658 			colour = m_hilightUnfocusBrush.GetColour();
659 		}
660 		dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
661 		dc->SetPen( colour.Blend(65).GetPen() );
662 	} else {
663 		dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
664 		dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
665 		dc->SetPen(*wxTRANSPARENT_PEN);
666 	}
667 	dc->SetBrush( dc->GetBackground() );
668 
669 	dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
670 
671 	dc->SetPen(*wxTRANSPARENT_PEN);
672 
673 	// Various constant values we use
674 	const int iTextOffset = (( rect.GetHeight() - dc->GetCharHeight() ) / 2) + 1 /* Fixes rounding in the centering math, much easier than floor() */;
675 	const int iOffset = 2;
676 	wxASSERT(m_ImageList.GetImageCount() > 0);
677 	int imageListBitmapYOffset = 0;
678 	int imageListBitmapXSize = 0;
679 	if (m_ImageList.GetSize(0, imageListBitmapXSize, imageListBitmapYOffset)) {
680 		imageListBitmapXSize += 2; // Padding.
681 		imageListBitmapYOffset = ((rect.GetHeight() - imageListBitmapYOffset) / 2) + 1 /* Fixes rounding like above */;
682 	} else {
683 		wxFAIL;
684 	}
685 
686 	wxRect cur_rec( iOffset, rect.y, 0, rect.height );
687 
688 	for (int i = 0; i < GetColumnCount(); ++i) {
689 
690 		int columnwidth = GetColumnWidth(i);
691 
692 		if (columnwidth > 2*iOffset) {
693 			// Make a copy of the current rectangle so we can apply specific tweaks
694 			wxRect target_rec = cur_rec;
695 			target_rec.width = columnwidth - 2*iOffset;
696 
697 			GenericColumnEnum cid = m_columndata.columns[i].cid;
698 
699 			// Draw the item
700 			DrawClientItem(dc, cid, target_rec, content, iTextOffset, imageListBitmapYOffset, imageListBitmapXSize);
701 		}
702 
703 		// Increment to the next column
704 		cur_rec.x += columnwidth;
705 	}
706 }
707 
DrawClientItem(wxDC * dc,int nColumn,const wxRect & rect,ClientCtrlItem_Struct * item,int iTextOffset,int iBitmapOffset,int iBitmapXSize) const708 void CGenericClientListCtrl::DrawClientItem(wxDC* dc, int nColumn, const wxRect& rect, ClientCtrlItem_Struct* item, int iTextOffset, int iBitmapOffset, int iBitmapXSize ) const
709 {
710 	wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
711 	wxString buffer;
712 
713 	const CClientRef& client = item->GetSource();
714 
715 	switch (nColumn) {
716 		// Client name + various icons
717 		case ColumnUserName: {
718 			// Point will get shifted per drawing.
719 
720 			wxPoint point( rect.GetX(), rect.GetY() );
721 
722 			uint8 image = Client_Grey_Smiley;
723 
724 			if (item->GetType() != A4AF_SOURCE) {
725 
726 				switch (client.GetDownloadState()) {
727 					case DS_CONNECTING:
728 					case DS_CONNECTED:
729 					case DS_WAITCALLBACK:
730 					case DS_TOOMANYCONNS:
731 						image = Client_Red_Smiley;
732 						break;
733 					case DS_ONQUEUE:
734 						if (client.IsRemoteQueueFull()) {
735 							image = Client_Grey_Smiley;
736 						} else {
737 							image = Client_Yellow_Smiley;
738 						}
739 						break;
740 					case DS_DOWNLOADING:
741 					case DS_REQHASHSET:
742 						image = Client_Green_Smiley;
743 						break;
744 					case DS_NONEEDEDPARTS:
745 					case DS_LOWTOLOWIP:
746 						image = Client_Grey_Smiley; // Redundant
747 						break;
748 					default: // DS_NONE i.e.
749 						image = Client_White_Smiley;
750 					}
751 
752 				} else {
753 					// Default (Client_Grey_Smiley)
754 				}
755 
756 				m_ImageList.Draw(image, *dc, point.x, point.y + iBitmapOffset, wxIMAGELIST_DRAW_TRANSPARENT);
757 
758 				// Next
759 
760 				point.x += iBitmapXSize;
761 
762 				uint8 clientImage = Client_Unknown;
763 
764 				if ( client.IsFriend() ) {
765 					clientImage = Client_Friend_Smiley;
766 				} else {
767 					switch ( client.GetClientSoft() ) {
768 						case SO_AMULE:
769 							clientImage = Client_aMule_Smiley;
770 							break;
771 						case SO_MLDONKEY:
772 						case SO_NEW_MLDONKEY:
773 						case SO_NEW2_MLDONKEY:
774 							clientImage = Client_mlDonkey_Smiley;
775 							break;
776 						case SO_EDONKEY:
777 						case SO_EDONKEYHYBRID:
778 							clientImage = Client_eDonkeyHybrid_Smiley;
779 							break;
780 						case SO_EMULE:
781 							clientImage = Client_eMule_Smiley;
782 							break;
783 						case SO_LPHANT:
784 							clientImage = Client_lphant_Smiley;
785 							break;
786 						case SO_SHAREAZA:
787 						case SO_NEW_SHAREAZA:
788 						case SO_NEW2_SHAREAZA:
789 							clientImage = Client_Shareaza_Smiley;
790 							break;
791 						case SO_LXMULE:
792 							clientImage = Client_xMule_Smiley;
793 							break;
794 						default:
795 							// cDonkey, Compatible, Unknown
796 							// No icon for those yet.
797 							// Using the eMule one + '?'
798 							// Which is a faillback to the default (Client_Unknown)
799 							break;
800 					}
801 				}
802 
803 				int realY = point.y + iBitmapOffset;
804 				m_ImageList.Draw(clientImage, *dc, point.x, realY, wxIMAGELIST_DRAW_TRANSPARENT);
805 
806 				if (client.GetScoreRatio() > 1) {
807 					// Has credits, draw the gold star
808 					m_ImageList.Draw(Client_CreditsYellow_Smiley, *dc, point.x, realY,
809 						wxIMAGELIST_DRAW_TRANSPARENT );
810 				}	else if ( !client.ExtProtocolAvailable() ) {
811 					// No Ext protocol -> Draw the '-'
812 					m_ImageList.Draw(Client_ExtendedProtocol_Smiley, *dc, point.x, realY,
813 						wxIMAGELIST_DRAW_TRANSPARENT);
814 				}
815 
816 				if (client.IsIdentified()) {
817 					// the 'v'
818 					m_ImageList.Draw(Client_SecIdent_Smiley, *dc, point.x, realY,
819 						wxIMAGELIST_DRAW_TRANSPARENT);
820 				} else if (client.IsBadGuy()) {
821 					// the 'X'
822 					m_ImageList.Draw(Client_BadGuy_Smiley, *dc, point.x, realY,
823 						wxIMAGELIST_DRAW_TRANSPARENT);
824 				}
825 
826 				if (client.GetObfuscationStatus() == OBST_ENABLED) {
827 					// the "¿" except it's a key
828 					m_ImageList.Draw(Client_Encryption_Smiley, *dc, point.x, realY,
829 						wxIMAGELIST_DRAW_TRANSPARENT);
830 				}
831 
832 				// Next
833 
834 				point.x += iBitmapXSize;
835 
836 				wxString userName;
837 #ifdef ENABLE_IP2COUNTRY
838 				if (theApp->amuledlg->m_IP2Country->IsEnabled() && thePrefs::IsGeoIPEnabled()) {
839 					// Draw the flag. Size can't be precached.
840 					const CountryData& countrydata = theApp->amuledlg->m_IP2Country->GetCountryData(client.GetFullIP());
841 
842 					realY = point.y + (rect.GetHeight() - countrydata.Flag.GetHeight())/2 + 1 /* floor() */;
843 
844 					dc->DrawBitmap(countrydata.Flag,
845 						point.x, realY,
846 						true);
847 
848 					userName << countrydata.Name;
849 
850 					userName << wxT(" - ");
851 
852 					point.x += countrydata.Flag.GetWidth() + 2 /*Padding*/;
853 				}
854 #endif // ENABLE_IP2COUNTRY
855 				if (client.GetUserName().IsEmpty()) {
856 					userName << wxT("?");
857 				} else {
858 					userName << client.GetUserName();
859 				}
860 
861 				dc->DrawText(userName, point.x, rect.GetY() + iTextOffset);
862 			}
863 			break;
864 
865 		case ColumnUserDownloaded:
866 			if (item->GetType() != A4AF_SOURCE && client.GetTransferredDown()) {
867 				buffer = CastItoXBytes(client.GetTransferredDown());
868 				dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
869 			}
870 			break;
871 		case ColumnUserUploaded:
872 			if (item->GetType() != A4AF_SOURCE && client.GetTransferredUp()) {
873 				buffer = CastItoXBytes(client.GetTransferredUp());
874 				dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
875 			}
876 			break;
877 		case ColumnUserSpeedDown:
878 			if (item->GetType() != A4AF_SOURCE && client.GetKBpsDown() > 0.001) {
879 				buffer = CFormat(_("%.1f kB/s")) % client.GetKBpsDown();
880 				dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
881 			}
882 			break;
883 		case ColumnUserSpeedUp:
884 			// Datarate is in bytes.
885 			if (item->GetType() != A4AF_SOURCE && client.GetUploadDatarate() > 1024) {
886 				buffer = CFormat(_("%.1f kB/s")) % (client.GetUploadDatarate() / 1024.0);
887 				dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
888 			}
889 			break;
890 		case ColumnUserProgress:
891 			if ( thePrefs::ShowProgBar() ) {
892 				int iWidth = rect.GetWidth() - 2;
893 				int iHeight = rect.GetHeight() - 2;
894 
895 				// don't draw Text beyond the bar
896 				dc->SetClippingRegion(rect.GetX(), rect.GetY() + 1, iWidth, iHeight);
897 
898 				if ( item->GetType() != A4AF_SOURCE ) {
899 					uint32 dwTicks = GetTickCount();
900 
901 					wxMemoryDC cdcStatus;
902 
903 					if ( item->dwUpdated < dwTicks || !item->status ||
904 							iWidth != item->status->GetWidth() ) {
905 
906 						if (item->status == NULL) {
907 							item->status = new wxBitmap(iWidth, iHeight);
908 						} else if ( item->status->GetWidth() != iWidth ) {
909 							// Only recreate if size has changed
910 							item->status->Create(iWidth, iHeight);
911 						}
912 
913 						cdcStatus.SelectObject(*(item->status));
914 
915 						if ( thePrefs::UseFlatBar() ) {
916 							DrawSourceStatusBar( client, &cdcStatus,
917 								wxRect(0, 0, iWidth, iHeight), true);
918 						} else {
919 							DrawSourceStatusBar( client, &cdcStatus,
920 								wxRect(1, 1, iWidth - 2, iHeight - 2), false);
921 
922 							// Draw black border
923 							cdcStatus.SetPen( *wxBLACK_PEN );
924 							cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
925 							cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
926 						}
927 
928 						// Plus ten seconds
929 						item->dwUpdated = dwTicks + 10000;
930 					} else {
931 						cdcStatus.SelectObject(*(item->status));
932 					}
933 
934 					dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
935 				} else {
936 					wxString a4af;
937 					CPartFile* p = client.GetRequestFile();
938 					if (p) {
939 						a4af = p->GetFileName().GetPrintable();
940 					} else {
941 						a4af = wxT("?");
942 					}
943 					buffer = CFormat(wxT("%s: %s")) % _("A4AF") % a4af;
944 
945 					int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
946 					int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
947 
948 					wxCoord txtwidth, txtheight;
949 
950 					dc->GetTextExtent(buffer, &txtwidth, &txtheight);
951 
952 					dc->SetTextForeground(*wxBLACK);
953 					dc->DrawText(buffer, wxMax(rect.GetX() + 2, midx - (txtwidth >> 1)), midy - (txtheight >> 1));
954 
955 					// Draw black border
956 					dc->SetPen( *wxBLACK_PEN );
957 					dc->SetBrush( *wxTRANSPARENT_BRUSH );
958 					dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
959 				}
960 			}
961 			break;
962 
963 		case ColumnUserAvailable: {
964 				if ( client.GetUpPartCount() ) {
965 					DrawStatusBar( client, dc, rect );
966 				}
967 				break;
968 			}
969 
970 		case ColumnUserVersion: {
971 				dc->DrawText(client.GetClientVerString(), rect.GetX(), rect.GetY() + iTextOffset);
972 				break;
973 			}
974 
975 		case ColumnUserQueueRankRemote: {
976 			sint16 qrDiff = 0;
977 			wxColour savedColour = dc->GetTextForeground();
978 			// We only show the queue rank for sources actually queued for that file
979 			if (	item->GetType() != A4AF_SOURCE && client.GetDownloadState() == DS_ONQUEUE ) {
980 				if (client.IsRemoteQueueFull()) {
981 					buffer = _("Queue Full");
982 				} else {
983 					uint16 rank = client.GetRemoteQueueRank();
984 					if (rank) {
985 						qrDiff = rank - client.GetOldRemoteQueueRank();
986 						if (qrDiff == rank) {
987 							qrDiff = 0;
988 						}
989 						if ( qrDiff < 0 ) {
990 							dc->SetTextForeground(*wxBLUE);
991 						}
992 						if ( qrDiff > 0 ) {
993 							dc->SetTextForeground(*wxRED);
994 						}
995 						buffer = CFormat(_("On Queue: %u (%i)")) % rank % qrDiff;
996 					} else {
997 						buffer = _("On Queue");
998 					}
999 				}
1000 			} else {
1001 				if (item->GetType() != A4AF_SOURCE) {
1002 					buffer = DownloadStateToStr( client.GetDownloadState(),
1003 						client.IsRemoteQueueFull() );
1004 				} else {
1005 					buffer = _("Asked for another file");
1006 					if (	client.GetRequestFile() &&
1007 						client.GetRequestFile()->GetFileName().IsOk()) {
1008 						buffer += CFormat(wxT(" (%s)"))
1009 							% client.GetRequestFile()->GetFileName();
1010 					}
1011 				}
1012 			}
1013 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1014 			if (qrDiff) {
1015 				dc->SetTextForeground(savedColour);
1016 			}
1017 			break;
1018 		}
1019 		case ColumnUserQueueRankLocal:
1020 			if (item->GetType() != A4AF_SOURCE) {
1021 				if (client.GetUploadState() == US_ONUPLOADQUEUE ) {
1022 					uint16 nRank = client.GetUploadQueueWaitingPosition();
1023 					if (nRank == 0) {
1024 						buffer = _("Waiting for upload slot");
1025 					} else {
1026 						buffer = CFormat(_("On Queue: %u")) % nRank;
1027 					}
1028 				} else if (client.GetUploadState() == US_UPLOADING) {
1029 					buffer = _("Uploading");
1030 				} else {
1031 					buffer = _("None");
1032 				}
1033 			} else {
1034 				buffer = _("Asked for another file");
1035 			}
1036 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1037 			break;
1038 		// Source comes from?
1039 		case ColumnUserOrigin: {
1040 			buffer = wxGetTranslation(OriginToText(client.GetSourceFrom()));
1041 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1042 			break;
1043 		}
1044 		// Local file name to identify on multi select
1045 		case ColumnUserFileNameDownload: {
1046 			const CPartFile * pf = client.GetRequestFile();
1047 			if (pf) {
1048 				buffer = pf->GetFileName().GetPrintable();
1049 			} else {
1050 				buffer = _("Unknown");
1051 				buffer = wxT("[") + buffer + wxT("]");
1052 			}
1053 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1054 			break;
1055 		}
1056 		case ColumnUserFileNameUpload: {
1057 			const CKnownFile * kf = client.GetUploadFile();
1058 			if (kf) {
1059 				buffer = kf->GetFileName().GetPrintable();
1060 			} else {
1061 				buffer = _("Unknown");
1062 				buffer = wxT("[") + buffer + wxT("]");
1063 			}
1064 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1065 			break;
1066 		}
1067 		case ColumnUserFileNameDownloadRemote: {
1068 			bool nameMissmatch = false;
1069 			wxColour savedColour = dc->GetTextForeground();
1070 			if (client.GetClientFilename().IsEmpty() || item->GetType() == A4AF_SOURCE) {
1071 				buffer = _("Unknown");
1072 				buffer = wxT("[") + buffer + wxT("]");
1073 			} else {
1074 				buffer = client.GetClientFilename();
1075 				const CPartFile * pf = client.GetRequestFile();
1076 				if (pf && (pf->GetFileName().GetPrintable().CmpNoCase(buffer) != 0)) {
1077 					nameMissmatch = true;
1078 					dc->SetTextForeground(*wxRED);
1079 				}
1080 			}
1081 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1082 			if (nameMissmatch) {
1083 				dc->SetTextForeground(savedColour);
1084 			}
1085 			break;
1086 		}
1087 		case ColumnUserSharedFiles: {
1088 			if(client.HasDisabledSharedFiles()) {
1089 				buffer = _("No");
1090 			} else {
1091 				buffer = _("Yes");
1092 			}
1093 			dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1094 			break;
1095 		}
1096 	}
1097 }
1098 
SortProc(wxUIntPtr param1,wxUIntPtr param2,long sortData)1099 int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1100 {
1101 	ClientCtrlItem_Struct* item1 = reinterpret_cast<ClientCtrlItem_Struct*>(param1);
1102 	ClientCtrlItem_Struct* item2 = reinterpret_cast<ClientCtrlItem_Struct*>(param2);
1103 
1104 	int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1105 	sortData &= CMuleListCtrl::COLUMN_MASK;
1106 	int comp = 0;
1107 
1108 	// Two sources, some different possibilites
1109 	// Avilable sources first, if we have both an
1110 	// available and an unavailable
1111 	comp = ( item2->GetType() - item1->GetType() );
1112 
1113 	if (comp) {
1114 			// unavailable and available. The order is fixed regardless of sort-order.
1115 		return comp;
1116 	} else {
1117 		comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
1118 	}
1119 
1120 	// We modify the result so that it matches with ascending or decending
1121 	return sortMod * comp;
1122 }
1123 
Compare(const CClientRef & client1,const CClientRef & client2,long lParamSort)1124 int CGenericClientListCtrl::Compare(
1125 	const CClientRef& client1, const CClientRef& client2, long lParamSort)
1126 {
1127 	switch (lParamSort) {
1128 		// Sort by name
1129 		case ColumnUserName:
1130 			return CmpAny( client1.GetUserName(), client2.GetUserName() );
1131 
1132 		// Sort by transferred in the following fields
1133 		case ColumnUserDownloaded:
1134 			return CmpAny( client1.GetTransferredDown(), client2.GetTransferredDown() );
1135 
1136 		// Sort by transferred in the following fields
1137 		case ColumnUserUploaded:
1138 			return CmpAny( client1.GetTransferredUp(), client2.GetTransferredUp() );
1139 
1140 		// Sort by speed
1141 		case ColumnUserSpeedDown:
1142 			return CmpAny( client1.GetKBpsDown(), client2.GetKBpsDown() );
1143 
1144 		// Sort by speed
1145 		case ColumnUserSpeedUp:
1146 			return CmpAny( client1.GetUploadDatarate(), client2.GetUploadDatarate() );
1147 
1148 		// Sort by parts offered
1149 		case ColumnUserProgress:
1150 			return CmpAny(
1151 				client1.GetAvailablePartCount(),
1152 				client2.GetAvailablePartCount() );
1153 
1154 		// Sort by client version
1155 		case ColumnUserVersion: {
1156 			int cmp = client1.GetSoftStr().Cmp(client2.GetSoftStr());
1157 
1158 			if (cmp == 0) {
1159 				cmp = CmpAny(client1.GetVersion(), client2.GetVersion());
1160 			}
1161 			if (cmp == 0) {
1162 				cmp = client1.GetClientModString().Cmp(client2.GetClientModString());
1163 			}
1164 			return cmp;
1165 		}
1166 
1167 		// Sort by Queue-Rank
1168 		case ColumnUserQueueRankRemote: {
1169 			// This will sort by download state: Downloading, OnQueue, Connecting ...
1170 			// However, Asked For Another will always be placed last, due to
1171 			// sorting in SortProc
1172 			if ( client1.GetDownloadState() != client2.GetDownloadState() ) {
1173 				return client1.GetDownloadState() - client2.GetDownloadState();
1174 			}
1175 
1176 			// Placing items on queue before items on full queues
1177 			if ( client1.IsRemoteQueueFull() ) {
1178 				if ( client2.IsRemoteQueueFull() ) {
1179 					return 0;
1180 				} else {
1181 					return  1;
1182 				}
1183 			} else if ( client2.IsRemoteQueueFull() ) {
1184 				return -1;
1185 			} else {
1186 				if ( client1.GetRemoteQueueRank() ) {
1187 					if ( client2.GetRemoteQueueRank() ) {
1188 						return CmpAny(
1189 							client1.GetRemoteQueueRank(),
1190 							client2.GetRemoteQueueRank() );
1191 					} else {
1192 						return -1;
1193 					}
1194 				} else {
1195 					if ( client2.GetRemoteQueueRank() ) {
1196 						return  1;
1197 					} else {
1198 						return  0;
1199 					}
1200 				}
1201 			}
1202 		}
1203 
1204 		// Sort by Queue-Rank
1205 		case ColumnUserQueueRankLocal: {
1206 			// This will sort by download state: Downloading, OnQueue, Connecting ...
1207 			// However, Asked For Another will always be placed last, due to
1208 			// sorting in SortProc
1209 			if ( client1.GetUploadState() != client2.GetUploadState() ) {
1210 				return client1.GetUploadState() - client2.GetUploadState();
1211 			}
1212 
1213 			uint16 rank1 = client1.GetUploadQueueWaitingPosition();
1214 			uint16 rank2 = client2.GetUploadQueueWaitingPosition();
1215 			// Placing items on queue before items on full queues
1216 			if ( !rank1 ) {
1217 				if ( !rank2 ) {
1218 					return 0;
1219 				} else {
1220 					return 1;
1221 				}
1222 			} else if ( !rank2 ) {
1223 				return -1;
1224 			} else {
1225 				if ( rank1 ) {
1226 					if ( rank2 ) {
1227 						return CmpAny(
1228 							rank1,
1229 							rank2 );
1230 					} else {
1231 						return -1;
1232 					}
1233 				} else {
1234 					if ( rank2 ) {
1235 						return  1;
1236 					} else {
1237 						return  0;
1238 					}
1239 				}
1240 			}
1241 		}
1242 
1243 		// Source of source ;)
1244 		case ColumnUserOrigin:
1245 			return CmpAny(client1.GetSourceFrom(), client2.GetSourceFrom());
1246 
1247 		// Sort by local filename (download)
1248 		case ColumnUserFileNameDownload: {
1249 			wxString buffer1, buffer2;
1250 			const CPartFile * pf1 = client1.GetRequestFile();
1251 			if (pf1) {
1252 				buffer1 = pf1->GetFileName().GetPrintable();
1253 			}
1254 			const CPartFile * pf2 = client2.GetRequestFile();
1255 			if (pf2) {
1256 				buffer2 = pf2->GetFileName().GetPrintable();
1257 			}
1258 			return CmpAny(buffer1, buffer2);
1259 		}
1260 
1261 		// Sort by local filename (upload)
1262 		case ColumnUserFileNameUpload: {
1263 			wxString buffer1, buffer2;
1264 			const CKnownFile * kf1 = client1.GetUploadFile();
1265 			if (kf1) {
1266 				buffer1 = kf1->GetFileName().GetPrintable();
1267 			}
1268 			const CKnownFile * kf2 = client2.GetUploadFile();
1269 			if (kf2) {
1270 				buffer2 = kf2->GetFileName().GetPrintable();
1271 			}
1272 			return CmpAny(buffer1, buffer2);
1273 		}
1274 
1275 		case ColumnUserFileNameDownloadRemote: {
1276 			return CmpAny(client1.GetClientFilename(), client2.GetClientFilename());
1277 		}
1278 
1279 		case ColumnUserSharedFiles: {
1280 			return CmpAny(client1.HasDisabledSharedFiles(), client2.HasDisabledSharedFiles());
1281 		}
1282 
1283 		default:
1284 			return 0;
1285 	}
1286 }
1287 
1288 
ShowSourcesCount(int diff)1289 void CGenericClientListCtrl::ShowSourcesCount( int diff )
1290 {
1291 	m_clientcount += diff;
1292 	wxStaticText* label = CastByID( ID_CLIENTCOUNT, GetParent(), wxStaticText );
1293 
1294 	if (label) {
1295 		label->SetLabel(CFormat(wxT("%i")) % m_clientcount);
1296 		label->GetParent()->Layout();
1297 	}
1298 }
1299 
1300 static const CMuleColour crBoth(0, 192, 0);
1301 static const CMuleColour crFlatBoth(0, 150, 0);
1302 
1303 static const CMuleColour crNeither(240, 240, 240);
1304 static const CMuleColour crFlatNeither(224, 224, 224);
1305 
1306 static const CMuleColour crClientOnly(104, 104, 104);
1307 static const CMuleColour crFlatClientOnly(0, 0, 0);
1308 
1309 static const CMuleColour crPending(255, 208, 0);
1310 static const CMuleColour crNextPending(255, 255, 100);
1311 
DrawSourceStatusBar(const CClientRef & source,wxDC * dc,const wxRect & rect,bool bFlat) const1312 void CGenericClientListCtrl::DrawSourceStatusBar(
1313 	const CClientRef& source, wxDC* dc, const wxRect& rect, bool bFlat) const
1314 {
1315 	static CBarShader s_StatusBar(16);
1316 
1317 	CPartFile* reqfile = source.GetRequestFile();
1318 
1319 	s_StatusBar.SetHeight(rect.height);
1320 	s_StatusBar.SetWidth(rect.width);
1321 	s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1322 	const BitVector& partStatus = source.GetPartStatus();
1323 
1324 	if (reqfile && reqfile->GetPartCount() == partStatus.size()) {
1325 		s_StatusBar.SetFileSize(reqfile->GetFileSize());
1326 		uint16 lastDownloadingPart = source.GetDownloadState() == DS_DOWNLOADING
1327 									? source.GetLastDownloadingPart() : 0xffff;
1328 		uint16 nextRequestedPart = source.GetNextRequestedPart();
1329 
1330 		for ( uint16 i = 0; i < partStatus.size(); i++ ) {
1331 			uint64 uStart = PARTSIZE * i;
1332 			uint64 uEnd = uStart + reqfile->GetPartSize(i) - 1;
1333 
1334 			CMuleColour colour;
1335 			if (!partStatus.get(i)) {
1336 				// client does not have this part
1337 				// light grey
1338 				colour = bFlat ? crFlatNeither : crNeither;
1339 			} else if ( reqfile->IsComplete(i)) {
1340 				// completed part
1341 				// green
1342 				colour = bFlat ? crFlatBoth : crBoth;
1343 			} else if (lastDownloadingPart == i) {
1344 				// downloading part
1345 				// yellow
1346 				colour = crPending;
1347 			} else if (nextRequestedPart == i) {
1348 				// requested part
1349 				// light yellow
1350 				colour = crNextPending;
1351 			} else {
1352 				// client has this part, we need it
1353 				// black
1354 				colour = bFlat ? crFlatClientOnly : crClientOnly;
1355 			}
1356 
1357 			if ( source.GetRequestFile()->IsStopped() ) {
1358 				colour.Blend(50);
1359 			}
1360 
1361 			s_StatusBar.FillRange(uStart, uEnd, colour);
1362 		}
1363 	} else {
1364 		s_StatusBar.SetFileSize(1);
1365 		s_StatusBar.FillRange(0, 1, bFlat ? crFlatNeither : crNeither);
1366 	}
1367 
1368 	s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
1369 }
1370 
1371 static const CMuleColour crUnavailable(240, 240, 240);
1372 static const CMuleColour crFlatUnavailable(224, 224, 224);
1373 
1374 static const CMuleColour crAvailable(104, 104, 104);
1375 static const CMuleColour crFlatAvailable(0, 0, 0);
1376 
DrawStatusBar(const CClientRef & client,wxDC * dc,const wxRect & rect1) const1377 void CGenericClientListCtrl::DrawStatusBar( const CClientRef& client, wxDC* dc, const wxRect& rect1 ) const
1378 {
1379 	wxRect rect = rect1;
1380 	rect.y		+= 1;
1381 	rect.height	-= 2;
1382 
1383 	wxPen   old_pen   = dc->GetPen();
1384 	wxBrush old_brush = dc->GetBrush();
1385 	bool bFlat = thePrefs::UseFlatBar();
1386 
1387 	wxRect barRect = rect;
1388 	if (!bFlat) { // round bar has a black border, the bar itself is 1 pixel less on each border
1389 		barRect.x ++;
1390 		barRect.y ++;
1391 		barRect.height -= 2;
1392 		barRect.width -= 2;
1393 	}
1394 	static CBarShader s_StatusBar(16);
1395 
1396 	uint32 partCount = client.GetUpPartCount();
1397 
1398 	// Seems the partfile in the client object is not necessarily valid when bar is drawn for the first time.
1399 	// Keep it simple and make all parts same size.
1400 	s_StatusBar.SetFileSize(partCount * PARTSIZE);
1401 	s_StatusBar.SetHeight(barRect.height);
1402 	s_StatusBar.SetWidth(barRect.width);
1403 	s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1404 
1405 	uint64 uEnd = 0;
1406 	for ( uint64 i = 0; i < partCount; i++ ) {
1407 		uint64 uStart = PARTSIZE * i;
1408 		uEnd = uStart + PARTSIZE - 1;
1409 
1410 		s_StatusBar.FillRange(uStart, uEnd, client.IsUpPartAvailable(i) ? (bFlat ? crFlatAvailable : crAvailable) : (bFlat ? crFlatUnavailable : crUnavailable));
1411 	}
1412 	// fill the rest (if partStatus is empty)
1413 	s_StatusBar.FillRange(uEnd + 1, partCount * PARTSIZE - 1, bFlat ? crFlatUnavailable : crUnavailable);
1414 	s_StatusBar.Draw(dc, barRect.x, barRect.y, bFlat);
1415 
1416 	if (!bFlat) {
1417 		// Draw black border
1418 		dc->SetPen( *wxBLACK_PEN );
1419 		dc->SetBrush( *wxTRANSPARENT_BRUSH );
1420 		dc->DrawRectangle(rect);
1421 	}
1422 
1423 	dc->SetPen( old_pen );
1424 	dc->SetBrush( old_brush );
1425 }
1426 
1427 // File_checked_for_headers
1428