Bump gEDA version
[geda-gaf.git] / gschem / src / x_window.c
blobe6b86bf53404e7e20937f3d4411826d8a3ef5a24
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
20 #include <config.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
26 #include "gdk/gdk.h"
27 #ifdef GDK_WINDOWING_X11
28 #include "gdk/gdkx.h"
29 #endif
31 #include "gschem.h"
32 #include "actions.decl.x"
34 #include "gschem_compselect_dockable.h"
35 #include "gschem_object_properties_dockable.h"
36 #include "gschem_text_properties_dockable.h"
37 #include "gschem_multiattrib_dockable.h"
38 #include "gschem_options_dockable.h"
39 #include "gschem_log_dockable.h"
40 #include "gschem_find_text_dockable.h"
41 #include "gschem_patch_dockable.h"
42 #include "gschem_pagesel_dockable.h"
44 #define GSCHEM_THEME_ICON_NAME "geda-gschem"
46 /*! \todo Finish function documentation!!!
47 * \brief
48 * \par Function Description
51 void x_window_setup (GschemToplevel *w_current)
53 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
55 /* immediately setup user params */
56 i_vars_set(w_current);
58 /* Initialize the autosave callback */
59 s_page_autosave_init(toplevel);
61 /* Initialize the clipboard callback */
62 x_clipboard_init (w_current);
64 /* Add to the list of windows */
65 global_window_list = g_list_append (global_window_list, w_current);
67 /* X related stuff */
68 x_menus_create_submenus (w_current);
69 x_window_create_main (w_current);
71 /* update sensitivity of paste action */
72 x_clipboard_update_menus (w_current);
74 gschem_action_set_sensitive (action_add_last_component, FALSE, w_current);
76 /* disable terminal REPL action if stdin is not a terminal */
77 gschem_action_set_sensitive (action_file_repl, isatty (STDIN_FILENO),
78 w_current);
81 /*! \todo Finish function documentation!!!
82 * \brief
83 * \par Function Description
86 void x_window_create_drawing(GtkWidget *scrolled, GschemToplevel *w_current)
88 /* drawing next */
89 w_current->drawing_area = GTK_WIDGET (gschem_page_view_new_with_page (w_current->toplevel->page_current));
90 /* Set the size here. Be sure that it has an aspect ratio of 1.333
91 * We could calculate this based on root window size, but for now
92 * lets just set it to:
93 * Width = root_width*3/4 Height = Width/1.3333333333
94 * 1.3333333 is the desired aspect ratio!
97 gtk_container_add(GTK_CONTAINER(scrolled), w_current->drawing_area);
99 GTK_WIDGET_SET_FLAGS (w_current->drawing_area, GTK_CAN_FOCUS );
100 gtk_widget_grab_focus (w_current->drawing_area);
101 gtk_widget_show (w_current->drawing_area);
104 /*! \brief Set up callbacks for window events that affect drawing.
105 * \par Function Description
107 * Installs GTK+ callback handlers for signals that are emitted by
108 * the drawing area, and some for the main window that affect the drawing
109 * area.
111 * \param [in] w_current The toplevel environment.
113 void x_window_setup_draw_events(GschemToplevel *w_current)
115 struct event_reg_t {
116 gchar *detailed_signal;
117 GCallback c_handler;
120 struct event_reg_t drawing_area_events[] = {
121 { "expose_event", G_CALLBACK(x_event_expose) },
122 { "expose_event", G_CALLBACK(x_event_raise_dialog_boxes) },
123 { "button_press_event", G_CALLBACK(x_event_button_pressed) },
124 { "button_release_event", G_CALLBACK(x_event_button_released) },
125 { "motion_notify_event", G_CALLBACK(x_event_motion) },
126 { "configure_event", G_CALLBACK(x_event_configure) },
127 { "key_press_event", G_CALLBACK(x_event_key) },
128 { "key_release_event", G_CALLBACK(x_event_key) },
129 { "scroll_event", G_CALLBACK(x_event_scroll) },
130 { "update-grid-info", G_CALLBACK(i_update_grid_info_callback) },
131 { "notify::page", G_CALLBACK(gschem_toplevel_notify_page_callback) },
132 { NULL, NULL } };
133 struct event_reg_t main_window_events[] = {
134 { "enter_notify_event", G_CALLBACK(x_event_enter) },
135 { NULL, NULL } };
136 struct event_reg_t *tmp;
138 /* is the configure event type missing here? hack */
139 gtk_widget_set_events (w_current->drawing_area,
140 GDK_EXPOSURE_MASK |
141 GDK_POINTER_MOTION_MASK |
142 GDK_BUTTON_PRESS_MASK |
143 GDK_ENTER_NOTIFY_MASK |
144 GDK_KEY_PRESS_MASK |
145 GDK_BUTTON_RELEASE_MASK);
147 for (tmp = drawing_area_events; tmp->detailed_signal != NULL; tmp++) {
148 g_signal_connect (w_current->drawing_area,
149 tmp->detailed_signal,
150 tmp->c_handler,
151 w_current);
154 for (tmp = main_window_events; tmp->detailed_signal != NULL; tmp++) {
155 g_signal_connect (w_current->main_window,
156 tmp->detailed_signal,
157 tmp->c_handler,
158 w_current);
163 static void
164 x_window_find_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
166 gint close = FALSE;
167 int count;
169 g_return_if_fail (w_current != NULL);
170 g_return_if_fail (w_current->toplevel != NULL);
172 switch (response) {
173 case GTK_RESPONSE_OK:
174 count = gschem_find_text_dockable_find (
175 GSCHEM_FIND_TEXT_DOCKABLE (w_current->find_text_dockable),
176 geda_list_get_glist (w_current->toplevel->pages),
177 gschem_find_text_widget_get_find_type (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
178 gschem_find_text_widget_get_find_text_string (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
179 gschem_find_text_widget_get_descend (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)));
180 if (count > 0) {
181 gschem_dockable_present (w_current->find_text_dockable);
182 close = TRUE;
184 break;
186 case GTK_RESPONSE_CANCEL:
187 case GTK_RESPONSE_DELETE_EVENT:
188 close = TRUE;
189 break;
191 default:
192 printf("x_window_find_text(): strange signal %d\n", response);
195 if (close) {
196 gtk_widget_grab_focus (w_current->drawing_area);
197 gtk_widget_hide (GTK_WIDGET (widget));
203 static void
204 x_window_hide_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
206 g_return_if_fail (w_current != NULL);
207 g_return_if_fail (w_current->toplevel != NULL);
209 if (response == GTK_RESPONSE_OK) {
210 o_edit_hide_specific_text (w_current,
211 s_page_objects (w_current->toplevel->page_current),
212 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
215 gtk_widget_grab_focus (w_current->drawing_area);
216 gtk_widget_hide (GTK_WIDGET (widget));
220 static void
221 x_window_show_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
223 g_return_if_fail (w_current != NULL);
224 g_return_if_fail (w_current->toplevel != NULL);
226 if (response == GTK_RESPONSE_OK) {
227 o_edit_show_specific_text (w_current,
228 s_page_objects (w_current->toplevel->page_current),
229 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
232 gtk_widget_grab_focus (w_current->drawing_area);
233 gtk_widget_hide (GTK_WIDGET (widget));
237 static void
238 x_window_invoke_macro (GschemMacroWidget *widget, int response, GschemToplevel *w_current)
240 if (response == GTK_RESPONSE_OK) {
241 const char *macro = gschem_macro_widget_get_macro_string (widget);
243 SCM interpreter = scm_list_2(scm_from_utf8_symbol("invoke-macro"),
244 scm_from_utf8_string(macro));
246 scm_dynwind_begin (0);
247 g_dynwind_window (w_current);
248 g_scm_eval_protected(interpreter, SCM_UNDEFINED);
249 scm_dynwind_end ();
252 gtk_widget_grab_focus (w_current->drawing_area);
253 gtk_widget_hide (GTK_WIDGET (widget));
256 static void
257 x_window_select_text (GschemFindTextDockable *dockable, OBJECT *object, GschemToplevel *w_current)
259 GschemPageView *view = gschem_toplevel_get_current_page_view (w_current);
260 g_return_if_fail (view != NULL);
262 OBJECT *page_obj;
264 g_return_if_fail (object != NULL);
265 page_obj = gschem_page_get_page_object(object);
266 g_return_if_fail (page_obj != NULL);
268 x_window_set_current_page (w_current, page_obj->page);
270 gschem_page_view_zoom_text (view, object, TRUE);
273 static gboolean
274 x_window_state_event (GtkWidget *widget,
275 GdkEventWindowState *event,
276 gpointer user_data)
278 eda_config_set_string (
279 eda_config_get_user_context (),
280 "gschem.window-geometry", "state",
281 (event->new_window_state &
282 GDK_WINDOW_STATE_FULLSCREEN) ? "fullscreen" :
283 (event->new_window_state &
284 GDK_WINDOW_STATE_MAXIMIZED) ? "maximized" : "normal");
286 return FALSE; /* propagate the event further */
289 static void
290 x_window_save_menu_geometry (GtkMenuShell *menu_shell,
291 GschemToplevel *w_current)
293 for (GList *l = gtk_container_get_children (GTK_CONTAINER (menu_shell));
294 l != NULL; l = l->next) {
295 GtkMenuItem *menu_item = GTK_MENU_ITEM (l->data);
297 GtkWidget *menu = menu_item->submenu;
298 if (menu == NULL)
299 /* not a submenu */
300 continue;
302 char *settings_name = g_object_get_data (G_OBJECT (menu), "settings-name");
303 if (settings_name == NULL)
304 /* menu doesn't have a settings name set */
305 continue;
307 gint coords[4];
308 gsize length = 0;
310 if (GTK_MENU (menu)->torn_off) {
311 GtkWidget *window = GTK_MENU (menu)->tearoff_window;
312 g_return_if_fail (window != NULL);
314 gtk_window_get_position (GTK_WINDOW (window), &coords[0], &coords[1]);
315 gtk_window_get_size (GTK_WINDOW (window), &coords[2], &coords[3]);
316 length = 4;
319 eda_config_set_int_list (
320 eda_config_get_user_context (),
321 "gschem.menu-geometry", settings_name, coords, length);
323 x_window_save_menu_geometry (GTK_MENU_SHELL (menu), w_current);
327 static void
328 x_window_save_geometry (GschemToplevel *w_current)
330 gchar *window_state;
331 GtkAllocation allocation;
333 /* save window geometry */
334 window_state = eda_config_get_string (eda_config_get_user_context (),
335 "gschem.window-geometry",
336 "state", NULL);
337 if (window_state != NULL && strcmp (window_state, "normal") == 0) {
338 gint width = -1, height = -1;
339 gtk_window_get_size (GTK_WINDOW (w_current->main_window), &width, &height);
340 if (width > 0 && height > 0) {
341 eda_config_set_int (eda_config_get_user_context (),
342 "gschem.window-geometry", "width", width);
343 eda_config_set_int (eda_config_get_user_context (),
344 "gschem.window-geometry", "height", height);
347 g_free (window_state);
349 /* save torn-off menus */
350 if (w_current->menubar != NULL)
351 x_window_save_menu_geometry (
352 GTK_MENU_SHELL (w_current->menubar), w_current);
354 /* save dock area geometry */
355 gtk_widget_get_allocation (w_current->left_notebook, &allocation);
356 if (allocation.width > 0)
357 eda_config_set_int (eda_config_get_user_context (),
358 "gschem.dock-geometry.left",
359 "size", allocation.width);
361 gtk_widget_get_allocation (w_current->bottom_notebook, &allocation);
362 if (allocation.height > 0)
363 eda_config_set_int (eda_config_get_user_context (),
364 "gschem.dock-geometry.bottom",
365 "size", allocation.height);
367 gtk_widget_get_allocation (w_current->right_notebook, &allocation);
368 if (allocation.width > 0)
369 eda_config_set_int (eda_config_get_user_context (),
370 "gschem.dock-geometry.right",
371 "size", allocation.width);
374 static void
375 x_window_restore_menu_geometry (GtkMenuShell *menu_shell,
376 GschemToplevel *w_current)
378 for (GList *l = gtk_container_get_children (GTK_CONTAINER (menu_shell));
379 l != NULL; l = l->next) {
380 GtkMenuItem *menu_item = GTK_MENU_ITEM (l->data);
382 GtkWidget *menu = menu_item->submenu;
383 if (menu == NULL)
384 /* not a submenu */
385 continue;
387 char *settings_name = g_object_get_data (G_OBJECT (menu), "settings-name");
388 if (settings_name == NULL)
389 /* menu doesn't have a settings name set */
390 continue;
392 gsize length = 0;
393 gint *coords = eda_config_get_int_list (
394 eda_config_get_user_context (),
395 "gschem.menu-geometry", settings_name, &length, NULL);
397 if (coords != NULL && length == 4) {
398 gtk_menu_set_tearoff_state (GTK_MENU (menu), TRUE);
400 GtkWidget *window = GTK_MENU (menu)->tearoff_window;
401 g_return_if_fail (window != NULL);
403 gtk_window_move (GTK_WINDOW (window), coords[0], coords[1]);
404 gtk_window_resize (GTK_WINDOW (window), coords[2], coords[3]);
406 g_free(coords);
408 x_window_restore_menu_geometry (GTK_MENU_SHELL (menu), w_current);
412 static gboolean
413 x_window_restore_all_menu_geometry (GschemToplevel *w_current)
415 g_signal_handlers_disconnect_by_func(
416 G_OBJECT (w_current->main_window),
417 G_CALLBACK (x_window_restore_all_menu_geometry), w_current);
419 if (w_current->menubar != NULL)
420 x_window_restore_menu_geometry (
421 GTK_MENU_SHELL (w_current->menubar), w_current);
423 return FALSE;
426 static void
427 x_window_restore_geometry (GschemToplevel *w_current)
429 gint width, height, dock_size;
430 gchar *window_state;
432 /* restore main window size */
433 width = eda_config_get_int (eda_config_get_user_context (),
434 "gschem.window-geometry", "width", NULL);
435 height = eda_config_get_int (eda_config_get_user_context (),
436 "gschem.window-geometry", "height", NULL);
437 if (width <= 0 || height <= 0) {
438 width = 1200;
439 height = 900;
441 g_object_set (w_current->main_window,
442 "default-width", width,
443 "default-height", height,
444 NULL);
446 /* restore main window state */
447 window_state = eda_config_get_string (eda_config_get_user_context (),
448 "gschem.window-geometry",
449 "state", NULL);
450 if (window_state != NULL && strcmp (window_state, "fullscreen") == 0)
451 gtk_window_fullscreen (GTK_WINDOW (w_current->main_window));
452 else if (window_state != NULL && strcmp (window_state, "maximized") == 0)
453 gtk_window_maximize (GTK_WINDOW (w_current->main_window));
454 g_free (window_state);
456 /* defer restoring torn-off menus until main window is shown */
457 g_signal_connect_swapped (
458 G_OBJECT (w_current->main_window), "focus-in-event",
459 G_CALLBACK (x_window_restore_all_menu_geometry), w_current);
461 /* restore docking area dimensions */
462 dock_size = eda_config_get_int (eda_config_get_user_context (),
463 "gschem.dock-geometry.left", "size", NULL);
464 if (dock_size <= 0)
465 dock_size = 300;
466 gtk_widget_set_size_request (w_current->left_notebook, dock_size, 0);
468 dock_size = eda_config_get_int (eda_config_get_user_context (),
469 "gschem.dock-geometry.bottom", "size", NULL);
470 if (dock_size <= 0)
471 dock_size = 150;
472 gtk_widget_set_size_request (w_current->bottom_notebook, 0, dock_size);
474 dock_size = eda_config_get_int (eda_config_get_user_context (),
475 "gschem.dock-geometry.right", "size", NULL);
476 if (dock_size <= 0)
477 dock_size = 300;
478 gtk_widget_set_size_request (w_current->right_notebook, dock_size, 0);
482 /*! \todo Finish function documentation!!!
483 * \brief
484 * \par Function Description
486 * \note
487 * When invoked (via signal delete_event), closes the current window
488 * if this is the last window, quit gschem
489 * used when you click the close button on the window which sends a DELETE
490 * signal to the app
492 static gboolean
493 x_window_close_wm (GtkWidget *widget, GdkEvent *event, gpointer data)
495 GschemToplevel *w_current = GSCHEM_TOPLEVEL (data);
496 g_return_val_if_fail ((w_current != NULL), TRUE);
498 x_window_close(w_current);
500 /* stop further propagation of the delete_event signal for window: */
501 /* - if user has cancelled the close the window should obvioulsy */
502 /* not be destroyed */
503 /* - otherwise window has already been destroyed, nothing more to */
504 /* do */
505 return TRUE;
509 void
510 x_window_update_file_change_notification (GschemToplevel *w_current,
511 PAGE *page)
513 if (page->is_untitled) {
514 g_object_set (w_current->file_change_notification,
515 "gschem-page", page,
516 "path", NULL,
517 NULL);
518 return;
521 gchar *basename = g_path_get_basename (page->page_filename);
522 gchar *markup = page->exists_on_disk
523 ? g_markup_printf_escaped (
524 _("<b>The file \"%s\" has changed on disk.</b>\n\n%s"),
525 basename,
526 page->CHANGED
527 ? _("Do you want to drop your changes and reload the file?")
528 : _("Do you want to reload it?"))
529 : g_markup_printf_escaped (
530 _("<b>The file \"%s\" has been created on disk.</b>\n\n%s"),
531 basename,
532 page->CHANGED
533 ? _("Do you want to drop your changes and load the file?")
534 : _("Do you want to open it?"));
535 g_object_set (w_current->file_change_notification,
536 "gschem-page", page,
537 "path", page->page_filename,
538 "has-known-mtime", page->exists_on_disk,
539 "known-mtime", &page->last_modified,
540 "button-stock-id", page->CHANGED
541 ? GTK_STOCK_REVERT_TO_SAVED
542 : page->exists_on_disk ? GTK_STOCK_REFRESH
543 : GTK_STOCK_OPEN,
544 "button-label", page->CHANGED
545 ? _("_Revert")
546 : page->exists_on_disk ? _("_Reload")
547 : _("_Open"),
548 "markup", markup,
549 NULL);
550 g_free (markup);
551 g_free (basename);
554 static void
555 x_window_file_change_response (GschemChangeNotification *chnot,
556 gint response_id, gpointer user_data)
558 if (response_id == GTK_RESPONSE_ACCEPT)
559 x_lowlevel_revert_page (chnot->w_current, chnot->page);
560 else {
561 chnot->page->exists_on_disk = chnot->has_current_mtime;
562 chnot->page->last_modified = chnot->current_mtime;
563 x_window_update_file_change_notification (chnot->w_current, chnot->page);
568 void
569 x_window_update_patch_change_notification (GschemToplevel *w_current,
570 PAGE *page)
572 gchar *patch_filename = x_patch_guess_filename (page);
573 gchar *basename, *markup;
575 if (patch_filename == NULL) {
576 basename = NULL;
577 markup = NULL;
578 } else {
579 struct stat buf;
580 basename = g_path_get_basename (patch_filename);
581 if (page->patch_filename != NULL)
582 markup = g_markup_printf_escaped (
583 _("<b>The back-annotation patch \"%s\" has been updated.</b>\n\n"
584 "Do you want to re-import it?"),
585 basename);
586 else if (page->patch_seen_on_disk)
587 markup = g_markup_printf_escaped (
588 _("<b>The back-annotation patch \"%s\" has been updated.</b>\n\n"
589 "Do you want to import it?"),
590 basename);
591 else if (stat (patch_filename, &buf) != -1)
592 markup = g_markup_printf_escaped (
593 _("<b>This file appears to have a back-annotation patch \"%s\" "
594 "associated with it.</b>\n\nDo you want to import it?"),
595 basename);
596 else
597 markup = g_markup_printf_escaped (
598 _("<b>A back-annotation patch \"%s\" has been created.</b>\n\n"
599 "Do you want to import it?"),
600 basename);
603 g_object_set (w_current->patch_change_notification,
604 "gschem-page", page,
605 "path", patch_filename,
606 "has-known-mtime", page->patch_seen_on_disk,
607 "known-mtime", &page->patch_mtime,
608 "button-label", _("_Import"),
609 "markup", markup,
610 NULL);
611 g_free (markup);
612 g_free (basename);
613 g_free (patch_filename);
616 static void
617 x_window_patch_change_response (GschemChangeNotification *chnot,
618 gint response_id, gpointer user_data)
620 if (response_id == GTK_RESPONSE_ACCEPT) {
621 if (chnot->page->patch_filename == NULL)
622 chnot->page->patch_filename = g_strdup (chnot->path);
623 x_patch_do_import (chnot->w_current, chnot->page);
624 } else {
625 chnot->page->patch_seen_on_disk = chnot->has_current_mtime;
626 chnot->page->patch_mtime = chnot->current_mtime;
627 x_window_update_patch_change_notification (chnot->w_current, chnot->page);
632 /*! \todo Finish function documentation!!!
633 * \brief
634 * \par Function Description
637 void x_window_create_main(GschemToplevel *w_current)
639 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
641 GtkPolicyType policy;
642 GtkWidget *main_box=NULL;
643 GtkWidget *handlebox=NULL;
644 GtkWidget *scrolled;
645 GtkAdjustment *hadjustment;
646 GtkAdjustment *vadjustment;
647 char *right_button_text;
648 GtkWidget *left_hpaned, *right_hpaned;
649 GtkWidget *vpaned;
650 GtkWidget *work_box;
652 w_current->main_window = GTK_WIDGET (gschem_main_window_new ());
654 gtk_widget_set_name (w_current->main_window, "gschem");
655 gtk_window_set_policy (GTK_WINDOW (w_current->main_window), TRUE, TRUE, TRUE);
657 /* We want the widgets to flow around the drawing area, so we don't
658 * set a size of the main window. The drawing area's size is fixed,
659 * see below
663 * normally we let the window manager handle locating and sizing
664 * the window. However, for some batch processing of schematics
665 * (generating a pdf of all schematics for example) we want to
666 * override this. Hence "auto_place_mode".
668 if( auto_place_mode )
669 gtk_widget_set_uposition (w_current->main_window, 10, 10);
671 /* this should work fine */
672 g_signal_connect (G_OBJECT (w_current->main_window), "delete_event",
673 G_CALLBACK (x_window_close_wm), w_current);
675 /* Containers first */
676 main_box = gtk_vbox_new(FALSE, 1);
677 gtk_container_set_border_width (GTK_CONTAINER (main_box), 0);
678 gtk_container_add(GTK_CONTAINER(w_current->main_window), main_box);
680 x_menus_create_main_menu (w_current);
681 if (w_current->menubar != NULL) {
682 if (w_current->handleboxes) {
683 handlebox = gtk_handle_box_new ();
684 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
685 gtk_container_add (GTK_CONTAINER (handlebox), w_current->menubar);
686 } else {
687 gtk_box_pack_start (GTK_BOX (main_box), w_current->menubar,
688 FALSE, FALSE, 0);
691 gschem_action_set_sensitive (action_view_menubar, w_current->menubar != NULL,
692 w_current);
693 gschem_action_set_active (action_view_menubar, w_current->menubar != NULL,
694 w_current);
696 gtk_widget_realize (w_current->main_window);
698 x_menus_create_toolbar (w_current);
699 if (w_current->toolbar != NULL) {
700 if (w_current->handleboxes) {
701 handlebox = gtk_handle_box_new ();
702 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
703 gtk_container_add (GTK_CONTAINER (handlebox), w_current->toolbar);
704 gtk_widget_set_visible (handlebox, w_current->toolbars);
705 gtk_widget_set_no_show_all (handlebox, TRUE);
706 } else {
707 gtk_box_pack_start (GTK_BOX (main_box), w_current->toolbar,
708 FALSE, FALSE, 0);
709 gtk_widget_set_visible (w_current->toolbar, w_current->toolbars);
710 gtk_widget_set_no_show_all (w_current->toolbar, TRUE);
713 gschem_action_set_sensitive (action_view_toolbar, w_current->toolbar != NULL,
714 w_current);
715 gschem_action_set_active (action_view_toolbar,
716 w_current->toolbars && w_current->toolbar != NULL,
717 w_current);
719 left_hpaned = gtk_hpaned_new ();
720 gtk_container_add (GTK_CONTAINER(main_box), left_hpaned);
722 w_current->left_notebook = gtk_notebook_new ();
723 gtk_paned_pack1 (GTK_PANED (left_hpaned),
724 w_current->left_notebook,
725 FALSE,
726 TRUE);
727 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->left_notebook),
728 "gschem-dock");
730 right_hpaned = gtk_hpaned_new ();
731 gtk_paned_pack2 (GTK_PANED (left_hpaned),
732 right_hpaned,
733 TRUE,
734 TRUE);
736 w_current->right_notebook = gtk_notebook_new ();
737 gtk_paned_pack2 (GTK_PANED (right_hpaned),
738 w_current->right_notebook,
739 FALSE,
740 TRUE);
741 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->right_notebook),
742 "gschem-dock");
744 vpaned = gtk_vpaned_new ();
745 gtk_paned_pack1 (GTK_PANED (right_hpaned),
746 vpaned,
747 TRUE,
748 TRUE);
750 w_current->bottom_notebook = gtk_notebook_new ();
751 gtk_paned_pack2 (GTK_PANED (vpaned),
752 w_current->bottom_notebook,
753 FALSE,
754 TRUE);
755 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->bottom_notebook),
756 "gschem-dock");
758 work_box = gtk_vbox_new (FALSE, 0);
759 gtk_paned_pack1 (GTK_PANED (vpaned),
760 work_box,
761 TRUE,
762 TRUE);
764 w_current->file_change_notification =
765 g_object_new (GSCHEM_TYPE_CHANGE_NOTIFICATION,
766 "gschem-toplevel", w_current,
767 "message-type", GTK_MESSAGE_QUESTION,
768 NULL);
769 g_signal_connect (w_current->file_change_notification, "response",
770 G_CALLBACK (x_window_file_change_response), NULL);
771 gtk_box_pack_start (GTK_BOX (work_box),
772 w_current->file_change_notification->info_bar,
773 FALSE, FALSE, 0);
775 w_current->patch_change_notification =
776 g_object_new (GSCHEM_TYPE_CHANGE_NOTIFICATION,
777 "gschem-toplevel", w_current,
778 "message-type", GTK_MESSAGE_INFO,
779 NULL);
780 g_signal_connect (w_current->patch_change_notification, "response",
781 G_CALLBACK (x_window_patch_change_response), NULL);
782 gtk_box_pack_start (GTK_BOX (work_box),
783 w_current->patch_change_notification->info_bar,
784 FALSE, FALSE, 0);
786 /* Try to create popup menu (appears in right mouse button */
787 x_menus_create_main_popup (w_current);
790 /* Setup a GtkScrolledWindow for the drawing area */
791 hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
792 toplevel->init_left,
793 toplevel->init_right,
794 100.0,
795 100.0,
796 10.0));
798 vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (toplevel->init_bottom,
799 0.0,
800 toplevel->init_bottom - toplevel->init_top,
801 100.0,
802 100.0,
803 10.0));
805 scrolled = gtk_scrolled_window_new (hadjustment, vadjustment);
806 gtk_container_add (GTK_CONTAINER (work_box), scrolled);
807 x_window_create_drawing(scrolled, w_current);
808 x_window_setup_draw_events(w_current);
810 policy = (w_current->scrollbars_flag) ? GTK_POLICY_ALWAYS : GTK_POLICY_NEVER;
811 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), policy, policy);
812 gschem_action_set_active (action_view_scrollbars, w_current->scrollbars_flag,
813 w_current);
815 /* find text box */
816 w_current->find_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_FIND_TEXT_WIDGET, NULL));
818 gtk_box_pack_start (GTK_BOX (work_box),
819 w_current->find_text_widget,
820 FALSE,
821 FALSE,
824 g_signal_connect (w_current->find_text_widget,
825 "response",
826 G_CALLBACK (&x_window_find_text),
827 w_current);
829 /* hide text box */
830 w_current->hide_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
831 "button-text", pgettext ("actuate", "Hide"),
832 "label-text", _("Hide text starting with:"),
833 NULL));
835 gtk_box_pack_start (GTK_BOX (work_box),
836 w_current->hide_text_widget,
837 FALSE,
838 FALSE,
841 g_signal_connect (w_current->hide_text_widget,
842 "response",
843 G_CALLBACK (&x_window_hide_text),
844 w_current);
846 /* show text box */
847 w_current->show_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
848 "button-text", pgettext ("actuate", "Show"),
849 "label-text", _("Show text starting with:"),
850 NULL));
852 gtk_box_pack_start (GTK_BOX (work_box),
853 w_current->show_text_widget,
854 FALSE,
855 FALSE,
858 g_signal_connect (w_current->show_text_widget,
859 "response",
860 G_CALLBACK (&x_window_show_text),
861 w_current);
863 /* macro box */
864 w_current->macro_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_MACRO_WIDGET, NULL));
866 gtk_box_pack_start (GTK_BOX (work_box),
867 w_current->macro_widget,
868 FALSE,
869 FALSE,
872 g_signal_connect (w_current->macro_widget,
873 "response",
874 G_CALLBACK (&x_window_invoke_macro),
875 w_current);
878 w_current->compselect_dockable = g_object_new (
879 GSCHEM_TYPE_COMPSELECT_DOCKABLE,
880 "title", _("Library"),
881 "settings-name", "compselect",
882 "cancellable", TRUE,
883 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
884 "initial-width", 500,
885 "initial-height", 600,
886 "gschem-toplevel", w_current,
887 NULL);
889 w_current->object_properties_dockable = g_object_new (
890 GSCHEM_TYPE_OBJECT_PROPERTIES_DOCKABLE,
891 "title", _("Object"),
892 "settings-name", "object-properties", /* line-type */
893 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
894 "initial-width", 400,
895 "initial-height", 600,
896 "gschem-toplevel", w_current,
897 NULL);
899 w_current->text_properties_dockable = g_object_new (
900 GSCHEM_TYPE_TEXT_PROPERTIES_DOCKABLE,
901 "title", _("Text"),
902 "settings-name", "text-edit",
903 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
904 "initial-width", 400,
905 "initial-height", 450,
906 "gschem-toplevel", w_current,
907 NULL);
909 w_current->multiattrib_dockable = g_object_new (
910 GSCHEM_TYPE_MULTIATTRIB_DOCKABLE,
911 "title", _("Attributes"),
912 "settings-name", "multiattrib",
913 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
914 "initial-width", 450,
915 "initial-height", 450,
916 "gschem-toplevel", w_current,
917 NULL);
919 w_current->options_dockable = g_object_new (
920 GSCHEM_TYPE_OPTIONS_DOCKABLE,
921 "title", _("Options"),
922 "settings-name", "options", /* snap-size */
923 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
924 "initial-width", 320,
925 "initial-height", 350,
926 "gschem-toplevel", w_current,
927 NULL);
929 w_current->log_dockable = g_object_new (
930 GSCHEM_TYPE_LOG_DOCKABLE,
931 "title", _("Status"),
932 "settings-name", "log",
933 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM,
934 "initial-width", 640,
935 "initial-height", 480,
936 "gschem-toplevel", w_current,
937 NULL);
939 w_current->find_text_dockable = g_object_new (
940 GSCHEM_TYPE_FIND_TEXT_DOCKABLE,
941 "title", _("Search results"),
942 "settings-name", "find-text",
943 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM,
944 "initial-width", 500,
945 "initial-height", 300,
946 "gschem-toplevel", w_current,
947 NULL);
948 g_signal_connect (w_current->find_text_dockable,
949 "select-object",
950 G_CALLBACK (&x_window_select_text),
951 w_current);
953 w_current->patch_dockable = g_object_new (
954 GSCHEM_TYPE_PATCH_DOCKABLE,
955 "title", _("Patch"),
956 "settings-name", "patch",
957 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
958 "initial-width", 500,
959 "initial-height", 300,
960 "gschem-toplevel", w_current,
961 NULL);
963 w_current->pagesel_dockable = g_object_new (
964 GSCHEM_TYPE_PAGESEL_DOCKABLE,
965 "title", _("Pages"),
966 "settings-name", "pagesel",
967 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
968 "initial-width", 515,
969 "initial-height", 180,
970 "gschem-toplevel", w_current,
971 NULL);
973 gschem_dockable_initialize_toplevel (w_current);
976 /* bottom box */
977 if (default_third_button == POPUP_ENABLED) {
978 right_button_text = _("Menu/Cancel");
979 } else {
980 right_button_text = _("Pan/Cancel");
983 w_current->bottom_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_BOTTOM_WIDGET,
984 "grid-mode", gschem_options_get_grid_mode (w_current->options),
985 "grid-size", gschem_options_get_snap_size (w_current->options), /* x_grid_query_drawn_spacing (w_current), -- occurs before the page is set */
986 "left-button-text", _("Pick"),
987 "middle-button-text", _("none"),
988 "right-button-text", right_button_text,
989 "snap-mode", gschem_options_get_snap_mode (w_current->options),
990 "snap-size", gschem_options_get_snap_size (w_current->options),
991 "status-text", _("Select Mode"),
992 NULL));
994 i_update_middle_button (w_current, NULL, NULL);
996 gtk_box_pack_start (GTK_BOX (main_box), w_current->bottom_widget, FALSE, FALSE, 0);
998 x_window_restore_geometry (w_current);
999 g_signal_connect (G_OBJECT (w_current->main_window), "window-state-event",
1000 G_CALLBACK (x_window_state_event), w_current);
1002 gtk_widget_show_all (w_current->main_window);
1004 /* hide unused notebooks */
1005 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->left_notebook)) == 0)
1006 gtk_widget_hide (w_current->left_notebook);
1007 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->bottom_notebook)) == 0)
1008 gtk_widget_hide (w_current->bottom_notebook);
1009 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->right_notebook)) == 0)
1010 gtk_widget_hide (w_current->right_notebook);
1013 /*! \todo Finish function documentation!!!
1014 * \brief
1015 * \par Function Description
1018 void x_window_close(GschemToplevel *w_current)
1020 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
1021 gboolean last_window = FALSE;
1023 /* If we're closing whilst inside an action, re-wind the
1024 * page contents back to their state before we started */
1025 if (w_current->inside_action) {
1026 i_cancel (w_current);
1029 /* last chance to save possible unsaved pages */
1030 if (!x_dialog_close_window (w_current)) {
1031 /* user somehow cancelled the close */
1032 return;
1035 x_clipboard_finish (w_current);
1037 #if DEBUG
1038 o_conn_print_hash(w_current->page_current->conn_table);
1039 #endif
1041 w_current->dont_invalidate = TRUE;
1043 /* save window geometry */
1044 x_window_save_geometry (w_current);
1046 /* close all the dialog boxes */
1047 if (w_current->sowindow)
1048 gtk_widget_destroy(w_current->sowindow);
1050 if (w_current->tiwindow)
1051 gtk_widget_destroy(w_current->tiwindow);
1053 if (w_current->aawindow)
1054 gtk_widget_destroy(w_current->aawindow);
1056 if (w_current->aewindow)
1057 gtk_widget_destroy(w_current->aewindow);
1059 if (w_current->hkwindow)
1060 gtk_widget_destroy(w_current->hkwindow);
1062 if (w_current->sewindow)
1063 gtk_widget_destroy(w_current->sewindow);
1065 /* save dock window geometry, close dock windows, disconnect signals */
1066 gschem_dockable_cleanup_toplevel (w_current);
1068 g_clear_object (&w_current->compselect_dockable);
1069 g_clear_object (&w_current->object_properties_dockable);
1070 g_clear_object (&w_current->text_properties_dockable);
1071 g_clear_object (&w_current->multiattrib_dockable);
1072 g_clear_object (&w_current->options_dockable);
1073 g_clear_object (&w_current->log_dockable);
1074 g_clear_object (&w_current->find_text_dockable);
1075 g_clear_object (&w_current->patch_dockable);
1076 g_clear_object (&w_current->pagesel_dockable);
1078 if (g_list_length (global_window_list) == 1) {
1079 /* no more window after this one, remember to quit */
1080 last_window = TRUE;
1083 if (toplevel->major_changed_refdes) {
1084 GList* current = toplevel->major_changed_refdes;
1085 while (current)
1087 /* printf("yeah freeing: %s\n", (char*) current->data); */
1088 g_free(current->data);
1089 current = g_list_next(current);
1091 g_list_free(toplevel->major_changed_refdes);
1094 /* stuff that has to be done before we free w_current */
1095 if (last_window) {
1096 /* close the log file */
1097 s_log_close ();
1098 /* free the buffers */
1099 o_buffer_free (w_current);
1102 /* Allow Scheme value for this window to be garbage-collected */
1103 if (!SCM_UNBNDP (w_current->smob)) {
1104 SCM_SET_SMOB_DATA (w_current->smob, NULL);
1105 scm_gc_unprotect_object (w_current->smob);
1106 w_current->smob = SCM_UNDEFINED;
1109 /* finally close the main window */
1110 gtk_widget_destroy(w_current->main_window);
1112 global_window_list = g_list_remove (global_window_list, w_current);
1113 gschem_toplevel_free (w_current);
1115 /* just closed last window, so quit */
1116 if (last_window) {
1117 gtk_main_quit();
1121 /*! \todo Finish function documentation!!!
1122 * \brief
1123 * \par Function Description
1126 void x_window_close_all(GschemToplevel *w_current)
1128 GschemToplevel *current;
1129 GList *list_copy, *iter;
1131 iter = list_copy = g_list_copy (global_window_list);
1132 while (iter != NULL ) {
1133 current = (GschemToplevel *)iter->data;
1134 iter = g_list_next (iter);
1135 x_window_close (current);
1137 g_list_free (list_copy);
1140 /*! \brief Changes the current page.
1141 * \par Function Description
1142 * This function displays the specified page <B>page</B> in the
1143 * window attached to <B>toplevel</B>.
1145 * It changes the <B>toplevel</B>'s current page to <B>page</B>,
1146 * draws it and updates the user interface.
1148 * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>.
1150 * \param [in] w_current The toplevel environment.
1151 * \param [in] page The page to become current page.
1153 void
1154 x_window_set_current_page (GschemToplevel *w_current, PAGE *page)
1156 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
1157 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
1158 GList *iter;
1160 g_return_if_fail (page_view != NULL);
1161 g_return_if_fail (toplevel != NULL);
1162 g_return_if_fail (page != NULL);
1164 g_warn_if_fail (page_view->page == toplevel->page_current ||
1165 page_view->page == NULL);
1167 if (page == toplevel->page_current && page_view->page != NULL)
1168 /* nothing to do */
1169 return;
1171 o_redraw_cleanstates (w_current);
1173 gschem_page_view_set_page (page_view, page);
1175 gschem_action_set_sensitive (action_page_revert,
1176 page->is_untitled == FALSE &&
1177 g_file_test (page->page_filename,
1178 G_FILE_TEST_EXISTS |
1179 G_FILE_TEST_IS_REGULAR),
1180 w_current);
1182 o_undo_update_actions (w_current, page);
1184 iter = g_list_find (geda_list_get_glist (toplevel->pages), page);
1185 gschem_action_set_sensitive (action_page_prev,
1186 w_current->enforce_hierarchy
1187 ? s_hierarchy_find_prev_page (
1188 toplevel->pages, page) != NULL
1189 : iter != NULL && iter->prev != NULL,
1190 w_current);
1191 gschem_action_set_sensitive (action_page_next,
1192 w_current->enforce_hierarchy
1193 ? s_hierarchy_find_next_page(
1194 toplevel->pages, page) != NULL
1195 : iter != NULL && iter->next != NULL,
1196 w_current);
1197 gschem_action_set_sensitive (action_hierarchy_up, page->up >= 0, w_current);
1199 i_update_menus (w_current);
1200 /* i_set_filename (w_current, page->page_filename); */
1202 x_window_update_file_change_notification (w_current, page);
1203 x_window_update_patch_change_notification (w_current, page);
1205 x_pagesel_update (w_current);
1206 x_multiattrib_update (w_current);
1209 /*! \brief Raise the main window to the front.
1211 * This is mostly equivalent to
1212 * gtk_window_present (GTK_WINDOW (w_current->main_window));
1214 * One of the two actions that \c gtk_window_present performs on an
1215 * already-visible window (\c gdk_window_show) is triggering a bug
1216 * with toolbar icon drawing, the other (\c gdk_window_focus) is the
1217 * one we actually want. In order to work around that bug, just call
1218 * \c gdk_window_focus directly.
1220 * \param [in] w_current the toplevel environment
1222 void x_window_present (GschemToplevel *w_current)
1224 //gdk_window_show (w_current->main_window->window); /* the culprit */
1226 #ifdef GDK_WINDOWING_X11
1227 GdkDisplay *display = gtk_widget_get_display (w_current->main_window);
1228 guint32 timestamp = gdk_x11_display_get_user_time (display);
1229 #else
1230 guint32 timestamp = gtk_get_current_event_time ();
1231 #endif
1233 gdk_window_focus (w_current->main_window->window, timestamp);
1236 /*! \brief Setup default icon for GTK windows
1238 * \par Function Description
1239 * Sets the default window icon by name, to be found in the current icon
1240 * theme. The name used is \#defined above as GSCHEM_THEME_ICON_NAME.
1242 void x_window_set_default_icon( void )
1244 gtk_window_set_default_icon_name( GSCHEM_THEME_ICON_NAME );
1247 /*! \brief Setup icon search paths.
1248 * \par Function Description
1249 * Add the icons installed by gschem to the search path for the
1250 * default icon theme, so that they can be automatically found by GTK.
1252 void
1253 x_window_init_icons (void)
1255 gchar *icon_path;
1257 g_return_if_fail (s_path_sys_data () != NULL);
1259 icon_path = g_build_filename (s_path_sys_data (), "icons", NULL);
1260 gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
1261 icon_path);
1262 g_free (icon_path);
1265 /*! \brief Creates a new X window.
1267 * \par Function description
1269 * Creates and initializes new GschemToplevel object and then sets
1270 * and setups its libgeda \a toplevel.
1272 * \param toplevel The libgeda TOPLEVEL object.
1273 * \return Pointer to the new GschemToplevel object.
1275 GschemToplevel* x_window_new (TOPLEVEL *toplevel)
1277 GschemToplevel *w_current;
1279 w_current = gschem_toplevel_new ();
1280 gschem_toplevel_set_toplevel (w_current,
1281 (toplevel != NULL) ? toplevel : s_toplevel_new ());
1283 gschem_toplevel_get_toplevel (w_current)->load_newer_backup_func = x_fileselect_load_backup;
1284 gschem_toplevel_get_toplevel (w_current)->load_newer_backup_data = w_current;
1286 o_text_set_rendered_bounds_func (gschem_toplevel_get_toplevel (w_current),
1287 o_text_get_rendered_bounds, w_current);
1289 /* Damage notifications should invalidate the object on screen */
1290 o_add_change_notify (gschem_toplevel_get_toplevel (w_current),
1291 (ChangeNotifyFunc) o_invalidate,
1292 (ChangeNotifyFunc) o_invalidate, w_current);
1294 x_window_setup (w_current);
1296 return w_current;