Improve comment
[amule.git] / src / GenericClientListCtrl.cpp
blob32582c22a866901c99bfbb2ad3fa5f927809b5c7
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 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.
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
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 = wxEmptyString;
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 ColumnInvalid:
188 default:
189 wxASSERT(0);
190 break;
193 return name;
196 void CGenericClientListCtrl::InitColumnData()
198 if (!m_columndata.n_columns) {
199 throw wxString(wxT("CRITICAL: Initialization of the column data lacks subclass information"));
202 for (int i = 0; i < m_columndata.n_columns; ++i) {
203 InsertColumn( i, wxGetTranslation(m_columndata.columns[i].title), wxLIST_FORMAT_LEFT, m_columndata.columns[i].width, TranslateCIDToName(m_columndata.columns[i].cid));
206 LoadSettings();
209 CGenericClientListCtrl::~CGenericClientListCtrl()
211 while ( !m_ListItems.empty() ) {
212 delete m_ListItems.begin()->second;
213 m_ListItems.erase( m_ListItems.begin() );
216 void CGenericClientListCtrl::RawAddSource(CKnownFile* owner, CClientRef source, SourceItemType type)
218 ClientCtrlItem_Struct* newitem = new ClientCtrlItem_Struct;
219 newitem->SetContents(owner, source, type);
221 m_ListItems.insert( ListItemsPair(source.ECID(), newitem) );
223 long item = InsertItem( GetItemCount(), wxEmptyString );
224 SetItemPtrData( item, reinterpret_cast<wxUIntPtr>(newitem) );
225 SetItemBackgroundColour( item, GetBackgroundColour() );
228 void CGenericClientListCtrl::AddSource(CKnownFile* owner, const CClientRef& source, SourceItemType type)
230 wxCHECK_RET(owner, wxT("NULL owner in CGenericClientListCtrl::AddSource"));
231 wxCHECK_RET(source.IsLinked(), wxT("Unlinked source in CGenericClientListCtrl::AddSource"));
233 // Update the other instances of this source
234 bool bFound = false;
235 ListIteratorPair rangeIt = m_ListItems.equal_range(source.ECID());
236 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
237 ClientCtrlItem_Struct* cur_item = it->second;
239 // Check if this source has been already added to this file => to be sure
240 if ( cur_item->GetOwner() == owner ) {
241 // Update this instance with its new setting
242 if ((type == A4AF_SOURCE) &&
243 cur_item->GetSource().GetRequestFile()
244 && std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), cur_item->GetSource().GetRequestFile())) {
245 cur_item->SetContents(owner, source, AVAILABLE_SOURCE);
246 } else {
247 cur_item->SetContents(owner, source, type);
249 cur_item->dwUpdated = 0;
250 bFound = true;
251 } else if ( type == AVAILABLE_SOURCE ) {
252 // The state 'Available' is exclusive
253 cur_item->SetContents(cur_item->GetOwner(), source, A4AF_SOURCE);
254 cur_item->dwUpdated = 0;
258 if ( bFound ) {
259 return;
262 if ( std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), owner) ) {
263 RawAddSource(owner, source, type);
265 ShowSourcesCount( 1 );
269 void CGenericClientListCtrl::RawRemoveSource( ListItems::iterator& it)
271 ClientCtrlItem_Struct* item = it->second;
273 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
275 if ( index > -1 ) {
276 DeleteItem( index );
279 delete item;
281 // Remove it from the m_ListItems
282 m_ListItems.erase( it );
285 void CGenericClientListCtrl::RemoveSource(uint32 source, const CKnownFile* owner)
287 // A NULL owner means remove it no matter what.
288 wxCHECK_RET(source, wxT("NULL source in CGenericClientListCtrl::RemoveSource"));
290 // Retrieve all entries matching the source
291 ListIteratorPair rangeIt = m_ListItems.equal_range(source);
293 int removedItems = 0;
295 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; /* no ++, it happens later */) {
296 ListItems::iterator tmp = it++;
298 if ( owner == NULL || owner == tmp->second->GetOwner() ) {
300 RawRemoveSource(tmp);
302 ++removedItems;
306 ShowSourcesCount((-1) * removedItems);
309 void CGenericClientListCtrl::UpdateItem(uint32 toupdate, SourceItemType type)
311 // Retrieve all entries matching the source
312 ListIteratorPair rangeIt = m_ListItems.equal_range( toupdate );
314 if ( rangeIt.first != rangeIt.second ) {
315 // Visible lines, default to all because not all platforms
316 // support the GetVisibleLines function
317 long first = 0, last = GetItemCount();
319 #ifndef __WXMSW__
320 // Get visible lines if we need them
321 GetVisibleLines( &first, &last );
322 #endif
324 for ( ListItems::iterator it = rangeIt.first; it != rangeIt.second; ++it ) {
325 ClientCtrlItem_Struct* item = it->second;
327 long index = FindItem( -1, reinterpret_cast<wxUIntPtr>(item) );
329 if ((type == A4AF_SOURCE) &&
330 item->GetSource().GetRequestFile()
331 && std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), item->GetSource().GetRequestFile())) {
333 item->SetType(AVAILABLE_SOURCE);
334 } else {
335 item->SetType(type);
338 item->dwUpdated = 0;
340 // Only update visible lines
341 if ( index >= first && index <= last) {
342 RefreshItem( index );
348 void CGenericClientListCtrl::ShowSources( const CKnownFileVector& files )
350 Freeze();
352 // The stored vector is sorted, as is the received one, so we can use binary_search
354 for (unsigned i = 0; i < m_knownfiles.size(); ++i) {
355 // Files that are not in the new list must have show set to false.
356 if (!std::binary_search(files.begin(), files.end(), m_knownfiles[i])) {
357 SetShowSources(m_knownfiles[i], false);
361 // This will call again SetShowSources in files that were in both vectors. Right now
362 // that function is just a inline setter, so any way to prevent it would be wasteful,
363 // but this must be reviewed if that fact changes.
365 for (unsigned i = 0; i < files.size(); ++i) {
366 SetShowSources(files[i], true);
369 // We must cleanup sources that are not in the received files.
371 int itemDiff = 0;
373 for (ListItems::iterator it = m_ListItems.begin(); it != m_ListItems.end(); /* no ++, it happens later */) {
374 ListItems::iterator tmp = it++;
375 ClientCtrlItem_Struct* item = tmp->second;
376 if (!std::binary_search(files.begin(), files.end(), item->GetOwner())) {
377 // Remove it from the m_ListItems
378 RawRemoveSource(tmp);
379 --itemDiff;
383 for (unsigned i = 0; i < files.size(); ++i) {
385 // Only those that weren't showing already
386 if (!std::binary_search(m_knownfiles.begin(), m_knownfiles.end(), files[i])) {
388 CKnownFile* file = files[i];
390 wxASSERT_MSG(file, wxT("NULL file in CGenericClientListCtrl::ShowSources"));
392 if (file) {
394 CKnownFile::SourceSet::const_iterator it;
396 if (IsShowingDownloadSources()) {
397 const CKnownFile::SourceSet& normSources = (dynamic_cast<CPartFile*>( file ))->GetSourceList();
398 const CKnownFile::SourceSet& a4afSources = (dynamic_cast<CPartFile*>( file ))->GetA4AFList();
400 // Adding normal sources
401 for ( it = normSources.begin(); it != normSources.end(); ++it ) {
402 switch (it->GetDownloadState()) {
403 case DS_DOWNLOADING:
404 case DS_ONQUEUE:
405 RawAddSource( file, *it, AVAILABLE_SOURCE );
406 ++itemDiff;
407 break;
408 default:
409 // Any other state
410 RawAddSource( file, *it, UNAVAILABLE_SOURCE );
411 ++itemDiff;
415 // Adding A4AF sources
416 for ( it = a4afSources.begin(); it != a4afSources.end(); ++it ) {
417 // Only add if the A4AF file is not in the shown list.
418 if (!std::binary_search(files.begin(), files.end(), it->GetRequestFile())) {
419 RawAddSource( file, *it, A4AF_SOURCE );
420 ++itemDiff;
423 } else {
424 // Known file
425 const CKnownFile::SourceSet& sources = file->m_ClientUploadList;
426 for ( it = sources.begin(); it != sources.end(); ++it ) {
427 switch (it->GetUploadState()) {
428 case US_UPLOADING:
429 case US_ONUPLOADQUEUE:
430 RawAddSource( file, *it, AVAILABLE_SOURCE );
431 ++itemDiff;
432 break;
433 default:
434 // Any other state
435 RawAddSource( file, *it, UNAVAILABLE_SOURCE );
436 ++itemDiff;
444 m_knownfiles = files;
446 ShowSourcesCount( itemDiff );
448 SortList();
450 Thaw();
454 * Helper-function: This function is used to gather selected items.
456 * @param list A pointer to the list to gather items from.
457 * @return A list containing the selected items of the choosen types.
459 ItemList GetSelectedItems( CGenericClientListCtrl* list )
461 ItemList results;
463 long index = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
465 while ( index > -1 ) {
466 ClientCtrlItem_Struct* item = (ClientCtrlItem_Struct*)list->GetItemData( index );
468 results.push_back( item );
470 index = list->GetNextItem( index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
473 return results;
476 void CGenericClientListCtrl::OnSwapSource( wxCommandEvent& WXUNUSED(event) )
478 ItemList sources = ::GetSelectedItems( this );
480 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
481 CKnownFile * kf = (*it)->GetOwner();
482 if (!kf->IsPartFile()) {
483 wxFAIL_MSG(wxT("File is not a partfile when swapping sources"));
484 continue;
486 (*it)->GetSource().SwapToAnotherFile( true, false, false, dynamic_cast<CPartFile*>(kf));
491 void CGenericClientListCtrl::OnViewFiles( wxCommandEvent& WXUNUSED(event) )
493 ItemList sources = ::GetSelectedItems( this );
495 if ( sources.size() == 1 ) {
496 sources.front()->GetSource().RequestSharedFileList();
501 void CGenericClientListCtrl::OnAddFriend( wxCommandEvent& WXUNUSED(event) )
503 ItemList sources = ::GetSelectedItems( this );
505 for ( ItemList::iterator it = sources.begin(); it != sources.end(); ++it ) {
506 CClientRef &client = (*it)->GetSource();
507 if (client.IsFriend()) {
508 theApp->friendlist->RemoveFriend(client.GetFriend());
509 } else {
510 theApp->friendlist->AddFriend(client);
516 void CGenericClientListCtrl::OnSetFriendslot(wxCommandEvent& evt)
518 ItemList sources = ::GetSelectedItems( this );
520 ItemList::iterator it = sources.begin();
521 if (it != sources.end()) {
522 CClientRef &client = (*it)->GetSource();
523 theApp->friendlist->SetFriendSlot(client.GetFriend(), evt.IsChecked());
524 it++;
526 if (it != sources.end()) {
527 wxMessageBox(_("You are not allowed to set more than one friendslot.\n Only one slot was assigned."), _("Multiple selection"), wxOK | wxICON_ERROR, this);
532 void CGenericClientListCtrl::OnSendMessage( wxCommandEvent& WXUNUSED(event) )
534 ItemList sources = ::GetSelectedItems( this );
536 if ( sources.size() == 1 ) {
537 CClientRef & source = (sources.front())->GetSource();
539 // These values are cached, since calling wxGetTextFromUser will
540 // start an event-loop, in which the client may be deleted.
541 wxString userName = source.GetUserName();
542 uint64 userID = GUI_ID(source.GetIP(), source.GetUserPort());
544 wxString message = ::wxGetTextFromUser(_("Send message to user"),
545 _("Message to send:"));
546 if ( !message.IsEmpty() ) {
547 theApp->amuledlg->m_chatwnd->SendMessage(message, userName, userID);
553 void CGenericClientListCtrl::OnViewClientInfo( wxCommandEvent& WXUNUSED(event) )
555 ItemList sources = ::GetSelectedItems( this );
557 if ( sources.size() == 1 ) {
558 CClientDetailDialog( this, sources.front()->GetSource() ).ShowModal();
563 void CGenericClientListCtrl::OnItemActivated( wxListEvent& evt )
565 CClientDetailDialog( this, ((ClientCtrlItem_Struct*)GetItemData( evt.GetIndex()))->GetSource() ).ShowModal();
569 void CGenericClientListCtrl::OnMouseRightClick(wxListEvent& evt)
571 long index = CheckSelection(evt);
572 if (index < 0) {
573 return;
576 delete m_menu;
577 m_menu = NULL;
579 ClientCtrlItem_Struct* item = (ClientCtrlItem_Struct*)GetItemData( index );
580 CClientRef& client = item->GetSource();
582 m_menu = new wxMenu(wxT("Clients"));
583 m_menu->Append(MP_DETAIL, _("Show &Details"));
584 m_menu->Append(MP_ADDFRIEND, client.IsFriend() ? _("Remove from friends") : _("Add to Friends"));
586 m_menu->AppendCheckItem(MP_FRIENDSLOT, _("Establish Friend Slot"));
587 if (client.IsFriend()) {
588 m_menu->Enable(MP_FRIENDSLOT, true);
589 m_menu->Check(MP_FRIENDSLOT, client.GetFriendSlot());
590 } else {
591 m_menu->Enable(MP_FRIENDSLOT, false);
594 m_menu->Append(MP_SHOWLIST, _("View Files"));
595 m_menu->Append(MP_SENDMESSAGE, _("Send message"));
597 m_menu->Append(MP_CHANGE2FILE, _("Swap to this file"));
599 // Only enable the Swap option for A4AF sources
600 m_menu->Enable(MP_CHANGE2FILE, (item->GetType() == A4AF_SOURCE));
601 // We need a valid IP if we are to message the client
602 m_menu->Enable(MP_SENDMESSAGE, (client.GetIP() != 0));
604 m_menu->Enable(MP_SHOWLIST, !client.HasDisabledSharedFiles());
606 PopupMenu(m_menu, evt.GetPoint());
608 delete m_menu;
609 m_menu = NULL;
614 void CGenericClientListCtrl::OnMouseMiddleClick(wxListEvent& evt)
616 // Check if clicked item is selected. If not, unselect all and select it.
617 long index = CheckSelection(evt);
618 if ( index < 0 ) {
619 return;
622 CClientDetailDialog(this, ((ClientCtrlItem_Struct*)GetItemData( index ))->GetSource()).ShowModal();
626 void CGenericClientListCtrl::OnKeyPressed( wxKeyEvent& event )
628 // No actions right now.
629 //switch (event.GetKeyCode()) {
630 // default:
631 event.Skip();
636 void CGenericClientListCtrl::OnDrawItem(
637 int item, wxDC* dc, const wxRect& rect, const wxRect& rectHL, bool highlighted)
639 // Don't do any drawing if there's nobody to see it.
640 if ( !theApp->amuledlg->IsDialogVisible( GetParentDialog() ) ) {
641 return;
644 ClientCtrlItem_Struct* content = (ClientCtrlItem_Struct *)GetItemData(item);
646 // Define text-color and background
647 // and the border of the drawn area
648 if (highlighted) {
649 CMuleColour colour;
650 if (GetFocus()) {
651 dc->SetBackground(m_hilightBrush);
652 colour = m_hilightBrush.GetColour();
653 } else {
654 dc->SetBackground(m_hilightUnfocusBrush);
655 colour = m_hilightUnfocusBrush.GetColour();
657 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
658 dc->SetPen( colour.Blend(65).GetPen() );
659 } else {
660 dc->SetBackground(*(wxTheBrushList->FindOrCreateBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX), wxSOLID)));
661 dc->SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
662 dc->SetPen(*wxTRANSPARENT_PEN);
664 dc->SetBrush( dc->GetBackground() );
666 dc->DrawRectangle( rectHL.x, rectHL.y, rectHL.width, rectHL.height );
668 dc->SetPen(*wxTRANSPARENT_PEN);
670 // Various constant values we use
671 const int iTextOffset = (( rect.GetHeight() - dc->GetCharHeight() ) / 2) + 1 /* Fixes rounding in the centering math, much easier than floor() */;
672 const int iOffset = 2;
673 wxASSERT(m_ImageList.GetImageCount() > 0);
674 int imageListBitmapYOffset = 0;
675 int imageListBitmapXSize = 0;
676 if (m_ImageList.GetSize(0, imageListBitmapXSize, imageListBitmapYOffset)) {
677 imageListBitmapXSize += 2; // Padding.
678 imageListBitmapYOffset = ((rect.GetHeight() - imageListBitmapYOffset) / 2) + 1 /* Fixes rounding like above */;
679 } else {
680 wxASSERT(0);
683 wxRect cur_rec( iOffset, rect.y, 0, rect.height );
685 for (int i = 0; i < GetColumnCount(); ++i) {
687 int columnwidth = GetColumnWidth(i);
689 if (columnwidth > 2*iOffset) {
690 // Make a copy of the current rectangle so we can apply specific tweaks
691 wxRect target_rec = cur_rec;
692 target_rec.width = columnwidth - 2*iOffset;
694 GenericColumnEnum cid = m_columndata.columns[i].cid;
696 // Draw the item
697 DrawClientItem(dc, cid, target_rec, content, iTextOffset, imageListBitmapYOffset, imageListBitmapXSize);
700 // Increment to the next column
701 cur_rec.x += columnwidth;
705 void CGenericClientListCtrl::DrawClientItem(wxDC* dc, int nColumn, const wxRect& rect, ClientCtrlItem_Struct* item, int iTextOffset, int iBitmapOffset, int iBitmapXSize ) const
707 wxDCClipper clipper( *dc, rect.GetX(), rect.GetY(), rect.GetWidth(), rect.GetHeight() );
708 wxString buffer;
710 const CClientRef& client = item->GetSource();
712 switch (nColumn) {
713 // Client name + various icons
714 case ColumnUserName: {
715 // Point will get shifted per drawing.
717 wxPoint point( rect.GetX(), rect.GetY() );
719 uint8 image = Client_Grey_Smiley;
721 if (item->GetType() != A4AF_SOURCE) {
723 switch (client.GetDownloadState()) {
724 case DS_CONNECTING:
725 case DS_CONNECTED:
726 case DS_WAITCALLBACK:
727 case DS_TOOMANYCONNS:
728 image = Client_Red_Smiley;
729 break;
730 case DS_ONQUEUE:
731 if (client.IsRemoteQueueFull()) {
732 image = Client_Grey_Smiley;
733 } else {
734 image = Client_Yellow_Smiley;
736 break;
737 case DS_DOWNLOADING:
738 case DS_REQHASHSET:
739 image = Client_Green_Smiley;
740 break;
741 case DS_NONEEDEDPARTS:
742 case DS_LOWTOLOWIP:
743 image = Client_Grey_Smiley; // Redundant
744 break;
745 default: // DS_NONE i.e.
746 image = Client_White_Smiley;
749 } else {
750 // Default (Client_Grey_Smiley)
753 m_ImageList.Draw(image, *dc, point.x, point.y + iBitmapOffset, wxIMAGELIST_DRAW_TRANSPARENT);
755 // Next
757 point.x += iBitmapXSize;
759 uint8 clientImage = Client_Unknown;
761 if ( client.IsFriend() ) {
762 clientImage = Client_Friend_Smiley;
763 } else {
764 switch ( client.GetClientSoft() ) {
765 case SO_AMULE:
766 clientImage = Client_aMule_Smiley;
767 break;
768 case SO_MLDONKEY:
769 case SO_NEW_MLDONKEY:
770 case SO_NEW2_MLDONKEY:
771 clientImage = Client_mlDonkey_Smiley;
772 break;
773 case SO_EDONKEY:
774 case SO_EDONKEYHYBRID:
775 clientImage = Client_eDonkeyHybrid_Smiley;
776 break;
777 case SO_EMULE:
778 clientImage = Client_eMule_Smiley;
779 break;
780 case SO_LPHANT:
781 clientImage = Client_lphant_Smiley;
782 break;
783 case SO_SHAREAZA:
784 case SO_NEW_SHAREAZA:
785 case SO_NEW2_SHAREAZA:
786 clientImage = Client_Shareaza_Smiley;
787 break;
788 case SO_LXMULE:
789 clientImage = Client_xMule_Smiley;
790 break;
791 default:
792 // cDonkey, Compatible, Unknown
793 // No icon for those yet.
794 // Using the eMule one + '?'
795 // Which is a faillback to the default (Client_Unknown)
796 break;
800 int realY = point.y + iBitmapOffset;
801 m_ImageList.Draw(clientImage, *dc, point.x, realY, wxIMAGELIST_DRAW_TRANSPARENT);
803 if (client.GetScoreRatio() > 1) {
804 // Has credits, draw the gold star
805 m_ImageList.Draw(Client_CreditsYellow_Smiley, *dc, point.x, realY,
806 wxIMAGELIST_DRAW_TRANSPARENT );
807 } else if ( !client.ExtProtocolAvailable() ) {
808 // No Ext protocol -> Draw the '-'
809 m_ImageList.Draw(Client_ExtendedProtocol_Smiley, *dc, point.x, realY,
810 wxIMAGELIST_DRAW_TRANSPARENT);
813 if (client.IsIdentified()) {
814 // the 'v'
815 m_ImageList.Draw(Client_SecIdent_Smiley, *dc, point.x, realY,
816 wxIMAGELIST_DRAW_TRANSPARENT);
817 } else if (client.IsBadGuy()) {
818 // the 'X'
819 m_ImageList.Draw(Client_BadGuy_Smiley, *dc, point.x, realY,
820 wxIMAGELIST_DRAW_TRANSPARENT);
823 if (client.GetObfuscationStatus() == OBST_ENABLED) {
824 // the "¿" except it's a key
825 m_ImageList.Draw(Client_Encryption_Smiley, *dc, point.x, realY,
826 wxIMAGELIST_DRAW_TRANSPARENT);
829 // Next
831 point.x += iBitmapXSize;
833 wxString userName;
834 #ifdef ENABLE_IP2COUNTRY
835 if (theApp->amuledlg->m_IP2Country->IsEnabled() && thePrefs::IsGeoIPEnabled()) {
836 // Draw the flag. Size can't be precached.
837 const CountryData& countrydata = theApp->amuledlg->m_IP2Country->GetCountryData(client.GetFullIP());
839 realY = point.y + (rect.GetHeight() - countrydata.Flag.GetHeight())/2 + 1 /* floor() */;
841 dc->DrawBitmap(countrydata.Flag,
842 point.x, realY,
843 true);
845 userName << countrydata.Name;
847 userName << wxT(" - ");
849 point.x += countrydata.Flag.GetWidth() + 2 /*Padding*/;
851 #endif // ENABLE_IP2COUNTRY
852 if (client.GetUserName().IsEmpty()) {
853 userName << wxT("?");
854 } else {
855 userName << client.GetUserName();
858 dc->DrawText(userName, point.x, rect.GetY() + iTextOffset);
860 break;
862 case ColumnUserDownloaded:
863 if (item->GetType() != A4AF_SOURCE && client.GetTransferredDown()) {
864 buffer = CastItoXBytes(client.GetTransferredDown());
865 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
867 break;
868 case ColumnUserUploaded:
869 if (item->GetType() != A4AF_SOURCE && client.GetTransferredUp()) {
870 buffer = CastItoXBytes(client.GetTransferredUp());
871 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
873 break;
874 case ColumnUserSpeedDown:
875 if (item->GetType() != A4AF_SOURCE && client.GetKBpsDown() > 0.001) {
876 buffer = CFormat(_("%.1f kB/s")) % client.GetKBpsDown();
877 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
879 break;
880 case ColumnUserSpeedUp:
881 // Datarate is in bytes.
882 if (item->GetType() != A4AF_SOURCE && client.GetUploadDatarate() > 1024) {
883 buffer = CFormat(_("%.1f kB/s")) % (client.GetUploadDatarate() / 1024.0);
884 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
886 break;
887 case ColumnUserProgress:
888 if ( thePrefs::ShowProgBar() ) {
889 int iWidth = rect.GetWidth() - 2;
890 int iHeight = rect.GetHeight() - 2;
892 // don't draw Text beyond the bar
893 dc->SetClippingRegion(rect.GetX(), rect.GetY() + 1, iWidth, iHeight);
895 if ( item->GetType() != A4AF_SOURCE ) {
896 uint32 dwTicks = GetTickCount();
898 wxMemoryDC cdcStatus;
900 if ( item->dwUpdated < dwTicks || !item->status ||
901 iWidth != item->status->GetWidth() ) {
903 if (item->status == NULL) {
904 item->status = new wxBitmap(iWidth, iHeight);
905 } else if ( item->status->GetWidth() != iWidth ) {
906 // Only recreate if size has changed
907 item->status->Create(iWidth, iHeight);
910 cdcStatus.SelectObject(*(item->status));
912 if ( thePrefs::UseFlatBar() ) {
913 DrawSourceStatusBar( client, &cdcStatus,
914 wxRect(0, 0, iWidth, iHeight), true);
915 } else {
916 DrawSourceStatusBar( client, &cdcStatus,
917 wxRect(1, 1, iWidth - 2, iHeight - 2), false);
919 // Draw black border
920 cdcStatus.SetPen( *wxBLACK_PEN );
921 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
922 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
925 // Plus ten seconds
926 item->dwUpdated = dwTicks + 10000;
927 } else {
928 cdcStatus.SelectObject(*(item->status));
931 dc->Blit(rect.GetX(), rect.GetY() + 1, iWidth, iHeight, &cdcStatus, 0, 0);
932 } else {
933 wxString a4af;
934 CPartFile* p = client.GetRequestFile();
935 if (p) {
936 a4af = p->GetFileName().GetPrintable();
937 } else {
938 a4af = wxT("?");
940 buffer = CFormat(wxT("%s: %s")) % _("A4AF") % a4af;
942 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
943 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
945 wxCoord txtwidth, txtheight;
947 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
949 dc->SetTextForeground(*wxBLACK);
950 dc->DrawText(buffer, wxMax(rect.GetX() + 2, midx - (txtwidth >> 1)), midy - (txtheight >> 1));
952 // Draw black border
953 dc->SetPen( *wxBLACK_PEN );
954 dc->SetBrush( *wxTRANSPARENT_BRUSH );
955 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
958 break;
960 case ColumnUserAvailable: {
961 if ( client.GetUpPartCount() ) {
962 DrawStatusBar( client, dc, rect );
964 break;
967 case ColumnUserVersion: {
968 dc->DrawText(client.GetClientVerString(), rect.GetX(), rect.GetY() + iTextOffset);
969 break;
972 case ColumnUserQueueRankRemote: {
973 sint16 qrDiff = 0;
974 wxColour savedColour = dc->GetTextForeground();
975 // We only show the queue rank for sources actually queued for that file
976 if ( item->GetType() != A4AF_SOURCE && client.GetDownloadState() == DS_ONQUEUE ) {
977 if (client.IsRemoteQueueFull()) {
978 buffer = _("Queue Full");
979 } else {
980 uint16 rank = client.GetRemoteQueueRank();
981 if (rank) {
982 qrDiff = rank - client.GetOldRemoteQueueRank();
983 if (qrDiff == rank) {
984 qrDiff = 0;
986 if ( qrDiff < 0 ) {
987 dc->SetTextForeground(*wxBLUE);
989 if ( qrDiff > 0 ) {
990 dc->SetTextForeground(*wxRED);
992 buffer = CFormat(_("On Queue: %u (%i)")) % rank % qrDiff;
993 } else {
994 buffer = _("On Queue");
997 } else {
998 if (item->GetType() != A4AF_SOURCE) {
999 buffer = DownloadStateToStr( client.GetDownloadState(),
1000 client.IsRemoteQueueFull() );
1001 } else {
1002 buffer = _("Asked for another file");
1003 if ( client.GetRequestFile() &&
1004 client.GetRequestFile()->GetFileName().IsOk()) {
1005 buffer += CFormat(wxT(" (%s)"))
1006 % client.GetRequestFile()->GetFileName();
1010 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1011 if (qrDiff) {
1012 dc->SetTextForeground(savedColour);
1014 break;
1016 case ColumnUserQueueRankLocal:
1017 if (item->GetType() != A4AF_SOURCE) {
1018 if (client.GetUploadState() == US_ONUPLOADQUEUE ) {
1019 uint16 nRank = client.GetUploadQueueWaitingPosition();
1020 if (nRank == 0) {
1021 buffer = _("Waiting for upload slot");
1022 } else {
1023 buffer = CFormat(_("On Queue: %u")) % nRank;
1025 } else if (client.GetUploadState() == US_UPLOADING) {
1026 buffer = _("Uploading");
1027 } else {
1028 buffer = _("None");
1030 } else {
1031 buffer = _("Asked for another file");
1033 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1034 break;
1035 // Source comes from?
1036 case ColumnUserOrigin: {
1037 buffer = wxGetTranslation(OriginToText(client.GetSourceFrom()));
1038 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1039 break;
1041 // Local file name to identify on multi select
1042 case ColumnUserFileNameDownload: {
1043 const CPartFile * pf = client.GetRequestFile();
1044 if (pf) {
1045 buffer = pf->GetFileName().GetPrintable();
1046 } else {
1047 buffer = _("Unknown");
1048 buffer = wxT("[") + buffer + wxT("]");
1050 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1051 break;
1053 case ColumnUserFileNameUpload: {
1054 const CKnownFile * kf = client.GetUploadFile();
1055 if (kf) {
1056 buffer = kf->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 ColumnUserFileNameDownloadRemote: {
1065 bool nameMissmatch = false;
1066 wxColour savedColour = dc->GetTextForeground();
1067 if (client.GetClientFilename().IsEmpty()) {
1068 buffer = _("Unknown");
1069 buffer = wxT("[") + buffer + wxT("]");
1070 } else {
1071 buffer = client.GetClientFilename();
1072 const CPartFile * pf = client.GetRequestFile();
1073 if (pf && (pf->GetFileName().GetPrintable().CmpNoCase(buffer) != 0)) {
1074 nameMissmatch = true;
1075 dc->SetTextForeground(*wxRED);
1078 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1079 if (nameMissmatch) {
1080 dc->SetTextForeground(savedColour);
1082 break;
1087 int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1089 ClientCtrlItem_Struct* item1 = (ClientCtrlItem_Struct*)param1;
1090 ClientCtrlItem_Struct* item2 = (ClientCtrlItem_Struct*)param2;
1092 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1093 sortData &= CMuleListCtrl::COLUMN_MASK;
1094 int comp = 0;
1096 // Two sources, some different possibilites
1097 // Avilable sources first, if we have both an
1098 // available and an unavailable
1099 comp = ( item2->GetType() - item1->GetType() );
1101 if (comp) {
1102 // unavailable and available. The order is fixed regardless of sort-order.
1103 return comp;
1104 } else {
1105 comp = Compare(item1->GetSource(), item2->GetSource(), sortData);
1108 // We modify the result so that it matches with ascending or decending
1109 return sortMod * comp;
1112 int CGenericClientListCtrl::Compare(
1113 const CClientRef& client1, const CClientRef& client2, long lParamSort)
1115 switch (lParamSort) {
1116 // Sort by name
1117 case ColumnUserName:
1118 return CmpAny( client1.GetUserName(), client2.GetUserName() );
1120 // Sort by transferred in the following fields
1121 case ColumnUserDownloaded:
1122 return CmpAny( client1.GetTransferredDown(), client2.GetTransferredDown() );
1124 // Sort by transferred in the following fields
1125 case ColumnUserUploaded:
1126 return CmpAny( client1.GetTransferredUp(), client2.GetTransferredUp() );
1128 // Sort by speed
1129 case ColumnUserSpeedDown:
1130 return CmpAny( client1.GetKBpsDown(), client2.GetKBpsDown() );
1132 // Sort by speed
1133 case ColumnUserSpeedUp:
1134 return CmpAny( client1.GetUploadDatarate(), client2.GetUploadDatarate() );
1136 // Sort by parts offered
1137 case ColumnUserProgress:
1138 return CmpAny(
1139 client1.GetAvailablePartCount(),
1140 client2.GetAvailablePartCount() );
1142 // Sort by client version
1143 case ColumnUserVersion: {
1144 int cmp = client1.GetSoftStr().Cmp(client2.GetSoftStr());
1146 if (cmp == 0) {
1147 cmp = CmpAny(client1.GetVersion(), client2.GetVersion());
1149 if (cmp == 0) {
1150 cmp = client1.GetClientModString().Cmp(client2.GetClientModString());
1152 return cmp;
1155 // Sort by Queue-Rank
1156 case ColumnUserQueueRankRemote: {
1157 // This will sort by download state: Downloading, OnQueue, Connecting ...
1158 // However, Asked For Another will always be placed last, due to
1159 // sorting in SortProc
1160 if ( client1.GetDownloadState() != client2.GetDownloadState() ) {
1161 return client1.GetDownloadState() - client2.GetDownloadState();
1164 // Placing items on queue before items on full queues
1165 if ( client1.IsRemoteQueueFull() ) {
1166 if ( client2.IsRemoteQueueFull() ) {
1167 return 0;
1168 } else {
1169 return 1;
1171 } else if ( client2.IsRemoteQueueFull() ) {
1172 return -1;
1173 } else {
1174 if ( client1.GetRemoteQueueRank() ) {
1175 if ( client2.GetRemoteQueueRank() ) {
1176 return CmpAny(
1177 client1.GetRemoteQueueRank(),
1178 client2.GetRemoteQueueRank() );
1179 } else {
1180 return -1;
1182 } else {
1183 if ( client2.GetRemoteQueueRank() ) {
1184 return 1;
1185 } else {
1186 return 0;
1192 // Sort by Queue-Rank
1193 case ColumnUserQueueRankLocal: {
1194 // This will sort by download state: Downloading, OnQueue, Connecting ...
1195 // However, Asked For Another will always be placed last, due to
1196 // sorting in SortProc
1197 if ( client1.GetUploadState() != client2.GetUploadState() ) {
1198 return client1.GetUploadState() - client2.GetUploadState();
1201 uint16 rank1 = client1.GetUploadQueueWaitingPosition();
1202 uint16 rank2 = client2.GetUploadQueueWaitingPosition();
1203 // Placing items on queue before items on full queues
1204 if ( !rank1 ) {
1205 if ( !rank2 ) {
1206 return 0;
1207 } else {
1208 return 1;
1210 } else if ( !rank2 ) {
1211 return -1;
1212 } else {
1213 if ( rank1 ) {
1214 if ( rank2 ) {
1215 return CmpAny(
1216 rank1,
1217 rank2 );
1218 } else {
1219 return -1;
1221 } else {
1222 if ( rank2 ) {
1223 return 1;
1224 } else {
1225 return 0;
1231 // Source of source ;)
1232 case ColumnUserOrigin:
1233 return CmpAny(client1.GetSourceFrom(), client2.GetSourceFrom());
1235 // Sort by local filename (download)
1236 case ColumnUserFileNameDownload: {
1237 wxString buffer1, buffer2;
1238 const CPartFile * pf1 = client1.GetRequestFile();
1239 if (pf1) {
1240 buffer1 = pf1->GetFileName().GetPrintable();
1242 const CPartFile * pf2 = client2.GetRequestFile();
1243 if (pf2) {
1244 buffer2 = pf2->GetFileName().GetPrintable();
1246 return CmpAny(buffer1, buffer2);
1249 // Sort by local filename (upload)
1250 case ColumnUserFileNameUpload: {
1251 wxString buffer1, buffer2;
1252 const CKnownFile * kf1 = client1.GetUploadFile();
1253 if (kf1) {
1254 buffer1 = kf1->GetFileName().GetPrintable();
1256 const CKnownFile * kf2 = client2.GetUploadFile();
1257 if (kf2) {
1258 buffer2 = kf2->GetFileName().GetPrintable();
1260 return CmpAny(buffer1, buffer2);
1263 case ColumnUserFileNameDownloadRemote: {
1264 return CmpAny(client1.GetClientFilename(), client2.GetClientFilename());
1267 default:
1268 return 0;
1273 void CGenericClientListCtrl::ShowSourcesCount( int diff )
1275 m_clientcount += diff;
1276 wxStaticText* label = CastByID( ID_CLIENTCOUNT, GetParent(), wxStaticText );
1278 if (label) {
1279 label->SetLabel(CFormat(wxT("%i")) % m_clientcount);
1280 label->GetParent()->Layout();
1284 static const CMuleColour crBoth(0, 192, 0);
1285 static const CMuleColour crFlatBoth(0, 150, 0);
1287 static const CMuleColour crNeither(240, 240, 240);
1288 static const CMuleColour crFlatNeither(224, 224, 224);
1290 static const CMuleColour crClientOnly(104, 104, 104);
1291 static const CMuleColour crFlatClientOnly(0, 0, 0);
1293 static const CMuleColour crPending(255, 208, 0);
1294 static const CMuleColour crNextPending(255, 255, 100);
1296 void CGenericClientListCtrl::DrawSourceStatusBar(
1297 const CClientRef& source, wxDC* dc, const wxRect& rect, bool bFlat) const
1299 static CBarShader s_StatusBar(16);
1301 CPartFile* reqfile = source.GetRequestFile();
1303 s_StatusBar.SetHeight(rect.height);
1304 s_StatusBar.SetWidth(rect.width);
1305 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1306 const BitVector& partStatus = source.GetPartStatus();
1308 if (reqfile && reqfile->GetPartCount() == partStatus.size()) {
1309 s_StatusBar.SetFileSize(reqfile->GetFileSize());
1310 uint16 lastDownloadingPart = source.GetDownloadState() == DS_DOWNLOADING
1311 ? source.GetLastDownloadingPart() : 0xffff;
1312 uint16 nextRequestedPart = source.GetNextRequestedPart();
1314 for ( uint16 i = 0; i < partStatus.size(); i++ ) {
1315 uint64 uStart = PARTSIZE * i;
1316 uint64 uEnd = uStart + reqfile->GetPartSize(i) - 1;
1318 CMuleColour colour;
1319 if (!partStatus.get(i)) {
1320 // client does not have this part
1321 // light grey
1322 colour = bFlat ? crFlatNeither : crNeither;
1323 } else if ( reqfile->IsComplete(i)) {
1324 // completed part
1325 // green
1326 colour = bFlat ? crFlatBoth : crBoth;
1327 } else if (lastDownloadingPart == i) {
1328 // downloading part
1329 // yellow
1330 colour = crPending;
1331 } else if (nextRequestedPart == i) {
1332 // requested part
1333 // light yellow
1334 colour = crNextPending;
1335 } else {
1336 // client has this part, we need it
1337 // black
1338 colour = bFlat ? crFlatClientOnly : crClientOnly;
1341 if ( source.GetRequestFile()->IsStopped() ) {
1342 colour.Blend(50);
1345 s_StatusBar.FillRange(uStart, uEnd, colour);
1347 } else {
1348 s_StatusBar.SetFileSize(1);
1349 s_StatusBar.FillRange(0, 1, bFlat ? crFlatNeither : crNeither);
1352 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
1355 static const CMuleColour crUnavailable(240, 240, 240);
1356 static const CMuleColour crFlatUnavailable(224, 224, 224);
1358 static const CMuleColour crAvailable(104, 104, 104);
1359 static const CMuleColour crFlatAvailable(0, 0, 0);
1361 void CGenericClientListCtrl::DrawStatusBar( const CClientRef& client, wxDC* dc, const wxRect& rect1 ) const
1363 wxRect rect = rect1;
1364 rect.y += 1;
1365 rect.height -= 2;
1367 wxPen old_pen = dc->GetPen();
1368 wxBrush old_brush = dc->GetBrush();
1369 bool bFlat = thePrefs::UseFlatBar();
1371 wxRect barRect = rect;
1372 if (!bFlat) { // round bar has a black border, the bar itself is 1 pixel less on each border
1373 barRect.x ++;
1374 barRect.y ++;
1375 barRect.height -= 2;
1376 barRect.width -= 2;
1378 static CBarShader s_StatusBar(16);
1380 uint32 partCount = client.GetUpPartCount();
1382 // Seems the partfile in the client object is not necessarily valid when bar is drawn for the first time.
1383 // Keep it simple and make all parts same size.
1384 s_StatusBar.SetFileSize(partCount * PARTSIZE);
1385 s_StatusBar.SetHeight(barRect.height);
1386 s_StatusBar.SetWidth(barRect.width);
1387 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1389 uint64 uEnd = 0;
1390 for ( uint64 i = 0; i < partCount; i++ ) {
1391 uint64 uStart = PARTSIZE * i;
1392 uEnd = uStart + PARTSIZE - 1;
1394 s_StatusBar.FillRange(uStart, uEnd, client.IsUpPartAvailable(i) ? (bFlat ? crFlatAvailable : crAvailable) : (bFlat ? crFlatUnavailable : crUnavailable));
1396 // fill the rest (if partStatus is empty)
1397 s_StatusBar.FillRange(uEnd + 1, partCount * PARTSIZE - 1, bFlat ? crFlatUnavailable : crUnavailable);
1398 s_StatusBar.Draw(dc, barRect.x, barRect.y, bFlat);
1400 if (!bFlat) {
1401 // Draw black border
1402 dc->SetPen( *wxBLACK_PEN );
1403 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1404 dc->DrawRectangle(rect);
1407 dc->SetPen( old_pen );
1408 dc->SetBrush( old_brush );
1411 // File_checked_for_headers