1 /** \file   uifliplist.c
2  * \brief   Fliplist menu management
3  *
4  * \author  Michael C. Martin <mcmartin@gmail.com>
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 
30 #include "attach.h"
31 #include "fliplist.h"
32 #include "lib.h"
33 #include "util.h"
34 #include "filechooserhelpers.h"
35 #include "ui.h"
36 
37 #include "uifliplist.h"
38 
ui_fliplist_add_current_cb(GtkWidget * widget,gpointer data)39 void ui_fliplist_add_current_cb(GtkWidget *widget, gpointer data)
40 {
41     int unit = GPOINTER_TO_INT(data);
42     fliplist_add_image(unit);
43 }
44 
ui_fliplist_remove_current_cb(GtkWidget * widget,gpointer data)45 void ui_fliplist_remove_current_cb(GtkWidget *widget, gpointer data)
46 {
47     int unit = GPOINTER_TO_INT(data);
48     fliplist_remove(unit, NULL);
49 }
50 
ui_fliplist_next_cb(GtkWidget * widget,gpointer data)51 void ui_fliplist_next_cb(GtkWidget *widget, gpointer data)
52 {
53     int unit = GPOINTER_TO_INT(data);
54     fliplist_attach_head(unit, 1);
55 }
56 
ui_fliplist_prev_cb(GtkWidget * widget,gpointer data)57 void ui_fliplist_prev_cb(GtkWidget *widget, gpointer data)
58 {
59     int unit = GPOINTER_TO_INT(data);
60     fliplist_attach_head(unit, 0);
61 }
62 
ui_fliplist_select_cb(GtkWidget * widget,gpointer data)63 static void ui_fliplist_select_cb(GtkWidget *widget, gpointer data)
64 {
65     int unit = GPOINTER_TO_INT(data) & 0xff;
66     int index = (GPOINTER_TO_INT(data) >> 8) & 0xff;
67     int i;
68     if (index == 0) {
69         fliplist_t list = fliplist_init_iterate(unit);
70         const char *image = fliplist_get_image(list);
71         file_system_attach_disk(unit, image);
72     } else {
73         for (i = 0; i < index; ++i) {
74             fliplist_attach_head(unit, 1);
75         }
76     }
77 }
78 
79 /** \brief Fill in a menu with controls for fliplist control.
80  *
81  * Fliplist controls are placed at the end of a menu, after a
82  * separator. Any previously-existing fliplist controls within the
83  * menu will be removed.
84  *
85  * \param menu            The menu to be edited.
86  * \param unit            The drive unit (8-11) that this menu
87  *                        will control.
88  * \param separator_count The number of menu separators in the
89  *                        part of the menu that does not involve
90  *                        the fliplist.
91  */
ui_populate_fliplist_menu(GtkWidget * menu,int unit,int separator_count)92 void ui_populate_fliplist_menu(GtkWidget *menu, int unit, int separator_count)
93 {
94     const char *fliplist_string;
95     GtkWidget *menu_item;
96     GList *children = gtk_container_get_children(GTK_CONTAINER(menu));
97     GList *child_iter = g_list_first(children);
98     int separators_so_far = 0;
99     while (child_iter) {
100         if (GTK_IS_SEPARATOR_MENU_ITEM(child_iter->data)) {
101             ++separators_so_far;
102         }
103         if (separators_so_far > separator_count) {
104             gtk_container_remove(GTK_CONTAINER(menu), child_iter->data);
105         }
106         child_iter = child_iter->next;
107     }
108     g_list_free(children);
109 
110     /* Fliplist controls in GTK2/GNOME are next/previous and then the
111      * full list of entries within it. For GTK3 we only show these if
112      * the fliplist isn't empty for this drive. */
113     /* TODO: Add/Remove current image to/from fliplist should really
114      * be here too. */
115     fliplist_string = fliplist_get_next(unit);
116     if (fliplist_string) {
117         char buf[128];
118         char *basename = NULL;
119         fliplist_t fliplist_iterator;
120         int index;
121         gtk_container_add(GTK_CONTAINER(menu), gtk_separator_menu_item_new());
122         util_fname_split(fliplist_string, NULL, &basename);
123         snprintf(buf, 128, "Next: %s", basename ? basename : fliplist_string);
124         lib_free(basename);
125         basename = NULL;
126         buf[127] = 0;
127         menu_item = gtk_menu_item_new_with_label(buf);
128         g_signal_connect(menu_item, "activate", G_CALLBACK(ui_fliplist_next_cb), GINT_TO_POINTER(unit));
129         gtk_container_add(GTK_CONTAINER(menu), menu_item);
130         fliplist_string = fliplist_get_prev(unit);
131         if (fliplist_string) {
132             util_fname_split(fliplist_string, NULL, &basename);
133             snprintf(buf, 128, "Previous: %s", basename ? basename : fliplist_string);
134             lib_free(basename);
135             basename = NULL;
136             buf[127] = 0;
137             menu_item = gtk_menu_item_new_with_label(buf);
138             g_signal_connect(menu_item, "activate", G_CALLBACK(ui_fliplist_prev_cb), GINT_TO_POINTER(unit));
139             gtk_container_add(GTK_CONTAINER(menu), menu_item);
140         }
141         gtk_container_add(GTK_CONTAINER(menu), gtk_separator_menu_item_new());
142         fliplist_iterator = fliplist_init_iterate(unit);
143         index = 0;
144         while (fliplist_iterator) {
145             fliplist_string = fliplist_get_image(fliplist_iterator);
146             util_fname_split(fliplist_string, NULL, &basename);
147             menu_item = gtk_menu_item_new_with_label(basename ? basename : fliplist_string);
148             lib_free(basename);
149             basename = NULL;
150             g_signal_connect(menu_item, "activate", G_CALLBACK(ui_fliplist_select_cb), GINT_TO_POINTER(unit+(index << 8)));
151             gtk_container_add(GTK_CONTAINER(menu), menu_item);
152             fliplist_iterator = fliplist_next_iterate(unit);
153             ++index;
154         }
155     }
156 }
157 
fliplist_load_response(GtkWidget * widget,gint response_id,gpointer user_data)158 static void fliplist_load_response(GtkWidget *widget, gint response_id, gpointer user_data)
159 {
160     int unit = GPOINTER_TO_INT(user_data);
161     gchar *filename;
162     filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
163     if (response_id == GTK_RESPONSE_ACCEPT) {
164         /* TODO: read autoattach out of an extra widget. */
165         /* TODO: Autoattach looks slightly buggy in fliplist.c */
166         fliplist_load_list(unit, filename, 0);
167     }
168     gtk_widget_destroy(widget);
169 }
170 
171 /** \brief   Create and show the "load fliplist" dialog.
172  *
173  *  \param   parent     Widget that sent the event
174  *  \param   data       Drive to load fliplist to, or FLIPLIST_ALL_UNITS
175  */
ui_fliplist_load_callback(GtkWidget * parent,gpointer data)176 void ui_fliplist_load_callback(GtkWidget *parent, gpointer data)
177 {
178     GtkWidget *dialog;
179     unsigned int unit = (unsigned int)GPOINTER_TO_INT(data);
180     if (unit != FLIPLIST_ALL_UNITS && (unit < 8 || unit > 11)) {
181         return;
182     }
183     dialog = gtk_file_chooser_dialog_new(
184         "Select flip list file",
185         ui_get_active_window(),
186         GTK_FILE_CHOOSER_ACTION_OPEN,
187         /* buttons */
188         "Open", GTK_RESPONSE_ACCEPT,
189         "Cancel", GTK_RESPONSE_REJECT,
190         NULL, NULL);
191     /* TODO: add a separate "extra widget" that will let the user
192      * select autoattach */
193     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
194             create_file_chooser_filter(file_chooser_filter_fliplist, FALSE));
195     g_signal_connect(dialog, "response", G_CALLBACK(fliplist_load_response), data);
196     gtk_widget_show_all(dialog);
197 }
198 
fliplist_save_response(GtkWidget * widget,gint response_id,gpointer user_data)199 static void fliplist_save_response(GtkWidget *widget, gint response_id, gpointer user_data)
200 {
201     int unit = GPOINTER_TO_INT(user_data);
202     gchar *filename;
203     filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
204     if (response_id == GTK_RESPONSE_ACCEPT) {
205         fliplist_save_list(unit, filename);
206     }
207     gtk_widget_destroy(widget);
208 }
209 
210 /** \brief   Create and show the "save fliplist" dialog.
211  *
212  *  \param   parent     Widget that sent the event
213  *  \param   data       Drive to save fliplist from, or FLIPLIST_ALL_UNITS
214  */
ui_fliplist_save_callback(GtkWidget * parent,gpointer data)215 void ui_fliplist_save_callback(GtkWidget *parent, gpointer data)
216 {
217     GtkWidget *dialog;
218     unsigned int unit = (unsigned int)GPOINTER_TO_INT(data);
219     if (unit != FLIPLIST_ALL_UNITS && (unit < 8 || unit > 11)) {
220         return;
221     }
222     dialog = gtk_file_chooser_dialog_new(
223         "Select flip list file",
224         ui_get_active_window(),
225         GTK_FILE_CHOOSER_ACTION_SAVE,
226         /* buttons */
227         "Save", GTK_RESPONSE_ACCEPT,
228         "Cancel", GTK_RESPONSE_REJECT,
229         NULL, NULL);
230     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
231             create_file_chooser_filter(file_chooser_filter_fliplist, FALSE));
232     gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
233     g_signal_connect(dialog, "response", G_CALLBACK(fliplist_save_response), data);
234     gtk_widget_show_all(dialog);
235 }
236