Recognize additional file types (gagar)
[amule.git] / src / GenericClientListCtrl.cpp
blob83197090bb84b221c2eb42b25e1b65b32fa133b6
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.
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;
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 __WXMSW__
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 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 = (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, ((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 = (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, ((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 = (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 buffer = CFormat(_("%.1f kB/s")) % client.GetKBpsDown();
880 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
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);
889 break;
890 case ColumnUserProgress:
891 if ( thePrefs::ShowProgBar() ) {
892 int iWidth = rect.GetWidth() - 2;
893 int iHeight = rect.GetHeight() - 2;
895 // don't draw Text beyond the bar
896 dc->SetClippingRegion(rect.GetX(), rect.GetY() + 1, iWidth, iHeight);
898 if ( item->GetType() != A4AF_SOURCE ) {
899 uint32 dwTicks = GetTickCount();
901 wxMemoryDC cdcStatus;
903 if ( item->dwUpdated < dwTicks || !item->status ||
904 iWidth != item->status->GetWidth() ) {
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);
913 cdcStatus.SelectObject(*(item->status));
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);
922 // Draw black border
923 cdcStatus.SetPen( *wxBLACK_PEN );
924 cdcStatus.SetBrush( *wxTRANSPARENT_BRUSH );
925 cdcStatus.DrawRectangle( 0, 0, iWidth, iHeight );
928 // Plus ten seconds
929 item->dwUpdated = dwTicks + 10000;
930 } else {
931 cdcStatus.SelectObject(*(item->status));
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("?");
943 buffer = CFormat(wxT("%s: %s")) % _("A4AF") % a4af;
945 int midx = (2*rect.GetX() + rect.GetWidth()) >> 1;
946 int midy = (2*rect.GetY() + rect.GetHeight()) >> 1;
948 wxCoord txtwidth, txtheight;
950 dc->GetTextExtent(buffer, &txtwidth, &txtheight);
952 dc->SetTextForeground(*wxBLACK);
953 dc->DrawText(buffer, wxMax(rect.GetX() + 2, midx - (txtwidth >> 1)), midy - (txtheight >> 1));
955 // Draw black border
956 dc->SetPen( *wxBLACK_PEN );
957 dc->SetBrush( *wxTRANSPARENT_BRUSH );
958 dc->DrawRectangle( rect.GetX(), rect.GetY() + 1, iWidth, iHeight );
961 break;
963 case ColumnUserAvailable: {
964 if ( client.GetUpPartCount() ) {
965 DrawStatusBar( client, dc, rect );
967 break;
970 case ColumnUserVersion: {
971 dc->DrawText(client.GetClientVerString(), rect.GetX(), rect.GetY() + iTextOffset);
972 break;
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;
989 if ( qrDiff < 0 ) {
990 dc->SetTextForeground(*wxBLUE);
992 if ( qrDiff > 0 ) {
993 dc->SetTextForeground(*wxRED);
995 buffer = CFormat(_("On Queue: %u (%i)")) % rank % qrDiff;
996 } else {
997 buffer = _("On Queue");
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();
1013 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1014 if (qrDiff) {
1015 dc->SetTextForeground(savedColour);
1017 break;
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;
1028 } else if (client.GetUploadState() == US_UPLOADING) {
1029 buffer = _("Uploading");
1030 } else {
1031 buffer = _("None");
1033 } else {
1034 buffer = _("Asked for another file");
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;
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("]");
1053 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1054 break;
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("]");
1064 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1065 break;
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);
1081 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1082 if (nameMissmatch) {
1083 dc->SetTextForeground(savedColour);
1085 break;
1087 case ColumnUserSharedFiles: {
1088 if(client.HasDisabledSharedFiles()) {
1089 buffer = _("No");
1090 } else {
1091 buffer = _("Yes");
1093 dc->DrawText(buffer, rect.GetX(), rect.GetY() + iTextOffset);
1094 break;
1099 int CGenericClientListCtrl::SortProc(wxUIntPtr param1, wxUIntPtr param2, long sortData)
1101 ClientCtrlItem_Struct* item1 = (ClientCtrlItem_Struct*)param1;
1102 ClientCtrlItem_Struct* item2 = (ClientCtrlItem_Struct*)param2;
1104 int sortMod = (sortData & CMuleListCtrl::SORT_DES) ? -1 : 1;
1105 sortData &= CMuleListCtrl::COLUMN_MASK;
1106 int comp = 0;
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() );
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);
1120 // We modify the result so that it matches with ascending or decending
1121 return sortMod * comp;
1124 int CGenericClientListCtrl::Compare(
1125 const CClientRef& client1, const CClientRef& client2, long lParamSort)
1127 switch (lParamSort) {
1128 // Sort by name
1129 case ColumnUserName:
1130 return CmpAny( client1.GetUserName(), client2.GetUserName() );
1132 // Sort by transferred in the following fields
1133 case ColumnUserDownloaded:
1134 return CmpAny( client1.GetTransferredDown(), client2.GetTransferredDown() );
1136 // Sort by transferred in the following fields
1137 case ColumnUserUploaded:
1138 return CmpAny( client1.GetTransferredUp(), client2.GetTransferredUp() );
1140 // Sort by speed
1141 case ColumnUserSpeedDown:
1142 return CmpAny( client1.GetKBpsDown(), client2.GetKBpsDown() );
1144 // Sort by speed
1145 case ColumnUserSpeedUp:
1146 return CmpAny( client1.GetUploadDatarate(), client2.GetUploadDatarate() );
1148 // Sort by parts offered
1149 case ColumnUserProgress:
1150 return CmpAny(
1151 client1.GetAvailablePartCount(),
1152 client2.GetAvailablePartCount() );
1154 // Sort by client version
1155 case ColumnUserVersion: {
1156 int cmp = client1.GetSoftStr().Cmp(client2.GetSoftStr());
1158 if (cmp == 0) {
1159 cmp = CmpAny(client1.GetVersion(), client2.GetVersion());
1161 if (cmp == 0) {
1162 cmp = client1.GetClientModString().Cmp(client2.GetClientModString());
1164 return cmp;
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();
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;
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;
1194 } else {
1195 if ( client2.GetRemoteQueueRank() ) {
1196 return 1;
1197 } else {
1198 return 0;
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();
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;
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;
1233 } else {
1234 if ( rank2 ) {
1235 return 1;
1236 } else {
1237 return 0;
1243 // Source of source ;)
1244 case ColumnUserOrigin:
1245 return CmpAny(client1.GetSourceFrom(), client2.GetSourceFrom());
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();
1254 const CPartFile * pf2 = client2.GetRequestFile();
1255 if (pf2) {
1256 buffer2 = pf2->GetFileName().GetPrintable();
1258 return CmpAny(buffer1, buffer2);
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();
1268 const CKnownFile * kf2 = client2.GetUploadFile();
1269 if (kf2) {
1270 buffer2 = kf2->GetFileName().GetPrintable();
1272 return CmpAny(buffer1, buffer2);
1275 case ColumnUserFileNameDownloadRemote: {
1276 return CmpAny(client1.GetClientFilename(), client2.GetClientFilename());
1279 case ColumnUserSharedFiles: {
1280 return CmpAny(client1.HasDisabledSharedFiles(), client2.HasDisabledSharedFiles());
1283 default:
1284 return 0;
1289 void CGenericClientListCtrl::ShowSourcesCount( int diff )
1291 m_clientcount += diff;
1292 wxStaticText* label = CastByID( ID_CLIENTCOUNT, GetParent(), wxStaticText );
1294 if (label) {
1295 label->SetLabel(CFormat(wxT("%i")) % m_clientcount);
1296 label->GetParent()->Layout();
1300 static const CMuleColour crBoth(0, 192, 0);
1301 static const CMuleColour crFlatBoth(0, 150, 0);
1303 static const CMuleColour crNeither(240, 240, 240);
1304 static const CMuleColour crFlatNeither(224, 224, 224);
1306 static const CMuleColour crClientOnly(104, 104, 104);
1307 static const CMuleColour crFlatClientOnly(0, 0, 0);
1309 static const CMuleColour crPending(255, 208, 0);
1310 static const CMuleColour crNextPending(255, 255, 100);
1312 void CGenericClientListCtrl::DrawSourceStatusBar(
1313 const CClientRef& source, wxDC* dc, const wxRect& rect, bool bFlat) const
1315 static CBarShader s_StatusBar(16);
1317 CPartFile* reqfile = source.GetRequestFile();
1319 s_StatusBar.SetHeight(rect.height);
1320 s_StatusBar.SetWidth(rect.width);
1321 s_StatusBar.Set3dDepth( thePrefs::Get3DDepth() );
1322 const BitVector& partStatus = source.GetPartStatus();
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();
1330 for ( uint16 i = 0; i < partStatus.size(); i++ ) {
1331 uint64 uStart = PARTSIZE * i;
1332 uint64 uEnd = uStart + reqfile->GetPartSize(i) - 1;
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;
1357 if ( source.GetRequestFile()->IsStopped() ) {
1358 colour.Blend(50);
1361 s_StatusBar.FillRange(uStart, uEnd, colour);
1363 } else {
1364 s_StatusBar.SetFileSize(1);
1365 s_StatusBar.FillRange(0, 1, bFlat ? crFlatNeither : crNeither);
1368 s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
1371 static const CMuleColour crUnavailable(240, 240, 240);
1372 static const CMuleColour crFlatUnavailable(224, 224, 224);
1374 static const CMuleColour crAvailable(104, 104, 104);
1375 static const CMuleColour crFlatAvailable(0, 0, 0);
1377 void CGenericClientListCtrl::DrawStatusBar( const CClientRef& client, wxDC* dc, const wxRect& rect1 ) const
1379 wxRect rect = rect1;
1380 rect.y += 1;
1381 rect.height -= 2;
1383 wxPen old_pen = dc->GetPen();
1384 wxBrush old_brush = dc->GetBrush();
1385 bool bFlat = thePrefs::UseFlatBar();
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;
1394 static CBarShader s_StatusBar(16);
1396 uint32 partCount = client.GetUpPartCount();
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() );
1405 uint64 uEnd = 0;
1406 for ( uint64 i = 0; i < partCount; i++ ) {
1407 uint64 uStart = PARTSIZE * i;
1408 uEnd = uStart + PARTSIZE - 1;
1410 s_StatusBar.FillRange(uStart, uEnd, client.IsUpPartAvailable(i) ? (bFlat ? crFlatAvailable : crAvailable) : (bFlat ? crFlatUnavailable : crUnavailable));
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);
1416 if (!bFlat) {
1417 // Draw black border
1418 dc->SetPen( *wxBLACK_PEN );
1419 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1420 dc->DrawRectangle(rect);
1423 dc->SetPen( old_pen );
1424 dc->SetBrush( old_brush );
1427 // File_checked_for_headers