1 /** \file   uivsidmenu.c
2  * \brief   Native GTK3 menus for the SID player, vsid
3  *
4  * \author  Marcus Sutton <loggedoubt@gmail.com>
5  * \author  Bas Wassink <b.wassink@ziggo.nl>
6  */
7 
8 /*
9  * $VICERES PSIDKeepEnv     vsid
10  * $VICERES MainCPU_TRACE   vsid
11  * $VICERES DoCoreDump      vsid
12  */
13 
14 /*
15  * This file is part of VICE, the Versatile Commodore Emulator.
16  * See README for copyright notice.
17  *
18  *  This program is free software; you can redistribute it and/or modify
19  *  it under the terms of the GNU General Public License as published by
20  *  the Free Software Foundation; either version 2 of the License, or
21  *  (at your option) any later version.
22  *
23  *  This program is distributed in the hope that it will be useful,
24  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *  GNU General Public License for more details.
27  *
28  *  You should have received a copy of the GNU General Public License
29  *  along with this program; if not, write to the Free Software
30  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
31  *  02111-1307  USA.
32  *
33  */
34 
35 
36 #include "vice.h"
37 
38 #include <stdlib.h>
39 #include <gtk/gtk.h>
40 
41 #include "debug.h"
42 #include "lib.h"
43 #include "machine.h"
44 #include "psid.h"
45 #include "ui.h"
46 #include "uiabout.h"
47 #include "uicmdline.h"
48 #include "uicommands.h"
49 #include "uicompiletimefeatures.h"
50 #include "uidebug.h"
51 #include "uimedia.h"
52 #include "uimenu.h"
53 #include "uimonarch.h"
54 #include "uisettings.h"
55 #include "uisidattach.h"
56 #include "uismartattach.h"
57 #include "uivsidmenu.h"
58 
59 
60 /*
61  * The following are translation unit local so we can create functions that
62  * modify menu contents or even functions that alter the top bar itself.
63  */
64 
65 
66 /** \brief  Main menu bar widget
67  *
68  * Contains the submenus on the menu main bar
69  *
70  * This one lives until ui_exit() or thereabouts
71  */
72 static GtkWidget *main_menu_bar = NULL;
73 
74 
75 /** \brief  File submenu
76  */
77 static GtkWidget *file_submenu = NULL;
78 
79 
80 /** \brief  Tune submenu
81  */
82 static GtkWidget *tune_submenu = NULL;
83 
84 
85 /** \brief  Settings submenu
86  */
87 static GtkWidget *settings_submenu = NULL;
88 
89 
90 #ifdef DEBUG
91 /** \brief  Debug submenu, only available when --enable-debug was specified
92  */
93 static GtkWidget *debug_submenu = NULL;
94 #endif
95 
96 
97 /** \brief  Help submenu
98  */
99 static GtkWidget *help_submenu = NULL;
100 
101 
102 /** \brief  List of items in the tune submenu
103  */
104 static GSList *tune_submenu_group = NULL;
105 
106 
107 /** \brief  File->Reset submenu
108  */
109 static ui_menu_item_t reset_submenu[] = {
110     { "Soft reset", UI_MENU_TYPE_ITEM_ACTION,
111         "reset-soft", ui_machine_reset_callback, GINT_TO_POINTER(MACHINE_RESET_MODE_SOFT),
112         GDK_KEY_F9, VICE_MOD_MASK },
113     { "Hard reset", UI_MENU_TYPE_ITEM_ACTION,
114         "reset-hard", ui_machine_reset_callback, GINT_TO_POINTER(MACHINE_RESET_MODE_HARD),
115         GDK_KEY_F12, VICE_MOD_MASK },
116 
117     UI_MENU_TERMINATOR
118 };
119 
120 
121 /** \brief  'File' menu
122  */
123 static ui_menu_item_t file_menu[] = {
124     { "Load PSID file ...", UI_MENU_TYPE_ITEM_ACTION,
125         "load-psid", uisidattach_show_dialog, NULL,
126         GDK_KEY_L, VICE_MOD_MASK },
127 
128     UI_MENU_SEPARATOR,
129 
130     /* XXX: this item might need its own dialog that only
131      *      contains sound recording options
132      */
133     { "Record sound file ...", UI_MENU_TYPE_ITEM_ACTION,
134         "sound-save", uimedia_dialog_show, NULL,
135         GDK_KEY_R, VICE_MOD_MASK | GDK_SHIFT_MASK },
136 
137     { "Stop sound recording", UI_MENU_TYPE_ITEM_ACTION,
138         "sound-stop", (void *)uimedia_stop_recording, NULL,
139         GDK_KEY_S, VICE_MOD_MASK | GDK_SHIFT_MASK },
140 
141     UI_MENU_SEPARATOR,
142 
143     /* monitor */
144     { "Activate monitor", UI_MENU_TYPE_ITEM_ACTION,
145         "monitor", ui_monitor_activate_callback, NULL,
146 #ifdef MACOSX_SUPPORT
147         /* use Command-Option-M on Mac */
148         GDK_KEY_M, VICE_MOD_MASK | GDK_MOD1_MASK
149 #else
150         GDK_KEY_H, VICE_MOD_MASK
151 #endif
152     },
153 
154     UI_MENU_SEPARATOR,
155 
156     { "Reset", UI_MENU_TYPE_SUBMENU,
157         NULL, NULL, reset_submenu,
158         0, 0 },
159 
160     UI_MENU_SEPARATOR,
161 
162     { "Exit player", UI_MENU_TYPE_ITEM_ACTION,
163         "exit", ui_close_callback, NULL,
164         GDK_KEY_Q, VICE_MOD_MASK },
165 
166     UI_MENU_TERMINATOR
167 };
168 
169 
170 /** \brief  'Tune' menu
171  */
172 #if 0
173 static ui_menu_item_t tune_menu[] = {
174 
175     UI_MENU_TERMINATOR
176 };
177 #endif
178 
179 
180 /** \brief  'Settings' menu
181  */
182 static ui_menu_item_t settings_menu[] = {
183     /* XXX: this item should perhaps be removed and its functionality
184      *      added to the settings dialog
185      */
186     { "Override PSID settings", UI_MENU_TYPE_ITEM_CHECK,
187         "psid-keep-env", (void *)(ui_toggle_resource), (void *)"PSIDKeepEnv",
188         0, 0 },
189 
190     UI_MENU_SEPARATOR,
191 
192     /* the settings dialog */
193     { "Settings ...", UI_MENU_TYPE_ITEM_ACTION,
194         "settings", (void *)ui_settings_dialog_create, NULL,
195         GDK_KEY_O, VICE_MOD_MASK },
196 
197     UI_MENU_TERMINATOR
198 };
199 
200 
201 /** \brief  'Debug' menu items
202  */
203 #ifdef DEBUG
204 static ui_menu_item_t debug_menu[] = {
205     { "Trace mode ...", UI_MENU_TYPE_ITEM_ACTION,
206         "tracemode", uidebug_trace_mode_callback, NULL,
207         0, 0 },
208 
209     UI_MENU_SEPARATOR,
210 
211     { "Main CPU trace", UI_MENU_TYPE_ITEM_CHECK,
212         "trace-maincpu", (void *)(ui_toggle_resource), (void *)"MainCPU_TRACE",
213         0, 0 },
214 
215     UI_MENU_SEPARATOR,
216 
217 
218     { "Autoplay playback frames ...", UI_MENU_TYPE_ITEM_ACTION,
219         "playframes", uidebug_playback_frames_callback, NULL,
220         0, 0 },
221     { "Save core dump", UI_MENU_TYPE_ITEM_CHECK,
222         "coredump", (void *)(ui_toggle_resource), (void *)"DoCoreDump",
223         0, 0 },
224 
225     UI_MENU_TERMINATOR
226 };
227 #endif
228 
229 
230 /** \brief  'Help' menu items
231  */
232 static ui_menu_item_t help_menu[] = {
233     { "Browse manual", UI_MENU_TYPE_ITEM_ACTION,
234         "manual", ui_open_manual_callback, NULL,
235         0, 0 },
236     { "Command line options ...", UI_MENU_TYPE_ITEM_ACTION,
237         "cmdline", uicmdline_dialog_show, NULL,
238         0, 0 },
239     { "Compile time features ...", UI_MENU_TYPE_ITEM_ACTION,
240         "features", uicompiletimefeatures_dialog_show, NULL,
241         0, 0 },
242     { "About VICE", UI_MENU_TYPE_ITEM_ACTION,
243         "about", ui_about_dialog_callback, NULL,
244         0, 0 },
245 
246     UI_MENU_TERMINATOR
247 };
248 
249 /** \brief  Play a tune when selected with the Tune menu
250  *
251  * \return  void
252  */
select_tune_from_menu(GtkMenuItem * menuitem,gpointer user_data)253 static void select_tune_from_menu(GtkMenuItem *menuitem,
254                                   gpointer     user_data) {
255     int tune;
256 
257     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
258         return;
259     }
260     tune = GPOINTER_TO_INT(user_data);
261     psid_init_driver();
262     machine_play_psid(tune);
263     machine_trigger_reset(MACHINE_RESET_MODE_SOFT);
264 }
265 
266 
267 /** \brief  Remove each of the old menu items before adding the new ones
268  *
269  * \return  void
270  */
remove_item_from_menu(GtkWidget * widget,gpointer data)271 static void remove_item_from_menu (GtkWidget *widget, gpointer data) {
272     gtk_widget_destroy(widget);
273 }
274 
275 /** \brief  Create the tune menu when a new PSID is loaded
276  *
277  * \return  void
278  */
ui_vsid_tune_menu_set_tune_count(int count)279 void ui_vsid_tune_menu_set_tune_count(int count) {
280     GtkWidget *item = NULL;
281     long i;
282     char *buf;
283 
284     gtk_container_foreach(GTK_CONTAINER(tune_submenu), remove_item_from_menu, NULL);
285     for (i = count; i >0; i--) {
286         buf = lib_msprintf("Tune %s%d", i < 10 ? "_" :"", i);
287         item = gtk_radio_menu_item_new_with_mnemonic_from_widget (GTK_RADIO_MENU_ITEM(item), buf);
288         lib_free(buf);
289         gtk_widget_show(item);
290         g_signal_connect(
291             item,
292             "activate",
293             G_CALLBACK(select_tune_from_menu),
294             GINT_TO_POINTER(i));
295         gtk_menu_shell_prepend(GTK_MENU_SHELL(tune_submenu), item);
296     }
297     tune_submenu_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
298 }
299 
300 
301 /** \brief  Ensure the correct menu element is selected when the current tune is changed by anything other than the menu
302  *
303  * \return  void
304  */
ui_vsid_tune_set_tune_current(int count)305 void ui_vsid_tune_set_tune_current(int count) {
306     if (tune_submenu_group) {
307         gpointer nth_item = g_slist_nth_data(tune_submenu_group, (guint)count - 1);
308         if (nth_item) {
309             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (nth_item), TRUE);
310         }
311     }
312 }
313 
314 /** \brief  Create the top menu bar with standard submenus
315  *
316  * \return  GtkMenuBar
317  */
ui_vsid_menu_bar_create(void)318 GtkWidget *ui_vsid_menu_bar_create(void)
319 {
320     GtkWidget *menu_bar;
321 
322     /* create the top menu bar */
323     menu_bar = gtk_menu_bar_new();
324 
325     /* create the top-level 'File' menu */
326     file_submenu = ui_menu_submenu_create(menu_bar, "File");
327 
328     /* create the top-level 'Tune' menu */
329     tune_submenu = ui_menu_submenu_create(menu_bar, "Tune");
330 
331     /* create the top-level 'Settings' menu */
332     settings_submenu = ui_menu_submenu_create(menu_bar, "Settings");
333 
334 #ifdef DEBUG
335     /* create the top-level 'Debug' menu (when --enable-debug is used) */
336     debug_submenu = ui_menu_submenu_create(menu_bar, "Debug");
337 #endif
338 
339     /* create the top-level 'Help' menu */
340     help_submenu = ui_menu_submenu_create(menu_bar, "Help");
341 
342 
343     /* add items to the File menu */
344     ui_menu_add(file_submenu, file_menu);
345 
346 #if 0
347     /* TODO: add items to the Tune menu */
348     ui_menu_add(tune_submenu, tune_menu);
349 #endif
350 
351     /* add items to the Settings menu */
352     ui_menu_add(settings_submenu, settings_menu);
353 
354 #ifdef DEBUG
355     /* add items to the Debug menu */
356     ui_menu_add(debug_submenu, debug_menu);
357 #endif
358 
359     /* add items to the Help menu */
360     ui_menu_add(help_submenu, help_menu);
361 
362     main_menu_bar = menu_bar;    /* XXX: do I need g_object_ref()/g_object_unref()
363                                          for this */
364     return menu_bar;
365 }
366