mirror of
https://github.com/mupen64plus/mupen64plus-oldsvn.git
synced 2025-04-02 10:52:35 -04:00
For example, before this fix, if you enabled the "Mario flies without wingcap" cheat during gameplay, it would work, but if you then disabled it during gameplay, Mario would still be able to fly. Now it correctly restores the old memory values so Mario can't fly anymore after you disable the cheat. Note, because cheats are basically writing to a random area of memory, this function may not always restore the previous state depending on the way that area of memory is used by the game.
783 lines
26 KiB
C
783 lines
26 KiB
C
/**
|
|
* Mupen64 - cheatdialog.c
|
|
* Copyright (C) 2008 ebenblues
|
|
*
|
|
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/
|
|
* or modify it under the terms of the GNU General Public Li-
|
|
* cence as published by the Free Software Foundation; either
|
|
* version 2 of the Licence, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be use-
|
|
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
|
|
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public Licence for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* Licence along with this program; if not, write to the Free
|
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
|
|
* USA.
|
|
*
|
|
**/
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "main_gtk.h"
|
|
#include "cheatdialog.h"
|
|
#include "../cheat.h"
|
|
#include "../translate.h"
|
|
#include "../util.h"
|
|
|
|
/** globals **/
|
|
static GtkWidget *g_CheatView = NULL;
|
|
|
|
// column numbering for cheat code list store
|
|
enum {
|
|
ADDRESS_COLUMN,
|
|
VALUE_COLUMN,
|
|
CHEAT_CODE_COLUMN,
|
|
NUM_COLUMNS
|
|
};
|
|
|
|
/** private functions **/
|
|
|
|
// user clicked ok button
|
|
static void cb_response(GtkWidget *widget, gint response, gpointer data)
|
|
{
|
|
cheat_write_config();
|
|
gtk_widget_destroy(widget);
|
|
}
|
|
|
|
// user clicked button to add a new cheat code
|
|
static void cb_addCode(GtkWidget *button, GtkTreeView *treeview)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreeSelection *selection;
|
|
|
|
cheat_t *cheat;
|
|
cheat_code_t *cheatcode;
|
|
|
|
// first, get cheat that these codes belong to
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_CheatView));
|
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
|
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &cheat,
|
|
-1);
|
|
|
|
// create new cheat code
|
|
cheatcode = cheat_new_cheat_code(cheat);
|
|
|
|
// add new cheat code row to list
|
|
model = gtk_tree_view_get_model(treeview);
|
|
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
|
|
gtk_list_store_set(GTK_LIST_STORE(model), &iter,
|
|
ADDRESS_COLUMN, cheatcode->address,
|
|
VALUE_COLUMN, cheatcode->value,
|
|
CHEAT_CODE_COLUMN, cheatcode,
|
|
-1);
|
|
|
|
// select new row
|
|
selection = gtk_tree_view_get_selection(treeview);
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
|
}
|
|
|
|
// user clicked button to remove a cheat code
|
|
static void cb_removeCode(GtkWidget *button, GtkTreeView *treeview)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreeSelection *selection;
|
|
|
|
cheat_t *cheat;
|
|
cheat_code_t *cheatcode;
|
|
|
|
// first, get cheat that these codes belong to
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_CheatView));
|
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
|
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &cheat,
|
|
-1);
|
|
|
|
// get cheat code pointer, then delete row from tree model
|
|
selection = gtk_tree_view_get_selection(treeview);
|
|
if(gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
gtk_tree_model_get(model, &iter,
|
|
CHEAT_CODE_COLUMN, &cheatcode,
|
|
-1);
|
|
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
|
|
|
|
// delete cheat code structure
|
|
cheat_delete_cheat_code(cheat, cheatcode);
|
|
}
|
|
}
|
|
|
|
// user edited cheat code (address or value)
|
|
static void cb_editCode(GtkCellRenderer *cell, gchar *path_string, gchar *new_text, GtkTreeView *view)
|
|
{
|
|
GtkTreeModel *model = gtk_tree_view_get_model(view);
|
|
GtkTreeIter iter;
|
|
guint col_num;
|
|
|
|
cheat_code_t *cheatcode;
|
|
|
|
// get column and row information
|
|
gtk_tree_model_get_iter_from_string(model, &iter, path_string);
|
|
col_num = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(cell), "col_num"));
|
|
|
|
// get pointer to cheatcode struct
|
|
gtk_tree_model_get(model, &iter,
|
|
CHEAT_CODE_COLUMN, &cheatcode,
|
|
-1);
|
|
|
|
// user edited address cell
|
|
if(col_num == ADDRESS_COLUMN)
|
|
{
|
|
cheatcode->address = strtoumax(new_text, NULL, 16);
|
|
gtk_list_store_set(GTK_LIST_STORE(model), &iter,
|
|
ADDRESS_COLUMN, cheatcode->address,
|
|
-1);
|
|
}
|
|
// user edited value cell
|
|
else
|
|
{
|
|
cheatcode->value = strtoumax(new_text, NULL, 16);
|
|
gtk_list_store_set(GTK_LIST_STORE(model), &iter,
|
|
VALUE_COLUMN, cheatcode->value,
|
|
-1);
|
|
}
|
|
}
|
|
|
|
|
|
// returns tree view of all user cheats
|
|
static GtkWidget *create_cheat_treeview(void)
|
|
{
|
|
GtkWidget *view;
|
|
GtkCellRenderer *rend;
|
|
GtkTreeViewColumn *col;
|
|
GtkTreeStore *model;
|
|
GtkTreeIter romIter,
|
|
cheatIter;
|
|
|
|
list_node_t *romnode,
|
|
*cheatnode;
|
|
rom_cheats_t *romcheat;
|
|
cheat_t *cheat;
|
|
|
|
// model stores either a rom name or cheat name, and a pointer to either the rom_cheat_t or cheat_t struct
|
|
model = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
|
|
|
|
// populate model
|
|
list_foreach(g_Cheats, romnode)
|
|
{
|
|
romcheat = (rom_cheats_t *)romnode->data;
|
|
|
|
// add rom name to tree
|
|
gtk_tree_store_append(model, &romIter, NULL);
|
|
gtk_tree_store_set(model, &romIter,
|
|
0, romcheat->rom_name,
|
|
1, romcheat,
|
|
-1);
|
|
|
|
// add all cheats for this rom as children in the tree
|
|
list_foreach(romcheat->cheats, cheatnode)
|
|
{
|
|
cheat = (cheat_t *)cheatnode->data;
|
|
|
|
gtk_tree_store_append(model, &cheatIter, &romIter);
|
|
gtk_tree_store_set(model, &cheatIter,
|
|
0, cheat->name,
|
|
1, cheat,
|
|
-1);
|
|
}
|
|
}
|
|
|
|
// create tree view
|
|
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
|
|
|
|
// allows model memory to be freed when tree view is destroyed.
|
|
g_object_unref(model);
|
|
|
|
// only add the string column to the view, the struct pointer is hidden data
|
|
rend = gtk_cell_renderer_text_new();
|
|
col = gtk_tree_view_column_new();
|
|
gtk_tree_view_column_pack_start(col, rend, TRUE);
|
|
gtk_tree_view_column_add_attribute(col, rend,
|
|
"text", 0);
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
|
|
|
return view;
|
|
}
|
|
|
|
// Adds new rom to cheat structures and tree view, and selects it so user can edit it
|
|
static void cb_newRom(GtkButton *button, GtkTreeView *view)
|
|
{
|
|
GtkTreeModel *model = gtk_tree_view_get_model(view);
|
|
GtkTreeIter iter;
|
|
GtkTreeSelection *selection;
|
|
|
|
rom_cheats_t *romcheat = cheat_new_rom();
|
|
romcheat->rom_name = strdup(tr("New Rom"));
|
|
|
|
// add new rom to tree model
|
|
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
|
|
0, romcheat->rom_name,
|
|
1, romcheat,
|
|
-1);
|
|
|
|
// select new rom
|
|
selection = gtk_tree_view_get_selection(view);
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
|
}
|
|
|
|
// Adds new cheat to cheat structures and tree view, and selects it so user can edit it
|
|
static void cb_newCheat(GtkButton *button, GtkTreeView *view)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter, selectedIter, parentIter, *romIter;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *path;
|
|
|
|
rom_cheats_t *romcheat;
|
|
cheat_t *cheat;
|
|
|
|
selection = gtk_tree_view_get_selection(view);
|
|
|
|
// something should always be selected, but just in case...
|
|
if(gtk_tree_selection_get_selected(selection, &model, &selectedIter))
|
|
{
|
|
|
|
// get pointer to rom iter
|
|
if(gtk_tree_model_iter_parent(model, &parentIter, &selectedIter))
|
|
romIter = &parentIter;
|
|
else
|
|
romIter = &selectedIter;
|
|
|
|
// grab pointer to rom cheat data out of model
|
|
gtk_tree_model_get(model, romIter,
|
|
1, &romcheat,
|
|
-1);
|
|
|
|
// create new cheat
|
|
cheat = cheat_new_cheat(romcheat);
|
|
cheat->name = strdup(tr("New Cheat"));
|
|
|
|
// add new cheat to the tree model
|
|
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, romIter);
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
|
|
0, cheat->name,
|
|
1, cheat,
|
|
-1);
|
|
|
|
// expand rom row if it isn't already
|
|
path = gtk_tree_model_get_path(model, romIter);
|
|
gtk_tree_view_expand_row(view,
|
|
path,
|
|
FALSE);
|
|
gtk_tree_path_free(path);
|
|
|
|
// select new cheat
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
|
}
|
|
}
|
|
|
|
// Remove currently selected rom/cheat from the tree
|
|
static void cb_delete(GtkButton *button, GtkTreeView *view)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter, parentIter;
|
|
GtkTreeSelection *selection;
|
|
GtkTreePath *path;
|
|
|
|
rom_cheats_t *romcheat = NULL;
|
|
cheat_t *cheat = NULL;
|
|
|
|
selection = gtk_tree_view_get_selection(view);
|
|
|
|
// something should always be selected, but just in case...
|
|
if(gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
// check if we're deleting a rom or cheat
|
|
if(gtk_tree_model_iter_parent(model, &parentIter, &iter))
|
|
{
|
|
// selected item is a cheat
|
|
gtk_tree_model_get(model, &parentIter,
|
|
1, &romcheat,
|
|
-1);
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &cheat,
|
|
-1);
|
|
|
|
if(confirm_message(tr("Are you sure you want to delete cheat \"%s\"?"), cheat->name))
|
|
{
|
|
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
|
|
cheat_delete_cheat(romcheat, cheat);
|
|
|
|
// set selection to parent rom
|
|
gtk_tree_selection_select_iter(selection, &parentIter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// selected item is a rom
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &romcheat,
|
|
-1);
|
|
|
|
if(confirm_message(tr("Are you sure you want to delete rom \"%s\" and all of its cheats?"), romcheat->rom_name))
|
|
{
|
|
gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
|
|
cheat_delete_rom(romcheat);
|
|
|
|
// set selection to first rom
|
|
gtk_tree_model_get_iter_first(model, &iter);
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updates tree model and rom_cheats_t structure with new rom name
|
|
static void cb_romNameChanged(GtkWidget *textbox, GtkTreeSelection *selection)
|
|
{
|
|
gchar *text = gtk_editable_get_chars(GTK_EDITABLE(textbox), 0, -1);
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
|
|
rom_cheats_t *romcheat;
|
|
|
|
// if we're here, something should be selected, but just in case...
|
|
if(gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &romcheat,
|
|
-1);
|
|
|
|
// change rom name in rom_cheats_t struct
|
|
if(romcheat->rom_name)
|
|
free(romcheat->rom_name);
|
|
romcheat->rom_name = strdup(text);
|
|
|
|
// change rom name in tree model
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
|
|
0, romcheat->rom_name,
|
|
-1);
|
|
}
|
|
|
|
g_free(text);
|
|
}
|
|
|
|
// Updates rom_cheats_t structure with new rom crc
|
|
static void cb_crcChanged(GtkWidget *textbox, unsigned int *rom_crc)
|
|
{
|
|
gchar *text = gtk_editable_get_chars(GTK_EDITABLE(textbox), 0, -1);
|
|
|
|
*rom_crc = strtoumax(text, NULL, 16);
|
|
|
|
g_free(text);
|
|
}
|
|
|
|
// Returns user form to edit rom info.
|
|
static GtkWidget *edit_rom_info_widget(rom_cheats_t *romcheat, GtkTreeSelection *selection)
|
|
{
|
|
int i;
|
|
char crc[9];
|
|
|
|
GtkWidget *label;
|
|
GtkWidget *table;
|
|
GtkWidget *textbox;
|
|
|
|
table = gtk_table_new(2, 3, FALSE);
|
|
|
|
// Rom name
|
|
label = gtk_label_new(tr("Name:"));
|
|
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 5);
|
|
|
|
textbox = gtk_entry_new();
|
|
if(romcheat->rom_name)
|
|
gtk_editable_insert_text(GTK_EDITABLE(textbox), romcheat->rom_name, strlen(romcheat->rom_name), &i);
|
|
|
|
// connect signal such that as the user changes the Rom name in the textbox, it's updated in the model
|
|
g_signal_connect(textbox, "changed", G_CALLBACK(cb_romNameChanged), selection);
|
|
gtk_table_attach(GTK_TABLE(table), textbox, 1, 3, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
|
|
|
|
// Rom CRC
|
|
label = gtk_label_new(tr("CRC:"));
|
|
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 5);
|
|
|
|
// CRC1 textbox
|
|
textbox = gtk_entry_new();
|
|
gtk_entry_set_max_length(GTK_ENTRY(textbox), 8);
|
|
gtk_widget_set_size_request(textbox, 85, -1);
|
|
snprintf(crc, 9, "%.8x", romcheat->crc1);
|
|
gtk_editable_insert_text(GTK_EDITABLE(textbox), crc, 8, &i);
|
|
|
|
// connect signal such that as the user changes the CRC value in the textbox, it's updated in the rom_cheats_t struct
|
|
g_signal_connect(textbox, "changed", G_CALLBACK(cb_crcChanged), &romcheat->crc1);
|
|
|
|
gtk_table_attach(GTK_TABLE(table), textbox, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
|
|
|
|
// CRC2 textbox
|
|
textbox = gtk_entry_new();
|
|
gtk_entry_set_max_length(GTK_ENTRY(textbox), 8);
|
|
gtk_widget_set_size_request(textbox, 85, -1);
|
|
snprintf(crc, 9, "%.8x", romcheat->crc2);
|
|
gtk_editable_delete_text(GTK_EDITABLE(textbox), 0, -1);
|
|
gtk_editable_insert_text(GTK_EDITABLE(textbox), crc, 8, &i);
|
|
|
|
// connect signal such that as the user changes the CRC value in the textbox, it's updated in the rom_cheats_t struct
|
|
g_signal_connect(textbox, "changed", G_CALLBACK(cb_crcChanged), &romcheat->crc2);
|
|
|
|
gtk_table_attach(GTK_TABLE(table), textbox, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 5, 5);
|
|
|
|
return table;
|
|
}
|
|
|
|
// Updates tree model and cheat_t structure with new cheat name
|
|
static void cb_cheatNameChanged(GtkWidget *textbox, GtkTreeSelection *selection)
|
|
{
|
|
gchar *text = gtk_editable_get_chars(GTK_EDITABLE(textbox), 0, -1);
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
|
|
cheat_t *cheat;
|
|
|
|
// if we're here, something should be selected, but just in case...
|
|
if(gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &cheat,
|
|
-1);
|
|
|
|
// change cheat name in cheat_t struct
|
|
if(cheat->name)
|
|
free(cheat->name);
|
|
cheat->name = strdup(text);
|
|
|
|
// change rom name in tree model
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
|
|
0, cheat->name,
|
|
-1);
|
|
}
|
|
|
|
g_free(text);
|
|
}
|
|
|
|
// user disabled cheat
|
|
static void cb_cheatDisabled(GtkWidget *button, cheat_t *cheat)
|
|
{
|
|
cheat->always_enabled = cheat->enabled = 0;
|
|
}
|
|
|
|
// user enabled cheat for this session only
|
|
static void cb_cheatEnabled(GtkWidget *button, cheat_t *cheat)
|
|
{
|
|
cheat->enabled = 1;
|
|
cheat->always_enabled = 0;
|
|
}
|
|
|
|
// user enabled cheat always
|
|
static void cb_cheatEnabledAlways(GtkWidget *button, cheat_t *cheat)
|
|
{
|
|
cheat->enabled = 0;
|
|
cheat->always_enabled = 1;
|
|
}
|
|
|
|
// function to display cheat codes in hex format
|
|
static void cb_cheat_data_func(GtkTreeViewColumn *col,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gpointer column)
|
|
{
|
|
unsigned int val;
|
|
char buf[20];
|
|
|
|
// get cell value from model
|
|
gtk_tree_model_get(tree_model, iter, column, &val, -1);
|
|
if(column == ADDRESS_COLUMN)
|
|
snprintf(buf, 20, "%.8x", val);
|
|
else
|
|
snprintf(buf, 20, "%.4x", val);
|
|
|
|
// set cell text to value
|
|
g_object_set(G_OBJECT(cell), "text", buf, NULL);
|
|
}
|
|
|
|
// Returns tree view of all cheat codes for the given cheat
|
|
static GtkWidget *create_cheat_code_treeview(cheat_t *cheat)
|
|
{
|
|
GtkWidget *view;
|
|
GtkCellRenderer *rend;
|
|
GtkTreeViewColumn *col;
|
|
GtkListStore *model;
|
|
GtkTreeIter iter;
|
|
|
|
list_node_t *node;
|
|
cheat_code_t *cheatcode;
|
|
|
|
model = gtk_list_store_new(NUM_COLUMNS,
|
|
G_TYPE_UINT, // address column
|
|
G_TYPE_UINT, // value column
|
|
G_TYPE_POINTER); // hidden pointer to cheat_code_t struct
|
|
|
|
// populate model with cheat codes
|
|
list_foreach(cheat->cheat_codes, node)
|
|
{
|
|
cheatcode = (cheat_code_t *)node->data;
|
|
|
|
// add cheat code to list model
|
|
gtk_list_store_append(model, &iter);
|
|
gtk_list_store_set(model, &iter,
|
|
ADDRESS_COLUMN, cheatcode->address,
|
|
VALUE_COLUMN, cheatcode->value,
|
|
CHEAT_CODE_COLUMN, cheatcode,
|
|
-1);
|
|
}
|
|
|
|
// create tree view
|
|
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
|
|
|
|
// allows list memory to be freed when tree view is destroyed
|
|
g_object_unref(model);
|
|
|
|
// address column
|
|
rend = gtk_cell_renderer_text_new();
|
|
// attach column identifier to this renderer
|
|
g_object_set_data(G_OBJECT(rend), "col_num", GUINT_TO_POINTER(ADDRESS_COLUMN));
|
|
// allow user to edit cells
|
|
g_object_set(rend, "editable", TRUE, NULL);
|
|
g_signal_connect(rend, "edited", G_CALLBACK(cb_editCode), GTK_TREE_VIEW(view));
|
|
col = gtk_tree_view_column_new_with_attributes(tr("Address"), rend,
|
|
"text", ADDRESS_COLUMN,
|
|
NULL);
|
|
gtk_tree_view_column_set_cell_data_func(col, rend, cb_cheat_data_func,
|
|
(gpointer)ADDRESS_COLUMN, NULL);
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
|
|
|
// value column
|
|
rend = gtk_cell_renderer_text_new();
|
|
// attach column identifier to this renderer
|
|
g_object_set_data(G_OBJECT(rend), "col_num", GUINT_TO_POINTER(VALUE_COLUMN));
|
|
// allow user to edit cells
|
|
g_object_set(rend, "editable", TRUE, NULL);
|
|
g_signal_connect(rend, "edited", G_CALLBACK(cb_editCode), GTK_TREE_VIEW(view));
|
|
col = gtk_tree_view_column_new_with_attributes(tr("Value"), rend,
|
|
"text", VALUE_COLUMN,
|
|
NULL);
|
|
gtk_tree_view_column_set_cell_data_func(col, rend, cb_cheat_data_func,
|
|
(gpointer)VALUE_COLUMN, NULL);
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
|
|
|
return view;
|
|
}
|
|
|
|
// Returns user form to edit a cheat (name, codes)
|
|
static GtkWidget *edit_cheat_widget(cheat_t *cheat, GtkTreeSelection *selection)
|
|
{
|
|
int i;
|
|
|
|
GtkWidget *table;
|
|
GtkWidget *hbox, *vbox;
|
|
GtkWidget *label;
|
|
GtkWidget *textbox;
|
|
GtkWidget *button;
|
|
GtkWidget *code_treeview;
|
|
GtkWidget *viewport;
|
|
|
|
table = gtk_table_new(5, 2, FALSE);
|
|
|
|
// Cheat name
|
|
label = gtk_label_new(tr("Name:"));
|
|
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 5);
|
|
|
|
textbox = gtk_entry_new();
|
|
if(cheat->name)
|
|
gtk_editable_insert_text(GTK_EDITABLE(textbox), cheat->name, strlen(cheat->name), &i);
|
|
|
|
// connect signal such that as the user changes the cheat name in the textbox, it's updated in the model
|
|
g_signal_connect(textbox, "changed", G_CALLBACK(cb_cheatNameChanged), selection);
|
|
gtk_table_attach(GTK_TABLE(table), textbox, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 5);
|
|
|
|
// Cheat enable
|
|
button = gtk_radio_button_new_with_label(NULL, tr("Disabled"));
|
|
g_signal_connect(button, "toggled", G_CALLBACK(cb_cheatDisabled), cheat);
|
|
gtk_table_attach(GTK_TABLE(table), button, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 5, 0);
|
|
|
|
button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), tr("Enabled (This session only)"));
|
|
g_signal_connect(button, "toggled", G_CALLBACK(cb_cheatEnabled), cheat);
|
|
if(cheat->enabled) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
|
|
gtk_table_attach(GTK_TABLE(table), button, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 5, 0);
|
|
|
|
button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), tr("Enabled (Always)"));
|
|
g_signal_connect(button, "toggled", G_CALLBACK(cb_cheatEnabledAlways), cheat);
|
|
if(cheat->always_enabled) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
|
|
gtk_table_attach(GTK_TABLE(table), button, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 5, 0);
|
|
|
|
// create tree view of cheat codes (address/value pairs)
|
|
hbox = gtk_hbox_new(FALSE, 0);
|
|
gtk_table_attach(GTK_TABLE(table), hbox, 0, 2, 4, 5, GTK_FILL, GTK_FILL, 5, 5);
|
|
|
|
viewport = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport),
|
|
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
|
gtk_widget_set_size_request(viewport, -1, 200);
|
|
gtk_box_pack_start(GTK_BOX(hbox), viewport, FALSE, FALSE, 10);
|
|
|
|
code_treeview = create_cheat_code_treeview(cheat);
|
|
gtk_container_add(GTK_CONTAINER(viewport), code_treeview);
|
|
|
|
// create column of add/remove buttons next to cheat codes
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
|
|
|
|
// add cheat code button
|
|
button = gtk_button_new_from_stock(GTK_STOCK_ADD);
|
|
g_signal_connect(button, "clicked", G_CALLBACK(cb_addCode), code_treeview);
|
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 5);
|
|
|
|
// remove cheat code button
|
|
button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
|
|
g_signal_connect(button, "clicked", G_CALLBACK(cb_removeCode), code_treeview);
|
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 5);
|
|
|
|
return table;
|
|
}
|
|
|
|
// Update contents of the detail frame with the details of the selected rom or cheat
|
|
static void cb_updateFrame(GtkTreeSelection *selection, GtkWidget *frame)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkWidget *frame_contents;
|
|
GtkWidget *label;
|
|
|
|
rom_cheats_t *romcheat;
|
|
cheat_t *cheat;
|
|
|
|
// empty current frame contents
|
|
frame_contents = gtk_bin_get_child(GTK_BIN(frame));
|
|
if(frame_contents)
|
|
gtk_widget_destroy(frame_contents);
|
|
|
|
// if nothing is selected, put a label in the frame saying so
|
|
if(!gtk_tree_selection_get_selected(selection, &model, &iter))
|
|
{
|
|
gtk_frame_set_label(GTK_FRAME(frame), NULL);
|
|
label = gtk_label_new(tr("No cheats found. Click \"New Rom\" to begin."));
|
|
gtk_container_add(GTK_CONTAINER(frame), label);
|
|
|
|
return;
|
|
}
|
|
|
|
// depth of 0 means the selected item is a rom
|
|
if(gtk_tree_store_iter_depth(GTK_TREE_STORE(model), &iter) == 0)
|
|
{
|
|
gtk_frame_set_label(GTK_FRAME(frame), "Edit Rom");
|
|
|
|
// grab pointer to rom cheat data out of model
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &romcheat,
|
|
-1);
|
|
frame_contents = edit_rom_info_widget(romcheat, selection);
|
|
}
|
|
// else, depth must be 1 meaning the selected item is a cheat
|
|
else
|
|
{
|
|
gtk_frame_set_label(GTK_FRAME(frame), "Edit Cheat");
|
|
|
|
// grab pointer to cheat data out of model
|
|
gtk_tree_model_get(model, &iter,
|
|
1, &cheat,
|
|
-1);
|
|
frame_contents = edit_cheat_widget(cheat, selection);
|
|
}
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), frame_contents);
|
|
|
|
// if we're being called to update the frame, display the updated frame contents
|
|
if(GTK_WIDGET_VISIBLE(frame))
|
|
gtk_widget_show_all(frame_contents);
|
|
}
|
|
|
|
/** public functions **/
|
|
|
|
// Create and display cheat dialog
|
|
void cb_cheatDialog(GtkWidget *widget)
|
|
{
|
|
// Local Variables
|
|
GtkWidget *dialog;
|
|
GtkWidget *label;
|
|
GtkWidget *frame;
|
|
GtkWidget *hbox, *vbox;
|
|
GtkWidget *viewport;
|
|
GtkWidget *button;
|
|
|
|
GtkTreeSelection *selection;
|
|
|
|
// Create Dialog Window
|
|
dialog = gtk_dialog_new_with_buttons(tr("Configure Cheats"),
|
|
GTK_WINDOW(g_MainWindow.window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
|
|
gtk_widget_set_size_request(dialog, 500, -1);
|
|
gtk_container_set_border_width( GTK_CONTAINER(dialog), 10 );
|
|
|
|
g_CheatView = create_cheat_treeview();
|
|
|
|
g_signal_connect(dialog, "response",
|
|
G_CALLBACK(cb_response), g_CheatView);
|
|
|
|
// create scrolled window for tree view
|
|
viewport = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
gtk_widget_set_size_request(viewport, 200, 400);
|
|
gtk_container_add(GTK_CONTAINER(viewport), g_CheatView);
|
|
|
|
// add cheat tree view within viewport to left side of dialog box
|
|
hbox = gtk_hbox_new(FALSE, 10);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), viewport, FALSE, FALSE, 0);
|
|
|
|
// right side of dialog
|
|
vbox = gtk_vbox_new(FALSE, 10);
|
|
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 10);
|
|
|
|
// row of add rom, add cheat, remove buttons
|
|
hbox = gtk_hbox_new(FALSE, 10);
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 10);
|
|
|
|
button = gtk_button_new_with_mnemonic(tr("_New Rom"));
|
|
g_signal_connect(button, "clicked", G_CALLBACK(cb_newRom), g_CheatView);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
|
|
|
|
button = gtk_button_new_with_mnemonic(tr("New _Cheat"));
|
|
g_signal_connect(button, "clicked", G_CALLBACK(cb_newCheat), g_CheatView);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
|
|
|
|
button = gtk_button_new_with_mnemonic(tr("_Delete"));
|
|
g_signal_connect(button, "clicked", G_CALLBACK(cb_delete), g_CheatView);
|
|
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
|
|
|
|
// frame containing detail of selected item in tree view (allows user to edit)
|
|
frame = gtk_frame_new(NULL);
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 10);
|
|
|
|
// setup callback such that whenever tree view selection is changed, frame gets updated
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(g_CheatView));
|
|
g_signal_connect(selection, "changed", G_CALLBACK(cb_updateFrame), frame);
|
|
|
|
// manually update the frame the first time
|
|
cb_updateFrame(selection, frame);
|
|
|
|
gtk_widget_show_all(dialog);
|
|
}
|