1 #include <wx/intl.h>
2 #include <wx/stattext.h>
3 #include <wx/statline.h>
4 #include <wx/textdlg.h>
5 #include <wx/combobox.h>
6 #include <wx/button.h>
7 #include <wx/sizer.h>
8 #include <wx/checkbox.h>
9 #include <wx/filename.h>
10 #include <wx/log.h>
11 #if wxUSE_TOGGLEBTN
12     #include <wx/tglbtn.h>
13 #endif
14 
15 #include "playbacklistctrl.h"
16 #include "replaylist.h"
17 #include "playbackthread.h"
18 #include "ui.h"
19 #include "chatpanel.h"
20 #include "uiutils.h"
21 #include "settings.h"
22 #include "mapctrl.h"
23 #include "playbackfilter.h"
24 #include "iconimagelist.h"
25 
26 #include "utils/customdialogs.h"
27 #include "utils/debug.h"
28 #include "hosting/battleroomlistctrl.h"
29 
30 
BEGIN_EVENT_TABLE_TEMPLATE1(PlaybackTab,wxPanel,PlaybackTraits)31 BEGIN_EVENT_TABLE_TEMPLATE1( PlaybackTab, wxPanel, PlaybackTraits )
32 
33     EVT_BUTTON              ( PLAYBACK_WATCH                , PlaybackTab::OnWatch          )
34     EVT_BUTTON              ( PLAYBACK_RELOAD               , PlaybackTab::OnReload         )
35     EVT_BUTTON              ( PLAYBACK_DELETE               , PlaybackTab::OnDelete         )
36     EVT_LIST_ITEM_SELECTED  ( RLIST_LIST                    , PlaybackTab::OnSelect         )
37     // this doesn't get triggered (?)
38     EVT_LIST_ITEM_DESELECTED( wxID_ANY                      , PlaybackTab::OnDeselect       )
39     EVT_CHECKBOX            ( PLAYBACK_LIST_FILTER_ACTIV    , PlaybackTab::OnFilterActiv    )
40     EVT_COMMAND             ( wxID_ANY, PlaybacksLoadedEvt  , PlaybackTab::AddAllPlaybacks  )
41     #if  wxUSE_TOGGLEBTN
42     EVT_TOGGLEBUTTON        ( PLAYBACK_LIST_FILTER_BUTTON   , PlaybackTab::OnFilter         )
43     #else
44     EVT_CHECKBOX            ( PLAYBACK_LIST_FILTER_BUTTON   , PlaybackTab::OnFilter         )
45     #endif
46 
47 END_EVENT_TABLE()
48 
49 template < class PlaybackTraits >
50 PlaybackTab<PlaybackTraits>::PlaybackTab( wxWindow* parent ):
51 	wxScrolledWindow( parent, -1 ),
52 	m_replay_loader ( 0 )
53 {
54 	wxLogMessage( _T( "PlaybackTab::PlaybackTab()" ) );
55 
56 	m_replay_loader = new LoaderType( this );
57 
58 	wxBoxSizer* m_main_sizer;
59 	m_main_sizer = new wxBoxSizer( wxVERTICAL );
60 
61 	wxBoxSizer* m_filter_sizer;
62 	m_filter_sizer = new wxBoxSizer( wxVERTICAL );
63 
64 	wxBoxSizer* m_replaylist_sizer;
65 	m_replaylist_sizer = new wxBoxSizer( wxVERTICAL );
66 
67 	m_replay_listctrl = new ListCtrlType( this );
68 	m_replaylist_sizer->Add( m_replay_listctrl, 1, wxEXPAND);
69 
70 	m_main_sizer->Add( m_replaylist_sizer, 1, wxEXPAND);;
71 
72 	wxBoxSizer* m_info_sizer;
73 	m_info_sizer = new wxBoxSizer( wxHORIZONTAL );
74 
75 	m_minimap = new MapCtrl( this, 100, 0, true, true, false, false );
76 	m_info_sizer->Add( m_minimap, 0, wxALL, 5 );
77 
78 	wxFlexGridSizer* m_data_sizer;
79 	m_data_sizer = new wxFlexGridSizer( 4, 2, 0, 0 );
80 
81 	m_map_lbl = new wxStaticText( this, wxID_ANY, _( "Map:" ), wxDefaultPosition, wxDefaultSize, 0 );
82 	m_data_sizer->Add( m_map_lbl, 1, wxALL | wxEXPAND, 5 );
83 
84 	m_map_text = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
85 	m_data_sizer->Add( m_map_text, 1, wxALL | wxEXPAND, 5 );
86 
87 	m_mod_lbl = new wxStaticText( this, wxID_ANY, _( "Game:" ), wxDefaultPosition, wxDefaultSize, 0 );
88 	m_data_sizer->Add( m_mod_lbl, 1, wxALL | wxEXPAND, 5 );
89 
90 	m_mod_text = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
91 	m_data_sizer->Add( m_mod_text, 1, wxALL | wxEXPAND, 5 );
92 
93 	m_players_lbl = new wxStaticText( this, wxID_ANY, _( "Players:" ), wxDefaultPosition, wxDefaultSize, 0 );
94 	m_data_sizer->Add( m_players_lbl, 1, wxALL | wxEXPAND, 5 );
95 
96 	m_players_text = new wxStaticText( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
97 	m_data_sizer->Add( m_players_text, 1, wxALL | wxEXPAND, 5 );
98 
99 	m_info_sizer->Add( m_data_sizer, 1, wxEXPAND | wxALL, 0 );
100 
101 	m_players = new BattleroomListCtrl( this, 0, true, false );
102 	m_info_sizer->Add( m_players , 2, wxALL | wxEXPAND, 0 );
103 
104 	m_main_sizer->Add( m_info_sizer, 0, wxEXPAND, 5 );
105 
106 
107 	m_filter = new PlaybackListFilter<ThisType>( this , wxID_ANY, this , wxDefaultPosition, wxSize( -1, -1 ), wxEXPAND );
108 	m_filter_sizer->Add( m_filter, 0, wxEXPAND, 5 );
109 
110 	m_main_sizer->Add( m_filter_sizer, 0, wxEXPAND, 5 );
111 
112 	m_buttons_sep = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
113 	m_main_sizer->Add( m_buttons_sep, 0, wxALL | wxEXPAND, 5 );
114 
115 	wxBoxSizer* m_buttons_sizer;
116 	m_buttons_sizer = new wxBoxSizer( wxHORIZONTAL );
117 
118 #if  wxUSE_TOGGLEBTN
119 	m_filter_show = new wxToggleButton( this, PLAYBACK_LIST_FILTER_BUTTON , wxT( " Filter " ), wxDefaultPosition , wxSize( -1, 28 ), 0 );
120 #else
121 	m_filter_show = new wxCheckBox( this, PLAYBACK_LIST_FILTER_BUTTON , wxT( " Filter " ), wxDefaultPosition , wxSize( -1, 28 ), 0 );
122 #endif
123 
124 	m_buttons_sizer->Add( m_filter_show, 0, 0, 5 );
125 
126 	m_filter_activ = new wxCheckBox( this, PLAYBACK_LIST_FILTER_ACTIV , wxT( "Activated" ), wxDefaultPosition, wxDefaultSize, 0 );
127 	m_buttons_sizer->Add( m_filter_activ, 1, wxALL | wxEXPAND, 5 );
128 
129 	m_buttons_sizer->Add( 0, 0, 1, wxEXPAND, 0 );
130 
131 	m_watch_btn = new wxButton( this, PLAYBACK_WATCH, PlaybackTraits::IsReplayType ? _( "Watch" ) : _( "Load" ), wxDefaultPosition, wxSize( -1, 28 ), 0 );
132 	m_buttons_sizer->Add( m_watch_btn, 0, wxBOTTOM | wxLEFT | wxRIGHT, 5 );
133 
134 	m_delete_btn = new wxButton( this, PLAYBACK_DELETE, _( "Delete" ), wxDefaultPosition, wxSize( -1, 28 ), 0 );
135 	m_buttons_sizer->Add( m_delete_btn, 0, wxBOTTOM | wxRIGHT, 5 );
136 
137 	m_reload_btn = new wxButton( this, PLAYBACK_RELOAD, _( "Reload list" ), wxDefaultPosition, wxSize( -1, 28 ), 0 );
138 	m_buttons_sizer->Add( m_reload_btn, 0, wxBOTTOM | wxRIGHT, 5 );
139 
140 	m_main_sizer->Add( m_buttons_sizer, 0, wxEXPAND, 5 );
141 
142 	m_filter->Hide();
143 
144 	SetSizer( m_main_sizer );
145 
146     ReloadList();
147 
148 	//none selected --> shouldn't watch/delete that
149 	Deselect();
150 
151 	SetScrollRate( SCROLL_RATE, SCROLL_RATE );
152 	Layout();
153 	ConnectGlobalEvent(this, GlobalEvent::OnUnitsyncReloaded, wxObjectEventFunction(&PlaybackTab::OnUnitsyncReloaded));
154 	ConnectGlobalEvent(this, GlobalEvent::OnSpringTerminated, wxObjectEventFunction(&PlaybackTab::OnSpringTerminated));
155 }
156 
157 template < class PlaybackTraits >
~PlaybackTab()158 PlaybackTab<PlaybackTraits>::~PlaybackTab()
159 {
160 	m_minimap->SetBattle( NULL );
161 	if ( m_filter != 0 )
162 		m_filter->SaveFilterValues();
163 
164 	wxLogDebugFunc( wxEmptyString );
165 }
166 
167 template < class PlaybackTraits >
AddAllPlaybacks(wxCommandEvent &)168 void PlaybackTab<PlaybackTraits>::AddAllPlaybacks( wxCommandEvent& /*unused*/ )
169 {
170 	assert(wxThread::IsMain());
171 	const typename ListType::playback_map_t& replays =
172 	    playbacklist<ListType>().GetPlaybacksMap();
173 
174 	for ( typename ListType::playback_const_iter_t i = replays.begin();i != replays.end();++i ) {
175 		AddPlayback( i->second  );
176 	}
177 	m_replay_listctrl->SortList( true );
178 }
179 
180 template < class PlaybackTraits >
AddPlayback(const PlaybackType & replay)181 void PlaybackTab<PlaybackTraits>::AddPlayback( const PlaybackType& replay ) {
182 
183 	if ( m_filter->GetActiv() && !m_filter->FilterPlayback( replay ) ) {
184 		return;
185 	}
186 
187 	m_replay_listctrl->AddPlayback( replay );
188 }
189 
190 template < class PlaybackTraits >
RemovePlayback(const PlaybackType & replay)191 void PlaybackTab<PlaybackTraits>::RemovePlayback( const PlaybackType& replay )
192 {
193 	int index = m_replay_listctrl->GetIndexFromData( &replay );
194 
195 	if ( index == -1 )
196 		return;
197 
198 	if ( index == m_replay_listctrl->GetSelectedIndex() )
199 		Deselect();
200 
201 	m_replay_listctrl->RemovePlayback( replay );
202 }
203 
204 template < class PlaybackTraits >
RemovePlayback(const int index)205 void PlaybackTab<PlaybackTraits>::RemovePlayback( const int index )
206 {
207 	if ( index == -1 )
208 		return;
209 
210 	if ( index == m_replay_listctrl->GetSelectedIndex() )
211 		Deselect();
212 
213 	m_replay_listctrl->RemovePlayback( index );
214 }
215 
216 template < class PlaybackTraits >
UpdatePlayback(const PlaybackType & replay)217 void PlaybackTab<PlaybackTraits>::UpdatePlayback( const PlaybackType& replay )
218 {
219 	if ( m_filter->GetActiv() && !m_filter->FilterPlayback( replay ) ) {
220 		RemovePlayback( replay );
221 		return;
222 	}
223 
224 	int index = m_replay_listctrl->GetIndexFromData( &replay );
225 
226 	if ( index != -1 )
227 		m_replay_listctrl->RefreshItem( index );
228 	else
229 		AddPlayback( replay );
230 
231 }
232 
233 template < class PlaybackTraits >
RemoveAllPlaybacks()234 void PlaybackTab<PlaybackTraits>::RemoveAllPlaybacks()
235 {
236 	m_replay_listctrl->Clear();
237 	//shouldn't list be cleared too here? (koshi)
238 }
239 
240 
241 template < class PlaybackTraits >
UpdateList()242 void PlaybackTab<PlaybackTraits>::UpdateList()
243 {
244 	const typename ListType::playback_map_t& replays =
245 	    playbacklist<ListType>().GetPlaybacksMap();
246 
247 	for ( typename ListType::playback_const_iter_t i = replays.begin(); i != replays.end(); ++i ) {
248 		UpdatePlayback( i->second );
249 	}
250 	m_replay_listctrl->RefreshVisibleItems();
251 }
252 
253 
254 template < class PlaybackTraits >
SetFilterActiv(bool activ)255 void PlaybackTab<PlaybackTraits>::SetFilterActiv( bool activ )
256 {
257 	m_filter->SetActiv( activ );
258 	m_filter_activ->SetValue( activ );
259 }
260 
261 template < class PlaybackTraits >
OnFilter(wxCommandEvent &)262 void PlaybackTab<PlaybackTraits>::OnFilter( wxCommandEvent& /*unused*/ )
263 {
264 	if ( m_filter_show->GetValue() ) {
265 		m_filter->Show(  );
266 		this->Layout();
267 	}
268 	else {
269 		m_filter->Hide(  );
270 		this->Layout();
271 	}
272 }
273 
274 template < class PlaybackTraits >
OnWatch(wxCommandEvent &)275 void PlaybackTab<PlaybackTraits>::OnWatch( wxCommandEvent& /*unused*/ )
276 {
277 	if ( m_replay_listctrl->GetSelectedIndex() != -1 ) {
278 		int m_sel_replay_id = m_replay_listctrl->GetSelectedData()->id;
279 
280 		wxString type = PlaybackTraits::IsReplayType ? _( "replay" ) : _( "savegame" ) ;
281 		wxLogMessage( _T( "Watching %s %d " ), type.c_str(), m_sel_replay_id );
282 		try {
283 			PlaybackType& rep = playbacklist<ListType>().GetPlaybackById( m_sel_replay_id );
284 
285 			bool versionfound = ui().IsSpringCompatible(_T("spring"), rep.SpringVersion);
286 			if ( !ReplayTraits::IsReplayType )
287                 versionfound = true; // quick hack to bypass spring version check
288 			if ( !versionfound ) {
289 				wxString message = wxFormat( _( "No compatible installed spring version has been found, this %s requires version: %s\n" ) ) % type% rep.SpringVersion;
290 				customMessageBox( SL_MAIN_ICON, message, _( "Spring error" ), wxICON_EXCLAMATION | wxOK );
291 				wxLogWarning ( _T( "no spring version supported by this replay found" ) );
292 				AskForceWatch( rep );
293 				return;
294 			}
295             rep.battle.GetMe().SetNick(sett().GetDefaultNick());
296 			bool watchable = rep.battle.MapExists() && rep.battle.ModExists();
297 			if ( watchable )
298 				rep.battle.StartSpring();
299 			else {
300 				wxString downloadProc = _( "Should i try to download it for you?\nYou can see the progress in the \"Download Manager\" tab." );
301 				OfflineBattle& battle = rep.battle;
302 
303 				if ( !battle.ModExists() ) {
304 					if ( customMessageBox( SL_MAIN_ICON, _( "You need to download the game before you can watch this replay.\n\n" ) + downloadProc, _( "Game not available" ), wxYES_NO | wxICON_QUESTION ) == wxYES ) {
305 						wxString modhash = battle.GetHostModHash();
306 						wxString modname = battle.GetHostModName();
307 						ui().Download (_T("game"), modname, modhash);
308 					}
309 					else {
310 						AskForceWatch( rep );
311 					}
312 					return;
313 				}
314 
315 				if ( !battle.MapExists() ) {
316 					if ( customMessageBox( SL_MAIN_ICON, _( " I couldn't find the map to be able to watch this replay\nThis can be caused by tasclient writing broken map hash value\nIf you're sure you have the map, press no\nYou need to download the map to be able to watch this replay.\n\n" ) + downloadProc, _( "Map not available" ), wxYES_NO | wxICON_QUESTION ) == wxYES ) {
317 						wxString maphash = battle.GetHostMapHash();
318 						wxString mapname = battle.GetHostMapName();
319 						ui().Download ( _T("map"), mapname, maphash );
320 					}
321 					else {
322 						AskForceWatch( rep );
323 					}
324 				}
325 			}
326 		} catch ( std::runtime_error ) {
327 			return;
328 		}
329 	} else {
330 		Deselected();
331 	}
332 }
333 
334 template < class PlaybackTraits >
AskForceWatch(typename PlaybackTab<PlaybackTraits>::PlaybackType & rep) const335 void PlaybackTab<PlaybackTraits>::AskForceWatch( typename PlaybackTab<PlaybackTraits>::PlaybackType& rep ) const
336 {
337 	if ( customMessageBox( SL_MAIN_ICON, _( "I don't think you will be able to watch this replay.\nTry anyways? (MIGHT CRASH!)" ) , _( "Invalid replay" ), wxYES_NO | wxICON_QUESTION ) == wxYES ) {
338 		rep.battle.StartSpring();
339 	}
340 }
341 
342 template < class PlaybackTraits >
OnDelete(wxCommandEvent &)343 void PlaybackTab<PlaybackTraits>::OnDelete( wxCommandEvent& /*unused*/ )
344 {
345 	int sel_index = m_replay_listctrl->GetSelectedIndex();
346 	if ( sel_index >= 0 ) {
347 		try {
348 			const PlaybackType& rep = *m_replay_listctrl->GetSelectedData();
349 			int m_sel_replay_id = rep.id;
350 			int index = m_replay_listctrl->GetIndexFromData( &rep );
351 			wxLogMessage( _T( "Deleting replay %d " ), m_sel_replay_id );
352 			wxString fn = rep.Filename;
353 			if ( !playbacklist<ListType>().DeletePlayback( m_sel_replay_id ) )
354 				customMessageBoxNoModal( SL_MAIN_ICON, _( "Could not delete Replay: " ) + fn,
355 				                         _( "Error" ) );
356 			else {
357 				RemovePlayback( index ); // Deselect is called in there too
358 			}
359 		} catch ( std::runtime_error ) {
360 			return;
361 		}
362 	} else {
363 		Deselected();
364 	}
365 }
366 
367 template < class PlaybackTraits >
OnFilterActiv(wxCommandEvent &)368 void PlaybackTab<PlaybackTraits>::OnFilterActiv( wxCommandEvent& /*unused*/ )
369 {
370 	m_filter->SetActiv( m_filter_activ->GetValue() );
371 }
372 
373 template < class PlaybackTraits >
OnSelect(wxListEvent & event)374 void PlaybackTab<PlaybackTraits>::OnSelect( wxListEvent& event )
375 {
376 	wxLogDebugFunc( wxEmptyString );
377 	if ( event.GetIndex() == -1 ) {
378 		Deselect();
379 	}
380 	else
381 	{
382 		try
383 		{
384 			m_watch_btn->Enable( true );
385 			m_delete_btn->Enable( true );
386 			int index = event.GetIndex();
387 			m_replay_listctrl->SetSelectedIndex( index );
388 
389 			//this might seem a bit backwards, but it's currently the only way that doesn't involve casting away constness
390 			int m_sel_replay_id = m_replay_listctrl->GetDataFromIndex( index )->id;
391 			PlaybackType& rep = playbacklist<ListType>().GetPlaybackById( m_sel_replay_id );
392 
393 
394 			wxLogMessage( _T( "Selected replay %d " ), m_sel_replay_id );
395 
396 			m_players_text->SetLabel( wxEmptyString );
397 			m_map_text->SetLabel( rep.battle.GetHostMapName() );
398 			m_mod_text->SetLabel( rep.battle.GetHostModName() );
399 			m_minimap->SetBattle( &( rep.battle ) );
400 			m_minimap->UpdateMinimap();
401 
402 			m_players->Clear();
403 			m_players->SetBattle( ( IBattle* )&rep.battle );
404 			for ( size_t i = 0; i < rep.battle.GetNumUsers(); ++i ) {
405 				try {
406 					User& usr = rep.battle.GetUser( i );
407 					m_players->AddUser( usr );
408 				}
409 				catch ( ... )
410                 {}
411 			}
412 		}
413 		catch ( ... ) {
414 			Deselect();
415 		}
416 		event.Skip();
417 	}
418 }
419 
420 template < class PlaybackTraits >
OnDeselect(wxListEvent &)421 void PlaybackTab<PlaybackTraits>::OnDeselect( wxListEvent& /*unused*/ )
422 {
423 	Deselected();
424 }
425 
426 template < class PlaybackTraits >
Deselect()427 void PlaybackTab<PlaybackTraits>::Deselect()
428 {
429 	m_replay_listctrl->SelectNone();
430 	Deselected();
431 }
432 
433 template < class PlaybackTraits >
Deselected()434 void PlaybackTab<PlaybackTraits>::Deselected()
435 {
436 	m_watch_btn->Enable( false );
437 	m_delete_btn->Enable( false );
438 	m_players_text->SetLabel( wxEmptyString );
439 	m_map_text->SetLabel( wxEmptyString );
440 	m_mod_text->SetLabel( wxEmptyString );
441 	m_minimap->SetBattle( NULL );
442 	m_minimap->UpdateMinimap();
443 	m_minimap->Refresh();
444 	m_players->Clear();
445 	m_players->SetBattle( NULL );
446 }
447 
448 template < class PlaybackTraits >
ReloadList()449 void PlaybackTab<PlaybackTraits>::ReloadList()
450 {
451 	Deselect();
452 	m_replay_listctrl->Clear();
453 	m_replay_loader->Run();
454 }
455 
456 template < class PlaybackTraits >
OnReload(wxCommandEvent &)457 void PlaybackTab<PlaybackTraits>::OnReload( wxCommandEvent& /*unused*/ )
458 {
459 	ReloadList();
460 }
461 
462 template < class PlaybackTraits >
OnSpringTerminated(wxCommandEvent &)463 void PlaybackTab<PlaybackTraits>::OnSpringTerminated( wxCommandEvent& /*data*/ )
464 {
465     ReloadList();
466 }
467 
468 template < class PlaybackTraits >
OnUnitsyncReloaded(wxCommandEvent &)469 void PlaybackTab<PlaybackTraits>::OnUnitsyncReloaded( wxCommandEvent& /*data*/ )
470 {
471 	ReloadList();
472 }
473