dolphin/Source/Core/DolphinWX/GameListCtrl.cpp
JosJuice 0a15aaaa12 Move DiscIO enums to a new file
At first there weren't many enums in Volume.h, but the number has been
growing, and I'm planning to add one more for regions. To not make
Volume.h too large, and to avoid needing to include Volume.h in code
that doesn't use volume objects, I'm moving the enums to a new file.
I'm also turning them into enum classes while I'm at it.
2016-07-13 17:29:27 +02:00

1391 lines
44 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <wx/app.h>
#include <wx/bitmap.h>
#include <wx/buffer.h>
#include <wx/colour.h>
#include <wx/dirdlg.h>
#include <wx/filedlg.h>
#include <wx/filefn.h>
#include <wx/filename.h>
#include <wx/gdicmn.h>
#include <wx/imaglist.h>
#include <wx/listctrl.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/settings.h>
#include <wx/tipwin.h>
#include <wx/wxcrt.h>
#include "Common/CDUtils.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
#include "Common/SysConf.h"
#include "Core/Boot/Boot.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/DVDInterface.h"
#include "Core/HW/WiiSaveCrypted.h"
#include "Core/Movie.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeCreator.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/ISOFile.h"
#include "DolphinWX/ISOProperties.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/WxUtils.h"
struct CompressionProgress final
{
public:
CompressionProgress(int items_done_, int items_total_, const std::string& current_filename_,
wxProgressDialog* dialog_)
: items_done(items_done_), items_total(items_total_), current_filename(current_filename_),
dialog(dialog_)
{
}
int items_done;
int items_total;
std::string current_filename;
wxProgressDialog* dialog;
};
static bool sorted = false;
static int CompareGameListItems(const GameListItem* iso1, const GameListItem* iso2,
long sortData = CGameListCtrl::COLUMN_TITLE)
{
int t = 1;
if (sortData < 0)
{
t = -1;
sortData = -sortData;
}
switch (sortData)
{
case CGameListCtrl::COLUMN_TITLE:
if (!strcasecmp(iso1->GetName().c_str(), iso2->GetName().c_str()))
{
if (iso1->GetUniqueID() != iso2->GetUniqueID())
return t * (iso1->GetUniqueID() > iso2->GetUniqueID() ? 1 : -1);
if (iso1->GetRevision() != iso2->GetRevision())
return t * (iso1->GetRevision() > iso2->GetRevision() ? 1 : -1);
if (iso1->GetDiscNumber() != iso2->GetDiscNumber())
return t * (iso1->GetDiscNumber() > iso2->GetDiscNumber() ? 1 : -1);
wxString iso1_filename = wxFileNameFromPath(iso1->GetFileName());
wxString iso2_filename = wxFileNameFromPath(iso2->GetFileName());
if (iso1_filename != iso2_filename)
return t * wxStricmp(iso1_filename, iso2_filename);
}
return strcasecmp(iso1->GetName().c_str(), iso2->GetName().c_str()) * t;
case CGameListCtrl::COLUMN_MAKER:
return strcasecmp(iso1->GetCompany().c_str(), iso2->GetCompany().c_str()) * t;
case CGameListCtrl::COLUMN_FILENAME:
return wxStricmp(wxFileNameFromPath(iso1->GetFileName()),
wxFileNameFromPath(iso2->GetFileName())) *
t;
case CGameListCtrl::COLUMN_ID:
return strcasecmp(iso1->GetUniqueID().c_str(), iso2->GetUniqueID().c_str()) * t;
case CGameListCtrl::COLUMN_COUNTRY:
if (iso1->GetCountry() > iso2->GetCountry())
return 1 * t;
if (iso1->GetCountry() < iso2->GetCountry())
return -1 * t;
return 0;
case CGameListCtrl::COLUMN_SIZE:
if (iso1->GetFileSize() > iso2->GetFileSize())
return 1 * t;
if (iso1->GetFileSize() < iso2->GetFileSize())
return -1 * t;
return 0;
case CGameListCtrl::COLUMN_PLATFORM:
if (iso1->GetPlatform() > iso2->GetPlatform())
return 1 * t;
if (iso1->GetPlatform() < iso2->GetPlatform())
return -1 * t;
return 0;
case CGameListCtrl::COLUMN_EMULATION_STATE:
{
const int nState1 = iso1->GetEmuState(), nState2 = iso2->GetEmuState();
if (nState1 > nState2)
return 1 * t;
if (nState1 < nState2)
return -1 * t;
else
return 0;
}
break;
}
return 0;
}
CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
: wxListCtrl(parent, id, pos, size, style), toolTip(nullptr)
{
Bind(wxEVT_SIZE, &CGameListCtrl::OnSize, this);
Bind(wxEVT_RIGHT_DOWN, &CGameListCtrl::OnRightClick, this);
Bind(wxEVT_LEFT_DOWN, &CGameListCtrl::OnLeftClick, this);
Bind(wxEVT_MOTION, &CGameListCtrl::OnMouseMotion, this);
Bind(wxEVT_LIST_KEY_DOWN, &CGameListCtrl::OnKeyPress, this);
Bind(wxEVT_LIST_COL_BEGIN_DRAG, &CGameListCtrl::OnColBeginDrag, this);
Bind(wxEVT_LIST_COL_CLICK, &CGameListCtrl::OnColumnClick, this);
Bind(wxEVT_MENU, &CGameListCtrl::OnProperties, this, IDM_PROPERTIES);
Bind(wxEVT_MENU, &CGameListCtrl::OnWiki, this, IDM_GAME_WIKI);
Bind(wxEVT_MENU, &CGameListCtrl::OnOpenContainingFolder, this, IDM_OPEN_CONTAINING_FOLDER);
Bind(wxEVT_MENU, &CGameListCtrl::OnOpenSaveFolder, this, IDM_OPEN_SAVE_FOLDER);
Bind(wxEVT_MENU, &CGameListCtrl::OnExportSave, this, IDM_EXPORT_SAVE);
Bind(wxEVT_MENU, &CGameListCtrl::OnSetDefaultISO, this, IDM_SET_DEFAULT_ISO);
Bind(wxEVT_MENU, &CGameListCtrl::OnCompressISO, this, IDM_COMPRESS_ISO);
Bind(wxEVT_MENU, &CGameListCtrl::OnMultiCompressISO, this, IDM_MULTI_COMPRESS_ISO);
Bind(wxEVT_MENU, &CGameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO);
Bind(wxEVT_MENU, &CGameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO);
Bind(wxEVT_MENU, &CGameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC);
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CGameListCtrl::OnLocalIniModified, this);
}
CGameListCtrl::~CGameListCtrl()
{
ClearIsoFiles();
}
template <typename T>
static void InitBitmap(wxImageList* img_list, std::vector<int>* vector, T index,
const std::string& name)
{
wxSize size(96, 32);
(*vector)[static_cast<size_t>(index)] = img_list->Add(WxUtils::LoadResourceBitmap(name, size));
}
void CGameListCtrl::InitBitmaps()
{
wxImageList* img_list = new wxImageList(96, 32);
AssignImageList(img_list, wxIMAGE_LIST_SMALL);
m_FlagImageIndex.resize(static_cast<size_t>(DiscIO::Country::NUMBER_OF_COUNTRIES));
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_JAPAN, "Flag_Japan");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_EUROPE, "Flag_Europe");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_USA, "Flag_USA");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_AUSTRALIA, "Flag_Australia");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_FRANCE, "Flag_France");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_GERMANY, "Flag_Germany");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_ITALY, "Flag_Italy");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_KOREA, "Flag_Korea");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_NETHERLANDS, "Flag_Netherlands");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_RUSSIA, "Flag_Russia");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_SPAIN, "Flag_Spain");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_TAIWAN, "Flag_Taiwan");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_WORLD, "Flag_International");
InitBitmap(img_list, &m_FlagImageIndex, DiscIO::Country::COUNTRY_UNKNOWN, "Flag_Unknown");
m_PlatformImageIndex.resize(static_cast<size_t>(DiscIO::Platform::NUMBER_OF_PLATFORMS));
InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::GAMECUBE_DISC, "Platform_Gamecube");
InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::WII_DISC, "Platform_Wii");
InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::WII_WAD, "Platform_Wad");
InitBitmap(img_list, &m_PlatformImageIndex, DiscIO::Platform::ELF_DOL, "Platform_File");
m_EmuStateImageIndex.resize(6);
InitBitmap(img_list, &m_EmuStateImageIndex, 0, "rating0");
InitBitmap(img_list, &m_EmuStateImageIndex, 1, "rating1");
InitBitmap(img_list, &m_EmuStateImageIndex, 2, "rating2");
InitBitmap(img_list, &m_EmuStateImageIndex, 3, "rating3");
InitBitmap(img_list, &m_EmuStateImageIndex, 4, "rating4");
InitBitmap(img_list, &m_EmuStateImageIndex, 5, "rating5");
}
void CGameListCtrl::BrowseForDirectory()
{
wxString dirHome;
wxGetHomeDir(&dirHome);
// browse
wxDirDialog dialog(this, _("Browse for a directory to add"), dirHome,
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
if (dialog.ShowModal() == wxID_OK)
{
std::string sPath(WxStrToStr(dialog.GetPath()));
std::vector<std::string>::iterator itResult =
std::find(SConfig::GetInstance().m_ISOFolder.begin(),
SConfig::GetInstance().m_ISOFolder.end(), sPath);
if (itResult == SConfig::GetInstance().m_ISOFolder.end())
{
SConfig::GetInstance().m_ISOFolder.push_back(sPath);
SConfig::GetInstance().SaveSettings();
}
Update();
}
}
void CGameListCtrl::Update()
{
int scrollPos = wxWindow::GetScrollPos(wxVERTICAL);
// Don't let the user refresh it while a game is running
if (Core::GetState() != Core::CORE_UNINITIALIZED)
return;
ScanForISOs();
Freeze();
ClearAll();
if (m_ISOFiles.size() != 0)
{
// Don't load bitmaps unless there are games to list
InitBitmaps();
// add columns
InsertColumn(COLUMN_DUMMY, "");
InsertColumn(COLUMN_PLATFORM, "");
InsertColumn(COLUMN_BANNER, _("Banner"));
InsertColumn(COLUMN_TITLE, _("Title"));
InsertColumn(COLUMN_MAKER, _("Maker"));
InsertColumn(COLUMN_FILENAME, _("File"));
InsertColumn(COLUMN_ID, _("ID"));
InsertColumn(COLUMN_COUNTRY, "");
InsertColumn(COLUMN_SIZE, _("Size"));
InsertColumn(COLUMN_EMULATION_STATE, _("State"));
#ifdef __WXMSW__
const int platform_padding = 0;
#else
const int platform_padding = 8;
#endif
const int platform_icon_padding = 1;
// set initial sizes for columns
SetColumnWidth(COLUMN_DUMMY, 0);
SetColumnWidth(COLUMN_PLATFORM, SConfig::GetInstance().m_showSystemColumn ?
32 + platform_icon_padding + platform_padding :
0);
SetColumnWidth(COLUMN_BANNER,
SConfig::GetInstance().m_showBannerColumn ? 96 + platform_padding : 0);
SetColumnWidth(COLUMN_TITLE, 175 + platform_padding);
SetColumnWidth(COLUMN_MAKER,
SConfig::GetInstance().m_showMakerColumn ? 150 + platform_padding : 0);
SetColumnWidth(COLUMN_FILENAME,
SConfig::GetInstance().m_showFileNameColumn ? 100 + platform_padding : 0);
SetColumnWidth(COLUMN_ID, SConfig::GetInstance().m_showIDColumn ? 75 + platform_padding : 0);
SetColumnWidth(COLUMN_COUNTRY,
SConfig::GetInstance().m_showRegionColumn ? 32 + platform_padding : 0);
SetColumnWidth(COLUMN_EMULATION_STATE,
SConfig::GetInstance().m_showStateColumn ? 48 + platform_padding : 0);
// add all items
for (int i = 0; i < (int)m_ISOFiles.size(); i++)
{
InsertItemInReportView(i);
if (SConfig::GetInstance().m_ColorCompressed && m_ISOFiles[i]->IsCompressed())
SetItemTextColour(i, wxColour(0xFF0000));
}
// Sort items by Title
if (!sorted)
last_column = 0;
sorted = false;
wxListEvent event;
event.m_col = SConfig::GetInstance().m_ListSort2;
OnColumnClick(event);
event.m_col = SConfig::GetInstance().m_ListSort;
OnColumnClick(event);
sorted = true;
SetColumnWidth(COLUMN_SIZE, SConfig::GetInstance().m_showSizeColumn ? wxLIST_AUTOSIZE : 0);
}
else
{
// Remove existing image list and replace it with the smallest possible one.
// The list needs an image list because it reserves screen pixels for the
// image even if we aren't going to use one. It uses the dimensions of the
// last non-null list so assigning nullptr doesn't work.
AssignImageList(new wxImageList(1, 1), wxIMAGE_LIST_SMALL);
wxString errorString;
// We just check for one hide setting to be enabled, as we may only
// have GC games for example, and hide them, so we should show the
// first message instead
if (IsHidingItems())
{
errorString =
_("Dolphin is currently set to hide all games. Double-click here to show all games...");
}
else
{
errorString = _("Dolphin could not find any GameCube/Wii ISOs or WADs. Double-click here to "
"set a games directory...");
}
InsertColumn(0, "");
long index = InsertItem(0, errorString);
SetItemFont(index, *wxITALIC_FONT);
SetColumnWidth(0, wxLIST_AUTOSIZE);
}
if (GetSelectedISO() == nullptr)
main_frame->UpdateGUI();
// Thaw before calling AutomaticColumnWidth so that GetClientSize will
// correctly account for the width of scrollbars if they appear.
Thaw();
AutomaticColumnWidth();
ScrollLines(scrollPos);
SetFocus();
}
static wxString NiceSizeFormat(u64 size)
{
// Return a pretty filesize string from byte count.
// e.g. 1134278 -> "1.08 MiB"
const char* const unit_symbols[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
// Find largest power of 2 less than size.
// div 10 to get largest named unit less than size
// 10 == log2(1024) (number of B in a KiB, KiB in a MiB, etc)
// Max value is 63 / 10 = 6
const int unit = IntLog2(std::max<u64>(size, 1)) / 10;
// Don't need exact values, only 5 most significant digits
double unit_size = std::pow(2, unit * 10);
return wxString::Format("%.2f %s", size / unit_size, unit_symbols[unit]);
}
// Update the column content of the item at _Index
void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
{
GameListItem& rISOFile = *m_ISOFiles[GetItemData(_Index)];
switch (column)
{
case COLUMN_PLATFORM:
{
SetItemColumnImage(_Index, COLUMN_PLATFORM,
m_PlatformImageIndex[static_cast<size_t>(rISOFile.GetPlatform())]);
break;
}
case COLUMN_BANNER:
{
int ImageIndex = -1;
if (rISOFile.GetBitmap().IsOk())
ImageIndex = GetImageList(wxIMAGE_LIST_SMALL)->Add(rISOFile.GetBitmap());
SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex);
break;
}
case COLUMN_TITLE:
{
wxString name = StrToWxStr(rISOFile.GetName());
int disc_number = rISOFile.GetDiscNumber() + 1;
if (disc_number > 1 &&
name.Lower().find(wxString::Format("disc %i", disc_number)) == std::string::npos &&
name.Lower().find(wxString::Format("disc%i", disc_number)) == std::string::npos)
{
name = wxString::Format(_("%s (Disc %i)"), name.c_str(), disc_number);
}
SetItem(_Index, COLUMN_TITLE, name, -1);
break;
}
case COLUMN_MAKER:
SetItem(_Index, COLUMN_MAKER, StrToWxStr(rISOFile.GetCompany()), -1);
break;
case COLUMN_FILENAME:
SetItem(_Index, COLUMN_FILENAME, wxFileNameFromPath(StrToWxStr(rISOFile.GetFileName())), -1);
break;
case COLUMN_EMULATION_STATE:
SetItemColumnImage(_Index, COLUMN_EMULATION_STATE,
m_EmuStateImageIndex[rISOFile.GetEmuState()]);
break;
case COLUMN_COUNTRY:
SetItemColumnImage(_Index, COLUMN_COUNTRY,
m_FlagImageIndex[static_cast<size_t>(rISOFile.GetCountry())]);
break;
case COLUMN_SIZE:
SetItem(_Index, COLUMN_SIZE, NiceSizeFormat(rISOFile.GetFileSize()), -1);
break;
case COLUMN_ID:
SetItem(_Index, COLUMN_ID, rISOFile.GetUniqueID(), -1);
break;
}
}
void CGameListCtrl::InsertItemInReportView(long index)
{
// When using wxListCtrl, there is no hope of per-column text colors.
// But for reference, here are the old colors that were used: (BGR)
// title: 0xFF0000
// company: 0x007030
// Insert a first column with nothing in it, that will be used as the Index
long item_index;
{
wxListItem li;
li.SetId(index);
li.SetData(index);
li.SetMask(wxLIST_MASK_DATA);
item_index = InsertItem(li);
}
// Iterate over all columns and fill them with content if they are visible
for (int i = 1; i < NUMBER_OF_COLUMN; i++)
{
if (GetColumnWidth(i) != 0)
UpdateItemAtColumn(item_index, i);
}
// Background color
SetBackgroundColor();
}
static wxColour blend50(const wxColour& c1, const wxColour& c2)
{
unsigned char r, g, b, a;
r = c1.Red() / 2 + c2.Red() / 2;
g = c1.Green() / 2 + c2.Green() / 2;
b = c1.Blue() / 2 + c2.Blue() / 2;
a = c1.Alpha() / 2 + c2.Alpha() / 2;
return a << 24 | b << 16 | g << 8 | r;
}
void CGameListCtrl::SetBackgroundColor()
{
for (long i = 0; i < GetItemCount(); i++)
{
wxColour color = (i & 1) ? blend50(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT),
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)) :
wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
CGameListCtrl::SetItemBackgroundColour(i, color);
}
}
void CGameListCtrl::ScanForISOs()
{
ClearIsoFiles();
// Load custom game titles from titles.txt
// http://www.gametdb.com/Wii/Downloads
std::unordered_map<std::string, std::string> custom_title_map;
std::ifstream titlestxt;
OpenFStream(titlestxt, File::GetUserPath(D_LOAD_IDX) + "titles.txt", std::ios::in);
if (!titlestxt.is_open())
OpenFStream(titlestxt, File::GetUserPath(D_LOAD_IDX) + "wiitdb.txt", std::ios::in);
if (titlestxt.is_open())
{
std::string line;
while (!titlestxt.eof() && std::getline(titlestxt, line))
{
const size_t equals_index = line.find('=');
if (equals_index != std::string::npos)
custom_title_map.emplace(StripSpaces(line.substr(0, equals_index)),
StripSpaces(line.substr(equals_index + 1)));
}
titlestxt.close();
}
std::vector<std::string> Extensions;
if (SConfig::GetInstance().m_ListGC)
Extensions.push_back(".gcm");
if (SConfig::GetInstance().m_ListWii || SConfig::GetInstance().m_ListGC)
{
Extensions.push_back(".iso");
Extensions.push_back(".ciso");
Extensions.push_back(".gcz");
Extensions.push_back(".wbfs");
}
if (SConfig::GetInstance().m_ListWad)
Extensions.push_back(".wad");
if (SConfig::GetInstance().m_ListElfDol)
{
Extensions.push_back(".dol");
Extensions.push_back(".elf");
}
auto rFilenames = DoFileSearch(Extensions, SConfig::GetInstance().m_ISOFolder,
SConfig::GetInstance().m_RecursiveISOFolder);
if (rFilenames.size() > 0)
{
wxProgressDialog dialog(
_("Scanning for ISOs"), _("Scanning..."), (int)rFilenames.size() - 1, this,
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
wxPD_REMAINING_TIME | wxPD_SMOOTH // - makes updates as small as possible (down to 1px)
);
for (u32 i = 0; i < rFilenames.size(); i++)
{
std::string FileName;
SplitPath(rFilenames[i], nullptr, &FileName, nullptr);
// Update with the progress (i) and the message
dialog.Update(i, wxString::Format(_("Scanning %s"), StrToWxStr(FileName)));
if (dialog.WasCancelled())
break;
auto iso_file = std::make_unique<GameListItem>(rFilenames[i], custom_title_map);
if (iso_file->IsValid())
{
bool list = true;
switch (iso_file->GetPlatform())
{
case DiscIO::Platform::WII_DISC:
if (!SConfig::GetInstance().m_ListWii)
list = false;
break;
case DiscIO::Platform::WII_WAD:
if (!SConfig::GetInstance().m_ListWad)
list = false;
break;
case DiscIO::Platform::ELF_DOL:
if (!SConfig::GetInstance().m_ListElfDol)
list = false;
break;
default:
if (!SConfig::GetInstance().m_ListGC)
list = false;
break;
}
switch (iso_file->GetCountry())
{
case DiscIO::Country::COUNTRY_AUSTRALIA:
if (!SConfig::GetInstance().m_ListAustralia)
list = false;
break;
case DiscIO::Country::COUNTRY_EUROPE:
if (!SConfig::GetInstance().m_ListPal)
list = false;
break;
case DiscIO::Country::COUNTRY_FRANCE:
if (!SConfig::GetInstance().m_ListFrance)
list = false;
break;
case DiscIO::Country::COUNTRY_GERMANY:
if (!SConfig::GetInstance().m_ListGermany)
list = false;
break;
case DiscIO::Country::COUNTRY_ITALY:
if (!SConfig::GetInstance().m_ListItaly)
list = false;
break;
case DiscIO::Country::COUNTRY_JAPAN:
if (!SConfig::GetInstance().m_ListJap)
list = false;
break;
case DiscIO::Country::COUNTRY_KOREA:
if (!SConfig::GetInstance().m_ListKorea)
list = false;
break;
case DiscIO::Country::COUNTRY_NETHERLANDS:
if (!SConfig::GetInstance().m_ListNetherlands)
list = false;
break;
case DiscIO::Country::COUNTRY_RUSSIA:
if (!SConfig::GetInstance().m_ListRussia)
list = false;
break;
case DiscIO::Country::COUNTRY_SPAIN:
if (!SConfig::GetInstance().m_ListSpain)
list = false;
break;
case DiscIO::Country::COUNTRY_TAIWAN:
if (!SConfig::GetInstance().m_ListTaiwan)
list = false;
break;
case DiscIO::Country::COUNTRY_USA:
if (!SConfig::GetInstance().m_ListUsa)
list = false;
break;
case DiscIO::Country::COUNTRY_WORLD:
if (!SConfig::GetInstance().m_ListWorld)
list = false;
break;
case DiscIO::Country::COUNTRY_UNKNOWN:
default:
if (!SConfig::GetInstance().m_ListUnknown)
list = false;
break;
}
if (list)
m_ISOFiles.push_back(iso_file.release());
}
}
}
if (SConfig::GetInstance().m_ListDrives)
{
const std::vector<std::string> drives = cdio_get_devices();
for (const auto& drive : drives)
{
auto gli = std::make_unique<GameListItem>(drive, custom_title_map);
if (gli->IsValid())
m_ISOFiles.push_back(gli.release());
}
}
std::sort(m_ISOFiles.begin(), m_ISOFiles.end());
}
void CGameListCtrl::OnLocalIniModified(wxCommandEvent& ev)
{
ev.Skip();
std::string game_id = WxStrToStr(ev.GetString());
// NOTE: The same game may occur multiple times if there are multiple
// physical copies in the search paths.
for (std::size_t i = 0; i < m_ISOFiles.size(); ++i)
{
if (m_ISOFiles[i]->GetUniqueID() != game_id)
continue;
m_ISOFiles[i]->ReloadINI();
// The indexes in m_ISOFiles and the list do not line up.
// We need to find the corresponding item in the list (if it exists)
long item_id = 0;
for (; item_id < GetItemCount(); ++item_id)
{
if (i == static_cast<std::size_t>(GetItemData(item_id)))
break;
}
// If the item is not currently being displayed then we're done.
if (item_id == GetItemCount())
continue;
// Update all the columns
for (int j = 1; j < NUMBER_OF_COLUMN; ++j)
{
// NOTE: Banner is not modified by the INI and updating it will
// duplicate it in memory which is not wanted.
if (j != COLUMN_BANNER && GetColumnWidth(j) != 0)
UpdateItemAtColumn(item_id, j);
}
}
}
void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
{
const int column_id = event.GetColumn();
if (column_id != COLUMN_TITLE && column_id != COLUMN_MAKER && column_id != COLUMN_FILENAME)
event.Veto();
}
const GameListItem* CGameListCtrl::GetISO(size_t index) const
{
if (index < m_ISOFiles.size())
return m_ISOFiles[index];
else
return nullptr;
}
static CGameListCtrl* caller;
static int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{
// return 1 if item1 > item2
// return -1 if item1 < item2
// return 0 for identity
const GameListItem* iso1 = caller->GetISO(item1);
const GameListItem* iso2 = caller->GetISO(item2);
return CompareGameListItems(iso1, iso2, sortData);
}
void CGameListCtrl::OnColumnClick(wxListEvent& event)
{
if (event.GetColumn() != COLUMN_BANNER)
{
int current_column = event.GetColumn();
if (sorted)
{
if (last_column == current_column)
{
last_sort = -last_sort;
}
else
{
SConfig::GetInstance().m_ListSort2 = last_sort;
last_column = current_column;
last_sort = current_column;
}
SConfig::GetInstance().m_ListSort = last_sort;
}
else
{
last_sort = current_column;
last_column = current_column;
}
caller = this;
SortItems(wxListCompare, last_sort);
}
SetBackgroundColor();
event.Skip();
}
// This is used by keyboard gamelist search
void CGameListCtrl::OnKeyPress(wxListEvent& event)
{
static int lastKey = 0, sLoop = 0;
int Loop = 0;
for (int i = 0; i < (int)m_ISOFiles.size(); i++)
{
// Easy way to get game string
wxListItem bleh;
bleh.SetId(i);
bleh.SetColumn(COLUMN_TITLE);
bleh.SetMask(wxLIST_MASK_TEXT);
GetItem(bleh);
wxString text = bleh.GetText();
if (text.MakeUpper()[0] == event.GetKeyCode())
{
if (lastKey == event.GetKeyCode() && Loop < sLoop)
{
Loop++;
if (i + 1 == (int)m_ISOFiles.size())
i = -1;
continue;
}
else if (lastKey != event.GetKeyCode())
{
sLoop = 0;
}
lastKey = event.GetKeyCode();
sLoop++;
UnselectAll();
SetItemState(i, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
EnsureVisible(i);
break;
}
// If we get past the last game in the list,
// we'll have to go back to the first one.
if (i + 1 == (int)m_ISOFiles.size() && sLoop > 0 && Loop > 0)
i = -1;
}
event.Skip();
}
// This shows a little tooltip with the current Game's emulation state
void CGameListCtrl::OnMouseMotion(wxMouseEvent& event)
{
int flags;
long subitem = 0;
const long item = HitTest(event.GetPosition(), flags, &subitem);
static int lastItem = -1;
if (GetColumnCount() <= 1)
return;
if (item != wxNOT_FOUND)
{
wxRect Rect;
#ifdef __WXMSW__
if (subitem == COLUMN_EMULATION_STATE)
#else
// The subitem parameter of HitTest is only implemented for wxMSW. On
// all other platforms it will always be -1. Check the x position
// instead.
GetItemRect(item, Rect);
if (Rect.GetX() + Rect.GetWidth() - GetColumnWidth(COLUMN_EMULATION_STATE) < event.GetX())
#endif
{
if (toolTip || lastItem == item || this != FindFocus())
{
if (lastItem != item)
lastItem = -1;
event.Skip();
return;
}
// Emulation status
static const char* const emuState[] = {"Broken", "Intro", "In-Game", "Playable", "Perfect"};
const GameListItem& rISO = *m_ISOFiles[GetItemData(item)];
const int emu_state = rISO.GetEmuState();
const std::string& issues = rISO.GetIssues();
// Show a tooltip containing the EmuState and the state description
if (emu_state > 0 && emu_state < 6)
{
char temp[2048];
sprintf(temp, "^ %s%s%s", emuState[emu_state - 1], issues.size() > 0 ? " :\n" : "",
issues.c_str());
toolTip = new wxEmuStateTip(this, StrToWxStr(temp), &toolTip);
}
else
{
toolTip = new wxEmuStateTip(this, _("Not Set"), &toolTip);
}
// Get item Coords
GetItemRect(item, Rect);
int mx = Rect.GetWidth();
int my = Rect.GetY();
#ifndef __WXMSW__
// For some reason the y position does not account for the header
// row, so subtract the y position of the first visible item.
GetItemRect(GetTopItem(), Rect);
my -= Rect.GetY();
#endif
// Convert to screen coordinates
ClientToScreen(&mx, &my);
toolTip->SetBoundingRect(wxRect(mx - GetColumnWidth(COLUMN_EMULATION_STATE), my,
GetColumnWidth(COLUMN_EMULATION_STATE), Rect.GetHeight()));
toolTip->SetPosition(
wxPoint(mx - GetColumnWidth(COLUMN_EMULATION_STATE), my - 5 + Rect.GetHeight()));
lastItem = item;
}
}
if (!toolTip)
lastItem = -1;
event.Skip();
}
void CGameListCtrl::OnLeftClick(wxMouseEvent& event)
{
// Focus the clicked item.
int flags;
long item = HitTest(event.GetPosition(), flags);
if ((item != wxNOT_FOUND) && (GetSelectedItemCount() == 0) && (!event.ControlDown()) &&
(!event.ShiftDown()))
{
SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
SetItemState(item, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
wxGetApp().GetCFrame()->UpdateGUI();
}
event.Skip();
}
void CGameListCtrl::OnRightClick(wxMouseEvent& event)
{
// Focus the clicked item.
int flags;
long item = HitTest(event.GetPosition(), flags);
if (item != wxNOT_FOUND)
{
if (GetItemState(item, wxLIST_STATE_SELECTED) != wxLIST_STATE_SELECTED)
{
UnselectAll();
SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
SetItemState(item, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
}
if (GetSelectedItemCount() == 1)
{
const GameListItem* selected_iso = GetSelectedISO();
if (selected_iso)
{
wxMenu popupMenu;
DiscIO::Platform platform = selected_iso->GetPlatform();
if (platform != DiscIO::Platform::ELF_DOL)
{
popupMenu.Append(IDM_PROPERTIES, _("&Properties"));
popupMenu.Append(IDM_GAME_WIKI, _("&Wiki"));
popupMenu.AppendSeparator();
}
if (platform == DiscIO::Platform::WII_DISC || platform == DiscIO::Platform::WII_WAD)
{
popupMenu.Append(IDM_OPEN_SAVE_FOLDER, _("Open Wii &save folder"));
popupMenu.Append(IDM_EXPORT_SAVE, _("Export Wii save (Experimental)"));
}
popupMenu.Append(IDM_OPEN_CONTAINING_FOLDER, _("Open &containing folder"));
if (platform != DiscIO::Platform::ELF_DOL)
popupMenu.AppendCheckItem(IDM_SET_DEFAULT_ISO, _("Set as &default ISO"));
// First we have to decide a starting value when we append it
if (selected_iso->GetFileName() == SConfig::GetInstance().m_strDefaultISO)
popupMenu.FindItem(IDM_SET_DEFAULT_ISO)->Check();
popupMenu.AppendSeparator();
popupMenu.Append(IDM_DELETE_ISO, _("&Delete File..."));
if (platform == DiscIO::Platform::GAMECUBE_DISC || platform == DiscIO::Platform::WII_DISC)
{
if (selected_iso->GetBlobType() == DiscIO::BlobType::GCZ)
popupMenu.Append(IDM_COMPRESS_ISO, _("Decompress ISO..."));
else if (selected_iso->GetBlobType() == DiscIO::BlobType::PLAIN)
popupMenu.Append(IDM_COMPRESS_ISO, _("Compress ISO..."));
wxMenuItem* changeDiscItem = popupMenu.Append(IDM_LIST_CHANGE_DISC, _("Change &Disc"));
changeDiscItem->Enable(Core::IsRunning());
}
if (platform == DiscIO::Platform::WII_WAD)
popupMenu.Append(IDM_LIST_INSTALL_WAD, _("Install to Wii Menu"));
PopupMenu(&popupMenu);
}
}
else if (GetSelectedItemCount() > 1)
{
wxMenu popupMenu;
popupMenu.Append(IDM_DELETE_ISO, _("&Delete selected ISOs..."));
popupMenu.AppendSeparator();
popupMenu.Append(IDM_MULTI_COMPRESS_ISO, _("Compress selected ISOs..."));
popupMenu.Append(IDM_MULTI_DECOMPRESS_ISO, _("Decompress selected ISOs..."));
PopupMenu(&popupMenu);
}
}
const GameListItem* CGameListCtrl::GetSelectedISO() const
{
if (m_ISOFiles.size() == 0)
{
return nullptr;
}
else if (GetSelectedItemCount() == 0)
{
return nullptr;
}
else
{
long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == wxNOT_FOUND)
return nullptr;
return m_ISOFiles[GetItemData(item)];
}
}
std::vector<const GameListItem*> CGameListCtrl::GetAllSelectedISOs() const
{
std::vector<const GameListItem*> result;
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == wxNOT_FOUND)
return result;
result.push_back(m_ISOFiles[GetItemData(item)]);
}
}
bool CGameListCtrl::IsHidingItems()
{
return !(SConfig::GetInstance().m_ListGC && SConfig::GetInstance().m_ListWii &&
SConfig::GetInstance().m_ListWad && SConfig::GetInstance().m_ListElfDol &&
SConfig::GetInstance().m_ListJap && SConfig::GetInstance().m_ListUsa &&
SConfig::GetInstance().m_ListPal && SConfig::GetInstance().m_ListAustralia &&
SConfig::GetInstance().m_ListFrance && SConfig::GetInstance().m_ListGermany &&
SConfig::GetInstance().m_ListItaly && SConfig::GetInstance().m_ListKorea &&
SConfig::GetInstance().m_ListNetherlands && SConfig::GetInstance().m_ListRussia &&
SConfig::GetInstance().m_ListSpain && SConfig::GetInstance().m_ListTaiwan &&
SConfig::GetInstance().m_ListWorld && SConfig::GetInstance().m_ListUnknown);
}
void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
wxFileName path = wxFileName::FileName(StrToWxStr(iso->GetFileName()));
path.MakeAbsolute();
WxUtils::Explore(WxStrToStr(path.GetPath()));
}
void CGameListCtrl::OnOpenSaveFolder(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
std::string path = iso->GetWiiFSPath();
if (!path.empty())
WxUtils::Explore(path);
}
void CGameListCtrl::OnExportSave(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
u64 title_id;
std::unique_ptr<DiscIO::IVolume> volume(DiscIO::CreateVolumeFromFilename(iso->GetFileName()));
if (volume && volume->GetTitleID(&title_id))
{
CWiiSaveCrypted::ExportWiiSave(title_id);
}
}
// Save this file as the default file
void CGameListCtrl::OnSetDefaultISO(wxCommandEvent& event)
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
if (event.IsChecked())
{
// Write the new default value and save it the ini file
SConfig::GetInstance().m_strDefaultISO = iso->GetFileName();
SConfig::GetInstance().SaveSettings();
}
else
{
// Otherwise blank the value and save it
SConfig::GetInstance().m_strDefaultISO = "";
SConfig::GetInstance().SaveSettings();
}
}
void CGameListCtrl::OnDeleteISO(wxCommandEvent& WXUNUSED(event))
{
const wxString message =
GetSelectedItemCount() == 1 ?
_("Are you sure you want to delete this file? It will be gone forever!") :
_("Are you sure you want to delete these files? They will be gone forever!");
if (wxMessageBox(message, _("Warning"), wxYES_NO | wxICON_EXCLAMATION) == wxYES)
{
for (const GameListItem* iso : GetAllSelectedISOs())
File::Delete(iso->GetFileName());
Update();
}
}
void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
CISOProperties* ISOProperties = new CISOProperties(*iso, this);
ISOProperties->Show();
}
void CGameListCtrl::OnWiki(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
std::string wikiUrl =
"https://wiki.dolphin-emu.org/dolphin-redirect.php?gameid=" + iso->GetUniqueID();
WxUtils::Launch(wikiUrl);
}
bool CGameListCtrl::MultiCompressCB(const std::string& text, float percent, void* arg)
{
CompressionProgress* progress = static_cast<CompressionProgress*>(arg);
float total_percent = ((float)progress->items_done + percent) / (float)progress->items_total;
wxString text_string(
StrToWxStr(StringFromFormat("%s (%i/%i) - %s", progress->current_filename.c_str(),
progress->items_done + 1, progress->items_total, text.c_str())));
return progress->dialog->Update(total_percent * progress->dialog->GetRange(), text_string);
}
void CGameListCtrl::OnMultiCompressISO(wxCommandEvent& /*event*/)
{
CompressSelection(true);
}
void CGameListCtrl::OnMultiDecompressISO(wxCommandEvent& /*event*/)
{
CompressSelection(false);
}
void CGameListCtrl::CompressSelection(bool _compress)
{
std::vector<const GameListItem*> items_to_compress;
bool wii_compression_warning_accepted = false;
for (const GameListItem* iso : GetAllSelectedISOs())
{
// Don't include items that we can't do anything with
if (iso->GetPlatform() != DiscIO::Platform::GAMECUBE_DISC &&
iso->GetPlatform() != DiscIO::Platform::WII_DISC)
continue;
if (iso->GetBlobType() != DiscIO::BlobType::PLAIN &&
iso->GetBlobType() != DiscIO::BlobType::GCZ)
continue;
items_to_compress.push_back(iso);
// Show the Wii compression warning if it's relevant and it hasn't been shown already
if (!wii_compression_warning_accepted && _compress && !iso->IsCompressed() &&
iso->GetPlatform() == DiscIO::Platform::WII_DISC)
{
if (WiiCompressWarning())
wii_compression_warning_accepted = true;
else
return;
}
}
wxString dirHome;
wxGetHomeDir(&dirHome);
wxDirDialog browseDialog(this, _("Browse for output directory"), dirHome,
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
if (browseDialog.ShowModal() != wxID_OK)
return;
bool all_good = true;
{
wxProgressDialog progressDialog(
_compress ? _("Compressing ISO") : _("Decompressing ISO"), _("Working..."),
1000, // Arbitrary number that's larger than the dialog's width in pixels
this, wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
wxPD_REMAINING_TIME | wxPD_SMOOTH);
CompressionProgress progress(0, items_to_compress.size(), "", &progressDialog);
for (const GameListItem* iso : items_to_compress)
{
if (!iso->IsCompressed() && _compress)
{
std::string FileName;
SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
progress.current_filename = FileName;
FileName.append(".gcz");
std::string OutputFileName;
BuildCompleteFilename(OutputFileName, WxStrToStr(browseDialog.GetPath()), FileName);
if (File::Exists(OutputFileName) &&
wxMessageBox(
wxString::Format(_("The file %s already exists.\nDo you wish to replace it?"),
StrToWxStr(OutputFileName)),
_("Confirm File Overwrite"), wxYES_NO) == wxNO)
continue;
all_good &=
DiscIO::CompressFileToBlob(iso->GetFileName(), OutputFileName,
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0,
16384, &MultiCompressCB, &progress);
}
else if (iso->IsCompressed() && !_compress)
{
std::string FileName;
SplitPath(iso->GetFileName(), nullptr, &FileName, nullptr);
progress.current_filename = FileName;
if (iso->GetPlatform() == DiscIO::Platform::WII_DISC)
FileName.append(".iso");
else
FileName.append(".gcm");
std::string OutputFileName;
BuildCompleteFilename(OutputFileName, WxStrToStr(browseDialog.GetPath()), FileName);
if (File::Exists(OutputFileName) &&
wxMessageBox(
wxString::Format(_("The file %s already exists.\nDo you wish to replace it?"),
StrToWxStr(OutputFileName)),
_("Confirm File Overwrite"), wxYES_NO) == wxNO)
continue;
all_good &= DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), OutputFileName.c_str(),
&MultiCompressCB, &progress);
}
progress.items_done++;
}
}
if (!all_good)
WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action."));
Update();
}
bool CGameListCtrl::CompressCB(const std::string& text, float percent, void* arg)
{
return ((wxProgressDialog*)arg)->Update((int)(percent * 1000), StrToWxStr(text));
}
void CGameListCtrl::OnCompressISO(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso)
return;
bool is_compressed = iso->GetBlobType() == DiscIO::BlobType::GCZ;
wxString path;
std::string FileName, FilePath, FileExtension;
SplitPath(iso->GetFileName(), &FilePath, &FileName, &FileExtension);
do
{
if (is_compressed)
{
wxString FileType;
if (iso->GetPlatform() == DiscIO::Platform::WII_DISC)
FileType = _("All Wii ISO files (iso)") + "|*.iso";
else
FileType = _("All GameCube GCM files (gcm)") + "|*.gcm";
path = wxFileSelector(_("Save decompressed GCM/ISO"), StrToWxStr(FilePath),
StrToWxStr(FileName) + FileType.After('*'), wxEmptyString,
FileType + "|" + wxGetTranslation(wxALL_FILES), wxFD_SAVE, this);
}
else
{
if (iso->GetPlatform() == DiscIO::Platform::WII_DISC && !WiiCompressWarning())
return;
path = wxFileSelector(_("Save compressed GCM/ISO"), StrToWxStr(FilePath),
StrToWxStr(FileName) + ".gcz", wxEmptyString,
_("All compressed GC/Wii ISO files (gcz)") +
wxString::Format("|*.gcz|%s", wxGetTranslation(wxALL_FILES)),
wxFD_SAVE, this);
}
if (!path)
return;
} while (
wxFileExists(path) &&
wxMessageBox(wxString::Format(_("The file %s already exists.\nDo you wish to replace it?"),
path.c_str()),
_("Confirm File Overwrite"), wxYES_NO) == wxNO);
bool all_good = false;
{
wxProgressDialog dialog(is_compressed ? _("Decompressing ISO") : _("Compressing ISO"),
_("Working..."), 1000, this,
wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME |
wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH);
if (is_compressed)
all_good =
DiscIO::DecompressBlobToFile(iso->GetFileName(), WxStrToStr(path), &CompressCB, &dialog);
else
all_good = DiscIO::CompressFileToBlob(
iso->GetFileName(), WxStrToStr(path),
(iso->GetPlatform() == DiscIO::Platform::WII_DISC) ? 1 : 0, 16384, &CompressCB, &dialog);
}
if (!all_good)
WxUtils::ShowErrorDialog(_("Dolphin was unable to complete the requested action."));
Update();
}
void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
{
const GameListItem* iso = GetSelectedISO();
if (!iso || !Core::IsRunning())
return;
DVDInterface::ChangeDiscAsHost(WxStrToStr(iso->GetFileName()));
}
void CGameListCtrl::OnSize(wxSizeEvent& event)
{
event.Skip();
if (lastpos == event.GetSize())
return;
lastpos = event.GetSize();
AutomaticColumnWidth();
}
void CGameListCtrl::AutomaticColumnWidth()
{
wxRect rc(GetClientRect());
Freeze();
if (GetColumnCount() == 1)
{
SetColumnWidth(0, rc.GetWidth());
}
else if (GetColumnCount() > 0)
{
int resizable =
rc.GetWidth() - (GetColumnWidth(COLUMN_PLATFORM) + GetColumnWidth(COLUMN_BANNER) +
GetColumnWidth(COLUMN_ID) + GetColumnWidth(COLUMN_COUNTRY) +
GetColumnWidth(COLUMN_SIZE) + GetColumnWidth(COLUMN_EMULATION_STATE));
if (SConfig::GetInstance().m_showMakerColumn && SConfig::GetInstance().m_showFileNameColumn)
{
SetColumnWidth(COLUMN_TITLE, resizable / 3);
SetColumnWidth(COLUMN_MAKER, resizable / 3);
SetColumnWidth(COLUMN_FILENAME, resizable / 3);
}
else if (SConfig::GetInstance().m_showMakerColumn)
{
SetColumnWidth(COLUMN_TITLE, resizable / 2);
SetColumnWidth(COLUMN_MAKER, resizable / 2);
}
else if (SConfig::GetInstance().m_showFileNameColumn)
{
SetColumnWidth(COLUMN_TITLE, resizable / 2);
SetColumnWidth(COLUMN_FILENAME, resizable / 2);
}
else
{
SetColumnWidth(COLUMN_TITLE, resizable);
}
}
Thaw();
}
void CGameListCtrl::UnselectAll()
{
for (int i = 0; i < GetItemCount(); i++)
{
SetItemState(i, 0, wxLIST_STATE_SELECTED);
}
}
bool CGameListCtrl::WiiCompressWarning()
{
return wxMessageBox(_("Compressing a Wii disc image will irreversibly change the compressed copy "
"by removing padding data. Your disc image will still work. Continue?"),
_("Warning"), wxYES_NO) == wxYES;
}