1 #include <glib-object.h>
3 #include <glib/gi18n-lib.h>
5 #include <libpeas/peas.h>
6 #include <libpeas-gtk/peas-gtk.h>
7 #include "chimara-frotz-plugin.h"
8 #include "frotz/frotz.h"
10 typedef struct _ChimaraFrotzPluginPrivate {
12 gboolean random_seed_set;
14 } ChimaraFrotzPluginPrivate;
16 #define CHIMARA_FROTZ_PLUGIN_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), CHIMARA_TYPE_FROTZ_PLUGIN, ChimaraFrotzPluginPrivate))
17 #define CHIMARA_FROTZ_PLUGIN_USE_PRIVATE ChimaraFrotzPluginPrivate *priv = CHIMARA_FROTZ_PLUGIN_PRIVATE(self)
19 static void chimara_frotz_plugin_configurable_init(PeasGtkConfigurableInterface *);
20 static GtkWidget *chimara_frotz_plugin_create_configure_widget(PeasGtkConfigurable *);
22 G_DEFINE_DYNAMIC_TYPE_EXTENDED(ChimaraFrotzPlugin, chimara_frotz_plugin, PEAS_TYPE_EXTENSION_BASE, 0,
23 G_IMPLEMENT_INTERFACE_DYNAMIC(PEAS_GTK_TYPE_CONFIGURABLE, chimara_frotz_plugin_configurable_init));
30 PROP_QUETZAL_SAVE_FORMAT,
32 PROP_EXPAND_ABBREVIATIONS,
35 PROP_TRANSCRIPT_COLUMNS,
40 peas_register_types(PeasObjectModule *module)
42 chimara_frotz_plugin_register_type(G_TYPE_MODULE(module));
43 peas_object_module_register_extension_type(module, PEAS_GTK_TYPE_CONFIGURABLE, CHIMARA_TYPE_FROTZ_PLUGIN);
47 chimara_frotz_plugin_init(ChimaraFrotzPlugin *self)
49 CHIMARA_FROTZ_PLUGIN_USE_PRIVATE;
50 priv->random_seed_set = FALSE;
51 priv->tandy_bit = FALSE;
54 #define PROCESS_FLAG(name) ((flags & (name))? 1 : 0)
57 chimara_frotz_plugin_set_property(GObject *self, unsigned prop_id, const GValue *value, GParamSpec *pspec)
59 CHIMARA_FROTZ_PLUGIN_USE_PRIVATE;
62 case PROP_DEBUG_MESSAGES:
64 unsigned flags = g_value_get_uint(value);
65 option_attribute_assignment = PROCESS_FLAG(CHIMARA_FROTZ_DEBUG_ATTRIBUTE_SETTING);
66 option_attribute_testing = PROCESS_FLAG(CHIMARA_FROTZ_DEBUG_ATTRIBUTE_TESTING);
67 option_object_movement = PROCESS_FLAG(CHIMARA_FROTZ_DEBUG_OBJECT_MOVEMENT);
68 option_object_locating = PROCESS_FLAG(CHIMARA_FROTZ_DEBUG_OBJECT_LOCATING);
69 g_object_notify(self, "debug-messages");
72 case PROP_IGNORE_ERRORS:
73 option_ignore_errors = g_value_get_boolean(value);
74 g_object_notify(self, "ignore-errors");
76 case PROP_PIRACY_MODE:
77 option_piracy = g_value_get_boolean(value);
78 g_object_notify(self, "piracy-mode");
80 case PROP_QUETZAL_SAVE_FORMAT:
81 option_save_quetzal = g_value_get_boolean(value);
82 g_object_notify(self, "quetzal-save-format");
85 priv->tandy_bit = g_value_get_boolean(value);
86 g_object_notify(self, "tandy-bit");
88 case PROP_EXPAND_ABBREVIATIONS:
89 option_expand_abbreviations = g_value_get_boolean(value);
90 g_object_notify(self, "expand-abbreviations");
92 case PROP_RANDOM_SEED:
93 priv->random_seed = g_value_get_int(value);
94 g_object_notify(self, "random-seed");
96 case PROP_RANDOM_SEED_SET:
97 priv->random_seed_set = g_value_get_boolean(value);
98 g_object_notify(self, "random-seed-set");
100 case PROP_TRANSCRIPT_COLUMNS:
101 option_script_cols = g_value_get_uint(value);
102 g_object_notify(self, "transcript-columns");
104 case PROP_UNDO_SLOTS:
105 option_undo_slots = g_value_get_uint(value);
106 g_object_notify(self, "undo-slots");
109 G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
116 chimara_frotz_plugin_get_property(GObject *self, unsigned prop_id, GValue *value, GParamSpec *pspec)
118 CHIMARA_FROTZ_PLUGIN_USE_PRIVATE;
121 case PROP_DEBUG_MESSAGES:
123 unsigned flags = option_attribute_assignment << 0
124 | option_attribute_testing << 1
125 | option_object_movement << 2
126 | option_object_locating << 3;
127 g_value_set_uint(value, flags);
130 case PROP_IGNORE_ERRORS:
131 g_value_set_boolean(value, option_ignore_errors);
133 case PROP_PIRACY_MODE:
134 g_value_set_boolean(value, option_piracy);
136 case PROP_QUETZAL_SAVE_FORMAT:
137 g_value_set_boolean(value, option_save_quetzal);
140 g_value_set_boolean(value, priv->tandy_bit);
142 case PROP_EXPAND_ABBREVIATIONS:
143 g_value_set_boolean(value, option_expand_abbreviations);
145 case PROP_RANDOM_SEED:
146 g_value_set_int(value, priv->random_seed);
148 case PROP_RANDOM_SEED_SET:
149 g_value_set_boolean(value, priv->random_seed_set);
151 case PROP_TRANSCRIPT_COLUMNS:
152 g_value_set_uint(value, option_script_cols);
154 case PROP_UNDO_SLOTS:
155 g_value_set_uint(value, option_undo_slots);
158 G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
163 chimara_frotz_plugin_class_init(ChimaraFrotzPluginClass *klass)
165 GObjectClass *object_class = G_OBJECT_CLASS(klass);
166 object_class->set_property = chimara_frotz_plugin_set_property;
167 object_class->get_property = chimara_frotz_plugin_get_property;
170 g_type_class_add_private(klass, sizeof(ChimaraFrotzPluginPrivate));
174 * ChimaraFrotzPlugin:debug-messages:
176 * Set of flags to control which debugging messages, if any, Frotz prints
177 * while interpreting the story. See #ChimaraFrotzDebugFlags.
179 /* TODO: register a flags type and use g_param_spec_flags() */
180 g_object_class_install_property(object_class, PROP_DEBUG_MESSAGES,
181 g_param_spec_uint("debug-messages", _("Kinds of debugging messages"),
182 _("Control which kinds of debugging messages to print"),
183 0, 255, CHIMARA_FROTZ_DEBUG_NONE,
184 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
186 * ChimaraFrotzPlugin:ignore-errors:
188 * Setting this property to %TRUE will cause the interpreter to ignore
189 * fatal Z-machine runtime errors.
191 g_object_class_install_property(object_class, PROP_IGNORE_ERRORS,
192 g_param_spec_boolean("ignore-errors", _("Ignore errors"),
193 _("Do not warn the user about fatal Z-machine errors"), FALSE,
194 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
196 * ChimaraFrotzPlugin:piracy-mode:
198 * The Z-machine specification defines a facility for games to ask the
199 * interpreter they are running on whether this copy of the game is pirated.
200 * How the interpreter is supposed to magically determine that it is running
201 * pirate software is unclear, and so the majority of games and interpreters
202 * ignore this feature. Set this property to %TRUE if you want the
203 * interpreter to pretend it has detected a pirated game.
205 g_object_class_install_property(object_class, PROP_PIRACY_MODE,
206 g_param_spec_boolean("piracy-mode", _("Piracy mode"),
207 _("Pretend the game is pirated"), FALSE,
208 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
210 * ChimaraFrotzPlugin:quetzal-save-format:
212 * If set to %TRUE, use the newer-style Quetzal format for saved games.
213 * (This is the default.)
215 g_object_class_install_property(object_class, PROP_QUETZAL_SAVE_FORMAT,
216 g_param_spec_boolean("quetzal-save-format", _("Use Quetzal save format"),
217 _("Use the Quetzal format for saved games"), TRUE,
218 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
220 * ChimaraFrotzPlugin:tandy-bit:
222 * Some early Infocom games were sold by the Tandy Corporation. Setting this
223 * property to %TRUE changes the wording of some Version 3 Infocom games
224 * slightly, so as to be less offensive. See <ulink
225 * url="http://www.ifarchive.org/if-archive/infocom/info/tandy_bits.html">
226 * http://www.ifarchive.org/if-archive/infocom/info/tandy_bits.html</ulink>.
228 * Only affects Z-machine interpreters.
230 g_object_class_install_property(object_class, PROP_TANDY_BIT,
231 g_param_spec_boolean("tandy-bit", _("Tandy bit"),
232 _("Censor certain Infocom games"), FALSE,
233 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
235 * ChimaraIF:expand-abbreviations:
237 * Most Z-machine games, in particular ones compiled with the Inform
238 * library, support the following one-letter abbreviations:
240 * <member>D — Down</member>
241 * <member>E — East</member>
242 * <member>G — aGain</member>
243 * <member>I — Inventory</member>
244 * <member>L — Look</member>
245 * <member>N — North</member>
246 * <member>O — Oops</member>
247 * <member>Q — Quit</member>
248 * <member>S — South</member>
249 * <member>U — Up</member>
250 * <member>W — West</member>
251 * <member>X — eXamine</member>
252 * <member>Y — Yes</member>
253 * <member>Z — wait (ZZZZ...)</member>
255 * Some early Infocom games might not recognize these abbreviations.
256 * However, Frotz can expand G, X, and Z regardless of what the game
257 * recognizes. Setting this property to %TRUE will cause Frotz to expand
258 * these abbreviations to the full words before passing the commands on to
261 g_object_class_install_property(object_class, PROP_EXPAND_ABBREVIATIONS,
262 g_param_spec_boolean("expand-abbreviations", _("Expand abbreviations"),
263 _("Expand abbreviations such as X for EXAMINE"), FALSE,
264 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
266 * ChimaraFrotzPlugin:random-seed:
268 * If the #ChimaraFrotzPlugin:random-seed-set property is %TRUE, then the
269 * interpreter will use the value of this property as a seed for the random
270 * number generator. Use this feature to duplicate sequences of random
271 * numbers for testing games.
273 * Note that the value -1 will cause Frotz to pick an arbitrary seed even
274 * when #ChimaraFrotzPlugin:random-seed-set is %TRUE.
276 g_object_class_install_property(object_class, PROP_RANDOM_SEED,
277 g_param_spec_int("random-seed", _("Random seed"),
278 _("Seed for the random number generator"), G_MININT, G_MAXINT, 0,
279 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
281 * ChimaraFrotzPlugin:random-seed-set:
283 * Whether to use or ignore the #ChimaraFrotzPlugin:random-seed property.
285 g_object_class_install_property(object_class, PROP_RANDOM_SEED_SET,
286 g_param_spec_boolean("random-seed-set", _("Random seed set"),
287 _("Whether the seed for the random number generator should be set manually"), FALSE,
288 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
290 * ChimaraFrotzPlugin:transcript-columns:
292 * How many columns to make the transcript output.
294 g_object_class_install_property(object_class, PROP_TRANSCRIPT_COLUMNS,
295 g_param_spec_uint("transcript-columns", _("Transcript columns"),
296 _("Number of columns for transcript output"),
298 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
300 * ChimaraFrotzPlugin:undo-slots:
302 * How many slots to reserve for multiple Undo commands.
304 g_object_class_install_property(object_class, PROP_UNDO_SLOTS,
305 g_param_spec_uint("undo-slots", _("Undo slots"),
306 _("Number of slots to reserve for multiple undo"),
307 0, MAX_UNDO_SLOTS, MAX_UNDO_SLOTS,
308 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_LAX_VALIDATION | G_PARAM_STATIC_STRINGS));
312 chimara_frotz_plugin_class_finalize(ChimaraFrotzPluginClass *klass)
317 chimara_frotz_plugin_configurable_init(PeasGtkConfigurableInterface *iface)
319 iface->create_configure_widget = chimara_frotz_plugin_create_configure_widget;
322 /* Helper function to transform flags value to boolean; @data contains the
323 GINT_TO_POINTER()'ed but position (0=LSB). */
325 debug_message_flags_transform_to(GBinding *binding, const GValue *source, GValue *target, gpointer data)
327 int bit_shift = GPOINTER_TO_INT(data);
328 unsigned flags = g_value_get_uint(source);
329 g_value_set_boolean(target, flags & (1 << bit_shift));
330 return TRUE; /* success */
333 /* Reverse of debug_message_flags_transform_to(). */
335 debug_message_flags_transform_from(GBinding *binding, const GValue *source, GValue *target, gpointer data)
337 int bit_shift = GPOINTER_TO_INT(data);
338 unsigned flags = g_value_get_uint(target);
339 int new_value = g_value_get_boolean(source)? 1 : 0;
340 g_value_set_uint(target, flags & (new_value << bit_shift));
341 return TRUE; /* success */
345 chimara_frotz_plugin_create_configure_widget(PeasGtkConfigurable *self)
347 GError *error = NULL;
348 const char *datadir = peas_plugin_info_get_data_dir(peas_engine_get_plugin_info(peas_engine_get_default(), "frotz"));
349 char *glade_file = g_build_filename(datadir, "chimara-frotz-plugin.glade", NULL);
350 GtkBuilder *builder = gtk_builder_new();
351 if(!gtk_builder_add_from_file(builder, glade_file, &error)) {
353 g_critical("Error building Frotz configuration dialog: %s\n", error->message);
357 GObject *retval = gtk_builder_get_object(builder, "frotz-configure-widget");
359 /* Bind GUI widget properties to this plugin's configuration properties */
360 g_object_bind_property_full(self, "debug-messages",
361 gtk_builder_get_object(builder, "attribute-setting-button"), "active",
362 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
363 debug_message_flags_transform_to,
364 debug_message_flags_transform_from,
365 GINT_TO_POINTER(0), NULL);
366 g_object_bind_property_full(self, "debug-messages",
367 gtk_builder_get_object(builder, "attribute-testing-button"), "active",
368 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
369 debug_message_flags_transform_to,
370 debug_message_flags_transform_from,
371 GINT_TO_POINTER(1), NULL);
372 g_object_bind_property_full(self, "debug-messages",
373 gtk_builder_get_object(builder, "object-movement-button"), "active",
374 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
375 debug_message_flags_transform_to,
376 debug_message_flags_transform_from,
377 GINT_TO_POINTER(2), NULL);
378 g_object_bind_property_full(self, "debug-messages",
379 gtk_builder_get_object(builder, "object-location-button"), "active",
380 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
381 debug_message_flags_transform_to,
382 debug_message_flags_transform_from,
383 GINT_TO_POINTER(3), NULL);
384 g_object_bind_property(self, "ignore-errors",
385 gtk_builder_get_object(builder, "ignore-errors-button"), "active",
386 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
387 g_object_bind_property(self, "piracy-mode",
388 gtk_builder_get_object(builder, "piracy-button"), "active",
389 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
390 g_object_bind_property(self, "quetzal-save-format",
391 gtk_builder_get_object(builder, "quetzal-button"), "active",
392 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
393 g_object_bind_property(self, "tandy-bit",
394 gtk_builder_get_object(builder, "tandy-button"), "active",
395 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
396 g_object_bind_property(self, "expand-abbreviations",
397 gtk_builder_get_object(builder, "expand-abbreviations-button"), "active",
398 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
399 g_object_bind_property(self, "random-seed",
400 gtk_builder_get_object(builder, "random-seed-adjustment"), "value",
401 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
402 g_object_bind_property(self, "random-seed-set",
403 gtk_builder_get_object(builder, "random-seed-set-button"), "active",
404 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
405 g_object_bind_property(self, "random-seed-set",
406 gtk_builder_get_object(builder, "random-seed-button"), "sensitive",
407 G_BINDING_SYNC_CREATE);
408 g_object_bind_property(self, "transcript-columns",
409 gtk_builder_get_object(builder, "columns-adjustment"), "value",
410 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
411 g_object_bind_property(self, "undo-slots",
412 gtk_builder_get_object(builder, "undo-adjustment"), "value",
413 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
415 /* Make sure the widget is returned with only one reference */
416 g_object_ref_sink(G_OBJECT(retval));
417 g_object_unref(builder);
418 return GTK_WIDGET(retval);