ti83-sdk/tool/tilem-src/gui/tool.c

434 lines
10 KiB
C

/*
* TilEm II
*
* Copyright (c) 2010-2011 Thibault Duponchelle
* Copyright (c) 2010 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <ticalcs.h>
#include <tilem.h>
#include "gui.h"
/* Create a frame around the given widget, with a boldface label in
the GNOME style */
GtkWidget* new_frame(const gchar* label, GtkWidget* contents)
{
GtkWidget *frame, *align;
char *str;
str = g_strconcat("<b>", label, "</b>", NULL);
frame = gtk_frame_new(str);
g_free(str);
g_object_set(gtk_frame_get_label_widget(GTK_FRAME(frame)),
"use-markup", TRUE, NULL);
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 12, 0);
gtk_widget_show(align);
gtk_container_add(GTK_CONTAINER(frame), align);
gtk_container_add(GTK_CONTAINER(align), contents);
gtk_widget_show(frame);
return frame;
}
/* Get model name (abbreviation) for a TilEm model ID. */
const char * model_to_name(int model)
{
const TilemHardware **models;
int nmodels, i;
tilem_get_supported_hardware(&models, &nmodels);
for (i = 0; i < nmodels; i++)
if (models[i]->model_id == model)
return models[i]->name;
return NULL;
}
/* Convert model name to a model ID. */
int name_to_model(const char *name)
{
char *s;
const TilemHardware **models;
int nmodels, i, j;
s = g_new(char, strlen(name) + 1);
for (i = j = 0; name[i]; i++) {
if (name[i] == '+')
s[j++] = 'p';
else if (name[i] != '-')
s[j++] = g_ascii_tolower(name[i]);
}
s[j] = 0;
tilem_get_supported_hardware(&models, &nmodels);
for (i = 0; i < nmodels; i++) {
if (!strcmp(s, models[i]->name)) {
g_free(s);
return models[i]->model_id;
}
}
g_free(s);
return 0;
}
/* Convert TilEm model ID to tifiles2 model ID. */
CalcModel model_to_calcmodel(int model)
{
switch (model) {
case TILEM_CALC_TI73:
return CALC_TI73;
case TILEM_CALC_TI82:
return CALC_TI82;
case TILEM_CALC_TI83:
case TILEM_CALC_TI76:
return CALC_TI83;
case TILEM_CALC_TI83P:
case TILEM_CALC_TI83P_SE:
return CALC_TI83P;
case TILEM_CALC_TI84P:
case TILEM_CALC_TI84P_SE:
case TILEM_CALC_TI84P_NSPIRE:
return CALC_TI84P;
case TILEM_CALC_TI85:
return CALC_TI85;
case TILEM_CALC_TI86:
return CALC_TI86;
default:
return CALC_NONE;
}
}
/* Convert tifiles2 model ID to TilEm model ID. */
int calcmodel_to_model(CalcModel model)
{
switch (model) {
case CALC_TI73:
return TILEM_CALC_TI73;
case CALC_TI82:
return TILEM_CALC_TI82;
case CALC_TI83:
return TILEM_CALC_TI83;
case CALC_TI83P:
return TILEM_CALC_TI83P;
case CALC_TI84P:
return TILEM_CALC_TI84P;
case CALC_TI85:
return TILEM_CALC_TI85;
case CALC_TI86:
return TILEM_CALC_TI86;
default:
return 0;
}
}
/* Get model ID for a given file. */
int file_to_model(const char *name)
{
const char *p;
TigContent *tig;
int model;
p = strrchr(name, '.');
if (!p || strlen(p) < 4 || strchr(p, '/') || strchr(p, '\\'))
return 0;
p++;
if (!g_ascii_strcasecmp(p, "prg"))
return TILEM_CALC_TI81;
if (!g_ascii_strncasecmp(p, "73", 2))
return TILEM_CALC_TI73;
if (!g_ascii_strncasecmp(p, "82", 2))
return TILEM_CALC_TI82;
if (!g_ascii_strncasecmp(p, "83", 2))
return TILEM_CALC_TI83;
if (!g_ascii_strncasecmp(p, "8x", 2))
return TILEM_CALC_TI83P;
if (!g_ascii_strncasecmp(p, "85", 2))
return TILEM_CALC_TI85;
if (!g_ascii_strncasecmp(p, "86", 2))
return TILEM_CALC_TI86;
if (!g_ascii_strcasecmp(p, "tig")
|| !g_ascii_strcasecmp(p, "zip")) {
/* read file and see what tifiles thinks the type is */
tig = tifiles_content_create_tigroup(CALC_NONE, 0);
tifiles_file_read_tigroup(name, tig);
model = calcmodel_to_model(tig->model);
tifiles_content_delete_tigroup(tig);
return model;
}
return 0;
}
/* Get "base" model for file type support. */
int model_to_base_model(int calc_model)
{
switch (calc_model) {
case TILEM_CALC_TI83:
case TILEM_CALC_TI76:
return TILEM_CALC_TI83;
case TILEM_CALC_TI83P:
case TILEM_CALC_TI83P_SE:
case TILEM_CALC_TI84P:
case TILEM_CALC_TI84P_SE:
case TILEM_CALC_TI84P_NSPIRE:
return TILEM_CALC_TI83P;
default:
return calc_model;
}
}
/* Check if calc is compatible with given file type. */
gboolean model_supports_file(int calc_model, int file_model)
{
calc_model = model_to_base_model(calc_model);
file_model = model_to_base_model(file_model);
if (file_model == calc_model)
return TRUE;
if (file_model == TILEM_CALC_TI82
&& (calc_model == TILEM_CALC_TI83
|| calc_model == TILEM_CALC_TI83P))
return TRUE;
if (file_model == TILEM_CALC_TI83
&& (calc_model == TILEM_CALC_TI83P))
return TRUE;
if (file_model == TILEM_CALC_TI85
&& (calc_model == TILEM_CALC_TI86))
return TRUE;
return FALSE;
}
/* A popup which is used to let the user choose the model at startup */
char choose_rom_popup(GtkWidget *parent_window, const char *filename,
char default_model)
{
const TilemHardware **models;
GtkWidget *dlg, *vbox, *frame, *btn;
GtkToggleButton **btns;
char *ids, id = 0;
int nmodels, noptions, i, j, defoption = 0, response;
dword romsize;
char *fn, *msg;
tilem_get_supported_hardware(&models, &nmodels);
/* determine ROM size for default model */
for (i = 0; i < nmodels; i++)
if (models[i]->model_id == default_model)
break;
g_return_val_if_fail(i < nmodels, 0);
romsize = models[i]->romsize;
/* all other models with same ROM size are candidates */
noptions = 0;
for (i = 0; i < nmodels; i++) {
if (models[i]->model_id == default_model)
defoption = noptions;
if (models[i]->romsize == romsize)
noptions++;
}
if (noptions < 2) /* no choice */
return default_model;
dlg = gtk_dialog_new_with_buttons("Select Calculator Type",
GTK_WINDOW(parent_window),
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);
vbox = gtk_vbox_new(TRUE, 0);
/* create radio buttons */
btns = g_new(GtkToggleButton*, noptions);
ids = g_new(char, noptions);
btn = NULL;
for (i = j = 0; i < nmodels; i++) {
if (models[i]->romsize == romsize) {
btn = gtk_radio_button_new_with_label_from_widget
(GTK_RADIO_BUTTON(btn), models[i]->desc);
btns[j] = GTK_TOGGLE_BUTTON(btn);
ids[j] = models[i]->model_id;
gtk_box_pack_start(GTK_BOX(vbox), btn, TRUE, TRUE, 3);
j++;
}
}
gtk_toggle_button_set_active(btns[defoption], TRUE);
fn = g_filename_display_basename(filename);
msg = g_strdup_printf("Calculator type for %s:", fn);
frame = new_frame(msg, vbox);
g_free(fn);
g_free(msg);
gtk_container_set_border_width(GTK_CONTAINER(frame), 6);
gtk_widget_show_all(frame);
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
response = gtk_dialog_run(GTK_DIALOG(dlg));
if (response == GTK_RESPONSE_OK) {
for (i = 0; i < noptions; i++) {
if (gtk_toggle_button_get_active(btns[i])) {
id = ids[i];
break;
}
}
}
else {
id = 0;
}
gtk_widget_destroy(dlg);
g_free(btns);
g_free(ids);
return id;
}
/* Convert UTF-8 to filename encoding. Use ASCII digits in place of
subscripts if necessary. If conversion fails utterly, fall back to
the UTF-8 name, which is broken but better than nothing. */
char * utf8_to_filename(const char *utf8str)
{
gchar *result, *ibuf, *obuf, *p;
gsize icount, ocount;
const gchar **charsets;
GIConv ic;
gunichar c;
if (g_get_filename_charsets(&charsets))
return g_strdup(utf8str);
ic = g_iconv_open(charsets[0], "UTF-8");
if (!ic) {
g_warning("utf8_to_filename: unsupported charset %s",
charsets[0]);
return g_strdup(utf8str);
}
ibuf = (gchar*) utf8str;
icount = strlen(utf8str);
ocount = icount * 2; /* be generous */
result = obuf = g_new(gchar, ocount + 1);
while (g_iconv(ic, &ibuf, &icount, &obuf, &ocount) == (gsize) -1) {
if (errno != EILSEQ) {
g_warning("utf8_to_filename: error in conversion");
g_free(result);
g_iconv_close(ic);
return g_strdup(utf8str);
}
c = g_utf8_get_char(ibuf);
if (c >= 0x2080 && c <= 0x2089)
*obuf = c - 0x2080 + '0';
else
*obuf = '_';
obuf++;
ocount--;
p = g_utf8_next_char(ibuf);
icount -= p - ibuf;
ibuf = p;
}
*obuf = 0;
g_iconv_close(ic);
return result;
}
/* Convert UTF-8 to a subset of UTF-8 that is compatible with the
locale */
char * utf8_to_restricted_utf8(const char *utf8str)
{
char *p, *q;
p = utf8_to_filename(utf8str);
q = g_filename_to_utf8(p, -1, NULL, NULL, NULL);
g_free(p);
if (q)
return q;
else
return g_strdup(utf8str);
}
/* Generate default filename (UTF-8) for a variable */
char * get_default_filename(const TilemVarEntry *tve)
{
GString *str = g_string_new("");
if (tve->slot_str) {
g_string_append(str, tve->slot_str);
if (tve->name_str && tve->name_str[0]) {
g_string_append_c(str, '-');
g_string_append(str, tve->name_str);
}
}
else if (tve->name_str && tve->name_str[0]) {
g_string_append(str, tve->name_str);
}
else {
g_string_append(str, "untitled");
}
g_string_append_c(str, '.');
g_string_append(str, tve->file_ext);
return g_string_free(str, FALSE);
}