1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2010-2011 Thibault Duponchelle
5 * Copyright (c) 2010 Benjamin Moody
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <gtk/gtk.h>
29 #include <glib/gstdio.h>
30 #include <ticalcs.h>
31 #include <tilem.h>
32
33 #include "gui.h"
34
35 /* Create a frame around the given widget, with a boldface label in
36 the GNOME style */
new_frame(const gchar * label,GtkWidget * contents)37 GtkWidget* new_frame(const gchar* label, GtkWidget* contents)
38 {
39 GtkWidget *frame, *align;
40 char *str;
41
42 str = g_strconcat("<b>", label, "</b>", NULL);
43 frame = gtk_frame_new(str);
44 g_free(str);
45
46 g_object_set(gtk_frame_get_label_widget(GTK_FRAME(frame)),
47 "use-markup", TRUE, NULL);
48 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
49
50 align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
51 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 12, 0);
52 gtk_widget_show(align);
53 gtk_container_add(GTK_CONTAINER(frame), align);
54 gtk_container_add(GTK_CONTAINER(align), contents);
55 gtk_widget_show(frame);
56
57 return frame;
58 }
59
60 /* Get model name (abbreviation) for a TilEm model ID. */
model_to_name(int model)61 const char * model_to_name(int model)
62 {
63 const TilemHardware **models;
64 int nmodels, i;
65
66 tilem_get_supported_hardware(&models, &nmodels);
67 for (i = 0; i < nmodels; i++)
68 if (models[i]->model_id == model)
69 return models[i]->name;
70
71 return NULL;
72 }
73
74 /* Convert model name to a model ID. */
name_to_model(const char * name)75 int name_to_model(const char *name)
76 {
77 char *s;
78 const TilemHardware **models;
79 int nmodels, i, j;
80
81 s = g_new(char, strlen(name) + 1);
82 for (i = j = 0; name[i]; i++) {
83 if (name[i] == '+')
84 s[j++] = 'p';
85 else if (name[i] != '-')
86 s[j++] = g_ascii_tolower(name[i]);
87 }
88 s[j] = 0;
89
90 tilem_get_supported_hardware(&models, &nmodels);
91 for (i = 0; i < nmodels; i++) {
92 if (!strcmp(s, models[i]->name)) {
93 g_free(s);
94 return models[i]->model_id;
95 }
96 }
97
98 g_free(s);
99 return 0;
100 }
101
102 /* Convert TilEm model ID to tifiles2 model ID. */
model_to_calcmodel(int model)103 CalcModel model_to_calcmodel(int model)
104 {
105 switch (model) {
106 case TILEM_CALC_TI73:
107 return CALC_TI73;
108
109 case TILEM_CALC_TI82:
110 return CALC_TI82;
111
112 case TILEM_CALC_TI83:
113 case TILEM_CALC_TI76:
114 return CALC_TI83;
115
116 case TILEM_CALC_TI83P:
117 case TILEM_CALC_TI83P_SE:
118 return CALC_TI83P;
119
120 case TILEM_CALC_TI84P:
121 case TILEM_CALC_TI84P_SE:
122 case TILEM_CALC_TI84P_NSPIRE:
123 return CALC_TI84P;
124
125 case TILEM_CALC_TI85:
126 return CALC_TI85;
127
128 case TILEM_CALC_TI86:
129 return CALC_TI86;
130
131 default:
132 return CALC_NONE;
133 }
134 }
135
136 /* Convert tifiles2 model ID to TilEm model ID. */
calcmodel_to_model(CalcModel model)137 int calcmodel_to_model(CalcModel model)
138 {
139 switch (model) {
140 case CALC_TI73:
141 return TILEM_CALC_TI73;
142 case CALC_TI82:
143 return TILEM_CALC_TI82;
144 case CALC_TI83:
145 return TILEM_CALC_TI83;
146 case CALC_TI83P:
147 return TILEM_CALC_TI83P;
148 case CALC_TI84P:
149 return TILEM_CALC_TI84P;
150 case CALC_TI85:
151 return TILEM_CALC_TI85;
152 case CALC_TI86:
153 return TILEM_CALC_TI86;
154 default:
155 return 0;
156 }
157 }
158
159 /* Get model ID for a given file. */
file_to_model(const char * name)160 int file_to_model(const char *name)
161 {
162 const char *p;
163 TigContent *tig;
164 int model;
165
166 p = strrchr(name, '.');
167 if (!p || strlen(p) < 4 || strchr(p, '/') || strchr(p, '\\'))
168 return 0;
169 p++;
170
171 if (!g_ascii_strcasecmp(p, "prg"))
172 return TILEM_CALC_TI81;
173
174 if (!g_ascii_strncasecmp(p, "73", 2))
175 return TILEM_CALC_TI73;
176 if (!g_ascii_strncasecmp(p, "82", 2))
177 return TILEM_CALC_TI82;
178 if (!g_ascii_strncasecmp(p, "83", 2))
179 return TILEM_CALC_TI83;
180 if (!g_ascii_strncasecmp(p, "8x", 2))
181 return TILEM_CALC_TI83P;
182 if (!g_ascii_strncasecmp(p, "85", 2))
183 return TILEM_CALC_TI85;
184 if (!g_ascii_strncasecmp(p, "86", 2))
185 return TILEM_CALC_TI86;
186
187 if (!g_ascii_strcasecmp(p, "tig")
188 || !g_ascii_strcasecmp(p, "zip")) {
189 /* read file and see what tifiles thinks the type is */
190 tig = tifiles_content_create_tigroup(CALC_NONE, 0);
191 tifiles_file_read_tigroup(name, tig);
192 model = calcmodel_to_model(tig->model);
193 tifiles_content_delete_tigroup(tig);
194 return model;
195 }
196
197 return 0;
198 }
199
200 /* Get "base" model for file type support. */
model_to_base_model(int calc_model)201 int model_to_base_model(int calc_model)
202 {
203 switch (calc_model) {
204 case TILEM_CALC_TI83:
205 case TILEM_CALC_TI76:
206 return TILEM_CALC_TI83;
207
208 case TILEM_CALC_TI83P:
209 case TILEM_CALC_TI83P_SE:
210 case TILEM_CALC_TI84P:
211 case TILEM_CALC_TI84P_SE:
212 case TILEM_CALC_TI84P_NSPIRE:
213 return TILEM_CALC_TI83P;
214
215 default:
216 return calc_model;
217 }
218 }
219
220 /* Check if calc is compatible with given file type. */
model_supports_file(int calc_model,int file_model)221 gboolean model_supports_file(int calc_model, int file_model)
222 {
223 calc_model = model_to_base_model(calc_model);
224 file_model = model_to_base_model(file_model);
225
226 if (file_model == calc_model)
227 return TRUE;
228
229 if (file_model == TILEM_CALC_TI82
230 && (calc_model == TILEM_CALC_TI83
231 || calc_model == TILEM_CALC_TI83P))
232 return TRUE;
233
234 if (file_model == TILEM_CALC_TI83
235 && (calc_model == TILEM_CALC_TI83P))
236 return TRUE;
237
238 if (file_model == TILEM_CALC_TI85
239 && (calc_model == TILEM_CALC_TI86))
240 return TRUE;
241
242 return FALSE;
243 }
244
245 /* A popup which is used to let the user choose the model at startup */
choose_rom_popup(GtkWidget * parent_window,const char * filename,char default_model)246 char choose_rom_popup(GtkWidget *parent_window, const char *filename,
247 char default_model)
248 {
249 const TilemHardware **models;
250 GtkWidget *dlg, *vbox, *frame, *btn;
251 GtkToggleButton **btns;
252 char *ids, id = 0;
253 int nmodels, noptions, i, j, defoption = 0, response;
254 dword romsize;
255 char *fn, *msg;
256
257 tilem_get_supported_hardware(&models, &nmodels);
258
259 /* determine ROM size for default model */
260 for (i = 0; i < nmodels; i++)
261 if (models[i]->model_id == default_model)
262 break;
263
264 g_return_val_if_fail(i < nmodels, 0);
265
266 romsize = models[i]->romsize;
267
268 /* all other models with same ROM size are candidates */
269 noptions = 0;
270 for (i = 0; i < nmodels; i++) {
271 if (models[i]->model_id == default_model)
272 defoption = noptions;
273 if (models[i]->romsize == romsize)
274 noptions++;
275 }
276
277 if (noptions < 2) /* no choice */
278 return default_model;
279
280 dlg = gtk_dialog_new_with_buttons("Select Calculator Type",
281 GTK_WINDOW(parent_window),
282 GTK_DIALOG_MODAL,
283 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
284 GTK_STOCK_OK, GTK_RESPONSE_OK,
285 NULL);
286 gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg),
287 GTK_RESPONSE_OK,
288 GTK_RESPONSE_CANCEL,
289 -1);
290 gtk_dialog_set_default_response(GTK_DIALOG(dlg),
291 GTK_RESPONSE_OK);
292
293 vbox = gtk_vbox_new(TRUE, 0);
294
295 /* create radio buttons */
296
297 btns = g_new(GtkToggleButton*, noptions);
298 ids = g_new(char, noptions);
299 btn = NULL;
300 for (i = j = 0; i < nmodels; i++) {
301 if (models[i]->romsize == romsize) {
302 btn = gtk_radio_button_new_with_label_from_widget
303 (GTK_RADIO_BUTTON(btn), models[i]->desc);
304 btns[j] = GTK_TOGGLE_BUTTON(btn);
305 ids[j] = models[i]->model_id;
306 gtk_box_pack_start(GTK_BOX(vbox), btn, TRUE, TRUE, 3);
307 j++;
308 }
309 }
310
311 gtk_toggle_button_set_active(btns[defoption], TRUE);
312
313 fn = g_filename_display_basename(filename);
314 msg = g_strdup_printf("Calculator type for %s:", fn);
315 frame = new_frame(msg, vbox);
316 g_free(fn);
317 g_free(msg);
318
319 gtk_container_set_border_width(GTK_CONTAINER(frame), 6);
320 gtk_widget_show_all(frame);
321
322 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
323 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
324
325 response = gtk_dialog_run(GTK_DIALOG(dlg));
326
327 if (response == GTK_RESPONSE_OK) {
328 for (i = 0; i < noptions; i++) {
329 if (gtk_toggle_button_get_active(btns[i])) {
330 id = ids[i];
331 break;
332 }
333 }
334 }
335 else {
336 id = 0;
337 }
338
339 gtk_widget_destroy(dlg);
340 g_free(btns);
341 g_free(ids);
342
343 return id;
344 }
345
346 /* Convert UTF-8 to filename encoding. Use ASCII digits in place of
347 subscripts if necessary. If conversion fails utterly, fall back to
348 the UTF-8 name, which is broken but better than nothing. */
utf8_to_filename(const char * utf8str)349 char * utf8_to_filename(const char *utf8str)
350 {
351 gchar *result, *ibuf, *obuf, *p;
352 gsize icount, ocount;
353 const gchar **charsets;
354 GIConv ic;
355 gunichar c;
356
357 if (g_get_filename_charsets(&charsets))
358 return g_strdup(utf8str);
359
360 ic = g_iconv_open(charsets[0], "UTF-8");
361 if (!ic) {
362 g_warning("utf8_to_filename: unsupported charset %s",
363 charsets[0]);
364 return g_strdup(utf8str);
365 }
366
367 ibuf = (gchar*) utf8str;
368 icount = strlen(utf8str);
369 ocount = icount * 2; /* be generous */
370 result = obuf = g_new(gchar, ocount + 1);
371
372 while (g_iconv(ic, &ibuf, &icount, &obuf, &ocount) == (gsize) -1) {
373 if (errno != EILSEQ) {
374 g_warning("utf8_to_filename: error in conversion");
375 g_free(result);
376 g_iconv_close(ic);
377 return g_strdup(utf8str);
378 }
379
380 c = g_utf8_get_char(ibuf);
381 if (c >= 0x2080 && c <= 0x2089)
382 *obuf = c - 0x2080 + '0';
383 else
384 *obuf = '_';
385 obuf++;
386 ocount--;
387
388 p = g_utf8_next_char(ibuf);
389 icount -= p - ibuf;
390 ibuf = p;
391 }
392
393 *obuf = 0;
394 g_iconv_close(ic);
395 return result;
396 }
397
398 /* Convert UTF-8 to a subset of UTF-8 that is compatible with the
399 locale */
utf8_to_restricted_utf8(const char * utf8str)400 char * utf8_to_restricted_utf8(const char *utf8str)
401 {
402 char *p, *q;
403 p = utf8_to_filename(utf8str);
404 q = g_filename_to_utf8(p, -1, NULL, NULL, NULL);
405 g_free(p);
406 if (q)
407 return q;
408 else
409 return g_strdup(utf8str);
410 }
411
412 /* Generate default filename (UTF-8) for a variable */
get_default_filename(const TilemVarEntry * tve)413 char * get_default_filename(const TilemVarEntry *tve)
414 {
415 GString *str = g_string_new("");
416
417 if (tve->slot_str) {
418 g_string_append(str, tve->slot_str);
419 if (tve->name_str && tve->name_str[0]) {
420 g_string_append_c(str, '-');
421 g_string_append(str, tve->name_str);
422 }
423 }
424 else if (tve->name_str && tve->name_str[0]) {
425 g_string_append(str, tve->name_str);
426 }
427 else {
428 g_string_append(str, "untitled");
429 }
430 g_string_append_c(str, '.');
431 g_string_append(str, tve->file_ext);
432 return g_string_free(str, FALSE);
433 }
434