gschem: Add file and patch change notifications
[geda-gaf.git] / gschem / src / x_window.c
blob8413decbe0cc932a177b139b0e48f0499d195a12
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 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 /* disable terminal REPL action if stdin is not a terminal */
72 gschem_action_set_sensitive (action_file_repl, isatty (STDIN_FILENO),
73 w_current);
76 /*! \todo Finish function documentation!!!
77 * \brief
78 * \par Function Description
81 void x_window_create_drawing(GtkWidget *scrolled, GschemToplevel *w_current)
83 /* drawing next */
84 w_current->drawing_area = GTK_WIDGET (gschem_page_view_new_with_page (w_current->toplevel->page_current));
85 /* Set the size here. Be sure that it has an aspect ratio of 1.333
86 * We could calculate this based on root window size, but for now
87 * lets just set it to:
88 * Width = root_width*3/4 Height = Width/1.3333333333
89 * 1.3333333 is the desired aspect ratio!
92 gtk_container_add(GTK_CONTAINER(scrolled), w_current->drawing_area);
94 GTK_WIDGET_SET_FLAGS (w_current->drawing_area, GTK_CAN_FOCUS );
95 gtk_widget_grab_focus (w_current->drawing_area);
96 gtk_widget_show (w_current->drawing_area);
99 /*! \brief Set up callbacks for window events that affect drawing.
100 * \par Function Description
102 * Installs GTK+ callback handlers for signals that are emitted by
103 * the drawing area, and some for the main window that affect the drawing
104 * area.
106 * \param [in] w_current The toplevel environment.
108 void x_window_setup_draw_events(GschemToplevel *w_current)
110 struct event_reg_t {
111 gchar *detailed_signal;
112 GCallback c_handler;
115 struct event_reg_t drawing_area_events[] = {
116 { "expose_event", G_CALLBACK(x_event_expose) },
117 { "expose_event", G_CALLBACK(x_event_raise_dialog_boxes) },
118 { "button_press_event", G_CALLBACK(x_event_button_pressed) },
119 { "button_release_event", G_CALLBACK(x_event_button_released) },
120 { "motion_notify_event", G_CALLBACK(x_event_motion) },
121 { "configure_event", G_CALLBACK(x_event_configure) },
122 { "key_press_event", G_CALLBACK(x_event_key) },
123 { "key_release_event", G_CALLBACK(x_event_key) },
124 { "scroll_event", G_CALLBACK(x_event_scroll) },
125 { "update-grid-info", G_CALLBACK(i_update_grid_info_callback) },
126 { "notify::page", G_CALLBACK(gschem_toplevel_notify_page_callback) },
127 { NULL, NULL } };
128 struct event_reg_t main_window_events[] = {
129 { "enter_notify_event", G_CALLBACK(x_event_enter) },
130 { NULL, NULL } };
131 struct event_reg_t *tmp;
133 /* is the configure event type missing here? hack */
134 gtk_widget_set_events (w_current->drawing_area,
135 GDK_EXPOSURE_MASK |
136 GDK_POINTER_MOTION_MASK |
137 GDK_BUTTON_PRESS_MASK |
138 GDK_ENTER_NOTIFY_MASK |
139 GDK_KEY_PRESS_MASK |
140 GDK_BUTTON_RELEASE_MASK);
142 for (tmp = drawing_area_events; tmp->detailed_signal != NULL; tmp++) {
143 g_signal_connect (w_current->drawing_area,
144 tmp->detailed_signal,
145 tmp->c_handler,
146 w_current);
149 for (tmp = main_window_events; tmp->detailed_signal != NULL; tmp++) {
150 g_signal_connect (w_current->main_window,
151 tmp->detailed_signal,
152 tmp->c_handler,
153 w_current);
158 static void
159 x_window_find_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
161 gint close = FALSE;
162 int count;
164 g_return_if_fail (w_current != NULL);
165 g_return_if_fail (w_current->toplevel != NULL);
167 switch (response) {
168 case GTK_RESPONSE_OK:
169 count = gschem_find_text_dockable_find (
170 GSCHEM_FIND_TEXT_DOCKABLE (w_current->find_text_dockable),
171 geda_list_get_glist (w_current->toplevel->pages),
172 gschem_find_text_widget_get_find_type (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
173 gschem_find_text_widget_get_find_text_string (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
174 gschem_find_text_widget_get_descend (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)));
175 if (count > 0) {
176 gschem_dockable_present (w_current->find_text_dockable);
177 close = TRUE;
179 break;
181 case GTK_RESPONSE_CANCEL:
182 case GTK_RESPONSE_DELETE_EVENT:
183 close = TRUE;
184 break;
186 default:
187 printf("x_window_find_text(): strange signal %d\n", response);
190 if (close) {
191 gtk_widget_grab_focus (w_current->drawing_area);
192 gtk_widget_hide (GTK_WIDGET (widget));
198 static void
199 x_window_hide_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
201 g_return_if_fail (w_current != NULL);
202 g_return_if_fail (w_current->toplevel != NULL);
204 if (response == GTK_RESPONSE_OK) {
205 o_edit_hide_specific_text (w_current,
206 s_page_objects (w_current->toplevel->page_current),
207 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
210 gtk_widget_grab_focus (w_current->drawing_area);
211 gtk_widget_hide (GTK_WIDGET (widget));
215 static void
216 x_window_show_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
218 g_return_if_fail (w_current != NULL);
219 g_return_if_fail (w_current->toplevel != NULL);
221 if (response == GTK_RESPONSE_OK) {
222 o_edit_show_specific_text (w_current,
223 s_page_objects (w_current->toplevel->page_current),
224 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
227 gtk_widget_grab_focus (w_current->drawing_area);
228 gtk_widget_hide (GTK_WIDGET (widget));
232 static void
233 x_window_invoke_macro (GschemMacroWidget *widget, int response, GschemToplevel *w_current)
235 if (response == GTK_RESPONSE_OK) {
236 const char *macro = gschem_macro_widget_get_macro_string (widget);
238 SCM interpreter = scm_list_2(scm_from_utf8_symbol("invoke-macro"),
239 scm_from_utf8_string(macro));
241 scm_dynwind_begin (0);
242 g_dynwind_window (w_current);
243 g_scm_eval_protected(interpreter, SCM_UNDEFINED);
244 scm_dynwind_end ();
247 gtk_widget_grab_focus (w_current->drawing_area);
248 gtk_widget_hide (GTK_WIDGET (widget));
251 static void
252 x_window_select_text (GschemFindTextDockable *dockable, OBJECT *object, GschemToplevel *w_current)
254 GschemPageView *view = gschem_toplevel_get_current_page_view (w_current);
255 g_return_if_fail (view != NULL);
257 OBJECT *page_obj;
259 g_return_if_fail (object != NULL);
260 page_obj = gschem_page_get_page_object(object);
261 g_return_if_fail (page_obj != NULL);
263 x_window_set_current_page (w_current, page_obj->page);
265 gschem_page_view_zoom_text (view, object, TRUE);
268 static gboolean
269 x_window_state_event (GtkWidget *widget,
270 GdkEventWindowState *event,
271 gpointer user_data)
273 eda_config_set_string (
274 eda_config_get_user_context (),
275 "gschem.window-geometry", "state",
276 (event->new_window_state &
277 GDK_WINDOW_STATE_FULLSCREEN) ? "fullscreen" :
278 (event->new_window_state &
279 GDK_WINDOW_STATE_MAXIMIZED) ? "maximized" : "normal");
281 return FALSE; /* propagate the event further */
284 static void
285 x_window_save_menu_geometry (GtkMenuShell *menu_shell,
286 GschemToplevel *w_current)
288 for (GList *l = gtk_container_get_children (GTK_CONTAINER (menu_shell));
289 l != NULL; l = l->next) {
290 GtkMenuItem *menu_item = GTK_MENU_ITEM (l->data);
292 GtkWidget *menu = menu_item->submenu;
293 if (menu == NULL)
294 /* not a submenu */
295 continue;
297 char *settings_name = g_object_get_data (G_OBJECT (menu), "settings-name");
298 if (settings_name == NULL)
299 /* menu doesn't have a settings name set */
300 continue;
302 gint coords[4];
303 gsize length = 0;
305 if (GTK_MENU (menu)->torn_off) {
306 GtkWidget *window = GTK_MENU (menu)->tearoff_window;
307 g_return_if_fail (window != NULL);
309 gtk_window_get_position (GTK_WINDOW (window), &coords[0], &coords[1]);
310 gtk_window_get_size (GTK_WINDOW (window), &coords[2], &coords[3]);
311 length = 4;
314 eda_config_set_int_list (
315 eda_config_get_user_context (),
316 "gschem.menu-geometry", settings_name, coords, length);
318 x_window_save_menu_geometry (GTK_MENU_SHELL (menu), w_current);
322 static void
323 x_window_save_geometry (GschemToplevel *w_current)
325 gchar *window_state;
326 GtkAllocation allocation;
328 /* save window geometry */
329 window_state = eda_config_get_string (eda_config_get_user_context (),
330 "gschem.window-geometry",
331 "state", NULL);
332 if (window_state != NULL && strcmp (window_state, "normal") == 0) {
333 gint width = -1, height = -1;
334 gtk_window_get_size (GTK_WINDOW (w_current->main_window), &width, &height);
335 if (width > 0 && height > 0) {
336 eda_config_set_int (eda_config_get_user_context (),
337 "gschem.window-geometry", "width", width);
338 eda_config_set_int (eda_config_get_user_context (),
339 "gschem.window-geometry", "height", height);
343 /* save torn-off menus */
344 if (w_current->menubar != NULL)
345 x_window_save_menu_geometry (
346 GTK_MENU_SHELL (w_current->menubar), w_current);
348 /* save dock area geometry */
349 gtk_widget_get_allocation (w_current->left_notebook, &allocation);
350 if (allocation.width > 0)
351 eda_config_set_int (eda_config_get_user_context (),
352 "gschem.dock-geometry.left",
353 "size", allocation.width);
355 gtk_widget_get_allocation (w_current->bottom_notebook, &allocation);
356 if (allocation.height > 0)
357 eda_config_set_int (eda_config_get_user_context (),
358 "gschem.dock-geometry.bottom",
359 "size", allocation.height);
361 gtk_widget_get_allocation (w_current->right_notebook, &allocation);
362 if (allocation.width > 0)
363 eda_config_set_int (eda_config_get_user_context (),
364 "gschem.dock-geometry.right",
365 "size", allocation.width);
368 static void
369 x_window_restore_menu_geometry (GtkMenuShell *menu_shell,
370 GschemToplevel *w_current)
372 for (GList *l = gtk_container_get_children (GTK_CONTAINER (menu_shell));
373 l != NULL; l = l->next) {
374 GtkMenuItem *menu_item = GTK_MENU_ITEM (l->data);
376 GtkWidget *menu = menu_item->submenu;
377 if (menu == NULL)
378 /* not a submenu */
379 continue;
381 char *settings_name = g_object_get_data (G_OBJECT (menu), "settings-name");
382 if (settings_name == NULL)
383 /* menu doesn't have a settings name set */
384 continue;
386 gsize length = 0;
387 gint *coords = eda_config_get_int_list (
388 eda_config_get_user_context (),
389 "gschem.menu-geometry", settings_name, &length, NULL);
391 if (coords != NULL && length == 4) {
392 gtk_menu_set_tearoff_state (GTK_MENU (menu), TRUE);
394 GtkWidget *window = GTK_MENU (menu)->tearoff_window;
395 g_return_if_fail (window != NULL);
397 gtk_window_move (GTK_WINDOW (window), coords[0], coords[1]);
398 gtk_window_resize (GTK_WINDOW (window), coords[2], coords[3]);
400 g_free(coords);
402 x_window_restore_menu_geometry (GTK_MENU_SHELL (menu), w_current);
406 static gboolean
407 x_window_restore_all_menu_geometry (GschemToplevel *w_current)
409 g_signal_handlers_disconnect_by_func(
410 G_OBJECT (w_current->main_window),
411 G_CALLBACK (x_window_restore_all_menu_geometry), w_current);
413 if (w_current->menubar != NULL)
414 x_window_restore_menu_geometry (
415 GTK_MENU_SHELL (w_current->menubar), w_current);
417 return FALSE;
420 static void
421 x_window_restore_geometry (GschemToplevel *w_current)
423 gint width, height, dock_size;
424 gchar *window_state;
426 /* restore main window size */
427 width = eda_config_get_int (eda_config_get_user_context (),
428 "gschem.window-geometry", "width", NULL);
429 height = eda_config_get_int (eda_config_get_user_context (),
430 "gschem.window-geometry", "height", NULL);
431 if (width <= 0 || height <= 0) {
432 width = 1200;
433 height = 900;
435 g_object_set (w_current->main_window,
436 "default-width", width,
437 "default-height", height,
438 NULL);
440 /* restore main window state */
441 window_state = eda_config_get_string (eda_config_get_user_context (),
442 "gschem.window-geometry",
443 "state", NULL);
444 if (window_state != NULL && strcmp (window_state, "fullscreen") == 0)
445 gtk_window_fullscreen (GTK_WINDOW (w_current->main_window));
446 else if (window_state != NULL && strcmp (window_state, "maximized") == 0)
447 gtk_window_maximize (GTK_WINDOW (w_current->main_window));
449 /* defer restoring torn-off menus until main window is shown */
450 g_signal_connect_swapped (
451 G_OBJECT (w_current->main_window), "focus-in-event",
452 G_CALLBACK (x_window_restore_all_menu_geometry), w_current);
454 /* restore docking area dimensions */
455 dock_size = eda_config_get_int (eda_config_get_user_context (),
456 "gschem.dock-geometry.left", "size", NULL);
457 if (dock_size <= 0)
458 dock_size = 300;
459 gtk_widget_set_size_request (w_current->left_notebook, dock_size, 0);
461 dock_size = eda_config_get_int (eda_config_get_user_context (),
462 "gschem.dock-geometry.bottom", "size", NULL);
463 if (dock_size <= 0)
464 dock_size = 150;
465 gtk_widget_set_size_request (w_current->bottom_notebook, 0, dock_size);
467 dock_size = eda_config_get_int (eda_config_get_user_context (),
468 "gschem.dock-geometry.right", "size", NULL);
469 if (dock_size <= 0)
470 dock_size = 300;
471 gtk_widget_set_size_request (w_current->right_notebook, dock_size, 0);
475 /*! \todo Finish function documentation!!!
476 * \brief
477 * \par Function Description
479 * \note
480 * When invoked (via signal delete_event), closes the current window
481 * if this is the last window, quit gschem
482 * used when you click the close button on the window which sends a DELETE
483 * signal to the app
485 static gboolean
486 x_window_close_wm (GtkWidget *widget, GdkEvent *event, gpointer data)
488 GschemToplevel *w_current = GSCHEM_TOPLEVEL (data);
489 g_return_val_if_fail ((w_current != NULL), TRUE);
491 x_window_close(w_current);
493 /* stop further propagation of the delete_event signal for window: */
494 /* - if user has cancelled the close the window should obvioulsy */
495 /* not be destroyed */
496 /* - otherwise window has already been destroyed, nothing more to */
497 /* do */
498 return TRUE;
502 void
503 x_window_update_file_change_notification (GschemToplevel *w_current,
504 PAGE *page)
506 if (page->is_untitled) {
507 g_object_set (w_current->file_change_notification,
508 "gschem-page", page,
509 "path", NULL,
510 NULL);
511 return;
514 gchar *basename = g_path_get_basename (page->page_filename);
515 gchar *markup = page->exists_on_disk
516 ? g_markup_printf_escaped (
517 _("<b>The file \"%s\" has changed on disk.</b>\n\n%s"),
518 basename,
519 page->CHANGED
520 ? _("Do you want to drop your changes and reload the file?")
521 : _("Do you want to reload it?"))
522 : g_markup_printf_escaped (
523 _("<b>The file \"%s\" has been created on disk.</b>\n\n%s"),
524 basename,
525 page->CHANGED
526 ? _("Do you want to drop your changes and load the file?")
527 : _("Do you want to open it?"));
528 g_object_set (w_current->file_change_notification,
529 "gschem-page", page,
530 "path", page->page_filename,
531 "has-known-mtime", page->exists_on_disk,
532 "known-mtime", &page->last_modified,
533 "button-stock-id", page->CHANGED
534 ? GTK_STOCK_REVERT_TO_SAVED
535 : page->exists_on_disk ? GTK_STOCK_REFRESH
536 : GTK_STOCK_OPEN,
537 "button-label", page->CHANGED
538 ? _("_Revert")
539 : page->exists_on_disk ? _("_Reload")
540 : _("_Open"),
541 "markup", markup,
542 NULL);
543 g_free (markup);
544 g_free (basename);
547 static void
548 x_window_file_change_response (GschemChangeNotification *chnot,
549 gint response_id, gpointer user_data)
551 if (response_id == GTK_RESPONSE_ACCEPT)
552 x_lowlevel_revert_page (chnot->w_current, chnot->page);
553 else {
554 chnot->page->exists_on_disk = chnot->has_current_mtime;
555 chnot->page->last_modified = chnot->current_mtime;
556 x_window_update_file_change_notification (chnot->w_current, chnot->page);
561 void
562 x_window_update_patch_change_notification (GschemToplevel *w_current,
563 PAGE *page)
565 gchar *patch_filename = x_patch_guess_filename (page);
566 gchar *basename, *markup;
568 if (patch_filename == NULL) {
569 basename = NULL;
570 markup = NULL;
571 } else {
572 struct stat buf;
573 basename = g_path_get_basename (patch_filename);
574 if (page->patch_filename != NULL)
575 markup = g_markup_printf_escaped (
576 _("<b>The back-annotation patch \"%s\" has been updated.</b>\n\n"
577 "Do you want to re-import it?"),
578 basename);
579 else if (page->patch_seen_on_disk)
580 markup = g_markup_printf_escaped (
581 _("<b>The back-annotation patch \"%s\" has been updated.</b>\n\n"
582 "Do you want to import it?"),
583 basename);
584 else if (stat (patch_filename, &buf) != -1)
585 markup = g_markup_printf_escaped (
586 _("<b>This file appears to have a back-annotation patch \"%s\" "
587 "associated with it.</b>\n\nDo you want to import it?"),
588 basename);
589 else
590 markup = g_markup_printf_escaped (
591 _("<b>A back-annotation patch \"%s\" has been created.</b>\n\n"
592 "Do you want to import it?"),
593 basename);
596 g_object_set (w_current->patch_change_notification,
597 "gschem-page", page,
598 "path", patch_filename,
599 "has-known-mtime", page->patch_seen_on_disk,
600 "known-mtime", &page->patch_mtime,
601 "button-label", _("_Import"),
602 "markup", markup,
603 NULL);
604 g_free (markup);
605 g_free (basename);
606 g_free (patch_filename);
609 static void
610 x_window_patch_change_response (GschemChangeNotification *chnot,
611 gint response_id, gpointer user_data)
613 if (response_id == GTK_RESPONSE_ACCEPT) {
614 if (chnot->page->patch_filename == NULL)
615 chnot->page->patch_filename = g_strdup (chnot->path);
616 x_patch_do_import (chnot->w_current, chnot->page);
617 } else {
618 chnot->page->patch_seen_on_disk = chnot->has_current_mtime;
619 chnot->page->patch_mtime = chnot->current_mtime;
620 x_window_update_patch_change_notification (chnot->w_current, chnot->page);
625 /*! \todo Finish function documentation!!!
626 * \brief
627 * \par Function Description
630 void x_window_create_main(GschemToplevel *w_current)
632 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
634 GtkPolicyType policy;
635 GtkWidget *main_box=NULL;
636 GtkWidget *handlebox=NULL;
637 GtkWidget *scrolled;
638 GtkAdjustment *hadjustment;
639 GtkAdjustment *vadjustment;
640 char *right_button_text;
641 GtkWidget *left_hpaned, *right_hpaned;
642 GtkWidget *vpaned;
643 GtkWidget *work_box;
645 w_current->main_window = GTK_WIDGET (gschem_main_window_new ());
647 gtk_widget_set_name (w_current->main_window, "gschem");
648 gtk_window_set_policy (GTK_WINDOW (w_current->main_window), TRUE, TRUE, TRUE);
650 /* We want the widgets to flow around the drawing area, so we don't
651 * set a size of the main window. The drawing area's size is fixed,
652 * see below
656 * normally we let the window manager handle locating and sizing
657 * the window. However, for some batch processing of schematics
658 * (generating a pdf of all schematics for example) we want to
659 * override this. Hence "auto_place_mode".
661 if( auto_place_mode )
662 gtk_widget_set_uposition (w_current->main_window, 10, 10);
664 /* this should work fine */
665 g_signal_connect (G_OBJECT (w_current->main_window), "delete_event",
666 G_CALLBACK (x_window_close_wm), w_current);
668 /* Containers first */
669 main_box = gtk_vbox_new(FALSE, 1);
670 gtk_container_set_border_width (GTK_CONTAINER (main_box), 0);
671 gtk_container_add(GTK_CONTAINER(w_current->main_window), main_box);
673 x_menus_create_main_menu (w_current);
674 if (w_current->menubar != NULL) {
675 if (w_current->handleboxes) {
676 handlebox = gtk_handle_box_new ();
677 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
678 gtk_container_add (GTK_CONTAINER (handlebox), w_current->menubar);
679 } else {
680 gtk_box_pack_start (GTK_BOX (main_box), w_current->menubar,
681 FALSE, FALSE, 0);
684 gschem_action_set_sensitive (action_view_menubar, w_current->menubar != NULL,
685 w_current);
686 gschem_action_set_active (action_view_menubar, w_current->menubar != NULL,
687 w_current);
689 gtk_widget_realize (w_current->main_window);
691 x_menus_create_toolbar (w_current);
692 if (w_current->toolbar != NULL) {
693 if (w_current->handleboxes) {
694 handlebox = gtk_handle_box_new ();
695 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
696 gtk_container_add (GTK_CONTAINER (handlebox), w_current->toolbar);
697 gtk_widget_set_visible (handlebox, w_current->toolbars);
698 gtk_widget_set_no_show_all (handlebox, TRUE);
699 } else {
700 gtk_box_pack_start (GTK_BOX (main_box), w_current->toolbar,
701 FALSE, FALSE, 0);
702 gtk_widget_set_visible (w_current->toolbar, w_current->toolbars);
703 gtk_widget_set_no_show_all (w_current->toolbar, TRUE);
706 gschem_action_set_sensitive (action_view_toolbar, w_current->toolbar != NULL,
707 w_current);
708 gschem_action_set_active (action_view_toolbar,
709 w_current->toolbars && w_current->toolbar != NULL,
710 w_current);
712 left_hpaned = gtk_hpaned_new ();
713 gtk_container_add (GTK_CONTAINER(main_box), left_hpaned);
715 w_current->left_notebook = gtk_notebook_new ();
716 gtk_paned_pack1 (GTK_PANED (left_hpaned),
717 w_current->left_notebook,
718 FALSE,
719 TRUE);
720 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->left_notebook),
721 "gschem-dock");
723 right_hpaned = gtk_hpaned_new ();
724 gtk_paned_pack2 (GTK_PANED (left_hpaned),
725 right_hpaned,
726 TRUE,
727 TRUE);
729 w_current->right_notebook = gtk_notebook_new ();
730 gtk_paned_pack2 (GTK_PANED (right_hpaned),
731 w_current->right_notebook,
732 FALSE,
733 TRUE);
734 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->right_notebook),
735 "gschem-dock");
737 vpaned = gtk_vpaned_new ();
738 gtk_paned_pack1 (GTK_PANED (right_hpaned),
739 vpaned,
740 TRUE,
741 TRUE);
743 w_current->bottom_notebook = gtk_notebook_new ();
744 gtk_paned_pack2 (GTK_PANED (vpaned),
745 w_current->bottom_notebook,
746 FALSE,
747 TRUE);
748 gtk_notebook_set_group_name (GTK_NOTEBOOK (w_current->bottom_notebook),
749 "gschem-dock");
751 work_box = gtk_vbox_new (FALSE, 0);
752 gtk_paned_pack1 (GTK_PANED (vpaned),
753 work_box,
754 TRUE,
755 TRUE);
757 w_current->file_change_notification =
758 g_object_new (GSCHEM_TYPE_CHANGE_NOTIFICATION,
759 "gschem-toplevel", w_current,
760 "message-type", GTK_MESSAGE_QUESTION,
761 NULL);
762 g_signal_connect (w_current->file_change_notification, "response",
763 G_CALLBACK (x_window_file_change_response), NULL);
764 gtk_box_pack_start (GTK_BOX (work_box),
765 w_current->file_change_notification->info_bar,
766 FALSE, FALSE, 0);
768 w_current->patch_change_notification =
769 g_object_new (GSCHEM_TYPE_CHANGE_NOTIFICATION,
770 "gschem-toplevel", w_current,
771 "message-type", GTK_MESSAGE_INFO,
772 NULL);
773 g_signal_connect (w_current->patch_change_notification, "response",
774 G_CALLBACK (x_window_patch_change_response), NULL);
775 gtk_box_pack_start (GTK_BOX (work_box),
776 w_current->patch_change_notification->info_bar,
777 FALSE, FALSE, 0);
779 /* Try to create popup menu (appears in right mouse button */
780 x_menus_create_main_popup (w_current);
783 /* Setup a GtkScrolledWindow for the drawing area */
784 hadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
785 toplevel->init_left,
786 toplevel->init_right,
787 100.0,
788 100.0,
789 10.0));
791 vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (toplevel->init_bottom,
792 0.0,
793 toplevel->init_bottom - toplevel->init_top,
794 100.0,
795 100.0,
796 10.0));
798 scrolled = gtk_scrolled_window_new (hadjustment, vadjustment);
799 gtk_container_add (GTK_CONTAINER (work_box), scrolled);
800 x_window_create_drawing(scrolled, w_current);
801 x_window_setup_draw_events(w_current);
803 policy = (w_current->scrollbars_flag) ? GTK_POLICY_ALWAYS : GTK_POLICY_NEVER;
804 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), policy, policy);
805 gschem_action_set_active (action_view_scrollbars, w_current->scrollbars_flag,
806 w_current);
808 /* find text box */
809 w_current->find_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_FIND_TEXT_WIDGET, NULL));
811 gtk_box_pack_start (GTK_BOX (work_box),
812 w_current->find_text_widget,
813 FALSE,
814 FALSE,
817 g_signal_connect (w_current->find_text_widget,
818 "response",
819 G_CALLBACK (&x_window_find_text),
820 w_current);
822 /* hide text box */
823 w_current->hide_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
824 "button-text", pgettext ("actuate", "Hide"),
825 "label-text", _("Hide text starting with:"),
826 NULL));
828 gtk_box_pack_start (GTK_BOX (work_box),
829 w_current->hide_text_widget,
830 FALSE,
831 FALSE,
834 g_signal_connect (w_current->hide_text_widget,
835 "response",
836 G_CALLBACK (&x_window_hide_text),
837 w_current);
839 /* show text box */
840 w_current->show_text_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
841 "button-text", pgettext ("actuate", "Show"),
842 "label-text", _("Show text starting with:"),
843 NULL));
845 gtk_box_pack_start (GTK_BOX (work_box),
846 w_current->show_text_widget,
847 FALSE,
848 FALSE,
851 g_signal_connect (w_current->show_text_widget,
852 "response",
853 G_CALLBACK (&x_window_show_text),
854 w_current);
856 /* macro box */
857 w_current->macro_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_MACRO_WIDGET, NULL));
859 gtk_box_pack_start (GTK_BOX (work_box),
860 w_current->macro_widget,
861 FALSE,
862 FALSE,
865 g_signal_connect (w_current->macro_widget,
866 "response",
867 G_CALLBACK (&x_window_invoke_macro),
868 w_current);
871 w_current->compselect_dockable = g_object_new (
872 GSCHEM_TYPE_COMPSELECT_DOCKABLE,
873 "title", _("Library"),
874 "settings-name", "compselect",
875 "cancellable", TRUE,
876 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
877 "initial-width", 500,
878 "initial-height", 600,
879 "gschem-toplevel", w_current,
880 NULL);
882 w_current->object_properties_dockable = g_object_new (
883 GSCHEM_TYPE_OBJECT_PROPERTIES_DOCKABLE,
884 "title", _("Object"),
885 "settings-name", "object-properties", /* line-type */
886 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
887 "initial-width", 400,
888 "initial-height", 600,
889 "gschem-toplevel", w_current,
890 NULL);
892 w_current->text_properties_dockable = g_object_new (
893 GSCHEM_TYPE_TEXT_PROPERTIES_DOCKABLE,
894 "title", _("Text"),
895 "settings-name", "text-edit",
896 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
897 "initial-width", 400,
898 "initial-height", 450,
899 "gschem-toplevel", w_current,
900 NULL);
902 w_current->multiattrib_dockable = g_object_new (
903 GSCHEM_TYPE_MULTIATTRIB_DOCKABLE,
904 "title", _("Attributes"),
905 "settings-name", "multiattrib",
906 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_RIGHT,
907 "initial-width", 450,
908 "initial-height", 450,
909 "gschem-toplevel", w_current,
910 NULL);
912 w_current->options_dockable = g_object_new (
913 GSCHEM_TYPE_OPTIONS_DOCKABLE,
914 "title", _("Options"),
915 "settings-name", "options", /* snap-size */
916 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
917 "initial-width", 320,
918 "initial-height", 350,
919 "gschem-toplevel", w_current,
920 NULL);
922 w_current->log_dockable = g_object_new (
923 GSCHEM_TYPE_LOG_DOCKABLE,
924 "title", _("Status"),
925 "settings-name", "log",
926 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM,
927 "initial-width", 640,
928 "initial-height", 480,
929 "gschem-toplevel", w_current,
930 NULL);
932 w_current->find_text_dockable = g_object_new (
933 GSCHEM_TYPE_FIND_TEXT_DOCKABLE,
934 "title", _("Search results"),
935 "settings-name", "find-text",
936 "initial-state", GSCHEM_DOCKABLE_STATE_DOCKED_BOTTOM,
937 "initial-width", 500,
938 "initial-height", 300,
939 "gschem-toplevel", w_current,
940 NULL);
941 g_signal_connect (w_current->find_text_dockable,
942 "select-object",
943 G_CALLBACK (&x_window_select_text),
944 w_current);
946 w_current->patch_dockable = g_object_new (
947 GSCHEM_TYPE_PATCH_DOCKABLE,
948 "title", _("Patch"),
949 "settings-name", "patch",
950 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
951 "initial-width", 500,
952 "initial-height", 300,
953 "gschem-toplevel", w_current,
954 NULL);
956 w_current->pagesel_dockable = g_object_new (
957 GSCHEM_TYPE_PAGESEL_DOCKABLE,
958 "title", _("Pages"),
959 "settings-name", "pagesel",
960 "initial-state", GSCHEM_DOCKABLE_STATE_HIDDEN,
961 "initial-width", 515,
962 "initial-height", 180,
963 "gschem-toplevel", w_current,
964 NULL);
966 gschem_dockable_initialize_toplevel (w_current);
969 /* bottom box */
970 if (default_third_button == POPUP_ENABLED) {
971 right_button_text = _("Menu/Cancel");
972 } else {
973 right_button_text = _("Pan/Cancel");
976 w_current->bottom_widget = GTK_WIDGET (g_object_new (GSCHEM_TYPE_BOTTOM_WIDGET,
977 "grid-mode", gschem_options_get_grid_mode (w_current->options),
978 "grid-size", gschem_options_get_snap_size (w_current->options), /* x_grid_query_drawn_spacing (w_current), -- occurs before the page is set */
979 "left-button-text", _("Pick"),
980 "middle-button-text", _("none"),
981 "right-button-text", right_button_text,
982 "snap-mode", gschem_options_get_snap_mode (w_current->options),
983 "snap-size", gschem_options_get_snap_size (w_current->options),
984 "status-text", _("Select Mode"),
985 NULL));
987 i_update_middle_button (w_current, NULL, NULL);
989 gtk_box_pack_start (GTK_BOX (main_box), w_current->bottom_widget, FALSE, FALSE, 0);
991 x_window_restore_geometry (w_current);
992 g_signal_connect (G_OBJECT (w_current->main_window), "window-state-event",
993 G_CALLBACK (x_window_state_event), w_current);
995 gtk_widget_show_all (w_current->main_window);
997 /* hide unused notebooks */
998 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->left_notebook)) == 0)
999 gtk_widget_hide (w_current->left_notebook);
1000 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->bottom_notebook)) == 0)
1001 gtk_widget_hide (w_current->bottom_notebook);
1002 if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (w_current->right_notebook)) == 0)
1003 gtk_widget_hide (w_current->right_notebook);
1006 /*! \todo Finish function documentation!!!
1007 * \brief
1008 * \par Function Description
1011 void x_window_close(GschemToplevel *w_current)
1013 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
1014 gboolean last_window = FALSE;
1016 /* If we're closing whilst inside an action, re-wind the
1017 * page contents back to their state before we started */
1018 if (w_current->inside_action) {
1019 i_cancel (w_current);
1022 /* last chance to save possible unsaved pages */
1023 if (!x_dialog_close_window (w_current)) {
1024 /* user somehow cancelled the close */
1025 return;
1028 x_clipboard_finish (w_current);
1030 #if DEBUG
1031 o_conn_print_hash(w_current->page_current->conn_table);
1032 #endif
1034 w_current->dont_invalidate = TRUE;
1036 /* save window geometry */
1037 x_window_save_geometry (w_current);
1039 /* close all the dialog boxes */
1040 if (w_current->sowindow)
1041 gtk_widget_destroy(w_current->sowindow);
1043 if (w_current->tiwindow)
1044 gtk_widget_destroy(w_current->tiwindow);
1046 if (w_current->aawindow)
1047 gtk_widget_destroy(w_current->aawindow);
1049 if (w_current->aewindow)
1050 gtk_widget_destroy(w_current->aewindow);
1052 if (w_current->hkwindow)
1053 gtk_widget_destroy(w_current->hkwindow);
1055 if (w_current->cowindow)
1056 gtk_widget_destroy(w_current->cowindow);
1058 if (w_current->sewindow)
1059 gtk_widget_destroy(w_current->sewindow);
1061 /* save dock window geometry, close dock windows, disconnect signals */
1062 gschem_dockable_cleanup_toplevel (w_current);
1064 g_clear_object (&w_current->compselect_dockable);
1065 g_clear_object (&w_current->object_properties_dockable);
1066 g_clear_object (&w_current->text_properties_dockable);
1067 g_clear_object (&w_current->multiattrib_dockable);
1068 g_clear_object (&w_current->options_dockable);
1069 g_clear_object (&w_current->log_dockable);
1070 g_clear_object (&w_current->find_text_dockable);
1071 g_clear_object (&w_current->patch_dockable);
1072 g_clear_object (&w_current->pagesel_dockable);
1074 if (g_list_length (global_window_list) == 1) {
1075 /* no more window after this one, remember to quit */
1076 last_window = TRUE;
1079 if (toplevel->major_changed_refdes) {
1080 GList* current = toplevel->major_changed_refdes;
1081 while (current)
1083 /* printf("yeah freeing: %s\n", (char*) current->data); */
1084 g_free(current->data);
1085 current = g_list_next(current);
1087 g_list_free(toplevel->major_changed_refdes);
1090 /* stuff that has to be done before we free w_current */
1091 if (last_window) {
1092 /* close the log file */
1093 s_log_close ();
1094 /* free the buffers */
1095 o_buffer_free (w_current);
1098 /* Clear Guile smob weak ref */
1099 if (w_current->smob != SCM_UNDEFINED) {
1100 SCM_SET_SMOB_DATA (w_current->smob, NULL);
1101 w_current->smob = SCM_UNDEFINED;
1104 /* finally close the main window */
1105 gtk_widget_destroy(w_current->main_window);
1107 global_window_list = g_list_remove (global_window_list, w_current);
1108 gschem_toplevel_free (w_current);
1110 /* just closed last window, so quit */
1111 if (last_window) {
1112 gschem_quit();
1116 /*! \todo Finish function documentation!!!
1117 * \brief
1118 * \par Function Description
1121 void x_window_close_all(GschemToplevel *w_current)
1123 GschemToplevel *current;
1124 GList *list_copy, *iter;
1126 iter = list_copy = g_list_copy (global_window_list);
1127 while (iter != NULL ) {
1128 current = (GschemToplevel *)iter->data;
1129 iter = g_list_next (iter);
1130 x_window_close (current);
1132 g_list_free (list_copy);
1135 /*! \brief Changes the current page.
1136 * \par Function Description
1137 * This function displays the specified page <B>page</B> in the
1138 * window attached to <B>toplevel</B>.
1140 * It changes the <B>toplevel</B>'s current page to <B>page</B>,
1141 * draws it and updates the user interface.
1143 * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>.
1145 * \param [in] w_current The toplevel environment.
1146 * \param [in] page The page to become current page.
1148 void
1149 x_window_set_current_page (GschemToplevel *w_current, PAGE *page)
1151 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
1152 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
1153 GList *iter;
1155 g_return_if_fail (page_view != NULL);
1156 g_return_if_fail (toplevel != NULL);
1157 g_return_if_fail (page != NULL);
1159 g_warn_if_fail (page_view->page == toplevel->page_current ||
1160 page_view->page == NULL);
1162 if (page == toplevel->page_current && page_view->page != NULL)
1163 /* nothing to do */
1164 return;
1166 o_redraw_cleanstates (w_current);
1168 gschem_page_view_set_page (page_view, page);
1170 gschem_action_set_sensitive (action_page_revert,
1171 page->is_untitled == FALSE &&
1172 g_file_test (page->page_filename,
1173 G_FILE_TEST_EXISTS |
1174 G_FILE_TEST_IS_REGULAR),
1175 w_current);
1177 o_undo_update_actions (w_current, page);
1179 iter = g_list_find (geda_list_get_glist (toplevel->pages), page);
1180 gschem_action_set_sensitive (action_page_prev,
1181 w_current->enforce_hierarchy
1182 ? s_hierarchy_find_prev_page (
1183 toplevel->pages, page) != NULL
1184 : iter != NULL && iter->prev != NULL,
1185 w_current);
1186 gschem_action_set_sensitive (action_page_next,
1187 w_current->enforce_hierarchy
1188 ? s_hierarchy_find_next_page(
1189 toplevel->pages, page) != NULL
1190 : iter != NULL && iter->next != NULL,
1191 w_current);
1192 gschem_action_set_sensitive (action_hierarchy_up, page->up >= 0, w_current);
1194 i_update_menus (w_current);
1195 /* i_set_filename (w_current, page->page_filename); */
1197 x_window_update_file_change_notification (w_current, page);
1198 x_window_update_patch_change_notification (w_current, page);
1200 x_pagesel_update (w_current);
1201 x_multiattrib_update (w_current);
1204 /*! \brief Raise the main window to the front.
1206 * This is mostly equivalent to
1207 * gtk_window_present (GTK_WINDOW (w_current->main_window));
1209 * One of the two actions that \c gtk_window_present performs on an
1210 * already-visible window (\c gdk_window_show) is triggering a bug
1211 * with toolbar icon drawing, the other (\c gdk_window_focus) is the
1212 * one we actually want. In order to work around that bug, just call
1213 * \c gdk_window_focus directly.
1215 * \param [in] w_current the toplevel environment
1217 void x_window_present (GschemToplevel *w_current)
1219 //gdk_window_show (w_current->main_window->window); /* the culprit */
1221 #ifdef GDK_WINDOWING_X11
1222 GdkDisplay *display = gtk_widget_get_display (w_current->main_window);
1223 guint32 timestamp = gdk_x11_display_get_user_time (display);
1224 #else
1225 guint32 timestamp = gtk_get_current_event_time ();
1226 #endif
1228 gdk_window_focus (w_current->main_window->window, timestamp);
1231 /*! \brief Setup default icon for GTK windows
1233 * \par Function Description
1234 * Sets the default window icon by name, to be found in the current icon
1235 * theme. The name used is \#defined above as GSCHEM_THEME_ICON_NAME.
1237 void x_window_set_default_icon( void )
1239 gtk_window_set_default_icon_name( GSCHEM_THEME_ICON_NAME );
1242 /*! \brief Setup icon search paths.
1243 * \par Function Description
1244 * Add the icons installed by gschem to the search path for the
1245 * default icon theme, so that they can be automatically found by GTK.
1247 void
1248 x_window_init_icons (void)
1250 gchar *icon_path;
1252 g_return_if_fail (s_path_sys_data () != NULL);
1254 icon_path = g_build_filename (s_path_sys_data (), "icons", NULL);
1255 gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
1256 icon_path);
1257 g_free (icon_path);
1260 /*! \brief Creates a new X window.
1262 * \par Function description
1264 * Creates and initializes new GschemToplevel object and then sets
1265 * and setups its libgeda \a toplevel.
1267 * \param toplevel The libgeda TOPLEVEL object.
1268 * \return Pointer to the new GschemToplevel object.
1270 GschemToplevel* x_window_new (TOPLEVEL *toplevel)
1272 GschemToplevel *w_current;
1274 w_current = gschem_toplevel_new ();
1275 gschem_toplevel_set_toplevel (w_current,
1276 (toplevel != NULL) ? toplevel : s_toplevel_new ());
1278 gschem_toplevel_get_toplevel (w_current)->load_newer_backup_func = x_fileselect_load_backup;
1279 gschem_toplevel_get_toplevel (w_current)->load_newer_backup_data = w_current;
1281 o_text_set_rendered_bounds_func (gschem_toplevel_get_toplevel (w_current),
1282 o_text_get_rendered_bounds, w_current);
1284 /* Damage notifications should invalidate the object on screen */
1285 o_add_change_notify (gschem_toplevel_get_toplevel (w_current),
1286 (ChangeNotifyFunc) o_invalidate,
1287 (ChangeNotifyFunc) o_invalidate, w_current);
1289 x_window_setup (w_current);
1291 return w_current;