1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
24 #include <glib-object.h>
25 #include <glib/gstdio.h>
28 #include <gdk/gdkkeysyms.h>
30 #ifdef HAVE_LIBDMALLOC
35 #include "../include/gschem_dialog.h"
37 /* Signal marshaller based on generated code from glib-genmarshal */
39 gschem_marshal_VOID__POINTER_STRING (GClosure
*closure
,
42 const GValue
*param_values
,
43 gpointer invocation_hint
,
44 gpointer marshal_data
)
46 typedef void (*GMarshalFunc_VOID__POINTER_STRING
) (gpointer data1
,
50 register GMarshalFunc_VOID__POINTER_STRING callback
;
51 register GCClosure
*cc
= (GCClosure
*) closure
;
52 register gpointer data1
, data2
;
54 g_return_if_fail (n_param_values
== 3);
56 if (G_CCLOSURE_SWAP_DATA (closure
)) {
57 data1
= closure
->data
;
58 data2
= g_value_peek_pointer (param_values
+ 0);
60 data1
= g_value_peek_pointer (param_values
+ 0);
61 data2
= closure
->data
;
63 callback
= (GMarshalFunc_VOID__POINTER_STRING
) (marshal_data
? marshal_data
: cc
->callback
);
66 g_value_get_pointer (param_values
+ 1),
67 (gchar
*)g_value_get_string (param_values
+ 2),
70 /* End section based on generated code from glib-genmashal */
74 PROP_SETTINGS_NAME
= 1,
85 static guint gschem_dialog_signals
[ LAST_SIGNAL
] = { 0 };
86 static GObjectClass
*gschem_dialog_parent_class
= NULL
;
88 static GKeyFile
*dialog_geometry
= NULL
;
90 #define DIALOG_GEOMETRY_STORE "gschem-dialog-geometry"
93 /*! \brief Save all geometry data into a file.
95 * \par Function Description
96 * This is called at program exit to save all window geometry data into a file
98 * \param [in] user_data unused
100 static void save_geometry_to_file(gpointer user_data
)
104 g_assert( dialog_geometry
!= NULL
);
106 data
= g_key_file_to_data(dialog_geometry
, NULL
, NULL
);
107 file
= g_build_filename(g_get_home_dir (), ".gEDA", DIALOG_GEOMETRY_STORE
,
109 g_file_set_contents(file
, data
, -1, NULL
);
115 /*! \brief GschemDialog "geometry_save" class method handler
117 * \par Function Description
118 * Save the dialog's current position and size to the passed GKeyFile
120 * \param [in] dialog The GschemDialog to save the position and size of.
121 * \param [in] key_file The GKeyFile to save the geometry data to.
122 * \param [in] group_name The group name in the key file to store the data under.
124 static void geometry_save (GschemDialog
*dialog
, GKeyFile
*key_file
, gchar
* group_name
)
126 gint x
, y
, width
, height
;
128 gtk_window_get_position (GTK_WINDOW (dialog
), &x
, &y
);
129 gtk_window_get_size (GTK_WINDOW (dialog
), &width
, &height
);
131 g_key_file_set_integer (key_file
, group_name
, "x", x
);
132 g_key_file_set_integer (key_file
, group_name
, "y", y
);
133 g_key_file_set_integer (key_file
, group_name
, "width", width
);
134 g_key_file_set_integer (key_file
, group_name
, "height", height
);
138 /*! \brief GschemDialog "geometry_restore" class method handler
140 * \par Function Description
141 * Restore dialog's last position and size from the passed GKeyFile
143 * \param [in] dialog The GschemDialog to restore the position and size of.
144 * \param [in] key_file The GKeyFile to load the geometry data from.
145 * \param [in] group_name The group name in the key file to find the data under.
147 static void geometry_restore (GschemDialog
*dialog
, GKeyFile
*key_file
, gchar
* group_name
)
149 gint x
, y
, width
, height
;
151 x
= g_key_file_get_integer (key_file
, group_name
, "x", NULL
);
152 y
= g_key_file_get_integer (key_file
, group_name
, "y", NULL
);
153 width
= g_key_file_get_integer (key_file
, group_name
, "width", NULL
);
154 height
= g_key_file_get_integer (key_file
, group_name
, "height", NULL
);
156 gtk_window_move (GTK_WINDOW (dialog
), x
, y
);
157 gtk_window_resize (GTK_WINDOW (dialog
), width
, height
);
161 /*! \brief Setup the GKeyFile for saving / restoring geometry
163 * \par Function Description
164 * Check if the GKeyFile for saving / restoring geometry is open.
165 * If it doesn't exist, we create it here, and also install a hook
166 * to ensure its contents are saved at program exit.
168 static void setup_keyfile ()
170 if (dialog_geometry
!= NULL
)
173 gchar
*file
= g_build_filename (g_get_home_dir (), ".gEDA",
174 DIALOG_GEOMETRY_STORE
, NULL
);
176 dialog_geometry
= g_key_file_new();
178 /* Remember to save data on program exit */
179 gschem_atexit(save_geometry_to_file
, NULL
);
181 if (!g_file_test (file
, G_FILE_TEST_EXISTS
)) {
182 gchar
*dir
= g_build_filename (g_get_home_dir (), ".gEDA", NULL
);
183 g_mkdir (dir
, S_IRWXU
| S_IRWXG
);
186 g_file_set_contents (file
, "", -1, NULL
);
189 if (!g_key_file_load_from_file (dialog_geometry
, file
, G_KEY_FILE_NONE
, NULL
)) {
190 /* error opening key file, create an empty one and try again */
191 g_file_set_contents (file
, "", -1, NULL
);
192 if ( !g_key_file_load_from_file (dialog_geometry
, file
, G_KEY_FILE_NONE
, NULL
)) {
201 /*! \brief GtkWidget show signal handler
203 * \par Function Description
204 * Just before the dialog widget is shown, call the hook
205 * to restore its previously saved position and size.
207 * \param [in] widget The GtkWidget being shown.
209 static void show_handler (GtkWidget
*widget
)
212 GschemDialog
*dialog
= GSCHEM_DIALOG( widget
);
214 group_name
= dialog
->settings_name
;
215 if (group_name
!= NULL
) {
218 g_assert( dialog_geometry
!= NULL
);
219 if (g_key_file_has_group (dialog_geometry
, group_name
)) {
220 g_signal_emit (dialog
, gschem_dialog_signals
[ GEOMETRY_RESTORE
], 0,
221 dialog_geometry
, group_name
);
225 /* Let GTK show the window */
226 GTK_WIDGET_CLASS (gschem_dialog_parent_class
)->show (widget
);
230 /*! \brief GtkWidget unmap signal handler
232 * \par Function Description
233 * Just before the dialog widget is unmapped, call the hook
234 * to save its current position and size.
236 * This typically happens when you call gtk_widget_destroy().
238 * \param [in] widget The GtkWidget being unmapped.
240 static void unmap_handler (GtkWidget
*widget
)
243 GschemDialog
*dialog
= GSCHEM_DIALOG (widget
);
245 group_name
= dialog
->settings_name
;
246 if (group_name
!= NULL
) {
248 g_assert( dialog_geometry
!= NULL
);
249 g_signal_emit (dialog
, gschem_dialog_signals
[ GEOMETRY_SAVE
], 0,
250 dialog_geometry
, group_name
);
253 /* Let GTK unmap the window */
254 GTK_WIDGET_CLASS (gschem_dialog_parent_class
)->unmap (widget
);
258 /*! \brief GObject finalise handler
260 * \par Function Description
261 * Just before the GschemDialog GObject is finalized, free our
262 * allocated data, and then chain up to the parent's finalize handler.
264 * \param [in] widget The GObject being finalized.
266 static void gschem_dialog_finalize (GObject
*object
)
268 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
270 g_free (dialog
->settings_name
);
272 G_OBJECT_CLASS (gschem_dialog_parent_class
)->finalize (object
);
276 /*! \brief GObject property setter function
278 * \par Function Description
279 * Setter function for GschemDialog's GObject properties,
280 * "settings-name" and "toplevel".
282 * \param [in] object The GObject whose properties we are setting
283 * \param [in] property_id The numeric id. under which the property was
284 * registered with g_object_class_install_property()
285 * \param [in] value The GValue the property is being set from
286 * \param [in] pspec A GParamSpec describing the property being set
288 static void gschem_dialog_set_property (GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
290 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
292 switch(property_id
) {
293 case PROP_SETTINGS_NAME
:
294 g_free (dialog
->settings_name
);
295 dialog
->settings_name
= g_strdup (g_value_get_string (value
));
297 case PROP_GSCHEM_TOPLEVEL
:
298 dialog
->w_current
= (GSCHEM_TOPLEVEL
*)g_value_get_pointer (value
);
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
307 /*! \brief GObject property getter function
309 * \par Function Description
310 * Getter function for GschemDialog's GObject properties,
311 * "settings-name" and "toplevel".
313 * \param [in] object The GObject whose properties we are getting
314 * \param [in] property_id The numeric id. under which the property was
315 * registered with g_object_class_install_property()
316 * \param [out] value The GValue in which to return the value of the property
317 * \param [in] pspec A GParamSpec describing the property being got
319 static void gschem_dialog_get_property (GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*pspec
)
321 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
323 switch(property_id
) {
324 case PROP_SETTINGS_NAME
:
325 g_value_set_string (value
, dialog
->settings_name
);
327 case PROP_GSCHEM_TOPLEVEL
:
328 g_value_set_pointer (value
, (gpointer
)dialog
->w_current
);
331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
337 /*! \brief GType class initialiser for GschemDialog
339 * \par Function Description
340 * GType class initialiser for GschemDialog. We override our parent
341 * virtual class methods as needed and register our GObject properties.
343 * \param [in] klass The GschemDialogClass we are initialising
345 static void gschem_dialog_class_init (GschemDialogClass
*klass
)
347 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
348 GtkWidgetClass
*gtkwidget_class
= GTK_WIDGET_CLASS (klass
);
350 klass
->geometry_save
= geometry_save
;
351 klass
->geometry_restore
= geometry_restore
;
353 gtkwidget_class
->show
= show_handler
;
354 gtkwidget_class
->unmap
= unmap_handler
;
356 gobject_class
->finalize
= gschem_dialog_finalize
;
357 gobject_class
->set_property
= gschem_dialog_set_property
;
358 gobject_class
->get_property
= gschem_dialog_get_property
;
360 gschem_dialog_parent_class
= g_type_class_peek_parent (klass
);
362 gschem_dialog_signals
[ GEOMETRY_SAVE
] =
363 g_signal_new ("geometry-save",
364 G_OBJECT_CLASS_TYPE( gobject_class
),
365 G_SIGNAL_RUN_FIRST
, /*signal_flags */
366 G_STRUCT_OFFSET( GschemDialogClass
, geometry_save
),
367 NULL
, /* accumulator */
368 NULL
, /* accu_data */
369 gschem_marshal_VOID__POINTER_STRING
,
376 gschem_dialog_signals
[ GEOMETRY_RESTORE
] =
377 g_signal_new ("geometry-restore",
378 G_OBJECT_CLASS_TYPE( gobject_class
),
379 G_SIGNAL_RUN_FIRST
, /*signal_flags */
380 G_STRUCT_OFFSET( GschemDialogClass
, geometry_restore
),
381 NULL
, /* accumulator */
382 NULL
, /* accu_data */
383 gschem_marshal_VOID__POINTER_STRING
,
390 g_object_class_install_property (
391 gobject_class
, PROP_SETTINGS_NAME
,
392 g_param_spec_string ("settings-name",
396 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
397 g_object_class_install_property (
398 gobject_class
, PROP_GSCHEM_TOPLEVEL
,
399 g_param_spec_pointer ("gschem-toplevel",
402 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
406 /*! \brief Function to retrieve GschemDialog's GType identifier.
408 * \par Function Description
409 * Function to retrieve GschemDialog's GType identifier.
410 * Upon first call, this registers the GschemDialog in the GType system.
411 * Subsequently it returns the saved value from its first execution.
413 * \return the GType identifier associated with GschemDialog.
415 GType
gschem_dialog_get_type ()
417 static GType gschem_dialog_type
= 0;
419 if (!gschem_dialog_type
) {
420 static const GTypeInfo gschem_dialog_info
= {
421 sizeof(GschemDialogClass
),
422 NULL
, /* base_init */
423 NULL
, /* base_finalize */
424 (GClassInitFunc
) gschem_dialog_class_init
,
425 NULL
, /* class_finalize */
426 NULL
, /* class_data */
427 sizeof(GschemDialog
),
429 NULL
, /* instance_init */
432 gschem_dialog_type
= g_type_register_static (GTK_TYPE_DIALOG
,
434 &gschem_dialog_info
, 0);
437 return gschem_dialog_type
;
441 /*! \brief Internal GTK function modified from GTK+-2.4.14 gtkdialog.c
442 * to support gschem_dialog_new_with_buttons(...)
444 * \par Function Description
445 * Convenience function which adds buttons to a pre-existing GtkDialog
447 * \param [in] dialog The GtkDialog buttons are being added to
448 * \param [in] first_button_text The text string for the first button
449 * \param [in] args The va_list containging the remaining button strings
451 static void gschem_dialog_add_buttons_valist (GtkDialog
*dialog
,
452 const gchar
*first_button_text
,
458 g_return_if_fail (GTK_IS_DIALOG (dialog
));
460 if (first_button_text
== NULL
)
463 text
= first_button_text
;
464 response_id
= va_arg (args
, gint
);
468 gtk_dialog_add_button (dialog
, text
, response_id
);
470 text
= va_arg (args
, gchar
*);
473 response_id
= va_arg (args
, int);
478 /*! \brief Internal GTK function modified from GTK+-2.4.14 gtkdialog.c
479 * to support gschem_dialog_new_with_buttons(...)
481 * \par Function Description
482 * Convenience function which creates a blank GschemDialog with various options.
484 * \param [in] title The title text of the dialog
485 * \param [in] parent The GtkWindow which will parent this dialog
486 * \param [in] flags The GtkDialogFlags to use when setting up the dialog
487 * \param [in] settings_name The name gschem should use to store this dialog's settings
488 * \param [in] w_current The GSCHEM_TOPLEVEL object this dialog is associated with
490 * \return The GschemDialog created.
492 static GtkWidget
* gschem_dialog_new_empty (const gchar
*title
,
494 GtkDialogFlags flags
,
495 const gchar
*settings_name
,
496 GSCHEM_TOPLEVEL
*w_current
)
498 GschemDialog
*dialog
;
500 dialog
= g_object_new (GSCHEM_TYPE_DIALOG
,
501 "settings-name", settings_name
,
502 "gschem-toplevel", w_current
,
506 gtk_window_set_title (GTK_WINDOW (dialog
), title
);
509 gtk_window_set_transient_for (GTK_WINDOW (dialog
), parent
);
511 if (flags
& GTK_DIALOG_MODAL
)
512 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
514 if (flags
& GTK_DIALOG_DESTROY_WITH_PARENT
)
515 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog
), TRUE
);
517 if (flags
& GTK_DIALOG_NO_SEPARATOR
)
518 gtk_dialog_set_has_separator (GTK_DIALOG (dialog
), FALSE
);
520 return GTK_WIDGET (dialog
);
524 /*! \brief GTK function modified from GTK+-2.4.14 gtkdialog.c
525 * to provide a GschemDialog equivelant of the convenience function
526 * gtk_dialog_new_with_buttons(...)
528 * \par Function Description
529 * Convenience function which creates a GschemDialog with buttons and options.
531 * \param [in] title The title text of the dialog
532 * \param [in] parent The GtkWindow which will parent this dialog
533 * \param [in] flags The GtkDialogFlags to use when setting up the dialog
534 * \param [in] settings_name The name gschem should use to store this dialog's settings
535 * \param [in] w_current The GSCHEM_TOPLEVEL object this dialog is associated with
536 * \param [in] first_button_text The text string for the first button
537 * \param [in] ... A variable number of arguments with the remaining button strings
539 * \return The GschemDialog created.
541 GtkWidget
* gschem_dialog_new_with_buttons (const gchar
*title
, GtkWindow
*parent
, GtkDialogFlags flags
,
542 const gchar
*settings_name
, GSCHEM_TOPLEVEL
*w_current
,
543 const gchar
*first_button_text
, ...)
545 GschemDialog
*dialog
;
548 dialog
= GSCHEM_DIALOG (gschem_dialog_new_empty (title
, parent
, flags
, settings_name
, w_current
));
550 va_start (args
, first_button_text
);
552 gschem_dialog_add_buttons_valist (GTK_DIALOG (dialog
),
558 return GTK_WIDGET (dialog
);