dolphin/Source/Core/DolphinWX/GameListCtrl.cpp
Lioncash e1359382be Kill off _T and wxT macros
Minor other alterations that relate to above as well.

Also added the PanicAlertT version of alerts for some error messages that
use PanicAlert. We want the user to actually understand why the error
occurred.
2014-05-17 16:25:51 -04:00

1339 lines
36 KiB
C++

// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
#include <wx/app.h>
#include <wx/bitmap.h>
#include <wx/buffer.h>
#include <wx/chartype.h>
#include <wx/colour.h>
#include <wx/defs.h>
#include <wx/dirdlg.h>
#include <wx/event.h>
#include <wx/filedlg.h>
#include <wx/filefn.h>
#include <wx/filename.h>
#include <wx/gdicmn.h>
#include <wx/imaglist.h>
#include <wx/listbase.h>
#include <wx/listctrl.h>
#include <wx/menu.h>
#include <wx/menuitem.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/tipwin.h>
#include <wx/translation.h>
#include <wx/unichar.h>
#include <wx/utils.h>
#include <wx/version.h>
#include <wx/window.h>
#include <wx/windowid.h>
#include "Common/CDUtils.h"
#include "Common/Common.h"
#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
#include "Common/MathUtil.h"
#include "Common/StdMakeUnique.h"
#include "Common/StringUtil.h"
#include "Common/SysConf.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreParameter.h"
#include "Core/Movie.h"
#include "Core/Boot/Boot.h"
#include "Core/HW/DVDInterface.h"
#include "DiscIO/Blob.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"
#include "DolphinWX/MemoryCards/WiiSaveCrypted.h"
#include "DolphinWX/resources/Flag_Europe.xpm"
#include "DolphinWX/resources/Flag_France.xpm"
#include "DolphinWX/resources/Flag_Germany.xpm"
#include "DolphinWX/resources/Flag_Italy.xpm"
#include "DolphinWX/resources/Flag_Japan.xpm"
#include "DolphinWX/resources/Flag_Korea.xpm"
#include "DolphinWX/resources/Flag_SDK.xpm"
#include "DolphinWX/resources/Flag_Taiwan.xpm"
#include "DolphinWX/resources/Flag_Unknown.xpm"
#include "DolphinWX/resources/Flag_USA.xpm"
#include "DolphinWX/resources/Platform_Gamecube.xpm"
#include "DolphinWX/resources/Platform_Wad.xpm"
#include "DolphinWX/resources/Platform_Wii.xpm"
#include "DolphinWX/resources/rating_gamelist.h"
size_t CGameListCtrl::m_currentItem = 0;
size_t CGameListCtrl::m_numberItem = 0;
std::string CGameListCtrl::m_currentFilename;
bool sorted = false;
extern CFrame* main_frame;
static int CompareGameListItems(const GameListItem* iso1, const GameListItem* iso2,
long sortData = CGameListCtrl::COLUMN_TITLE)
{
int t = 1;
if (sortData < 0)
{
t = -1;
sortData = -sortData;
}
int indexOne = 0;
int indexOther = 0;
// index only matters for WADS and PAL GC games, but invalid indicies for the others
// will return the (only) language in the list
if (iso1->GetPlatform() == GameListItem::WII_WAD)
{
indexOne = SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.LNG");
}
else // GC
{
indexOne = SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage;
}
if (iso2->GetPlatform() == GameListItem::WII_WAD)
{
indexOther = SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.LNG");
}
else // GC
{
indexOther = SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage;
}
switch (sortData)
{
case CGameListCtrl::COLUMN_TITLE:
if (!strcasecmp(iso1->GetName(indexOne).c_str(),iso2->GetName(indexOther).c_str()))
{
if (iso1->IsDiscTwo())
return 1 * t;
else if (iso2->IsDiscTwo())
return -1 * t;
}
return strcasecmp(iso1->GetName(indexOne).c_str(),
iso2->GetName(indexOther).c_str()) * t;
case CGameListCtrl::COLUMN_NOTES:
{
std::string cmp1 =
(iso1->GetPlatform() == GameListItem::GAMECUBE_DISC) ?
iso1->GetCompany() : iso1->GetDescription(indexOne);
std::string cmp2 =
(iso2->GetPlatform() == GameListItem::GAMECUBE_DISC) ?
iso2->GetCompany() : iso2->GetDescription(indexOther);
return strcasecmp(cmp1.c_str(), cmp2.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;
}
bool operator < (const GameListItem &one, const GameListItem &other)
{
return CompareGameListItems(&one, &other) < 0;
}
BEGIN_EVENT_TABLE(wxEmuStateTip, wxTipWindow)
EVT_KEY_DOWN(wxEmuStateTip::OnKeyDown)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(CGameListCtrl, wxListCtrl)
EVT_SIZE(CGameListCtrl::OnSize)
EVT_RIGHT_DOWN(CGameListCtrl::OnRightClick)
EVT_LEFT_DOWN(CGameListCtrl::OnLeftClick)
EVT_LIST_KEY_DOWN(LIST_CTRL, CGameListCtrl::OnKeyPress)
EVT_MOTION(CGameListCtrl::OnMouseMotion)
EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL, CGameListCtrl::OnColBeginDrag)
EVT_LIST_COL_CLICK(LIST_CTRL, CGameListCtrl::OnColumnClick)
EVT_MENU(IDM_PROPERTIES, CGameListCtrl::OnProperties)
EVT_MENU(IDM_GAMEWIKI, CGameListCtrl::OnWiki)
EVT_MENU(IDM_OPENCONTAININGFOLDER, CGameListCtrl::OnOpenContainingFolder)
EVT_MENU(IDM_OPENSAVEFOLDER, CGameListCtrl::OnOpenSaveFolder)
EVT_MENU(IDM_EXPORTSAVE, CGameListCtrl::OnExportSave)
EVT_MENU(IDM_SETDEFAULTGCM, CGameListCtrl::OnSetDefaultGCM)
EVT_MENU(IDM_COMPRESSGCM, CGameListCtrl::OnCompressGCM)
EVT_MENU(IDM_MULTICOMPRESSGCM, CGameListCtrl::OnMultiCompressGCM)
EVT_MENU(IDM_MULTIDECOMPRESSGCM, CGameListCtrl::OnMultiDecompressGCM)
EVT_MENU(IDM_DELETEGCM, CGameListCtrl::OnDeleteGCM)
END_EVENT_TABLE()
CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const
wxPoint& pos, const wxSize& size, long style)
: wxListCtrl(parent, id, pos, size, style), toolTip(nullptr)
{
DragAcceptFiles(true);
Connect(wxEVT_DROP_FILES, wxDropFilesEventHandler(CGameListCtrl::OnDropFiles), nullptr, this);
}
CGameListCtrl::~CGameListCtrl()
{
if (m_imageListSmall)
delete m_imageListSmall;
ClearIsoFiles();
}
void CGameListCtrl::InitBitmaps()
{
m_imageListSmall = new wxImageList(96, 32);
SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL);
m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES);
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = m_imageListSmall->Add(wxBitmap(Flag_Europe_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_GERMANY] = m_imageListSmall->Add(wxBitmap(Flag_Germany_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = m_imageListSmall->Add(wxBitmap(Flag_France_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = m_imageListSmall->Add(wxBitmap(Flag_USA_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAPAN] = m_imageListSmall->Add(wxBitmap(Flag_Japan_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_KOREA] = m_imageListSmall->Add(wxBitmap(Flag_Korea_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_ITALY] = m_imageListSmall->Add(wxBitmap(Flag_Italy_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_TAIWAN] = m_imageListSmall->Add(wxBitmap(Flag_Taiwan_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_SDK] = m_imageListSmall->Add(wxBitmap(Flag_SDK_xpm));
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = m_imageListSmall->Add(wxBitmap(Flag_Unknown_xpm));
m_PlatformImageIndex.resize(3);
m_PlatformImageIndex[0] = m_imageListSmall->Add(wxBitmap(Platform_Gamecube_xpm));
m_PlatformImageIndex[1] = m_imageListSmall->Add(wxBitmap(Platform_Wii_xpm));
m_PlatformImageIndex[2] = m_imageListSmall->Add(wxBitmap(Platform_Wad_xpm));
m_EmuStateImageIndex.resize(6);
m_EmuStateImageIndex[0] = m_imageListSmall->Add(wxBitmap(rating_0));
m_EmuStateImageIndex[1] = m_imageListSmall->Add(wxBitmap(rating_1));
m_EmuStateImageIndex[2] = m_imageListSmall->Add(wxBitmap(rating_2));
m_EmuStateImageIndex[3] = m_imageListSmall->Add(wxBitmap(rating_3));
m_EmuStateImageIndex[4] = m_imageListSmall->Add(wxBitmap(rating_4));
m_EmuStateImageIndex[5] = m_imageListSmall->Add(wxBitmap(rating_5));
}
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;
if (m_imageListSmall)
{
delete m_imageListSmall;
m_imageListSmall = nullptr;
}
Hide();
ScanForISOs();
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"));
// Instead of showing the notes + the company, which is unknown with
// wii titles We show in the same column : company for GC games and
// description for wii/wad games
InsertColumn(COLUMN_NOTES, _("Notes"));
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
// set initial sizes for columns
SetColumnWidth(COLUMN_DUMMY,0);
SetColumnWidth(COLUMN_PLATFORM, 35 + platform_padding);
SetColumnWidth(COLUMN_BANNER, 96 + platform_padding);
SetColumnWidth(COLUMN_TITLE, 200 + platform_padding);
SetColumnWidth(COLUMN_NOTES, 200 + platform_padding);
SetColumnWidth(COLUMN_COUNTRY, 32 + platform_padding);
SetColumnWidth(COLUMN_EMULATION_STATE, 50 + platform_padding);
// add all items
for (int i = 0; i < (int)m_ISOFiles.size(); i++)
{
InsertItemInReportView(i);
if (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, wxLIST_AUTOSIZE);
}
else
{
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
// second message instead
if ((SConfig::GetInstance().m_ListGC &&
SConfig::GetInstance().m_ListWii &&
SConfig::GetInstance().m_ListWad) &&
(SConfig::GetInstance().m_ListJap &&
SConfig::GetInstance().m_ListUsa &&
SConfig::GetInstance().m_ListPal))
{
errorString = _("Dolphin could not find any GC/Wii ISOs. Doubleclick here to browse for files...");
}
else
{
errorString = _("Dolphin is currently set to hide all games. Doubleclick here to show all games...");
}
InsertColumn(0, _("No ISOs or WADS found"));
long index = InsertItem(0, errorString);
SetItemFont(index, *wxITALIC_FONT);
SetColumnWidth(0, wxLIST_AUTOSIZE);
}
if (GetSelectedISO() == nullptr)
main_frame->UpdateGUI();
Show();
AutomaticColumnWidth();
ScrollLines(scrollPos);
SetFocus();
}
wxString NiceSizeFormat(u64 _size)
{
const char* const unit_symbols[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"};
const u64 unit = Log2(std::max<u64>(_size, 1)) / 10;
const u64 unit_size = (1 << (unit * 10));
// ugly rounding integer math
const u64 value = (_size + unit_size / 2) / unit_size;
const u64 frac = (_size % unit_size * 10 + unit_size / 2) / unit_size % 10;
return StrToWxStr(StringFromFormat("%" PRIu64 ".%" PRIu64 " %s", value, frac, unit_symbols[unit]));
}
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
int ImageIndex = -1;
GameListItem& rISOFile = *m_ISOFiles[_Index];
// Insert a first row with nothing in it, that will be used as the Index
long ItemIndex = InsertItem(_Index, wxEmptyString);
// Insert the platform's image in the first (visible) column
SetItemColumnImage(_Index, COLUMN_PLATFORM, m_PlatformImageIndex[rISOFile.GetPlatform()]);
if (rISOFile.GetBitmap().IsOk())
ImageIndex = m_imageListSmall->Add(rISOFile.GetBitmap());
// Set the game's banner in the second column
SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex);
int SelectedLanguage = SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage;
// Is this sane?
if (rISOFile.GetPlatform() == GameListItem::WII_WAD)
{
SelectedLanguage = SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.LNG");
}
std::string const name = rISOFile.GetName(SelectedLanguage);
SetItem(_Index, COLUMN_TITLE, StrToWxStr(name), -1);
// We show the company string on Gamecube only
// On Wii we show the description instead as the company string is empty
std::string const notes = (rISOFile.GetPlatform() == GameListItem::GAMECUBE_DISC) ?
rISOFile.GetCompany() : rISOFile.GetDescription(SelectedLanguage);
SetItem(_Index, COLUMN_NOTES, StrToWxStr(notes), -1);
// Emulation state
SetItemColumnImage(_Index, COLUMN_EMULATION_STATE, m_EmuStateImageIndex[rISOFile.GetEmuState()]);
// Country
SetItemColumnImage(_Index, COLUMN_COUNTRY, m_FlagImageIndex[rISOFile.GetCountry()]);
// File size
SetItem(_Index, COLUMN_SIZE, NiceSizeFormat(rISOFile.GetFileSize()), -1);
// Background color
SetBackgroundColor();
// Item data
SetItemData(_Index, ItemIndex);
}
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();
CFileSearch::XStringVector Directories(SConfig::GetInstance().m_ISOFolder);
if (SConfig::GetInstance().m_RecursiveISOFolder)
{
for (u32 i = 0; i < Directories.size(); i++)
{
File::FSTEntry FST_Temp;
File::ScanDirectoryTree(Directories[i], FST_Temp);
for (auto& Entry : FST_Temp.children)
{
if (Entry.isDirectory)
{
bool duplicate = false;
for (auto& Directory : Directories)
{
if (Directory == Entry.physicalName)
{
duplicate = true;
break;
}
}
if (!duplicate)
Directories.push_back(Entry.physicalName);
}
}
}
}
CFileSearch::XStringVector 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");
CFileSearch FileSearch(Extensions, Directories);
const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames();
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]);
if (iso_file->IsValid())
{
bool list = true;
switch(iso_file->GetPlatform())
{
case GameListItem::WII_DISC:
if (!SConfig::GetInstance().m_ListWii)
list = false;
break;
case GameListItem::WII_WAD:
if (!SConfig::GetInstance().m_ListWad)
list = false;
break;
default:
if (!SConfig::GetInstance().m_ListGC)
list = false;
break;
}
switch(iso_file->GetCountry())
{
case DiscIO::IVolume::COUNTRY_TAIWAN:
if (!SConfig::GetInstance().m_ListTaiwan)
list = false;
case DiscIO::IVolume::COUNTRY_KOREA:
if (!SConfig::GetInstance().m_ListKorea)
list = false;
break;
case DiscIO::IVolume::COUNTRY_JAPAN:
if (!SConfig::GetInstance().m_ListJap)
list = false;
break;
case DiscIO::IVolume::COUNTRY_USA:
if (!SConfig::GetInstance().m_ListUsa)
list = false;
break;
case DiscIO::IVolume::COUNTRY_FRANCE:
if (!SConfig::GetInstance().m_ListFrance)
list = false;
break;
case DiscIO::IVolume::COUNTRY_ITALY:
if (!SConfig::GetInstance().m_ListItaly)
list = false;
break;
default:
if (!SConfig::GetInstance().m_ListPal)
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);
if (gli->IsValid())
m_ISOFiles.push_back(gli.release());
}
}
std::sort(m_ISOFiles.begin(), m_ISOFiles.end());
}
void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
{
if (event.GetColumn() != COLUMN_TITLE && event.GetColumn() != COLUMN_NOTES)
event.Veto();
}
const GameListItem *CGameListCtrl::GetISO(size_t index) const
{
if (index < m_ISOFiles.size())
return m_ISOFiles[index];
else
return nullptr;
}
CGameListCtrl *caller;
#if wxCHECK_VERSION(2, 9, 0)
int wxCALLBACK wxListCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
#else // 2.8.x
int wxCALLBACK wxListCompare(long item1, long item2, long sortData)
#endif
{
// 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();
#ifdef __WXGTK__
if (text.MakeLower()[0] == event.GetKeyCode())
#else
if (text.MakeUpper()[0] == event.GetKeyCode())
#endif
{
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 = new wxMenu;
popupMenu->Append(IDM_PROPERTIES, _("&Properties"));
popupMenu->Append(IDM_GAMEWIKI, _("&Wiki"));
popupMenu->AppendSeparator();
if (selected_iso->GetPlatform() != GameListItem::GAMECUBE_DISC)
{
popupMenu->Append(IDM_OPENSAVEFOLDER, _("Open Wii &save folder"));
popupMenu->Append(IDM_EXPORTSAVE, _("Export Wii save (Experimental)"));
}
popupMenu->Append(IDM_OPENCONTAININGFOLDER, _("Open &containing folder"));
popupMenu->AppendCheckItem(IDM_SETDEFAULTGCM, _("Set as &default ISO"));
// First we have to decide a starting value when we append it
if (selected_iso->GetFileName() == SConfig::GetInstance().
m_LocalCoreStartupParameter.m_strDefaultGCM)
popupMenu->FindItem(IDM_SETDEFAULTGCM)->Check();
popupMenu->AppendSeparator();
popupMenu->Append(IDM_DELETEGCM, _("&Delete ISO..."));
if (selected_iso->GetPlatform() != GameListItem::WII_WAD)
{
if (selected_iso->IsCompressed())
popupMenu->Append(IDM_COMPRESSGCM, _("Decompress ISO..."));
else if (selected_iso->GetFileName().substr(selected_iso->GetFileName().find_last_of(".")) != ".ciso" &&
selected_iso->GetFileName().substr(selected_iso->GetFileName().find_last_of(".")) != ".wbfs")
popupMenu->Append(IDM_COMPRESSGCM, _("Compress ISO..."));
}
else
{
popupMenu->Append(IDM_LIST_INSTALLWAD, _("Install to Wii Menu"));
}
PopupMenu(popupMenu);
}
}
else if (GetSelectedItemCount() > 1)
{
wxMenu* popupMenu = new wxMenu;
popupMenu->Append(IDM_DELETEGCM, _("&Delete selected ISOs..."));
popupMenu->AppendSeparator();
popupMenu->Append(IDM_MULTICOMPRESSGCM, _("Compress selected ISOs..."));
popupMenu->Append(IDM_MULTIDECOMPRESSGCM, _("Decompress selected ISOs..."));
PopupMenu(popupMenu);
}
}
const GameListItem * CGameListCtrl::GetSelectedISO()
{
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;
}
else
{
// Here is a little workaround for multiselections:
// when > 1 item is selected, return info on the first one
// and deselect it so the next time GetSelectedISO() is called,
// the next item's info is returned
if (GetSelectedItemCount() > 1)
SetItemState(item, 0, wxLIST_STATE_SELECTED);
return m_ISOFiles[GetItemData(item)];
}
}
}
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;
DiscIO::IVolume *Iso = DiscIO::CreateVolumeFromFilename(iso->GetFileName());
if (Iso)
{
if (Iso->GetTitleID((u8*)&title))
{
title = Common::swap64(title);
CWiiSaveCrypted::ExportWiiSave(title);
}
delete Iso;
}
}
// Save this file as the default file
void CGameListCtrl::OnSetDefaultGCM(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_LocalCoreStartupParameter.m_strDefaultGCM =
iso->GetFileName();
SConfig::GetInstance().SaveSettings();
}
else
{
// Otherwise blank the value and save it
SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM = "";
SConfig::GetInstance().SaveSettings();
}
}
void CGameListCtrl::OnDeleteGCM(wxCommandEvent& WXUNUSED (event))
{
if (GetSelectedItemCount() == 1)
{
const GameListItem *iso = GetSelectedISO();
if (!iso)
return;
if (wxMessageBox(_("Are you sure you want to delete this file? It will be gone forever!"),
wxMessageBoxCaptionStr, wxYES_NO | wxICON_EXCLAMATION) == wxYES)
{
File::Delete(iso->GetFileName());
Update();
}
}
else
{
if (wxMessageBox(_("Are you sure you want to delete these files?\nThey will be gone forever!"),
wxMessageBoxCaptionStr, wxYES_NO | wxICON_EXCLAMATION) == wxYES)
{
int selected = GetSelectedItemCount();
for (int i = 0; i < selected; i++)
{
const GameListItem *iso = GetSelectedISO();
File::Delete(iso->GetFileName());
}
Update();
}
}
}
void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED (event))
{
const GameListItem *iso = GetSelectedISO();
if (!iso)
return;
CISOProperties ISOProperties(iso->GetFileName(), this);
if (ISOProperties.ShowModal() == wxID_OK)
Update();
}
void CGameListCtrl::OnWiki(wxCommandEvent& WXUNUSED (event))
{
const GameListItem *iso = GetSelectedISO();
if (!iso)
return;
std::string wikiUrl = "http://wiki.dolphin-emu.org/dolphin-redirect.php?gameid=[GAME_ID]";
wikiUrl = ReplaceAll(wikiUrl, "[GAME_ID]", UriEncode(iso->GetUniqueID()));
if (UriEncode(iso->GetName(0)).length() < 100)
wikiUrl = ReplaceAll(wikiUrl, "[GAME_NAME]", UriEncode(iso->GetName(0)));
else
wikiUrl = ReplaceAll(wikiUrl, "[GAME_NAME]", "");
WxUtils::Launch(wikiUrl);
}
void CGameListCtrl::MultiCompressCB(const char* text, float percent, void* arg)
{
percent = (((float)m_currentItem) + percent) / (float)m_numberItem;
wxString textString(StrToWxStr(StringFromFormat("%s (%i/%i) - %s",
m_currentFilename.c_str(), (int)m_currentItem+1,
(int)m_numberItem, text)));
((wxProgressDialog*)arg)->Update((int)(percent*1000), textString);
}
void CGameListCtrl::OnMultiCompressGCM(wxCommandEvent& /*event*/)
{
CompressSelection(true);
}
void CGameListCtrl::OnMultiDecompressGCM(wxCommandEvent& /*event*/)
{
CompressSelection(false);
}
void CGameListCtrl::CompressSelection(bool _compress)
{
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,
this,
wxPD_APP_MODAL |
wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME |
wxPD_SMOOTH
);
m_currentItem = 0;
m_numberItem = GetSelectedItemCount();
for (u32 i=0; i < m_numberItem; i++)
{
const GameListItem *iso = GetSelectedISO();
if (!iso->IsCompressed() && _compress)
{
std::string FileName, FileExt;
SplitPath(iso->GetFileName(), nullptr, &FileName, &FileExt);
m_currentFilename = FileName;
FileName.append(".gcz");
std::string OutputFileName;
BuildCompleteFilename(OutputFileName,
WxStrToStr(browseDialog.GetPath()),
FileName);
if (wxFileExists(StrToWxStr(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() == GameListItem::WII_DISC) ? 1 : 0,
16384, &MultiCompressCB, &progressDialog);
}
else if (iso->IsCompressed() && !_compress)
{
std::string FileName, FileExt;
SplitPath(iso->GetFileName(), nullptr, &FileName, &FileExt);
m_currentFilename = FileName;
if (iso->GetPlatform() == GameListItem::WII_DISC)
FileName.append(".iso");
else
FileName.append(".gcm");
std::string OutputFileName;
BuildCompleteFilename(OutputFileName,
WxStrToStr(browseDialog.GetPath()),
FileName);
if (wxFileExists(StrToWxStr(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, &progressDialog);
}
m_currentItem++;
}
}
if (!all_good)
wxMessageBox(_("Dolphin was unable to complete the requested action."));
Update();
}
void CGameListCtrl::CompressCB(const char* text, float percent, void* arg)
{
((wxProgressDialog*)arg)->
Update((int)(percent*1000), StrToWxStr(text));
}
void CGameListCtrl::OnCompressGCM(wxCommandEvent& WXUNUSED (event))
{
const GameListItem *iso = GetSelectedISO();
if (!iso)
return;
wxString path;
std::string FileName, FilePath, FileExtension;
SplitPath(iso->GetFileName(), &FilePath, &FileName, &FileExtension);
do
{
if (iso->IsCompressed())
{
wxString FileType;
if (iso->GetPlatform() == GameListItem::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
{
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(
iso->IsCompressed() ? _("Decompressing ISO") : _("Compressing ISO"),
_("Working..."),
1000,
this,
wxPD_APP_MODAL |
wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME |
wxPD_SMOOTH
);
if (iso->IsCompressed())
all_good = DiscIO::DecompressBlobToFile(iso->GetFileName(),
WxStrToStr(path), &CompressCB, &dialog);
else
all_good = DiscIO::CompressFileToBlob(iso->GetFileName(),
WxStrToStr(path),
(iso->GetPlatform() == GameListItem::WII_DISC) ? 1 : 0,
16384, &CompressCB, &dialog);
}
if (!all_good)
wxMessageBox(_("Dolphin was unable to complete the requested action."));
Update();
}
void CGameListCtrl::OnSize(wxSizeEvent& event)
{
if (lastpos == event.GetSize())
return;
lastpos = event.GetSize();
AutomaticColumnWidth();
event.Skip();
}
void CGameListCtrl::AutomaticColumnWidth()
{
wxRect rc(GetClientRect());
if (GetColumnCount() == 1)
{
SetColumnWidth(0, rc.GetWidth());
}
else if (GetColumnCount() > 4)
{
int resizable = rc.GetWidth() - (
GetColumnWidth(COLUMN_BANNER)
+ GetColumnWidth(COLUMN_COUNTRY)
+ GetColumnWidth(COLUMN_SIZE)
+ GetColumnWidth(COLUMN_EMULATION_STATE)
+ GetColumnWidth(COLUMN_PLATFORM));
// We hide the Notes column if the window is too small
if (resizable > 400)
{
SetColumnWidth(COLUMN_TITLE, resizable / 2);
SetColumnWidth(COLUMN_NOTES, resizable / 2);
}
else
{
SetColumnWidth(COLUMN_TITLE, resizable);
SetColumnWidth(COLUMN_NOTES, 0);
}
}
}
void CGameListCtrl::UnselectAll()
{
for (int i=0; i<GetItemCount(); i++)
{
SetItemState(i, 0, wxLIST_STATE_SELECTED);
}
}
void CGameListCtrl::OnDropFiles(wxDropFilesEvent& event)
{
if (event.GetNumberOfFiles() != 1)
return;
if (File::IsDirectory(WxStrToStr(event.GetFiles()[0])))
return;
wxFileName file = event.GetFiles()[0];
if (file.GetExt() == "dtm")
{
if (Core::IsRunning())
return;
if (!Movie::IsReadOnly())
{
// let's make the read-only flag consistent at the start of a movie.
Movie::SetReadOnly(true);
main_frame->GetMenuBar()->FindItem(IDM_RECORDREADONLY)->Check(true);
}
if (Movie::PlayInput(WxStrToStr(file.GetFullPath())))
main_frame->BootGame("");
}
else if (!Core::IsRunning())
{
main_frame->BootGame(WxStrToStr(file.GetFullPath()));
}
else
{
DVDInterface::ChangeDisc(WxStrToStr(file.GetFullPath()));
}
}