mupen64plus-oldsvn/main/gui_gtk/cheatdialog.c
James Hood ef14c2cabd Added cheat system support such that if a cheat is disabled during gameplay, the original memory values before the cheat was applied will be written back to memory to attempt to reverse the cheat.
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.
2008-04-24 07:40:31 +00:00

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);
}