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