1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2010-2011 Thibault Duponchelle
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.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 <ticonv.h>
32 #include <tilem.h>
33 #include <tilemdb.h>
34 #include <scancodes.h>
35 
36 #include "gui.h"
37 #include "disasmview.h"
38 #include "memmodel.h"
39 #include "files.h"
40 #include "filedlg.h"
41 #include "msgbox.h"
42 #include "fixedtreeview.h"
43 
44 static GtkTreeModel* fill_varlist(TilemReceiveDialog *rcvdialog);
45 TilemReceiveDialog* create_receive_menu(TilemCalcEmulator *emu);
46 
47 /* Columns */
48 enum
49 {
50 	COL_ENTRY = 0,
51 	COL_SLOT_STR,
52 	COL_NAME_STR,
53 	COL_TYPE_STR,
54 	COL_SIZE_STR,
55 	COL_SIZE,
56   	NUM_COLS
57 };
58 
59 #define RESPONSE_REFRESH 1
60 
61 /* Prompt to overwrite a list of files. */
prompt_overwrite(GtkWindow * win,const char * dirname,char ** filenames)62 static gboolean prompt_overwrite(GtkWindow *win, const char *dirname,
63                                  char **filenames)
64 {
65 	int i;
66 	char *dname;
67 	GString *conflicts = NULL;
68 	int nconflicts = 0;
69 	GtkWidget *dlg, *btn;
70 	int response;
71 
72 	for (i = 0; filenames[i]; i++) {
73 		if (g_file_test(filenames[i], G_FILE_TEST_EXISTS)) {
74 			if (conflicts)
75 				g_string_append_c(conflicts, '\n');
76 			else
77 				conflicts = g_string_new(NULL);
78 			dname = g_filename_display_basename(filenames[i]);
79 			g_string_append(conflicts, dname);
80 			g_free(dname);
81 			nconflicts++;
82 		}
83 	}
84 
85 	if (!conflicts)
86 		return TRUE;
87 
88 	dname = g_filename_display_basename(dirname);
89 
90 	dlg = gtk_message_dialog_new
91 		(win, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
92 		 (nconflicts == 1
93 		  ? "Replace existing file?"
94 		  : "Replace existing files?"));
95 
96 	gtk_message_dialog_format_secondary_text
97 		(GTK_MESSAGE_DIALOG(dlg),
98 		 (nconflicts == 1
99 		  ? "The file \"%2$s\" already exists in \"%1$s\"."
100 		  "  Replacing it will overwrite its contents."
101 		  : "The following files already exist in \"%s\"."
102 		  "  Replacing them will overwrite their contents:\n%s"),
103 		 dname, conflicts->str);
104 
105 	g_free(dname);
106 	g_string_free(conflicts, TRUE);
107 
108 	gtk_dialog_add_button(GTK_DIALOG(dlg),
109 	                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
110 
111 	btn = gtk_button_new_with_mnemonic("_Replace");
112 	gtk_button_set_image(GTK_BUTTON(btn),
113 	                     gtk_image_new_from_stock(GTK_STOCK_SAVE,
114 	                                              GTK_ICON_SIZE_BUTTON));
115 	gtk_widget_show(btn);
116 	gtk_dialog_add_action_widget(GTK_DIALOG(dlg), btn,
117 	                             GTK_RESPONSE_ACCEPT);
118 
119 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg),
120 	                                        GTK_RESPONSE_ACCEPT,
121 	                                        GTK_RESPONSE_CANCEL,
122 	                                        -1);
123 
124 	response = gtk_dialog_run(GTK_DIALOG(dlg));
125 	gtk_widget_destroy(dlg);
126 	return (response == GTK_RESPONSE_ACCEPT);
127 }
128 
129 /* #### SIGNALS CALLBACK #### */
130 
131 /* Prompt to save a single file. */
prompt_save_single(TilemReceiveDialog * rcvdialog,TilemVarEntry * tve)132 static gboolean prompt_save_single(TilemReceiveDialog *rcvdialog, TilemVarEntry *tve)
133 {
134 	char *dir, *default_filename, *default_filename_r, *filename, *pattern;
135 
136 	default_filename = get_default_filename(tve);
137 	default_filename_r = utf8_to_restricted_utf8(default_filename);
138 	g_free(default_filename);
139 
140 	tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL);
141 	if (!dir) dir = g_get_current_dir();
142 
143 	pattern = g_strconcat("*.", tve->file_ext, NULL);
144 
145 	filename = prompt_save_file("Save File", GTK_WINDOW(rcvdialog->window),
146 	                            default_filename_r, dir,
147 	                            tve->filetype_desc, pattern,
148 	                            "All files", "*",
149 	                            NULL);
150 	g_free(default_filename_r);
151 	g_free(pattern);
152 	g_free(dir);
153 
154 	if (!filename)
155 		return FALSE;
156 
157 	dir = g_path_get_dirname(filename);
158 	tilem_config_set("download", "receivefile_recentdir/f", dir, NULL);
159 	g_free(dir);
160 
161 	tilem_link_receive_file(rcvdialog->emu, tve, filename);
162 	g_free(filename);
163 	return TRUE;
164 }
165 
166 /* Prompt to save a list of variables as a group file. */
prompt_save_group(TilemReceiveDialog * rcvdialog,GList * rows)167 static gboolean prompt_save_group(TilemReceiveDialog *rcvdialog, GList *rows)
168 {
169 	char *dir, *default_filename, *pattern_desc, *pattern, *filename, *fext;
170 	int tfmodel;
171 	gboolean can_group = TRUE;
172 	const char *model_str;
173 	GList *l;
174 	GtkTreePath *path;
175 	GtkTreeIter iter;
176 	TilemVarEntry *tve;
177 	GSList *velist = NULL;
178 
179 	tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL);
180 	if (!dir) dir = g_get_current_dir();
181 
182 	for (l = rows; l; l = l->next) {
183 		path = (GtkTreePath*) l->data;
184 		gtk_tree_model_get_iter(rcvdialog->model, &iter, path);
185 		gtk_tree_model_get(rcvdialog->model, &iter, COL_ENTRY, &tve, -1);
186 		velist = g_slist_prepend(velist, tve);
187 		if (!tve->can_group)
188 			can_group = FALSE;
189 	}
190 
191 	velist = g_slist_reverse(velist);
192 
193 	tfmodel = get_calc_model(rcvdialog->emu->calc);
194 
195 	fext = g_ascii_strdown(tifiles_fext_of_group(tfmodel), -1);
196 	pattern = g_strconcat("*.", fext, NULL);
197 	default_filename = g_strdup_printf("untitled.%s",
198 	                                   (can_group ? fext : "tig"));
199 	g_free(fext);
200 
201 	model_str = tifiles_model_to_string(tfmodel);
202 	pattern_desc = g_strdup_printf("%s group files", model_str);
203 
204 	filename = prompt_save_file("Save File", GTK_WINDOW(rcvdialog->window),
205 	                            default_filename, dir,
206 	                            pattern_desc, (can_group ? pattern : ""),
207 	                            "TIGroup files", "*.tig",
208 	                            "All files", "*",
209 	                            NULL);
210 
211 	g_free(default_filename);
212 	g_free(dir);
213 	g_free(pattern_desc);
214 	g_free(pattern);
215 
216 	if (!filename) {
217 		g_slist_free(velist);
218 		return FALSE;
219 	}
220 
221 	dir = g_path_get_dirname(filename);
222 	tilem_config_set("download",
223 	                 "receivefile_recentdir/f", dir,
224 	                 "save_as_group/b", TRUE,
225 	                 NULL);
226 	g_free(dir);
227 
228 	tilem_link_receive_group(rcvdialog->emu, velist, filename);
229 
230 	g_free(filename);
231 	g_slist_free(velist);
232 	return TRUE;
233 }
234 
235 /* Prompt to save a list of files.  Input is a list of GtkTreePaths */
prompt_save_multiple(TilemReceiveDialog * rcvdialog,GList * rows)236 static gboolean prompt_save_multiple(TilemReceiveDialog *rcvdialog, GList *rows)
237 {
238 	char *dir, *dir_selected, *default_filename, *default_filename_f;
239 	GList *l;
240 	GtkTreePath *path;
241 	GtkTreeIter iter;
242 	TilemVarEntry *tve, **vars;
243 	char **names;
244 	gboolean is_81, use_group;
245 	int i;
246 
247 	is_81 = (rcvdialog->emu->calc->hw.model_id == TILEM_CALC_TI81);
248 	use_group = gtk_toggle_button_get_active
249 		(GTK_TOGGLE_BUTTON(rcvdialog->group_rb));
250 
251 	if (use_group && !is_81)
252 		return prompt_save_group(rcvdialog, rows);
253 
254 	tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL);
255 	if (!dir) dir = g_get_current_dir();
256 
257 	dir_selected = prompt_select_dir("Save Files to Directory",
258 	                                 GTK_WINDOW(rcvdialog->window),
259 	                                 dir);
260 	g_free(dir);
261 
262 	if (!dir_selected)
263 		return FALSE;
264 
265 	tilem_config_set("download",
266 	                 "receivefile_recentdir/f", dir_selected,
267 	                 "save_as_group/b", use_group,
268 	                 NULL);
269 
270 	vars = g_new(TilemVarEntry *, g_list_length(rows) + 1);
271 	names = g_new(char *, g_list_length(rows) + 1);
272 
273 	for (l = rows, i = 0; l; l = l->next, i++) {
274 		path = (GtkTreePath*) l->data;
275 		gtk_tree_model_get_iter(rcvdialog->model, &iter, path);
276 		gtk_tree_model_get(rcvdialog->model, &iter, COL_ENTRY, &tve, -1);
277 
278 		vars[i] = tve;
279 
280 		default_filename = get_default_filename(tve);
281 		default_filename_f = utf8_to_filename(default_filename);
282 		names[i] = g_build_filename(dir_selected,
283 		                            default_filename_f, NULL);
284 		g_free(default_filename);
285 		g_free(default_filename_f);
286 	}
287 
288 	vars[i] = NULL;
289 	names[i] = NULL;
290 
291 	if (!prompt_overwrite(GTK_WINDOW(rcvdialog->window),
292 	                      dir_selected, names)) {
293 		g_free(vars);
294 		g_strfreev(names);
295 		g_free(dir_selected);
296 		return FALSE;
297 	}
298 
299 	for (i = 0; vars[i]; i++)
300 		tilem_link_receive_file(rcvdialog->emu, vars[i], names[i]);
301 
302 	g_free(vars);
303 	g_strfreev(names);
304 	g_free(dir_selected);
305 	return TRUE;
306 }
307 
308 /* Event called on Send button click. Get the selected var/app and save it. */
prompt_save(TilemReceiveDialog * rcvdialog)309 static gboolean prompt_save(TilemReceiveDialog *rcvdialog)
310 {
311 	TilemVarEntry *tve;		/* Variable entry */
312 	GtkTreeSelection* selection = NULL; /* GtkTreeSelection */
313 	GtkTreeModel *model;
314 	GtkTreeIter iter;
315 	GList *rows, *l;
316 	GtkTreePath *path;
317 	gboolean status;
318 
319 	/* Get the selected entry */
320 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rcvdialog->treeview));
321 	rows = gtk_tree_selection_get_selected_rows(selection, &model);
322 
323 	if (!rows)
324 		return FALSE;
325 
326 	if (!rows->next) {
327 		path = (GtkTreePath*) rows->data;
328 		gtk_tree_model_get_iter(model, &iter, path);
329 		gtk_tree_model_get(model, &iter, COL_ENTRY, &tve, -1);
330 		status = prompt_save_single(rcvdialog, tve);
331 	}
332 	else {
333 		status = prompt_save_multiple(rcvdialog, rows);
334 	}
335 
336 	for (l = rows; l; l = l->next)
337 		gtk_tree_path_free(l->data);
338 	g_list_free(rows);
339 	return status;
340 }
341 
342 /* Dialog response button clicked */
dialog_response(GtkDialog * dlg,gint response,gpointer data)343 static void dialog_response(GtkDialog *dlg, gint response, gpointer data)
344 {
345 	TilemReceiveDialog* rcvdialog = (TilemReceiveDialog*) data;
346 
347 	switch (response) {
348 	case RESPONSE_REFRESH:
349 		if (!rcvdialog->refresh_pending) {
350 			rcvdialog->refresh_pending = TRUE;
351 			tilem_link_get_dirlist(rcvdialog->emu);
352 		}
353 		break;
354 
355 	case GTK_RESPONSE_ACCEPT:
356 		if (!prompt_save(rcvdialog))
357 			break;
358 	default:
359 		gtk_widget_hide(GTK_WIDGET(dlg));
360 	}
361 }
362 
363 /* Selection changed */
selection_changed(GtkTreeSelection * sel,gpointer data)364 static void selection_changed(GtkTreeSelection *sel, gpointer data)
365 {
366 	TilemReceiveDialog* rcvdialog = data;
367 	int n = gtk_tree_selection_count_selected_rows(sel);
368 
369 	gtk_dialog_set_response_sensitive(GTK_DIALOG(rcvdialog->window),
370 	                                  GTK_RESPONSE_ACCEPT, (n > 0));
371 	gtk_widget_set_sensitive(rcvdialog->mode_box, (n > 1));
372 }
373 
374 /* Row activated in tree view */
row_activated(G_GNUC_UNUSED GtkTreeView * treeview,G_GNUC_UNUSED GtkTreePath * path,G_GNUC_UNUSED GtkTreeViewColumn * col,gpointer data)375 static void row_activated(G_GNUC_UNUSED GtkTreeView *treeview,
376                           G_GNUC_UNUSED GtkTreePath *path,
377                           G_GNUC_UNUSED GtkTreeViewColumn *col,
378                           gpointer data)
379 {
380 	TilemReceiveDialog* rcvdialog = (TilemReceiveDialog*) data;
381 	gtk_dialog_response(GTK_DIALOG(rcvdialog->window), GTK_RESPONSE_ACCEPT);
382 }
383 
384 /* #### WIDGET CREATION #### */
385 
386 /* Create a new scrolled window with sensible default settings. */
new_scrolled_window(GtkWidget * contents)387 static GtkWidget *new_scrolled_window(GtkWidget *contents)
388 {
389         GtkWidget *sw;
390         sw = gtk_scrolled_window_new(NULL, NULL);
391         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
392                                        GTK_POLICY_NEVER,
393                                        GTK_POLICY_AUTOMATIC);
394         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
395                                             GTK_SHADOW_IN);
396         gtk_container_add(GTK_CONTAINER(sw), contents);
397         return sw;
398 }
399 
400 /* Create the (empty) GtkTreeView to show the vars list */
create_varlist(TilemReceiveDialog * rcvdialog)401 static GtkWidget *create_varlist(TilemReceiveDialog *rcvdialog)
402 {
403 	GtkCellRenderer   *renderer;
404 	GtkWidget         *treeview;
405 	GtkTreeSelection  *sel;
406 	GtkTreeViewColumn *c1, *c2, *c3, *c4;
407 	gboolean           is_81;
408 
409 	g_return_val_if_fail(rcvdialog->emu != NULL, NULL);
410 	g_return_val_if_fail(rcvdialog->emu->calc != NULL, NULL);
411 
412 	is_81 = (rcvdialog->emu->calc->hw.model_id == TILEM_CALC_TI81);
413 
414 	/* Create the stack list tree view and set title invisible */
415 	treeview = gtk_tree_view_new();
416 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
417 	gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE);
418 	gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE);
419 
420 	/* Allow multiple selection */
421 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
422 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
423 
424 	/* Create the columns */
425 	renderer = gtk_cell_renderer_text_new();
426 
427 	if (is_81) {
428 		c1 = gtk_tree_view_column_new_with_attributes
429 			("Slot", renderer, "text", COL_SLOT_STR, NULL);
430 
431 		gtk_tree_view_column_set_sizing(c1, GTK_TREE_VIEW_COLUMN_FIXED);
432 		gtk_tree_view_column_set_sort_column_id(c1, COL_SLOT_STR);
433 		gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c1);
434 	}
435 
436 	c2 = gtk_tree_view_column_new_with_attributes
437 		("Name", renderer, "text", COL_NAME_STR, NULL);
438 
439 	gtk_tree_view_column_set_sizing(c2, GTK_TREE_VIEW_COLUMN_FIXED);
440 	gtk_tree_view_column_set_sort_column_id(c2, COL_NAME_STR);
441 	gtk_tree_view_column_set_expand(c2, TRUE);
442 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c2);
443 
444 	if (!is_81) {
445 		c3 = gtk_tree_view_column_new_with_attributes
446 			("Type", renderer, "text", COL_TYPE_STR, NULL);
447 
448 		gtk_tree_view_column_set_sizing(c3, GTK_TREE_VIEW_COLUMN_FIXED);
449 		gtk_tree_view_column_set_sort_column_id(c3, COL_TYPE_STR);
450 		gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c3);
451 	}
452 
453 	renderer = gtk_cell_renderer_text_new();
454 	g_object_set(renderer, "xalign", 1.0, NULL);
455 	c4 = gtk_tree_view_column_new_with_attributes
456 		("Size", renderer, "text", COL_SIZE_STR, NULL);
457 
458 	gtk_tree_view_column_set_sizing(c4, GTK_TREE_VIEW_COLUMN_FIXED);
459 	gtk_tree_view_column_set_sort_column_id(c4, COL_SIZE);
460 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c4);
461 
462 	g_signal_connect(sel, "changed",
463 	                 G_CALLBACK(selection_changed), rcvdialog);
464 
465 	g_signal_connect(treeview, "row-activated",
466 	                 G_CALLBACK(row_activated), rcvdialog);
467 
468 	return treeview;
469 }
470 
471 /* Fill the list of vars. In fact, add all vars from list to a GtkListStore */
fill_varlist(TilemReceiveDialog * rcvdialog)472 static GtkTreeModel* fill_varlist(TilemReceiveDialog *rcvdialog)
473 {
474 	GSList *l;
475 	TilemVarEntry *tve;
476 	GtkListStore *store;
477 	GtkTreeIter iter;
478 	char *size_str;
479 
480 	store = gtk_list_store_new(6,
481 	                           G_TYPE_POINTER,
482 	                           G_TYPE_STRING,
483 	                           G_TYPE_STRING,
484 	                           G_TYPE_STRING,
485 	                           G_TYPE_STRING,
486 	                           G_TYPE_INT);
487 
488 	for (l = rcvdialog->vars; l; l = l->next) {
489 		tve = l->data;
490 		gtk_list_store_append(store, &iter);
491 #ifdef G_OS_WIN32
492 		size_str = g_strdup_printf("%d", tve->size);
493 #else
494 		size_str = g_strdup_printf("%'d", tve->size);
495 #endif
496 		gtk_list_store_set(store, &iter,
497 		                   COL_ENTRY, tve,
498 		                   COL_SLOT_STR, tve->slot_str,
499 		                   COL_NAME_STR, tve->name_str,
500 		                   COL_TYPE_STR, tve->type_str,
501 		                   COL_SIZE_STR, size_str,
502 		                   COL_SIZE, tve->size,
503 		                   -1);
504 		g_free(size_str);
505 	}
506 
507 	return GTK_TREE_MODEL(store);
508 }
509 
510 /* Create a new menu for receiving vars. */
511 /* Previous allocated and filled varlist is needed */
tilem_receive_dialog_new(TilemCalcEmulator * emu)512 TilemReceiveDialog* tilem_receive_dialog_new(TilemCalcEmulator *emu)
513 {
514 	TilemReceiveDialog* rcvdialog = g_slice_new0(TilemReceiveDialog);
515 	GtkWidget *scroll, *btn, *vbox, *lbl, *rb, *vbox2;
516 	int defheight = 300;
517 	gboolean is_81;
518 	gboolean use_group;
519 
520 	g_return_val_if_fail(emu != NULL, NULL);
521 	g_return_val_if_fail(emu->ewin != NULL, NULL);
522 	g_return_val_if_fail(emu->calc != NULL, NULL);
523 
524 	rcvdialog->emu = emu;
525 	emu->rcvdlg = rcvdialog;
526 
527 	is_81 = (emu->calc->hw.model_id == TILEM_CALC_TI81);
528 
529 	rcvdialog->window = gtk_dialog_new();
530 	gtk_window_set_transient_for(GTK_WINDOW(rcvdialog->window),
531 	                             GTK_WINDOW(emu->ewin->window));
532 
533 	gtk_window_set_title(GTK_WINDOW(rcvdialog->window), "Receive File");
534 
535 	btn = gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window),
536 	                            GTK_STOCK_REFRESH, RESPONSE_REFRESH);
537 
538 	if (is_81)
539 		gtk_widget_hide(btn);
540 
541 	gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window),
542 	                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
543 	gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window),
544 	                      GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT);
545 
546 	gtk_dialog_set_default_response(GTK_DIALOG(rcvdialog->window),
547 	                                GTK_RESPONSE_ACCEPT);
548 	gtk_dialog_set_alternative_button_order(GTK_DIALOG(rcvdialog->window),
549 	                                        RESPONSE_REFRESH,
550 	                                        GTK_RESPONSE_ACCEPT,
551 	                                        GTK_RESPONSE_CANCEL,
552 	                                        -1);
553 
554 	/* Set the size of the dialog */
555 	gtk_window_set_default_size(GTK_WINDOW(rcvdialog->window), -1, defheight);
556 
557 	/* Create and fill tree view */
558 	rcvdialog->treeview = create_varlist(rcvdialog);
559 
560 	/* Allow scrolling the list because we can't know how many vars the calc contains */
561 	scroll = new_scrolled_window(rcvdialog->treeview);
562 
563 	vbox = gtk_vbox_new(FALSE, 6);
564 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
565 	gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scroll), TRUE, TRUE, 0);
566 
567 	rcvdialog->mode_box = gtk_hbox_new(FALSE, 6);
568 	lbl = gtk_label_new("Save as:");
569 	gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), lbl, FALSE, FALSE, 0);
570 
571 	rb = gtk_radio_button_new_with_mnemonic(NULL, "S_eparate files");
572 	gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), rb, FALSE, FALSE, 0);
573 	rcvdialog->multiple_rb = rb;
574 
575 	rb = gtk_radio_button_new_with_mnemonic_from_widget
576 		(GTK_RADIO_BUTTON(rb), "_Group file");
577 	gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), rb, FALSE, FALSE, 0);
578 	rcvdialog->group_rb = rb;
579 
580 	tilem_config_get("download", "save_as_group/b=1", &use_group, NULL);
581 	if (use_group)
582 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb), TRUE);
583 
584 	if (is_81)
585 		gtk_widget_set_no_show_all(rcvdialog->mode_box, TRUE);
586 
587 	gtk_box_pack_start(GTK_BOX(vbox), rcvdialog->mode_box, FALSE, FALSE, 0);
588 	vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(rcvdialog->window));
589 	gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0);
590 
591 	/* Signals callback */
592 	g_signal_connect(rcvdialog->window, "response",
593 	                 G_CALLBACK(dialog_response), rcvdialog);
594 	g_signal_connect(rcvdialog->window, "delete-event",
595 	                 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
596 
597 	gtk_widget_show_all(vbox);
598 
599 	return rcvdialog;
600 }
601 
602 /* Destroy a TilemReceiveDialog */
tilem_receive_dialog_free(TilemReceiveDialog * rcvdialog)603 void tilem_receive_dialog_free(TilemReceiveDialog *rcvdialog)
604 {
605 	GSList *l;
606 
607 	g_return_if_fail(rcvdialog != NULL);
608 
609 	gtk_widget_destroy(rcvdialog->window);
610 
611 	for (l = rcvdialog->vars; l; l = l->next)
612 		tilem_var_entry_free(l->data);
613 	g_slist_free(rcvdialog->vars);
614 
615 	g_slice_free(TilemReceiveDialog, rcvdialog);
616 }
617 
tilem_receive_dialog_update(TilemReceiveDialog * rcvdialog,GSList * varlist)618 void tilem_receive_dialog_update(TilemReceiveDialog *rcvdialog, GSList *varlist)
619 {
620 	GSList *l;
621 
622 	g_return_if_fail(rcvdialog != NULL);
623 
624 	rcvdialog->refresh_pending = FALSE;
625 
626 	for (l = rcvdialog->vars; l; l = l->next)
627 		tilem_var_entry_free(l->data);
628 	g_slist_free(rcvdialog->vars);
629 
630 	rcvdialog->vars = varlist;
631 	rcvdialog->model = fill_varlist(rcvdialog);
632 	gtk_tree_view_set_model(GTK_TREE_VIEW(rcvdialog->treeview), rcvdialog->model);
633 
634 	fixed_tree_view_init(rcvdialog->treeview, 0,
635 	                     COL_SLOT_STR, "PrgmM ",
636 	                     COL_NAME_STR, "MMMMMMMMM ",
637 	                     COL_TYPE_STR, "MMMMMM ",
638 	                     COL_SIZE_STR, "00,000,000",
639 	                     -1);
640 
641 	gtk_widget_grab_focus(rcvdialog->treeview);
642 	gtk_window_present(GTK_WINDOW(rcvdialog->window));
643 }
644 
645 /* Popup the receive window */
646 /* This is the entry point */
popup_receive_menu(TilemEmulatorWindow * ewin)647 void popup_receive_menu(TilemEmulatorWindow *ewin)
648 {
649 	g_return_if_fail(ewin != NULL);
650 	g_return_if_fail(ewin->emu != NULL);
651 	g_return_if_fail(ewin->emu->calc != NULL);
652 
653 	if (ewin->emu->rcvdlg && ewin->emu->rcvdlg->refresh_pending)
654 		return;
655 
656 	/* TI-81 takes no time to refresh, so do it automatically */
657 	if (!ewin->emu->rcvdlg
658 	    || ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) {
659 		tilem_link_get_dirlist(ewin->emu);
660 	}
661 	else {
662 		gtk_widget_grab_focus(ewin->emu->rcvdlg->treeview);
663 		gtk_window_present(GTK_WINDOW(ewin->emu->rcvdlg->window));
664 	}
665 }
666