1 /* Copyright (C) 2007 The SpringLobby Team. All rights reserved. */
2 //
3 // Class: BattleroomListCtrl
4 //
5 
6 #include <wx/intl.h>
7 #include <wx/menu.h>
8 #include <wx/numdlg.h>
9 #include <wx/colordlg.h>
10 #include <wx/colour.h>
11 #include <wx/log.h>
12 
13 #include <stdexcept>
14 #include <vector>
15 
16 #include "battleroomlistctrl.h"
17 #include "iconimagelist.h"
18 #include "battle.h"
19 #include "uiutils.h"
20 #include "user.h"
21 #include "server.h"
22 #include "utils/debug.h"
23 #include "utils/conversion.h"
24 #include "uiutils.h"
25 #include "countrycodes.h"
26 #include "mainwindow.h"
27 #include "aui/auimanager.h"
28 #include "utils/customdialogs.h"
29 #include "settings.h"
30 
31 template<> SortOrder CustomVirtListCtrl<User*,BattleroomListCtrl>::m_sortorder = SortOrder();
32 
BEGIN_EVENT_TABLE(BattleroomListCtrl,BattleroomListCtrl::BaseType)33 BEGIN_EVENT_TABLE( BattleroomListCtrl,  BattleroomListCtrl::BaseType )
34 
35   EVT_LIST_ITEM_RIGHT_CLICK( BRLIST_LIST, BattleroomListCtrl::OnListRightClick )
36   EVT_MENU                 ( BRLIST_SPEC, BattleroomListCtrl::OnSpecSelect )
37   EVT_MENU                 ( BRLIST_KICK, BattleroomListCtrl::OnKickPlayer )
38   EVT_LIST_ITEM_ACTIVATED( BRLIST_LIST, BattleroomListCtrl::OnActivateItem )
39 //  EVT_MENU                 ( BRLIST_ADDCREATEGROUP, BattleroomListCtrl::OnPlayerAddToGroup )
40 //  EVT_MENU                 ( BRLIST_ADDTOGROUP, BattleroomListCtrl::OnPlayerAddToGroup )
41   EVT_MENU                 ( BRLIST_RING, BattleroomListCtrl::OnRingPlayer )
42   EVT_MENU                 ( BRLIST_COLOUR, BattleroomListCtrl::OnColourSelect )
43   EVT_MENU                 ( BRLIST_HANDICAP, BattleroomListCtrl::OnHandicapSelect )
44 #if wxUSE_TIPWINDOW
45 #ifndef __WXMSW__ //disables tooltips on win
46   EVT_MOTION(BattleroomListCtrl::OnMouseMotion)
47 #endif
48 #endif
49 END_EVENT_TABLE()
50 
51 
52 
53 BattleroomListCtrl::BattleroomListCtrl( wxWindow* parent, IBattle* battle, bool readonly, bool showingame )
54 	: CustomVirtListCtrl< User *,BattleroomListCtrl>(parent, BRLIST_LIST, wxDefaultPosition, wxDefaultSize,
55 													 wxSUNKEN_BORDER | wxLC_REPORT | wxLC_SINGLE_SEL, _T("BattleroomListCtrl"),
56 													 3, &BattleroomListCtrl::CompareOneCrit,
57 													true /*highlight*/, UserActions::ActHighlight, !readonly /*periodic sort*/ ),
58 	m_battle(battle),
59 	m_popup(0),
60     m_sel_user(0),
61     m_sides(0),
62     m_spec_item(0),
63     m_handicap_item(0),
64     m_ro(readonly),
65 	m_showingame(showingame),
66 	m_status_column_index(-1),
67 	m_ingame_column_index(-1),
68 	m_faction_column_index(-1),
69 	m_colour_column_index(-1),
70 	m_country_column_index(-1),
71 	m_rank_column_index(-1),
72 	m_nick_column_index(-1),
73 	m_team_column_index(-1),
74 	m_ally_column_index(-1),
75 	m_resourcebonus_column_index(-1)
76 {
77 	GetAui().manager->AddPane( this, wxLEFT, _T("battleroomlistctrl") );
78 
79 	wxListItem col;
80 
81 	int count = 0;
82     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _T("Status"), _T("Player/Bot") );
83 	m_status_column_index = count;
84 	count++;
85 	if ( m_showingame )
86 	{
87         AddColumn( count, 55, _T("Ingame"), _T("Battleroom status") );
88 		m_ingame_column_index = count;
89         count++;
90     }
91 
92     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _T("Faction"), _T("Faction icon") );
93 	m_faction_column_index = count;
94 	count++;
95     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _T("Colour"), _T("Teamcolour") );
96 	m_colour_column_index = count;
97 	count++;
98     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _T("Country"), _T("Country") );
99 	m_country_column_index = count;
100 	count++;
101     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _T("Rank"), _T("Rank") );
102 	m_rank_column_index = count;
103 	count++;
104     AddColumn( count, 165, _("Nickname"), _T("Ingame name"));
105 	m_nick_column_index = count;
106 	count++;
107     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _("Team"), _T("Team number") );
108 	m_team_column_index = count;
109 	count++;
110     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _("Ally"), _T("Ally number") );
111 	m_ally_column_index = count;
112 	count++;
113     AddColumn( count, wxLIST_AUTOSIZE_USEHEADER, _("Resource Bonus"), _T("Resource Bonus") );
114 	m_resourcebonus_column_index = count;
115 
116     if ( m_sortorder.size() == 0 ) {
117 		m_sortorder[0].col = m_team_column_index;
118         m_sortorder[0].direction = true;
119 		m_sortorder[1].col = m_ally_column_index;
120         m_sortorder[1].direction = true;
121 		m_sortorder[2].col = m_nick_column_index;
122         m_sortorder[2].direction = true;
123     }
124 
125   	if ( !m_ro )
126 	{
127 		m_popup = new UserMenu(this, this);
128 		wxMenu* m_teams;
129 		m_teams = new wxMenu();
130 
131 		for ( unsigned int i = 0; i < SPRING_MAX_TEAMS; i++ )
132 		{
133 			wxMenuItem* team = new wxMenuItem( m_teams, BRLIST_TEAM + i, wxFormat( _T("%d") ) % (i+1), wxEmptyString, wxITEM_NORMAL );
134 			m_teams->Append( team );
135 			Connect( BRLIST_TEAM + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnTeamSelect ) );
136 		}
137 		m_popup->Append( -1, _("Team"), m_teams );
138 
139 		wxMenu* m_allies = new wxMenu();
140 		for ( unsigned int i = 0; i < SPRING_MAX_ALLIES; i++ )
141 		{
142 			wxMenuItem* ally = new wxMenuItem( m_allies, BRLIST_ALLY + i, wxFormat( _T("%d") ) % (i+1), wxEmptyString, wxITEM_NORMAL );
143 			m_allies->Append( ally );
144 			Connect( BRLIST_ALLY + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnAllySelect ) );
145 		}
146 		m_popup->Append( -1, _("Ally"), m_allies );
147 
148 		m_sides = new wxMenu();
149 		SetBattle( battle );
150 		m_popup->Append( -1, _("Side"), m_sides );
151 
152 		m_popup->AppendSeparator();
153 
154 		wxMenuItem* m_colours = new wxMenuItem( m_popup, BRLIST_COLOUR, _("Set color"), wxEmptyString, wxITEM_NORMAL );
155 		m_popup->Append( m_colours );
156 
157 		m_popup->AppendSeparator();
158 
159 		m_handicap_item = new wxMenuItem( m_popup, BRLIST_HANDICAP, _("Set Resource Bonus"), wxEmptyString, wxITEM_NORMAL );
160 		m_popup->Append( m_handicap_item );
161 
162 		m_popup->AppendSeparator();
163 
164 		m_spec_item = new wxMenuItem( m_popup, BRLIST_SPEC, wxString( _("Spectator") ) , wxEmptyString, wxITEM_CHECK );
165 		m_popup->Append( m_spec_item );
166 
167 		m_popup->AppendSeparator();
168 
169 		wxMenuItem* kick = new wxMenuItem( m_popup, BRLIST_KICK, wxString( _("Kick") ) , wxEmptyString, wxITEM_NORMAL );
170 		m_popup->Append( kick );
171 		wxMenuItem* ring = new wxMenuItem( m_popup, BRLIST_RING, wxString( _("Ring") ) , wxEmptyString, wxITEM_NORMAL );
172 		m_popup->Append( ring );
173 
174 	}
175 }
176 
177 
~BattleroomListCtrl()178 BattleroomListCtrl::~BattleroomListCtrl()
179 {
180 
181 }
182 
SetBattle(IBattle * battle)183 void BattleroomListCtrl::SetBattle( IBattle* battle )
184 {
185 	m_battle = battle;
186 	for ( unsigned int i = 0; i < side_vector.size(); i++ )
187 	{
188 		wxMenuItem* side = side_vector[i];
189 		m_popup->Remove( side );
190 		m_sides->Destroy( side ); // delete side;
191 		Disconnect( BRLIST_SIDE + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnSideSelect ) );
192 	}
193 	m_data.clear();
194 	side_vector.clear();
195 	if ( m_battle && m_sides )
196 	{
197 		try
198 		{
199             const wxArrayString sides = LSL::Util::vectorToArrayString(
200                         LSL::usync().GetSides(STD_STRING(m_battle->GetHostModName())));
201 			for ( unsigned int i = 0; i < sides.GetCount(); i++ )
202 			{
203 				wxMenuItem* side = new wxMenuItem( m_sides, BRLIST_SIDE + i, sides[i], wxEmptyString, wxITEM_NORMAL );
204 				m_sides->Append( side );
205 				side_vector.push_back( side );
206 				Connect( BRLIST_SIDE + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnSideSelect ) );
207 			}
208 		} catch (...) {}
209 	}
210 }
211 
GetBattle()212 IBattle& BattleroomListCtrl::GetBattle()
213 {
214 	ASSERT_EXCEPTION( m_battle, _T("m_battle == 0") );
215 	return *m_battle;
216 }
217 
AddUser(User & user)218 void BattleroomListCtrl::AddUser( User& user )
219 {
220 	//first time setting is necessary to have color in replay/savegame used controls
221 	if ( !user.BattleStatus().spectator )
222 		icons().SetColourIcon( user.BattleStatus().colour );
223     if ( AddItem( &user ) )
224         return;
225 
226     wxLogWarning( _T("user already in battleroom list.") );
227 }
228 
RemoveUser(User & user)229 void BattleroomListCtrl::RemoveUser( User& user )
230 {
231     if ( RemoveItem( &user ) )
232         return;
233 
234     wxLogError( _T("Didn't find the user to remove in battleroomlistctrl.") );
235 }
236 
UpdateUser(User & user)237 void BattleroomListCtrl::UpdateUser( User& user )
238 {
239     if ( !user.BattleStatus().spectator )
240 		icons().SetColourIcon( user.BattleStatus().colour );
241     wxArrayString sides = LSL::Util::vectorToArrayString(LSL::usync().GetSides(STD_STRING(m_battle->GetHostModName())));
242     ASSERT_EXCEPTION( user.BattleStatus().side < (long)sides.GetCount(), _T("Side index too high") );
243     user.SetSideiconIndex( icons().GetSideIcon( m_battle->GetHostModName(), user.BattleStatus().side ) );
244     int index = GetIndexFromData( &user );
245     UpdateUser( index );
246 }
247 
GetItemAttr(long item) const248 wxListItemAttr * BattleroomListCtrl::GetItemAttr(long item) const
249 {
250     if ( item == -1 || item >= (long)m_data.size())
251         return NULL;
252 
253     const User& user = *GetDataFromIndex( item );
254     bool is_bot = user.BattleStatus().IsBot();
255 
256     if ( !is_bot ) {
257         return HighlightItemUser( user.GetNick() );
258     }
259 
260     return NULL;
261 }
262 
GetItemColumnImage(long item,long column) const263 int BattleroomListCtrl::GetItemColumnImage(long item, long column) const
264 {
265     if ( (item == -1) || (item >= (long)m_data.size()) || (m_battle == NULL) )
266         return -1;
267 
268     const User& user = *GetDataFromIndex( item );
269     bool is_bot = user.BattleStatus().IsBot();
270     bool is_spec = user.BattleStatus().spectator;
271 	 if ( column == m_status_column_index ) {
272 		if ( !is_bot ) {
273 			if ( m_battle->IsFounder( user ) ) {
274 				return icons().GetHostIcon( is_spec );
275 			}
276 			else {
277 				return icons().GetReadyIcon( is_spec, user.BattleStatus().ready, user.BattleStatus().sync, is_bot );
278 			}
279 		}
280 		else
281 			return icons().ICON_BOT;
282 	}
283 	 if ( column == m_ingame_column_index ) return user.GetStatusIconIndex();
284 	 if ( column == m_colour_column_index ) return is_spec ? -1 : icons().GetColourIcon( user.BattleStatus().colour );
285 	 if ( column == m_country_column_index ) return is_bot ? -1 : icons().GetFlagIcon( user.GetCountry() );
286 	 if ( column == m_rank_column_index ) return is_bot ? -1 : icons().GetRankIcon( user.GetStatus().rank );
287 	 if ( column == m_faction_column_index ) return is_spec ? -1 : user.GetSideiconIndex();
288 	 if ( column == m_nick_column_index ) return -1;
289 	 else
290 	 {
291 		const wxString msg =  wxFormat(_("column oob in BattleroomListCtrl::OnGetItemColumnImage: %d" )) % column;
292 		wxLogWarning( msg);
293 		return -1;
294 	 }
295 }
296 
GetItemText(long item,long column) const297 wxString BattleroomListCtrl::GetItemText(long item, long column) const
298 {
299 	if ( (item == -1) || (item >= (long)m_data.size()) || (m_battle == NULL))
300 		return wxEmptyString;
301 
302 	const User& user = *GetDataFromIndex( item );
303 	bool is_bot = user.BattleStatus().IsBot();
304 	bool is_spec = user.BattleStatus().spectator;
305 
306 	if ( column == m_faction_column_index ) {
307 		try {
308             auto sides = LSL::usync().GetSides(STD_STRING(m_battle->GetHostModName()));
309             ASSERT_EXCEPTION( user.BattleStatus().side < (long)sides.size(), _T("Side index too high") );
310 		}
311 		catch ( ... ) {
312 			return wxFormat( _T("s%d") ) % (user.BattleStatus().side + 1);
313 		}
314 		return wxEmptyString;
315 	}
316     if ( column == m_nick_column_index )  {
317         if ( is_bot ) {
318             wxString botname = user.BattleStatus().aishortname;
319             if ( !user.BattleStatus().aiversion.IsEmpty() ) botname += _T(" ") + user.BattleStatus().aiversion;
320             if ( !LSL::usync().VersionSupports( LSL::USYNC_GetSkirmishAI ) )
321             {
322                 if ( botname.Find(_T('.')) != wxNOT_FOUND ) botname = botname.BeforeLast(_T('.'));
323                 if ( botname.Find(_T('/')) != wxNOT_FOUND ) botname = botname.AfterLast(_T('/'));
324                 if ( botname.Find(_T('\\')) != wxNOT_FOUND ) botname = botname.AfterLast(_T('\\'));
325                 if ( botname.Find(_T("LuaAI:")) != wxNOT_FOUND ) botname = botname.AfterFirst(_T(':'));
326             }
327             return (wxFormat(_T("%s - %s (%s)")) % user.GetNick() % botname % user.BattleStatus().owner);
328         }
329         else
330             return user.GetNick();
331     }
332 	if ( column == m_team_column_index ) return is_spec ? (wxString)wxEmptyString : (wxFormat( _T("%d") ) % ( user.BattleStatus().team + 1 ) );
333 	if ( column == m_ally_column_index ) return is_spec ? (wxString)wxEmptyString : (wxFormat( _T("%d") ) % ( user.BattleStatus().ally + 1 ) );
334 	if ( column == m_resourcebonus_column_index ) return is_spec ? (wxString)wxEmptyString : (wxFormat( _T("%d%%") ) % user.BattleStatus().handicap );
335 	if ( column == m_country_column_index ) return wxEmptyString;
336 
337 	return wxEmptyString;
338 }
339 
UpdateUser(const int & index)340 void BattleroomListCtrl::UpdateUser( const int& index )
341 {
342     wxWindowUpdateLocker lock( this );
343     RefreshItem( index );
344     MarkDirtySort();
345 }
346 
OnListRightClick(wxListEvent & event)347 void BattleroomListCtrl::OnListRightClick( wxListEvent& event )
348 {
349     wxLogDebugFunc( wxEmptyString );
350 	if ( m_ro ) return;
351 	int index = event.GetIndex();
352 
353     if ( index == -1 || index >= (long)m_data.size()) return;
354 
355     User& user = *GetDataFromIndex( event.GetIndex() );
356     m_sel_user = &user; //this is set for event handlers
357 
358     if ( user.BattleStatus().IsBot() )
359     {
360         wxLogMessage(_T("Bot"));
361 
362         int item = m_popup->FindItem( _("Spectator") );
363         m_popup->Enable( item, false );
364         m_popup->Check( item, false );
365         m_popup->Enable( m_popup->FindItem( _("Ring") ), false );
366         m_popup->Enable( m_popup->FindItem( _("Kick") ),true);
367     }
368     else
369     {
370         wxLogMessage(_T("User"));
371         assert( m_popup );
372         int item = m_popup->FindItem( _("Spectator") );
373         m_popup->Check( item, m_sel_user->BattleStatus().spectator );
374         m_popup->Enable( item, true );
375         m_popup->Enable( m_popup->FindItem( _("Ring") ), true );
376         bool isSelUserMe =  ( ui().IsThisMe(user) );
377         m_popup->Enable( m_popup->FindItem( _("Kick") ),!isSelUserMe);
378     }
379 
380     wxLogMessage(_T("Popup"));
381     m_popup->EnableItems( !user.BattleStatus().IsBot(), GetSelectedUserNick() );//this updates groups, therefore we need to update the connection to evt handlers too
382     std::vector<int> groups_ids = m_popup->GetGroupIds();
383     for (std::vector<int>::const_iterator it = groups_ids.begin(); it != groups_ids.end(); ++it) {
384         Connect( *it, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnUserMenuAddToGroup ), 0, this );
385     }
386     Connect( GROUP_ID_NEW, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnUserMenuCreateGroup), 0, this );
387     PopupMenu( m_popup );
388     wxLogMessage(_T("Done"));
389 }
390 
391 
OnTeamSelect(wxCommandEvent & event)392 void BattleroomListCtrl::OnTeamSelect( wxCommandEvent& event )
393 {
394   wxLogDebugFunc( wxEmptyString );
395   int team = event.GetId() - BRLIST_TEAM;
396 	if( m_sel_user ) ((Battle*)m_battle)->ForceTeam( *m_sel_user, team );
397 }
398 
399 
OnAllySelect(wxCommandEvent & event)400 void BattleroomListCtrl::OnAllySelect( wxCommandEvent& event )
401 {
402   wxLogDebugFunc( wxEmptyString );
403   int ally = event.GetId() - BRLIST_ALLY;
404 	if( m_sel_user ) ((Battle*)m_battle)->ForceAlly( *m_sel_user, ally );
405 }
406 
407 
OnColourSelect(wxCommandEvent &)408 void BattleroomListCtrl::OnColourSelect( wxCommandEvent& /*unused*/ )
409 {
410   wxLogDebugFunc( wxEmptyString );
411 
412 	wxColour CurrentColour = m_sel_user->BattleStatus().colour;
413 	CurrentColour = GetColourFromUser(this, CurrentColour);
414 	if ( !CurrentColour.IsOk() ) return;
415 	if( m_sel_user ) ((Battle*)m_battle)->ForceColour( *m_sel_user, CurrentColour );
416 
417 }
418 
419 
OnSideSelect(wxCommandEvent & event)420 void BattleroomListCtrl::OnSideSelect( wxCommandEvent& event )
421 {
422   wxLogDebugFunc( wxEmptyString );
423   int side = event.GetId() - BRLIST_SIDE;
424   if( m_sel_user ) ((Battle*)m_battle)->ForceSide( *m_sel_user, side );
425 }
426 
427 
OnHandicapSelect(wxCommandEvent &)428 void BattleroomListCtrl::OnHandicapSelect( wxCommandEvent& /*unused*/ )
429 {
430   wxLogDebugFunc( wxEmptyString );
431   if( !m_sel_user ) return;
432   long handicap = wxGetNumberFromUser( _("Please enter a value between 0 and 100"), _("Set Resource Bonus"), wxEmptyString, m_sel_user->BattleStatus().handicap, 0, 100, (wxWindow*)&ui().mw(), wxDefaultPosition );
433 	if ( handicap != -1 )
434 	{
435      ((Battle*)m_battle)->SetHandicap( *m_sel_user, handicap );
436   }
437 }
438 
439 
OnSpecSelect(wxCommandEvent &)440 void BattleroomListCtrl::OnSpecSelect( wxCommandEvent& /*unused*/ )
441 {
442   wxLogDebugFunc( wxEmptyString );
443   if ( m_sel_user ) ((Battle*)m_battle)->ForceSpectator( *m_sel_user, m_spec_item->IsChecked() );
444 }
445 
446 
OnKickPlayer(wxCommandEvent &)447 void BattleroomListCtrl::OnKickPlayer( wxCommandEvent& /*unused*/ )
448 {
449   wxLogDebugFunc( wxEmptyString );
450 	if ( m_sel_user ) ((Battle*)m_battle)->KickPlayer( *m_sel_user );
451 }
452 
453 
OnRingPlayer(wxCommandEvent &)454 void BattleroomListCtrl::OnRingPlayer( wxCommandEvent& /*unused*/ )
455 {
456   wxLogDebugFunc( wxEmptyString );
457   if ( m_sel_user ) ((Battle*)m_battle)->GetServer().Ring( m_sel_user->GetNick() );
458 }
459 
Sort()460 void BattleroomListCtrl::Sort()
461 {
462     if ( m_data.size() > 0 )
463     {
464         SaveSelection();
465         SLInsertionSort( m_data, m_comparator );
466         RestoreSelection();
467     }
468 }
469 
CompareOneCrit(DataType u1,DataType u2,int col,int dir) const470 int BattleroomListCtrl::CompareOneCrit(DataType u1, DataType u2, int col, int dir) const
471 {
472 	if ( col == m_status_column_index ) return dir * CompareStatus( u1, u2, m_battle );
473 	if ( col == m_ingame_column_index ) return dir * CompareLobbyStatus( u1, u2 );
474 	if ( col == m_faction_column_index ) return dir * CompareSide( u1, u2 );
475 	if ( col == m_colour_column_index ) return dir * CompareColor( u1, u2 );
476 	if ( col == m_country_column_index ) return dir * compareSimple( u1, u2 );
477 	if ( col == m_rank_column_index ) return dir * CompareRank( u1, u2 );
478 	if ( col == m_nick_column_index ) return dir * u1->GetNick().CmpNoCase( u2->GetNick() );
479 	if ( col == m_team_column_index ) return dir * CompareTeam( u1, u2 );
480 	if ( col == m_ally_column_index ) return dir * CompareAlly( u1, u2 );
481 	if ( col == m_resourcebonus_column_index ) return dir * CompareHandicap( u1, u2 );
482 	return 0;
483 }
484 
CompareLobbyStatus(const DataType user1,const DataType user2)485 int BattleroomListCtrl::CompareLobbyStatus( const DataType user1, const DataType user2 )
486 {
487 	int u1 = 0, u2 = 0;
488 
489 	if ( user1->GetStatus().bot )
490 		u1 += 1000;
491 	if ( user2->GetStatus().bot )
492 		u2 += 1000;
493 	if ( user1->GetStatus().moderator )
494 		u1 += 100;
495 	if ( user2->GetStatus().moderator )
496 		u2 += 100;
497 	if ( user1->GetStatus().in_game )
498 		u1 += -10;
499 	if ( user2->GetStatus().in_game )
500 		u2 += -10;
501 	if ( user1->GetStatus().away )
502 		u1 += -5;
503 	if ( user2->GetStatus().away )
504 		u2 += -5;
505 
506 	// inverse the order
507 	if ( u1 < u2 )
508 		return -1;
509 	if ( u1 > u2 )
510 		return 1;
511 
512 	return 0;
513 }
514 
CompareStatus(const DataType user1,const DataType user2,const IBattle * battle)515 int BattleroomListCtrl::CompareStatus(const DataType user1, const DataType user2, const IBattle* battle )
516 {
517   int status1 = 0;
518   if ( user1->BattleStatus().IsBot() )
519   {
520     status1 = 9;
521   }
522   else
523   {
524   	try
525   	{
526 	if ( &battle->GetFounder() != user1 )
527       status1 = 1;
528 		}catch(...){}
529     if ( user1->BattleStatus().ready )
530       status1 += 5;
531     if ( user1->BattleStatus().sync )
532       status1 += 7;
533     if ( user1->BattleStatus().spectator )
534       status1 += 10;
535   }
536 
537   int status2 = 0;
538   if ( user2->BattleStatus().IsBot() )
539   {
540     status2 = 9;
541   }
542   else
543   {
544   	try
545   	{
546 	if ( &battle->GetFounder() != user2 )
547       status2 = 1;
548 		}catch(...){}
549     if ( user2->BattleStatus().ready )
550       status2 += 5;
551     if ( user2->BattleStatus().sync )
552       status2 += 7;
553     if ( user2->BattleStatus().spectator )
554       status2 += 10;
555   }
556 
557   if ( status1 < status2 )
558       return -1;
559   if ( status1 > status2 )
560       return 1;
561 
562   return 0;
563 }
564 
CompareSide(const DataType user1,const DataType user2)565 int BattleroomListCtrl::CompareSide(const DataType user1, const DataType user2)
566 {
567 	if ( !user1 && !user2 ) return 0;
568 	else if ( !user1 ) return 1;
569 	else if ( !user2 ) return -1;
570 
571   int side1;
572   if ( user1->BattleStatus().spectator )
573     side1 = 1000;
574   else
575     side1 = user1->BattleStatus().side;
576 
577   int side2;
578   if ( user2->BattleStatus().spectator )
579     side2 = 1000;
580   else
581     side2 = user2->BattleStatus().side;
582 
583   if ( side1 < side2 )
584       return -1;
585   if ( side1 > side2 )
586       return 1;
587 
588   return 0;
589 }
590 
CompareColor(const DataType user1,const DataType user2)591 int BattleroomListCtrl::CompareColor(const DataType user1, const DataType user2)
592 {
593 	if ( !user1 && !user2 ) return 0;
594 	else if ( !user1 ) return 1;
595 	else if ( !user2 ) return -1;
596 
597   int color1_r, color1_g, color1_b;
598 
599 	if ( user1->BattleStatus().spectator ) return -1;
600 	color1_r = user1->BattleStatus().colour.Red();
601 	color1_g = user1->BattleStatus().colour.Green();
602 	color1_b = user1->BattleStatus().colour.Blue();
603 
604   int color2_r, color2_g, color2_b;
605 
606 	if ( user2->BattleStatus().spectator ) return 1;
607 	color2_r = user2->BattleStatus().colour.Red();
608 	color2_g = user2->BattleStatus().colour.Green();
609 	color2_b = user2->BattleStatus().colour.Blue();
610 
611   if ( (color1_r + color1_g + color1_b)/3 < (color2_r + color2_g + color2_b)/3 )
612       return -1;
613   if ( (color1_r + color1_g + color1_b)/3 > (color2_r + color2_g + color2_b)/3 )
614       return 1;
615 
616   return 0;
617 }
618 
619 
CompareRank(const DataType user1,const DataType user2)620 int BattleroomListCtrl::CompareRank(const DataType user1, const DataType user2)
621 {
622 	if ( !user1 && !user2 ) return 0;
623 	else if ( !user1 ) return 1;
624 	else if ( !user2 ) return -1;
625 
626   int rank1;
627   if ( user1->BattleStatus().IsBot() )
628     rank1 = 1000;
629   else
630     rank1 = user1->GetStatus().rank;
631 
632   int rank2;
633   if ( user2->BattleStatus().IsBot() )
634     rank2 = 1000;
635   else
636     rank2 = user2->GetStatus().rank;
637 
638   if ( rank1 < rank2 )
639       return -1;
640   if ( rank1 > rank2 )
641       return 1;
642 
643   return 0;
644 }
645 
CompareTeam(const DataType user1,const DataType user2)646 int BattleroomListCtrl::CompareTeam(const DataType user1, const DataType user2)
647 {
648 	if ( !user1 && !user2 ) return 0;
649 	else if ( !user1 ) return 1;
650 	else if ( !user2 ) return -1;
651 
652   int team1;
653   if ( user1->BattleStatus().spectator )
654     team1 = 1000;
655   else
656     team1 = user1->BattleStatus().team;
657 
658   int team2;
659   if ( user2->BattleStatus().spectator )
660     team2 = 1000;
661   else
662     team2 = user2->BattleStatus().team;
663 
664   if ( team1 < team2 )
665       return -1;
666   if ( team1 > team2 )
667       return 1;
668 
669   return 0;
670 }
671 
CompareAlly(const DataType user1,const DataType user2)672 int BattleroomListCtrl::CompareAlly(const DataType user1, const DataType user2)
673 {
674 	if ( !user1 && !user2 ) return 0;
675 	else if ( !user1 ) return 1;
676 	else if ( !user2 ) return -1;
677 
678   int ally1;
679   if ( user1->BattleStatus().spectator )
680     ally1 = 1000;
681   else
682     ally1 = user1->BattleStatus().ally;
683 
684   int ally2;
685   if ( user2->BattleStatus().spectator )
686     ally2 = 1000;
687   else
688     ally2 = user2->BattleStatus().ally;
689 
690   if ( ally1 < ally2 )
691       return -1;
692   if ( ally1 > ally2 )
693       return 1;
694 
695   return 0;
696 }
697 
698 
CompareHandicap(const DataType user1,const DataType user2)699 int BattleroomListCtrl::CompareHandicap(const DataType user1, const DataType user2)
700 {
701   int handicap1;
702 	if ( user1->BattleStatus().spectator )
703     handicap1 = 1000;
704   else
705     handicap1 = user1->BattleStatus().handicap;
706 
707   int handicap2;
708 	if ( user2->BattleStatus().spectator )
709     handicap2 = 1000;
710   else
711     handicap2 = user2->BattleStatus().handicap;
712 
713   if ( handicap1 < handicap2 )
714       return -1;
715   if ( handicap1 > handicap2 )
716       return 1;
717 
718   return 0;
719 }
720 
SetTipWindowText(const long item_hit,const wxPoint & position)721 void BattleroomListCtrl::SetTipWindowText( const long item_hit, const wxPoint& position)
722 {
723     if ( item_hit < 0 || item_hit >= (long)m_data.size() )
724         return;
725 	if ( !m_battle ) return;
726     const User& user = *GetDataFromIndex( item_hit );
727 
728     int column = getColumnFromPosition( position );
729     if (column > (int)m_colinfovec.size() || column < 0)
730     {
731         m_tiptext = wxEmptyString;
732     }
733     else
734     {
735 		if ( column == m_status_column_index ) // is bot?
736 		{
737 			m_tiptext = wxEmptyString;
738 
739 			if ( user.BattleStatus().IsBot() )
740 			{
741 				m_tiptext += _("AI (bot)\n");
742 			}
743 			else
744 			{
745 				m_tiptext += _("Human\n");
746 			}
747 
748 			if ( user.BattleStatus().spectator )
749 			{
750 				m_tiptext += _("Spectator\n");
751 			}
752 			else
753 			{
754 				m_tiptext += _("Player\n");
755 			}
756 
757 			if ( m_battle->IsFounder( user ) )
758 			{
759 				m_tiptext += _("Host\n");
760 			}
761 			else
762 			{
763 				m_tiptext += _("Client\n");
764 			}
765 
766 			if ( user.BattleStatus().ready )
767 			{
768 				m_tiptext += _("Ready\n");
769 			}
770 			else
771 			{
772 				m_tiptext += _("Not ready\n");
773 			}
774 
775 			if  ( user.BattleStatus().sync == SYNC_SYNCED )
776 			{
777 				m_tiptext += _("In sync");
778 			}
779 			else
780 			{
781 				m_tiptext += _("Not in sync");
782 			}
783 		}
784 		else if ( column == m_faction_column_index ) // icon
785 		{
786 			m_tiptext = wxEmptyString;
787 			if ( user.BattleStatus().spectator )
788 			{
789 				m_tiptext = _T("Spectators have no side");
790 			}
791 			else
792 			{
793 				try
794 				{
795                     const auto sides = LSL::usync().GetSides(STD_STRING(m_battle->GetHostModName()));
796                     const int side = user.BattleStatus().side;
797                     if ( side < (int)sides.size() )
798                         m_tiptext = TowxString(sides[side]);
799 				}
800 				catch (...){}
801 			}
802 		}
803 		else if ( column == m_ingame_column_index ) // lobby status
804 		{
805 			m_tiptext = _T( "This " );
806 			if ( user.GetStatus().bot )
807 			{
808 				m_tiptext << _T( "bot " );
809 			}
810 			else if ( user.GetStatus().moderator )
811 			{
812 				m_tiptext << _T( "moderator " );
813 			}
814 			else
815 			{
816 				m_tiptext << _T( "player " );
817 			}
818 
819 			if ( user.GetStatus().in_game )
820 			{
821 				m_tiptext <<  _T( "is ingame" );
822 			}
823 			else if ( user.GetStatus().away )
824 			{
825 				m_tiptext <<  _T( "is away" );
826 			}
827 			else
828 			{
829 				m_tiptext << _T( "is available" );
830 			}
831 		}
832 		else if ( column == m_country_column_index ) // country
833 		{
834 			m_tiptext = user.BattleStatus().IsBot() ? _T("This bot is from nowhere particular") : GetFlagNameFromCountryCode(user.GetCountry());
835 		}
836 		else if ( column == m_rank_column_index ) // rank
837 		{
838 			m_tiptext = user.BattleStatus().IsBot() ? _T("This bot has no rank") : user.GetRankName(user.GetStatus().rank);
839 		}
840 		else if ( column == m_nick_column_index ) //name
841 		{
842 			m_tiptext = user.BattleStatus().IsBot() ?user.GetNick() : user.GetNick();
843 		}
844 		else m_tiptext = m_colinfovec[column].tip;
845     }
846 }
847 
OnUserMenuAddToGroup(wxCommandEvent & event)848 void BattleroomListCtrl::OnUserMenuAddToGroup( wxCommandEvent& event )
849 {
850     int id  = event.GetId();
851     wxString groupname = m_popup->GetGroupByEvtID(id);
852     wxString nick = GetSelectedUserNick();
853     useractions().AddUserToGroup( groupname, nick );
854     Disconnect( GROUP_ID_NEW, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( BattleroomListCtrl::OnUserMenuCreateGroup), 0, this );
855 }
856 
OnUserMenuDeleteFromGroup(wxCommandEvent &)857 void BattleroomListCtrl::OnUserMenuDeleteFromGroup( wxCommandEvent& /*unused*/ )
858 {
859     wxString nick = GetSelectedUserNick();
860     useractions().RemoveUser( nick );
861 }
862 
OnUserMenuCreateGroup(wxCommandEvent &)863 void BattleroomListCtrl::OnUserMenuCreateGroup( wxCommandEvent& /*unused*/ )
864 {
865     wxString name;
866     if ( ui().AskText( _("Enter name"),
867         _("Please enter the name for the new group.\nAfter clicking ok you will be taken to adjust its settings."), name ) )
868     {
869         wxString nick = GetSelectedUserNick();
870 
871         useractions().AddGroup( name );
872         useractions().AddUserToGroup( name, nick );
873         ui().mw().ShowConfigure( MainWindow::OPT_PAGE_GROUPS );
874     }
875 }
876 
GetSelectedUserNick()877 wxString BattleroomListCtrl::GetSelectedUserNick()
878 {
879     if ( m_selected_index < 0 || m_selected_index >= (long)m_data.size() )
880         return wxEmptyString;
881     else
882         return m_data[m_selected_index]->GetNick();
883 }
884 
OnActivateItem(wxListEvent &)885 void BattleroomListCtrl::OnActivateItem( wxListEvent& /*unused*/ )
886 {
887     if ( m_ro )
888         return;
889     if ( m_selected_index < 0 || m_selected_index >= (long)m_data.size() )
890         return;
891 
892     const User* usr = m_data[m_selected_index];
893     if ( usr != NULL && !usr->BattleStatus().IsBot() )
894         ui().mw().OpenPrivateChat( *usr );
895 }
896 
GetIndexFromData(const DataType & data) const897 int BattleroomListCtrl::GetIndexFromData(const DataType& data) const
898 {
899     const User* user = data;
900     static long seekpos;
901     seekpos = LSL::Util::Clamp( seekpos, 0l , (long)m_data.size() );
902     int index = seekpos;
903 
904     for ( DataCIter f_idx = m_data.begin() + seekpos; f_idx != m_data.end() ; ++f_idx )
905     {
906         if ( user == *f_idx )
907         {
908             seekpos = index;
909             return seekpos;
910         }
911         index++;
912     }
913     //it's ok to init with seekpos, if it had changed this would not be reached
914     int r_index = seekpos - 1;
915     for ( DataRevCIter r_idx = m_data.rbegin() + ( m_data.size() - seekpos ); r_idx != m_data.rend() ; ++r_idx )
916     {
917         if ( user == *r_idx )
918         {
919             seekpos = r_index;
920             return seekpos;
921         }
922         r_index--;
923     }
924 
925     return -1;
926 }
927 
928