1 /* ui.c: User interface routines, but those which are independent of any UI
2    Copyright (c) 2002-2015 Philip Kendall
3    Copyright (c) 2016 Sergio Baldoví
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation, Inc.,
17    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19    Author contact information:
20 
21    E-mail: philip-fuse@shadowmagic.org.uk
22 
23 */
24 
25 #include <config.h>
26 
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include <libspectrum.h>
33 
34 #include "fuse.h"
35 #include "peripherals/if1.h"
36 #include "peripherals/kempmouse.h"
37 #include "settings.h"
38 #include "tape.h"
39 #include "ui/ui.h"
40 #include "ui/uimedia.h"
41 #include "ui/widget/widget.h"
42 
43 #define MESSAGE_MAX_LENGTH 256
44 
45 /* We don't start in a widget */
46 int ui_widget_level = -1;
47 
48 static char last_message[ MESSAGE_MAX_LENGTH ] = "";
49 static size_t frames_since_last_message = 0;
50 
51 static int
52 print_error_to_stderr( ui_error_level severity, const char *message );
53 
54 int
ui_error(ui_error_level severity,const char * format,...)55 ui_error( ui_error_level severity, const char *format, ... )
56 {
57   int error;
58   va_list ap;
59 
60   va_start( ap, format );
61   error = ui_verror( severity, format, ap );
62   va_end( ap );
63 
64   return error;
65 }
66 
67 int
ui_verror(ui_error_level severity,const char * format,va_list ap)68 ui_verror( ui_error_level severity, const char *format, va_list ap )
69 {
70   char message[ MESSAGE_MAX_LENGTH ];
71 
72   vsnprintf( message, MESSAGE_MAX_LENGTH, format, ap );
73 
74   /* Skip the message if the same message was displayed recently */
75   if( frames_since_last_message < 50 && !strcmp( message, last_message ) ) {
76     frames_since_last_message = 0;
77     return 0;
78   }
79 
80   /* And store the 'last message' */
81   strncpy( last_message, message, MESSAGE_MAX_LENGTH );
82   last_message[ MESSAGE_MAX_LENGTH - 1 ] = '\0';
83 
84   print_error_to_stderr( severity, message );
85 
86   /* Do any UI-specific bits as well */
87   ui_error_specific( severity, message );
88 
89   return 0;
90 }
91 
92 ui_confirm_save_t
ui_confirm_save(const char * format,...)93 ui_confirm_save( const char *format, ... )
94 {
95   va_list ap;
96   char message[ MESSAGE_MAX_LENGTH ];
97   ui_confirm_save_t confirm;
98 
99   va_start( ap, format );
100 
101   vsnprintf( message, MESSAGE_MAX_LENGTH, format, ap );
102   confirm = ui_confirm_save_specific( message );
103 
104   va_end( ap );
105 
106   return confirm;
107 }
108 
109 static int
print_error_to_stderr(ui_error_level severity,const char * message)110 print_error_to_stderr( ui_error_level severity, const char *message )
111 {
112   /* Print the error to stderr if it's more significant than just
113      informational */
114   if( severity > UI_ERROR_INFO ) {
115 
116     /* For the fb and svgalib UIs, we don't want to write to stderr if
117        it's a terminal, as it's then likely to be what we're currently
118        using for graphics output, and writing text to it isn't a good
119        idea. Things are OK if we're exiting though */
120 #if defined( UI_FB ) || defined( UI_SVGA )
121     if( isatty( STDERR_FILENO ) && !fuse_exiting ) return 1;
122 #endif			/* #if defined( UI_FB ) || defined( UI_SVGA ) */
123 
124     fprintf( stderr, "%s: ", fuse_progname );
125 
126     switch( severity ) {
127 
128     case UI_ERROR_INFO: break;		/* Shouldn't happen */
129 
130     case UI_ERROR_WARNING: fprintf( stderr, "warning: " ); break;
131     case UI_ERROR_ERROR: fprintf( stderr, "error: " ); break;
132     }
133 
134     fprintf( stderr, "%s\n", message );
135   }
136 
137   return 0;
138 }
139 
140 libspectrum_error
ui_libspectrum_error(libspectrum_error error GCC_UNUSED,const char * format,va_list ap)141 ui_libspectrum_error( libspectrum_error error GCC_UNUSED, const char *format,
142 		      va_list ap )
143 {
144   char new_format[ 257 ];
145   snprintf( new_format, 256, "libspectrum: %s", format );
146 
147   ui_verror( UI_ERROR_ERROR, new_format, ap );
148 
149   return LIBSPECTRUM_ERROR_NONE;
150 }
151 
152 void
ui_error_frame(void)153 ui_error_frame( void )
154 {
155   frames_since_last_message++;
156 }
157 
158 int ui_mouse_present = 0;
159 int ui_mouse_grabbed = 0;
160 static int mouse_grab_suspended = 0;
161 
162 void
ui_mouse_button(int button,int down)163 ui_mouse_button( int button, int down )
164 {
165   int kempston_button = !settings_current.mouse_swap_buttons;
166 
167   if( !ui_mouse_grabbed && !mouse_grab_suspended ) button = 2;
168 
169   /* Possibly we'll end up handling _more_ than one mouse interface... */
170   switch( button ) {
171   case 1:
172     if( ui_mouse_grabbed ) kempmouse_update( 0, 0, kempston_button, down );
173     break;
174   case 3:
175     if( ui_mouse_grabbed ) kempmouse_update( 0, 0, !kempston_button, down );
176     break;
177   case 2:
178     if( ui_mouse_present && settings_current.kempston_mouse
179 	&& !down && !mouse_grab_suspended )
180       ui_mouse_grabbed =
181 	ui_mouse_grabbed ? ui_mouse_release( 1 ) : ui_mouse_grab( 0 );
182     break;
183   }
184 }
185 
186 void
ui_mouse_motion(int x,int y)187 ui_mouse_motion( int x, int y )
188 {
189   if( ui_mouse_grabbed ) kempmouse_update( x, y, -1, 0 );
190 }
191 
192 void
ui_mouse_suspend(void)193 ui_mouse_suspend( void )
194 {
195   mouse_grab_suspended = ui_mouse_grabbed ? 2 : 1;
196 
197   if( ui_mouse_grabbed )
198     ui_mouse_grabbed = ui_mouse_release( 1 );
199 }
200 
201 void
ui_mouse_resume(void)202 ui_mouse_resume( void )
203 {
204   if( mouse_grab_suspended == 2) ui_mouse_grabbed = ui_mouse_grab( 0 );
205   mouse_grab_suspended = 0;
206 }
207 
208 struct menu_item_entries {
209   ui_menu_item item;
210   const char *string1;
211   const char *string2; int string2_inverted;
212   const char *string3; int string3_inverted;
213   const char *string4; int string4_inverted;
214   const char *string5; int string5_inverted;
215   const char *string6; int string6_inverted;
216   const char *string7; int string7_inverted;
217 };
218 
219 static const struct menu_item_entries menu_item_lookup[] = {
220 
221   { UI_MENU_ITEM_FILE_SVG_CAPTURE,
222     "/File/Scalable Vector Graphics/Stop capture",
223     "/File/Scalable Vector Graphics/Start capture in dot mode...", 1,
224     "/File/Scalable Vector Graphics/Start capture in line mode...", 1
225   },
226 
227   { UI_MENU_ITEM_FILE_MOVIE_RECORDING, "/File/Movie/Stop",
228     "/File/Movie/Pause", 0,
229     "/File/Movie/Continue", 0,
230     "/File/Movie/Record...", 1,
231     "/File/Movie/Record from RZX...", 1
232   },
233 
234   { UI_MENU_ITEM_FILE_MOVIE_PAUSE, "/File/Movie/Pause",
235     "/File/Movie/Continue", 1,
236   },
237 
238   { UI_MENU_ITEM_MACHINE_PROFILER, "/Machine/Profiler/Stop",
239     "/Machine/Profiler/Start", 1 },
240 
241   { UI_MENU_ITEM_MACHINE_MULTIFACE, "/Machine/Multiface Red Button" },
242 
243   { UI_MENU_ITEM_MACHINE_DIDAKTIK80_SNAP, "/Machine/Didaktik SNAP" },
244 
245   { UI_MENU_ITEM_MEDIA_CARTRIDGE, "/Media/Cartridge" },
246 
247   { UI_MENU_ITEM_MEDIA_CARTRIDGE_DOCK, "/Media/Cartridge/Timex Dock" },
248 
249   { UI_MENU_ITEM_MEDIA_CARTRIDGE_DOCK_EJECT,
250     "/Media/Cartridge/Timex Dock/Eject" },
251 
252   { UI_MENU_ITEM_MEDIA_IF1, "/Media/Interface 1" },
253 
254   { UI_MENU_ITEM_MEDIA_IF1_M1_EJECT,
255     "/Media/Interface 1/Microdrive 1/Eject",
256     "/Media/Interface 1/Microdrive 1/Save As...", 0,
257     "/Media/Interface 1/Microdrive 1/Save", 0,
258     "/Media/Interface 1/Microdrive 1/Write protect", 0 },
259 
260   { UI_MENU_ITEM_MEDIA_IF1_M1_WP_SET,
261     "/Media/Interface 1/Microdrive 1/Write protect/Enable",
262     "/Media/Interface 1/Microdrive 1/Write protect/Disable", 1 },
263 
264   { UI_MENU_ITEM_MEDIA_IF1_M2_EJECT,
265     "/Media/Interface 1/Microdrive 2/Eject",
266     "/Media/Interface 1/Microdrive 2/Save As...", 0,
267     "/Media/Interface 1/Microdrive 2/Save", 0,
268     "/Media/Interface 1/Microdrive 2/Write protect", 0 },
269 
270   { UI_MENU_ITEM_MEDIA_IF1_M2_WP_SET,
271     "/Media/Interface 1/Microdrive 2/Write protect/Enable",
272     "/Media/Interface 1/Microdrive 2/Write protect/Disable", 1 },
273 
274   { UI_MENU_ITEM_MEDIA_IF1_M3_EJECT,
275     "/Media/Interface 1/Microdrive 3/Eject",
276     "/Media/Interface 1/Microdrive 3/Save As...", 0,
277     "/Media/Interface 1/Microdrive 3/Save", 0,
278     "/Media/Interface 1/Microdrive 3/Write protect", 0 },
279 
280   { UI_MENU_ITEM_MEDIA_IF1_M3_WP_SET,
281     "/Media/Interface 1/Microdrive 3/Write protect/Enable",
282     "/Media/Interface 1/Microdrive 3/Write protect/Disable", 1 },
283 
284   { UI_MENU_ITEM_MEDIA_IF1_M4_EJECT,
285     "/Media/Interface 1/Microdrive 4/Eject",
286     "/Media/Interface 1/Microdrive 4/Save As...", 0,
287     "/Media/Interface 1/Microdrive 4/Save", 0,
288     "/Media/Interface 1/Microdrive 4/Write protect", 0 },
289 
290   { UI_MENU_ITEM_MEDIA_IF1_M4_WP_SET,
291     "/Media/Interface 1/Microdrive 4/Write protect/Enable",
292     "/Media/Interface 1/Microdrive 4/Write protect/Disable", 1 },
293 
294   { UI_MENU_ITEM_MEDIA_IF1_M5_EJECT,
295     "/Media/Interface 1/Microdrive 5/Eject",
296     "/Media/Interface 1/Microdrive 5/Save As...", 0,
297     "/Media/Interface 1/Microdrive 5/Save", 0,
298     "/Media/Interface 1/Microdrive 5/Write protect", 0 },
299 
300   { UI_MENU_ITEM_MEDIA_IF1_M5_WP_SET,
301     "/Media/Interface 1/Microdrive 5/Write protect/Enable",
302     "/Media/Interface 1/Microdrive 5/Write protect/Disable", 1 },
303 
304   { UI_MENU_ITEM_MEDIA_IF1_M6_EJECT,
305     "/Media/Interface 1/Microdrive 6/Eject",
306     "/Media/Interface 1/Microdrive 6/Save As...", 0,
307     "/Media/Interface 1/Microdrive 6/Save", 0,
308     "/Media/Interface 1/Microdrive 6/Write protect", 0 },
309 
310   { UI_MENU_ITEM_MEDIA_IF1_M6_WP_SET,
311     "/Media/Interface 1/Microdrive 6/Write protect/Enable",
312     "/Media/Interface 1/Microdrive 6/Write protect/Disable", 1 },
313 
314   { UI_MENU_ITEM_MEDIA_IF1_M7_EJECT,
315     "/Media/Interface 1/Microdrive 7/Eject",
316     "/Media/Interface 1/Microdrive 7/Save As...", 0,
317     "/Media/Interface 1/Microdrive 7/Save", 0,
318     "/Media/Interface 1/Microdrive 7/Write protect", 0 },
319 
320   { UI_MENU_ITEM_MEDIA_IF1_M7_WP_SET,
321     "/Media/Interface 1/Microdrive 7/Write protect/Enable",
322     "/Media/Interface 1/Microdrive 7/Write protect/Disable", 1 },
323 
324   { UI_MENU_ITEM_MEDIA_IF1_M8_EJECT,
325     "/Media/Interface 1/Microdrive 8/Eject",
326     "/Media/Interface 1/Microdrive 8/Save As...", 0,
327     "/Media/Interface 1/Microdrive 8/Save", 0,
328     "/Media/Interface 1/Microdrive 8/Write protect", 0 },
329 
330   { UI_MENU_ITEM_MEDIA_IF1_M8_WP_SET,
331     "/Media/Interface 1/Microdrive 8/Write protect/Enable",
332     "/Media/Interface 1/Microdrive 8/Write protect/Disable", 1 },
333 
334   { UI_MENU_ITEM_MEDIA_IF1_RS232_UNPLUG_R,
335     "/Media/Interface 1/RS232/Unplug RxD" },
336 
337   { UI_MENU_ITEM_MEDIA_IF1_RS232_UNPLUG_T,
338     "/Media/Interface 1/RS232/Unplug TxD" },
339 
340   { UI_MENU_ITEM_MEDIA_IF1_SNET_UNPLUG,
341     "/Media/Interface 1/Sinclair NET/Unplug" },
342 
343   { UI_MENU_ITEM_MEDIA_CARTRIDGE_IF2, "/Media/Cartridge/Interface 2" },
344 
345   { UI_MENU_ITEM_MEDIA_CARTRIDGE_IF2_EJECT,
346     "/Media/Cartridge/Interface 2/Eject" },
347 
348   { UI_MENU_ITEM_MEDIA_DISK, "/Media/Disk" },
349 
350   { UI_MENU_ITEM_MEDIA_DISK_PLUS3, "/Media/Disk/+3" },
351 
352   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_EJECT,
353     "/Media/Disk/+3/Drive A:/Eject",
354     "/Media/Disk/+3/Drive A:/Save As...", 0,
355     "/Media/Disk/+3/Drive A:/Save", 0,
356     "/Media/Disk/+3/Drive A:/Flip disk", 0,
357     "/Media/Disk/+3/Drive A:/Write protect", 0 },
358 
359   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_FLIP_SET,
360     "/Media/Disk/+3/Drive A:/Flip disk/Turn upside down",
361     "/Media/Disk/+3/Drive A:/Flip disk/Turn back", 1 },
362 
363   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_WP_SET,
364     "/Media/Disk/+3/Drive A:/Write protect/Enable",
365     "/Media/Disk/+3/Drive A:/Write protect/Disable", 1 },
366 
367   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_B, "/Media/Disk/+3/Drive B:" },
368 
369   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_EJECT,
370     "/Media/Disk/+3/Drive B:/Eject",
371     "/Media/Disk/+3/Drive B:/Save As...", 0,
372     "/Media/Disk/+3/Drive B:/Save", 0,
373     "/Media/Disk/+3/Drive B:/Flip disk", 0,
374     "/Media/Disk/+3/Drive B:/Write protect", 0 },
375 
376   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_FLIP_SET,
377     "/Media/Disk/+3/Drive B:/Flip disk/Turn upside down",
378     "/Media/Disk/+3/Drive B:/Flip disk/Turn back", 1 },
379 
380   { UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_WP_SET,
381     "/Media/Disk/+3/Drive B:/Write protect/Enable",
382     "/Media/Disk/+3/Drive B:/Write protect/Disable", 1 },
383 
384   { UI_MENU_ITEM_MEDIA_DISK_BETA, "/Media/Disk/Beta" },
385 
386   { UI_MENU_ITEM_MEDIA_DISK_BETA_A, "/Media/Disk/Beta/Drive A:" },
387 
388   { UI_MENU_ITEM_MEDIA_DISK_BETA_A_EJECT,
389     "/Media/Disk/Beta/Drive A:/Eject",
390     "/Media/Disk/Beta/Drive A:/Save As...", 0,
391     "/Media/Disk/Beta/Drive A:/Save", 0,
392     "/Media/Disk/Beta/Drive A:/Flip disk", 0,
393     "/Media/Disk/Beta/Drive A:/Write protect", 0 },
394 
395   { UI_MENU_ITEM_MEDIA_DISK_BETA_A_FLIP_SET,
396     "/Media/Disk/Beta/Drive A:/Flip disk/Turn upside down",
397     "/Media/Disk/Beta/Drive A:/Flip disk/Turn back", 1 },
398 
399   { UI_MENU_ITEM_MEDIA_DISK_BETA_A_WP_SET,
400     "/Media/Disk/Beta/Drive A:/Write protect/Enable",
401     "/Media/Disk/Beta/Drive A:/Write protect/Disable", 1 },
402 
403   { UI_MENU_ITEM_MEDIA_DISK_BETA_B, "/Media/Disk/Beta/Drive B:" },
404 
405   { UI_MENU_ITEM_MEDIA_DISK_BETA_B_EJECT,
406     "/Media/Disk/Beta/Drive B:/Eject",
407     "/Media/Disk/Beta/Drive B:/Save As...", 0,
408     "/Media/Disk/Beta/Drive B:/Save", 0,
409     "/Media/Disk/Beta/Drive B:/Flip disk", 0,
410     "/Media/Disk/Beta/Drive B:/Write protect", 0 },
411 
412   { UI_MENU_ITEM_MEDIA_DISK_BETA_B_FLIP_SET,
413     "/Media/Disk/Beta/Drive B:/Flip disk/Turn upside down",
414     "/Media/Disk/Beta/Drive B:/Flip disk/Turn back", 1 },
415 
416   { UI_MENU_ITEM_MEDIA_DISK_BETA_B_WP_SET,
417     "/Media/Disk/Beta/Drive B:/Write protect/Enable",
418     "/Media/Disk/Beta/Drive B:/Write protect/Disable", 1 },
419 
420   { UI_MENU_ITEM_MEDIA_DISK_BETA_C, "/Media/Disk/Beta/Drive C:" },
421 
422   { UI_MENU_ITEM_MEDIA_DISK_BETA_C_EJECT,
423     "/Media/Disk/Beta/Drive C:/Eject",
424     "/Media/Disk/Beta/Drive C:/Save As...", 0,
425     "/Media/Disk/Beta/Drive C:/Save", 0,
426     "/Media/Disk/Beta/Drive C:/Flip disk", 0,
427     "/Media/Disk/Beta/Drive C:/Write protect", 0 },
428 
429   { UI_MENU_ITEM_MEDIA_DISK_BETA_C_FLIP_SET,
430     "/Media/Disk/Beta/Drive C:/Flip disk/Turn upside down",
431     "/Media/Disk/Beta/Drive C:/Flip disk/Turn back", 1 },
432 
433   { UI_MENU_ITEM_MEDIA_DISK_BETA_C_WP_SET,
434     "/Media/Disk/Beta/Drive C:/Write protect/Enable",
435     "/Media/Disk/Beta/Drive C:/Write protect/Disable", 1 },
436 
437   { UI_MENU_ITEM_MEDIA_DISK_BETA_D, "/Media/Disk/Beta/Drive D:" },
438 
439   { UI_MENU_ITEM_MEDIA_DISK_BETA_D_EJECT,
440     "/Media/Disk/Beta/Drive D:/Eject",
441     "/Media/Disk/Beta/Drive D:/Save As...", 0,
442     "/Media/Disk/Beta/Drive D:/Save", 0,
443     "/Media/Disk/Beta/Drive D:/Flip disk", 0,
444     "/Media/Disk/Beta/Drive D:/Write protect", 0 },
445 
446   { UI_MENU_ITEM_MEDIA_DISK_BETA_D_FLIP_SET,
447     "/Media/Disk/Beta/Drive D:/Flip disk/Turn upside down",
448     "/Media/Disk/Beta/Drive D:/Flip disk/Turn back", 1 },
449 
450   { UI_MENU_ITEM_MEDIA_DISK_BETA_D_WP_SET,
451     "/Media/Disk/Beta/Drive D:/Write protect/Enable",
452     "/Media/Disk/Beta/Drive D:/Write protect/Disable", 1 },
453 
454   { UI_MENU_ITEM_MEDIA_DISK_PLUSD, "/Media/Disk/+D" },
455 
456   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_1, "/Media/Disk/+D/Drive 1" },
457 
458   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_1_EJECT,
459     "/Media/Disk/+D/Drive 1/Eject",
460     "/Media/Disk/+D/Drive 1/Save As...", 0,
461     "/Media/Disk/+D/Drive 1/Save", 0,
462     "/Media/Disk/+D/Drive 1/Flip disk", 0,
463     "/Media/Disk/+D/Drive 1/Write protect", 0 },
464 
465   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_1_FLIP_SET,
466     "/Media/Disk/+D/Drive 1/Flip disk/Turn upside down",
467     "/Media/Disk/+D/Drive 1/Flip disk/Turn back", 1 },
468 
469   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_1_WP_SET,
470     "/Media/Disk/+D/Drive 1/Write protect/Enable",
471     "/Media/Disk/+D/Drive 1/Write protect/Disable", 1 },
472 
473   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_2, "/Media/Disk/+D/Drive 2" },
474 
475   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_2_EJECT,
476     "/Media/Disk/+D/Drive 2/Eject",
477     "/Media/Disk/+D/Drive 2/Save As...", 0,
478     "/Media/Disk/+D/Drive 2/Save", 0,
479     "/Media/Disk/+D/Drive 2/Flip disk", 0,
480     "/Media/Disk/+D/Drive 2/Write protect", 0 },
481 
482   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_2_FLIP_SET,
483     "/Media/Disk/+D/Drive 2/Flip disk/Turn upside down",
484     "/Media/Disk/+D/Drive 2/Flip disk/Turn back", 1 },
485 
486   { UI_MENU_ITEM_MEDIA_DISK_PLUSD_2_WP_SET,
487     "/Media/Disk/+D/Drive 2/Write protect/Enable",
488     "/Media/Disk/+D/Drive 2/Write protect/Disable", 1 },
489 
490   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK, "/Media/Disk/Didaktik 80" },
491 
492   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_A, "/Media/Disk/Didaktik 80/Drive A" },
493 
494   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_A_EJECT,
495     "/Media/Disk/Didaktik 80/Drive A/Eject",
496     "/Media/Disk/Didaktik 80/Drive A/Save As...", 0,
497     "/Media/Disk/Didaktik 80/Drive A/Save", 0,
498     "/Media/Disk/Didaktik 80/Drive A/Flip disk", 0,
499     "/Media/Disk/Didaktik 80/Drive A/Write protect", 0 },
500 
501   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_A_FLIP_SET,
502     "/Media/Disk/Didaktik 80/Drive A/Flip disk/Turn upside down",
503     "/Media/Disk/Didaktik 80/Drive A/Flip disk/Turn back", 1 },
504 
505   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_A_WP_SET,
506     "/Media/Disk/Didaktik 80/Drive A/Write protect/Enable",
507     "/Media/Disk/Didaktik 80/Drive A/Write protect/Disable", 1 },
508 
509   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_B, "/Media/Disk/Didaktik 80/Drive B" },
510 
511   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_B_EJECT,
512     "/Media/Disk/Didaktik 80/Drive B/Eject",
513     "/Media/Disk/Didaktik 80/Drive B/Save As...", 0,
514     "/Media/Disk/Didaktik 80/Drive B/Save", 0,
515     "/Media/Disk/Didaktik 80/Drive B/Flip disk", 0,
516     "/Media/Disk/Didaktik 80/Drive B/Write protect", 0 },
517 
518   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_B_FLIP_SET,
519     "/Media/Disk/Didaktik 80/Drive B/Flip disk/Turn upside down",
520     "/Media/Disk/Didaktik 80/Drive B/Flip disk/Turn back", 1 },
521 
522   { UI_MENU_ITEM_MEDIA_DISK_DIDAKTIK_B_WP_SET,
523     "/Media/Disk/Didaktik 80/Drive B/Write protect/Enable",
524     "/Media/Disk/Didaktik 80/Drive B/Write protect/Disable", 1 },
525 
526   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE, "/Media/Disk/DISCiPLE" },
527 
528   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_1, "/Media/Disk/DISCiPLE/Drive 1" },
529 
530   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_1_EJECT,
531     "/Media/Disk/DISCiPLE/Drive 1/Eject",
532     "/Media/Disk/DISCiPLE/Drive 1/Save As...", 0,
533     "/Media/Disk/DISCiPLE/Drive 1/Save", 0,
534     "/Media/Disk/DISCiPLE/Drive 1/Flip disk", 0,
535     "/Media/Disk/DISCiPLE/Drive 1/Write protect", 0 },
536 
537   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_1_FLIP_SET,
538     "/Media/Disk/DISCiPLE/Drive 1/Flip disk/Turn upside down",
539     "/Media/Disk/DISCiPLE/Drive 1/Flip disk/Turn back", 1 },
540 
541   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_1_WP_SET,
542     "/Media/Disk/DISCiPLE/Drive 1/Write protect/Enable",
543     "/Media/Disk/DISCiPLE/Drive 1/Write protect/Disable", 1 },
544 
545   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_2, "/Media/Disk/DISCiPLE/Drive 2" },
546 
547   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_2_EJECT,
548     "/Media/Disk/DISCiPLE/Drive 2/Eject",
549     "/Media/Disk/DISCiPLE/Drive 2/Save As...", 0,
550     "/Media/Disk/DISCiPLE/Drive 2/Save", 0,
551     "/Media/Disk/DISCiPLE/Drive 2/Flip disk", 0,
552     "/Media/Disk/DISCiPLE/Drive 2/Write protect", 0 },
553 
554   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_2_FLIP_SET,
555     "/Media/Disk/DISCiPLE/Drive 2/Flip disk/Turn upside down",
556     "/Media/Disk/DISCiPLE/Drive 2/Flip disk/Turn back", 1 },
557 
558   { UI_MENU_ITEM_MEDIA_DISK_DISCIPLE_2_WP_SET,
559     "/Media/Disk/DISCiPLE/Drive 2/Write protect/Enable",
560     "/Media/Disk/DISCiPLE/Drive 2/Write protect/Disable", 1 },
561 
562   { UI_MENU_ITEM_MEDIA_DISK_OPUS, "/Media/Disk/Opus" },
563 
564   { UI_MENU_ITEM_MEDIA_DISK_OPUS_1, "/Media/Disk/Opus/Drive 1" },
565 
566   { UI_MENU_ITEM_MEDIA_DISK_OPUS_1_EJECT,
567     "/Media/Disk/Opus/Drive 1/Eject",
568     "/Media/Disk/Opus/Drive 1/Save As...", 0,
569     "/Media/Disk/Opus/Drive 1/Save", 0,
570     "/Media/Disk/Opus/Drive 1/Flip disk", 0,
571     "/Media/Disk/Opus/Drive 1/Write protect", 0 },
572 
573   { UI_MENU_ITEM_MEDIA_DISK_OPUS_1_FLIP_SET,
574     "/Media/Disk/Opus/Drive 1/Flip disk/Turn upside down",
575     "/Media/Disk/Opus/Drive 1/Flip disk/Turn back", 1 },
576 
577   { UI_MENU_ITEM_MEDIA_DISK_OPUS_1_WP_SET,
578     "/Media/Disk/Opus/Drive 1/Write protect/Enable",
579     "/Media/Disk/Opus/Drive 1/Write protect/Disable", 1 },
580 
581   { UI_MENU_ITEM_MEDIA_DISK_OPUS_2, "/Media/Disk/Opus/Drive 2" },
582 
583   { UI_MENU_ITEM_MEDIA_DISK_OPUS_2_EJECT,
584     "/Media/Disk/Opus/Drive 2/Eject",
585     "/Media/Disk/Opus/Drive 2/Save As...", 0,
586     "/Media/Disk/Opus/Drive 2/Save", 0,
587     "/Media/Disk/Opus/Drive 2/Flip disk", 0,
588     "/Media/Disk/Opus/Drive 2/Write protect", 0 },
589 
590   { UI_MENU_ITEM_MEDIA_DISK_OPUS_2_FLIP_SET,
591     "/Media/Disk/Opus/Drive 2/Flip disk/Turn upside down",
592     "/Media/Disk/Opus/Drive 2/Flip disk/Turn back", 1 },
593 
594   { UI_MENU_ITEM_MEDIA_DISK_OPUS_2_WP_SET,
595     "/Media/Disk/Opus/Drive 2/Write protect/Enable",
596     "/Media/Disk/Opus/Drive 2/Write protect/Disable", 1 },
597 
598   { UI_MENU_ITEM_MEDIA_IDE, "/Media/IDE" },
599 
600   { UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT, "/Media/IDE/Simple 8-bit" },
601 
602   { UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT_MASTER_EJECT,
603     "/Media/IDE/Simple 8-bit/Master/Commit",
604     "/Media/IDE/Simple 8-bit/Master/Eject", 0 },
605 
606   { UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT_SLAVE_EJECT,
607     "/Media/IDE/Simple 8-bit/Slave/Commit",
608     "/Media/IDE/Simple 8-bit/Slave/Eject", 0 },
609 
610   { UI_MENU_ITEM_MEDIA_IDE_ZXATASP, "/Media/IDE/ZXATASP" },
611 
612   { UI_MENU_ITEM_MEDIA_IDE_ZXATASP_MASTER_EJECT,
613     "/Media/IDE/ZXATASP/Master/Commit",
614     "/Media/IDE/ZXATASP/Master/Eject", 0 },
615 
616   { UI_MENU_ITEM_MEDIA_IDE_ZXATASP_SLAVE_EJECT,
617     "/Media/IDE/ZXATASP/Slave/Commit",
618     "/Media/IDE/ZXATASP/Slave/Eject", 0 },
619 
620   { UI_MENU_ITEM_MEDIA_IDE_ZXCF, "/Media/IDE/ZXCF CompactFlash" },
621 
622   { UI_MENU_ITEM_MEDIA_IDE_ZXCF_EJECT,
623     "/Media/IDE/ZXCF CompactFlash/Commit",
624     "/Media/IDE/ZXCF CompactFlash/Eject", 0 },
625 
626   { UI_MENU_ITEM_MEDIA_IDE_DIVIDE, "/Media/IDE/DivIDE" },
627 
628   { UI_MENU_ITEM_MEDIA_IDE_DIVIDE_MASTER_EJECT,
629     "/Media/IDE/DivIDE/Master/Commit",
630     "/Media/IDE/DivIDE/Master/Eject", 0 },
631 
632   { UI_MENU_ITEM_MEDIA_IDE_DIVIDE_SLAVE_EJECT,
633     "/Media/IDE/DivIDE/Slave/Commit",
634     "/Media/IDE/DivIDE/Slave/Eject", 0 },
635 
636   { UI_MENU_ITEM_MEDIA_IDE_DIVMMC, "/Media/IDE/DivMMC" },
637 
638   { UI_MENU_ITEM_MEDIA_IDE_DIVMMC_EJECT,
639     "/Media/IDE/DivMMC/Commit",
640     "/Media/IDE/DivMMC/Eject", 0 },
641 
642   { UI_MENU_ITEM_MEDIA_IDE_ZXMMC, "/Media/IDE/ZXMMC" },
643 
644   { UI_MENU_ITEM_MEDIA_IDE_ZXMMC_EJECT,
645     "/Media/IDE/ZXMMC/Commit",
646     "/Media/IDE/ZXMMC/Eject", 0 },
647 
648   { UI_MENU_ITEM_RECORDING,
649     "/File/Recording/Stop",
650     "/File/Recording/Record...", 1,
651     "/File/Recording/Record from snapshot...", 1,
652     "/File/Recording/Continue recording...", 1,
653     "/File/Recording/Play...", 1,
654     "/File/Recording/Finalise...", 1 },
655 
656   { UI_MENU_ITEM_RECORDING_ROLLBACK,
657     "/File/Recording/Insert snapshot",
658     "/File/Recording/Rollback", 0,
659     "/File/Recording/Rollback to...", 0 },
660 
661   { UI_MENU_ITEM_AY_LOGGING,
662     "/File/AY Logging/Stop",
663     "/File/AY Logging/Record...", 1, },
664 
665   { UI_MENU_ITEM_TAPE_RECORDING,
666     "/Media/Tape/Record Stop",
667     "/Media/Tape/Record Start", 1,
668     "/Media/Tape/Open...", 1,
669     "/Media/Tape/Play", 1,
670     "/Media/Tape/Rewind", 1,
671     "/Media/Tape/Clear", 1,
672     "/Media/Tape/Write...", 1 },
673 
674   { UI_MENU_ITEM_TAPE_RECORDING, NULL },	/* End marker */
675 
676 };
677 
678 int
ui_menu_activate(ui_menu_item item,int active)679 ui_menu_activate( ui_menu_item item, int active )
680 {
681   const struct menu_item_entries *ptr;
682 
683   for( ptr = menu_item_lookup; ptr->string1; ptr++ ) {
684 
685     if( item == ptr->item ) {
686       ui_menu_item_set_active( ptr->string1, active );
687       if( ptr->string2 )
688 	ui_menu_item_set_active( ptr->string2,
689 				 ptr->string2_inverted ? !active : active );
690       if( ptr->string3 )
691 	ui_menu_item_set_active( ptr->string3,
692 				 ptr->string3_inverted ? !active : active );
693       if( ptr->string4 )
694 	ui_menu_item_set_active( ptr->string4,
695 				 ptr->string4_inverted ? !active : active );
696       if( ptr->string5 )
697 	ui_menu_item_set_active( ptr->string5,
698 				 ptr->string5_inverted ? !active : active );
699       if( ptr->string6 )
700 	ui_menu_item_set_active( ptr->string6,
701 				 ptr->string6_inverted ? !active : active );
702       if( ptr->string7 )
703 	ui_menu_item_set_active( ptr->string7,
704 				 ptr->string7_inverted ? !active : active );
705       return 0;
706     }
707 
708   }
709 
710   ui_error( UI_ERROR_ERROR, "ui_menu_activate: unknown item %d", item );
711   return 1;
712 }
713 
714 void
ui_menu_disk_update(void)715 ui_menu_disk_update( void )
716 {
717   int drives_avail;
718 
719   drives_avail = ui_media_drive_any_available();
720 
721   /* Set the disk menu items and statusbar appropriately */
722 
723   if( drives_avail ) {
724     ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK, 1 );
725     ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
726   } else {
727     ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK, 0 );
728     ui_statusbar_update( UI_STATUSBAR_ITEM_DISK,
729                          UI_STATUSBAR_STATE_NOT_AVAILABLE );
730   }
731 
732   ui_media_drive_update_parent_menus();
733 }
734 
735 int
ui_tape_write(void)736 ui_tape_write( void )
737 {
738   char *filename;
739 
740   fuse_emulation_pause();
741 
742   filename = ui_get_save_filename( "Fuse - Write Tape" );
743   if( !filename ) { fuse_emulation_unpause(); return 1; }
744 
745   tape_write( filename );
746 
747   libspectrum_free( filename );
748 
749   fuse_emulation_unpause();
750 
751   return 0;
752 }
753 
754 int
ui_mdr_write(int which,int saveas)755 ui_mdr_write( int which, int saveas )
756 {
757   int err;
758   char *filename = NULL, title[80];
759 
760   fuse_emulation_pause();
761 
762   snprintf( title, 80, "Fuse - Write Microdrive Cartridge %i", which + 1 );
763 
764   if( saveas ) {
765     filename = ui_get_save_filename( title );
766     if( !filename ) { fuse_emulation_unpause(); return 1; }
767   }
768 
769   err = if1_mdr_write( which, filename );
770 
771   if( saveas ) libspectrum_free( filename );
772 
773   fuse_emulation_unpause();
774 
775   return err;
776 }
777 
778 #ifdef USE_WIDGET
779 int
ui_widget_init(void)780 ui_widget_init( void )
781 {
782   return widget_init();
783 }
784 
785 int
ui_widget_end(void)786 ui_widget_end( void )
787 {
788   return widget_end();
789 }
790 #else
791 int
ui_widget_init(void)792 ui_widget_init( void )
793 {
794   return 0;
795 }
796 
797 int
ui_widget_end(void)798 ui_widget_end( void )
799 {
800   return 0;
801 }
802 
803 void
ui_popup_menu(int native_key)804 ui_popup_menu( int native_key )
805 {
806 }
807 
808 void
ui_widget_keyhandler(int native_key)809 ui_widget_keyhandler( int native_key )
810 {
811 }
812 #endif				/* #ifndef USE_WIDGET */
813