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 53struct ClientCtrlItem_Struct 54{ 55 ClientCtrlItem_Struct() 56 : dwUpdated(0), 57 status(NULL), 58 m_owner(NULL), 59 m_type(UNAVAILABLE_SOURCE) 60 { } 61 62 ~ClientCtrlItem_Struct() { 63 delete status; 64 } 65 66 SourceItemType GetType() const { 67 return m_type; 68 } 69 70 CKnownFile* GetOwner() const { 71 return m_owner; 72 } 73 74 75 CClientRef& GetSource() { 76 return m_sourceValue; 77 } 78 79 void SetContents(CKnownFile* owner, const CClientRef& source, SourceItemType type) { 80 m_owner = owner; 81 m_sourceValue = source; 82 m_type = type; 83 } 84 85 void SetType(SourceItemType type) { m_type = type; } 86 87 uint32 dwUpdated; 88 wxBitmap* status; 89 90private: 91 CKnownFile* m_owner; 92 CClientRef m_sourceValue; 93 SourceItemType m_type; 94}; 95 96#define m_ImageList theApp->amuledlg->m_imagelist 97 98BEGIN_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 ) 111END_EVENT_TABLE() 112 113//! This listtype is used when gathering the selected items. 114typedef std::list<ClientCtrlItem_Struct*> ItemList; 115 116CGenericClientListCtrl::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: 121CMuleListCtrl( parent, winid, pos, size, style | wxLC_OWNERDRAW | wxLC_VRULES | wxLC_HRULES, validator, name ), 122m_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 140wxString 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 199void 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 212CGenericClientListCtrl::~CGenericClientListCtrl() 213{ 214 while ( !m_ListItems.empty() ) { 215 delete m_ListItems.begin()->second; 216 m_ListItems.erase( m_ListItems.begin() ); 217 } 218} 219void 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 231void 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 272void 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 288void 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 312void 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 __WXMSW__ 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 351void 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 */ 462ItemList 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 = (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 479void 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 494void 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 504void 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 519void 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 535void 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 556void 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 566void CGenericClientListCtrl::OnItemActivated( wxListEvent& evt ) 567{ 568 CClientDetailDialog( this, ((ClientCtrlItem_Struct*)GetItemData( evt.GetIndex()))->GetSource() ).ShowModal(); 569} 570 571 572void 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 = (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 617void 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, ((ClientCtrlItem_Struct*)GetItemData( index ))->GetSource()).ShowModal(); 626} 627 628 629void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent& event ) 630{ 631 // No actions right now. 632 //switch (event.GetKeyCode()) { 633 // default: 634 event.Skip(); 635 //} 636} 637 638 639void 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 = (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 708void 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 1099int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData) 1100{ 1101 ClientCtrlItem_Struct* item1 = (ClientCtrlItem_Struct*)param1; 1102 ClientCtrlItem_Struct* item2 = (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 1124int 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 1289void 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 1300static const CMuleColour crBoth(0, 192, 0); 1301static const CMuleColour crFlatBoth(0, 150, 0); 1302 1303static const CMuleColour crNeither(240, 240, 240); 1304static const CMuleColour crFlatNeither(224, 224, 224); 1305 1306static const CMuleColour crClientOnly(104, 104, 104); 1307static const CMuleColour crFlatClientOnly(0, 0, 0); 1308 1309static const CMuleColour crPending(255, 208, 0); 1310static const CMuleColour crNextPending(255, 255, 100); 1311 1312void 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 1371static const CMuleColour crUnavailable(240, 240, 240); 1372static const CMuleColour crFlatUnavailable(224, 224, 224); 1373 1374static const CMuleColour crAvailable(104, 104, 104); 1375static const CMuleColour crFlatAvailable(0, 0, 0); 1376 1377void 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