missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / gschem_dockable.c
blobda34e6c8be6aa472ebda2697c08a03144179d4e1
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <config.h>
22 #include "gschem.h"
23 #include <gdk/gdkkeysyms.h>
26 enum {
27 PROP_GSCHEM_TOPLEVEL = 1,
28 PROP_TITLE,
29 PROP_SETTINGS_NAME,
30 PROP_CANCELLABLE,
31 PROP_HELP_PAGE,
32 PROP_INITIAL_STATE,
33 PROP_INITIAL_WIDTH,
34 PROP_INITIAL_HEIGHT,
35 PROP_INITIAL_WIN_POS,
36 PROP_STATE
39 static gpointer parent_class = NULL;
41 static void class_init (GschemDockableClass *class);
42 static void instance_init (GschemDockable *dockable);
43 static void constructed (GObject *object);
44 static void dispose (GObject *object);
45 static void finalize (GObject *object);
46 static void set_property (GObject *object, guint property_id,
47 const GValue *value, GParamSpec *pspec);
48 static void get_property (GObject *object, guint property_id,
49 GValue *value, GParamSpec *pspec);
51 static GtkWidget *real_create_widget (GschemDockable *dockable);
52 static void real_post_present (GschemDockable *dockable);
53 static void real_cancel (GschemDockable *dockable);
54 static void real_save_internal_geometry (GschemDockable *dockable,
55 EdaConfig *cfg, gchar *group_name);
56 static void real_restore_internal_geometry (GschemDockable *dockable,
57 EdaConfig *cfg, gchar *group_name);
59 static const gchar *get_notebook_group_name (
60 GschemToplevel *w_current, GtkNotebook *notebook);
61 static GschemDockable *get_dockable_by_widget (
62 GschemToplevel *w_current, GtkWidget *widget);
63 static GschemDockable *get_dockable_by_settings_name (
64 GschemToplevel *w_current, const gchar *settings_name);
66 static void save_internal_geometry (GschemDockable *dockable);
67 static void save_window_geometry (GschemDockable *dockable);
68 static void save_state (GschemDockable *dockable);
69 static void save_page_order (GschemToplevel *w_current,
70 GtkNotebook *notebook);
71 static void save_current_page (GschemToplevel *w_current,
72 GtkNotebook *notebook,
73 GschemDockable *dockable);
74 static void restore_internal_geometry (GschemDockable *dockable);
75 static void restore_window_geometry (GschemDockable *dockable);
76 static void restore_state (GschemDockable *dockable);
77 static void restore_page_order (GschemToplevel *w_current,
78 GtkNotebook *notebook);
79 static void restore_current_page (GschemToplevel *w_current,
80 GtkNotebook *notebook);
81 static void restore_detached_dockables (GschemToplevel *w_current);
83 static void create_widget (GschemDockable *dockable);
84 static void create_window (GschemDockable *dockable);
85 static void create_menu (GschemDockable *dockable);
86 static void update_menu_items (GschemDockable *dockable);
87 static void present_in_notebook (GschemDockable *dockable);
89 static void connect_notebook_signals (GschemToplevel *w_current,
90 GtkWidget *notebook);
92 static gboolean
93 callback_after_window_key_press_event (GtkWidget *widget,
94 GdkEventKey *event,
95 GschemDockable *dockable);
96 static gboolean
97 callback_window_delete_event (GtkWidget *widget,
98 GdkEvent *event,
99 GschemDockable *dockable);
100 static void
101 callback_hide_button_clicked (GtkWidget *button,
102 GschemDockable *dockable);
103 static void
104 callback_cancel_button_clicked (GtkWidget *button,
105 GschemDockable *dockable);
106 static void
107 callback_help_button_clicked (GtkWidget *button,
108 GschemDockable *dockable);
109 static void
110 callback_dock_item_toggled (GtkWidget *check_menu_item,
111 GschemDockable *dockable);
112 static void
113 callback_menu_item_activate (GtkWidget *menu_item,
114 GschemDockable *dockable);
115 static gboolean
116 callback_event_box_button_press_event (GtkWidget *widget,
117 GdkEventButton *event,
118 GschemDockable *dockable);
120 static gboolean
121 callback_main_window_focus_in_event (GtkWidget *widget,
122 GdkEvent *event,
123 GschemToplevel *w_current);
124 static void
125 callback_notebook_page_added (GtkWidget *notebook,
126 GtkWidget *child,
127 guint page_num,
128 GschemToplevel *w_current);
129 static void
130 callback_notebook_page_added_removed_reordered (GtkWidget *notebook,
131 GtkWidget *child,
132 guint page_num,
133 GschemToplevel *w_current);
134 static void
135 callback_notebook_switch_page (GtkWidget *notebook,
136 GtkWidget *arg1,
137 guint arg2,
138 GschemToplevel *w_current);
139 static GtkNotebook *
140 callback_notebook_create_window (GtkWidget *notebook,
141 GtkWidget *page,
142 gint x, gint y,
143 GschemToplevel *w_current);
144 static gboolean
145 callback_notebook_key_press_event (GtkWidget *notebook,
146 GdkEventKey *event,
147 GschemToplevel *w_current);
148 static gboolean
149 callback_notebook_button_press_event (GtkWidget *notebook,
150 GdkEventButton *event,
151 GschemToplevel *w_current);
154 GType
155 gschem_dockable_state_get_type ()
157 static GType type = 0;
159 if (type == 0) {
160 static const GEnumValue values[] = {
161 { GSCHEM_DOCKABLE_STATE_HIDDEN,
162 "GSCHEM_DOCKABLE_STATE_HIDDEN", "hidden" },
163 { GSCHEM_DOCKABLE_STATE_DIALOG,
164 "GSCHEM_DOCKABLE_STATE_DIALOG", "dialog" },
165 { GSCHEM_DOCKABLE_STATE_WINDOW,
166 "GSCHEM_DOCKABLE_STATE_WINDOW", "window" },
167 { GSCHEM_DOCKABLE_STATE_DOCKED_LEFT,
168 "GSCHEM_DOCKABLE_STATE_DOCKED_LEFT", "docked-left" },
169 { GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM,
170 "GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM", "docked-bottom" },
171 { GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
172 "GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT", "docked-right" },
173 { 0, NULL, NULL }
176 type = g_enum_register_static ("GschemDockableState", values);
179 return type;
183 GType
184 gschem_dockable_get_type ()
186 static GType type = 0;
188 if (type == 0) {
189 static const GTypeInfo info = {
190 sizeof (GschemDockableClass),
191 NULL, /* base_init */
192 NULL, /* base_finalize */
193 (GClassInitFunc) class_init,
194 NULL, /* class_finalize */
195 NULL, /* class_data */
196 sizeof (GschemDockable),
197 0, /* n_preallocs */
198 (GInstanceInitFunc) instance_init,
199 NULL /* value_table */
202 type = g_type_register_static (G_TYPE_OBJECT,
203 "GschemDockable",
204 &info,
205 G_TYPE_FLAG_ABSTRACT);
208 return type;
212 static void
213 class_init (GschemDockableClass *class)
215 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
217 class->create_widget = real_create_widget;
218 class->post_present = real_post_present;
219 class->cancel = real_cancel;
220 class->save_internal_geometry = real_save_internal_geometry;
221 class->restore_internal_geometry = real_restore_internal_geometry;
223 gobject_class->constructed = constructed;
224 gobject_class->dispose = dispose;
225 gobject_class->finalize = finalize;
226 gobject_class->set_property = set_property;
227 gobject_class->get_property = get_property;
229 parent_class = g_type_class_peek_parent (class);
231 g_object_class_install_property (
232 gobject_class, PROP_GSCHEM_TOPLEVEL,
233 g_param_spec_pointer ("gschem-toplevel",
236 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
238 g_object_class_install_property (
239 gobject_class, PROP_TITLE,
240 g_param_spec_string ("title",
243 NULL,
244 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
245 g_object_class_install_property (
246 gobject_class, PROP_SETTINGS_NAME,
247 g_param_spec_string ("settings-name",
250 NULL,
251 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
252 g_object_class_install_property (
253 gobject_class, PROP_CANCELLABLE,
254 g_param_spec_boolean ("cancellable",
257 FALSE,
258 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
259 g_object_class_install_property (
260 gobject_class, PROP_HELP_PAGE,
261 g_param_spec_string ("help-page",
264 NULL,
265 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
267 g_object_class_install_property (
268 gobject_class, PROP_INITIAL_STATE,
269 g_param_spec_enum ("initial-state",
272 GSCHEM_TYPE_DOCKABLE_STATE,
273 GSCHEM_DOCKABLE_STATE_WINDOW,
274 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
275 g_object_class_install_property (
276 gobject_class, PROP_INITIAL_WIDTH,
277 g_param_spec_int ("initial-width",
281 G_MAXINT,
283 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
284 g_object_class_install_property (
285 gobject_class, PROP_INITIAL_HEIGHT,
286 g_param_spec_int ("initial-height",
290 G_MAXINT,
292 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
293 g_object_class_install_property (
294 gobject_class, PROP_INITIAL_WIN_POS,
295 g_param_spec_enum ("initial-window-position",
298 GTK_TYPE_WINDOW_POSITION,
299 GTK_WIN_POS_NONE,
300 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
302 g_object_class_install_property (
303 gobject_class, PROP_STATE,
304 g_param_spec_enum ("state",
307 GSCHEM_TYPE_DOCKABLE_STATE,
308 GSCHEM_DOCKABLE_STATE_HIDDEN,
309 G_PARAM_READWRITE));
313 static void
314 instance_init (GschemDockable *dockable)
316 dockable->w_current = NULL;
318 dockable->title = NULL;
319 dockable->settings_name = NULL;
320 dockable->group_name = NULL;
321 dockable->cancellable = FALSE;
322 dockable->help_page = NULL;
324 dockable->initial_state = GSCHEM_DOCKABLE_STATE_HIDDEN;
325 dockable->initial_width = -1;
326 dockable->initial_height = -1;
327 dockable->initial_position = GTK_WIN_POS_NONE;
329 dockable->widget = NULL;
330 dockable->window = NULL;
331 dockable->vbox = NULL;
332 dockable->action_area = NULL;
333 dockable->hide_button = NULL;
334 dockable->cancel_button = NULL;
335 dockable->help_button = NULL;
337 dockable->dock_left_item = NULL;
338 dockable->dock_bottom_item = NULL;
339 dockable->dock_right_item = NULL;
341 dockable->menu = NULL;
342 dockable->detach_item = NULL;
343 dockable->move_left_item = NULL;
344 dockable->move_bottom_item = NULL;
345 dockable->move_right_item = NULL;
346 dockable->close_item = NULL;
350 static void
351 constructed (GObject *object)
353 G_OBJECT_CLASS (parent_class)->constructed (object);
355 GschemDockable *dockable = GSCHEM_DOCKABLE (object);
356 g_return_if_fail (dockable->w_current != NULL);
358 dockable->w_current->dockables = g_list_append (
359 dockable->w_current->dockables, dockable);
361 dockable->dock_left_item =
362 gtk_check_menu_item_new_with_label (dockable->title);
363 gtk_widget_show (dockable->dock_left_item);
364 dockable->dock_bottom_item =
365 gtk_check_menu_item_new_with_label (dockable->title);
366 gtk_widget_show (dockable->dock_bottom_item);
367 dockable->dock_right_item =
368 gtk_check_menu_item_new_with_label (dockable->title);
369 gtk_widget_show (dockable->dock_right_item);
371 restore_state (dockable);
373 g_signal_connect (dockable->dock_left_item, "toggled",
374 G_CALLBACK (callback_dock_item_toggled), dockable);
375 g_signal_connect (dockable->dock_bottom_item, "toggled",
376 G_CALLBACK (callback_dock_item_toggled), dockable);
377 g_signal_connect (dockable->dock_right_item, "toggled",
378 G_CALLBACK (callback_dock_item_toggled), dockable);
382 static void
383 dispose (GObject *object)
385 GschemDockable *dockable = GSCHEM_DOCKABLE (object);
387 g_clear_object (&dockable->widget);
388 g_clear_object (&dockable->menu);
390 G_OBJECT_CLASS (parent_class)->dispose (object);
394 static void
395 finalize (GObject *object)
397 GschemDockable *dockable = GSCHEM_DOCKABLE (object);
399 g_free (dockable->title);
400 g_free (dockable->settings_name);
401 g_free (dockable->group_name);
402 g_free (dockable->help_page);
404 G_OBJECT_CLASS (parent_class)->finalize (object);
408 static void
409 set_property (GObject *object, guint property_id,
410 const GValue *value, GParamSpec *pspec)
412 GschemDockable *dockable = GSCHEM_DOCKABLE (object);
414 switch (property_id) {
415 /* Properties specified as G_PARAM_CONSTRUCT[_ONLY] are set during
416 * construction--the instance isn't fully initialized at that time.
417 * Don't do any setup logic here. */
419 case PROP_GSCHEM_TOPLEVEL:
420 dockable->w_current = GSCHEM_TOPLEVEL (g_value_get_pointer (value));
421 break;
423 case PROP_TITLE:
424 g_free (dockable->title);
425 dockable->title = g_strdup (g_value_get_string (value));
426 break;
427 case PROP_SETTINGS_NAME:
428 g_free (dockable->settings_name);
429 g_free (dockable->group_name);
430 dockable->settings_name = g_strdup (g_value_get_string (value));
431 dockable->group_name = g_strdup_printf ("gschem.dialog-geometry.%s",
432 dockable->settings_name);
433 break;
434 case PROP_CANCELLABLE:
435 dockable->cancellable = g_value_get_boolean (value);
436 break;
437 case PROP_HELP_PAGE:
438 g_free (dockable->help_page);
439 dockable->help_page = g_strdup (g_value_get_string (value));
440 break;
442 case PROP_INITIAL_STATE:
443 dockable->initial_state = g_value_get_enum (value);
444 break;
445 case PROP_INITIAL_WIDTH:
446 dockable->initial_width = g_value_get_int (value);
447 break;
448 case PROP_INITIAL_HEIGHT:
449 dockable->initial_height = g_value_get_int (value);
450 break;
451 case PROP_INITIAL_WIN_POS:
452 dockable->initial_position = g_value_get_enum (value);
453 break;
455 /* Properties not specified as G_PARAM_CONSTRUCT[_ONLY] will only
456 * be set after the object has been fully initialized. */
458 case PROP_STATE:
459 gschem_dockable_set_state (dockable, g_value_get_enum (value));
460 break;
462 default:
463 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
468 static void
469 get_property (GObject *object, guint property_id,
470 GValue *value, GParamSpec *pspec)
472 GschemDockable *dockable = GSCHEM_DOCKABLE (object);
474 switch (property_id) {
475 case PROP_GSCHEM_TOPLEVEL:
476 g_value_set_pointer (value, dockable->w_current);
477 break;
478 case PROP_TITLE:
479 g_value_set_string (value, dockable->title);
480 break;
481 case PROP_SETTINGS_NAME:
482 g_value_set_string (value, dockable->settings_name);
483 break;
484 case PROP_CANCELLABLE:
485 g_value_set_boolean (value, dockable->cancellable);
486 break;
487 case PROP_HELP_PAGE:
488 g_value_set_string (value, dockable->help_page);
489 break;
490 case PROP_INITIAL_STATE:
491 g_value_set_enum (value, dockable->initial_state);
492 break;
493 case PROP_INITIAL_WIDTH:
494 g_value_set_int (value, dockable->initial_width);
495 break;
496 case PROP_INITIAL_HEIGHT:
497 g_value_set_int (value, dockable->initial_height);
498 break;
499 case PROP_INITIAL_WIN_POS:
500 g_value_set_enum (value, dockable->initial_position);
501 break;
502 case PROP_STATE:
503 g_value_set_enum (value, gschem_dockable_get_state (dockable));
504 break;
505 default:
506 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
511 static GtkWidget *
512 real_create_widget (GschemDockable *dockable)
514 g_assert_not_reached ();
518 static void
519 real_post_present (GschemDockable *dockable)
521 /* default action: none */
525 static void
526 real_cancel (GschemDockable *dockable)
528 /* default action: ignore */
532 static void
533 real_save_internal_geometry (GschemDockable *dockable,
534 EdaConfig *cfg, gchar *group_name)
536 /* override if dockable has internal geometry */
540 static void
541 real_restore_internal_geometry (GschemDockable *dockable,
542 EdaConfig *cfg, gchar *group_name)
544 /* override if dockable has internal geometry */
548 /******************************************************************************/
551 static const gchar *
552 get_notebook_group_name (GschemToplevel *w_current, GtkNotebook *notebook)
554 if (GTK_WIDGET (notebook) == w_current->left_notebook)
555 return "gschem.dock-geometry.left";
556 if (GTK_WIDGET (notebook) == w_current->bottom_notebook)
557 return "gschem.dock-geometry.bottom";
558 if (GTK_WIDGET (notebook) == w_current->right_notebook)
559 return "gschem.dock-geometry.right";
560 g_assert_not_reached ();
564 static GschemDockable *
565 get_dockable_by_widget (GschemToplevel *w_current, GtkWidget *widget)
567 g_return_val_if_fail (widget != NULL, NULL);
569 GValue value = G_VALUE_INIT;
570 g_value_init (&value, G_TYPE_POINTER);
571 g_object_get_property (G_OBJECT (widget), "user-data", &value);
572 return GSCHEM_DOCKABLE (g_value_get_pointer (&value));
576 static GschemDockable *
577 get_dockable_by_settings_name (GschemToplevel *w_current,
578 const gchar *settings_name)
580 g_return_val_if_fail (settings_name != NULL, NULL);
582 for (GList *l = w_current->dockables; l != NULL; l = l->next) {
583 GschemDockable *dockable = GSCHEM_DOCKABLE (l->data);
584 if (strcmp (settings_name, dockable->settings_name) == 0)
585 return dockable;
588 return NULL; /* unknown dockable name */
592 /*! \brief Save current internal geometry of a dockable */
593 static void
594 save_internal_geometry (GschemDockable *dockable)
596 EdaConfig *cfg = eda_config_get_user_context ();
597 g_return_if_fail (cfg != NULL);
598 g_return_if_fail (dockable->widget != NULL);
600 GSCHEM_DOCKABLE_GET_CLASS (dockable)
601 ->save_internal_geometry (dockable, cfg, dockable->group_name);
605 /*! \brief Save current window position and size of a detached dockable */
606 static void
607 save_window_geometry (GschemDockable *dockable)
609 EdaConfig *cfg = eda_config_get_user_context ();
610 g_return_if_fail (cfg != NULL);
611 g_return_if_fail (dockable->window != NULL);
613 gint x, y, width, height;
614 gtk_window_get_position (GTK_WINDOW (dockable->window), &x, &y);
615 gtk_window_get_size (GTK_WINDOW (dockable->window), &width, &height);
617 eda_config_set_int (cfg, dockable->group_name, "x", x);
618 eda_config_set_int (cfg, dockable->group_name, "y", y);
619 eda_config_set_int (cfg, dockable->group_name, "width", width);
620 eda_config_set_int (cfg, dockable->group_name, "height", height);
624 /*! \brief Save current state of a dockable */
625 static void
626 save_state (GschemDockable *dockable)
628 EdaConfig *cfg = eda_config_get_user_context ();
629 g_return_if_fail (cfg != NULL);
631 GschemDockableState state = gschem_dockable_get_state (dockable);
632 const char *value =
633 state == GSCHEM_DOCKABLE_STATE_WINDOW ? "detached" :
634 state == GSCHEM_DOCKABLE_STATE_DOCKED_LEFT ? "docked-left" :
635 state == GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM ? "docked-bottom" :
636 state == GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT ? "docked-right" : "hidden";
638 eda_config_set_string (cfg, dockable->group_name, "state", value);
642 static void
643 save_page_order (GschemToplevel *w_current,
644 GtkNotebook *notebook)
646 EdaConfig *cfg = eda_config_get_user_context ();
647 g_return_if_fail (cfg != NULL);
649 gint page_count = gtk_notebook_get_n_pages (notebook);
651 /* write new page order to configuration */
652 const gchar **list = g_new (const gchar *, page_count);
654 for (gint page_num = 0; page_num < page_count; page_num++) {
655 GtkWidget *widget = gtk_notebook_get_nth_page (notebook, page_num);
656 GschemDockable *dockable = get_dockable_by_widget (w_current, widget);
657 g_return_if_fail (dockable != NULL);
658 list[page_num] = dockable->settings_name;
661 eda_config_set_string_list (
662 cfg, get_notebook_group_name (w_current, notebook),
663 "page-order", list, page_count);
665 g_free (list);
667 /* clear current page key if notebook is empty */
668 if (page_count == 0)
669 eda_config_set_string (
670 cfg, get_notebook_group_name (w_current, notebook),
671 "current-page", "");
675 static void
676 save_current_page (GschemToplevel *w_current,
677 GtkNotebook *notebook,
678 GschemDockable *dockable)
680 EdaConfig *cfg = eda_config_get_user_context ();
681 g_return_if_fail (cfg != NULL);
683 eda_config_set_string (
684 cfg, get_notebook_group_name (w_current, notebook),
685 "current-page", dockable->settings_name);
689 /*! \brief Restore last internal geometry of a dockable */
690 static void
691 restore_internal_geometry (GschemDockable *dockable)
693 EdaConfig *cfg = eda_config_get_user_context ();
694 g_return_if_fail (cfg != NULL);
695 g_return_if_fail (dockable->widget != NULL);
697 GSCHEM_DOCKABLE_GET_CLASS (dockable)
698 ->restore_internal_geometry (dockable, cfg, dockable->group_name);
702 /*! \brief Restore last window position and size of a detached dockable */
703 static void
704 restore_window_geometry (GschemDockable *dockable)
706 EdaConfig *cfg = eda_config_get_user_context ();
707 g_return_if_fail (cfg != NULL);
708 g_return_if_fail (dockable->window != NULL);
710 if (!eda_config_has_group (cfg, dockable->group_name))
711 gtk_window_set_position (GTK_WINDOW (dockable->window),
712 dockable->initial_position);
713 else {
714 gint x, y, width, height;
715 x = eda_config_get_int (cfg, dockable->group_name, "x", NULL);
716 y = eda_config_get_int (cfg, dockable->group_name, "y", NULL);
717 width = eda_config_get_int (cfg, dockable->group_name, "width", NULL);
718 height = eda_config_get_int (cfg, dockable->group_name, "height", NULL);
720 gtk_window_move (GTK_WINDOW (dockable->window), x, y);
721 if (width > 0 && height > 0)
722 gtk_window_resize (GTK_WINDOW (dockable->window), width, height);
727 /*! \brief Restore last state of a dockable (if it was docked) */
728 static void
729 restore_state (GschemDockable *dockable)
731 EdaConfig *cfg = eda_config_get_user_context ();
732 g_return_if_fail (cfg != NULL);
734 gchar *state = eda_config_get_string (
735 cfg, dockable->group_name, "state", NULL);
737 if (state == NULL) {
738 if (dockable->initial_state != GSCHEM_DOCKABLE_STATE_WINDOW)
739 gschem_dockable_set_state (dockable, dockable->initial_state);
740 } else if (strcmp (state, "detached") == 0)
741 /* detached docks are restored later */;
742 else if (strcmp (state, "docked-left") == 0)
743 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_LEFT);
744 else if (strcmp (state, "docked-bottom") == 0)
745 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM);
746 else if (strcmp (state, "docked-right") == 0)
747 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT);
749 g_free (state);
753 static void
754 restore_page_order (GschemToplevel *w_current,
755 GtkNotebook *notebook)
757 EdaConfig *cfg = eda_config_get_user_context ();
758 g_return_if_fail (cfg != NULL);
760 gchar **list;
761 gsize page_count = 0;
762 gint page_num;
764 list = eda_config_get_string_list (
765 cfg, get_notebook_group_name (w_current, notebook),
766 "page-order", &page_count, NULL);
768 for (page_num = 0; page_num < page_count; page_num++) {
769 GschemDockable *dockable =
770 get_dockable_by_settings_name (w_current, list[page_num]);
771 if (dockable != NULL && dockable->widget != NULL)
772 gtk_notebook_reorder_child (notebook, dockable->widget, page_num);
775 g_strfreev (list);
779 static void
780 restore_current_page (GschemToplevel *w_current,
781 GtkNotebook *notebook)
783 EdaConfig *cfg = eda_config_get_user_context ();
784 g_return_if_fail (cfg != NULL);
786 gchar *current_page = eda_config_get_string (
787 cfg, get_notebook_group_name (w_current, notebook),
788 "current-page", NULL);
790 GschemDockable *dockable = NULL;
791 if (current_page != NULL && *current_page != '\0')
792 dockable = get_dockable_by_settings_name (w_current, current_page);
794 g_free (current_page);
795 if (dockable == NULL || dockable->widget == NULL)
796 return;
798 gint page_num = gtk_notebook_page_num (notebook, dockable->widget);
799 if (page_num == -1)
800 return;
802 /* non-dockable tabs aren't necessarily visible at this point */
803 gtk_widget_show (dockable->widget);
805 gtk_notebook_set_current_page (notebook, page_num);
809 static void
810 restore_detached_dockables (GschemToplevel *w_current)
812 EdaConfig *cfg = eda_config_get_user_context ();
813 g_return_if_fail (cfg != NULL);
815 for (GList *l = w_current->dockables; l != NULL; l = l->next) {
816 GschemDockable *dockable = GSCHEM_DOCKABLE (l->data);
817 gchar *state = eda_config_get_string (
818 cfg, dockable->group_name, "state", NULL);
820 if (state != NULL ? strcmp (state, "detached") == 0 :
821 dockable->initial_state == GSCHEM_DOCKABLE_STATE_WINDOW)
822 gschem_dockable_detach (dockable, FALSE);
824 g_free (state);
827 /* open up log window on startup */
828 if (w_current->log_window == MAP_ON_STARTUP)
829 switch (gschem_dockable_get_state (w_current->log_dockable)) {
830 case GSCHEM_DOCKABLE_STATE_HIDDEN:
831 /* For whatever reason, this only works if the action area is
832 * hidden while showing the log window. Otherwise, scrolling
833 * down to the bottom of the log will cause drawing the main
834 * window toolbar icons to break [sic]; but only if the log
835 * window height is about the length of the log or smaller.
837 * I suspect this may somehow be connected to widget size
838 * allocation, but I'm giving up on debugging this now and
839 * just accept that it may flicker a bit. */
841 gschem_dockable_detach (w_current->log_dockable, FALSE);
842 gtk_widget_show (w_current->log_dockable->action_area);
843 break;
845 case GSCHEM_DOCKABLE_STATE_DIALOG:
846 case GSCHEM_DOCKABLE_STATE_WINDOW:
847 gtk_window_present (GTK_WINDOW (w_current->log_dockable->window));
848 break;
850 case GSCHEM_DOCKABLE_STATE_DOCKED_LEFT:
851 case GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM:
852 case GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT:
853 present_in_notebook (w_current->log_dockable);
854 break;
856 else if (w_current->log_dockable->widget == NULL)
857 /* create the widget so the log window can auto-show if necessary */
858 create_widget (w_current->log_dockable);
862 /******************************************************************************/
865 static void
866 create_widget (GschemDockable *dockable)
868 g_return_if_fail (dockable->widget == NULL);
870 dockable->widget =
871 GSCHEM_DOCKABLE_GET_CLASS (dockable)->create_widget (dockable);
872 g_object_ref_sink (dockable->widget);
874 /* GtkNotebook doesn't allow storing a pointer along with a widget,
875 * so store the dockable in the user-data property of the widget */
877 GValue value = G_VALUE_INIT;
878 g_value_init (&value, G_TYPE_POINTER);
879 g_value_set_pointer (&value, dockable);
880 g_object_set_property (G_OBJECT (dockable->widget), "user-data", &value);
884 static void
885 create_window (GschemDockable *dockable)
887 g_return_if_fail (dockable->window == NULL);
889 dockable->window = g_object_new (GTK_TYPE_WINDOW,
890 "title", dockable->title,
891 "default-width", dockable->initial_width,
892 "default-height", dockable->initial_height,
893 NULL);
895 gtk_window_set_transient_for (GTK_WINDOW (dockable->window),
896 GTK_WINDOW (dockable->w_current->main_window));
897 gtk_window_set_type_hint (GTK_WINDOW (dockable->window),
898 GDK_WINDOW_TYPE_HINT_DIALOG);
900 g_signal_connect_after (dockable->window,
901 "key-press-event",
902 G_CALLBACK (callback_after_window_key_press_event),
903 dockable);
904 g_signal_connect (dockable->window,
905 "delete-event",
906 G_CALLBACK (callback_window_delete_event),
907 dockable);
910 /* create containers */
912 dockable->vbox = g_object_new (GTK_TYPE_VBOX,
913 "border-width", 2,
914 "spacing", 0,
915 NULL);
916 gtk_container_add (GTK_CONTAINER (dockable->window), dockable->vbox);
917 gtk_widget_show (dockable->vbox);
919 dockable->action_area = g_object_new (GTK_TYPE_HBUTTON_BOX,
920 "border-width", 5,
921 "spacing", 6,
922 "layout-style", GTK_BUTTONBOX_END,
923 NULL);
924 gtk_box_pack_end (GTK_BOX (dockable->vbox),
925 dockable->action_area,
926 FALSE, TRUE, 0);
927 gtk_widget_show (dockable->action_area);
930 /* add buttons */
932 if (dockable->cancellable) {
933 dockable->cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
934 gtk_widget_set_can_default (dockable->cancel_button, TRUE);
935 gtk_widget_show (dockable->cancel_button);
936 g_signal_connect (dockable->cancel_button, "clicked",
937 G_CALLBACK (callback_cancel_button_clicked), dockable);
938 gtk_box_pack_end (GTK_BOX (dockable->action_area),
939 dockable->cancel_button,
940 FALSE, TRUE, 0);
943 dockable->hide_button = gtk_button_new_from_stock (
944 dockable->cancellable ? GTK_STOCK_OK : GTK_STOCK_CLOSE);
945 gtk_widget_set_can_default (dockable->hide_button, TRUE);
946 gtk_widget_show (dockable->hide_button);
947 g_signal_connect (dockable->hide_button, "clicked",
948 G_CALLBACK (callback_hide_button_clicked), dockable);
949 gtk_box_pack_end (GTK_BOX (dockable->action_area),
950 dockable->hide_button,
951 FALSE, TRUE, 0);
953 if (dockable->help_page != NULL) {
954 dockable->help_button = gtk_button_new_from_stock (GTK_STOCK_HELP);
955 gtk_widget_set_can_default (dockable->help_button, TRUE);
956 gtk_widget_show (dockable->help_button);
957 g_signal_connect (dockable->help_button, "clicked",
958 G_CALLBACK (callback_help_button_clicked), dockable);
959 gtk_box_pack_end (GTK_BOX (dockable->action_area),
960 dockable->help_button,
961 FALSE, TRUE, 0);
962 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dockable->action_area),
963 dockable->help_button, TRUE);
966 GdkScreen *screen = gtk_widget_get_screen (dockable->window);
967 if (gtk_alternative_dialog_button_order (screen)) {
968 gtk_box_reorder_child (GTK_BOX (dockable->action_area),
969 dockable->hide_button, 0);
970 if (dockable->cancel_button != NULL)
971 gtk_box_reorder_child (GTK_BOX (dockable->action_area),
972 dockable->cancel_button, 1);
973 if (dockable->help_button != NULL)
974 gtk_box_reorder_child (GTK_BOX (dockable->action_area),
975 dockable->help_button, 2);
978 gtk_widget_grab_default (dockable->hide_button);
982 static void
983 create_menu (GschemDockable *dockable)
985 g_return_if_fail (dockable->menu == NULL);
987 dockable->menu = gtk_menu_new ();
988 g_object_ref_sink (dockable->menu);
990 dockable->detach_item = gtk_menu_item_new_with_mnemonic (_("_Detach"));
991 g_signal_connect (dockable->detach_item, "activate",
992 G_CALLBACK (callback_menu_item_activate), dockable);
993 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
994 dockable->detach_item);
996 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
997 gtk_separator_menu_item_new ());
999 dockable->move_left_item =
1000 gtk_menu_item_new_with_mnemonic (_("Move to _left dock"));
1001 g_signal_connect (dockable->move_left_item, "activate",
1002 G_CALLBACK (callback_menu_item_activate), dockable);
1003 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
1004 dockable->move_left_item);
1006 dockable->move_bottom_item =
1007 gtk_menu_item_new_with_mnemonic (_("Move to _bottom dock"));
1008 g_signal_connect (dockable->move_bottom_item, "activate",
1009 G_CALLBACK (callback_menu_item_activate), dockable);
1010 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
1011 dockable->move_bottom_item);
1013 dockable->move_right_item =
1014 gtk_menu_item_new_with_mnemonic (_("Move to _right dock"));
1015 g_signal_connect (dockable->move_right_item, "activate",
1016 G_CALLBACK (callback_menu_item_activate), dockable);
1017 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
1018 dockable->move_right_item);
1020 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
1021 gtk_separator_menu_item_new ());
1023 dockable->close_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE,
1024 NULL);
1025 g_signal_connect (dockable->close_item, "activate",
1026 G_CALLBACK (callback_menu_item_activate), dockable);
1027 gtk_menu_shell_append (GTK_MENU_SHELL (dockable->menu),
1028 dockable->close_item);
1030 gtk_widget_show_all (dockable->menu);
1034 static void
1035 update_menu_items (GschemDockable *dockable)
1037 GschemDockableState state = gschem_dockable_get_state (dockable);
1039 gtk_check_menu_item_set_active (
1040 GTK_CHECK_MENU_ITEM (dockable->dock_left_item),
1041 state == GSCHEM_DOCKABLE_STATE_DOCKED_LEFT);
1042 gtk_check_menu_item_set_active (
1043 GTK_CHECK_MENU_ITEM (dockable->dock_bottom_item),
1044 state == GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM);
1045 gtk_check_menu_item_set_active (
1046 GTK_CHECK_MENU_ITEM (dockable->dock_right_item),
1047 state == GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT);
1051 static void
1052 present_in_notebook (GschemDockable *dockable)
1054 GtkWidget *parent;
1055 gint i;
1057 g_return_if_fail (dockable->widget != NULL);
1059 parent = gtk_widget_get_parent (dockable->widget);
1060 g_return_if_fail (GTK_IS_NOTEBOOK (parent));
1062 i = gtk_notebook_page_num (GTK_NOTEBOOK (parent), dockable->widget);
1063 g_return_if_fail (i != -1);
1065 gtk_notebook_set_current_page (GTK_NOTEBOOK (parent), i);
1067 x_window_present (dockable->w_current);
1068 gtk_widget_grab_focus (dockable->widget);
1072 void
1073 gschem_dockable_hide (GschemDockable *dockable)
1075 g_return_if_fail (GSCHEM_IS_DOCKABLE (dockable));
1077 GtkWidget *parent = dockable->widget == NULL ? NULL :
1078 gtk_widget_get_parent (dockable->widget);
1080 if (parent == NULL ||
1081 (dockable->window != NULL && parent == dockable->vbox &&
1082 !gtk_widget_get_visible (dockable->window)))
1083 /* already hidden, no action required */
1084 return;
1086 if (dockable->window != NULL && parent == dockable->vbox) {
1087 /* hide window */
1088 save_internal_geometry (dockable);
1089 save_window_geometry (dockable);
1090 gtk_widget_hide (dockable->window);
1091 } else {
1092 /* remove from notebook */
1093 save_internal_geometry (dockable);
1094 gtk_container_remove (GTK_CONTAINER (parent), dockable->widget);
1097 update_menu_items (dockable);
1098 save_state (dockable);
1102 void
1103 gschem_dockable_detach (GschemDockable *dockable, gboolean show_action_area)
1105 g_return_if_fail (GSCHEM_IS_DOCKABLE (dockable));
1107 if (dockable->widget == NULL)
1108 create_widget (dockable);
1110 if (dockable->window == NULL)
1111 create_window (dockable);
1113 GtkWidget *parent = gtk_widget_get_parent (dockable->widget);
1115 if (parent == NULL) {
1116 /* freshly created or was removed from notebook */
1118 gtk_box_pack_start (GTK_BOX (dockable->vbox),
1119 dockable->widget,
1120 TRUE, TRUE, 0);
1122 if (show_action_area)
1123 gtk_widget_show (dockable->action_area);
1124 else
1125 gtk_widget_hide (dockable->action_area);
1127 restore_window_geometry (dockable);
1128 restore_internal_geometry (dockable);
1130 /* detached docks are only restored after the main window is visible */
1131 g_assert (gtk_widget_get_visible (dockable->w_current->main_window));
1133 gtk_window_present (GTK_WINDOW (dockable->window));
1134 save_state (dockable);
1136 } else if (dockable->window != NULL && parent == dockable->vbox &&
1137 !gtk_widget_get_visible (dockable->window)) {
1138 /* already in window, but window is currently hidden */
1140 if (show_action_area)
1141 gtk_widget_show (dockable->action_area);
1142 else
1143 gtk_widget_hide (dockable->action_area);
1145 restore_window_geometry (dockable);
1146 restore_internal_geometry (dockable);
1148 gtk_window_present (GTK_WINDOW (dockable->window));
1149 save_state (dockable);
1151 } else if (dockable->window != NULL && parent == dockable->vbox) {
1152 /* already in window, and window is currently visible
1153 just show/hide action area as appropriate */
1155 if (show_action_area && !gtk_widget_get_visible (dockable->action_area)) {
1156 gtk_widget_show (dockable->action_area);
1157 save_state (dockable);
1159 if (!show_action_area && gtk_widget_get_visible (dockable->action_area)) {
1160 gtk_widget_hide (dockable->action_area);
1161 save_state (dockable);
1164 } else {
1165 /* currently docked */
1167 g_assert (GTK_IS_NOTEBOOK (parent));
1168 save_internal_geometry (dockable);
1169 gtk_container_remove (GTK_CONTAINER (parent), dockable->widget);
1171 gtk_box_pack_start (GTK_BOX (dockable->vbox),
1172 dockable->widget,
1173 TRUE, TRUE, 0);
1175 if (show_action_area)
1176 gtk_widget_show (dockable->action_area);
1177 else
1178 gtk_widget_hide (dockable->action_area);
1180 restore_window_geometry (dockable);
1181 restore_internal_geometry (dockable);
1183 update_menu_items (dockable);
1185 gtk_window_present (GTK_WINDOW (dockable->window));
1186 save_state (dockable);
1191 void
1192 gschem_dockable_attach_to_notebook (GschemDockable *dockable,
1193 GtkWidget *notebook)
1195 g_return_if_fail (GSCHEM_IS_DOCKABLE (dockable));
1196 g_return_if_fail (notebook == dockable->w_current->left_notebook ||
1197 notebook == dockable->w_current->bottom_notebook ||
1198 notebook == dockable->w_current->right_notebook);
1199 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
1201 if (dockable->widget == NULL)
1202 create_widget (dockable);
1204 GtkWidget *parent = gtk_widget_get_parent (dockable->widget);
1206 if (parent == NULL) {
1207 /* hidden and not attached to window */
1208 } else if (dockable->window != NULL && parent == dockable->vbox) {
1209 /* attached to window (hidden or visible) */
1210 if (gtk_widget_get_visible (dockable->window)) {
1211 save_internal_geometry (dockable);
1212 save_window_geometry (dockable);
1213 gtk_widget_hide (dockable->window);
1215 gtk_container_remove (GTK_CONTAINER (parent), dockable->widget);
1216 } else {
1217 if (parent == notebook)
1218 /* already in requested notebook, nothing to do */
1219 return;
1221 g_assert (GTK_IS_NOTEBOOK (parent));
1222 save_internal_geometry (dockable);
1223 gtk_container_remove (GTK_CONTAINER (parent), dockable->widget);
1226 g_assert (gtk_widget_get_parent (dockable->widget) == NULL);
1228 /* add widget to notebook */
1230 GtkWidget *event_box = gtk_event_box_new ();
1231 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
1232 g_signal_connect (event_box,
1233 "button-press-event",
1234 G_CALLBACK (callback_event_box_button_press_event),
1235 dockable);
1237 GtkWidget *label = gtk_label_new (dockable->title);
1238 gtk_container_add (GTK_CONTAINER (event_box), label);
1239 gtk_widget_show (label);
1241 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1242 dockable->widget,
1243 event_box);
1244 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (notebook),
1245 dockable->widget, TRUE);
1246 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (notebook),
1247 dockable->widget, TRUE);
1249 restore_internal_geometry (dockable);
1250 update_menu_items (dockable);
1252 /* don't present when restoring state on startup */
1253 if (gtk_widget_get_visible (dockable->w_current->main_window)) {
1254 present_in_notebook (dockable);
1255 save_state (dockable);
1260 void
1261 gschem_dockable_set_state (GschemDockable *dockable, GschemDockableState state)
1263 g_return_if_fail (GSCHEM_IS_DOCKABLE (dockable));
1265 switch (state) {
1266 case GSCHEM_DOCKABLE_STATE_HIDDEN:
1267 gschem_dockable_hide (dockable);
1268 break;
1269 case GSCHEM_DOCKABLE_STATE_DIALOG:
1270 gschem_dockable_detach (dockable, TRUE);
1271 break;
1272 case GSCHEM_DOCKABLE_STATE_WINDOW:
1273 gschem_dockable_detach (dockable, FALSE);
1274 break;
1275 case GSCHEM_DOCKABLE_STATE_DOCKED_LEFT:
1276 gschem_dockable_attach_to_notebook (
1277 dockable, dockable->w_current->left_notebook);
1278 break;
1279 case GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM:
1280 gschem_dockable_attach_to_notebook (
1281 dockable, dockable->w_current->bottom_notebook);
1282 break;
1283 case GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT:
1284 gschem_dockable_attach_to_notebook (
1285 dockable, dockable->w_current->right_notebook);
1286 break;
1287 default:
1288 g_assert_not_reached ();
1293 GschemDockableState
1294 gschem_dockable_get_state (GschemDockable *dockable)
1296 g_return_val_if_fail (GSCHEM_IS_DOCKABLE (dockable),
1297 GSCHEM_DOCKABLE_STATE_HIDDEN);
1299 if (dockable->widget == NULL)
1300 return GSCHEM_DOCKABLE_STATE_HIDDEN;
1302 GtkWidget *parent = gtk_widget_get_parent (dockable->widget);
1303 if (parent == NULL)
1304 return GSCHEM_DOCKABLE_STATE_HIDDEN;
1306 if (dockable->window != NULL && parent == dockable->vbox) {
1307 if (!gtk_widget_get_visible (dockable->window))
1308 return GSCHEM_DOCKABLE_STATE_HIDDEN;
1309 if (!gtk_widget_get_visible (dockable->action_area))
1310 return GSCHEM_DOCKABLE_STATE_WINDOW;
1311 return GSCHEM_DOCKABLE_STATE_DIALOG;
1314 if (parent == dockable->w_current->left_notebook)
1315 return GSCHEM_DOCKABLE_STATE_DOCKED_LEFT;
1316 if (parent == dockable->w_current->bottom_notebook)
1317 return GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM;
1318 if (parent == dockable->w_current->right_notebook)
1319 return GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT;
1321 g_assert_not_reached ();
1325 void
1326 gschem_dockable_present (GschemDockable *dockable)
1328 g_return_if_fail (GSCHEM_IS_DOCKABLE (dockable));
1330 switch (gschem_dockable_get_state (dockable)) {
1331 case GSCHEM_DOCKABLE_STATE_HIDDEN:
1332 gschem_dockable_detach (dockable, TRUE);
1333 break;
1334 case GSCHEM_DOCKABLE_STATE_DIALOG:
1335 case GSCHEM_DOCKABLE_STATE_WINDOW:
1336 gtk_window_present (GTK_WINDOW (dockable->window));
1337 break;
1338 case GSCHEM_DOCKABLE_STATE_DOCKED_LEFT:
1339 case GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM:
1340 case GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT:
1341 present_in_notebook (dockable);
1342 break;
1343 default:
1344 g_assert_not_reached ();
1347 GSCHEM_DOCKABLE_GET_CLASS (dockable)->post_present (dockable);
1351 /******************************************************************************/
1354 static void
1355 connect_notebook_signals (GschemToplevel *w_current, GtkWidget *notebook)
1357 g_signal_connect (
1358 notebook, "create-window",
1359 G_CALLBACK (callback_notebook_create_window), w_current);
1360 g_signal_connect (
1361 notebook, "page-added",
1362 G_CALLBACK (callback_notebook_page_added), w_current);
1363 g_signal_connect (
1364 notebook, "page-added",
1365 G_CALLBACK (callback_notebook_page_added_removed_reordered), w_current);
1366 g_signal_connect (
1367 notebook, "page-removed",
1368 G_CALLBACK (callback_notebook_page_added_removed_reordered), w_current);
1369 g_signal_connect (
1370 notebook, "page-reordered",
1371 G_CALLBACK (callback_notebook_page_added_removed_reordered), w_current);
1372 g_signal_connect (
1373 notebook, "switch-page",
1374 G_CALLBACK (callback_notebook_switch_page), w_current);
1375 g_signal_connect (
1376 notebook, "key-press-event",
1377 G_CALLBACK (callback_notebook_key_press_event), w_current);
1378 g_signal_connect (
1379 notebook, "button-press-event",
1380 G_CALLBACK (callback_notebook_button_press_event), w_current);
1384 void
1385 gschem_dockable_initialize_toplevel (GschemToplevel *w_current)
1387 /* restore saved page order and last opened page */
1388 restore_page_order (w_current, GTK_NOTEBOOK (w_current->left_notebook));
1389 restore_current_page (w_current, GTK_NOTEBOOK (w_current->left_notebook));
1391 restore_page_order (w_current, GTK_NOTEBOOK (w_current->bottom_notebook));
1392 restore_current_page (w_current, GTK_NOTEBOOK (w_current->bottom_notebook));
1394 restore_page_order (w_current, GTK_NOTEBOOK (w_current->right_notebook));
1395 restore_current_page (w_current, GTK_NOTEBOOK (w_current->right_notebook));
1397 /* connect signals */
1398 connect_notebook_signals (w_current, w_current->left_notebook);
1399 connect_notebook_signals (w_current, w_current->bottom_notebook);
1400 connect_notebook_signals (w_current, w_current->right_notebook);
1402 g_signal_connect (
1403 G_OBJECT (w_current->main_window), "focus-in-event",
1404 G_CALLBACK (callback_main_window_focus_in_event), w_current);
1406 /* populate docking area menus */
1407 for (GList *l = w_current->dockables; l != NULL; l = l->next) {
1408 gtk_menu_shell_append (GTK_MENU_SHELL (w_current->left_docking_area_menu),
1409 GSCHEM_DOCKABLE (l->data)->dock_left_item);
1410 gtk_menu_shell_append (GTK_MENU_SHELL (w_current->bottom_docking_area_menu),
1411 GSCHEM_DOCKABLE (l->data)->dock_bottom_item);
1412 gtk_menu_shell_append (GTK_MENU_SHELL (w_current->right_docking_area_menu),
1413 GSCHEM_DOCKABLE (l->data)->dock_right_item);
1418 void
1419 gschem_dockable_cleanup_toplevel (GschemToplevel *w_current)
1421 /* disconnect notebook signals */
1422 g_signal_handlers_disconnect_by_data (w_current->left_notebook, w_current);
1423 g_signal_handlers_disconnect_by_data (w_current->bottom_notebook, w_current);
1424 g_signal_handlers_disconnect_by_data (w_current->right_notebook, w_current);
1426 /* destroy dock windows */
1427 for (GList *l = w_current->dockables; l != NULL; l = l->next) {
1428 GschemDockable *dockable = GSCHEM_DOCKABLE (l->data);
1430 /* if the dockable is visible, save geometry */
1431 switch (gschem_dockable_get_state (dockable)) {
1432 case GSCHEM_DOCKABLE_STATE_HIDDEN:
1433 /* nothing to do */
1434 break;
1435 case GSCHEM_DOCKABLE_STATE_DIALOG:
1436 case GSCHEM_DOCKABLE_STATE_WINDOW:
1437 save_internal_geometry (dockable);
1438 save_window_geometry (dockable);
1439 break;
1440 case GSCHEM_DOCKABLE_STATE_DOCKED_LEFT:
1441 case GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM:
1442 case GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT:
1443 save_internal_geometry (dockable);
1444 break;
1447 if (dockable->window != NULL) {
1448 gtk_widget_destroy (dockable->window);
1449 dockable->window = NULL;
1452 if (dockable->widget != NULL) {
1453 gtk_widget_destroy (dockable->widget);
1454 dockable->widget = NULL;
1460 /******************************************************************************/
1463 static gboolean
1464 callback_after_window_key_press_event (GtkWidget *widget,
1465 GdkEventKey *event,
1466 GschemDockable *dockable)
1468 if ((event->keyval == GDK_KEY_Return ||
1469 event->keyval == GDK_KEY_ISO_Enter ||
1470 event->keyval == GDK_KEY_KP_Enter) &&
1471 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0) {
1472 if (gschem_dockable_get_state (dockable) == GSCHEM_DOCKABLE_STATE_DIALOG)
1473 gschem_dockable_hide (dockable);
1474 else {
1475 gtk_widget_grab_focus (dockable->w_current->drawing_area);
1476 x_window_present (dockable->w_current);
1478 return TRUE;
1481 if (event->keyval == GDK_KEY_Escape &&
1482 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0) {
1483 if (gschem_dockable_get_state (dockable) == GSCHEM_DOCKABLE_STATE_DIALOG)
1484 gschem_dockable_hide (dockable);
1485 else {
1486 gtk_widget_grab_focus (dockable->w_current->drawing_area);
1487 x_window_present (dockable->w_current);
1490 if (dockable->cancellable)
1491 GSCHEM_DOCKABLE_GET_CLASS (dockable)->cancel (dockable);
1492 return TRUE;
1495 return FALSE;
1499 static gboolean
1500 callback_window_delete_event (GtkWidget *widget,
1501 GdkEvent *event,
1502 GschemDockable *dockable)
1504 GschemDockableState state = gschem_dockable_get_state (dockable);
1505 g_return_val_if_fail (state == GSCHEM_DOCKABLE_STATE_DIALOG ||
1506 state == GSCHEM_DOCKABLE_STATE_WINDOW, FALSE);
1508 gschem_dockable_hide (dockable);
1510 if (state == GSCHEM_DOCKABLE_STATE_DIALOG && dockable->cancellable)
1511 GSCHEM_DOCKABLE_GET_CLASS (dockable)->cancel (dockable);
1513 return TRUE;
1517 static void
1518 callback_hide_button_clicked (GtkWidget *button,
1519 GschemDockable *dockable)
1521 gschem_dockable_hide (dockable);
1525 static void
1526 callback_cancel_button_clicked (GtkWidget *button,
1527 GschemDockable *dockable)
1529 gschem_dockable_hide (dockable);
1531 if (dockable->cancellable)
1532 GSCHEM_DOCKABLE_GET_CLASS (dockable)->cancel (dockable);
1536 static void
1537 callback_help_button_clicked (GtkWidget *button,
1538 GschemDockable *dockable)
1540 g_return_if_fail (dockable->help_page != NULL);
1542 scm_dynwind_begin (0);
1543 g_dynwind_window (dockable->w_current);
1544 g_scm_eval_protected (scm_list_2 (
1545 scm_variable_ref (
1546 scm_c_public_variable ("gschem gschemdoc",
1547 "show-wiki")),
1548 scm_from_utf8_string (dockable->help_page)),
1549 SCM_UNDEFINED);
1550 scm_dynwind_end ();
1554 static void
1555 callback_dock_item_toggled (GtkWidget *check_menu_item,
1556 GschemDockable *dockable)
1558 gboolean active = gtk_check_menu_item_get_active (
1559 GTK_CHECK_MENU_ITEM (check_menu_item));
1561 GschemDockableState current_state = gschem_dockable_get_state (dockable);
1562 GschemDockableState target_state;
1564 if (check_menu_item == dockable->dock_left_item)
1565 target_state = GSCHEM_DOCKABLE_STATE_DOCKED_LEFT;
1566 else if (check_menu_item == dockable->dock_bottom_item)
1567 target_state = GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM;
1568 else if (check_menu_item == dockable->dock_right_item)
1569 target_state = GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT;
1570 else
1571 g_assert_not_reached ();
1573 if (active && current_state != target_state)
1574 gschem_dockable_set_state (dockable, target_state);
1575 else if (!active && current_state == target_state)
1576 gschem_dockable_hide (dockable);
1580 static void
1581 callback_menu_item_activate (GtkWidget *menu_item,
1582 GschemDockable *dockable)
1584 if (menu_item == dockable->detach_item)
1585 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_WINDOW);
1586 else if (menu_item == dockable->move_left_item)
1587 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_LEFT);
1588 else if (menu_item == dockable->move_bottom_item)
1589 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM);
1590 else if (menu_item == dockable->move_right_item)
1591 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT);
1592 else if (menu_item == dockable->close_item)
1593 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_HIDDEN);
1594 else
1595 g_assert_not_reached ();
1599 static gboolean
1600 callback_event_box_button_press_event (GtkWidget *widget,
1601 GdkEventButton *event,
1602 GschemDockable *dockable)
1604 if (event->button == 2 && event->type == GDK_BUTTON_PRESS) {
1605 gschem_dockable_hide (dockable);
1606 return TRUE;
1609 if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
1610 if (dockable->menu == NULL)
1611 create_menu (dockable);
1613 GschemDockableState state = gschem_dockable_get_state (dockable);
1614 gtk_widget_set_sensitive (dockable->detach_item,
1615 state != GSCHEM_DOCKABLE_STATE_WINDOW);
1616 gtk_widget_set_sensitive (dockable->move_left_item,
1617 state != GSCHEM_DOCKABLE_STATE_DOCKED_LEFT);
1618 gtk_widget_set_sensitive (dockable->move_bottom_item,
1619 state != GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM);
1620 gtk_widget_set_sensitive (dockable->move_right_item,
1621 state != GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT);
1622 gtk_widget_set_sensitive (dockable->close_item,
1623 state != GSCHEM_DOCKABLE_STATE_HIDDEN);
1625 gtk_menu_popup (GTK_MENU (dockable->menu), NULL, NULL, NULL, NULL,
1626 event->button, event->time);
1627 return TRUE;
1630 return FALSE;
1634 static gboolean
1635 callback_main_window_focus_in_event (GtkWidget *widget,
1636 GdkEvent *event,
1637 GschemToplevel *w_current)
1639 static gboolean initialized = FALSE;
1641 for (GList *l = w_current->dockables; l != NULL; l = l->next) {
1642 GschemDockable *dockable = GSCHEM_DOCKABLE (l->data);
1643 if (gschem_dockable_get_state (dockable) == GSCHEM_DOCKABLE_STATE_DIALOG)
1644 gschem_dockable_set_state (dockable, GSCHEM_DOCKABLE_STATE_WINDOW);
1647 if (!initialized) {
1648 initialized = TRUE;
1649 restore_detached_dockables (w_current);
1652 return FALSE;
1656 static void
1657 callback_notebook_page_added (GtkWidget *notebook,
1658 GtkWidget *child,
1659 guint page_num,
1660 GschemToplevel *w_current)
1662 GschemDockable *dockable = get_dockable_by_widget (w_current, child);
1663 g_return_if_fail (dockable != NULL);
1665 update_menu_items (dockable);
1666 save_state (dockable);
1670 static void
1671 callback_notebook_page_added_removed_reordered (GtkWidget *notebook,
1672 GtkWidget *child,
1673 guint page_num,
1674 GschemToplevel *w_current)
1676 save_page_order (w_current, GTK_NOTEBOOK (notebook));
1678 /* show or hide notebook as appropriate */
1679 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) == 0)
1680 gtk_widget_hide (notebook);
1681 else
1682 gtk_widget_show (notebook);
1686 static void
1687 callback_notebook_switch_page (GtkWidget *notebook,
1688 GtkWidget *arg1,
1689 guint arg2,
1690 GschemToplevel *w_current)
1692 GschemDockable *dockable = get_dockable_by_widget (w_current, arg1);
1693 g_return_if_fail (dockable != NULL);
1695 save_current_page (w_current, GTK_NOTEBOOK (notebook), dockable);
1699 static GtkNotebook *
1700 callback_notebook_create_window (GtkWidget *notebook,
1701 GtkWidget *page,
1702 gint x, gint y,
1703 GschemToplevel *w_current)
1705 GschemDockable *dockable = get_dockable_by_widget (w_current, page);
1706 g_return_val_if_fail (dockable != NULL, NULL);
1708 /* This is kind of a hack: Write the desired window position to the
1709 configuration so it is restored when detaching */
1711 EdaConfig *cfg = eda_config_get_user_context ();
1712 if (cfg != NULL) {
1713 eda_config_set_int (cfg, dockable->group_name, "x", x);
1714 eda_config_set_int (cfg, dockable->group_name, "y", y);
1717 gschem_dockable_detach (dockable, FALSE);
1718 return NULL;
1722 static gboolean
1723 callback_notebook_key_press_event (GtkWidget *notebook,
1724 GdkEventKey *event,
1725 GschemToplevel *w_current)
1727 gint page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
1728 GtkWidget *widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook),
1729 page_num);
1730 GschemDockable *dockable = get_dockable_by_widget (w_current, widget);
1731 g_return_val_if_fail (dockable != NULL, FALSE);
1733 if ((event->keyval == GDK_KEY_Return ||
1734 event->keyval == GDK_KEY_ISO_Enter ||
1735 event->keyval == GDK_KEY_KP_Enter) &&
1736 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0) {
1737 gtk_widget_grab_focus (w_current->drawing_area);
1738 return TRUE;
1741 if (event->keyval == GDK_KEY_Escape &&
1742 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0) {
1743 gtk_widget_grab_focus (w_current->drawing_area);
1744 if (GSCHEM_DOCKABLE_GET_CLASS (dockable)->cancel != NULL)
1745 GSCHEM_DOCKABLE_GET_CLASS (dockable)->cancel (dockable);
1746 return TRUE;
1749 return FALSE;
1753 static gboolean
1754 callback_notebook_button_press_event (GtkWidget *notebook,
1755 GdkEventButton *event,
1756 GschemToplevel *w_current)
1758 if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
1759 return FALSE;
1761 gpointer data;
1762 gdk_window_get_user_data (event->window, &data);
1763 if (data != notebook)
1764 return FALSE;
1766 GtkWidget *menu = notebook == w_current->left_notebook ?
1767 w_current->left_docking_area_menu :
1768 notebook == w_current->bottom_notebook ?
1769 w_current->bottom_docking_area_menu :
1770 notebook == w_current->right_notebook ?
1771 w_current->right_docking_area_menu : NULL;
1772 g_return_val_if_fail (menu != NULL, FALSE);
1773 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1774 event->button, event->time);
1776 return TRUE;