windows gtk2 gui: brought across cdrom volume list changes; reworked cdrom combo box

- Reworked the way the CD-ROM drive combo box works a bit; existing
  cdrom drives just show up in the volumes list on load, and you can
  add multiple drives using the combo box with its new Add button
This commit is contained in:
rakslice 2025-01-22 01:00:41 -08:00
parent d29a6f3d03
commit a223adc4e1

View File

@ -28,6 +28,8 @@
#include <shellapi.h>
#include <fstream>
#include "user_strings.h"
#include "version.h"
#include "cdrom.h"
@ -53,6 +55,8 @@ static void create_ethernet_pane(GtkWidget *top);
static void create_memory_pane(GtkWidget *top);
static void create_jit_pane(GtkWidget *top);
static void read_settings(void);
static void add_volume_entry_with_type(const char * filename, bool cdrom);
static void add_volume_entry_guessed(const char * filename);
/*
@ -102,6 +106,7 @@ gchar * tchar_to_g_utf8(const TCHAR * str) {
struct opt_desc {
int label_id;
GtkSignalFunc func;
GtkWidget ** save_ref = NULL;
};
struct combo_desc {
@ -188,6 +193,9 @@ static GtkWidget *make_button_box(GtkWidget *top, int border, const opt_desc *bu
gtk_widget_show(button);
gtk_signal_connect_object(GTK_OBJECT(button), "clicked", buttons->func, NULL);
gtk_box_pack_start(GTK_BOX(bb), button, TRUE, TRUE, 0);
if (buttons->save_ref) {
*(buttons->save_ref) = button;
}
buttons++;
}
return bb;
@ -506,6 +514,7 @@ bool PrefsEditor(void)
{
// Create window
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(win), 640, 480); // a little bigger than the default
gtk_window_set_title(GTK_WINDOW(win), GetString(STR_PREFS_TITLE));
gtk_signal_connect(GTK_OBJECT(win), "delete_event", GTK_SIGNAL_FUNC(window_closed), NULL);
gtk_signal_connect(GTK_OBJECT(win), "destroy", GTK_SIGNAL_FUNC(window_destroyed), NULL);
@ -564,7 +573,8 @@ bool PrefsEditor(void)
static GtkWidget *w_enableextfs, *w_extdrives, *w_cdrom_drive;
static GtkWidget *volume_list;
static int selected_volume;
static GtkListStore *volume_list_model;
static GtkWidget *volume_remove_button;
// Set sensitivity of widgets
static void set_volumes_sensitive(void)
@ -575,10 +585,39 @@ static void set_volumes_sensitive(void)
gtk_widget_set_sensitive(w_cdrom_drive, !no_cdrom);
}
// Volume in list selected
static void cl_selected(GtkWidget *list, int row, int column)
// Volume list selection changed
static void cl_selected(GtkTreeSelection * selection, gpointer user_data) {
if (selection) {
bool have_selection = gtk_tree_selection_get_selected(selection, NULL, NULL);
gtk_widget_set_sensitive(GTK_WIDGET(volume_remove_button), have_selection);
}
}
// Process proposed drop
gboolean volume_list_drag_motion (GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, guint time,
gpointer user_data)
{
selected_volume = row;
GtkTreePath *path;
GtkTreeViewDropPosition pos;
// Don't allow tree-style drops onto, only list-style drops between
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(volume_list), x,y, &path, &pos)) {
switch (pos) {
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(volume_list), path, GTK_TREE_VIEW_DROP_AFTER);
break;
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(volume_list), path, GTK_TREE_VIEW_DROP_BEFORE);
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_AFTER:
// these are ok, no change
break;
}
gdk_drag_status(drag_context, drag_context->suggested_action, time);
return TRUE;
}
return FALSE;
}
// Something dropped on volume list
@ -598,7 +637,36 @@ static void drag_data_received(GtkWidget *list, GdkDragContext *drag_context, gi
gchar * filename = g_filename_from_uri(*uri, NULL, NULL);
if (filename) {
gtk_clist_append(GTK_CLIST(volume_list), &filename);
add_volume_entry_guessed(filename);
// figure out where in the list they dropped
GtkTreePath *path;
GtkTreeViewDropPosition pos;
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(volume_list), x,y, &path, &pos)) {
GtkTreeIter dest_iter;
if (gtk_tree_model_get_iter(GTK_TREE_MODEL(volume_list_model), &dest_iter, path)) {
// Find the item we just added and put it in place
GtkTreeIter last;
GtkTreeIter cur;
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(volume_list_model), &cur)) {
do {
last = cur;
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(volume_list_model), &cur));
}
switch (pos) {
case GTK_TREE_VIEW_DROP_AFTER:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
gtk_list_store_move_after(volume_list_model, &last, &dest_iter);
break;
case GTK_TREE_VIEW_DROP_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
gtk_list_store_move_before(volume_list_model, &last, &dest_iter);
break;
}
}
}
g_free(filename);
}
}
@ -609,7 +677,7 @@ static void drag_data_received(GtkWidget *list, GdkDragContext *drag_context, gi
static void add_volume_ok(GtkWidget *button, file_req_assoc *assoc)
{
gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
gtk_clist_append(GTK_CLIST(volume_list), &file);
add_volume_entry_guessed(file);
gtk_widget_destroy(assoc->req);
delete assoc;
}
@ -624,9 +692,14 @@ static void create_volume_ok(GtkWidget *button, file_req_assoc *assoc)
int fd = _open(file, _O_WRONLY | _O_CREAT | _O_BINARY | _O_TRUNC, _S_IREAD | _S_IWRITE);
if (fd >= 0) {
bool created_ok = false;
if (_chsize(fd, size) == 0)
gtk_clist_append(GTK_CLIST(volume_list), &file);
created_ok = true;
_close(fd);
if (created_ok) {
// A created empty volume is always a new disk
add_volume_entry_with_type(file, false);
}
}
gtk_widget_destroy(GTK_WIDGET(assoc->req));
delete assoc;
@ -669,7 +742,13 @@ static void cb_create_volume(...)
// "Remove Volume" button clicked
static void cb_remove_volume(...)
{
gtk_clist_remove(GTK_CLIST(volume_list), selected_volume);
GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(volume_list));
if (sel) {
GtkTreeIter row;
if (gtk_tree_selection_get_selected(sel, NULL, &row)) {
gtk_list_store_remove(volume_list_model, &row);
}
}
}
// "Boot From" selected
@ -711,64 +790,231 @@ static void tb_pollmedia(GtkWidget *widget)
PrefsReplaceBool("pollmedia", GTK_TOGGLE_BUTTON(widget)->active);
}
// Data source for the volumes list
enum {
VOLUME_LIST_FILENAME = 0,
VOLUME_LIST_CDROM,
VOLUME_LIST_SIZE_DESC,
NUM_VOLUME_LIST_FIELDS
};
static void init_volume_model() {
volume_list_model = gtk_list_store_new(NUM_VOLUME_LIST_FIELDS,
G_TYPE_STRING, // filename
G_TYPE_BOOLEAN, // is CD-ROM
G_TYPE_STRING // size desc
);
}
static void toggle_cdrom_val(GtkTreeIter *row) {
gboolean cdrom;
gtk_tree_model_get(GTK_TREE_MODEL(volume_list_model), row,
VOLUME_LIST_CDROM, &cdrom,
-1);
cdrom = !cdrom;
gtk_list_store_set(volume_list_model, row,
VOLUME_LIST_CDROM, cdrom,
-1);
}
// Read settings from widgets and set preferences
static void read_volumes_settings(void)
{
while (PrefsFindString("disk"))
PrefsRemoveItem("disk");
while (PrefsFindString("cdrom"))
PrefsRemoveItem("cdrom");
for (int i=0; i<GTK_CLIST(volume_list)->rows; i++) {
char *str;
gtk_clist_get_text(GTK_CLIST(volume_list), i, 0, &str);
PrefsAddString("disk", str);
GtkTreeModel * m = GTK_TREE_MODEL(volume_list_model);
GtkTreeIter row;
if (gtk_tree_model_get_iter_first(m, &row)) {
do {
GValue filename = G_VALUE_INIT, is_cdrom = G_VALUE_INIT;
gtk_tree_model_get_value(m, &row, VOLUME_LIST_FILENAME, &filename);
gtk_tree_model_get_value(m, &row, VOLUME_LIST_CDROM, &is_cdrom);
//D(bug("handling %d: %s\n", g_value_get_boolean(&is_cdrom), g_value_get_string(&filename)));
const char * pref_name = g_value_get_boolean(&is_cdrom) ? "cdrom": "disk";
PrefsAddString(pref_name, g_value_get_string(&filename));
g_value_unset(&filename);
g_value_unset(&is_cdrom);
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(volume_list_model), &row));
}
PrefsReplaceString("extdrives", get_file_entry_path(w_extdrives));
// Add one more cdrom entry if present in the combo
const char *str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(w_cdrom_drive)->entry));
if (str && strlen(str))
PrefsReplaceString("cdrom", str);
else
PrefsRemoveItem("cdrom");
PrefsReplaceString("extdrives", get_file_entry_path(w_extdrives));
PrefsAddString("cdrom", str);
}
// Gets the size of the volume as a pretty string
static const char* get_file_size_str (const char * filename)
{
if (strlen(filename) == 3 && filename[1] == ':' && filename[2] == '\\') {
// e.g. real CD-ROM drive
return "";
}
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
if (in.is_open()) {
uint64_t size = in.tellg();
in.close();
char *sizestr = g_format_size_full(size, G_FORMAT_SIZE_IEC_UNITS);
return sizestr;
}
else
{
return "Not Found";
}
}
// Add a volume file as the given type
static void add_volume_entry_with_type(const char * filename, bool cdrom) {
GtkTreeIter row;
gtk_list_store_append(GTK_LIST_STORE(volume_list_model), &row);
// set the values for the new row
gtk_list_store_set(GTK_LIST_STORE(volume_list_model), &row,
VOLUME_LIST_FILENAME, filename,
VOLUME_LIST_CDROM, cdrom,
VOLUME_LIST_SIZE_DESC, get_file_size_str(filename),
-1);
}
static bool has_file_ext (const char * str, const char *ext)
{
char *file_ext = g_utf8_strrchr(str, 255, '.');
if (!file_ext)
return 0;
return (g_strcmp0(file_ext, ext) == 0);
}
static bool guess_if_file_is_cdrom(const char * volume) {
return has_file_ext(volume, ".iso") ||
#ifdef BINCUE
has_file_ext(volume, ".cue") ||
#endif
has_file_ext(volume, ".toast");
}
// Add a volume file and guess the type
static void add_volume_entry_guessed(const char * filename) {
add_volume_entry_with_type(filename, guess_if_file_is_cdrom(filename));
}
// CD-ROM checkbox changed
static void cb_cdrom (GtkCellRendererToggle *cell, char *path_str, gpointer data)
{
GtkTreeIter iter;
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
if (gtk_tree_model_get_iter (GTK_TREE_MODEL(volume_list_model), &iter, path)) {
toggle_cdrom_val(&iter);
}
gtk_tree_path_free (path);
}
static void cb_cdrom_add(...) {
const char *str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(w_cdrom_drive)->entry));
if (str && strcmp(str, "") != 0) {
add_volume_entry_with_type(str, true);
gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(w_cdrom_drive)->entry), "");
}
}
// Create "Volumes" pane
static void create_volumes_pane(GtkWidget *top)
{
GtkWidget *box, *scroll, *menu;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
box = make_pane(top, STR_VOLUMES_PANE_TITLE);
scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_widget_show(scroll);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
volume_list = gtk_clist_new(1);
init_volume_model();
volume_list = gtk_tree_view_new();
gtk_tree_view_set_model(GTK_TREE_VIEW(volume_list), GTK_TREE_MODEL(volume_list_model));
gtk_widget_show(volume_list);
gtk_clist_set_selection_mode(GTK_CLIST(volume_list), GTK_SELECTION_SINGLE);
gtk_clist_set_shadow_type(GTK_CLIST(volume_list), GTK_SHADOW_NONE);
gtk_clist_set_reorderable(GTK_CLIST(volume_list), true);
gtk_signal_connect(GTK_OBJECT(volume_list), "select_row", GTK_SIGNAL_FUNC(cl_selected), NULL);
gtk_tree_view_set_reorderable(GTK_TREE_VIEW(volume_list), true);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, GetString(STR_VOL_HEADING_LOCATION));
gtk_tree_view_column_set_expand(column, true);
gtk_tree_view_append_column(GTK_TREE_VIEW(volume_list), column);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
// connect tree column to model field
gtk_tree_view_column_add_attribute(column, renderer, "text", VOLUME_LIST_FILENAME);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, GetString(STR_VOL_HEADING_CDROM));
gtk_tree_view_append_column(GTK_TREE_VIEW(volume_list), column);
renderer = gtk_cell_renderer_toggle_new();
g_signal_connect (renderer, "toggled",
G_CALLBACK (cb_cdrom), NULL);
gtk_tree_view_column_set_alignment(column, 0.5);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
// connect tree column to model field
gtk_tree_view_column_add_attribute(column, renderer, "active", VOLUME_LIST_CDROM);
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, GetString(STR_VOL_HEADING_SIZE));
gtk_tree_view_append_column(GTK_TREE_VIEW(volume_list), column);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
// connect tree column to model field
gtk_tree_view_column_add_attribute(column, renderer, "text", VOLUME_LIST_SIZE_DESC);
GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(volume_list));
g_signal_connect(sel, "changed", G_CALLBACK(cl_selected), NULL);
gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
// also support volume files dragged onto the list from outside
gtk_drag_dest_add_uri_targets(volume_list);
// add a drop handler to get dropped files; don't supersede the drop handler for reordering
gtk_signal_connect_after(GTK_OBJECT(volume_list), "drag_data_received", GTK_SIGNAL_FUNC(drag_data_received), NULL);
// process proposed drops to limit drop locations
gtk_signal_connect(GTK_OBJECT(volume_list), "drag-motion", GTK_SIGNAL_FUNC(volume_list_drag_motion), NULL);
char *str;
int32 index = 0;
while ((str = const_cast<char *>(PrefsFindString("disk", index++))) != NULL)
gtk_clist_append(GTK_CLIST(volume_list), &str);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), volume_list);
int32 index;
const char * types[] = {"disk", "cdrom", NULL};
for (const char ** type = types; *type != NULL; type++) {
index = 0;
while ((str = const_cast<char *>(PrefsFindString(*type, index))) != NULL) {
bool is_cdrom = strcmp(*type, "cdrom") == 0;
add_volume_entry_with_type(str, is_cdrom);
index++;
}
}
gtk_container_add(GTK_CONTAINER(scroll), volume_list);
gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0);
selected_volume = 0;
static const opt_desc buttons[] = {
{STR_ADD_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_add_volume)},
{STR_CREATE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_create_volume)},
{STR_REMOVE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_remove_volume)},
{STR_REMOVE_VOLUME_BUTTON, G_CALLBACK(cb_remove_volume), &volume_remove_button},
{0, NULL},
};
make_button_box(box, 0, buttons);
gtk_widget_set_sensitive(volume_remove_button, FALSE);
make_separator(box);
static const opt_desc options[] = {
@ -786,11 +1032,17 @@ static void create_volumes_pane(GtkWidget *top)
make_checkbox(box, STR_NOCDROM_CTRL, "nocdrom", GTK_SIGNAL_FUNC(tb_nocdrom));
GList *glist = add_cdrom_names();
str = const_cast<char *>(PrefsFindString("cdrom"));
if (str == NULL)
//str = const_cast<char *>(PrefsFindString("cdrom"));
//if (str == NULL)
str = "";
w_cdrom_drive = make_combobox(box, STR_CDROM_DRIVE_CTRL, str, glist);
GtkWidget * cdrom_hbox = gtk_widget_get_parent(w_cdrom_drive);
GtkWidget * cdrom_add_button = gtk_button_new_with_label("Add");
gtk_widget_show(cdrom_add_button);
gtk_signal_connect_object(GTK_OBJECT(cdrom_add_button), "clicked", GTK_SIGNAL_FUNC(cb_cdrom_add), NULL);
gtk_box_pack_start(GTK_BOX(cdrom_hbox), cdrom_add_button, FALSE, TRUE, 0);
make_checkbox(box, STR_POLLMEDIA_CTRL, "pollmedia", GTK_SIGNAL_FUNC(tb_pollmedia));
make_separator(box);