Always include the boost-package. Otherwise it won't get searched on a second run.
[amule.git] / src / GenericClientListCtrl.cpp
blob963e1d5ec344edccb17f67d56b3b0e23d13601bf
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.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "GenericClientListCtrl.h"
26 #include <protocol/ed2k/ClientSoftware.h>
27 #include <common/MenuIDs.h>
29 #include <common/Format.h> // Needed for CFormat
30 #include "amule.h" // Needed for theApp
31 #include "amuleDlg.h" // Needed for CamuleDlg
32 #include "BarShader.h" // Needed for CBarShader
33 #include "BitVector.h"
34 #include "ClientDetailDialog.h" // Needed for CClientDetailDialog
35 #include "ChatWnd.h" // Needed for CChatWnd
36 #include "CommentDialogLst.h" // Needed for CCommentDialogLst
37 #include "DataToText.h" // Needed for PriorityToStr
38 #include "FileDetailDialog.h" // Needed for CFileDetailDialog
39 #include "GetTickCount.h" // Needed for GetTickCount
40 #include "GuiEvents.h" // Needed for CoreNotify_*
41 #ifdef ENABLE_IP2COUNTRY
42 #include "IP2Country.h" // Needed for IP2Country
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"
53 struct ClientCtrlItem_Struct
55 ClientCtrlItem_Struct()
56 : dwUpdated(0),
57 status(NULL),
58 m_owner(NULL),
59 m_type(UNAVAILABLE_SOURCE)
60 { }
62 ~ClientCtrlItem_Struct() {
63 delete status;
66 SourceItemType GetType() const {
67 return m_type;
70 CKnownFile* GetOwner() const {
71 return m_owner;
75 CClientRef& GetSource() {
76 return m_sourceValue;
79 void SetContents(CKnownFile* owner, const CClientRef& source, SourceItemType type) {
80 m_owner = owner;
81 m_sourceValue = source;
82 m_type = type;
85 void SetType(SourceItemType type) { m_type = type; }
87 uint32 dwUpdated;
88 wxBitmap* status;
90 private:
91 CKnownFile* m_owner;
92 CClientRef m_sourceValue;
93 SourceItemType m_type;
96 #define m_ImageList theApp->amuledlg->m_imagelist
98 BEGIN_EVENT_TABLE(CGenericClientListCtrl, CMuleListCtrl)
99 EVT_LIST_ITEM_ACTIVATED(wxID_ANY, CGenericClientListCtrl::OnItemActivated)
100 EVT_LIST_ITEM_RIGHT_CLICK(wxID_ANY, CGenericClientListCtrl::OnMouseRightClick)
101 EVT_LIST_ITEM_MIDDLE_CLICK(wxID_ANY, CGenericClientListCtrl::OnMouseMiddleClick)
103 EVT_CHAR( CGenericClientListCtrl::OnKeyPressed )
105 EVT_MENU( MP_CHANGE2FILE, CGenericClientListCtrl::OnSwapSource )
106 EVT_MENU( MP_SHOWLIST, CGenericClientListCtrl::OnViewFiles )
107 EVT_MENU( MP_ADDFRIEND, CGenericClientListCtrl::OnAddFriend )
108 EVT_MENU( MP_FRIENDSLOT, CGenericClientListCtrl::OnSetFriendslot )
109 EVT_MENU( MP_SENDMESSAGE, CGenericClientListCtrl::OnSendMessage )
110 EVT_MENU( MP_DETAIL, CGenericClientListCtrl::OnViewClientInfo )
111 END_EVENT_TABLE()
113 //! This listtype is used when gathering the selected items.
114 typedef std::list<ClientCtrlItem_Struct*> ItemList;
116 CGenericClientListCtrl::CGenericClientListCtrl(
117 const wxString& tablename,
118 wxWindow *parent, wxWindowID winid, const wxPoint& pos, const wxSize& size,
119 long style, const wxValidator& validator, const wxString& name )
121 CMuleListCtrl( parent, winid, pos, size, style | wxLC_OWNERDRAW | wxLC_VRULES | wxLC_HRULES, validator, name ),
122 m_columndata(0, NULL)
124 // Setting the sorter function must be done in the derived class, to use the translation function correctly.
125 // SetSortFunc( SortProc );
127 // Set the table-name (for loading and saving preferences).
128 SetTableName( tablename );
130 m_menu = NULL;
131 m_showing = false;
133 m_hilightBrush = CMuleColour(wxSYS_COLOUR_HIGHLIGHT).Blend(125).GetBrush();
135 m_hilightUnfocusBrush = CMuleColour(wxSYS_COLOUR_BTNSHADOW).Blend(125).GetBrush();
137 m_clientcount = 0;
140 wxString CGenericClientListCtrl::TranslateCIDToName(GenericColumnEnum cid)
142 wxString name;
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;
196 return name;
199 void CGenericClientListCtrl::InitColumnData()
201 if (!m_columndata.n_columns) {
202 throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
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));
209 LoadSettings();
212 CGenericClientListCtrl::~CGenericClientListCtrl()
214 while ( !m_ListItems.empty() ) {
215 delete m_ListItems.begin()->second;
216 m_ListItems.erase( m_ListItems.begin() );
219 void CGenericClientListCtrl::RawAddSource(CKnownFile* owner, CClientRef source, SourceItemType type)
221 ClientCtrlItem_Struct* newitem = new ClientCtrlItem_Struct;
222 newitem->SetContents(owner, source, type);
224 m_ListItems.insert( ListItemsPair(source.ECID(), newitem) );
226 long item = InsertItem( GetItemCount(), wxEmptyString );
227 SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
228 SetItemBackgroundColour( item, GetBackgroundColour() );
231 void CGenericClientListCtrl::AddSource(CKnownFile* owner, const CClientRef& source, SourceItemType type)
233 wxCHECK_RET(owner, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
234 wxCHECK_RET(source.IsLinked(), wxT("Unlinked source in CGenericClientListCtrl::AddSource"));
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;
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);
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;
261 if ( bFound ) {
262 return;
265 if ( std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), owner) ) {
266 RawAddSource(owner, source, type);
268 ShowSourcesCount( 1 );
272 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator& it)
274 ClientCtrlItem_Struct* item = it->second;
276 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
278 if ( index > -1 ) {
279 DeleteItem( index );
282 delete item;
284 // Remove it from the m_ListItems
285 m_ListItems.erase( it );
288 void CGenericClientListCtrl::RemoveSource(uint32 source, const CKnownFile* owner)
290 // A NULL owner means remove it no matter what.
291 wxCHECK_RET(source, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
293 // Retrieve all entries matching the source
294 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
296 int removedItems = 0;
298 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; /* no ++, it happens later */) {
299 ListItems::iterator tmp = it++;
301 if ( owner == NULL || owner == tmp->second->GetOwner() ) {
303 RawRemoveSource(tmp);
305 ++removedItems;
309 ShowSourcesCount((-1) * removedItems);
312 void CGenericClientListCtrl::UpdateItem(uint32 toupdate, SourceItemType type)
314 // Retrieve all entries matching the source
315 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
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();
322 #ifndef __WINDOWS__
323 // Get visible lines if we need them
324 GetVisibleLines( &first, &last );
325 #endif
327 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
328 ClientCtrlItem_Struct* item = it->second;
330 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
332 if ((type == A4AF_SOURCE) &&
333 item->GetSource().GetRequestFile()
334 && std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), item->GetSource().GetRequestFile())) {
336 item->SetType(AVAILABLE_SOURCE);
337 } else {
338 item->SetType(type);
341 item->dwUpdated = 0;
343 // Only update visible lines
344 if ( index >= first && index <= last) {
345 RefreshItem( index );
351 void CGenericClientListCtrl::ShowSources( const CKnownFileVector& files )
353 Freeze();
355 // The stored vector is sorted, as is the received one, so we can use binary_search
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);
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.
368 for (unsigned i = 0; i < files.size(); ++i) {
369 SetShowSources(files[i], true);
372 // We must cleanup sources that are not in the received files.
374 int itemDiff = 0;
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;
386 for (unsigned i = 0; i < files.size(); ++i) {
388 // Only those that weren't showing already
389 if (!std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), files[i])) {
391 CKnownFile* file = files[i];
393 wxASSERT_MSG(file, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
395 if (file) {
397 CKnownFile::SourceSet::const_iterator it;
399 if (IsShowingDownloadSources()) {
400 const CKnownFile::SourceSet& normSources = (dynamic_cast<CPartFile*>( file ))->GetSourceList();
401 const CKnownFile::SourceSet& a4afSources = (dynamic_cast<CPartFile*>( file ))->GetA4AFList();
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;
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;
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;
447 m_knownfiles = files;
449 ShowSourcesCount( itemDiff );
451 SortList();
453 Thaw();
457 * Helper-function: This function is used to gather selected items.
459 * @param list A pointer to the list to gather items from.
460 * @return A list containing the selected items of the choosen types.
462 static ItemList GetSelectedItems( CGenericClientListCtrl* list )
464 ItemList results;
466 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
468 while ( index > -1 ) {
469 ClientCtrlItem_Struct* item = reinterpret_cast<ClientCtrlItem_Struct*>(list->GetItemData( index ));
471 results.push_back( item );
473 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
476 return results;
479 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
481 ItemList sources = ::GetSelectedItems( this );
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;
489 (*it)->GetSource().SwapToAnotherFile( true, false, false, dynamic_cast<CPartFile*>(kf));
494 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
496 ItemList sources = ::GetSelectedItems( this );
498 if ( sources.size() == 1 ) {
499 sources.front()->GetSource().RequestSharedFileList();
504 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
506 ItemList sources = ::GetSelectedItems( this );
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);
519 void CGenericClientListCtrl::OnSetFriendslot(wxCommandEvent& evt)
521 ItemList sources = ::GetSelectedItems( this );
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;
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);
535 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
537 ItemList sources = ::GetSelectedItems( this );
539 if ( sources.size() == 1 ) {
540 CClientRef & source = (sources.front())->GetSource();
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());
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);
556 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
558 ItemList sources = ::GetSelectedItems( this );
560 if ( sources.size() == 1 ) {
561 CClientDetailDialog( this, sources.front()->GetSource() ).ShowModal();
566 void CGenericClientListCtrl::OnItemActivated( wxListEvent& evt )
568 CClientDetailDialog( this, reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( evt.GetIndex()))->GetSource() ).ShowModal();
572 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent& evt)
574 long index = CheckSelection(evt);
575 if (index < 0) {
576 return;
579 delete m_menu;
580 m_menu = NULL;
582 ClientCtrlItem_Struct* item = reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( index ));
583 CClientRef& client = item->GetSource();
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"));
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);
597 m_menu->Append(MP_SHOWLIST, _("View Files"));
598 m_menu->Append(MP_SENDMESSAGE, _("Send message"));
600 m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
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));
607 m_menu->Enable(MP_SHOWLIST, !client.HasDisabledSharedFiles());
609 PopupMenu(m_menu, evt.GetPoint());
611 delete m_menu;
612 m_menu = NULL;
617 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent& evt)
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;
625 CClientDetailDialog(this, reinterpret_cast<ClientCtrlItem_Struct*>(GetItemData( index ))->GetSource()).ShowModal();
629 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent& event )
631 // No actions right now.
632 //switch (event.GetKeyCode()) {
633 // default:
634 event.Skip();
639 void CGenericClientListCtrl::OnDrawItem(
640 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
642 // Don't do any drawing if there's nobody to see it.
643 if ( !theApp->amuledlg->IsDialogVisible( GetParentDialog() ) ) {
644 return;
647 ClientCtrlItem_Struct* content = reinterpret_cast<ClientCtrlItem_Struct *>(GetItemData(item));
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();
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);
667 dc->SetBrush( dc->GetBackground() );
669 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
671 dc->SetPen(*wxTRANSPARENT_PEN);
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;
686 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
688 for (int i = 0; i < GetColumnCount(); ++i) {
690 int columnwidth = GetColumnWidth(i);
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;
697 GenericColumnEnum cid = m_columndata.columns[i].cid;
699 // Draw the item
700 DrawClientItem(dc, cid, target_rec, content, iTextOffset, imageListBitmapYOffset, imageListBitmapXSize);
703 // Increment to the next column
704 cur_rec.x += columnwidth;
708 void CGenericClientListCtrl::DrawClientItem(wxDC* dc, int nColumn, const wxRect& rect, ClientCtrlItem_Struct* item, int iTextOffset, int iBitmapOffset, int iBitmapXSize ) const
710 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
711 wxString buffer;
713 const CClientRef& client = item->GetSource();
715 switch (nColumn) {
716 // Client name + various icons
717 case ColumnUserName: {
718 // Point will get shifted per drawing.
720 wxPoint point( rect.GetX(), rect.GetY() );
722 uint8 image = Client_Grey_Smiley;
724 if (item->GetType() != A4AF_SOURCE) {
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;
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;
752 } else {
753 // Default (Client_Grey_Smiley)
756 m_ImageList.Draw(image, *dc, point.x, point.y + iBitmapOffset, wxIMAGELIST_DRAW_TRANSPARENT);
758 // Next
760 point.x += iBitmapXSize;
762 uint8 clientImage = Client_Unknown;
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;
803 int realY = point.y + iBitmapOffset;
804 m_ImageList.Draw(clientImage, *dc, point.x, realY, wxIMAGELIST_DRAW_TRANSPARENT);
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);
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);
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);
832 // Next
834 point.x += iBitmapXSize;
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());
842 realY = point.y + (rect.GetHeight() - countrydata.Flag.GetHeight())/2 + 1 /* floor() */;
844 dc->DrawBitmap(countrydata.Flag,
845 point.x, realY,
846 true);
848 userName << countrydata.Name;
850 userName << wxT(" - ");
852 point.x += countrydata.Flag.GetWidth() + 2 /*Padding*/;
854 #endif // ENABLE_IP2COUNTRY
855 if (client.GetUserName().IsEmpty()) {
856 userName << wxT("?");
857 } else {
858 userName << client.GetUserName();
861 dc->DrawText(userName, point.x, rect.GetY() + iTextOffset);
863 break;
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);
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);
876 break;
877 case ColumnUserSpeedDown:
878 if (item->GetType() != A4AF_SOURCE && client.GetKBpsDown() > 0.001) {
879 if (client.GetKBpsDown() >= 1024) {
880 buffer = CFormat(_("%.1f MB/s")) % (client.GetKBpsDown() / 1024.0);
881 } else {
882 buffer = CFormat(_("%.1f kB/s")) % client.GetKBpsDown();
884 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
886 break;
887 case ColumnUserSpeedUp:
888 // Datarate is in bytes.
889 if (item->GetType() != A4AF_SOURCE && client.GetUploadDatarate() >= 1024) {
890 if (client.GetUploadDatarate() >= 1048576) {
891 buffer = CFormat(_("%.1f MB/s")) % (client.GetUploadDatarate() / 1048576.0);
892 } else {
893 buffer = CFormat(_("%.1f kB/s")) % (client.GetUploadDatarate() / 1024.0);
895 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
897 break;
898 case ColumnUserProgress:
899 if ( thePrefs::ShowProgBar() ) {
900 int iWidth = rect.GetWidth() - 2;
901 int iHeight = rect.GetHeight() - 2;
903 // don't draw Text beyond the bar
904 dc->SetClippingRegion(rect.GetX(), rect.GetY() + 1, iWidth, iHeight);
906 if ( item->GetType() != A4AF_SOURCE ) {
907 uint32 dwTicks = GetTickCount();
909 wxMemoryDC cdcStatus;
911 if ( item->dwUpdated < dwTicks || !item->status ||
912 iWidth != item->status->GetWidth() ) {
914 if (item->status == NULL) {
915 item->status = new wxBitmap(iWidth, iHeight);
916 } else if ( item->status->GetWidth() != iWidth ) {
917 // Only recreate if size has changed
918 item->status->Create(iWidth, iHeight);
921 cdcStatus.SelectObject(*(item->status));
923 if ( thePrefs::UseFlatBar() ) {
924 DrawSourceStatusBar( client, &cdcStatus,
925 wxRect(0, 0, iWidth, iHeight), true);
926 } else {
927 DrawSourceStatusBar( client, &cdcStatus,
928 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
930 // Draw black border
931 cdcStatus.SetPen( *wxBLACK_PEN );
932 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
933 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
936 // Plus ten seconds
937 item->dwUpdated = dwTicks + 10000;
938 } else {
939 cdcStatus.SelectObject(*(item->status));
942 dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
943 } else {
944 wxString a4af;
945 CPartFile* p = client.GetRequestFile();
946 if (p) {
947 a4af = p->GetFileName().GetPrintable();
948 } else {
949 a4af = wxT("?");
951 buffer = CFormat(wxT("%s: %s")) % _("A4AF") % a4af;
953 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
954 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
956 wxCoord txtwidth, txtheight;
958 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
960 dc->SetTextForeground(*wxBLACK);
961 dc->DrawText(buffer, wxMax(rect.GetX() + 2, midx - (txtwidth >> 1)), midy - (txtheight >> 1));
963 // Draw black border
964 dc->SetPen( *wxBLACK_PEN );
965 dc->SetBrush( *wxTRANSPARENT_BRUSH );
966 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
969 break;
971 case ColumnUserAvailable: {
972 if ( client.GetUpPartCount() ) {
973 DrawStatusBar( client, dc, rect );
975 break;
978 case ColumnUserVersion: {
979 dc->DrawText(client.GetClientVerString(), rect.GetX(), rect.GetY() + iTextOffset);
980 break;
983 case ColumnUserQueueRankRemote: {
984 sint16 qrDiff = 0;
985 wxColour savedColour = dc->GetTextForeground();
986 // We only show the queue rank for sources actually queued for that file
987 if ( item->GetType() != A4AF_SOURCE && client.GetDownloadState() == DS_ONQUEUE ) {
988 if (client.IsRemoteQueueFull()) {
989 buffer = _("Queue Full");
990 } else {
991 uint16 rank = client.GetRemoteQueueRank();
992 if (rank) {
993 qrDiff = rank - client.GetOldRemoteQueueRank();
994 if (qrDiff == rank) {
995 qrDiff = 0;
997 if ( qrDiff < 0 ) {
998 dc->SetTextForeground(*wxBLUE);
1000 if ( qrDiff > 0 ) {
1001 dc->SetTextForeground(*wxRED);
1003 buffer = CFormat(_("On Queue: %u (%i)")) % rank % qrDiff;
1004 } else {
1005 buffer = _("On Queue");
1008 } else {
1009 if (item->GetType() != A4AF_SOURCE) {
1010 buffer = DownloadStateToStr( client.GetDownloadState(),
1011 client.IsRemoteQueueFull() );
1012 } else {
1013 buffer = _("Asked for another file");
1014 if ( client.GetRequestFile() &&
1015 client.GetRequestFile()->GetFileName().IsOk()) {
1016 buffer += CFormat(wxT(" (%s)"))
1017 % client.GetRequestFile()->GetFileName();
1021 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1022 if (qrDiff) {
1023 dc->SetTextForeground(savedColour);
1025 break;
1027 case ColumnUserQueueRankLocal:
1028 if (item->GetType() != A4AF_SOURCE) {
1029 if (client.GetUploadState() == US_ONUPLOADQUEUE ) {
1030 uint16 nRank = client.GetUploadQueueWaitingPosition();
1031 if (nRank == 0) {
1032 buffer = _("Waiting for upload slot");
1033 } else {
1034 buffer = CFormat(_("On Queue: %u")) % nRank;
1036 } else if (client.GetUploadState() == US_UPLOADING) {
1037 buffer = _("Uploading");
1038 } else {
1039 buffer = _("None");
1041 } else {
1042 buffer = _("Asked for another file");
1044 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1045 break;
1046 // Source comes from?
1047 case ColumnUserOrigin: {
1048 buffer = wxGetTranslation(OriginToText(client.GetSourceFrom()));
1049 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1050 break;
1052 // Local file name to identify on multi select
1053 case ColumnUserFileNameDownload: {
1054 const CPartFile * pf = client.GetRequestFile();
1055 if (pf) {
1056 buffer = pf->GetFileName().GetPrintable();
1057 } else {
1058 buffer = _("Unknown");
1059 buffer = wxT("[") + buffer + wxT("]");
1061 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1062 break;
1064 case ColumnUserFileNameUpload: {
1065 const CKnownFile * kf = client.GetUploadFile();
1066 if (kf) {
1067 buffer = kf->GetFileName().GetPrintable();
1068 } else {
1069 buffer = _("Unknown");
1070 buffer = wxT("[") + buffer + wxT("]");
1072 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1073 break;
1075 case ColumnUserFileNameDownloadRemote: {
1076 bool nameMissmatch = false;
1077 wxColour savedColour = dc->GetTextForeground();
1078 if (client.GetClientFilename().IsEmpty() || item->GetType() == A4AF_SOURCE) {
1079 buffer = _("Unknown");
1080 buffer = wxT("[") + buffer + wxT("]");
1081 } else {
1082 buffer = client.GetClientFilename();
1083 const CPartFile * pf = client.GetRequestFile();
1084 if (pf && (pf->GetFileName().GetPrintable().CmpNoCase(buffer) != 0)) {
1085 nameMissmatch = true;
1086 dc->SetTextForeground(*wxRED);
1089 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1090 if (nameMissmatch) {
1091 dc->SetTextForeground(savedColour);
1093 break;
1095 case ColumnUserSharedFiles: {
1096 if(client.HasDisabledSharedFiles()) {
1097 buffer = _("No");
1098 } else {
1099 buffer = _("Yes");
1101 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1102 break;
1107 int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1109 ClientCtrlItem_Struct* item1 = reinterpret_cast<ClientCtrlItem_Struct*>(param1);
1110 ClientCtrlItem_Struct* item2 = reinterpret_cast<ClientCtrlItem_Struct*>(param2);
1112 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1113 sortData &= CMuleListCtrl::COLUMN_MASK;
1114 int comp = 0;
1116 // Two sources, some different possibilites
1117 // Avilable sources first, if we have both an
1118 // available and an unavailable
1119 comp = ( item2->GetType() - item1->GetType() );
1121 if (comp) {
1122 // unavailable and available. The order is fixed regardless of sort-order.
1123 return comp;
1124 } else {
1125 comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
1128 // We modify the result so that it matches with ascending or decending
1129 return sortMod * comp;
1132 int CGenericClientListCtrl::Compare(
1133 const CClientRef& client1, const CClientRef& client2, long lParamSort)
1135 switch (lParamSort) {
1136 // Sort by name
1137 case ColumnUserName:
1138 return CmpAny( client1.GetUserName(), client2.GetUserName() );
1140 // Sort by transferred in the following fields
1141 case ColumnUserDownloaded:
1142 return CmpAny( client1.GetTransferredDown(), client2.GetTransferredDown() );
1144 // Sort by transferred in the following fields
1145 case ColumnUserUploaded:
1146 return CmpAny( client1.GetTransferredUp(), client2.GetTransferredUp() );
1148 // Sort by speed
1149 case ColumnUserSpeedDown:
1150 return CmpAny( client1.GetKBpsDown(), client2.GetKBpsDown() );
1152 // Sort by speed
1153 case ColumnUserSpeedUp:
1154 return CmpAny( client1.GetUploadDatarate(), client2.GetUploadDatarate() );
1156 // Sort by parts offered
1157 case ColumnUserProgress:
1158 return CmpAny(
1159 client1.GetAvailablePartCount(),
1160 client2.GetAvailablePartCount() );
1162 // Sort by client version
1163 case ColumnUserVersion: {
1164 int cmp = client1.GetSoftStr().Cmp(client2.GetSoftStr());
1166 if (cmp == 0) {
1167 cmp = CmpAny(client1.GetVersion(), client2.GetVersion());
1169 if (cmp == 0) {
1170 cmp = client1.GetClientModString().Cmp(client2.GetClientModString());
1172 return cmp;
1175 // Sort by Queue-Rank
1176 case ColumnUserQueueRankRemote: {
1177 // This will sort by download state: Downloading, OnQueue, Connecting ...
1178 // However, Asked For Another will always be placed last, due to
1179 // sorting in SortProc
1180 if ( client1.GetDownloadState() != client2.GetDownloadState() ) {
1181 return client1.GetDownloadState() - client2.GetDownloadState();
1184 // Placing items on queue before items on full queues
1185 if ( client1.IsRemoteQueueFull() ) {
1186 if ( client2.IsRemoteQueueFull() ) {
1187 return 0;
1188 } else {
1189 return 1;
1191 } else if ( client2.IsRemoteQueueFull() ) {
1192 return -1;
1193 } else {
1194 if ( client1.GetRemoteQueueRank() ) {
1195 if ( client2.GetRemoteQueueRank() ) {
1196 return CmpAny(
1197 client1.GetRemoteQueueRank(),
1198 client2.GetRemoteQueueRank() );
1199 } else {
1200 return -1;
1202 } else {
1203 if ( client2.GetRemoteQueueRank() ) {
1204 return 1;
1205 } else {
1206 return 0;
1212 // Sort by Queue-Rank
1213 case ColumnUserQueueRankLocal: {
1214 // This will sort by download state: Downloading, OnQueue, Connecting ...
1215 // However, Asked For Another will always be placed last, due to
1216 // sorting in SortProc
1217 if ( client1.GetUploadState() != client2.GetUploadState() ) {
1218 return client1.GetUploadState() - client2.GetUploadState();
1221 uint16 rank1 = client1.GetUploadQueueWaitingPosition();
1222 uint16 rank2 = client2.GetUploadQueueWaitingPosition();
1223 // Placing items on queue before items on full queues
1224 if ( !rank1 ) {
1225 if ( !rank2 ) {
1226 return 0;
1227 } else {
1228 return 1;
1230 } else if ( !rank2 ) {
1231 return -1;
1232 } else {
1233 if ( rank1 ) {
1234 if ( rank2 ) {
1235 return CmpAny(
1236 rank1,
1237 rank2 );
1238 } else {
1239 return -1;
1241 } else {
1242 if ( rank2 ) {
1243 return 1;
1244 } else {
1245 return 0;
1251 // Source of source ;)
1252 case ColumnUserOrigin:
1253 return CmpAny(client1.GetSourceFrom(), client2.GetSourceFrom());
1255 // Sort by local filename (download)
1256 case ColumnUserFileNameDownload: {
1257 wxString buffer1, buffer2;
1258 const CPartFile * pf1 = client1.GetRequestFile();
1259 if (pf1) {
1260 buffer1 = pf1->GetFileName().GetPrintable();
1262 const CPartFile * pf2 = client2.GetRequestFile();
1263 if (pf2) {
1264 buffer2 = pf2->GetFileName().GetPrintable();
1266 return CmpAny(buffer1, buffer2);
1269 // Sort by local filename (upload)
1270 case ColumnUserFileNameUpload: {
1271 wxString buffer1, buffer2;
1272 const CKnownFile * kf1 = client1.GetUploadFile();
1273 if (kf1) {
1274 buffer1 = kf1->GetFileName().GetPrintable();
1276 const CKnownFile * kf2 = client2.GetUploadFile();
1277 if (kf2) {
1278 buffer2 = kf2->GetFileName().GetPrintable();
1280 return CmpAny(buffer1, buffer2);
1283 case ColumnUserFileNameDownloadRemote: {
1284 return CmpAny(client1.GetClientFilename(), client2.GetClientFilename());
1287 case ColumnUserSharedFiles: {
1288 return CmpAny(client1.HasDisabledSharedFiles(), client2.HasDisabledSharedFiles());
1291 default:
1292 return 0;
1297 void CGenericClientListCtrl::ShowSourcesCount( int diff )
1299 m_clientcount += diff;
1300 wxStaticText* label = CastByID( ID_CLIENTCOUNT, GetParent(), wxStaticText );
1302 if (label) {
1303 label->SetLabel(CFormat(wxT("%i")) % m_clientcount);
1304 label->GetParent()->Layout();
1308 static const CMuleColour crBoth(0, 192, 0);
1309 static const CMuleColour crFlatBoth(0, 150, 0);
1311 static const CMuleColour crNeither(240, 240, 240);
1312 static const CMuleColour crFlatNeither(224, 224, 224);
1314 static const CMuleColour crClientOnly(104, 104, 104);
1315 static const CMuleColour crFlatClientOnly(0, 0, 0);
1317 static const CMuleColour crPending(255, 208, 0);
1318 static const CMuleColour crNextPending(255, 255, 100);
1320 void CGenericClientListCtrl::DrawSourceStatusBar(
1321 const CClientRef& source, wxDC* dc, const wxRect& rect, bool bFlat) const
1323 static CBarShader s_StatusBar(16);
1325 CPartFile* reqfile = source.GetRequestFile();
1327 s_StatusBar.SetHeight(rect.height);
1328 s_StatusBar.SetWidth(rect.width);
1329 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1330 const BitVector& partStatus = source.GetPartStatus();
1332 if (reqfile && reqfile->GetPartCount() == partStatus.size()) {
1333 s_StatusBar.SetFileSize(reqfile->GetFileSize());
1334 uint16 lastDownloadingPart = source.GetDownloadState() == DS_DOWNLOADING
1335 ? source.GetLastDownloadingPart() : 0xffff;
1336 uint16 nextRequestedPart = source.GetNextRequestedPart();
1338 for ( uint16 i = 0; i < partStatus.size(); i++ ) {
1339 uint64 uStart = PARTSIZE * i;
1340 uint64 uEnd = uStart + reqfile->GetPartSize(i) - 1;
1342 CMuleColour colour;
1343 if (!partStatus.get(i)) {
1344 // client does not have this part
1345 // light grey
1346 colour = bFlat ? crFlatNeither : crNeither;
1347 } else if ( reqfile->IsComplete(i)) {
1348 // completed part
1349 // green
1350 colour = bFlat ? crFlatBoth : crBoth;
1351 } else if (lastDownloadingPart == i) {
1352 // downloading part
1353 // yellow
1354 colour = crPending;
1355 } else if (nextRequestedPart == i) {
1356 // requested part
1357 // light yellow
1358 colour = crNextPending;
1359 } else {
1360 // client has this part, we need it
1361 // black
1362 colour = bFlat ? crFlatClientOnly : crClientOnly;
1365 if ( source.GetRequestFile()->IsStopped() ) {
1366 colour.Blend(50);
1369 s_StatusBar.FillRange(uStart, uEnd, colour);
1371 } else {
1372 s_StatusBar.SetFileSize(1);
1373 s_StatusBar.FillRange(0, 1, bFlat ? crFlatNeither : crNeither);
1376 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
1379 static const CMuleColour crUnavailable(240, 240, 240);
1380 static const CMuleColour crFlatUnavailable(224, 224, 224);
1382 static const CMuleColour crAvailable(104, 104, 104);
1383 static const CMuleColour crFlatAvailable(0, 0, 0);
1385 void CGenericClientListCtrl::DrawStatusBar( const CClientRef& client, wxDC* dc, const wxRect& rect1 ) const
1387 wxRect rect = rect1;
1388 rect.y += 1;
1389 rect.height -= 2;
1391 wxPen old_pen = dc->GetPen();
1392 wxBrush old_brush = dc->GetBrush();
1393 bool bFlat = thePrefs::UseFlatBar();
1395 wxRect barRect = rect;
1396 if (!bFlat) { // round bar has a black border, the bar itself is 1 pixel less on each border
1397 barRect.x ++;
1398 barRect.y ++;
1399 barRect.height -= 2;
1400 barRect.width -= 2;
1402 static CBarShader s_StatusBar(16);
1404 uint32 partCount = client.GetUpPartCount();
1406 // Seems the partfile in the client object is not necessarily valid when bar is drawn for the first time.
1407 // Keep it simple and make all parts same size.
1408 s_StatusBar.SetFileSize(partCount * PARTSIZE);
1409 s_StatusBar.SetHeight(barRect.height);
1410 s_StatusBar.SetWidth(barRect.width);
1411 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1413 uint64 uEnd = 0;
1414 for ( uint64 i = 0; i < partCount; i++ ) {
1415 uint64 uStart = PARTSIZE * i;
1416 uEnd = uStart + PARTSIZE - 1;
1418 s_StatusBar.FillRange(uStart, uEnd, client.IsUpPartAvailable(i) ? (bFlat ? crFlatAvailable : crAvailable) : (bFlat ? crFlatUnavailable : crUnavailable));
1420 // fill the rest (if partStatus is empty)
1421 s_StatusBar.FillRange(uEnd + 1, partCount * PARTSIZE - 1, bFlat ? crFlatUnavailable : crUnavailable);
1422 s_StatusBar.Draw(dc, barRect.x, barRect.y, bFlat);
1424 if (!bFlat) {
1425 // Draw black border
1426 dc->SetPen( *wxBLACK_PEN );
1427 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1428 dc->DrawRectangle(rect);
1431 dc->SetPen( old_pen );
1432 dc->SetBrush( old_brush );
1435 // File_checked_for_headers