1 /** \file uidiskcreate.c
2 * \brief Gtk3 dialog to create and attach a new disk image
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 #include "vice.h"
28 #include <gtk/gtk.h>
29 #include <string.h>
30
31 #include "basewidgets.h"
32 #include "basedialogs.h"
33 #include "debug_gtk3.h"
34 #include "widgethelpers.h"
35 #include "driveunitwidget.h"
36 #include "diskimage.h"
37 #include "filechooserhelpers.h"
38 #include "util.h"
39 #include "lib.h"
40 #include "charset.h"
41 #include "attach.h"
42 #include "vdrive/vdrive-internal.h"
43 #include "imagecontents.h"
44 #include "resources.h"
45 #include "ui.h"
46
47 #include "uidiskcreate.h"
48
49
50 /** \brief Struct holding image type names and IDs
51 */
52 typedef struct disk_image_type_s {
53 const char *name; /**< name (ext) */
54 int id; /**< image type ID */
55 } disk_image_type_t;
56
57
58 /* forward declaration */
59 static gboolean create_disk_image(const char *filename);
60
61
62 /** \brief List of supported disk image types
63 *
64 * XXX: perhaps some function in diskimage.c or so producing a list of
65 * currently supported images types like this one would be better, that
66 * would avoid having to update UI's when a new image type is added or
67 * removed.
68 */
69 static disk_image_type_t disk_image_types[] = {
70 { "d64", DISK_IMAGE_TYPE_D64 },
71 { "d67", DISK_IMAGE_TYPE_D67 },
72 { "d71", DISK_IMAGE_TYPE_D71 },
73 { "d80", DISK_IMAGE_TYPE_D80 },
74 { "d81", DISK_IMAGE_TYPE_D81 },
75 { "d82", DISK_IMAGE_TYPE_D82 },
76 { "d1m", DISK_IMAGE_TYPE_D1M },
77 { "d2m", DISK_IMAGE_TYPE_D2M },
78 { "d4m", DISK_IMAGE_TYPE_D4M },
79 { "g64", DISK_IMAGE_TYPE_G64 },
80 { "g71", DISK_IMAGE_TYPE_G71 },
81 { "p64", DISK_IMAGE_TYPE_P64 },
82 { "x64", DISK_IMAGE_TYPE_X64 },
83 { NULL, -1 }
84 };
85
86
87 /** \brief Drive unit to attach image to */
88 static int unit_number = 8;
89 /** \brief Disk image type to create */
90 static int image_type = 1541;
91
92 /** \brief GtkEntry containing the disk name */
93 static GtkWidget *disk_name;
94 /** \brief GtkEntry containing the disk ID */
95 static GtkWidget *disk_id;
96 /** \brief Set drive type when attaching */
97 static GtkWidget *set_drive_type;
98
99
100 /** \brief Handler for 'response' event of the dialog
101 *
102 * This handler is called when the user clicks a button in the dialog.
103 *
104 * \param[in] widget the dialog
105 * \param[in] response_id response ID
106 * \param[in] data extra data (unused)
107 */
on_response(GtkWidget * widget,gint response_id,gpointer data)108 static void on_response(GtkWidget *widget, gint response_id, gpointer data)
109 {
110 gchar *filename;
111 int status = TRUE;
112
113 switch (response_id) {
114
115 case GTK_RESPONSE_ACCEPT:
116 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
117 if (filename != NULL) {
118 status = create_disk_image(filename);
119 }
120 g_free(filename);
121 if (status) {
122 /* image creation and attaching was succesful, exit dialog */
123 gtk_widget_destroy(widget);
124 }
125 break;
126
127 case GTK_RESPONSE_REJECT:
128 gtk_widget_destroy(widget);
129 break;
130 default:
131 debug_gtk3("warning: unhandled response ID %d\n", response_id);
132 break;
133 }
134 }
135
136
137 /** \brief Handler for the 'changed' event of the image type combo box
138 *
139 * \param[in] combo combo box
140 * \param[in] data extra event data (unused)
141 *
142 */
on_disk_image_type_changed(GtkComboBox * combo,gpointer data)143 static void on_disk_image_type_changed(GtkComboBox *combo, gpointer data)
144 {
145 GtkTreeModel *model;
146 GtkTreeIter iter;
147
148 if (gtk_combo_box_get_active(combo) >= 0) {
149 model = gtk_combo_box_get_model(combo);
150 if (gtk_combo_box_get_active_iter(combo, &iter)) {
151 gtk_tree_model_get(model, &iter, 1, &image_type, -1);
152 debug_gtk3("got disk image type %d\n", image_type);
153 }
154 }
155 }
156
157
attempt_to_set_drive_type(void)158 static gboolean attempt_to_set_drive_type(void)
159 {
160 if (resources_set_int_sprintf("Drive%dType", image_type, unit_number) < 0) {
161 debug_gtk3("failed to set Drive%dType to %d\n", unit_number, image_type);
162 return FALSE;
163 }
164 return TRUE;
165 }
166
167
168 /** \brief Get the extension for image \a type
169 *
170 * \param[in] type image type
171 *
172 * \return extension or `NULL` when not found
173 */
get_ext_by_image_type(int type)174 static const char *get_ext_by_image_type(int type)
175 {
176 int i = 0;
177
178 for (i = 0; disk_image_types[i].name != NULL; i++) {
179 if (disk_image_types[i].id == type) {
180 return disk_image_types[i].name;
181 }
182 }
183 return NULL;
184 }
185
186
187 /** \brief Actually create the disk image and attach it
188 *
189 * \param[in] filename filename of the new image
190 *
191 * \return bool
192 */
create_disk_image(const char * filename)193 static gboolean create_disk_image(const char *filename)
194 {
195 char *fname_copy;
196 char name_vice[IMAGE_CONTENTS_NAME_LEN + 1];
197 char id_vice[IMAGE_CONTENTS_ID_LEN + 1];
198 const char *name_gtk3;
199 const char *id_gtk3;
200 char *vdr_text;
201 int status = TRUE;
202
203 memset(name_vice, 0, IMAGE_CONTENTS_NAME_LEN + 1);
204 memset(id_vice, 0, IMAGE_CONTENTS_ID_LEN + 1);
205 name_gtk3 = gtk_entry_get_text(GTK_ENTRY(disk_name));
206 id_gtk3 = gtk_entry_get_text(GTK_ENTRY(disk_id));
207
208 /* fix extension of filename */
209 fname_copy = util_add_extension_const(filename,
210 get_ext_by_image_type(image_type));
211
212 /* convert name & ID to PETSCII */
213 if (name_gtk3 != NULL && *name_gtk3 != '\0') {
214 strncpy(name_vice, name_gtk3, IMAGE_CONTENTS_NAME_LEN);
215 charset_petconvstring((unsigned char *)name_vice, 0);
216 }
217 if (id_gtk3 != NULL && *id_gtk3 != '\0') {
218 strncpy(id_vice, id_gtk3, IMAGE_CONTENTS_ID_LEN);
219 charset_petconvstring((unsigned char *)id_vice, 0);
220 } else {
221 strcpy(id_vice, "00");
222 }
223
224 vdr_text = util_concat(name_vice, ",", id_vice, NULL);
225 #if 0
226 vice_gtk3_message_info("Creating disk image",
227 "Attaching '%s' at unit #%d, type %d, name '%s', ID '%s'\n"
228 "Passing \"%s\" to vdrive",
229 filename, unit_number, image_type, name_gtk3, id_gtk3,
230 vdr_text);
231 #endif
232
233 /* create image */
234 if (vdrive_internal_create_format_disk_image(fname_copy, vdr_text,
235 image_type) < 0) {
236 vice_gtk3_message_error("Fail", "Could not create image '%s'",
237 fname_copy);
238 status = FALSE;
239 } else {
240 /* do we need to attempt to set the proper drive type? */
241 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_drive_type))) {
242 /* try to set the proper drive type, but keep going if it fails */
243 if (!attempt_to_set_drive_type()) {
244 vice_gtk3_message_error("Core error",
245 "Failed to set drive type to %d\nContinuing.",
246 image_type);
247 }
248 }
249
250 /* finally attach the disk image */
251 if (file_system_attach_disk(unit_number, fname_copy) < 0) {
252 vice_gtk3_message_error("fail", "Could not attach image '%s'",
253 fname_copy);
254 status = FALSE;
255 }
256 }
257
258 lib_free(fname_copy);
259 lib_free(vdr_text);
260 return status;
261 }
262
263
264
265
266 /** \brief Create model for the image type combo box
267 *
268 * \return model
269 */
create_disk_image_type_model(void)270 static GtkListStore *create_disk_image_type_model(void)
271 {
272 GtkListStore *model;
273 GtkTreeIter iter;
274 int i;
275
276 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
277 for (i = 0; disk_image_types[i].name != NULL; i++) {
278 gtk_list_store_append(model, &iter);
279 gtk_list_store_set(model, &iter,
280 0, disk_image_types[i].name,
281 1, disk_image_types[i].id,
282 -1);
283 }
284 return model;
285 }
286
287
288 /** \brief Create combo box with image types
289 *
290 * \return GtkComboBox
291 */
create_disk_image_type_widget(void)292 static GtkWidget *create_disk_image_type_widget(void)
293 {
294 GtkWidget *combo;
295 GtkListStore *model;
296 GtkCellRenderer *renderer;
297
298 model = create_disk_image_type_model();
299 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
300 renderer = gtk_cell_renderer_text_new();
301 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
302 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
303 "text", 0, NULL);
304 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
305
306 g_signal_connect(combo, "changed", G_CALLBACK(on_disk_image_type_changed),
307 NULL);
308 return combo;
309 }
310
311
312 /** \brief Create the 'extra' widget for the dialog
313 *
314 * \param[in] parent parent widget (dialog, unused at the moment)
315 * \param[in] unit default unit number (unused)
316 *
317 * \return GtkGrid
318 */
create_extra_widget(GtkWidget * parent,int unit)319 static GtkWidget *create_extra_widget(GtkWidget *parent, int unit)
320 {
321 GtkWidget *grid;
322 GtkWidget *unit_widget;
323 GtkWidget *type_widget;
324 GtkWidget *label;
325
326 /* create a grid with some spacing and margins */
327 grid = vice_gtk3_grid_new_spaced(VICE_GTK3_DEFAULT, VICE_GTK3_DEFAULT);
328 g_object_set(grid, "margin-left", 16, "margin-right", 16, NULL);
329
330 /* add unit selection widget */
331 unit_widget = drive_unit_widget_create(unit, &unit_number, NULL);
332 gtk_widget_set_valign(unit_widget, GTK_ALIGN_CENTER);
333 gtk_grid_attach(GTK_GRID(grid), unit_widget, 0, 0, 1, 1);
334
335 /* disk name */
336 label = gtk_label_new("Name:");
337 gtk_widget_set_halign(label, GTK_ALIGN_START);
338 disk_name = gtk_entry_new();
339 gtk_entry_set_width_chars(GTK_ENTRY(disk_name), IMAGE_CONTENTS_NAME_LEN);
340 gtk_entry_set_max_length(GTK_ENTRY(disk_name), IMAGE_CONTENTS_NAME_LEN);
341 gtk_grid_attach(GTK_GRID(grid), label, 1, 0, 1, 1);
342 gtk_grid_attach(GTK_GRID(grid), disk_name, 2, 0, 1, 1);
343
344 /* disk ID */
345 label = gtk_label_new("ID:");
346 gtk_widget_set_halign(label, GTK_ALIGN_START);
347 disk_id = gtk_entry_new();
348 gtk_entry_set_width_chars(GTK_ENTRY(disk_id), IMAGE_CONTENTS_ID_LEN);
349 gtk_entry_set_max_length(GTK_ENTRY(disk_id), IMAGE_CONTENTS_ID_LEN);
350 gtk_grid_attach(GTK_GRID(grid), label, 3, 0, 1, 1);
351 gtk_grid_attach(GTK_GRID(grid), disk_id, 4, 0, 1, 1);
352
353 /* add image type selection widget */
354 label = gtk_label_new("Type:");
355 type_widget = create_disk_image_type_widget();
356 gtk_grid_attach(GTK_GRID(grid), label, 5, 0, 1, 1);
357 gtk_grid_attach(GTK_GRID(grid), type_widget, 6, 0, 1, 1);
358
359 /* add 'set drive type for attached image' checkbox */
360 set_drive_type = gtk_check_button_new_with_label(
361 "Set proper drive type when attaching image");
362 /* disable by default */
363 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_drive_type), FALSE);
364 gtk_grid_attach(GTK_GRID(grid), set_drive_type, 0, 1, 4, 1);
365
366 gtk_widget_show_all(grid);
367 return grid;
368 }
369
370
371 /** \brief Create and show 'attach new disk image' dialog
372 *
373 */
uidiskcreate_dialog_show(GtkWidget * parent,gpointer data)374 void uidiskcreate_dialog_show(GtkWidget *parent, gpointer data)
375 {
376 GtkWidget *dialog;
377 GtkFileFilter *filter;
378 int unit;
379
380 unit = GPOINTER_TO_INT(data);
381 /* TODO: stuff some UNIT_MIN/UNIT_MAX defines in some file */
382 if (unit < 8 || unit > 11) {
383 unit = 8;
384 }
385 unit_number = unit;
386
387 dialog = gtk_file_chooser_dialog_new(
388 "Create and attach a new disk image",
389 ui_get_active_window(),
390 GTK_FILE_CHOOSER_ACTION_SAVE,
391 /* buttons */
392 "Save", GTK_RESPONSE_ACCEPT,
393 "Close", GTK_RESPONSE_REJECT,
394 NULL, NULL);
395
396 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog),
397 create_extra_widget(dialog, unit));
398
399 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
400 TRUE);
401
402 filter = create_file_chooser_filter(file_chooser_filter_disk, FALSE);
403 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
404
405 g_signal_connect(dialog, "response", G_CALLBACK(on_response), NULL);
406
407 gtk_widget_show(dialog);
408 }
409