snes9x/gtk/src/gtk_cheat.cpp

381 lines
11 KiB
C++
Raw Normal View History

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
2010-09-25 17:46:12 +02:00
#include "gtk_s9x.h"
#include "gtk_cheat.h"
#include "cheats.h"
#include "display.h"
2010-09-25 17:46:12 +02:00
enum {
2010-09-25 17:46:12 +02:00
COLUMN_ENABLED = 0,
COLUMN_DESCRIPTION = 1,
COLUMN_CHEAT = 2,
NUM_COLS
};
extern SCheatData Cheat;
static void display_errorbox(const char *error)
2010-09-25 17:46:12 +02:00
{
auto dialog = Gtk::MessageDialog(error, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dialog.set_title(_("Error"));
dialog.run();
dialog.hide();
2010-09-25 17:46:12 +02:00
}
static Snes9xCheats *cheats_dialog = nullptr;
void open_snes9x_cheats_dialog()
2010-09-25 17:46:12 +02:00
{
if (!cheats_dialog)
cheats_dialog = new Snes9xCheats;
2010-09-25 17:46:12 +02:00
cheats_dialog->show();
2018-06-06 02:17:17 +02:00
}
Snes9xCheats::Snes9xCheats()
: GtkBuilderWindow("cheat_window")
2018-06-06 02:17:17 +02:00
{
dst_row = -1;
auto view = get_object<Gtk::TreeView>("cheat_treeview");
view->signal_row_activated().connect(sigc::mem_fun(*this, &Snes9xCheats::row_activated));
view->set_reorderable();
2018-06-06 02:17:17 +02:00
auto toggle_renderer = new Gtk::CellRendererToggle();
toggle_renderer->set_activatable();
toggle_renderer->signal_toggled().connect(sigc::mem_fun(*this, &Snes9xCheats::toggle_code));
2010-09-25 17:46:12 +02:00
view->insert_column("\xe2\x86\x91", *toggle_renderer, COLUMN_ENABLED);
auto column = view->get_column(COLUMN_ENABLED);
column->set_clickable();
column->set_alignment(0.5);
column->add_attribute(*toggle_renderer, "active", COLUMN_ENABLED);
column->signal_clicked().connect([&] {
sort_cheats();
});
2018-04-28 03:35:20 +02:00
auto text_renderer = new Gtk::CellRendererText();
view->insert_column(_("Description"), *text_renderer, COLUMN_DESCRIPTION);
column = view->get_column(COLUMN_DESCRIPTION);
column->set_resizable();
column->set_min_width(40);
column->add_attribute(*text_renderer, "text", COLUMN_DESCRIPTION);
2018-04-28 03:35:20 +02:00
view->insert_column(_("Cheat"), *text_renderer, COLUMN_CHEAT);
column = view->get_column(COLUMN_CHEAT);
column->set_resizable();
column->set_min_width(40);
column->add_attribute(*text_renderer, "text", COLUMN_CHEAT);
2018-04-28 03:35:20 +02:00
Gtk::TreeModelColumn<bool> column1;
Gtk::TreeModelColumn<Glib::ustring> column2;
Gtk::TreeModelColumn<Glib::ustring> column3;
Gtk::TreeModelColumnRecord record;
record.add(column1);
record.add(column2);
record.add(column3);
2018-04-28 03:35:20 +02:00
store = Gtk::ListStore::create(record);
view->set_model(store);
2010-09-25 17:46:12 +02:00
delete_id = store->signal_row_deleted().connect([&](const Gtk::TreeModel::Path &path) {
row_deleted(get_index_from_path(path));
});
2018-06-05 02:27:24 +02:00
insert_id = store->signal_row_inserted().connect([&](const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter) {
row_inserted(get_index_from_path(path));
});
2018-06-05 02:27:24 +02:00
get_object<Gtk::Button>("add_code")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::add_code));
get_object<Gtk::Button>("remove_code")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::remove_code));
get_object<Gtk::Button>("update_button")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::update_code));
get_object<Gtk::Button>("disable_all_button")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::disable_all));
get_object<Gtk::Button>("delete_all_cheats_button")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::delete_all_cheats));
get_object<Gtk::Button>("cheat_search_button")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::search_database));
get_object<Gtk::Button>("update_button")->signal_clicked().connect(sigc::mem_fun(*this, &Snes9xCheats::update_code));
2018-06-05 02:27:24 +02:00
gtk_widget_realize(GTK_WIDGET(window->gobj()));
2010-09-25 17:46:12 +02:00
}
Snes9xCheats::~Snes9xCheats()
2010-09-25 17:46:12 +02:00
{
}
void Snes9xCheats::enable_dnd(bool enable)
2018-06-05 02:27:24 +02:00
{
if (enable)
{
delete_id.unblock();
insert_id.unblock();
2018-06-05 02:27:24 +02:00
}
else
{
delete_id.block();
insert_id.unblock();
2018-06-05 02:27:24 +02:00
}
}
void Snes9xCheats::show()
2010-09-25 17:46:12 +02:00
{
top_level->pause_from_focus_change();
2010-09-25 17:46:12 +02:00
window->set_transient_for(*top_level->window.get());
2010-09-25 17:46:12 +02:00
refresh_tree_view();
Glib::RefPtr<Gtk::Dialog>::cast_static(window)->run();
window->hide();
2018-06-05 02:27:24 +02:00
top_level->unpause_from_focus_change();
2010-09-25 17:46:12 +02:00
}
static void cheat_move(int src, int dst)
2018-06-05 02:27:24 +02:00
{
2022-05-11 03:47:12 +02:00
Cheat.group.insert(Cheat.group.begin() + dst, Cheat.group[src]);
2018-06-05 02:27:24 +02:00
if (dst < src)
src++;
2022-05-11 03:47:12 +02:00
Cheat.group.erase(Cheat.group.begin() + src);
2018-06-05 02:27:24 +02:00
}
static void cheat_gather_enabled()
2018-06-05 02:27:24 +02:00
{
unsigned int enabled = 0;
2022-05-11 03:47:12 +02:00
for (unsigned int i = 0; i < Cheat.group.size(); i++)
2018-06-05 02:27:24 +02:00
{
2022-05-11 03:47:12 +02:00
if (Cheat.group[i].enabled && i >= enabled)
2018-06-05 02:27:24 +02:00
{
cheat_move(i, enabled);
enabled++;
}
}
}
void Snes9xCheats::row_deleted(int src_row)
2018-06-05 02:27:24 +02:00
{
if (dst_row >= 0)
{
if (src_row >= dst_row)
src_row--;
cheat_move(src_row, dst_row);
2018-06-05 02:27:24 +02:00
dst_row = -1;
}
}
void Snes9xCheats::row_inserted(int new_row)
2018-06-05 02:27:24 +02:00
{
dst_row = new_row;
}
int Snes9xCheats::get_selected_index()
2010-09-25 17:46:12 +02:00
{
auto selection = get_object<Gtk::TreeView>("cheat_treeview")->get_selection();
auto rows = selection->get_selected_rows();
if (rows.empty())
return -1;
auto indices = gtk_tree_path_get_indices(rows[0].gobj());
return indices[0];
}
2010-09-25 17:46:12 +02:00
int Snes9xCheats::get_index_from_path(Gtk::TreeModel::Path path)
{
gint *indices = gtk_tree_path_get_indices(path.gobj());
int index = indices[0];
2010-09-25 17:46:12 +02:00
return index;
}
int Snes9xCheats::get_index_from_path(const Glib::ustring &path)
2010-09-25 17:46:12 +02:00
{
return get_index_from_path(Gtk::TreeModel::Path(path));
2010-09-25 17:46:12 +02:00
}
void Snes9xCheats::refresh_tree_view()
2010-09-25 17:46:12 +02:00
{
enable_dnd(false);
2010-09-25 17:46:12 +02:00
auto list_size = store->children().size();
2018-04-28 03:35:20 +02:00
2022-05-11 03:47:12 +02:00
if (Cheat.group.size() == 0)
2018-04-28 03:35:20 +02:00
return;
2022-05-11 03:47:12 +02:00
for (unsigned int i = 0; i < Cheat.group.size() - list_size; i++)
store->append();
2010-09-25 17:46:12 +02:00
auto iter = store->children().begin();
2022-05-11 03:47:12 +02:00
for (unsigned int i = 0; i < Cheat.group.size (); i++)
2010-09-25 17:46:12 +02:00
{
2022-05-11 03:47:12 +02:00
auto str = S9xCheatGroupToText(i);
Glib::ustring description = Cheat.group[i].name[0] == '\0' ? "" :Cheat.group[i].name;
iter->set_value(COLUMN_ENABLED, Cheat.group[i].enabled);
iter->set_value(COLUMN_DESCRIPTION, description);
iter->set_value(COLUMN_CHEAT, Glib::ustring(str));
iter++;
2010-09-25 17:46:12 +02:00
}
2018-06-06 02:17:17 +02:00
enable_dnd(true);
2010-09-25 17:46:12 +02:00
}
void Snes9xCheats::add_code()
2010-09-25 17:46:12 +02:00
{
std::string code = get_entry_text("code_entry");
std::string description = get_entry_text("description_entry");
2010-09-25 17:46:12 +02:00
if (description.empty())
description = _("No description");
2022-05-11 03:47:12 +02:00
if (S9xAddCheatGroup(description, code) < 0)
2018-06-06 02:17:17 +02:00
{
display_errorbox(_("Couldn't find any cheat codes in input."));
2018-06-06 02:17:17 +02:00
return;
}
2022-05-11 03:47:12 +02:00
auto parsed_code = S9xCheatGroupToText(Cheat.group.size() - 1);
set_entry_text("code_entry", parsed_code);
2010-09-25 17:46:12 +02:00
get_object<Gtk::Entry>("code_entry")->grab_focus();
2010-09-25 17:46:12 +02:00
refresh_tree_view();
2010-09-25 17:46:12 +02:00
while (Gtk::Main::events_pending())
Gtk::Main::iteration(false);
2018-06-06 02:17:17 +02:00
auto selection = get_object<Gtk::TreeView>("cheat_treeview")->get_selection();
Gtk::TreePath path;
2022-05-11 03:47:12 +02:00
path.push_back(Cheat.group.size() - 1);
selection->select(path);
2018-06-06 02:17:17 +02:00
auto adj = get_object<Gtk::ScrolledWindow>("cheat_scrolledwindow")->get_vadjustment();
adj->set_value(adj->get_upper());
2010-09-25 17:46:12 +02:00
}
void Snes9xCheats::remove_code()
2010-09-25 17:46:12 +02:00
{
int index = get_selected_index();
2010-09-25 17:46:12 +02:00
if (index < 0)
return;
enable_dnd(false);
Gtk::TreePath path;
path.push_back(index);
2018-04-28 03:35:20 +02:00
auto iter = store->get_iter(path);
store->erase(iter);
enable_dnd(true);
S9xDeleteCheatGroup(index);
2018-04-28 03:35:20 +02:00
}
void Snes9xCheats::delete_all_cheats()
2018-04-28 03:35:20 +02:00
{
enable_dnd(false);
S9xDeleteCheats();
store->clear();
enable_dnd(true);
2010-09-25 17:46:12 +02:00
}
void Snes9xCheats::search_database()
2010-09-25 17:46:12 +02:00
{
2018-04-28 03:35:20 +02:00
std::string filename;
int result;
int reason = 0;
2010-09-25 17:46:12 +02:00
2022-05-11 03:47:12 +02:00
for (const auto &dir : { S9xGetDirectory(CHEAT_DIR),
get_config_dir(),
std::string(DATADIR) })
2018-04-28 03:35:20 +02:00
{
2022-05-11 03:47:12 +02:00
filename = dir + "/cheats.bml";
result = S9xImportCheatsFromDatabase(filename);
if (result == 0)
{
refresh_tree_view();
return;
}
2018-04-28 03:35:20 +02:00
2022-05-11 03:47:12 +02:00
if (result < reason)
reason = result;
2018-04-28 03:35:20 +02:00
}
auto dialog = Gtk::MessageDialog(*window.get(), reason == -1 ? _("Couldn't Find Cheats Database") : _("No Matching Game Found"), true);
dialog.set_secondary_text(reason == -1 ? _("The database file <b>cheats.bml</b> was not found. It is normally installed with "
"Snes9x, but you may also place a custom copy in your configuration or cheats directory.")
: _("No matching game was found in the databases. If you are using a non-official "
2022-05-11 03:47:12 +02:00
"translation or modified copy, you may be able to find and manually enter the codes."), true);
dialog.run();
dialog.hide();
2018-04-28 03:35:20 +02:00
}
void Snes9xCheats::sort_cheats()
2018-06-05 02:27:24 +02:00
{
cheat_gather_enabled();
refresh_tree_view();
2018-06-05 02:27:24 +02:00
}
void Snes9xCheats::row_activated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column)
2018-04-28 03:35:20 +02:00
{
int index = get_index_from_path(path);
2018-04-28 03:35:20 +02:00
2022-05-11 03:47:12 +02:00
auto cheat_text = S9xCheatGroupToText(index);
set_entry_text("code_entry", cheat_text);
2022-05-11 03:47:12 +02:00
set_entry_text("description_entry", Cheat.group[index].name);
2018-04-28 03:35:20 +02:00
}
void Snes9xCheats::toggle_code(const Glib::ustring &path)
2018-04-28 03:35:20 +02:00
{
int index = get_index_from_path(path);
auto iter = store->get_iter(path);
bool enabled;
iter->get_value(COLUMN_ENABLED, enabled);
enabled = !enabled;
iter->set_value(COLUMN_ENABLED, enabled);
2018-04-28 03:35:20 +02:00
if (enabled)
S9xEnableCheatGroup(index);
2018-04-28 03:35:20 +02:00
else
S9xDisableCheatGroup(index);
2018-06-06 02:17:17 +02:00
}
void Snes9xCheats::update_code()
2018-06-06 02:17:17 +02:00
{
int index = get_selected_index();
2018-06-06 02:17:17 +02:00
if (index < 0)
return;
std::string code = get_entry_text("code_entry");
std::string description = get_entry_text("description_entry");
if (description.empty())
2018-06-06 02:17:17 +02:00
description = _("No description");
2022-05-11 03:47:12 +02:00
auto parsed_code = S9xCheatValidate(code);
if (parsed_code.empty())
2018-06-06 02:17:17 +02:00
{
display_errorbox(_("Couldn't find any cheat codes in input."));
2018-06-06 02:17:17 +02:00
return;
}
2022-05-11 03:47:12 +02:00
S9xModifyCheatGroup(index, description, parsed_code);
set_entry_text("code_entry", parsed_code);
2018-06-06 02:17:17 +02:00
get_object<Gtk::Entry>("code_entry")->grab_focus();
2018-06-06 02:17:17 +02:00
refresh_tree_view();
2018-06-06 02:17:17 +02:00
}
void Snes9xCheats::disable_all()
2018-06-06 02:17:17 +02:00
{
2022-05-11 03:47:12 +02:00
for (unsigned int i = 0; i < Cheat.group.size(); i++)
2018-06-06 02:17:17 +02:00
{
2022-05-11 03:47:12 +02:00
if (Cheat.group[i].enabled)
S9xDisableCheatGroup(i);
2018-06-06 02:17:17 +02:00
}
refresh_tree_view();
2010-09-25 17:46:12 +02:00
}