gschem: Always set gschem state fluid & catch errors for hooks.
[geda-gaf/whiteaudio.git] / gschem / src / x_window.c
blob20c579c28304162af2184fe9b13c9f9ee43fe010
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>
21 #include <missing.h>
23 #include <stdio.h>
25 #include "gschem.h"
27 #ifdef HAVE_LIBDMALLOC
28 #include <dmalloc.h>
29 #endif
31 #define GSCHEM_THEME_ICON_NAME "geda-gschem"
33 /*! \todo Finish function documentation!!!
34 * \brief
35 * \par Function Description
38 void x_window_setup (GSCHEM_TOPLEVEL *w_current)
40 TOPLEVEL *toplevel = w_current->toplevel;
42 /* immediately setup user params */
43 i_vars_set(w_current);
45 /* Initialize the autosave callback */
46 s_page_autosave_init(toplevel);
48 /* Initialize the clipboard callback */
49 x_clipboard_init (w_current);
51 /* x_window_setup_world() - BEGIN */
52 toplevel->init_left = -45;
53 toplevel->init_top = -45;
54 /* init_right and _bottom are set before this function is called */
56 toplevel->width = default_width;
57 toplevel->height = default_height;
59 w_current->win_width = toplevel->width;
60 w_current->win_height = toplevel->height;
61 /* x_window_setup_world() - END */
63 /* Add to the list of windows */
64 global_window_list = g_list_append (global_window_list, w_current);
66 /* X related stuff */
67 x_window_create_main (w_current);
69 x_menu_attach_recent_files_submenu(w_current);
72 /*! \todo Finish function documentation!!!
73 * \brief
74 * \par Function Description
77 void x_window_setup_gc(GSCHEM_TOPLEVEL *w_current)
79 w_current->gc = gdk_gc_new(w_current->window);
81 if (w_current->gc == NULL) {
82 fprintf(stderr, _("Couldn't allocate gc\n"));
83 exit(-1);
87 /*! \todo Finish function documentation!!!
88 * \brief
89 * \par Function Description
92 void x_window_free_gc(GSCHEM_TOPLEVEL *w_current)
94 gdk_gc_unref(w_current->gc);
97 /*! \todo Finish function documentation!!!
98 * \brief
99 * \par Function Description
102 void x_window_create_drawing(GtkWidget *drawbox, GSCHEM_TOPLEVEL *w_current)
104 /* drawing next */
105 w_current->drawing_area = gtk_drawing_area_new ();
106 /* Set the size here. Be sure that it has an aspect ratio of 1.333
107 * We could calculate this based on root window size, but for now
108 * lets just set it to:
109 * Width = root_width*3/4 Height = Width/1.3333333333
110 * 1.3333333 is the desired aspect ratio!
113 gtk_drawing_area_size (GTK_DRAWING_AREA (w_current->drawing_area),
114 w_current->win_width,
115 w_current->win_height);
117 gtk_box_pack_start (GTK_BOX (drawbox), w_current->drawing_area,
118 TRUE, TRUE, 0);
119 GTK_WIDGET_SET_FLAGS (w_current->drawing_area, GTK_CAN_FOCUS );
120 gtk_widget_grab_focus (w_current->drawing_area);
121 gtk_widget_show (w_current->drawing_area);
125 /*! \todo Finish function documentation!!!
126 * \brief
127 * \par Function Description
130 void x_window_setup_draw_events(GSCHEM_TOPLEVEL *w_current)
132 struct event_reg_t {
133 gchar *detailed_signal;
134 GCallback c_handler;
137 struct event_reg_t drawing_area_events[] = {
138 { "expose_event", G_CALLBACK(x_event_expose) },
139 { "button_press_event", G_CALLBACK(x_event_button_pressed) },
140 { "button_release_event", G_CALLBACK(x_event_button_released) },
141 { "motion_notify_event", G_CALLBACK(x_event_motion) },
142 { "configure_event", G_CALLBACK(x_event_configure) },
143 { "key_press_event", G_CALLBACK(x_event_key) },
144 { "key_release_event", G_CALLBACK(x_event_key) },
145 { NULL, NULL } };
146 struct event_reg_t main_window_events[] = {
147 { "enter_notify_event", G_CALLBACK(x_event_enter) },
148 { "scroll_event", G_CALLBACK(x_event_scroll) },
149 { NULL, NULL } };
150 struct event_reg_t *tmp;
152 /* is the configure event type missing here? hack */
153 gtk_widget_set_events (w_current->drawing_area,
154 GDK_EXPOSURE_MASK |
155 GDK_POINTER_MOTION_MASK |
156 GDK_BUTTON_PRESS_MASK |
157 GDK_ENTER_NOTIFY_MASK |
158 GDK_KEY_PRESS_MASK |
159 GDK_BUTTON_RELEASE_MASK);
161 for (tmp = drawing_area_events; tmp->detailed_signal != NULL; tmp++) {
162 g_signal_connect (w_current->drawing_area,
163 tmp->detailed_signal,
164 tmp->c_handler,
165 w_current);
168 for (tmp = main_window_events; tmp->detailed_signal != NULL; tmp++) {
169 g_signal_connect (w_current->main_window,
170 tmp->detailed_signal,
171 tmp->c_handler,
172 w_current);
177 /*! \brief Creates a new GtkImage displaying a GTK stock icon if available.
179 * If a stock GTK icon with the requested name was not found, this function
180 * falls back to the bitmap icons provided in the distribution.
182 * \param stock Name of the stock icon ("new", "open", etc.)
183 * \param w_current Schematic top level
184 * \return Pointer to the new GtkImage object.
186 static GtkWidget *x_window_stock_pixmap(const char *stock, GSCHEM_TOPLEVEL *w_current)
188 GtkWidget *wpixmap = NULL;
189 GdkPixmap *pixmap;
190 GdkBitmap *mask;
191 GtkStockItem item;
193 GdkWindow *window=w_current->main_window->window;
194 GdkColor *background=&w_current->main_window->style->bg[GTK_STATE_NORMAL];
196 gchar *filename=g_strconcat(w_current->toplevel->bitmap_directory,
197 G_DIR_SEPARATOR_S,
198 "gschem-", stock, ".xpm", NULL);
200 gchar *stockid=g_strconcat("gtk-", stock, NULL);
202 /* First check if GTK knows this stock icon */
203 if(gtk_stock_lookup(stockid, &item)) {
204 wpixmap = gtk_image_new_from_stock(stockid,
205 GTK_ICON_SIZE_SMALL_TOOLBAR);
206 } else {
207 /* Fallback to the original custom icon */
208 pixmap = gdk_pixmap_create_from_xpm (window, &mask,
209 background, filename);
210 if (pixmap != NULL) {
211 wpixmap = gtk_image_new_from_pixmap (pixmap, mask);
212 } else {
213 s_log_message("Could not find image at file: %s.\n", filename);
214 wpixmap = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE ,
215 GTK_ICON_SIZE_SMALL_TOOLBAR);
219 g_free(filename);
220 g_free(stockid);
222 return wpixmap;
225 static void x_window_invoke_macro(GtkEntry *entry, void *userdata)
227 GSCHEM_TOPLEVEL *w_current = userdata;
228 SCM interpreter;
230 interpreter = scm_list_2(scm_from_utf8_symbol("invoke-macro"),
231 scm_from_utf8_string(gtk_entry_get_text(entry)));
233 scm_dynwind_begin (0);
234 g_dynwind_window (w_current);
235 g_scm_eval_protected(interpreter, SCM_UNDEFINED);
236 scm_dynwind_end ();
238 gtk_widget_hide(w_current->macro_box);
239 gtk_widget_grab_focus(w_current->drawing_area);
242 /*! \todo Finish function documentation!!!
243 * \brief
244 * \par Function Description
247 void x_window_create_main(GSCHEM_TOPLEVEL *w_current)
249 TOPLEVEL *toplevel = w_current->toplevel;
251 GtkWidget *label=NULL;
252 GtkWidget *main_box=NULL;
253 GtkWidget *menubar=NULL;
254 GtkWidget *drawbox=NULL;
255 GtkWidget *bottom_box=NULL;
256 GtkWidget *toolbar=NULL;
257 GtkWidget *handlebox=NULL;
259 /* used to signify that the window isn't mapped yet */
260 w_current->window = NULL;
262 w_current->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
264 gtk_widget_set_name (w_current->main_window, "gschem");
265 gtk_window_set_policy (GTK_WINDOW (w_current->main_window), TRUE, TRUE, TRUE);
267 /* We want the widgets to flow around the drawing area, so we don't
268 * set a size of the main window. The drawing area's size is fixed,
269 * see below
273 * normally we let the window manager handle locating and sizing
274 * the window. However, for some batch processing of schematics
275 * (generating a pdf of all schematics for example) we want to
276 * override this. Hence "auto_place_mode".
278 if( auto_place_mode )
279 gtk_widget_set_uposition (w_current->main_window, 10, 10);
281 /* this should work fine */
282 g_signal_connect (G_OBJECT (w_current->main_window), "delete_event",
283 G_CALLBACK (i_callback_close_wm),
284 w_current);
286 /* Containers first */
287 main_box = gtk_vbox_new(FALSE, 1);
288 gtk_container_border_width(GTK_CONTAINER(main_box), 0);
289 gtk_container_add(GTK_CONTAINER(w_current->main_window), main_box);
291 menubar = get_main_menu (w_current);
292 if (w_current->handleboxes) {
293 handlebox = gtk_handle_box_new ();
294 gtk_box_pack_start(GTK_BOX(main_box), handlebox, FALSE, FALSE, 0);
295 gtk_container_add (GTK_CONTAINER (handlebox), menubar);
296 } else {
297 gtk_box_pack_start(GTK_BOX(main_box), menubar, FALSE, FALSE, 0);
300 w_current->menubar = menubar;
301 gtk_widget_realize (w_current->main_window);
303 if (w_current->handleboxes && w_current->toolbars) {
304 handlebox = gtk_handle_box_new ();
305 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
308 if (w_current->toolbars) {
309 toolbar = gtk_toolbar_new();
310 gtk_toolbar_set_orientation (GTK_TOOLBAR(toolbar),
311 GTK_ORIENTATION_HORIZONTAL);
312 gtk_toolbar_set_style (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
314 if (w_current->handleboxes) {
315 gtk_container_add (GTK_CONTAINER (handlebox), toolbar);
316 } else {
317 gtk_box_pack_start(GTK_BOX(main_box), toolbar, FALSE, FALSE, 0);
320 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
321 _("New"),
322 _("New file"),
323 "toolbar/new",
324 x_window_stock_pixmap("new", w_current),
325 (GtkSignalFunc) i_callback_toolbar_file_new,
326 w_current);
327 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
328 _("Open"),
329 _("Open file..."),
330 "toolbar/open",
331 x_window_stock_pixmap("open", w_current),
332 (GtkSignalFunc) i_callback_toolbar_file_open,
333 w_current);
334 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
335 _("Save"),
336 _("Save file"),
337 "toolbar/save",
338 x_window_stock_pixmap("save", w_current),
339 (GtkSignalFunc) i_callback_toolbar_file_save,
340 w_current);
341 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
342 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
343 _("Undo"),
344 _("Undo last operation"),
345 "toolbar/undo",
346 x_window_stock_pixmap("undo", w_current),
347 (GtkSignalFunc) i_callback_toolbar_edit_undo,
348 w_current);
349 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
350 _("Redo"),
351 _("Redo last undo"),
352 "toolbar/redo",
353 x_window_stock_pixmap("redo", w_current),
354 (GtkSignalFunc) i_callback_toolbar_edit_redo,
355 w_current);
356 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
357 /* not part of any radio button group */
358 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
359 _("Component"),
360 _("Add component...\nSelect library and component from list, move the mouse into main window, click to place\nRight mouse button to cancel"),
361 "toolbar/component",
362 x_window_stock_pixmap("comp", w_current),
363 (GtkSignalFunc) i_callback_toolbar_add_component,
364 w_current);
365 w_current->toolbar_net =
366 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
367 GTK_TOOLBAR_CHILD_RADIOBUTTON,
368 NULL,
369 _("Nets"),
370 _("Add nets mode\nRight mouse button to cancel"),
371 "toolbar/nets",
372 x_window_stock_pixmap("net", w_current),
373 (GtkSignalFunc) i_callback_toolbar_add_net,
374 w_current);
375 w_current->toolbar_bus =
376 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
377 GTK_TOOLBAR_CHILD_RADIOBUTTON,
378 w_current->toolbar_net,
379 _("Bus"),
380 _("Add buses mode\nRight mouse button to cancel"),
381 "toolbar/bus",
382 x_window_stock_pixmap("bus", w_current),
383 (GtkSignalFunc) i_callback_toolbar_add_bus,
384 w_current);
385 /* not part of any radio button group */
386 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
387 _("Text"),
388 _("Add Text..."),
389 "toolbar/text",
390 x_window_stock_pixmap("text", w_current),
391 (GtkSignalFunc) i_callback_toolbar_add_text,
392 w_current);
393 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
394 w_current->toolbar_select =
395 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
396 GTK_TOOLBAR_CHILD_RADIOBUTTON,
397 w_current->toolbar_bus,
398 _("Select"),
399 _("Select mode"),
400 "toolbar/select",
401 x_window_stock_pixmap("select", w_current),
402 (GtkSignalFunc) i_callback_toolbar_edit_select,
403 w_current);
406 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
407 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_current->toolbar_select),
408 TRUE);
412 /* Try to create popup menu (appears in right mouse button */
413 w_current->popup_menu = (GtkWidget *) get_main_popup(w_current);
415 drawbox = gtk_hbox_new(FALSE, 0);
416 gtk_container_border_width(GTK_CONTAINER(drawbox), 0);
417 gtk_container_add(GTK_CONTAINER(main_box), drawbox);
419 x_window_create_drawing(drawbox, w_current);
420 x_window_setup_draw_events(w_current);
422 if (w_current->scrollbars_flag == TRUE) {
423 /* setup scroll bars */
424 w_current->v_adjustment = GTK_ADJUSTMENT (
425 gtk_adjustment_new (toplevel->init_bottom, 0.0, toplevel->init_bottom,
426 100.0, 100.0, 10.0));
428 w_current->v_scrollbar = gtk_vscrollbar_new (w_current->v_adjustment);
430 gtk_range_set_update_policy (GTK_RANGE (w_current->v_scrollbar),
431 GTK_UPDATE_CONTINUOUS);
433 gtk_box_pack_start (GTK_BOX (drawbox), w_current->v_scrollbar,
434 FALSE, FALSE, 0);
436 g_signal_connect (w_current->v_adjustment,
437 "value_changed",
438 G_CALLBACK (x_event_vschanged),
439 w_current);
441 w_current->h_adjustment = GTK_ADJUSTMENT (
442 gtk_adjustment_new (0.0, 0.0, toplevel->init_right, 100.0, 100.0, 10.0));
444 w_current->h_scrollbar = gtk_hscrollbar_new (w_current->h_adjustment);
446 gtk_range_set_update_policy (GTK_RANGE (w_current->h_scrollbar),
447 GTK_UPDATE_CONTINUOUS);
449 gtk_box_pack_start (GTK_BOX (main_box), w_current->h_scrollbar,
450 FALSE, FALSE, 0);
452 g_signal_connect (w_current->h_adjustment,
453 "value_changed",
454 G_CALLBACK (x_event_hschanged),
455 w_current);
458 /* macro box */
459 w_current->macro_entry = gtk_entry_new();
460 g_signal_connect(w_current->macro_entry, "activate",
461 G_CALLBACK(&x_window_invoke_macro), w_current);
463 w_current->macro_box = gtk_hbox_new(FALSE, 0);
464 gtk_box_pack_start(GTK_BOX (w_current->macro_box),
465 gtk_label_new (_("Evaluate:")), FALSE, FALSE, 2);
466 gtk_box_pack_start(GTK_BOX(w_current->macro_box), w_current->macro_entry,
467 TRUE, TRUE, 2);
468 gtk_container_border_width(GTK_CONTAINER(w_current->macro_box), 1);
469 gtk_box_pack_start (GTK_BOX (main_box), w_current->macro_box,
470 FALSE, FALSE, 0);
472 /* bottom box */
473 bottom_box = gtk_hbox_new(FALSE, 0);
474 gtk_container_border_width(GTK_CONTAINER(bottom_box), 1);
475 gtk_box_pack_start (GTK_BOX (main_box), bottom_box, FALSE, FALSE, 0);
477 /* label = gtk_label_new ("Mouse buttons:");
478 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 10);
481 label = gtk_label_new (" ");
482 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 2);
484 w_current->left_label = gtk_label_new (_("Pick"));
485 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->left_label,
486 FALSE, FALSE, 0);
488 label = gtk_label_new ("|");
489 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
491 if (w_current->middle_button == STROKE) {
492 #ifdef HAVE_LIBSTROKE
493 w_current->middle_label = gtk_label_new (_("Stroke"));
494 #else
495 w_current->middle_label = gtk_label_new (_("none"));
496 #endif
497 } else if (w_current->middle_button == ACTION) {
498 w_current->middle_label = gtk_label_new (_("Action"));
499 } else {
500 w_current->middle_label = gtk_label_new (_("Repeat/none"));
503 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->middle_label,
504 FALSE, FALSE, 0);
506 label = gtk_label_new ("|");
507 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
509 if (default_third_button == POPUP_ENABLED) {
510 w_current->right_label = gtk_label_new (_("Menu/Cancel"));
511 } else {
512 w_current->right_label = gtk_label_new (_("Pan/Cancel"));
514 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->right_label,
515 FALSE, FALSE, 0);
517 label = gtk_label_new (" ");
518 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
520 w_current->grid_label = gtk_label_new (" ");
521 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->grid_label,
522 FALSE, FALSE, 10);
524 w_current->status_label = gtk_label_new (_("Select Mode"));
525 gtk_box_pack_end (GTK_BOX (bottom_box), w_current->status_label, FALSE,
526 FALSE, 10);
528 gtk_widget_show_all (w_current->main_window);
529 gtk_widget_hide(w_current->macro_box);
531 w_current->window = w_current->drawing_area->window;
533 w_current->drawable = w_current->window;
535 x_window_setup_gc(w_current);
538 /*! \todo Finish function documentation!!!
539 * \brief
540 * \par Function Description
543 void x_window_close(GSCHEM_TOPLEVEL *w_current)
545 TOPLEVEL *toplevel = w_current->toplevel;
546 gboolean last_window = FALSE;
548 /* If we're closing whilst inside a move action, re-wind the
549 * page contents back to their state before we started */
550 if (w_current->inside_action &&
551 (w_current->event_state == MOVE ||
552 w_current->event_state == ENDMOVE)) {
553 o_move_cancel (w_current);
556 /* last chance to save possible unsaved pages */
557 if (!x_dialog_close_window (w_current)) {
558 /* user somehow cancelled the close */
559 return;
562 x_clipboard_finish (w_current);
564 #if DEBUG
565 o_conn_print_hash(w_current->page_current->conn_table);
566 #endif
568 /* close all the dialog boxes */
569 if (w_current->sowindow)
570 gtk_widget_destroy(w_current->sowindow);
572 if (w_current->cswindow)
573 gtk_widget_destroy(w_current->cswindow);
575 if (w_current->tiwindow)
576 gtk_widget_destroy(w_current->tiwindow);
578 if (w_current->tewindow)
579 gtk_widget_destroy(w_current->tewindow);
581 if (w_current->aawindow)
582 gtk_widget_destroy(w_current->aawindow);
584 x_multiattrib_close (w_current);
586 if (w_current->aewindow)
587 gtk_widget_destroy(w_current->aewindow);
589 if (w_current->trwindow)
590 gtk_widget_destroy(w_current->trwindow);
592 x_pagesel_close (w_current);
594 if (w_current->tswindow)
595 gtk_widget_destroy(w_current->tswindow);
597 if (w_current->iwindow)
598 gtk_widget_destroy(w_current->iwindow);
600 if (w_current->hkwindow)
601 gtk_widget_destroy(w_current->hkwindow);
603 if (w_current->cowindow)
604 gtk_widget_destroy(w_current->cowindow);
606 if (w_current->clwindow)
607 gtk_widget_destroy(w_current->clwindow);
609 if (w_current->sewindow)
610 gtk_widget_destroy(w_current->sewindow);
612 if (g_list_length (global_window_list) == 1) {
613 /* no more window after this one, remember to quit */
614 last_window = TRUE;
617 if (toplevel->major_changed_refdes) {
618 GList* current = toplevel->major_changed_refdes;
619 while (current)
621 /* printf("yeah freeing: %s\n", (char*) current->data); */
622 g_free(current->data);
623 current = g_list_next(current);
625 g_list_free(toplevel->major_changed_refdes);
628 /* stuff that has to be done before we free w_current */
629 if (last_window) {
630 /* close the log file */
631 s_log_close ();
632 /* free the buffers */
633 o_buffer_free (w_current);
636 x_window_free_gc(w_current);
638 /* Clear Guile smob weak ref */
639 if (w_current->smob != SCM_UNDEFINED) {
640 SCM_SET_SMOB_DATA (w_current->smob, NULL);
641 w_current->smob = SCM_UNDEFINED;
644 /* finally close the main window */
645 gtk_widget_destroy(w_current->main_window);
647 s_toplevel_delete (toplevel);
648 global_window_list = g_list_remove (global_window_list, w_current);
649 g_free (w_current);
651 /* just closed last window, so quit */
652 if (last_window) {
653 gschem_quit();
657 /*! \todo Finish function documentation!!!
658 * \brief
659 * \par Function Description
662 void x_window_close_all(GSCHEM_TOPLEVEL *w_current)
664 GSCHEM_TOPLEVEL *current;
665 GList *list_copy, *iter;
667 iter = list_copy = g_list_copy (global_window_list);
668 while (iter != NULL ) {
669 current = (GSCHEM_TOPLEVEL *)iter->data;
670 iter = g_list_next (iter);
671 x_window_close (current);
673 g_list_free (list_copy);
676 /*! \brief Opens a new page from a file.
677 * \par Function Description
678 * This function opens the file whose name is <B>filename</B> in a
679 * new PAGE of <B>toplevel</B>.
681 * If there is no page for <B>filename</B> in <B>toplevel</B>'s list
682 * of pages, it creates a new PAGE, loads the file in it and returns
683 * a pointer on the new page. Otherwise it returns a pointer on the
684 * existing page.
686 * If the filename passed is NULL, this function creates an empty,
687 * untitled page. The name of the untitled page is build from
688 * configuration data ('untitled-name') and a counter for uniqueness.
690 * The opened page becomes the current page of <B>toplevel</B>.
692 * \param [in] w_current The toplevel environment.
693 * \param [in] filename The name of the file to open or NULL for a blank page.
694 * \returns A pointer on the new page.
696 * \bug This code should check to make sure any untitled filename
697 * does not conflict with a file on disk.
699 PAGE*
700 x_window_open_page (GSCHEM_TOPLEVEL *w_current, const gchar *filename)
702 TOPLEVEL *toplevel = w_current->toplevel;
703 PAGE *old_current, *page;
704 gchar *fn;
706 g_return_val_if_fail (toplevel != NULL, NULL);
708 /* Generate untitled filename if none was specified */
709 if (filename == NULL) {
710 gchar *cwd, *tmp;
711 cwd = g_get_current_dir ();
712 tmp = g_strdup_printf ("%s_%d.sch",
713 toplevel->untitled_name,
714 ++w_current->num_untitled);
715 fn = g_build_filename (cwd, tmp, NULL);
716 g_free(cwd);
717 g_free(tmp);
718 } else {
719 fn = g_strdup (filename);
722 /* Return existing page if it is already loaded */
723 page = s_page_search (toplevel, fn);
724 if ( page != NULL ) {
725 g_free(fn);
726 return page;
729 old_current = toplevel->page_current;
730 page = s_page_new (toplevel, fn);
731 s_page_goto (toplevel, page);
733 /* Load from file if necessary, otherwise just print a message */
734 if (filename != NULL) {
735 GError *err = NULL;
736 if (!quiet_mode)
737 s_log_message (_("Loading schematic [%s]\n"), fn);
739 if (!f_open (toplevel, page, (gchar *) fn, &err)) {
740 GtkWidget *dialog;
742 g_warning ("%s\n", err->message);
743 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
744 GTK_DIALOG_DESTROY_WITH_PARENT,
745 GTK_MESSAGE_ERROR,
746 GTK_BUTTONS_CLOSE,
747 "%s",
748 err->message);
749 gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to load file"));
750 gtk_dialog_run (GTK_DIALOG (dialog));
751 gtk_widget_destroy (dialog);
752 g_error_free (err);
753 } else {
754 gtk_recent_manager_add_item (recent_manager, g_filename_to_uri(fn, NULL, NULL));
756 } else {
757 if (!quiet_mode)
758 s_log_message (_("New file [%s]\n"),
759 toplevel->page_current->page_filename);
761 g_run_hook_page (w_current, "%new-page-hook", toplevel->page_current);
764 a_zoom_extents (w_current,
765 s_page_objects (toplevel->page_current),
766 A_PAN_DONT_REDRAW);
768 o_undo_savestate (w_current, UNDO_ALL);
770 if ( old_current != NULL )
771 s_page_goto (toplevel, old_current);
773 /* This line is generally un-needed, however if some code
774 * wants to open a page, yet not bring it to the front, it is
775 * needed needed to add it into the page manager. Otherwise,
776 * it will get done in x_window_set_current_page(...)
778 x_pagesel_update (w_current); /* ??? */
780 g_free (fn);
782 return page;
785 /*! \brief Changes the current page.
786 * \par Function Description
787 * This function displays the specified page <B>page</B> in the
788 * window attached to <B>toplevel</B>.
790 * It changes the <B>toplevel</B>'s current page to <B>page</B>,
791 * draws it and updates the user interface.
793 * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>.
795 * \param [in] w_current The toplevel environment.
796 * \param [in] page The page to become current page.
798 void
799 x_window_set_current_page (GSCHEM_TOPLEVEL *w_current, PAGE *page)
801 TOPLEVEL *toplevel = w_current->toplevel;
803 g_return_if_fail (toplevel != NULL);
804 g_return_if_fail (page != NULL);
806 o_redraw_cleanstates (w_current);
808 s_page_goto (toplevel, page);
810 i_update_menus (w_current);
811 i_set_filename (w_current, page->page_filename);
813 x_pagesel_update (w_current);
814 x_multiattrib_update (w_current);
816 x_manual_resize (w_current);
817 x_hscrollbar_update (w_current);
818 x_vscrollbar_update (w_current);
820 o_invalidate_all (w_current);
823 /*! \brief Saves a page to a file.
824 * \par Function Description
825 * This function saves the page <B>page</B> to a file named
826 * <B>filename</B>.
828 * It returns the value returned by function <B>f_save()</B> trying
829 * to save page <B>page</B> to file <B>filename</B> (1 on success, 0
830 * on failure).
832 * <B>page</B> may not be the current page of <B>toplevel</B>. The
833 * current page of <B>toplevel</B> is not affected by this function.
835 * \param [in] w_current The toplevel environment.
836 * \param [in] page The page to save.
837 * \param [in] filename The name of the file in which to save page.
838 * \returns 1 on success, 0 otherwise.
840 gint
841 x_window_save_page (GSCHEM_TOPLEVEL *w_current, PAGE *page, const gchar *filename)
843 TOPLEVEL *toplevel = w_current->toplevel;
844 PAGE *old_current;
845 const gchar *log_msg, *state_msg;
846 gint ret;
847 GError *err = NULL;
849 g_return_val_if_fail (toplevel != NULL, 0);
850 g_return_val_if_fail (page != NULL, 0);
851 g_return_val_if_fail (filename != NULL, 0);
853 /* save current page for restore after opening */
854 old_current = toplevel->page_current;
856 /* change to page */
857 s_page_goto (toplevel, page);
858 /* and try saving current page to filename */
859 ret = (gint)f_save (toplevel, toplevel->page_current, filename, &err);
860 if (ret != 1) {
861 log_msg = _("Could NOT save page [%s]\n");
862 state_msg = _("Error while trying to save");
864 GtkWidget *dialog;
866 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
867 GTK_DIALOG_DESTROY_WITH_PARENT,
868 GTK_MESSAGE_ERROR,
869 GTK_BUTTONS_CLOSE,
870 "%s",
871 err->message);
872 gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to save file"));
873 gtk_dialog_run (GTK_DIALOG (dialog));
874 gtk_widget_destroy (dialog);
875 g_clear_error (&err);
876 } else {
877 /* successful save of page to file, update page... */
878 /* change page name if necessary and prepare log message */
879 if (g_ascii_strcasecmp (page->page_filename, filename) != 0) {
880 g_free (page->page_filename);
881 page->page_filename = g_strdup (filename);
883 log_msg = _("Saved as [%s]\n");
884 } else {
885 log_msg = _("Saved [%s]\n");
887 state_msg = _("Saved");
889 /* reset page CHANGED flag */
890 page->CHANGED = 0;
892 /* add to recent file list */
893 gtk_recent_manager_add_item (recent_manager, g_filename_to_uri(filename, NULL, NULL));
896 /* log status of operation */
897 s_log_message (log_msg, filename);
899 /* update display and page manager */
900 x_window_set_current_page (w_current, old_current);
902 i_set_state_msg (w_current, SELECT, state_msg);
903 i_update_toolbar (w_current);
905 return ret;
908 /*! \brief Closes a page.
909 * \par Function Description
910 * This function closes the page <B>page</B> of toplevel
911 * <B>toplevel</B>.
913 * If necessary, the current page of <B>toplevel</B> is changed to
914 * the next valid page or to a new untitled page.
916 * \param [in] w_current The toplevel environment.
917 * \param [in] page The page to close.
919 void
920 x_window_close_page (GSCHEM_TOPLEVEL *w_current, PAGE *page)
922 TOPLEVEL *toplevel = w_current->toplevel;
923 PAGE *new_current = NULL;
924 GList *iter;
926 g_return_if_fail (toplevel != NULL);
927 g_return_if_fail (page != NULL);
929 g_assert (page->pid != -1);
931 /* If we're closing whilst inside a move action, re-wind the
932 * page contents back to their state before we started */
933 if (w_current->inside_action &&
934 (w_current->event_state == MOVE ||
935 w_current->event_state == ENDMOVE)) {
936 o_move_cancel (w_current);
939 if (page == toplevel->page_current) {
940 /* as it will delete current page, select new current page */
941 /* first look up in page hierarchy */
942 new_current = s_page_search_by_page_id (toplevel->pages, page->up);
944 if (new_current == NULL) {
945 /* no up in hierarchy, choice is prev, next, new page */
946 iter = g_list_find( geda_list_get_glist( toplevel->pages ), page );
948 if ( g_list_previous( iter ) ) {
949 new_current = (PAGE *)g_list_previous( iter )->data;
950 } else if ( g_list_next( iter ) ) {
951 new_current = (PAGE *)g_list_next( iter )->data;
952 } else {
953 /* need to add a new untitled page */
954 new_current = NULL;
957 /* new_current will be the new current page at the end of the function */
960 s_log_message (page->CHANGED ?
961 _("Discarding page [%s]\n") : _("Closing [%s]\n"),
962 page->page_filename);
963 /* remove page from toplevel list of page and free */
964 s_page_delete (toplevel, page);
966 /* Switch to a different page if we just removed the current */
967 if (toplevel->page_current == NULL) {
969 /* Create a new page if there wasn't another to switch to */
970 if (new_current == NULL) {
971 new_current = x_window_open_page (w_current, NULL);
974 /* change to new_current and update display */
975 x_window_set_current_page (w_current, new_current);
980 /*! \brief Setup default icon for GTK windows
982 * \par Function Description
983 * Sets the default window icon by name, to be found in the current icon
984 * theme. The name used is \#defined above as GSCHEM_THEME_ICON_NAME.
986 void x_window_set_default_icon( void )
988 gtk_window_set_default_icon_name( GSCHEM_THEME_ICON_NAME );