2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <SDL_image.h>
19 #include <glib/gstdio.h>
33 char *gd_last_folder
=NULL
;
34 char *gd_caveset_filename
=NULL
;
37 /* operation successful, so we should remember file name. */
39 gd_caveset_file_operation_successful(const char *filename
)
41 /* if it is a bd file, remember new filename */
42 if (g_str_has_suffix(filename
, ".bd")) {
45 /* first make copy, then free and set pointer. we might be called with filename=caveset_filename */
46 stored
=g_strdup(filename
);
47 g_free(gd_caveset_filename
);
48 gd_caveset_filename
=stored
;
50 g_free(gd_caveset_filename
);
51 gd_caveset_filename
=NULL
;
56 gd_open_caveset(const char *directory
)
61 /* if the caveset is edited, ask the user if to save. */
62 /* if does not want to discard, get out here */
63 if (!gd_discard_changes())
67 gd_last_folder
=g_strdup(g_get_home_dir());
69 filter
=g_strjoinv(";", gd_caveset_extensions
);
70 filename
=gd_select_file("SELECT CAVESET TO LOAD", directory
?directory
:gd_last_folder
, filter
, FALSE
);
73 /* if file selected */
75 /* remember last directory */
76 g_free(gd_last_folder
);
77 gd_last_folder
=g_path_get_dirname(filename
);
79 gd_save_highscore(gd_user_config_dir
);
81 gd_caveset_load_from_file(filename
, gd_user_config_dir
);
83 /* if successful loading and this is a bd file, and we load highscores from our own config dir */
84 if (!gd_has_new_error() && g_str_has_suffix(filename
, ".bd") && !gd_use_bdcff_highscore
)
85 gd_load_highscore(gd_user_config_dir
);
96 filenamesort(gconstpointer a
, gconstpointer b
)
98 gchar
**a_
=(gpointer
) a
, **b_
=(gpointer
) b
;
99 return g_ascii_strcasecmp(*a_
, *b_
);
104 /* set title line (the first line in the screen) to text */
106 gd_title_line(const char *format
, ...)
111 va_start(args
, format
);
112 text
=g_strdup_vprintf(format
, args
);
113 gd_blittext_n(gd_screen
, -1, 0, GD_GDASH_WHITE
, text
);
118 /* set status line (the last line in the screen) to text */
120 gd_status_line(const char *text
)
122 gd_blittext_n(gd_screen
, -1, gd_screen
->h
-gd_font_height(), GD_GDASH_GRAY2
, text
);
126 gd_status_line_red(const char *text
)
128 gd_blittext_n(gd_screen
, -1, gd_screen
->h
-gd_font_height(), GD_GDASH_RED
, text
);
134 /* runs a file selection dialog */
135 /* returns new, full-path filename (to be freed later by the caller) */
136 /* glob: semicolon separated list of globs */
138 gd_select_file(const char *title
, const char *start_dir
, const char *glob
, gboolean for_save
)
149 static char *current_dir
=NULL
; /* static to not worry about freeing */
150 const int yd
=gd_line_height();
151 const int names_per_page
=gd_screen
->h
/yd
-3;
158 if (glob
==NULL
|| g_str_equal(glob
, ""))
160 globs
=g_strsplit_set(glob
, ";", -1);
162 /* remember current directory, as we step into others */
165 current_dir
=g_get_current_dir();
167 gd_backup_and_dark_screen();
168 gd_title_line(title
);
170 /* for saving, we allow the user to select a new filename. */
171 gd_status_line("CRSR:SELECT N:NEW J:JUMP ESC:CANCEL");
173 gd_status_line("MOVE: SELECT J: JUMP ESC: CANCEL");
175 /* this is somewhat hackish; finds out the absolute path of start_dir. also tests is we can enter that directory */
176 if (g_chdir(start_dir
)==-1) {
177 g_warning("cannot change to directory: %s", start_dir
);
178 /* stay in current_dir */
180 directory
=g_get_current_dir();
181 g_chdir(current_dir
);
184 filestate
=GD_NOT_YET
;
185 while (!gd_quit
&& filestate
==GD_NOT_YET
) {
192 files
=g_ptr_array_new();
193 if (g_chdir(directory
)==-1) {
194 g_warning("cannot change to directory: %s", directory
);
198 dir
=g_dir_open(".", 0, NULL
);
200 g_warning("cannot open directory: %s", directory
);
204 while ((name
=g_dir_read_name(dir
))!=NULL
) {
206 /* on windows, skip hidden files? */
208 /* on unix, skip file names starting with a '.' - those are hidden files */
212 if (g_file_test(name
, G_FILE_TEST_IS_DIR
))
213 g_ptr_array_add(files
, g_strdup_printf("%s%s", name
, G_DIR_SEPARATOR_S
));
216 gboolean match
=FALSE
;
218 for (i
=0; globs
[i
]!=NULL
&& match
==FALSE
; i
++)
219 if (g_pattern_match_simple(globs
[i
], name
))
223 g_ptr_array_add(files
, g_strdup(name
));
227 g_chdir(current_dir
); /* step back to directory where we started */
229 /* add "directory up" and sort */
231 /* if we are NOT in a root directory */
232 if (!g_str_has_suffix(directory
, ":\\")) /* root directory is "X:\" */
233 g_ptr_array_add(files
, g_strdup_printf("..%s", G_DIR_SEPARATOR_S
));
235 if (!g_str_equal(directory
, "/"))
236 g_ptr_array_add(files
, g_strdup_printf("..%s", G_DIR_SEPARATOR_S
));
238 g_ptr_array_sort(files
, filenamesort
);
240 /* show current directory */
241 gd_clear_line(gd_screen
, 1*yd
);
242 gd_blittext_n(gd_screen
, -1, 1*yd
, GD_GDASH_YELLOW
, gd_filename_to_utf8(directory
));
244 /* do file selection menu */
248 while (state
==GD_NOT_YET
) {
252 page
=sel
/names_per_page
;
255 for (i
=0, cur
=page
*names_per_page
; i
<names_per_page
; i
++, cur
++) {
258 col
=cur
==sel
?GD_GDASH_YELLOW
:GD_GDASH_LIGHTBLUE
;
260 gd_clear_line(gd_screen
, (i
+2)*yd
);
261 if (cur
<files
->len
) /* may not be as much filenames as it would fit on the screen */
262 gd_blittext_n(gd_screen
, 0, (i
+2)*yd
, col
, gd_filename_to_utf8(g_ptr_array_index(files
, cur
)));
269 /* check for incoming events */
271 while (SDL_PollEvent(&event
)) {
272 switch (event
.type
) {
279 switch (event
.key
.keysym
.sym
) {
282 sel
=gd_clamp(sel
-1, 0, files
->len
-1);
286 sel
=gd_clamp(sel
+1, 0, files
->len
-1);
290 sel
=gd_clamp(sel
-names_per_page
, 0, files
->len
-1);
294 sel
=gd_clamp(sel
+names_per_page
, 0, files
->len
-1);
306 /* jump to directory (name will be typed) */
310 /* enter new filename - only if saving allowed */
316 /* select current file/directory */
327 /* other keys do nothing */
333 /* other events are not interesting now */
335 } /* switch event.type */
336 } /* while pollevent */
337 } /* while state=nothing happened */
339 /* now check the state variable to see what happend. maybe act upon it. */
341 /* user requested to enter a new filename */
345 char *extension_added
;
347 /* make up a suggested filename */
348 if (!g_str_equal(gd_caveset_data
->name
, ""))
349 extension_added
=g_strdup_printf("%s.bd", gd_caveset_data
->name
);
351 extension_added
=NULL
;
352 /* if extension added is null, g_build_path will sense that as the end of the list. */
353 name
=g_build_path(G_DIR_SEPARATOR_S
, directory
, extension_added
, NULL
);
354 g_free(extension_added
);
355 new_name
=gd_input_string("ENTER NEW FILE NAME", name
);
357 /* if enters a file name, remember that, and exit the function via setting filestate variable */
365 /* user requested to ask for another directory name to jump to */
366 if (state
==GD_JUMP
) {
369 newdir
=gd_input_string("JUMP TO DIRECTORY", directory
);
371 /* first change to dir, then to newdir: newdir entered by the user might not be absolute. */
372 if (g_chdir(directory
)==-1 || g_chdir(newdir
)==-1) {
373 g_warning("cannot change to directory: %s", newdir
);
380 directory
=g_get_current_dir();
381 g_chdir(current_dir
);
385 /* if selected any from the list, it can be a file or a directory. */
387 if (g_str_has_suffix(g_ptr_array_index(files
, sel
), G_DIR_SEPARATOR_S
)) {
388 /* directory selected */
391 if (g_chdir(directory
)==-1) {
392 g_warning("cannot change to directory: %s", directory
);
396 if (g_chdir(g_ptr_array_index(files
, sel
))==-1) {
397 g_warning("cannot change to directory: %s", (char *)g_ptr_array_index(files
, sel
));
401 newdir
=g_get_current_dir();
402 g_chdir(current_dir
); /* step back to directory where we started */
407 result
=g_build_path(G_DIR_SEPARATOR_S
, directory
, g_ptr_array_index(files
, sel
), NULL
);
411 /* pass state to break loop */
414 g_ptr_array_foreach(files
, (GFunc
) g_free
, NULL
);
415 g_ptr_array_free(files
, TRUE
);
418 /* if selecting a file to write to, check if overwrite */
419 if (filestate
==GD_YES
&& result
&& for_save
&& g_file_test(result
, G_FILE_TEST_EXISTS
)) {
420 gboolean said_yes
, answer
;
422 answer
=gd_ask_yes_no("File exists. Overwrite?", "No", "Yes", &said_yes
);
423 if (!answer
|| !said_yes
) { /* if did not answer or answered no, forget filename. we do not overwrite */
445 is_image_ok_for_theme(const char *filename
)
447 SDL_Surface
*surface
;
448 gboolean result
=FALSE
;
450 surface
=IMG_Load(filename
);
453 /* if the image is loaded */
455 gd_error_set_context("%s", filename
);
456 if (gd_is_surface_ok_for_theme(surface
)) /* if image passes all checks, result is "OK" */
458 gd_error_set_context(NULL
);
459 SDL_FreeSurface(surface
);
467 add_file_to_themes(GPtrArray
*themes
, const char *filename
)
471 g_assert(filename
!=NULL
);
473 /* if file name already in themes list, remove. */
474 for (i
=0; i
<themes
->len
; i
++)
475 if (g_ptr_array_index(themes
, i
)!=NULL
&& g_str_equal(g_ptr_array_index(themes
, i
), filename
))
476 g_ptr_array_remove_index_fast(themes
, i
);
478 if (is_image_ok_for_theme(filename
))
479 g_ptr_array_add(themes
, g_strdup(filename
));
484 add_dir_to_themes(GPtrArray
*themes
, const char *directory_name
)
489 dir
=g_dir_open(directory_name
, 0, NULL
);
491 /* silently ignore unable-to-open directories */
493 while((name
=g_dir_read_name(dir
))) {
497 filename
=g_build_filename(directory_name
, name
, NULL
);
498 lower
=g_ascii_strdown(filename
, -1);
500 /* we only allow bmp and png files. converted to lowercase, to be able to check for .bmp */
501 if ((g_str_has_suffix(lower
, ".bmp") || g_str_has_suffix(lower
, ".png")) && g_file_test(filename
, G_FILE_TEST_IS_REGULAR
))
502 /* try to add the file. */
503 add_file_to_themes(themes
, filename
);
510 /* this is in glib 2.16, but that is too new for some users. */
511 /* also, this one treats NULL as the "lowest", so it will be at the start of the list */
513 strcmp0(const char *str1
, const char *str2
)
515 if (str1
==NULL
&& str2
==NULL
)
521 return strcmp(str1
, str2
);
524 /* will create a list of file names which can be used as themes. */
525 /* the first item will be a NULL to represent the default, built-in theme. */
527 gd_create_themes_list()
531 gboolean current_found
;
533 themes
=g_ptr_array_new();
534 g_ptr_array_add(themes
, NULL
); /* this symbolizes the default theme */
535 add_dir_to_themes(themes
, gd_system_data_dir
);
536 add_dir_to_themes(themes
, gd_user_config_dir
);
538 /* check if current theme is in the array */
540 for (i
=0; i
<themes
->len
; i
++)
541 if (strcmp0(gd_sdl_theme
, g_ptr_array_index(themes
, i
))==0)
544 add_file_to_themes(themes
, gd_sdl_theme
);
551 * SDASH SETTINGS MENU
557 static GPtrArray
*themes
=NULL
;
559 const char *yes
="yes", *no
="no";
561 typedef enum _settingtype
{
574 const char **stringv
;
576 { 0, TypeBoolean
, "Fullscreen", &gd_sdl_fullscreen
},
577 { 0, TypeTheme
, "Theme", NULL
},
578 { 0, TypeScale
, "Scale", &gd_sdl_scale
},
579 { 0, TypeBoolean
, "PAL emulation", &gd_sdl_pal_emulation
},
580 { 0, TypePercent
, "PAL scanline shade", &gd_pal_emu_scanline_shade
},
581 { 0, TypeBoolean
, "Even lines vertical scroll", &gd_even_line_pal_emu_vertical_scroll
},
582 { 0, TypeBoolean
, "Fine scrolling", &gd_fine_scroll
},
583 { 0, TypeStringv
, "C64 palette", &gd_c64_palette
, gd_color_get_c64_palette_names() },
584 { 0, TypeStringv
, "C64DTV palette", &gd_c64dtv_palette
, gd_color_get_c64dtv_palette_names() },
585 { 0, TypeStringv
, "Atari palette", &gd_atari_palette
, gd_color_get_atari_palette_names() },
586 { 0, TypeStringv
, "Preferred palette", &gd_preferred_palette
, gd_color_get_palette_types_names() },
589 { 1, TypeBoolean
, "Sound", &gd_sdl_sound
},
590 { 1, TypePercent
, "Music volume", &gd_sound_music_volume_percent
},
591 { 1, TypePercent
, "Cave volume", &gd_sound_chunks_volume_percent
},
592 { 1, TypeBoolean
, "Classic sounds only", &gd_classic_sound
},
593 { 1, TypeBoolean
, "16-bit mixing", &gd_sdl_16bit_mixing
},
594 { 1, TypeBoolean
, "44kHz mixing", &gd_sdl_44khz_mixing
},
596 { 1, TypeBoolean
, "Use BDCFF highscore", &gd_use_bdcff_highscore
},
597 { 1, TypeBoolean
, "Show caveset name at uncover", &gd_show_name_of_game
},
598 { 1, TypeBoolean
, "Show story", &gd_show_story
},
599 { 1, TypeStringv
, "Status bar colors", &gd_status_bar_type
, gd_status_bar_type_get_names() },
600 { 1, TypeBoolean
, "All caves selectable", &gd_all_caves_selectable
},
601 { 1, TypeBoolean
, "Import as all selectable", &gd_import_as_all_caves_selectable
},
602 { 1, TypeBoolean
, "No invisible outbox", &gd_no_invisible_outbox
},
603 { 1, TypeBoolean
, "Random colors", &gd_random_colors
},
605 { 2, TypeKey
, "Key for left", &gd_sdl_key_left
},
606 { 2, TypeKey
, "Key for right", &gd_sdl_key_right
},
607 { 2, TypeKey
, "Key for up", &gd_sdl_key_up
},
608 { 2, TypeKey
, "Key for down", &gd_sdl_key_down
},
609 { 2, TypeKey
, "Key for fire", &gd_sdl_key_fire_1
},
610 { 2, TypeKey
, "Key for fire (alt.)", &gd_sdl_key_fire_2
},
611 { 2, TypeKey
, "Key for suicide", &gd_sdl_key_suicide
},
613 const int numpages
=settings
[G_N_ELEMENTS(settings
)-1].page
+1; /* take it from last element of settings[] */
616 int y1
[numpages
], yd
;
618 /* optionally create the list of themes, and also find the current one in the list. */
619 themes
=gd_create_themes_list();
621 for (n
=0; n
<themes
->len
; n
++)
622 if (strcmp0(gd_sdl_theme
, g_ptr_array_index(themes
, n
))==0)
625 g_warning("theme %s not found in array", gd_sdl_theme
);
629 /* check pages, and x1,y1 coordinates for each */
630 yd
=gd_font_height()+2;
631 for (page
=0; page
<numpages
; page
++) {
635 for (n
=0; n
<G_N_ELEMENTS(settings
); n
++)
636 if (settings
[n
].page
==page
)
638 y1
[page
]=(gd_screen
->h
-num
*yd
)/2;
641 gd_backup_and_dark_screen();
642 gd_status_line("CRSR: MOVE SPACE: CHANGE ESC: EXIT");
643 gd_blittext_n(gd_screen
, -1, gd_screen
->h
-3*gd_line_height(), GD_GDASH_GRAY1
, "Some changes require restart.");
644 gd_blittext_n(gd_screen
, -1, gd_screen
->h
-2*gd_line_height(), GD_GDASH_GRAY1
, "Use T in the title for a new theme.");
648 while (!finished
&& !gd_quit
) {
652 page
=settings
[current
].page
; /* take the current page number from the current setting line */
653 gd_clear_line(gd_screen
, 0); /* clear for title line */
654 gd_title_line("GDASH OPTIONS, PAGE %d/%d", page
+1, numpages
);
658 for (n
=0; n
<G_N_ELEMENTS(settings
); n
++) {
659 if (settings
[n
].page
==page
) {
660 const GdColor c_name
=GD_GDASH_LIGHTBLUE
;
661 const GdColor c_selected
=GD_GDASH_YELLOW
;
662 const GdColor c_value
=GD_GDASH_GREEN
;
667 y
=y1
[page
]+linenum
*yd
;
668 x
=gd_blittext_n(gd_screen
, x
, y
, current
==n
?c_selected
:c_name
, settings
[n
].name
);
669 x
+=2*gd_font_width();
670 switch(settings
[n
].type
) {
672 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, *(gboolean
*)settings
[n
].var
?yes
:no
);
675 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, gd_scaling_name
[*(GdScalingType
*)settings
[n
].var
]);
678 x
=gd_blittext_printf_n(gd_screen
, x
, y
, c_value
, "%d%%", *(int *)settings
[n
].var
);
682 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, "[Default]");
685 thm
=g_filename_display_basename(g_ptr_array_index(themes
, themenum
));
686 if (strrchr(thm
, '.')) /* remove extension */
687 *strrchr(thm
, '.')='\0';
688 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, thm
);
693 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, settings
[n
].stringv
[*(int *)settings
[n
].var
]);
696 x
=gd_blittext_n(gd_screen
, x
, y
, c_value
, gd_key_name(*(guint
*)settings
[n
].var
));
705 /* we don't leave text on the screen after us. */
706 /* so next iteration will have a nice empty screen to draw on :) */
708 for (n
=0; n
<G_N_ELEMENTS(settings
); n
++) {
709 if (settings
[n
].page
==page
) {
710 gd_clear_line(gd_screen
, y1
[page
]+linenum
*yd
);
716 while (SDL_PollEvent(&event
)) {
723 switch(event
.key
.keysym
.sym
) {
725 case SDLK_UP
: /* key up */
726 current
=gd_clamp(current
-1, 0, G_N_ELEMENTS(settings
)-1);
728 case SDLK_DOWN
: /* key down */
729 current
=gd_clamp(current
+1, 0, G_N_ELEMENTS(settings
)-1);
733 while (settings
[current
].page
==page
)
734 current
--; /* decrement until previous page is found */
738 while (settings
[current
].page
==page
)
739 current
++; /* increment until previous page is found */
742 /* CHANGE SETTINGS */
743 case SDLK_LEFT
: /* key left */
744 switch(settings
[current
].type
) {
746 *(gboolean
*)settings
[current
].var
=FALSE
;
749 *(int *)settings
[current
].var
=MAX(0,(*(int *)settings
[current
].var
)-1);
752 *(int *)settings
[current
].var
=MAX(0,(*(int *)settings
[current
].var
)-5);
755 themenum
=gd_clamp(themenum
-1, 0, themes
->len
-1);
758 *(int *)settings
[current
].var
=gd_clamp(*(int *)settings
[current
].var
-1, 0, g_strv_length((char **) settings
[current
].stringv
)-1);
765 case SDLK_RIGHT
: /* key right */
766 switch(settings
[current
].type
) {
768 *(gboolean
*)settings
[current
].var
=TRUE
;
771 *(int *)settings
[current
].var
=MIN(GD_SCALING_MAX
-1,(*(int *)settings
[current
].var
)+1);
774 *(int *)settings
[current
].var
=MIN(100,(*(int *)settings
[current
].var
)+5);
777 themenum
=gd_clamp(themenum
+1, 0, themes
->len
-1);
780 *(int *)settings
[current
].var
=gd_clamp(*(int *)settings
[current
].var
+1, 0, g_strv_length((char **) settings
[current
].stringv
)-1);
787 case SDLK_SPACE
: /* key left */
789 switch (settings
[current
].type
) {
791 *(gboolean
*)settings
[current
].var
=!*(gboolean
*)settings
[current
].var
;
794 *(int *)settings
[current
].var
=(*(int *)settings
[current
].var
+1)%GD_SCALING_MAX
;
797 *(int *)settings
[current
].var
=CLAMP((*(int *)settings
[current
].var
+5), 0, 100);
800 themenum
=(themenum
+1)%themes
->len
;
803 *(int *)settings
[current
].var
=(*(int *)settings
[current
].var
+1)%g_strv_length((char **) settings
[current
].stringv
);
809 i
=gd_select_key(settings
[current
].name
);
811 *(guint
*)settings
[current
].var
=i
;
817 case SDLK_ESCAPE
: /* finished options menu */
822 /* other keys do nothing */
824 } /* switch keypress key */
825 } /* switch event type */
826 } /* while pollevent */
829 /* set the theme. other variables are already set by the above code. */
830 g_free(gd_sdl_theme
);
833 gd_sdl_theme
=g_strdup(g_ptr_array_index(themes
, themenum
));
834 gd_load_theme(); /* this loads the theme given in the global variable gd_sdl_theme. */
836 /* forget list of themes */
837 g_ptr_array_foreach(themes
, (GFunc
) g_free
, NULL
);
838 g_ptr_array_free(themes
, TRUE
);
843 gd_sound_set_music_volume(gd_sound_music_volume_percent
);
844 gd_sound_set_chunk_volumes(gd_sound_chunks_volume_percent
);
855 filename
=gd_select_file("SELECT IMAGE IMAGE FOR THEME", g_get_home_dir(), "*.bmp;*.png", FALSE
);
858 if (is_image_ok_for_theme(filename
)) {
859 /* if file is said to be ok as a theme */
860 char *basename
, *new_filename
;
865 /* make up new filename */
866 basename
=g_path_get_basename(filename
);
867 new_filename
=g_build_path(G_DIR_SEPARATOR_S
, gd_user_config_dir
, basename
, NULL
);
870 /* copy theme to user config directory */
871 if (g_file_get_contents(filename
, &contents
, &length
, &error
) && g_file_set_contents(new_filename
, contents
, length
, &error
)) {
875 /* unable to copy file. */
876 g_warning("%s", error
->message
);
879 /* if file is not ok as a theme */
880 g_warning("%s cannot be used as a theme", filename
);
887 gd_show_highscore(GdCave
*highlight_cave
, int highlight_line
)
890 const int screen_height
=gd_screen
->h
/gd_line_height();
891 int max
=MIN(G_N_ELEMENTS(cave
->highscore
), screen_height
-3);
893 GList
*current
; /* current cave to view */
896 current
=g_list_find(gd_caveset
, highlight_cave
);
898 gd_backup_and_dark_screen();
899 gd_title_line("THE HALL OF FAME");
900 gd_status_line("CRSR: CAVE SPACE: EXIT");
904 while (!finished
&& !gd_quit
) {
909 /* current cave or game */
915 scores
=gd_caveset_data
->highscore
;
917 scores
=cave
->highscore
;
919 gd_clear_line(gd_screen
, gd_font_height());
920 gd_blittext_n(gd_screen
, -1, gd_font_height(), GD_GDASH_YELLOW
, cave
?cave
->name
:gd_caveset_data
->name
);
924 for (i
=0; i
<max
; i
++) {
927 gd_clear_line(gd_screen
, (i
+2)*gd_line_height());
928 c
=i
/5%2?GD_GDASH_PURPLE
:GD_GDASH_GREEN
;
929 if (cave
==highlight_cave
&& i
==highlight_line
)
931 if (scores
[i
].score
!=0)
932 gd_blittext_printf_n(gd_screen
, 0, (i
+2)*gd_line_height(), c
, "%2d %6d %s", i
+1, scores
[i
].score
, scores
[i
].name
);
938 while (SDL_PollEvent(&event
)) {
939 switch (event
.type
) {
945 switch (event
.key
.keysym
.sym
) {
951 current
=current
->prev
;
957 /* if showing a cave, go to next cave (if any) */
958 if (current
->next
!=NULL
)
959 current
=current
->next
;
962 /* if showing game, show first cave. */
972 /* other keys do nothing */
976 /* other events do nothing */
988 gd_show_cave_info(GdCave
*show_cave
)
991 const int screen_height
=gd_screen
->h
/gd_font_height();
993 GList
*current
=NULL
; /* current cave to view */
996 current
=g_list_find(gd_caveset
, show_cave
);
997 gd_backup_and_dark_screen();
998 gd_title_line("CAVESET INFORMATION");
999 gd_status_line("LEFT, RIGHT: CAVE SPACE: EXIT");
1002 while (!finished
&& !gd_quit
) {
1009 /* current cave or game */
1015 for (i
=1; i
<screen_height
-1; i
++)
1016 gd_clear_line(gd_screen
, i
*gd_font_height());
1018 gd_blittext_n(gd_screen
, -1, gd_font_height(), GD_GDASH_YELLOW
, cave
?cave
->name
:gd_caveset_data
->name
);
1019 y
=gd_font_height()+2*gd_line_height();
1022 text
=g_string_new(NULL
);
1024 /* ... FOR CAVESET */
1026 if (!g_str_equal(gd_caveset_data
->author
, "")) {
1027 g_string_append_printf(text
, "%s", gd_caveset_data
->author
);
1028 if (!g_str_equal(gd_caveset_data
->date
, ""))
1029 g_string_append_printf(text
, ", %s", gd_caveset_data
->date
);
1030 g_string_append_c(text
, '\n');
1033 if (!g_str_equal(gd_caveset_data
->description
, ""))
1034 g_string_append_printf(text
, "%s\n", gd_caveset_data
->description
);
1036 if (gd_caveset_data
->story
->len
>0) {
1037 g_string_append(text
, gd_caveset_data
->story
->str
);
1038 /* if the cave story has no enter on the end, add one. */
1039 if (text
->str
[text
->len
-1]!='\n')
1040 g_string_append_c(text
, '\n');
1042 if (gd_caveset_data
->remark
->len
>0) {
1043 g_string_append(text
, gd_caveset_data
->remark
->str
);
1044 /* if the cave story has no enter on the end, add one. */
1045 if (text
->str
[text
->len
-1]!='\n')
1046 g_string_append_c(text
, '\n');
1050 if (!g_str_equal(cave
->author
, "")) {
1051 g_string_append_printf(text
, "%s", cave
->author
);
1052 if (!g_str_equal(cave
->date
, ""))
1053 g_string_append_printf(text
, ", %s", cave
->date
);
1054 g_string_append_c(text
, '\n');
1057 if (!g_str_equal(cave
->description
, ""))
1058 g_string_append_printf(text
, "%s\n", cave
->description
);
1060 if (cave
->story
->len
>0) {
1061 g_string_append(text
, cave
->story
->str
);
1062 /* if the cave story has no enter on the end, add one. */
1063 if (text
->str
[text
->len
-1]!='\n')
1064 g_string_append_c(text
, '\n');
1066 if (cave
->remark
->len
>0) {
1067 g_string_append(text
, cave
->remark
->str
);
1068 /* if the cave story has no enter on the end, add one. */
1069 if (text
->str
[text
->len
-1]!='\n')
1070 g_string_append_c(text
, '\n');
1073 wrapped
=gd_wrap_text(text
->str
, gd_screen
->w
/gd_font_width()-2);
1074 gd_blittext_n(gd_screen
, gd_font_width(), gd_line_height()*2, GD_GDASH_LIGHTBLUE
, wrapped
);
1076 g_string_free(text
, TRUE
);
1077 SDL_Flip(gd_screen
);
1079 /* process events */
1080 SDL_WaitEvent(NULL
);
1081 while (SDL_PollEvent(&event
)) {
1082 switch (event
.type
) {
1088 switch (event
.key
.keysym
.sym
) {
1094 current
=current
->prev
;
1099 if (current
!=NULL
) {
1100 /* if showing a cave, go to next cave (if any) */
1101 if (current
->next
!=NULL
)
1102 current
=current
->next
;
1105 /* if showing game, show first cave. */
1115 /* other keys do nothing */
1119 /* other events do nothing */
1125 gd_restore_screen();
1136 while (!gd_quit
&& !stop
) {
1139 while(SDL_PollEvent(&event
)) {
1140 switch(event
.type
) {
1156 gd_wait_for_key_releases();
1164 gd_help(const char **strings
)
1170 /* remember screen contents */
1171 gd_backup_and_dark_screen();
1173 gd_title_line("GDASH HELP");
1174 gd_status_line("SPACE: EXIT");
1176 numstrings
=g_strv_length((gchar
**) strings
);
1179 for (n
=0; n
<numstrings
; n
+=2)
1180 charwidth
=MAX(charwidth
, strlen(strings
[n
])+1+strlen(strings
[n
+1]));
1181 x1
=gd_screen
->w
/2-charwidth
*gd_font_width()/2;
1183 y
=(gd_screen
->h
-numstrings
*(gd_line_height())/2)/2;
1184 for (n
=0; n
<numstrings
; n
+=2) {
1187 x
=gd_blittext_printf_n(gd_screen
, x1
, y
, GD_GDASH_YELLOW
, "%s ", strings
[n
]);
1188 x
=gd_blittext_printf_n(gd_screen
, x
, y
, GD_GDASH_LIGHTBLUE
, "%s", strings
[n
+1]);
1190 y
+=gd_line_height();
1193 SDL_Flip(gd_screen
);
1195 wait_for_keypress();
1197 /* copy screen contents back */
1198 gd_restore_screen();
1202 draw_window(SDL_Rect
*rect
)
1208 SDL_FillRect(gd_screen
, rect
, SDL_MapRGB(gd_screen
->format
, 64, 64, 64));
1213 SDL_FillRect(gd_screen
, rect
, SDL_MapRGB(gd_screen
->format
, 128, 128, 128));
1218 SDL_FillRect(gd_screen
, rect
, SDL_MapRGB(gd_screen
->format
, 0, 0, 0));
1223 gd_message(const char *message
)
1231 wrapped
=gd_wrap_text(message
, 38);
1232 /* wrapped always has a \n on the end, so this returns at least 2 */
1233 lines
=gd_lines_in_text(wrapped
);
1235 height
=(1+lines
)*gd_font_height();
1236 gd_backup_and_dark_screen();
1237 gd_status_line("SPACE: CONTINUE");
1239 y1
=(gd_screen
->h
-height
)/2;
1240 rect
.x
=gd_font_width();
1241 rect
.w
=gd_screen
->w
-2*gd_font_width();
1245 SDL_SetClipRect(gd_screen
, &rect
);
1247 /* lines is at least 2. so 3 and above means multiple lines */
1253 gd_blittext_n(gd_screen
, x
, y1
+gd_font_height(), GD_GDASH_WHITE
, wrapped
);
1254 SDL_Flip(gd_screen
);
1255 wait_for_keypress();
1257 SDL_SetClipRect(gd_screen
, NULL
);
1258 gd_restore_screen();
1263 gd_input_string(const char *title
, const char *current
)
1267 gboolean enter
, escape
;
1272 gd_backup_and_black_screen();
1274 height
=6*gd_line_height();
1275 y1
=(gd_screen
->h
-height
)/2; /* middle of the screen */
1278 rect
.w
=gd_screen
->w
-2*8;
1281 SDL_SetClipRect(gd_screen
, &rect
);
1283 width
=rect
.w
/gd_font_width();
1285 gd_blittext_n(gd_screen
, -1, y1
+gd_line_height(), GD_GDASH_WHITE
, title
);
1287 text
=g_string_new(current
);
1289 /* setup keyboard */
1290 SDL_EnableUNICODE(1);
1294 while (!gd_quit
&& !enter
&& !escape
) {
1298 n
=(n
+1)%10; /* for blinking cursor */
1299 gd_clear_line(gd_screen
, y1
+3*gd_line_height());
1300 len
=g_utf8_strlen(text
->str
, -1);
1302 x
=-1; /* if fits on screen (+1 for cursor), centered */
1304 x
=rect
.x
+rect
.w
-(len
+1)*gd_font_width(); /* otherwise show end, +1 for cursor */
1306 gd_blittext_printf_n(gd_screen
, x
, y1
+3*gd_line_height(), GD_GDASH_WHITE
, "%s%c", text
->str
, n
>=5?'_':' ');
1307 SDL_Flip(gd_screen
);
1309 while(SDL_PollEvent(&event
))
1310 switch(event
.type
) {
1316 if (event
.key
.keysym
.sym
==SDLK_RETURN
)
1319 if (event
.key
.keysym
.sym
==SDLK_ESCAPE
)
1322 if (event
.key
.keysym
.sym
==SDLK_BACKSPACE
|| event
.key
.keysym
.sym
==SDLK_DELETE
) {
1323 /* delete one character from the end */
1325 char *ptr
=text
->str
+text
->len
; /* string pointer + length: points to the terminating zero */
1327 ptr
=g_utf8_prev_char(ptr
); /* step back one utf8 character */
1328 g_string_truncate(text
, ptr
-text
->str
);
1332 if (event
.key
.keysym
.unicode
!=0)
1333 g_string_append_unichar(text
, event
.key
.keysym
.unicode
);
1340 /* forget special keyboard settings we needed here */
1341 SDL_EnableUNICODE(0);
1342 /* restore screen */
1343 SDL_SetClipRect(gd_screen
, NULL
);
1344 gd_restore_screen();
1346 gd_wait_for_key_releases();
1348 /* if quit, return nothing. */
1353 return g_string_free(text
, FALSE
);
1354 /* here must be escape=TRUE, we return NULL as no string is really entered */
1355 g_string_free(text
, TRUE
);
1359 /* select a keysim for some action. returns -1 if error, or the keysym. */
1361 gd_select_key(const char *title
)
1363 const int height
=5*gd_line_height();
1367 guint key
=0; /* default value to avoid compiler warning */
1369 y1
=(gd_screen
->h
-height
)/2;
1370 rect
.x
=gd_font_width();
1371 rect
.w
=gd_screen
->w
-2*gd_font_width();
1374 gd_backup_and_black_screen();
1376 gd_blittext_n(gd_screen
, -1, y1
+gd_line_height(), GD_GDASH_WHITE
, title
);
1377 gd_blittext_n(gd_screen
, -1, y1
+3*gd_line_height(), GD_GDASH_GRAY2
, "Press desired key for action!");
1378 SDL_Flip(gd_screen
);
1381 while (!gd_quit
&& !got_key
) {
1384 SDL_WaitEvent(&event
);
1385 switch(event
.type
) {
1391 key
=event
.key
.keysym
.sym
;
1396 /* other events not interesting */
1401 /* restore screen */
1402 gd_restore_screen();
1404 gd_wait_for_key_releases();
1418 const int yd
=gd_line_height();
1419 const int names_per_page
=gd_screen
->h
/yd
-3;
1420 gboolean exit
, clear
;
1425 gd_message("No error messages.");
1429 err
=g_ptr_array_new();
1430 for (iter
=gd_errors
; iter
!=NULL
; iter
=iter
->next
)
1431 g_ptr_array_add(err
, iter
->data
);
1433 /* the user has seen the errors, clear the "has new error" flag */
1434 gd_clear_error_flag();
1436 gd_backup_and_dark_screen();
1437 gd_title_line("GDASH ERRORS");
1438 gd_status_line("CRSR: SELECT C: CLEAR ESC: EXIT");
1446 while (!gd_quit
&& !exit
&& !clear
) {
1449 page
=sel
/names_per_page
;
1453 for (i
=0, cur
=page
*names_per_page
; i
<names_per_page
; i
++, cur
++) {
1454 GdErrorMessage
*m
=g_ptr_array_index(err
, cur
);
1457 col
=cur
==sel
?GD_GDASH_YELLOW
:GD_GDASH_LIGHTBLUE
;
1459 gd_clear_line(gd_screen
, (i
+2)*yd
);
1460 if (cur
<err
->len
) /* may not be as much filenames as it would fit on the screen */
1461 gd_blittext_n(gd_screen
, 0, (i
+2)*yd
, col
, m
->message
);
1464 SDL_Flip(gd_screen
);
1469 gd_process_pending_events();
1471 /* cursor movement */
1473 sel
=gd_clamp(sel
-1, 0, err
->len
-1), redraw
=TRUE
;
1475 sel
=gd_clamp(sel
+1, 0, err
->len
-1), redraw
=TRUE
;
1476 if (gd_keystate
[SDLK_PAGEUP
])
1477 sel
=gd_clamp(sel
-names_per_page
, 0, err
->len
-1), redraw
=TRUE
;
1478 if (gd_keystate
[SDLK_PAGEDOWN
])
1479 sel
=gd_clamp(sel
+names_per_page
, 0, err
->len
-1), redraw
=TRUE
;
1480 if (gd_keystate
[SDLK_HOME
])
1482 if (gd_keystate
[SDLK_END
])
1483 sel
=err
->len
-1, redraw
=TRUE
;
1485 if (gd_fire() || gd_keystate
[SDLK_RETURN
] || gd_keystate
[SDLK_SPACE
])
1486 /* show one error */
1488 gd_wait_for_key_releases();
1489 gd_show_error(g_ptr_array_index(err
, sel
));
1492 if (gd_keystate
[SDLK_c
])
1495 if (gd_keystate
[SDLK_ESCAPE
])
1501 /* wait until the user releases return and escape, as it might be passed to the caller accidentally */
1502 /* also wait because we do not want to process one enter keypress more than once */
1503 gd_wait_for_key_releases();
1505 g_ptr_array_free(err
, TRUE
);
1510 gd_restore_screen();
1515 gd_ask_yes_no(const char *question
, const char *answer1
, const char *answer2
, gboolean
*result
)
1520 gboolean success
, escape
;
1523 gd_backup_and_black_screen();
1524 height
=5*gd_line_height();
1525 y1
=(gd_screen
->h
-height
)/2; /* middle of the screen */
1528 rect
.w
=gd_screen
->w
-2*8;
1531 SDL_SetClipRect(gd_screen
, &rect
);
1533 gd_blittext_n(gd_screen
, -1, y1
+gd_line_height(), GD_GDASH_WHITE
, question
);
1534 gd_blittext_printf_n(gd_screen
, -1, y1
+3*gd_line_height(), GD_GDASH_WHITE
, "N: %s, Y: %s", answer1
, answer2
);
1535 SDL_Flip(gd_screen
);
1537 success
=escape
=FALSE
;
1539 while (!gd_quit
&& !success
&& !escape
) {
1544 while(SDL_PollEvent(&event
))
1545 switch(event
.type
) {
1551 if (event
.key
.keysym
.sym
==SDLK_y
) { /* user pressed yes */
1556 if (event
.key
.keysym
.sym
==SDLK_n
) { /* user pressed no */
1561 if (event
.key
.keysym
.sym
==SDLK_ESCAPE
) /* user pressed escape */
1568 /* restore screen */
1569 gd_restore_screen();
1571 gd_wait_for_key_releases();
1573 SDL_SetClipRect(gd_screen
, NULL
);
1575 /* this will return true, if y or n pressed. returns false, if escape, or quit event */
1581 gd_discard_changes()
1583 gboolean answered
, result
;
1585 /* if not edited, simply answer yes. */
1586 if (!gd_caveset_edited
)
1589 /* if the caveset is edited, ask the user if to save. */
1590 answered
=gd_ask_yes_no("New replays are added. Discard them?", "Cancel", "Discard", &result
);
1591 if (!answered
|| !result
)
1592 /* if does not want to discard, say false */
1605 /* remember screen contents */
1606 gd_backup_and_dark_screen();
1608 gd_title_line("GDASH LICENSE");
1609 gd_status_line("SPACE: EXIT");
1611 wrapped
=gd_wrap_text(gd_about_license
, gd_screen
->w
/gd_font_width());
1612 gd_blittext_n(gd_screen
, 0, gd_line_height(), GD_GDASH_LIGHTBLUE
, wrapped
);
1614 SDL_Flip(gd_screen
);
1616 wait_for_keypress();
1618 /* copy screen contents back */
1619 gd_restore_screen();
1625 help_writeattrib(int x
, int y
, const char *name
, const char *content
)
1627 const int yd
=gd_line_height();
1629 gd_blittext_n(gd_screen
, x
, y
, GD_GDASH_YELLOW
, name
);
1630 gd_blittext_n(gd_screen
, x
+10, y
+yd
, GD_GDASH_LIGHTBLUE
, content
);
1631 if (strchr(content
, '\n'))
1638 help_writeattribs(int x
, int y
, const char *name
, const char *content
[])
1640 const int yd
=gd_line_height();
1642 if (content
!=NULL
&& content
[0]!=NULL
) {
1645 gd_blittext_n(gd_screen
, x
, y
, GD_GDASH_YELLOW
, name
);
1648 for (i
=0; content
[i
]!=NULL
; i
++) {
1649 gd_blittext_n(gd_screen
, x
+10, y
, GD_GDASH_LIGHTBLUE
, content
[i
]);
1664 /* remember screen contents */
1665 gd_backup_and_dark_screen();
1667 gd_title_line("GDASH " PACKAGE_VERSION
);
1668 gd_status_line("SPACE: EXIT");
1672 y
=help_writeattrib(-1, y
, "", gd_about_comments
);
1673 y
=help_writeattrib(10, y
, "WEBSITE", gd_about_website
);
1674 y
=help_writeattribs(10, y
, "AUTHORS", gd_about_authors
);
1675 y
=help_writeattribs(10, y
, "ARTISTS", gd_about_artists
);
1676 y
=help_writeattribs(10, y
, "DOCUMENTERS", gd_about_documenters
);
1677 /* extern char *gd_about_translator_credits; - NO TRANSLATION IN SDASH */
1678 SDL_Flip(gd_screen
);
1680 wait_for_keypress();
1682 /* copy screen contents back */
1683 gd_restore_screen();
1688 gd_show_error(GdErrorMessage
*error
)
1695 wrapped
=gd_wrap_text(error
->message
, gd_screen
->w
/gd_font_width()-2);
1696 gd_backup_and_dark_screen();
1697 lines
=g_strsplit_set(wrapped
, "\n", -1);
1698 linenum
=g_strv_length(lines
);
1700 yd
=gd_line_height();
1701 y1
=gd_screen
->h
/2-(linenum
+1)*yd
/2;
1703 gd_title_line("GDASH ERROR");
1704 gd_status_line("ANY KEY: CONTINUE");
1705 for (i
=0; lines
[i
]!=NULL
; i
++)
1706 gd_blittext_n(gd_screen
, 8, y1
+(i
+1)*yd
, GD_GDASH_WHITE
, lines
[i
]);
1707 SDL_Flip(gd_screen
);
1709 wait_for_keypress();
1711 gd_restore_screen();
1718 show_comment(const char *text
)
1725 wrapped
=gd_wrap_text(text
, gd_screen
->w
/gd_font_width()-2);
1726 gd_backup_and_dark_screen();
1727 lines
=g_strsplit_set(wrapped
, "\n", -1);
1728 linenum
=g_strv_length(lines
);
1730 yd
=gd_line_height();
1731 y1
=gd_screen
->h
/2-(linenum
+1)*yd
/2;
1733 gd_title_line("REPLAY COMMENT");
1734 gd_status_line("ANY KEY: CONTINUE");
1735 for (i
=0; lines
[i
]!=NULL
; i
++)
1736 gd_blittext_n(gd_screen
, 8, y1
+(i
+1)*yd
, GD_GDASH_WHITE
, lines
[i
]);
1737 SDL_Flip(gd_screen
);
1739 wait_for_keypress();
1741 gd_restore_screen();
1749 * SDASH REPLAYS MENU
1752 gd_replays_menu(void (*play_func
) (GdCave
*cave
, GdReplay
*replay
), gboolean for_game
)
1755 /* an item stores a cave (to see its name) or a cave+replay */
1756 typedef struct _item
{
1762 const int lines_per_page
=gd_screen
->h
/gd_line_height()-5;
1764 GPtrArray
*items
=NULL
;
1768 items
=g_ptr_array_new();
1770 for (citer
=gd_caveset
; citer
!=NULL
; citer
=citer
->next
) {
1774 /* if cave has replays... */
1775 if (cave
->replays
!=NULL
) {
1779 g_ptr_array_add(items
, g_memdup(&i
, sizeof(i
)));
1781 /* add replays, too */
1782 for (riter
=cave
->replays
; riter
!=NULL
; riter
=riter
->next
) {
1783 i
.replay
=(GdReplay
*)riter
->data
;
1784 g_ptr_array_add(items
, g_memdup(&i
, sizeof(i
)));
1789 if (items
->len
==0) {
1790 gd_message("No replays.");
1793 gd_backup_and_dark_screen();
1795 gd_status_line("CRSR:MOVE C:COMMENT S:SAVED ESC:EXIT");
1797 gd_status_line("CRSR: MOVE SPACE: SAVE ESC: EXIT");
1801 while (!finished
&& !gd_quit
) {
1802 page
=current
/lines_per_page
; /* show 18 lines per page */
1806 gd_clear_line(gd_screen
, 0); /* for empty top row */
1807 for (n
=0; n
<lines_per_page
; n
++) { /* for empty caves&replays rows */
1808 int y
=(n
+2)*gd_line_height();
1810 gd_clear_line(gd_screen
, y
);
1813 gd_title_line("GDASH REPLAYS, PAGE %d/%d", page
+1, items
->len
/lines_per_page
+1);
1814 for (n
=0; n
<lines_per_page
&& page
*lines_per_page
+n
<items
->len
; n
++) {
1815 int pos
=page
*lines_per_page
+n
;
1816 GdColor col_cave
=current
==pos
?GD_GDASH_YELLOW
:GD_GDASH_LIGHTBLUE
; /* selected=yellow, otherwise blue */
1817 GdColor col
=current
==pos
?GD_GDASH_YELLOW
:GD_GDASH_GRAY3
; /* selected=yellow, otherwise blue */
1821 i
=(Item
*) g_ptr_array_index(items
, pos
);
1824 y
=(n
+2)*gd_line_height();
1827 /* no replay pointer: this is a cave, so write its name. */
1828 x
=gd_blittext_n(gd_screen
, x
, y
, col_cave
, i
->cave
->name
);
1834 /* successful or not */
1835 x
=gd_blittext_printf_n(gd_screen
, x
, y
, i
->replay
->success
?GD_GDASH_GREEN
:GD_GDASH_RED
, " %c ", GD_BALL_CHAR
);
1838 g_utf8_strncpy(buffer
, i
->replay
->player_name
, 20); /* name: maximum 20 characters */
1839 x
=gd_blittext_n(gd_screen
, x
, y
, col
, buffer
);
1840 /* put 20-length spaces */
1841 for (c
=g_utf8_strlen(buffer
, -1); c
<20; c
++)
1842 x
=gd_blittext_n(gd_screen
, x
, y
, col
, " ");
1843 /* always put one space after name */
1844 x
=gd_blittext_n(gd_screen
, x
, y
, col
, " ");
1847 if (!g_str_equal(i
->replay
->date
, ""))
1848 comm
=i
->replay
->date
;
1852 g_utf8_strncpy(buffer
, comm
, 11); /* date: maximum 11 characters */
1853 x
=gd_blittext_n(gd_screen
, x
, y
, col
, comm
);
1854 /* put 20-length spaces */
1855 for (c
=g_utf8_strlen(buffer
, -1); c
<11; c
++)
1856 x
=gd_blittext_n(gd_screen
, x
, y
, col
, " ");
1859 x
=gd_blittext_printf_n(gd_screen
, x
, y
, col
, " %d", i
->replay
->level
+1);
1861 x
=gd_blittext_printf_n(gd_screen
, x
, y
, col
, "%c", i
->replay
->comment
->len
!=0?GD_COMMENT_CHAR
:' ');
1862 /* saved - check box */
1863 x
=gd_blittext_printf_n(gd_screen
, x
, y
, col
, "%c", i
->replay
->saved
?GD_CHECKED_BOX_CHAR
:GD_UNCHECKED_BOX_CHAR
);
1866 SDL_Flip(gd_screen
); /* draw to usere's screen */
1868 SDL_WaitEvent(&event
);
1869 switch (event
.type
) {
1875 switch (event
.key
.keysym
.sym
) {
1878 current
=gd_clamp(current
-1, 1, items
->len
-1);
1879 } while (((Item
*)g_ptr_array_index(items
, current
))->replay
==NULL
&& current
>=1);
1883 current
=gd_clamp(current
+1, 1, items
->len
-1);
1884 } while (((Item
*)g_ptr_array_index(items
, current
))->replay
==NULL
&& current
<items
->len
);
1889 Item
*i
=(Item
*)g_ptr_array_index(items
, current
);
1891 if (i
->replay
&& !g_str_equal(i
->replay
->comment
->str
, ""))
1892 show_comment(i
->replay
->comment
->str
);
1896 /* only allow toggling the "saved" flag, if we are in a game. not in the replay->video converter app. */
1898 Item
*i
=(Item
*)g_ptr_array_index(items
, current
);
1901 i
->replay
->saved
=!i
->replay
->saved
;
1902 gd_caveset_edited
=TRUE
;
1909 Item
*i
=(Item
*)g_ptr_array_index(items
, current
);
1912 gd_backup_and_black_screen();
1913 SDL_Flip(gd_screen
);
1914 play_func(i
->cave
, i
->replay
);
1915 gd_restore_screen();
1924 current
=gd_clamp(current
-lines_per_page
, 0, items
->len
-1);
1928 current
=gd_clamp(current
+lines_per_page
, 0, items
->len
-1);
1932 /* other keys do nothing */
1938 /* other events do nothing */
1943 gd_restore_screen();
1946 /* set the theme. other variables are already set by the above code. */
1947 /* forget list of themes */
1948 g_ptr_array_foreach(items
, (GFunc
) g_free
, NULL
);
1949 g_ptr_array_free(items
, TRUE
);