1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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(s_path_user_config (), 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 (s_path_user_config (),
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 g_mkdir (s_path_user_config (), S_IRWXU
| S_IRWXG
);
184 g_file_set_contents (file
, "", -1, NULL
);
187 if (!g_key_file_load_from_file (dialog_geometry
, file
, G_KEY_FILE_NONE
, NULL
)) {
188 /* error opening key file, create an empty one and try again */
189 g_file_set_contents (file
, "", -1, NULL
);
190 if ( !g_key_file_load_from_file (dialog_geometry
, file
, G_KEY_FILE_NONE
, NULL
)) {
199 /*! \brief GtkWidget show signal handler
201 * \par Function Description
202 * Just before the dialog widget is shown, call the hook
203 * to restore its previously saved position and size.
205 * \param [in] widget The GtkWidget being shown.
207 static void show_handler (GtkWidget
*widget
)
210 GschemDialog
*dialog
= GSCHEM_DIALOG( widget
);
212 group_name
= dialog
->settings_name
;
213 if (group_name
!= NULL
) {
216 g_assert( dialog_geometry
!= NULL
);
217 if (g_key_file_has_group (dialog_geometry
, group_name
)) {
218 g_signal_emit (dialog
, gschem_dialog_signals
[ GEOMETRY_RESTORE
], 0,
219 dialog_geometry
, group_name
);
223 /* Let GTK show the window */
224 GTK_WIDGET_CLASS (gschem_dialog_parent_class
)->show (widget
);
228 /*! \brief GtkWidget unmap signal handler
230 * \par Function Description
231 * Just before the dialog widget is unmapped, call the hook
232 * to save its current position and size.
234 * This typically happens when you call gtk_widget_destroy().
236 * \param [in] widget The GtkWidget being unmapped.
238 static void unmap_handler (GtkWidget
*widget
)
241 GschemDialog
*dialog
= GSCHEM_DIALOG (widget
);
243 group_name
= dialog
->settings_name
;
244 if (group_name
!= NULL
) {
246 g_assert( dialog_geometry
!= NULL
);
247 g_signal_emit (dialog
, gschem_dialog_signals
[ GEOMETRY_SAVE
], 0,
248 dialog_geometry
, group_name
);
251 /* Let GTK unmap the window */
252 GTK_WIDGET_CLASS (gschem_dialog_parent_class
)->unmap (widget
);
256 /*! \brief GObject finalise handler
258 * \par Function Description
259 * Just before the GschemDialog GObject is finalized, free our
260 * allocated data, and then chain up to the parent's finalize handler.
262 * \param [in] object The GObject being finalized.
264 static void gschem_dialog_finalize (GObject
*object
)
266 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
268 g_free (dialog
->settings_name
);
270 G_OBJECT_CLASS (gschem_dialog_parent_class
)->finalize (object
);
274 /*! \brief GObject property setter function
276 * \par Function Description
277 * Setter function for GschemDialog's GObject properties,
278 * "settings-name" and "toplevel".
280 * \param [in] object The GObject whose properties we are setting
281 * \param [in] property_id The numeric id. under which the property was
282 * registered with g_object_class_install_property()
283 * \param [in] value The GValue the property is being set from
284 * \param [in] pspec A GParamSpec describing the property being set
286 static void gschem_dialog_set_property (GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
288 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
290 switch(property_id
) {
291 case PROP_SETTINGS_NAME
:
292 g_free (dialog
->settings_name
);
293 dialog
->settings_name
= g_strdup (g_value_get_string (value
));
295 case PROP_GSCHEM_TOPLEVEL
:
296 dialog
->w_current
= (GSCHEM_TOPLEVEL
*)g_value_get_pointer (value
);
299 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
305 /*! \brief GObject property getter function
307 * \par Function Description
308 * Getter function for GschemDialog's GObject properties,
309 * "settings-name" and "toplevel".
311 * \param [in] object The GObject whose properties we are getting
312 * \param [in] property_id The numeric id. under which the property was
313 * registered with g_object_class_install_property()
314 * \param [out] value The GValue in which to return the value of the property
315 * \param [in] pspec A GParamSpec describing the property being got
317 static void gschem_dialog_get_property (GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*pspec
)
319 GschemDialog
*dialog
= GSCHEM_DIALOG (object
);
321 switch(property_id
) {
322 case PROP_SETTINGS_NAME
:
323 g_value_set_string (value
, dialog
->settings_name
);
325 case PROP_GSCHEM_TOPLEVEL
:
326 g_value_set_pointer (value
, (gpointer
)dialog
->w_current
);
329 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
335 /*! \brief GType class initialiser for GschemDialog
337 * \par Function Description
338 * GType class initialiser for GschemDialog. We override our parent
339 * virtual class methods as needed and register our GObject properties.
341 * \param [in] klass The GschemDialogClass we are initialising
343 static void gschem_dialog_class_init (GschemDialogClass
*klass
)
345 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
346 GtkWidgetClass
*gtkwidget_class
= GTK_WIDGET_CLASS (klass
);
348 klass
->geometry_save
= geometry_save
;
349 klass
->geometry_restore
= geometry_restore
;
351 gtkwidget_class
->show
= show_handler
;
352 gtkwidget_class
->unmap
= unmap_handler
;
354 gobject_class
->finalize
= gschem_dialog_finalize
;
355 gobject_class
->set_property
= gschem_dialog_set_property
;
356 gobject_class
->get_property
= gschem_dialog_get_property
;
358 gschem_dialog_parent_class
= g_type_class_peek_parent (klass
);
360 gschem_dialog_signals
[ GEOMETRY_SAVE
] =
361 g_signal_new ("geometry-save",
362 G_OBJECT_CLASS_TYPE( gobject_class
),
363 G_SIGNAL_RUN_FIRST
, /*signal_flags */
364 G_STRUCT_OFFSET( GschemDialogClass
, geometry_save
),
365 NULL
, /* accumulator */
366 NULL
, /* accu_data */
367 gschem_marshal_VOID__POINTER_STRING
,
374 gschem_dialog_signals
[ GEOMETRY_RESTORE
] =
375 g_signal_new ("geometry-restore",
376 G_OBJECT_CLASS_TYPE( gobject_class
),
377 G_SIGNAL_RUN_FIRST
, /*signal_flags */
378 G_STRUCT_OFFSET( GschemDialogClass
, geometry_restore
),
379 NULL
, /* accumulator */
380 NULL
, /* accu_data */
381 gschem_marshal_VOID__POINTER_STRING
,
388 g_object_class_install_property (
389 gobject_class
, PROP_SETTINGS_NAME
,
390 g_param_spec_string ("settings-name",
394 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
395 g_object_class_install_property (
396 gobject_class
, PROP_GSCHEM_TOPLEVEL
,
397 g_param_spec_pointer ("gschem-toplevel",
400 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
404 /*! \brief Function to retrieve GschemDialog's GType identifier.
406 * \par Function Description
407 * Function to retrieve GschemDialog's GType identifier.
408 * Upon first call, this registers the GschemDialog in the GType system.
409 * Subsequently it returns the saved value from its first execution.
411 * \return the GType identifier associated with GschemDialog.
413 GType
gschem_dialog_get_type ()
415 static GType gschem_dialog_type
= 0;
417 if (!gschem_dialog_type
) {
418 static const GTypeInfo gschem_dialog_info
= {
419 sizeof(GschemDialogClass
),
420 NULL
, /* base_init */
421 NULL
, /* base_finalize */
422 (GClassInitFunc
) gschem_dialog_class_init
,
423 NULL
, /* class_finalize */
424 NULL
, /* class_data */
425 sizeof(GschemDialog
),
427 NULL
, /* instance_init */
430 gschem_dialog_type
= g_type_register_static (GTK_TYPE_DIALOG
,
432 &gschem_dialog_info
, 0);
435 return gschem_dialog_type
;
439 /*! \brief Internal GTK function modified from GTK+-2.4.14 gtkdialog.c
440 * to support gschem_dialog_new_with_buttons(...)
442 * \par Function Description
443 * Convenience function which adds buttons to a pre-existing GtkDialog
445 * \param [in] dialog The GtkDialog buttons are being added to
446 * \param [in] first_button_text The text string for the first button
447 * \param [in] args The va_list containging the remaining button strings
449 static void gschem_dialog_add_buttons_valist (GtkDialog
*dialog
,
450 const gchar
*first_button_text
,
456 g_return_if_fail (GTK_IS_DIALOG (dialog
));
458 if (first_button_text
== NULL
)
461 text
= first_button_text
;
462 response_id
= va_arg (args
, gint
);
466 gtk_dialog_add_button (dialog
, text
, response_id
);
468 text
= va_arg (args
, gchar
*);
471 response_id
= va_arg (args
, int);
476 /*! \brief Internal GTK function modified from GTK+-2.4.14 gtkdialog.c
477 * to support gschem_dialog_new_with_buttons(...)
479 * \par Function Description
480 * Convenience function which creates a blank GschemDialog with various options.
482 * \param [in] title The title text of the dialog
483 * \param [in] parent The GtkWindow which will parent this dialog
484 * \param [in] flags The GtkDialogFlags to use when setting up the dialog
485 * \param [in] settings_name The name gschem should use to store this dialog's settings
486 * \param [in] w_current The GSCHEM_TOPLEVEL object this dialog is associated with
488 * \return The GschemDialog created.
490 static GtkWidget
* gschem_dialog_new_empty (const gchar
*title
,
492 GtkDialogFlags flags
,
493 const gchar
*settings_name
,
494 GSCHEM_TOPLEVEL
*w_current
)
496 GschemDialog
*dialog
;
498 dialog
= g_object_new (GSCHEM_TYPE_DIALOG
,
499 "settings-name", settings_name
,
500 "gschem-toplevel", w_current
,
504 gtk_window_set_title (GTK_WINDOW (dialog
), title
);
507 gtk_window_set_transient_for (GTK_WINDOW (dialog
), parent
);
509 if (flags
& GTK_DIALOG_MODAL
)
510 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
512 if (flags
& GTK_DIALOG_DESTROY_WITH_PARENT
)
513 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog
), TRUE
);
515 if (flags
& GTK_DIALOG_NO_SEPARATOR
)
516 gtk_dialog_set_has_separator (GTK_DIALOG (dialog
), FALSE
);
518 return GTK_WIDGET (dialog
);
522 /*! \brief GTK function modified from GTK+-2.4.14 gtkdialog.c
523 * to provide a GschemDialog equivelant of the convenience function
524 * gtk_dialog_new_with_buttons(...)
526 * \par Function Description
527 * Convenience function which creates a GschemDialog with buttons and options.
529 * \param [in] title The title text of the dialog
530 * \param [in] parent The GtkWindow which will parent this dialog
531 * \param [in] flags The GtkDialogFlags to use when setting up the dialog
532 * \param [in] settings_name The name gschem should use to store this dialog's settings
533 * \param [in] w_current The GSCHEM_TOPLEVEL object this dialog is associated with
534 * \param [in] first_button_text The text string for the first button
535 * \param [in] ... A variable number of arguments with the remaining button strings
537 * \return The GschemDialog created.
539 GtkWidget
* gschem_dialog_new_with_buttons (const gchar
*title
, GtkWindow
*parent
, GtkDialogFlags flags
,
540 const gchar
*settings_name
, GSCHEM_TOPLEVEL
*w_current
,
541 const gchar
*first_button_text
, ...)
543 GschemDialog
*dialog
;
546 dialog
= GSCHEM_DIALOG (gschem_dialog_new_empty (title
, parent
, flags
, settings_name
, w_current
));
548 va_start (args
, first_button_text
);
550 gschem_dialog_add_buttons_valist (GTK_DIALOG (dialog
),
556 return GTK_WIDGET (dialog
);