/[pcsx2_0.9.7]/trunk/pcsx2/gui/Panels/GameDatabasePanel.cpp
ViewVC logotype

Contents of /trunk/pcsx2/gui/Panels/GameDatabasePanel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations) (download)
Thu Dec 23 12:02:12 2010 UTC (9 years, 1 month ago) by william
File size: 13766 byte(s)
re-commit (had local access denied errors when committing)
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "App.h"
18 #include "AppGameDatabase.h"
19 #include "ConfigurationPanels.h"
20
21 #include <wx/listctrl.h>
22
23 extern wxString DiscSerial;
24
25 using namespace pxSizerFlags;
26
27
28 enum GameDataColumnId
29 {
30 GdbCol_Serial = 0,
31 GdbCol_Title,
32 GdbCol_Region,
33 GdbCol_Compat,
34 GdbCol_Patches,
35
36 GdbCol_Count
37 };
38
39 struct ListViewColumnInfo
40 {
41 const wxChar* name;
42 int width;
43 wxListColumnFormat align;
44 };
45
46 // --------------------------------------------------------------------------------------
47 // GameDatabaseListView
48 // --------------------------------------------------------------------------------------
49 class GameDatabaseListView : public wxListView
50 {
51 typedef wxListView _parent;
52
53 protected:
54 wxArrayString m_GamesInView;
55
56 public:
57 virtual ~GameDatabaseListView() throw() { }
58 GameDatabaseListView( wxWindow* parent );
59
60 void CreateColumns();
61 GameDatabaseListView& AddGame( const wxString& serial );
62 GameDatabaseListView& RemoveGame( const wxString& serial );
63 GameDatabaseListView& ClearAllGames();
64 GameDatabaseListView& SortBy( GameDataColumnId column );
65
66 protected:
67 // Overrides for wxLC_VIRTUAL
68 virtual wxString OnGetItemText(long item, long column) const;
69 virtual int OnGetItemImage(long item) const;
70 virtual int OnGetItemColumnImage(long item, long column) const;
71 virtual wxListItemAttr* OnGetItemAttr(long item) const;
72
73 const ListViewColumnInfo& GetDefaultColumnInfo( uint idx ) const;
74 };
75
76 // --------------------------------------------------------------------------------------
77 // GameDatabaseListView (implementations)
78 // --------------------------------------------------------------------------------------
79 GameDatabaseListView::GameDatabaseListView( wxWindow* parent )
80 : _parent( parent )
81 {
82 CreateColumns();
83 }
84
85 const ListViewColumnInfo& GameDatabaseListView::GetDefaultColumnInfo( uint idx ) const
86 {
87 static const ListViewColumnInfo columns[] =
88 {
89 { L"Serial", 96, wxLIST_FORMAT_LEFT },
90 { L"Title", 132, wxLIST_FORMAT_LEFT },
91 { L"Region", 72, wxLIST_FORMAT_CENTER },
92 { L"Compat", 48, wxLIST_FORMAT_CENTER },
93 { L"Patches", 48, wxLIST_FORMAT_CENTER },
94 };
95
96 pxAssumeDev( idx < ArraySize(columns), "ListView column index is out of bounds." );
97 return columns[idx];
98 }
99
100 void GameDatabaseListView::CreateColumns()
101 {
102 for( int i=0; i<GdbCol_Count; ++i )
103 {
104 const ListViewColumnInfo& info = GetDefaultColumnInfo(i);
105 InsertColumn( i, pxGetTranslation(info.name), info.align, info.width );
106 }
107 }
108
109 GameDatabaseListView& GameDatabaseListView::AddGame( const wxString& serial )
110 {
111 if (m_GamesInView.Index( serial, false ) != wxNOT_FOUND) return *this;
112 m_GamesInView.Add( serial );
113
114 return *this;
115 }
116
117 GameDatabaseListView& GameDatabaseListView::RemoveGame( const wxString& serial )
118 {
119 m_GamesInView.Remove( serial );
120 return *this;
121 }
122
123 GameDatabaseListView& GameDatabaseListView::ClearAllGames()
124 {
125 m_GamesInView.Clear();
126 return *this;
127 }
128
129
130 class BaseGameListSort
131 {
132 protected:
133 IGameDatabase* m_GameDB;
134 bool m_descending;
135
136 public:
137 BaseGameListSort( bool descend )
138 {
139 m_GameDB = AppHost_GetGameDatabase();
140 m_descending = descend;
141 }
142
143 virtual ~BaseGameListSort() throw() {}
144
145 // Note: Return TRUE if the first value is less than the second value.
146 bool operator()(const wxString& i1, const wxString& i2)
147 {
148 if (!m_GameDB || (i1 == i2)) return false;
149
150 // note: Anything not in the database gets sorted to the bottom of the list ...
151 Game_Data first, second;
152 if (!m_GameDB->findGame(first, i1)) return false;
153 if (!m_GameDB->findGame(second, i2)) return true;
154
155 if (int retval = _doCompare(first, second))
156 return m_descending ? (retval>0) : (retval<0);
157
158 return 0;
159 }
160
161 virtual int _doCompare( const Game_Data& first, const Game_Data& second )=0;
162 };
163
164 class GLSort_bySerial : public BaseGameListSort
165 {
166 public:
167 GLSort_bySerial( bool descend ) : BaseGameListSort( descend ) { }
168
169 protected:
170 int _doCompare( const Game_Data& g1, const Game_Data& g2 )
171 {
172 return g1.getString("Serial").CmpNoCase( g2.getString("Serial") );
173 }
174 };
175
176 class GLSort_byTitle : public BaseGameListSort
177 {
178 public:
179 GLSort_byTitle( bool descend ) : BaseGameListSort( descend ) { }
180
181 protected:
182 int _doCompare( const Game_Data& g1, const Game_Data& g2 )
183 {
184 return g1.getString("Name").Cmp( g2.getString("Name") );
185 }
186 };
187
188 class GLSort_byRegion : public BaseGameListSort
189 {
190 public:
191 GLSort_byRegion( bool descend ) : BaseGameListSort( descend ) { }
192
193 protected:
194 int _doCompare( const Game_Data& g1, const Game_Data& g2 )
195 {
196 return g1.getString("Region").CmpNoCase( g2.getString("Region") );
197 }
198 };
199
200 class GLSort_byCompat : public BaseGameListSort
201 {
202 public:
203 GLSort_byCompat( bool descend ) : BaseGameListSort( descend ) { }
204
205 protected:
206 int _doCompare( const Game_Data& g1, const Game_Data& g2 )
207 {
208 return g1.getInt("Compat") - g2.getInt("Compat");
209 }
210 };
211
212 class GLSort_byPatches : public BaseGameListSort
213 {
214 public:
215 GLSort_byPatches( bool descend ) : BaseGameListSort( descend ) { }
216
217 protected:
218 int _doCompare( const Game_Data& g1, const Game_Data& g2 )
219 {
220 bool hasPatches1 = !g1.getString("[patches]").IsEmpty();
221 bool hasPatches2 = !g2.getString("[patches]").IsEmpty();
222
223 if (hasPatches1 == hasPatches2) return 0;
224
225 return hasPatches1 ? -1 : 1;
226 }
227 };
228
229 GameDatabaseListView& GameDatabaseListView::SortBy( GameDataColumnId column )
230 {
231 wxArrayString::CompareFunction cmpfunc = NULL;
232
233 const bool isDescending = false;
234
235 wxArrayString::iterator begin = m_GamesInView.begin();
236 wxArrayString::iterator end = m_GamesInView.end();
237
238 // Note: std::sort does not pass predicate instances by reference, which means we can't use
239 // object polymorphism to simplify the code below. --air
240
241 switch( column )
242 {
243 case GdbCol_Serial: std::sort(begin, end, GLSort_bySerial(isDescending)); break;
244 case GdbCol_Title: std::sort(begin, end, GLSort_byTitle(isDescending)); break;
245 case GdbCol_Region: std::sort(begin, end, GLSort_byRegion(isDescending)); break;
246 case GdbCol_Compat: std::sort(begin, end, GLSort_byCompat(isDescending)); break;
247 case GdbCol_Patches: std::sort(begin, end, GLSort_byPatches(isDescending)); break;
248
249 // do not use jNO_DEFAULT here -- keeps release builds from crashing (it'll just
250 // ignore the sort request!)
251 }
252 //m_GamesInView.( );
253
254 return *this;
255 }
256
257 // return the text for the given column of the given item
258 wxString GameDatabaseListView::OnGetItemText(long item, long column) const
259 {
260 IGameDatabase* GameDB = AppHost_GetGameDatabase();
261
262 if (!GameDB || (item < 0) || ((uint)item >= m_GamesInView.GetCount()))
263 return _parent::OnGetItemText(item, column);
264
265 Game_Data game;
266 if (!GameDB->findGame(game, m_GamesInView[item]))
267 {
268 pxFail( "Unknown row index in GameDatabaseListView -- returning default value." );
269 return _parent::OnGetItemText(item, column);
270 }
271
272 switch( column )
273 {
274 case GdbCol_Serial: return m_GamesInView[item];
275 case GdbCol_Title: return game.getString("Name");
276 case GdbCol_Region: return game.getString("Region");
277 case GdbCol_Compat: return game.getString("Compat");
278 case GdbCol_Patches: return game.getString("[patches]").IsEmpty() ? L"No" : L"Yes";
279 }
280
281 pxFail( "Unknown column index in GameDatabaseListView -- returning an empty string." );
282 return wxEmptyString;
283 }
284
285 // return the icon for the given item. In report view, OnGetItemImage will
286 // only be called for the first column. See OnGetItemColumnImage for
287 // details.
288 int GameDatabaseListView::OnGetItemImage(long item) const
289 {
290 return _parent::OnGetItemImage( item );
291 }
292
293 // return the icon for the given item and column.
294 int GameDatabaseListView::OnGetItemColumnImage(long item, long column) const
295 {
296 return _parent::OnGetItemColumnImage( item, column );
297 }
298
299 static wxListItemAttr m_ItemAttr;
300
301 // return the attribute for the item (may return NULL if none)
302 wxListItemAttr* GameDatabaseListView::OnGetItemAttr(long item) const
303 {
304 m_ItemAttr = wxListItemAttr(); // Wipe it clean!
305
306 // For eventual drag&drop ?
307 //if( m_TargetedItem == item )
308 // m_ItemAttr.SetBackgroundColour( wxColour(L"Wheat") );
309
310 return &m_ItemAttr;
311 }
312
313
314 #define blankLine() { \
315 sizer1+=5; sizer1+=5; sizer1+=Text(wxEmptyString); sizer1+=5; sizer1+=5; \
316 }
317
318 #define placeTextBox(wxBox, txt) { \
319 sizer1 += Label(_(txt)); \
320 sizer1 += 5; \
321 sizer1 += wxBox | pxCenter; \
322 sizer1 += 5; \
323 sizer1 += 5; \
324 }
325
326 wxTextCtrl* CreateMultiLineTextCtrl( wxWindow* parent, int digits, long flags = 0 )
327 {
328 wxTextCtrl* ctrl = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
329 pxFitToDigits(ctrl, digits);
330 return ctrl;
331 }
332
333 Panels::GameDatabasePanel::GameDatabasePanel( wxWindow* parent )
334 : BaseApplicableConfigPanel( parent )
335 {
336 searchBtn = new wxButton (this, wxID_ANY, _("Search"));
337
338 serialBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
339 nameBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
340 regionBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
341 compatBox = CreateNumericalTextCtrl(this, 40, wxTE_LEFT);
342 commentBox = CreateMultiLineTextCtrl(this, 40, wxTE_LEFT);
343 patchesBox = CreateMultiLineTextCtrl(this, 40, wxTE_LEFT);
344
345 for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i)
346 gameFixes[i] = new pxCheckBox(this, EnumToString(i), wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER );
347
348 *this += Heading(_("Game Database Editor")).Bold() | StdExpand();
349
350 *this += new GameDatabaseListView( this ) | StdExpand();
351
352 wxFlexGridSizer& sizer1(*new wxFlexGridSizer(5, StdPadding));
353 sizer1.AddGrowableCol(0);
354
355 blankLine();
356 sizer1 += Label(L"Serial: ");
357 sizer1 += 5;
358 sizer1 += serialBox | pxCenter;
359 sizer1 += 5;
360 sizer1 += searchBtn;
361
362 placeTextBox(nameBox, "Name: ");
363 placeTextBox(regionBox, "Region: ");
364 placeTextBox(compatBox, "Compatibility: ");
365 placeTextBox(commentBox, "Comments: ");
366 placeTextBox(patchesBox, "Patches: ");
367
368 blankLine();
369
370 wxStaticBoxSizer& sizer2 = *new wxStaticBoxSizer(wxVERTICAL, this, _("Gamefixes"));
371 wxFlexGridSizer& sizer3(*new wxFlexGridSizer(3, 0, StdPadding*4));
372 sizer3.AddGrowableCol(0);
373
374 for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i)
375 sizer3 += gameFixes[i];
376
377 sizer2 += sizer3 | StdCenter();
378
379 *this += sizer1 | pxCenter;
380 *this += sizer2 | pxCenter;
381
382 Connect(searchBtn->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(GameDatabasePanel::Search_Click));
383 PopulateFields();
384 }
385
386 void Panels::GameDatabasePanel::PopulateFields( const wxString& id ) {
387 IGameDatabase* GameDB = AppHost_GetGameDatabase();
388 if (!pxAssert(GameDB)) return;
389
390 Game_Data game;
391 if (GameDB->findGame(game, id.IsEmpty() ? SysGetDiscID() : id))
392 {
393 serialBox ->SetLabel(game.getString("Serial"));
394 nameBox ->SetLabel(game.getString("Name"));
395 regionBox ->SetLabel(game.getString("Region"));
396 compatBox ->SetLabel(game.getString("Compat"));
397 commentBox->SetLabel(game.getString("[comments]"));
398 patchesBox->SetLabel(game.getString("[patches]"));
399
400 for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i)
401 {
402 wxString keyName (EnumToString(i)); keyName += L"Hack";
403 if( game.keyExists(keyName) )
404 gameFixes[i]->SetValue(game.getBool(keyName));
405 else
406 gameFixes[i]->SetIndeterminate();
407 }
408 }
409 else {
410 serialBox ->SetLabel(wxEmptyString);
411 nameBox ->SetLabel(wxEmptyString);
412 regionBox ->SetLabel(wxEmptyString);
413 compatBox ->SetLabel(wxEmptyString);
414 commentBox->SetLabel(wxEmptyString);
415 patchesBox->SetLabel(wxEmptyString);
416 for (int i = 0; i < GamefixId_COUNT; i++) {
417 gameFixes[i]->SetValue(0);
418 }
419 }
420 }
421
422 #define writeTextBoxToDB(_key, _value) { \
423 if (_value.IsEmpty()) GameDB->deleteKey(wxT(_key)); \
424 else GameDB->writeString(wxT(_key), _value); \
425 }
426
427 // returns True if the database is modified, or FALSE if no changes to save.
428 bool Panels::GameDatabasePanel::WriteFieldsToDB() {
429 IGameDatabase* GameDB = AppHost_GetGameDatabase();
430 if (!GameDB) return false;
431
432 if (serialBox->GetValue().IsEmpty()) return false;
433
434 Game_Data game;
435 GameDB->findGame(game, serialBox->GetValue());
436
437 game.id = serialBox->GetValue();
438
439 game.writeString(L"Serial", serialBox->GetValue());
440 game.writeString(L"Name", nameBox->GetValue());
441 game.writeString(L"Region", regionBox->GetValue());
442 game.writeString(L"Compat", compatBox->GetValue());
443 game.writeString(L"[comments]", commentBox->GetValue());
444 game.writeString(L"[patches]", patchesBox->GetValue());
445
446 for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) {
447 wxString keyName (EnumToString(i)); keyName += L"Hack";
448
449 if (gameFixes[i]->IsIndeterminate())
450 game.deleteKey(keyName);
451 else
452 game.writeBool(keyName, gameFixes[i]->GetValue());
453 }
454 GameDB->updateGame(game);
455 return true;
456 }
457
458 void Panels::GameDatabasePanel::Search_Click(wxCommandEvent& evt) {
459 IGameDatabase* GameDB = AppHost_GetGameDatabase();
460 if( !GameDB ) return;
461
462 PopulateFields( serialBox->GetValue() );
463 evt.Skip();
464 }
465
466 void Panels::GameDatabasePanel::Apply() {
467 AppGameDatabase* GameDB = wxGetApp().GetGameDatabase();
468 if( WriteFieldsToDB() )
469 {
470 Console.WriteLn("Saving changes to Game Database...");
471 GameDB->SaveToFile();
472 }
473 }
474
475 void Panels::GameDatabasePanel::AppStatusEvent_OnSettingsApplied()
476 {
477 }

  ViewVC Help
Powered by ViewVC 1.1.22