/* * TilEm II * * Copyright (c) 2010-2011 Thibault Duponchelle * Copyright (c) 2010-2011 Benjamin Moody * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "gui.h" #include "emucore.h" #include "filedlg.h" #include "ti81prg.h" /* Send a series of files */ static void send_files(TilemCalcEmulator *emu, char **filenames, int *slots) { int i; for (i = 0; filenames[i]; i++) { tilem_link_send_file(emu, filenames[i], slots ? slots[i] : -1, (i == 0), (filenames[i + 1] == NULL)); /* FIXME: macros should record slot numbers */ if (emu->isMacroRecording) tilem_macro_add_action(emu->macro, 1, filenames[i]); } } static int string_to_slot(const char *str) { if (!g_ascii_strncasecmp(str, "prgm", 4)) str += 4; else if (!g_ascii_strncasecmp(str, "ti81_", 5)) str += 5; else return TI81_SLOT_AUTO; if (g_ascii_isdigit(str[0]) && !g_ascii_isalnum(str[1])) return TI81_SLOT_0 + str[0] - '0'; else if (g_ascii_isalpha(str[0]) && !g_ascii_isalnum(str[1])) return TI81_SLOT_A + g_ascii_toupper(str[0]) - 'A'; else if (str[0] == '@' || !g_ascii_strncasecmp(str, "theta", 5) || !strncmp(str, "\316\270", 2) || !strncmp(str, "\316\230", 2)) return TI81_SLOT_THETA; else return TI81_SLOT_AUTO; } /* Guess program slot for a filename */ static int guess_slot(const char *filename) { char *base; int slot; base = g_filename_display_basename(filename); slot = string_to_slot(base); g_free(base); return slot; } static int display_index_to_slot(int i) { if (i < 9) return i + 1; else if (i == 9) return 0; else return i; } struct slotdialog { int nfiles; char **filenames; int *slots; TI81ProgInfo info[TI81_SLOT_MAX + 1]; GtkTreeModel *prgm_model; GtkTreeModel *slot_model; }; static void slot_edited(G_GNUC_UNUSED GtkCellRendererText *cell, gchar *pathstr, gchar *text, gpointer data) { struct slotdialog *slotdlg = data; GtkTreeIter iter; int n, slot; char *end; slot = string_to_slot(text); if (slot < 0) return; n = strtol(pathstr, &end, 10); gtk_tree_model_iter_nth_child(slotdlg->prgm_model, &iter, NULL, n); gtk_list_store_set(GTK_LIST_STORE(slotdlg->prgm_model), &iter, 1, text, -1); slotdlg->slots[n] = slot; } /* Prompt user to assign program slots to filenames */ static void prompt_program_slots(TilemCalcEmulator *emu, struct slotdialog *slotdlg) { GtkWidget *parent, *dlg, *vbox, *vbox2, *sw, *tv, *lbl; GtkListStore *prgmstore, *slotstore; GtkTreeIter iter; GtkCellRenderer *cell; GtkTreeViewColumn *col; int i, j, slot; int used[TI81_SLOT_MAX + 1]; char *slotstr, *namestr; char *slotlabel[TI81_SLOT_MAX + 1]; if (emu->ewin) parent = emu->ewin->window; else parent = NULL; /* Generate list of existing programs */ slotstore = gtk_list_store_new(1, G_TYPE_STRING); slotdlg->slot_model = GTK_TREE_MODEL(slotstore); for (i = 0; i <= TI81_SLOT_MAX; i++) { slot = display_index_to_slot(i); slotstr = ti81_program_slot_to_string(slot); namestr = ti81_program_name_to_string(slotdlg->info[slot].name); if (slotdlg->info[slot].size == 0) { slotlabel[slot] = g_strdup(slotstr); used[slot] = 0; } else if (namestr && namestr[0]) { slotlabel[slot] = g_strdup_printf("%s (in use: %s)", slotstr, namestr); used[slot] = 1; } else { slotlabel[slot] = g_strdup_printf("%s (in use)", slotstr); used[slot] = 1; } gtk_list_store_append(slotstore, &iter); gtk_list_store_set(slotstore, &iter, 0, slotlabel[slot], -1); g_free(slotstr); g_free(namestr); } /* Assign default slots to files */ for (i = 0; i < slotdlg->nfiles; i++) { slot = guess_slot(slotdlg->filenames[i]); if (slotdlg->slots[i] < 0) slotdlg->slots[i] = slot; if (slot >= 0) used[slot] = 1; } for (i = 0; i < slotdlg->nfiles; i++) { if (slotdlg->slots[i] < 0) { for (j = 0; j <= TI81_SLOT_MAX; j++) { slot = display_index_to_slot(j); if (!used[slot]) { slotdlg->slots[i] = slot; used[slot] = 1; break; } } } if (slotdlg->slots[i] < 0) slotdlg->slots[i] = TI81_SLOT_1; } /* Generate list of filenames and assigned slots */ prgmstore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); slotdlg->prgm_model = GTK_TREE_MODEL(prgmstore); for (i = 0; i < slotdlg->nfiles; i++) { namestr = g_filename_display_basename(slotdlg->filenames[i]); slot = slotdlg->slots[i]; gtk_list_store_append(prgmstore, &iter); gtk_list_store_set(prgmstore, &iter, 0, namestr, 1, slotlabel[slot], -1); g_free(namestr); } for (i = 0; i <= TI81_SLOT_MAX; i++) g_free(slotlabel[i]); /* Create tree view */ tv = gtk_tree_view_new_with_model(slotdlg->prgm_model); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv), TRUE); cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes ("File", cell, "text", 0, NULL); gtk_tree_view_column_set_expand(col, TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col); cell = gtk_cell_renderer_combo_new(); g_object_set(cell, "model", slotstore, "text-column", 0, "editable", TRUE, "has-entry", FALSE, NULL); col = gtk_tree_view_column_new_with_attributes ("Slot", cell, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col); g_signal_connect(cell, "edited", G_CALLBACK(slot_edited), slotdlg); /* Create dialog */ dlg = gtk_dialog_new_with_buttons("Select Program Slots", GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK); gtk_window_set_default_size(GTK_WINDOW(dlg), -1, 250); sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(sw), tv); vbox = gtk_vbox_new(FALSE, 6); gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); lbl = gtk_label_new("Select a slot where each program should be" " loaded. If a program slot is already in use," " its contents will be overwritten."); gtk_misc_set_alignment(GTK_MISC(lbl), 0.0, 0.0); gtk_label_set_line_wrap(GTK_LABEL(lbl), TRUE); gtk_label_set_width_chars(GTK_LABEL(lbl), 45); gtk_box_pack_start(GTK_BOX(vbox), lbl, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); gtk_widget_show_all(vbox); vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0); if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) send_files(emu, slotdlg->filenames, slotdlg->slots); gtk_widget_destroy(dlg); } /* Check status of existing programs */ static gboolean check_prog_slots_main(TilemCalcEmulator *emu, gpointer data) { struct slotdialog *slotdlg = data; int i; tilem_em_wake_up(emu, TRUE); for (i = 0; i <= TI81_SLOT_MAX; i++) ti81_get_program_info(emu->calc, i, &slotdlg->info[i]); return TRUE; } static void check_prog_slots_finished(TilemCalcEmulator *emu, gpointer data, gboolean cancelled) { struct slotdialog *slotdlg = data; if (!cancelled) prompt_program_slots(emu, slotdlg); g_free(slotdlg->slots); g_strfreev(slotdlg->filenames); g_slice_free(struct slotdialog, slotdlg); } #define PAT_TI81 "*.prg" #define PAT_TI73 "*.73?" #define PAT_TI73_NUM "*.73n;*.73l;*.73m;*.73i" #define PAT_TI82 "*.82?" #define PAT_TI82_NUM "*.82n;*.82l;*.82m;*.82i" #define PAT_TI82_TEXT "*.82s;*.82y;*.82p" #define PAT_TI83 "*.83?" #define PAT_TI83_NUM "*.83n;*.83l;*.83m;*.83i" #define PAT_TI83_TEXT "*.83s;*.83y;*.83p" #define PAT_TI83P "*.8x?;*.8xgrp" #define PAT_TI83P_NUM "*.8xn;*.8xl;*.8xm;*.8xi" #define PAT_TI83P_TEXT "*.8xs;*.8xy;*.8xp" #define PAT_TI85 "*.85?" #define PAT_TI86 "*.86?" #define PAT_TIG "*.tig" #define FLT_TI81 "TI-81 programs", PAT_TI81 #define FLT_TI73 "TI-73 files", PAT_TI73 #define FLT_TI82 "TI-82 files", PAT_TI82 #define FLT_TI83 "TI-83 files", PAT_TI83 #define FLT_TI83P "TI-83 Plus files", PAT_TI83P #define FLT_TI85 "TI-85 files", PAT_TI85 #define FLT_TI86 "TI-86 files", PAT_TI86 #define FLT_TIG "TIGroup files", PAT_TIG #define FLT_ALL "All files", "*" #define DESC_COMPAT "All compatible files" #define FLT_TI73_COMPAT DESC_COMPAT, (PAT_TI73 ";" PAT_TIG ";" \ PAT_TI82_NUM ";" \ PAT_TI83_NUM ";" \ PAT_TI83P_NUM) #define FLT_TI82_COMPAT DESC_COMPAT, (PAT_TI82 ";" PAT_TIG ";" \ PAT_TI83_TEXT ";" PAT_TI83_NUM ";" \ PAT_TI83P_TEXT ";" PAT_TI83P_NUM ";" \ PAT_TI73_NUM) #define FLT_TI83_COMPAT DESC_COMPAT, (PAT_TI83 ";" PAT_TIG ";" \ PAT_TI82_TEXT ";" PAT_TI82_NUM ";" \ PAT_TI83P_TEXT ";" PAT_TI83P_NUM ";" \ PAT_TI73_NUM) #define FLT_TI83P_COMPAT DESC_COMPAT, (PAT_TI83P ";" PAT_TIG ";" \ PAT_TI82_TEXT ";" PAT_TI82_NUM ";" \ PAT_TI83_TEXT ";" PAT_TI83_NUM ";" \ PAT_TI73_NUM) #define FLT_TI8586_COMPAT DESC_COMPAT, (PAT_TI85 ";" PAT_TI86 ";" PAT_TIG) static char ** prompt_link_files(const char *title, GtkWindow *parent, const char *dir, int model) { switch (model) { case TILEM_CALC_TI73: return prompt_open_files(title, parent, dir, FLT_TI73_COMPAT, FLT_TI73, FLT_TI82, FLT_TI83, FLT_TI83P, FLT_TIG, FLT_ALL, NULL); case TILEM_CALC_TI81: return prompt_open_files(title, parent, dir, FLT_TI81, FLT_ALL, NULL); case TILEM_CALC_TI82: return prompt_open_files(title, parent, dir, FLT_TI82_COMPAT, FLT_TI73, FLT_TI82, FLT_TI83, FLT_TI83P, FLT_TIG, FLT_ALL, NULL); case TILEM_CALC_TI83: case TILEM_CALC_TI76: return prompt_open_files(title, parent, dir, FLT_TI83_COMPAT, FLT_TI73, FLT_TI82, FLT_TI83, FLT_TI83P, FLT_TIG, FLT_ALL, NULL); case TILEM_CALC_TI83P: case TILEM_CALC_TI83P_SE: case TILEM_CALC_TI84P: case TILEM_CALC_TI84P_SE: case TILEM_CALC_TI84P_NSPIRE: return prompt_open_files(title, parent, dir, FLT_TI83P_COMPAT, FLT_TI73, FLT_TI82, FLT_TI83, FLT_TI83P, FLT_TIG, FLT_ALL, NULL); case TILEM_CALC_TI85: case TILEM_CALC_TI86: return prompt_open_files(title, parent, dir, FLT_TI8586_COMPAT, FLT_TI85, FLT_TI86, FLT_TIG, FLT_ALL, NULL); default: return prompt_open_files(title, parent, dir, FLT_ALL, NULL); } } /* Load a list of files through the GUI. The list of filenames must end with NULL. */ void load_files(TilemEmulatorWindow *ewin, char **filenames) { struct slotdialog *slotdlg; int i; g_return_if_fail(ewin->emu->calc != NULL); if (ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) { slotdlg = g_slice_new0(struct slotdialog); slotdlg->filenames = g_strdupv(filenames); slotdlg->nfiles = g_strv_length(filenames); slotdlg->slots = g_new(int, slotdlg->nfiles); for (i = 0; i < slotdlg->nfiles; i++) slotdlg->slots[i] = TI81_SLOT_AUTO; tilem_calc_emulator_begin(ewin->emu, &check_prog_slots_main, &check_prog_slots_finished, slotdlg); } else { send_files(ewin->emu, filenames, NULL); } } static int get_cmdline_slot(const char *str, const char **name) { char *e; int n; n = strtol(str, &e, 10); if (*e == '=') { *name = e + 1; return n; } if (g_ascii_isalpha(str[0]) && str[1] == '=') { *name = str + 2; return TI81_SLOT_A + g_ascii_toupper(str[0]) - 'A'; } if (str[0] == '@' && str[1] == '=') { *name = str + 2; return TI81_SLOT_THETA; } if (!g_ascii_strncasecmp(str, "theta=", 6)) { *name = str + 6; return TI81_SLOT_THETA; } *name = str; return TI81_SLOT_AUTO; } /* Load a list of files from the command line. Filenames may begin with an optional slot designation. */ void load_files_cmdline(TilemEmulatorWindow *ewin, char **filenames) { struct slotdialog *slotdlg; int i; gboolean need_prompt = FALSE; const char *name; g_return_if_fail(ewin->emu->calc != NULL); slotdlg = g_slice_new0(struct slotdialog); slotdlg->nfiles = g_strv_length(filenames); slotdlg->slots = g_new(int, slotdlg->nfiles); slotdlg->filenames = g_new0(char *, slotdlg->nfiles + 1); for (i = 0; i < slotdlg->nfiles; i++) { slotdlg->slots[i] = get_cmdline_slot(filenames[i], &name); slotdlg->filenames[i] = g_strdup(name); if (slotdlg->slots[i] < 0) need_prompt = TRUE; } if (need_prompt && ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) { tilem_calc_emulator_begin(ewin->emu, &check_prog_slots_main, &check_prog_slots_finished, slotdlg); } else { send_files(ewin->emu, slotdlg->filenames, slotdlg->slots); g_free(slotdlg->slots); g_strfreev(slotdlg->filenames); g_slice_free(struct slotdialog, slotdlg); } } /* Prompt user to load a file */ void load_file_dialog(TilemEmulatorWindow *ewin) { char **filenames, *dir; tilem_config_get("upload", "sendfile_recentdir/f", &dir, NULL); filenames = prompt_link_files("Send File", GTK_WINDOW(ewin->window), dir, ewin->emu->calc->hw.model_id); g_free(dir); if (!filenames || !filenames[0]) { g_free(filenames); return; } dir = g_path_get_dirname(filenames[0]); tilem_config_set("upload", "sendfile_recentdir/f", dir, NULL); g_free(dir); load_files(ewin, filenames); g_strfreev(filenames); }