1 /*
2 Copyright (C) 2004, 2011 Dale Mellor
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <config.h>
19
20 #include "guile-hooks.h"
21
22 #include <stdlib.h>
23 #include <dirent.h>
24 #include <stdio.h>
25
26 #include "cube.h"
27 #include "menus.h"
28
29 #include <libguile.h>
30
31 #if SCM_MAJOR_VERSION < 2
32 #define scm_to_utf8_string(name) scm_to_locale_string(name)
33 #endif
34
35 /* This function is called from the menu when the user makes a selection. The
36 data is a string which was registered with the menu system and gives the name
37 of a scheme procedure to execute. */
38
39 static void
run_scheme(GtkAction * act,SCM exp)40 run_scheme (GtkAction * act, SCM exp)
41 {
42 scm_eval (exp, scm_interaction_environment ());
43 }
44
45 /* The menu manager */
46 static GtkUIManager *uim;
47
48 static SCM
gnubik_create_menu(SCM name,SCM loc)49 gnubik_create_menu (SCM name, SCM loc)
50 {
51 char *ml = scm_to_utf8_string (name);
52 char *loc_str = NULL;
53
54 GtkActionGroup *ag = gtk_action_group_new (ml);
55
56 GtkActionEntry gae;
57
58 if (SCM_UNBNDP (loc))
59 loc_str = g_strdup ("/ui/MainMenu/scripts-menu");
60 else
61 loc_str = scm_to_locale_string (loc);
62
63 gae.name = ml;
64 gae.stock_id = NULL;
65 gae.label = ml;
66 gae.accelerator = NULL;
67 gae.tooltip = NULL;
68 gae.callback = NULL;
69
70 gtk_action_group_add_actions (ag, &gae, 1, NULL);
71
72 gtk_ui_manager_insert_action_group (uim, ag, 0);
73
74 gtk_ui_manager_add_ui (uim,
75 gtk_ui_manager_new_merge_id (uim),
76 loc_str, ml, ml, GTK_UI_MANAGER_MENU, TRUE);
77
78 char *menuloc = g_strdup_printf ("%s/%s", loc_str, ml);
79
80 SCM sml = scm_from_locale_string (menuloc);
81
82 free (ml);
83 free (menuloc);
84 free (loc_str);
85
86 return sml;
87 }
88
89 /*
90 Function callable from scheme (as gnubik-register-script) which allows a
91 script to specify a menu entry and the name of a procedure to call when that
92 menu entry is selected. Note that /Scripts/ is always appended, so all
93 scripts are forced under the Scripts main menu item.
94 */
95 static SCM
gnubik_register_script(SCM menu_location,SCM callback,SCM loc)96 gnubik_register_script (SCM menu_location, SCM callback, SCM loc)
97 {
98 char *ml = scm_to_utf8_string (menu_location);
99
100 char *loc_str = scm_to_locale_string (loc);
101
102 GtkActionGroup *ag = gtk_action_group_new (ml);
103
104 GtkActionEntry gae;
105 gae.name = ml;
106 gae.stock_id = NULL;
107 gae.label = ml;
108 gae.accelerator = NULL;
109 gae.tooltip = NULL;
110 gae.callback = G_CALLBACK (run_scheme);
111
112 scm_permanent_object (callback);
113
114 gtk_action_group_add_actions (ag, &gae, 1, callback);
115
116 gtk_ui_manager_insert_action_group (uim, ag, 0);
117
118 gtk_ui_manager_add_ui (uim, gtk_ui_manager_new_merge_id (uim),
119 loc_str, ml, ml, GTK_UI_MANAGER_MENUITEM, TRUE);
120
121 free (loc_str);
122 free (ml);
123
124 return SCM_UNSPECIFIED;
125 }
126
127
128
129
130 extern GbkGame *the_game;
131
132 /* Function callable from scheme as gnubik-cube-state which returns a structure
133 reflecting the current state of the cube. */
134
135 static SCM
gnubik_cube_state()136 gnubik_cube_state ()
137 {
138 return make_scm_cube (the_game->cube);
139 }
140
141
142
143 /* Function which, when called from scheme as gnubik-append-moves,
144 appends moves to the queue and then runs the queue. */
145 static SCM
gnubik_append_moves(SCM list)146 gnubik_append_moves (SCM list)
147 {
148 if (!gbk_game_at_end (the_game))
149 gbk_game_delete_moves (the_game, the_game->iter->next);
150
151 for (; !scm_is_null (list); list = SCM_CDR (list))
152 {
153 struct move_data *move = move_create (scm_to_int (SCM_CADAR (list)),
154 scm_to_int (SCM_CAAR (list)),
155 scm_to_int (SCM_CADDAR (list)));
156
157 gbk_game_insert_move (the_game, move, &the_game->end_of_moves);
158
159 move_unref (move);
160 }
161
162 gbk_game_replay (the_game);
163
164 return SCM_UNSPECIFIED;
165 }
166
167
168
169 /* Function to allow a guile script to display a message to the user. */
170
171 static SCM
gnubik_error_dialog(SCM message)172 gnubik_error_dialog (SCM message)
173 {
174 char *msg = scm_to_utf8_string (message);
175 error_dialog (the_game->toplevel, msg);
176 free (msg);
177
178 return SCM_UNSPECIFIED;
179 }
180
181
182
183
184 /* Function to scan the named directory for all files with a .scm extension, and
185 execute the contents of each file. */
186 static void
read_script_directory(const char * dir_name)187 read_script_directory (const char *dir_name)
188 {
189 static char buffer[1024];
190
191 DIR *directory = opendir (dir_name);
192
193 if (directory)
194 {
195 struct dirent *entry;
196
197 for (entry = readdir (directory); entry; entry = readdir (directory))
198
199 if (strcmp (".scm", entry->d_name + strlen (entry->d_name) - 4) == 0)
200 {
201 snprintf (buffer, 1024, "%s/%s", dir_name, entry->d_name);
202
203 scm_primitive_load (scm_from_locale_string (buffer));
204 }
205
206 closedir (directory);
207 }
208 }
209
210
211
212
213 /* This function initializes the scheme world for us, and once the scripts have
214 all been run, it returns the requested menu structure to the caller. Before
215 running the scripts, however, it first makes sure all the pertinent C
216 functions are registered in the guile world. */
217
218 void
startup_guile_scripts(GtkUIManager * ui_manager)219 startup_guile_scripts (GtkUIManager * ui_manager)
220 {
221 /* Register C functions that the scheme world can access. */
222
223 scm_c_define_gsubr ("gnubik-create-menu", 1, 1, 0, gnubik_create_menu);
224
225 scm_c_define_gsubr ("gnubik-register-script",
226 3, 0, 0, gnubik_register_script);
227
228 scm_c_define_gsubr ("gnubik-cube-state", 0, 0, 0, gnubik_cube_state);
229
230 scm_c_define_gsubr ("gnubik-append-moves", 1, 0, 0, gnubik_append_moves);
231
232 scm_c_define_gsubr ("gnubik-error-dialog", 1, 0, 0, gnubik_error_dialog);
233
234 uim = ui_manager;
235
236 /* Run all the initialization files in .../share/gnubik/guile, and the
237 system scripts in .../share/gnubik/scripts. */
238
239 read_script_directory (GUILEDIR);
240 read_script_directory (SCRIPTDIR);
241
242 {
243 gchar *cfd = g_strdup_printf ("%s/gnubik", g_get_user_config_dir ());
244
245 read_script_directory (cfd);
246
247 g_free (cfd);
248 }
249 }
250