1 /** \file uicart.c
2 * \brief Widget to attach carts
3 *
4 * \author Bas Wassink <b.wassink@ziggo.nl>
5 */
6
7 /*
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29 #include <gtk/gtk.h>
30 #include <string.h>
31
32 #include "machine.h"
33 #include "resources.h"
34 #include "debug_gtk3.h"
35 #include "basewidgets.h"
36 #include "widgethelpers.h"
37 #include "basedialogs.h"
38 #include "cartimagewidget.h"
39 #include "filechooserhelpers.h"
40 #include "lastdir.h"
41 #include "openfiledialog.h"
42 #include "savefiledialog.h"
43 #include "cartridge.h"
44 #include "vsync.h"
45 #include "ui.h"
46 #include "uimachinewindow.h"
47 #include "crtpreviewwidget.h"
48
49 #include "uicart.h"
50
51
52 /** \brief Enum with various cart types, independent from cartridge.h
53 *
54 * The #define's in cartridge.h don't provide the values I need, so this will
55 * have to do.
56 */
57 typedef enum uicart_type_e {
58
59 /* C64 cart types */
60 UICART_C64_SMART = 0,
61 UICART_C64_8KB,
62 UICART_C64_16KB,
63 UICART_C64_ULTIMAX,
64 UICART_C64_FREEZER,
65 UICART_C64_GAME,
66 UICART_C64_UTIL,
67
68 /* VIC20 cart types */
69 UICART_VIC20_SMART,
70 UICART_VIC20_BEHRBONZ,
71 UICART_VIC20_MEGACART,
72 UICART_VIC20_FINALEXP,
73 UICART_VIC20_ULTIMEM,
74 UICART_VIC20_FLASHPLUGIN,
75 UICART_VIC20_GENERIC,
76 UICART_VIC20_ADD_GENERIC,
77
78 /* Plus4 cart types */
79 UICART_PLUS4_SMART,
80 UICART_PLUS4_NEWROM,
81 UICART_PLUS4_16KB_C0LO,
82 UICART_PLUS4_16KB_C0HI,
83 UICART_PLUS4_16KB_C1LO,
84 UICART_PLUS4_16KB_C1HI,
85 UICART_PLUS4_16KB_C2LO,
86 UICART_PLUS4_16KB_C2HI,
87 UICART_PLUS4_32KB_C0,
88 UICART_PLUS4_32KB_C1,
89 UICART_PLUS4_32KB_C2,
90
91 /* CBM2 cart types */
92 /*UICART_CBM2_SMART,*/
93 UICART_CBM2_8KB_1000,
94 UICART_CBM2_8KB_2000,
95 UICART_CBM2_16KB_4000,
96 UICART_CBM2_16KB_6000
97
98 } uicart_type_t;
99
100
101 /** \brief Indici of filename patterns
102 */
103 enum {
104 UICART_PATTERN_CRT = 0, /* '*.crt' */
105 UICART_PATTERN_BIN, /* '*.bin' */
106 UICART_PATTERN_BIN_PRG, /* '*.bin;*.prg' */
107 UICART_PATTERN_ALL /* '*' */
108 };
109
110
111 /** \brief Simple (text,id) data structure for the cart type model
112 */
113 typedef struct cart_type_list_s {
114 const char *name;
115 int id;
116 } cart_type_list_t;
117
118
119 /** \brief Available C64 cart types
120 *
121 * When the 'type' is freezer, games or utilities, a second combo box will
122 * be populated with cartridges which fall in that category.
123 */
124 static const cart_type_list_t c64_cart_types[] = {
125 { "Smart-attach", UICART_C64_SMART },
126 { "Raw 8KB", UICART_C64_8KB },
127 { "Raw 16KB", UICART_C64_16KB },
128 { "Raw Ultimax", UICART_C64_ULTIMAX },
129 { "Freezer", UICART_C64_FREEZER },
130 { "Games", UICART_C64_GAME },
131 { "Utilities", UICART_C64_UTIL },
132 { NULL, -1 }
133 };
134
135
136 static const cart_type_list_t vic20_cart_types[] = {
137 { "Smart-attach", UICART_VIC20_SMART },
138 { "Behr Bonz", UICART_VIC20_BEHRBONZ },
139 { "Mega Cart", UICART_VIC20_MEGACART },
140 { "Final Expansion", UICART_VIC20_FINALEXP },
141 { "UltiMem", UICART_VIC20_ULTIMEM },
142 { "Vic Flash Plugin", UICART_VIC20_FLASHPLUGIN },
143 { "Generic", UICART_VIC20_GENERIC },
144 { "Add to generic cartridge", UICART_VIC20_ADD_GENERIC },
145 { NULL, -1 }
146 };
147
148 static const cart_type_list_t plus4_cart_types[] = {
149 { "Smart-attach", UICART_PLUS4_SMART },
150 { "NewROM", UICART_PLUS4_NEWROM },
151 { "16k C0 Low", UICART_PLUS4_16KB_C0LO },
152 { "16k C0 High", UICART_PLUS4_16KB_C0HI },
153 { "16k C1 Low", UICART_PLUS4_16KB_C1LO },
154 { "16k C1 High", UICART_PLUS4_16KB_C1HI },
155 { "16k C2 Low", UICART_PLUS4_16KB_C2LO },
156 { "16k C2 High", UICART_PLUS4_16KB_C2HI },
157 { "32k C0", UICART_PLUS4_32KB_C0 },
158 { "32k C1", UICART_PLUS4_32KB_C1 },
159 { "32k C2", UICART_PLUS4_32KB_C2 },
160 { NULL, -1 }
161 };
162
163 static const cart_type_list_t cbm2_cart_types[] = {
164 /*{ "Smart-attach", UICART_CBM2_SMART },*/
165 { "8k at $1000", UICART_CBM2_8KB_1000 },
166 { "8k at $2000", UICART_CBM2_8KB_2000 },
167 { "16k at $4000", UICART_CBM2_16KB_4000 },
168 { "16k at $6000", UICART_CBM2_16KB_6000 },
169 { NULL, -1 }
170 };
171
172
173 static const cart_type_list_t vic20_cart_types_generic[] = {
174 { "Add smart-attach cartridge image", CARTRIDGE_VIC20_DETECT },
175 { "Add 4/8/16KB cartridge at $2000", CARTRIDGE_VIC20_16KB_2000 },
176 { "Add 4/8/16KB cartridge at $4000", CARTRIDGE_VIC20_16KB_4000 },
177 { "Add 4/8/16KB cartridge at $6000", CARTRIDGE_VIC20_16KB_6000 },
178 { "Add 4/8KB cartridge at $A000", CARTRIDGE_VIC20_8KB_A000 },
179 { "Add 4KB cartridge at $B000", CARTRIDGE_VIC20_4KB_B000 },
180 { NULL, -1 }
181 };
182
183
184
185
186 /** \brief File filter pattern for CRT images */
187 static const char *pattern_crt[] = { "*.crt", NULL };
188
189
190 /** \brief File filter pattern for raw images */
191 static const char *pattern_bin[] = { "*.bin", NULL };
192
193 /** \brief File filter pattern for raw images */
194 static const char *pattern_bin_prg[] = { "*.bin", "*.prg", NULL };
195
196
197
198 /** \brief File type filters for the dialog
199 */
200 static ui_file_filter_t filters[] = {
201 { "CRT images", pattern_crt },
202 { "Raw images", pattern_bin },
203 { "Raw images", pattern_bin_prg }, /* VIC20 */
204 { "All files", file_chooser_pattern_all },
205 { NULL, NULL }
206 };
207
208
209 /** \brief Last used directory
210 */
211 static gchar *last_dir = NULL;
212
213
214 /* list of cartridge handling functions (to avoid vsid link errors) */
215 static int (*crt_detect_func)(const char *filename) = NULL;
216 static int (*crt_attach_func)(int type, const char *filename) = NULL;
217 static void (*crt_freeze_func)(void) = NULL;
218 static void (*crt_detach_func)(int type) = NULL;
219 static cartridge_info_t *(*crt_list_func)(void) = NULL;
220 static void (*crt_default_func)(void) = NULL;
221 static const char * (*crt_filename_func)(void) = NULL;
222 static void (*crt_wipe_func)(void) = NULL;
223
224 /* references to widgets used in various event handlers */
225 static GtkWidget *cart_dialog = NULL;
226 static GtkWidget *cart_type_widget = NULL;
227 static GtkWidget *cart_id_widget = NULL;
228 static GtkWidget *cart_preview_widget = NULL;
229 static GtkWidget *cart_set_default_widget = NULL;
230
231 static GtkWidget *cart_id_label = NULL;
232
233 static GtkFileFilter *flt_crt = NULL;
234 static GtkFileFilter *flt_bin = NULL;
235 static GtkFileFilter *flt_bin_prg = NULL;
236 static GtkFileFilter *flt_all = NULL;
237
238 /* forward declarations of functions */
239 static GtkListStore *create_cart_id_model(unsigned int flags);
240 static int get_cart_type(void);
241 static int get_cart_id(void);
242 static int attach_cart_image(int type, int id, const char *path);
243 static GtkListStore *create_cart_id_model_vic20(void);
244
245
246 /** \brief Handler for the "response" event of the dialog
247 *
248 * \param[in] dialog dialog
249 * \param[in] response_id response ID
250 * \param[in] data extra event data (unused)
251 */
on_response(GtkWidget * dialog,gint response_id,gpointer data)252 static void on_response(GtkWidget *dialog, gint response_id, gpointer data)
253 {
254 gchar *filename;
255
256 debug_gtk3("got response ID %d.", response_id);
257 switch (response_id) {
258 case GTK_RESPONSE_DELETE_EVENT:
259 gtk_widget_destroy(dialog);
260 break;
261 case GTK_RESPONSE_ACCEPT:
262 lastdir_update(dialog, &last_dir);
263 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
264 if (filename != NULL) {
265 debug_gtk3("attaching '%s'.", filename);
266 if (!attach_cart_image(get_cart_type(), get_cart_id(), filename)) {
267 vice_gtk3_message_error("VICE Error",
268 "Failed to smart-attach '%s'", filename);
269 }
270 g_free(filename);
271 }
272 gtk_widget_destroy(dialog);
273 break;
274 }
275
276 ui_set_ignore_mouse_hide(FALSE);
277 }
278
279
280 /** \brief Set the file filter pattern for the dialog
281 *
282 * \param[in] pattern UICART_PATTERN_\* enum value
283 */
set_pattern(int pattern)284 static void set_pattern(int pattern)
285 {
286 GtkFileFilter *filter = NULL;
287
288 switch (pattern) {
289 case UICART_PATTERN_CRT:
290 filter = flt_crt;
291 break;
292 case UICART_PATTERN_BIN:
293 filter = flt_bin;
294 break;
295 default:
296 filter = flt_all;
297 break;
298 }
299 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(cart_dialog), filter);
300 }
301
302
303
304
305 /** \brief Handler for the "changed" event of the cart type combo box
306 *
307 * \param[in] combo cart type combo
308 * \param[in] data extra event data (unused)
309 */
on_cart_type_changed(GtkComboBox * combo,gpointer data)310 static void on_cart_type_changed(GtkComboBox *combo, gpointer data)
311 {
312 GtkListStore *id_model; /* cart 'ID' model */
313 unsigned int mask = ~0;
314 int pattern = UICART_PATTERN_BIN;
315 int crt_type;
316
317 crt_type = get_cart_type();
318 if (crt_type < 0) {
319 return;
320 }
321
322 switch (machine_class) {
323 case VICE_MACHINE_C64: /* fall through */
324 case VICE_MACHINE_C64SC: /* fall through */
325 case VICE_MACHINE_C128: /* fall through */
326 case VICE_MACHINE_SCPU64:
327 switch (crt_type) {
328 case UICART_C64_SMART:
329 pattern = UICART_PATTERN_CRT;
330 break;
331 case UICART_C64_FREEZER:
332 mask = CARTRIDGE_GROUP_FREEZER;
333 break;
334 case UICART_C64_GAME:
335 mask = CARTRIDGE_GROUP_GAME;
336 break;
337 case UICART_C64_UTIL:
338 mask = CARTRIDGE_GROUP_UTIL;
339 break;
340 default:
341 mask = 0x0;
342 break;
343 }
344
345 /* update cart ID model and set it */
346 id_model = create_cart_id_model(mask);
347 gtk_combo_box_set_model(GTK_COMBO_BOX(cart_id_widget), GTK_TREE_MODEL(id_model));
348 gtk_combo_box_set_active(GTK_COMBO_BOX(cart_id_widget), 0);
349 /* only show cart ID list when needed */
350 if ((pattern == UICART_PATTERN_CRT) || (mask == 0x0)) {
351 gtk_widget_hide(GTK_WIDGET(cart_id_widget));
352 gtk_widget_hide(GTK_WIDGET(cart_id_label));
353 } else {
354 gtk_widget_show(GTK_WIDGET(cart_id_widget));
355 gtk_widget_show(GTK_WIDGET(cart_id_label));
356 }
357
358 /* update filename pattern */
359 set_pattern(pattern);
360
361 break;
362 case VICE_MACHINE_VIC20:
363 if (crt_type == UICART_VIC20_ADD_GENERIC) {
364 id_model = create_cart_id_model_vic20();
365 //gtk_widget_set_sensitive(cart_id_widget, TRUE);
366 gtk_widget_show(GTK_WIDGET(cart_id_widget));
367 gtk_widget_show(GTK_WIDGET(cart_id_label));
368 } else {
369 /* empty model */
370 id_model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
371 //gtk_widget_set_sensitive(cart_id_widget, FALSE);
372 gtk_widget_hide(GTK_WIDGET(cart_id_widget));
373 gtk_widget_hide(GTK_WIDGET(cart_id_label));
374 }
375 gtk_combo_box_set_model(GTK_COMBO_BOX(cart_id_widget), GTK_TREE_MODEL(id_model));
376 gtk_combo_box_set_active(GTK_COMBO_BOX(cart_id_widget), 0);
377
378 break;
379
380 default:
381 break;
382 }
383 }
384
385
386 /** \brief Get the ID of the model for the 'cart type' combo box
387 *
388 * \return ID or -1 on error
389 */
get_cart_type(void)390 static int get_cart_type(void)
391 {
392 GtkTreeModel *model;
393 GtkTreeIter iter;
394 GtkComboBox *combo;
395 int crt_type = -1;
396
397 combo = GTK_COMBO_BOX(cart_type_widget);
398
399 if (gtk_combo_box_get_active(combo) >= 0) {
400 model = gtk_combo_box_get_model(combo);
401 if (gtk_combo_box_get_active_iter(combo, &iter)) {
402 gtk_tree_model_get(model, &iter, 1, &crt_type, -1);
403 }
404 }
405 return crt_type;
406 }
407
408
409 /** \brief Get the ID of the model for the 'cart ID' combo box
410 *
411 * \return ID or -1 on error
412 */
get_cart_id(void)413 static int get_cart_id(void)
414 {
415 GtkTreeModel *model;
416 GtkTreeIter iter;
417 GtkComboBox *combo;
418 int crt_id = -1;
419
420 if (cart_id_widget == NULL) {
421 return crt_id;
422 }
423
424 combo = GTK_COMBO_BOX(cart_id_widget);
425 if (gtk_combo_box_get_active(combo) >= 0) {
426 model = gtk_combo_box_get_model(combo);
427 if (gtk_combo_box_get_active_iter(combo, &iter)) {
428 gtk_tree_model_get(model, &iter, 1, &crt_id, -1);
429 }
430 }
431 return crt_id;
432 }
433
434
435 /** \brief Cart attach handler
436 *
437 * \param[in] type cartridge type
438 * \param[in] id cartridge ID
439 * \param[in] path path to cartrige image
440 *
441 * \return bool
442 */
attach_cart_image(int type,int id,const char * path)443 static int attach_cart_image(int type, int id, const char *path)
444 {
445 switch (machine_class) {
446 case VICE_MACHINE_C64: /* fall through */
447 case VICE_MACHINE_C64SC: /* fall through */
448 case VICE_MACHINE_C128: /* fall through */
449 case VICE_MACHINE_SCPU64:
450 debug_gtk3("attaching cart type %d, cart ID %d.", type, id);
451 switch (type) {
452 case UICART_C64_SMART:
453 id = CARTRIDGE_CRT;
454 break;
455 case UICART_C64_FREEZER: /* fall through */
456 case UICART_C64_GAME: /* fall through */
457 case UICART_C64_UTIL:
458 /* id is correct I think */
459 break;
460 default:
461 debug_gtk3("error: shouldn't get here.");
462 break;
463 }
464 break;
465
466 case VICE_MACHINE_VIC20:
467 switch (type) {
468 case UICART_VIC20_SMART:
469 id = CARTRIDGE_VIC20_DETECT;
470 break;
471 case UICART_VIC20_GENERIC:
472 id = CARTRIDGE_VIC20_GENERIC;
473 break;
474 case UICART_VIC20_BEHRBONZ:
475 id = CARTRIDGE_VIC20_BEHRBONZ;
476 break;
477 case UICART_VIC20_MEGACART:
478 id = CARTRIDGE_VIC20_MEGACART;
479 break;
480 case UICART_VIC20_FINALEXP:
481 id = CARTRIDGE_VIC20_FINAL_EXPANSION;
482 break;
483 case UICART_VIC20_ULTIMEM:
484 id = CARTRIDGE_VIC20_UM;
485 break;
486 case UICART_VIC20_FLASHPLUGIN:
487 id = CARTRIDGE_VIC20_FP;
488 break;
489 default:
490 /* add to generic, id is already set */
491 debug_gtk3("error: shouldn't get here.");
492 break;
493 }
494 break;
495
496 case VICE_MACHINE_PLUS4:
497 switch (type) {
498 case UICART_PLUS4_SMART:
499 id = CARTRIDGE_PLUS4_DETECT;
500 break;
501 case UICART_PLUS4_NEWROM:
502 id = CARTRIDGE_PLUS4_NEWROM;
503 break;
504 case UICART_PLUS4_16KB_C0LO:
505 id = CARTRIDGE_PLUS4_16KB_C0LO;
506 break;
507 case UICART_PLUS4_16KB_C0HI:
508 id = CARTRIDGE_PLUS4_16KB_C0HI;
509 break;
510 case UICART_PLUS4_16KB_C1LO:
511 id = CARTRIDGE_PLUS4_16KB_C1LO;
512 break;
513 case UICART_PLUS4_16KB_C1HI:
514 id = CARTRIDGE_PLUS4_16KB_C1HI;
515 break;
516 case UICART_PLUS4_16KB_C2LO:
517 id = CARTRIDGE_PLUS4_16KB_C2LO;
518 break;
519 case UICART_PLUS4_16KB_C2HI:
520 id = CARTRIDGE_PLUS4_16KB_C2HI;
521 break;
522 case UICART_PLUS4_32KB_C0:
523 id = CARTRIDGE_PLUS4_32KB_C0;
524 break;
525 case UICART_PLUS4_32KB_C1:
526 id = CARTRIDGE_PLUS4_32KB_C1;
527 break;
528 case UICART_PLUS4_32KB_C2:
529 id = CARTRIDGE_PLUS4_32KB_C2;
530 break;
531 default:
532 /* oops */
533 debug_gtk3("error: shouldn't get here.");
534 break;
535 }
536 break;
537
538 case VICE_MACHINE_CBM5x0: /* fall through */
539 case VICE_MACHINE_CBM6x0:
540 switch (type) {
541 /*case UICART_CBM2_SMART:
542 return (crt_attach_func(CARTRIDGE_CBM2_DETECT, path) == 0);*/
543 case UICART_CBM2_8KB_1000:
544 id = CARTRIDGE_CBM2_8KB_1000;
545 break;
546 case UICART_CBM2_8KB_2000:
547 id = CARTRIDGE_CBM2_8KB_2000;
548 break;
549 case UICART_CBM2_16KB_4000:
550 id = CARTRIDGE_CBM2_16KB_4000;
551 break;
552 case UICART_CBM2_16KB_6000:
553 id = CARTRIDGE_CBM2_16KB_6000;
554 break;
555 default:
556 /* oops */
557 debug_gtk3("error: shouldn't get here.");
558 break;
559 }
560 break;
561
562 default:
563 debug_gtk3("very oops: type = %d, id = %d, path = '%s'.",
564 type, id, path);
565 return 0;
566 break;
567 }
568
569 debug_gtk3("attaching cart type %d, cart ID %04x.", type, id);
570 if ((crt_attach_func(id, path) == 0)) {
571 /* check 'set default' */
572 if ((cart_set_default_widget != NULL)
573 & (gtk_toggle_button_get_active(
574 GTK_TOGGLE_BUTTON(cart_set_default_widget)))) {
575 /* set cart as default, there's no return value, so let's assume
576 * this works */
577 debug_gtk3("setting cart with ID %04x as default.", id);
578 crt_default_func();
579 }
580 return 1;
581 }
582 return 0;
583 }
584
585
586
587 /** \brief Create model for the 'cart type' combo box
588 *
589 * This depends on the `machine_class`, so for some machines, this may return
590 * an empty (useless) model
591 *
592 * \return model
593 */
create_cart_type_model(void)594 static GtkListStore *create_cart_type_model(void)
595 {
596 GtkListStore *model;
597 GtkTreeIter iter;
598 const cart_type_list_t *types;
599 int i;
600
601 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
602 switch (machine_class) {
603 case VICE_MACHINE_C64: /* fall through */
604 case VICE_MACHINE_C64SC: /* fall through */
605 case VICE_MACHINE_C128: /* fall through */
606 case VICE_MACHINE_SCPU64:
607 types = c64_cart_types;
608 break;
609 case VICE_MACHINE_VIC20:
610 types = vic20_cart_types;
611 break;
612 case VICE_MACHINE_PLUS4:
613 types = plus4_cart_types;
614 break;
615 case VICE_MACHINE_CBM5x0:
616 case VICE_MACHINE_CBM6x0:
617 types = cbm2_cart_types;
618 break;
619 default:
620 return model;
621 }
622
623 for (i = 0; types[i].name != NULL; i++) {
624 gtk_list_store_append(model, &iter);
625 gtk_list_store_set(model, &iter, 0, types[i].name, 1, types[i].id, -1);
626 }
627 return model;
628 }
629
630
631
632 /** \brief Create a list of cartridges, filtered with \a flags
633 *
634 * Only valid for c64/c128/scpu
635 *
636 * \return Three-column list store (name, crtid, flags)
637 */
create_cart_id_model(unsigned int flags)638 static GtkListStore *create_cart_id_model(unsigned int flags)
639 {
640 GtkListStore *model;
641 GtkTreeIter iter;
642 cartridge_info_t *list;
643 int i;
644
645 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT);
646
647 if (crt_list_func != NULL) {
648 list = crt_list_func();
649 } else {
650 return model;
651 }
652
653
654 for (i = 0; list[i].name != NULL; i++) {
655 if (list[i].flags & flags) {
656 gtk_list_store_append(model, &iter);
657 gtk_list_store_set(model, &iter,
658 0, list[i].name, /* cart name */
659 1, list[i].crtid, /* cart ID */
660 2, list[i].flags, /* cart flags */
661 -1);
662 }
663 }
664 return model;
665 }
666
667
668 /** \brief Create a list of cartridges for VIC-20
669 *
670 * Only valid for VIC-20
671 *
672 * \return Two-column list store (name, id)
673 */
create_cart_id_model_vic20(void)674 static GtkListStore *create_cart_id_model_vic20(void)
675 {
676 GtkListStore *model;
677 GtkTreeIter iter;
678 int i;
679
680 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
681
682 for (i = 0; vic20_cart_types_generic[i].name != NULL; i++) {
683 gtk_list_store_append(model, &iter);
684 gtk_list_store_set(model, &iter,
685 0, vic20_cart_types_generic[i].name, /* cart name */
686 1, vic20_cart_types_generic[i].id, /* cart ID */
687 -1);
688 }
689 return model;
690 }
691
692
693
694 /** \brief Create combo box with main cartridge types
695 *
696 * \return GtkComboBox
697 */
create_cart_type_combo_box(void)698 static GtkWidget *create_cart_type_combo_box(void)
699 {
700 GtkWidget *combo;
701 GtkListStore *model;
702 GtkCellRenderer *renderer;
703
704 model = create_cart_type_model();
705 if (model == NULL) {
706 return gtk_combo_box_new();
707 }
708 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
709 g_object_unref(model);
710
711 renderer = gtk_cell_renderer_text_new();
712 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
713 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
714 "text", 0, NULL);
715
716 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
717
718 g_signal_connect(combo, "changed", G_CALLBACK(on_cart_type_changed), NULL);
719 return combo;
720 }
721
722
723 /** \brief Create combo box with cartridges with adhere to \a mask
724 *
725 * \param[in] mask bitmask to filter cartridges
726 *
727 * \return GtkComboBox
728 *
729 * \note Only for x64/x64sc/xscp64/x128
730 */
create_cart_id_combo_box(unsigned int mask)731 static GtkWidget *create_cart_id_combo_box(unsigned int mask)
732 {
733 GtkWidget *combo;
734 GtkListStore *model;
735 GtkCellRenderer *renderer;
736
737 model = create_cart_id_model(mask);
738 if (model == NULL) {
739 return gtk_combo_box_new();
740 }
741 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
742 g_object_unref(model);
743
744 renderer = gtk_cell_renderer_text_new();
745 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
746 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
747 "text", 0, NULL);
748
749 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
750 return combo;
751 }
752
753
754 /** \brief Create combo box with generic VIC-20 cartridges
755 *
756 * \return GtkComboBox
757 */
create_cart_id_combo_box_vic20(void)758 static GtkWidget *create_cart_id_combo_box_vic20(void)
759 {
760 GtkWidget *combo;
761 GtkListStore *model;
762 GtkCellRenderer *renderer;
763
764 model = create_cart_id_model_vic20();
765 if (model == NULL) {
766 return gtk_combo_box_new();
767 }
768 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
769 g_object_unref(model);
770
771 renderer = gtk_cell_renderer_text_new();
772 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
773 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
774 "text", 0, NULL);
775
776 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
777 return combo;
778 }
779
780
781
782 /** \brief Create the 'extra' widget for the dialog
783 *
784 * \return GtkGrid
785 */
create_extra_widget(void)786 static GtkWidget *create_extra_widget(void)
787 {
788 GtkWidget *grid;
789 GtkWidget *label;
790
791 grid = gtk_grid_new();
792 gtk_grid_set_column_spacing(GTK_GRID(grid), 16);
793 gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
794
795 label = gtk_label_new("cartridge type");
796 gtk_widget_set_halign(label, GTK_ALIGN_START);
797 cart_type_widget = create_cart_type_combo_box();
798 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
799 gtk_grid_attach(GTK_GRID(grid), cart_type_widget, 1, 0, 1, 1);
800
801 /* create "Set cartridge as default" check button */
802 switch (machine_class) {
803 case VICE_MACHINE_C64: /* fall through */
804 case VICE_MACHINE_C64SC: /* fall through */
805 case VICE_MACHINE_SCPU64: /* fall through */
806 case VICE_MACHINE_VIC20:
807 cart_set_default_widget = gtk_check_button_new_with_label(
808 "Set cartridge as default");
809 gtk_toggle_button_set_active(
810 GTK_TOGGLE_BUTTON(cart_set_default_widget), FALSE);
811
812 gtk_grid_attach(GTK_GRID(grid), cart_set_default_widget, 0, 1, 4, 1);
813 break;
814 default:
815 /* Set cart as default is not supported for the current machine */
816 break;
817 }
818
819 /* only for c64/c128 */
820 switch (machine_class) {
821 case VICE_MACHINE_C64: /* fall through */
822 case VICE_MACHINE_C64SC: /* fall through */
823 case VICE_MACHINE_C128: /* fall through */
824 case VICE_MACHINE_SCPU64:
825
826 cart_id_label = gtk_label_new("cartridge ID");
827 gtk_widget_set_halign(cart_id_label, GTK_ALIGN_START);
828 cart_id_widget = create_cart_id_combo_box(0x0);
829 gtk_grid_attach(GTK_GRID(grid), cart_id_label, 2, 0, 1, 1);
830 gtk_grid_attach(GTK_GRID(grid), cart_id_widget, 3, 0, 1, 1);
831 break;
832 case VICE_MACHINE_VIC20:
833 cart_id_label = gtk_label_new("cartridge class");
834 gtk_widget_set_halign(cart_id_label, GTK_ALIGN_START);
835 cart_id_widget = create_cart_id_combo_box_vic20();
836 gtk_grid_attach(GTK_GRID(grid), cart_id_label, 2, 0, 1, 1);
837 gtk_grid_attach(GTK_GRID(grid), cart_id_widget, 3, 0, 1, 1);
838 break;
839
840 default:
841 break;
842 }
843
844 gtk_widget_show_all(grid);
845 return grid;
846 }
847
848
849 /** \brief Create the 'preview' widget for the dialog
850 *
851 * \return GtkGrid
852 */
create_preview_widget(void)853 static GtkWidget *create_preview_widget(void)
854 {
855 if ((machine_class != VICE_MACHINE_C64)
856 && (machine_class != VICE_MACHINE_C64SC)) {
857 GtkWidget *grid = NULL;
858 GtkWidget *label;
859 grid = gtk_grid_new();
860 gtk_grid_set_column_spacing(GTK_GRID(grid), 16);
861 gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
862
863 label = gtk_label_new(NULL);
864 gtk_label_set_markup(GTK_LABEL(label), "<b>Cartridge info</b>");
865 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
866
867 label = gtk_label_new("Error: groepaz was here!");
868 g_object_set(label, "margin-left", 16, NULL);
869 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
870
871 gtk_widget_show_all(grid);
872 return grid;
873 } else {
874 return crt_preview_widget_create();
875 }
876
877 }
878
879 /** \brief Update the 'preview' widget for the dialog
880 *
881 * \return GtkGrid
882 */
update_preview(GtkFileChooser * file_chooser,gpointer data)883 static void update_preview(GtkFileChooser *file_chooser, gpointer data)
884 {
885 gchar *path = NULL;
886
887 debug_gtk3("update_preview");
888 path = gtk_file_chooser_get_filename(file_chooser);
889 if (path != NULL) {
890 crt_preview_widget_update(path);
891 g_free(path);
892 }
893 }
894
895 /** \brief Set function to get a list of cartridges
896 *
897 * \param[in] func list function
898 */
uicart_set_list_func(cartridge_info_t * (* func)(void))899 void uicart_set_list_func(cartridge_info_t *(*func)(void))
900 {
901 crt_list_func = func;
902 }
903
904
905
906
907 /** \brief Set function to detect a cartridge's type
908 *
909 * Appears to be CBM2/Plus4 only
910 *
911 * \param[in] func detect function
912 */
uicart_set_detect_func(int (* func)(const char *))913 void uicart_set_detect_func(int (*func)(const char *))
914 {
915 crt_detect_func = func;
916 }
917
918
919 /** \brief Set function to attach a cartridge image
920 *
921 * \param[in] func attach function
922 */
uicart_set_attach_func(int (* func)(int,const char *))923 void uicart_set_attach_func(int (*func)(int, const char *))
924 {
925 crt_attach_func = func;
926 }
927
928
929 /** \brief Set function to trigger a cartridge freeze-button click
930 *
931 * \param[in] func freeze function
932 */
uicart_set_freeze_func(void (* func)(void))933 void uicart_set_freeze_func(void (*func)(void))
934 {
935 crt_freeze_func = func;
936 }
937
938
939 /** \brief Set function to detach a/all cartridges
940 *
941 * \param[in] func freeze function
942 */
uicart_set_detach_func(void (* func)(int))943 void uicart_set_detach_func(void (*func)(int))
944 {
945 crt_detach_func = func;
946 }
947
948
949 /** \brief Set function to set active cart as default
950 *
951 * \param[in] func default func
952 */
uicart_set_default_func(void (* func)(void))953 void uicart_set_default_func(void (*func)(void))
954 {
955 crt_default_func = func;
956 }
957
958
959 /** \brief Set function to get filename of currently attached cart
960 *
961 * \param[in] func filename func
962 */
uicart_set_filename_func(const char * (* func)(void))963 void uicart_set_filename_func(const char * (*func)(void))
964 {
965 crt_filename_func = func;
966 }
967
968
969 /** \brief Set function to wipe internal cart filename to allow resources to work
970 *
971 * \param[in] func wipe func
972 */
uicart_set_wipe_func(void (* func)(void))973 void uicart_set_wipe_func(void (*func)(void))
974 {
975 crt_wipe_func = func;
976 }
977
978
979
980
981
982 /** \brief Try to smart-attach a cartridge image
983 *
984 * \param[in] widget parent widget (unused)
985 * \param[in] user_data extra event data (unused)
986 *
987 * \return TRUE
988 */
uicart_smart_attach_dialog(GtkWidget * widget,gpointer user_data)989 gboolean uicart_smart_attach_dialog(GtkWidget *widget, gpointer user_data)
990 {
991 gchar *filename;
992
993 vsync_suspend_speed_eval();
994 ui_set_ignore_mouse_hide(TRUE);
995
996 filename = vice_gtk3_open_file_dialog(
997 "Smart-attach cartridge image",
998 "Cartridge images",
999 file_chooser_pattern_cart,
1000 last_dir);
1001
1002 if (filename != NULL) {
1003 debug_gtk3("Got filename '%s'.", filename);
1004 lastdir_update(widget, &last_dir);
1005 if (crt_attach_func != NULL) {
1006 if (crt_attach_func(CARTRIDGE_CRT, filename) < 0) {
1007 vice_gtk3_message_error("VICE error",
1008 "Failed to attach '%s' as a cartridge image",
1009 filename);
1010 } else {
1011 debug_gtk3("Attached '%s' as valid cartridge image.", filename);
1012 }
1013 }
1014 g_free(filename);
1015 }
1016
1017 ui_set_ignore_mouse_hide(FALSE);
1018 return TRUE;
1019 }
1020
1021
1022 /** \brief Trigger cartridge freeze
1023 *
1024 * Called from the menu
1025 *
1026 *
1027 * \return TRUE
1028 */
uicart_trigger_freeze(void)1029 gboolean uicart_trigger_freeze(void)
1030 {
1031 if (crt_freeze_func != NULL) {
1032 debug_gtk3("triggering cart freeze.");
1033 crt_freeze_func();
1034 }
1035 return TRUE;
1036 }
1037
1038
1039 /** \brief Detach all cartridge images
1040 *
1041 * TODO: Doesn't work for carts like Expert, these seem to have their own
1042 * resources and not use the CartridgeType/CartridgeFile resources.
1043 *
1044 * \return TRUE
1045 */
uicart_detach(void)1046 gboolean uicart_detach(void)
1047 {
1048 const char *default_cart;
1049 const char *current_cart;
1050
1051 if (crt_detach_func != NULL) {
1052 resources_get_string("CartridgeFile", &default_cart);
1053 debug_gtk3("default cartidge file = '%s'.",
1054 default_cart == NULL || *default_cart == '\0'
1055 ? NULL : default_cart);
1056
1057 if (crt_filename_func != NULL) {
1058 current_cart = crt_filename_func();
1059
1060 debug_gtk3("current cartridge file = '%s'.", current_cart);
1061
1062 if ((current_cart != NULL && default_cart != NULL)) {
1063 if (strcmp(default_cart, current_cart) == 0) {
1064 gboolean result;
1065
1066 debug_gtk3("names match: pop up UI to ask to delete"
1067 " default cart from resources.");
1068
1069 result = vice_gtk3_message_confirm("Detach cartridge",
1070 "You're detaching the default cartridge '%s'.\n\n"
1071 "Would you also like to unregister this cartridge"
1072 " as the default cartridge?",
1073 default_cart);
1074 if (result) {
1075 debug_gtk3("removing default cart (still requires"
1076 " saving resources.");
1077 if (crt_wipe_func != NULL) {
1078 debug_gtk3("wiping internal ctr file name(s).");
1079 crt_wipe_func();
1080 }
1081 /*�does this actually do anything, seeing how I
1082 * need to wipe internal vars in cart code? */
1083 #if 0
1084 if (resources_set_string("CartridgeFile", "") != 0) {
1085 debug_gtk3("failed to set resource.");
1086 }
1087 #endif
1088 }
1089 }
1090 }
1091 debug_gtk3("detaching latest cartridge image.");
1092 crt_detach_func(-1);
1093 }
1094 }
1095 return TRUE;
1096 }
1097
1098 /** \brief Pop up the cart-attach dialog
1099 *
1100 * \param[in] widget parent widget (unused)
1101 * \param[in] data extra event data (unused)
1102 */
uicart_show_dialog(GtkWidget * widget,gpointer data)1103 void uicart_show_dialog(GtkWidget *widget, gpointer data)
1104 {
1105 GtkWidget *dialog;
1106
1107 ui_set_ignore_mouse_hide(TRUE);
1108
1109 dialog = gtk_file_chooser_dialog_new(
1110 "Attach a cartridge image",
1111 ui_get_active_window(),
1112 GTK_FILE_CHOOSER_ACTION_OPEN,
1113 /* buttons */
1114 "Attach", GTK_RESPONSE_ACCEPT,
1115 "Close", GTK_RESPONSE_DELETE_EVENT,
1116 NULL, NULL);
1117
1118 /* set modal so mouse-grab doesn't get triggered */
1119 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1120
1121 /* set last directory */
1122 lastdir_set(dialog, &last_dir);
1123
1124 /* add extra widget */
1125 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog),
1126 create_extra_widget());
1127
1128 /* add preview widget */
1129 cart_preview_widget = create_preview_widget();
1130 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog),
1131 cart_preview_widget);
1132 gtk_file_chooser_set_use_preview_label(GTK_FILE_CHOOSER(dialog), FALSE);
1133
1134 /* add filters */
1135 flt_crt = create_file_chooser_filter(filters[UICART_PATTERN_CRT], FALSE);
1136 flt_bin = create_file_chooser_filter(filters[UICART_PATTERN_BIN], FALSE);
1137 flt_bin_prg = create_file_chooser_filter(filters[UICART_PATTERN_BIN_PRG], FALSE);
1138 flt_all = create_file_chooser_filter(filters[UICART_PATTERN_ALL], TRUE);
1139
1140 switch (machine_class) {
1141 case VICE_MACHINE_C64: /* fall through */
1142 case VICE_MACHINE_C64SC: /* fall through */
1143 case VICE_MACHINE_C128: /* fall through */
1144 case VICE_MACHINE_SCPU64:
1145 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), flt_crt);
1146 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), flt_bin);
1147 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), flt_all);
1148 break;
1149 case VICE_MACHINE_VIC20:
1150 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), flt_bin_prg);
1151 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), flt_all);
1152 break;
1153 default:
1154 break;
1155 }
1156
1157 cart_dialog = dialog;
1158
1159 g_signal_connect(dialog, "response", G_CALLBACK(on_response), NULL);
1160 g_signal_connect(dialog, "update-preview", G_CALLBACK(update_preview), NULL);
1161
1162 /* those should be hidden by default */
1163 if (cart_id_label) {
1164 gtk_widget_hide(cart_id_label);
1165 }
1166 if (cart_id_widget) {
1167 gtk_widget_hide(cart_id_widget);
1168 }
1169
1170 gtk_widget_show(dialog);
1171 }
1172
1173
1174 /** \brief Clean up the last directory string
1175 */
uicart_shutdown(void)1176 void uicart_shutdown(void)
1177 {
1178 lastdir_shutdown(&last_dir);
1179 }
1180