Take page as input and take advantage of s_visit_page.
[geda-gaf/berndj.git] / gschem / src / x_window.c
blob0fc09c58b9743ce84b16a318b1c80e3c61cace31
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 #include <config.h>
22 #include <stdio.h>
24 #include "gschem.h"
26 #ifdef HAVE_LIBDMALLOC
27 #include <dmalloc.h>
28 #endif
30 #define GSCHEM_THEME_ICON_NAME "geda-gschem"
32 /*! \todo Finish function documentation!!!
33 * \brief
34 * \par Function Description
37 void x_window_setup (GSCHEM_TOPLEVEL *w_current)
39 TOPLEVEL *toplevel = w_current->toplevel;
41 /* immediately setup user params */
42 i_vars_set(w_current);
44 /* Use an alternate factory. */
45 toplevel->factory = gschem_factory(w_current);
47 /* Initialize the autosave callback */
48 s_page_autosave_init(toplevel);
50 /* x_window_setup_world() - BEGIN */
51 toplevel->init_left = -45;
52 toplevel->init_top = -45;
53 /* init_right and _bottom are set before this function is called */
55 w_current->win_width = default_width;
56 w_current->win_height = default_height;
57 /* x_window_setup_world() - END */
59 /* Add to the list of windows */
60 global_window_list = g_list_append (global_window_list, w_current);
62 /* X related stuff */
63 x_window_create_main (w_current);
65 x_menu_attach_recent_files_submenu(w_current);
68 /*! \todo Finish function documentation!!!
69 * \brief
70 * \par Function Description
73 void x_window_setup_gc(GSCHEM_TOPLEVEL *w_current)
75 GdkGCValues values;
76 GdkGCValuesMask values_mask;
78 w_current->gc = gdk_gc_new(w_current->window);
80 if (w_current->gc == NULL) {
81 fprintf(stderr, _("Couldn't allocate gc\n"));
82 exit(-1);
85 values.foreground = white;
86 values.background = black;
88 values.function = GDK_XOR;
89 values_mask = GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FUNCTION;
90 w_current->xor_gc = gdk_gc_new_with_values(w_current->window,
91 &values, values_mask);
93 if (w_current->xor_gc == NULL) {
94 fprintf(stderr, _("Couldn't allocate xor_gc\n"));
95 exit(-1);
98 values.foreground = white;
99 values.background = black;
101 values.function = GDK_XOR;
102 values_mask = GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FUNCTION;
103 w_current->outline_xor_gc = gdk_gc_new_with_values(w_current->window,
104 &values, values_mask);
106 if (w_current->outline_xor_gc == NULL) {
107 fprintf(stderr, _("Couldn't allocate outline_xor_gc\n"));
108 exit(-1);
111 values.foreground = white;
112 values.background = black;
114 values.function = GDK_XOR;
115 values.line_style = GDK_LINE_ON_OFF_DASH;
116 values_mask = GDK_GC_FOREGROUND | GDK_GC_BACKGROUND |
117 GDK_GC_LINE_STYLE | GDK_GC_FUNCTION;
119 w_current->bounding_xor_gc = gdk_gc_new_with_values(w_current->window,
120 &values, values_mask);
122 if (w_current->bounding_xor_gc == NULL) {
123 fprintf(stderr, _("Couldn't allocate bounding_xor_gc\n"));
124 exit(-1);
127 w_current->bus_gc = gdk_gc_new(w_current->window);
129 if (w_current->bus_gc == NULL) {
130 fprintf(stderr, _("Couldn't allocate bus_gc\n"));
131 exit(-1);
135 /*! \todo Finish function documentation!!!
136 * \brief
137 * \par Function Description
140 void x_window_free_gc(GSCHEM_TOPLEVEL *w_current)
142 gdk_gc_unref(w_current->gc);
143 gdk_gc_unref(w_current->xor_gc);
144 gdk_gc_unref(w_current->bus_gc);
145 gdk_gc_unref(w_current->bounding_xor_gc);
146 gdk_gc_unref(w_current->outline_xor_gc);
149 /*! \todo Finish function documentation!!!
150 * \brief
151 * \par Function Description
154 void x_window_create_drawing(GtkWidget *drawbox, GSCHEM_TOPLEVEL *w_current)
156 /* drawing next */
157 w_current->drawing_area = gtk_drawing_area_new ();
158 /* Set the size here. Be sure that it has an aspect ratio of 1.333
159 * We could calculate this based on root window size, but for now
160 * lets just set it to:
161 * Width = root_width*3/4 Height = Width/1.3333333333
162 * 1.3333333 is the desired aspect ratio!
165 gtk_drawing_area_size (GTK_DRAWING_AREA (w_current->drawing_area),
166 w_current->win_width,
167 w_current->win_height);
169 gtk_box_pack_start (GTK_BOX (drawbox), w_current->drawing_area,
170 TRUE, TRUE, 0);
171 GTK_WIDGET_SET_FLAGS (w_current->drawing_area, GTK_CAN_FOCUS );
172 gtk_widget_grab_focus (w_current->drawing_area);
173 gtk_widget_show (w_current->drawing_area);
177 /*! \brief Set up callbacks for window events that affect drawing.
178 * \par Function Description
180 * Installs GTK+ callback handlers for signals that are emitted by
181 * the drawing area, and some for the main window that affect the drawing
182 * area.
184 * \param [in] w_current The toplevel environment.
186 void x_window_setup_draw_events(GSCHEM_TOPLEVEL *w_current)
188 struct event_reg_t {
189 gchar *detailed_signal;
190 GCallback c_handler;
193 struct event_reg_t drawing_area_events[] = {
194 { "expose_event", G_CALLBACK(x_event_expose) },
195 { "button_press_event", G_CALLBACK(x_event_button_pressed) },
196 { "button_release_event", G_CALLBACK(x_event_button_released) },
197 { "motion_notify_event", G_CALLBACK(x_event_motion) },
198 { "configure_event", G_CALLBACK(x_event_configure) },
199 { "key_press_event", G_CALLBACK(x_event_key_press) },
200 { NULL, NULL } };
201 struct event_reg_t main_window_events[] = {
202 { "enter_notify_event", G_CALLBACK(x_event_enter) },
203 { "scroll_event", G_CALLBACK(x_event_scroll) },
204 { NULL, NULL } };
205 struct event_reg_t *tmp;
207 /* is the configure event type missing here? hack */
208 gtk_widget_set_events (w_current->drawing_area,
209 GDK_EXPOSURE_MASK |
210 GDK_POINTER_MOTION_MASK |
211 GDK_BUTTON_PRESS_MASK |
212 GDK_ENTER_NOTIFY_MASK |
213 GDK_KEY_PRESS_MASK |
214 GDK_BUTTON_RELEASE_MASK);
216 for (tmp = drawing_area_events; tmp->detailed_signal != NULL; tmp++) {
217 g_signal_connect (w_current->drawing_area,
218 tmp->detailed_signal,
219 tmp->c_handler,
220 w_current);
223 for (tmp = main_window_events; tmp->detailed_signal != NULL; tmp++) {
224 g_signal_connect (w_current->main_window,
225 tmp->detailed_signal,
226 tmp->c_handler,
227 w_current);
232 /*! \brief Creates a new GtkImage displaying a GTK stock icon if available.
234 * If a stock GTK icon with the requested name was not found, this function
235 * falls back to the bitmap icons provided in the distribution.
237 * \param stock Name of the stock icon ("new", "open", etc.)
238 * \return Pointer to the new GtkImage object.
240 static GtkWidget *x_window_stock_pixmap(const char *stock, GSCHEM_TOPLEVEL *w_current)
242 GtkWidget *wpixmap = NULL;
243 GdkPixmap *pixmap;
244 GdkBitmap *mask;
245 GtkStockItem item;
247 GdkWindow *window=w_current->main_window->window;
248 GdkColor *background=&w_current->main_window->style->bg[GTK_STATE_NORMAL];
250 gchar *filename=g_strconcat(w_current->toplevel->bitmap_directory,
251 G_DIR_SEPARATOR_S,
252 "gschem-", stock, ".xpm", NULL);
254 gchar *stockid=g_strconcat("gtk-", stock, NULL);
256 /* First check if GTK knows this stock icon */
257 if(gtk_stock_lookup(stockid, &item)) {
258 wpixmap = gtk_image_new_from_stock(stockid,
259 GTK_ICON_SIZE_SMALL_TOOLBAR);
260 } else {
261 /* Fallback to the original custom icon */
262 pixmap = gdk_pixmap_create_from_xpm (window, &mask,
263 background, filename);
264 if (pixmap != NULL) {
265 wpixmap = gtk_image_new_from_pixmap (pixmap, mask);
266 } else {
267 s_log_message("Could not find image at file: %s.\n", filename);
268 wpixmap = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE ,
269 GTK_ICON_SIZE_SMALL_TOOLBAR);
273 g_free(filename);
274 g_free(stockid);
276 return wpixmap;
279 /*! \todo Finish function documentation!!!
280 * \brief
281 * \par Function Description
284 void x_window_create_main(GSCHEM_TOPLEVEL *w_current)
286 TOPLEVEL *toplevel = w_current->toplevel;
288 GtkWidget *label=NULL;
289 GtkWidget *main_box=NULL;
290 GtkWidget *menubar=NULL;
291 GtkWidget *drawbox=NULL;
292 GtkWidget *bottom_box=NULL;
293 GtkWidget *toolbar=NULL;
294 GtkWidget *handlebox=NULL;
296 /* used to signify that the window isn't mapped yet */
297 w_current->window = NULL;
299 w_current->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
301 gtk_widget_set_name (w_current->main_window, "gschem");
302 gtk_window_set_policy (GTK_WINDOW (w_current->main_window), TRUE, TRUE, TRUE);
304 /* We want the widgets to flow around the drawing area, so we don't
305 * set a size of the main window. The drawing area's size is fixed,
306 * see below
310 * normally we let the window manager handle locating and sizing
311 * the window. However, for some batch processing of schematics
312 * (generating a pdf of all schematics for example) we want to
313 * override this. Hence "auto_place_mode".
315 if( auto_place_mode )
316 gtk_widget_set_uposition (w_current->main_window, 10, 10);
318 /* this should work fine */
319 gtk_signal_connect (GTK_OBJECT (w_current->main_window), "delete_event",
320 GTK_SIGNAL_FUNC (i_callback_close_wm),
321 w_current);
323 /* Containers first */
324 main_box = gtk_vbox_new(FALSE, 1);
325 gtk_container_border_width(GTK_CONTAINER(main_box), 0);
326 gtk_container_add(GTK_CONTAINER(w_current->main_window), main_box);
328 get_main_menu(&menubar);
329 if (w_current->handleboxes) {
330 handlebox = gtk_handle_box_new ();
331 gtk_box_pack_start(GTK_BOX(main_box), handlebox, FALSE, FALSE, 0);
332 gtk_container_add (GTK_CONTAINER (handlebox), menubar);
333 } else {
334 gtk_box_pack_start(GTK_BOX(main_box), menubar, FALSE, FALSE, 0);
337 w_current->menubar = menubar;
338 gtk_widget_realize (w_current->main_window);
340 if (w_current->handleboxes && w_current->toolbars) {
341 handlebox = gtk_handle_box_new ();
342 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
345 if (w_current->toolbars) {
346 toolbar = gtk_toolbar_new();
347 gtk_toolbar_set_orientation (GTK_TOOLBAR(toolbar),
348 GTK_ORIENTATION_HORIZONTAL);
349 gtk_toolbar_set_style (GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
351 if (w_current->handleboxes) {
352 gtk_container_add (GTK_CONTAINER (handlebox), toolbar);
353 } else {
354 gtk_box_pack_start(GTK_BOX(main_box), toolbar, FALSE, FALSE, 0);
357 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
358 _("New"),
359 _("New file"),
360 "toolbar/new",
361 x_window_stock_pixmap("new", w_current),
362 (GtkSignalFunc) i_callback_toolbar_file_new,
363 w_current);
364 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
365 _("Open"),
366 _("Open file..."),
367 "toolbar/open",
368 x_window_stock_pixmap("open", w_current),
369 (GtkSignalFunc) i_callback_toolbar_file_open,
370 w_current);
371 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
372 _("Save"),
373 _("Save file"),
374 "toolbar/save",
375 x_window_stock_pixmap("save", w_current),
376 (GtkSignalFunc) i_callback_toolbar_file_save,
377 w_current);
378 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
379 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
380 _("Undo"),
381 _("Undo last operation"),
382 "toolbar/undo",
383 x_window_stock_pixmap("undo", w_current),
384 (GtkSignalFunc) i_callback_toolbar_edit_undo,
385 w_current);
386 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
387 _("Redo"),
388 _("Redo last undo"),
389 "toolbar/redo",
390 x_window_stock_pixmap("redo", w_current),
391 (GtkSignalFunc) i_callback_toolbar_edit_redo,
392 w_current);
393 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
394 /* not part of any radio button group */
395 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
396 _("Component"),
397 _("Add component...\nSelect library and component from list, move the mouse into main window, click to place\nRight mouse button to cancel"),
398 "toolbar/component",
399 x_window_stock_pixmap("comp", w_current),
400 (GtkSignalFunc) i_callback_toolbar_add_component,
401 w_current);
402 w_current->toolbar_net =
403 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
404 GTK_TOOLBAR_CHILD_RADIOBUTTON,
405 NULL,
406 _("Nets"),
407 _("Add nets mode\nRight mouse button to cancel"),
408 "toolbar/nets",
409 x_window_stock_pixmap("net", w_current),
410 (GtkSignalFunc) i_callback_toolbar_add_net,
411 w_current);
412 w_current->toolbar_bus =
413 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
414 GTK_TOOLBAR_CHILD_RADIOBUTTON,
415 w_current->toolbar_net,
416 _("Bus"),
417 _("Add buses mode\nRight mouse button to cancel"),
418 "toolbar/bus",
419 x_window_stock_pixmap("bus", w_current),
420 (GtkSignalFunc) i_callback_toolbar_add_bus,
421 w_current);
422 /* not part of any radio button group */
423 gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
424 _("Text"),
425 _("Add Text..."),
426 "toolbar/text",
427 x_window_stock_pixmap("text", w_current),
428 (GtkSignalFunc) i_callback_toolbar_add_text,
429 w_current);
430 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
431 w_current->toolbar_select =
432 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
433 GTK_TOOLBAR_CHILD_RADIOBUTTON,
434 w_current->toolbar_bus,
435 _("Select"),
436 _("Select mode"),
437 "toolbar/select",
438 x_window_stock_pixmap("select", w_current),
439 (GtkSignalFunc) i_callback_toolbar_edit_select,
440 w_current);
443 gtk_toolbar_append_space (GTK_TOOLBAR(toolbar));
444 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_current->toolbar_select),
445 TRUE);
449 /* Try to create popup menu (appears in right mouse button */
450 w_current->popup_menu = (GtkWidget *) get_main_popup(w_current);
452 drawbox = gtk_hbox_new(FALSE, 0);
453 gtk_container_border_width(GTK_CONTAINER(drawbox), 0);
454 gtk_container_add(GTK_CONTAINER(main_box), drawbox);
456 x_window_create_drawing(drawbox, w_current);
457 x_window_setup_draw_events(w_current);
459 if (w_current->scrollbars_flag == TRUE) {
460 /* setup scroll bars */
461 w_current->v_adjustment =
462 gtk_adjustment_new (toplevel->init_bottom,
463 0.0, toplevel->init_bottom,
464 100.0, 100.0, 10.0);
466 w_current->v_scrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (
467 w_current->v_adjustment));
469 gtk_range_set_update_policy (GTK_RANGE (w_current->v_scrollbar),
470 GTK_UPDATE_CONTINUOUS);
472 gtk_box_pack_start (GTK_BOX (drawbox), w_current->v_scrollbar,
473 FALSE, FALSE, 0);
475 gtk_signal_connect (GTK_OBJECT (w_current->v_adjustment),
476 "value_changed",
477 GTK_SIGNAL_FUNC (x_event_vschanged),
478 w_current);
480 w_current->h_adjustment = gtk_adjustment_new (0.0, 0.0,
481 toplevel->init_right,
482 100.0, 100.0, 10.0);
484 w_current->h_scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (
485 w_current->h_adjustment));
487 gtk_range_set_update_policy (GTK_RANGE (w_current->h_scrollbar),
488 GTK_UPDATE_CONTINUOUS);
490 gtk_box_pack_start (GTK_BOX (main_box), w_current->h_scrollbar,
491 FALSE, FALSE, 0);
493 gtk_signal_connect (GTK_OBJECT (w_current->h_adjustment),
494 "value_changed",
495 GTK_SIGNAL_FUNC (x_event_hschanged),
496 w_current);
499 /* bottom box */
500 bottom_box = gtk_hbox_new(FALSE, 0);
501 gtk_container_border_width(GTK_CONTAINER(bottom_box), 1);
502 gtk_box_pack_start (GTK_BOX (main_box), bottom_box, FALSE, FALSE, 0);
504 /* label = gtk_label_new ("Mouse buttons:");
505 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 10);
508 label = gtk_label_new (" ");
509 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 2);
511 w_current->left_label = gtk_label_new (_("Pick"));
512 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->left_label,
513 FALSE, FALSE, 0);
515 label = gtk_label_new ("|");
516 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
518 if (w_current->middle_button == STROKE) {
519 #ifdef HAS_LIBSTROKE
520 w_current->middle_label = gtk_label_new (_("Stroke"));
521 #else
522 w_current->middle_label = gtk_label_new (_("none"));
523 #endif
524 } else if (w_current->middle_button == ACTION) {
525 w_current->middle_label = gtk_label_new (_("Action"));
526 } else {
527 w_current->middle_label = gtk_label_new (_("Repeat/none"));
530 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->middle_label,
531 FALSE, FALSE, 0);
533 label = gtk_label_new ("|");
534 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
536 switch (default_third_button) {
537 case POPUP_ENABLED:
538 w_current->right_label = gtk_label_new (_("Menu/Cancel"));
539 break;
540 case MOUSEPAN_ENABLED:
541 w_current->right_label = gtk_label_new (_("Pan/Cancel"));
542 break;
544 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->right_label,
545 FALSE, FALSE, 0);
547 label = gtk_label_new (" ");
548 gtk_box_pack_start (GTK_BOX (bottom_box), label, FALSE, FALSE, 5);
550 w_current->grid_label = gtk_label_new (" ");
551 gtk_box_pack_start (GTK_BOX (bottom_box), w_current->grid_label,
552 FALSE, FALSE, 10);
554 w_current->status_label = gtk_label_new (_("Select Mode"));
555 gtk_box_pack_end (GTK_BOX (bottom_box), w_current->status_label, FALSE,
556 FALSE, 10);
559 GtkCellRenderer *prompt_cell;
560 GtkListStore *l;
562 l = gtk_list_store_new(SCM_PROMPT_N_COLUMNS,
563 G_TYPE_POINTER,
564 G_TYPE_STRING);
566 w_current->scm_prompts = gtk_combo_box_new_with_model(GTK_TREE_MODEL(l));
567 g_object_set(G_OBJECT(w_current->scm_prompts),
568 "focus-on-click", FALSE, NULL);
569 g_signal_connect(w_current->scm_prompts, "changed",
570 G_CALLBACK(x_event_scm_prompt_changed), w_current);
572 prompt_cell = gtk_cell_renderer_text_new();
573 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w_current->scm_prompts),
574 prompt_cell,
575 TRUE);
576 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(w_current->scm_prompts),
577 prompt_cell, "text", SCM_PROMPT_MESSAGE);
579 gtk_box_pack_end(GTK_BOX(bottom_box), w_current->scm_prompts, FALSE,
580 FALSE, 10);
583 gtk_widget_show_all (w_current->main_window);
585 w_current->window = w_current->drawing_area->window;
587 w_current->backingstore = gdk_pixmap_new(w_current->window,
588 w_current->drawing_area->allocation.width,
589 w_current->drawing_area->allocation.height,
590 -1);
591 x_window_setup_gc(w_current);
594 static void x_window_close_widgets(GSCHEM_TOPLEVEL *w_current)
596 if (w_current->sowindow)
597 gtk_widget_destroy(w_current->sowindow);
599 if (w_current->cswindow)
600 gtk_widget_destroy(w_current->cswindow);
602 if (w_current->tiwindow)
603 gtk_widget_destroy(w_current->tiwindow);
605 if (w_current->tewindow)
606 gtk_widget_destroy(w_current->tewindow);
608 if (w_current->aawindow)
609 gtk_widget_destroy(w_current->aawindow);
611 x_multiattrib_close (w_current);
613 if (w_current->aewindow)
614 gtk_widget_destroy(w_current->aewindow);
616 if (w_current->trwindow)
617 gtk_widget_destroy(w_current->trwindow);
619 x_pagesel_close (w_current);
621 if (w_current->tswindow)
622 gtk_widget_destroy(w_current->tswindow);
624 if (w_current->abwindow)
625 gtk_widget_destroy(w_current->abwindow);
627 if (w_current->iwindow)
628 gtk_widget_destroy(w_current->iwindow);
630 if (w_current->hkwindow)
631 gtk_widget_destroy(w_current->hkwindow);
633 if (w_current->cowindow)
634 gtk_widget_destroy(w_current->cowindow);
636 if (w_current->clwindow)
637 gtk_widget_destroy(w_current->clwindow);
639 if (w_current->sewindow)
640 gtk_widget_destroy(w_current->sewindow);
642 /* finally close the main window */
643 gtk_widget_destroy(w_current->main_window);
646 /*! \todo Finish function documentation!!!
647 * \brief
648 * \par Function Description
651 void x_window_close(GSCHEM_TOPLEVEL *w_current)
653 TOPLEVEL *toplevel = w_current->toplevel;
654 gboolean last_window = FALSE;
656 /* last chance to save possible unsaved pages */
657 if (!x_dialog_close_window (w_current)) {
658 /* user somehow cancelled the close */
659 return;
662 #if DEBUG
663 o_conn_print_hash(w_current->page_current->conn_table);
664 #endif
666 if (g_list_length (global_window_list) == 1) {
667 /* no more window after this one, remember to quit */
668 last_window = TRUE;
671 if (toplevel->major_changed_refdes) {
672 GList* current = toplevel->major_changed_refdes;
673 while (current)
675 /* printf("yeah freeing: %s\n", (char*) current->data); */
676 g_free(current->data);
677 current = g_list_next(current);
679 g_list_free(toplevel->major_changed_refdes);
682 /* stuff that has to be done before we free w_current */
683 if (last_window) {
684 /* free all fonts */
685 o_text_freeallfonts (toplevel);
686 /* close the log file */
687 s_log_close ();
689 * free the buffers
690 * FIXME: this is a double-bind: either we risk leaving pointers to stale
691 * objects in the global buffers, or we clear out the buffers when we don't
692 * need to. Fix this by keeping buffer objects in their very own TOPLEVEL.
694 o_buffer_free (w_current);
697 toplevel->DONT_REDRAW = 1; /* Bernd's slotting mechanism would make o_text.c angry. */
698 g_object_unref(G_OBJECT(toplevel));
699 global_window_list = g_list_remove (global_window_list, w_current);
701 /* close all the dialog boxes and release GDK resources */
702 x_window_close_widgets(w_current);
703 if (w_current->backingstore) {
704 gdk_pixmap_unref(w_current->backingstore);
707 x_window_free_gc(w_current);
709 i_vars_freestrings(w_current);
710 g_free (w_current);
712 /* just closed last window, so quit */
713 if (last_window) {
714 gschem_quit();
718 /*! \todo Finish function documentation!!!
719 * \brief
720 * \par Function Description
723 void x_window_close_all(GSCHEM_TOPLEVEL *w_current)
725 GSCHEM_TOPLEVEL *current;
726 GList *list_copy, *iter;
728 iter = list_copy = g_list_copy (global_window_list);
729 while (iter != NULL ) {
730 current = (GSCHEM_TOPLEVEL *)iter->data;
731 iter = g_list_next (iter);
732 x_window_close (current);
734 g_list_free (list_copy);
737 /*! \brief Opens a new page from a file.
738 * \par Function Description
739 * This function opens the file whose name is <B>filename</B> in a
740 * new PAGE of <B>toplevel</B>.
742 * If there is no page for <B>filename</B> in <B>toplevel</B>'s list
743 * of pages, it creates a new PAGE, loads the file in it and returns
744 * a pointer on the new page. Otherwise it returns a pointer on the
745 * existing page.
747 * If the filename passed is NULL, this function creates an empty,
748 * untitled page. The name of the untitled page is build from
749 * configuration data ('untitled-name') and a counter for uniqueness.
751 * The opened page becomes the current page of <B>toplevel</B>.
753 * \param [in] toplevel The toplevel environment.
754 * \param [in] filename The name of the file to open or NULL for a blank page.
755 * \returns A pointer on the new page.
757 * \bug This code should check to make sure any untitled filename
758 * does not conflict with a file on disk.
760 PAGE*
761 x_window_open_page (GSCHEM_TOPLEVEL *w_current, const gchar *filename)
763 TOPLEVEL *toplevel = w_current->toplevel;
764 PAGE *old_current, *page;
765 gchar *fn;
767 g_return_val_if_fail (toplevel != NULL, NULL);
769 /* Generate untitled filename if none was specified */
770 if (filename == NULL) {
771 gchar *cwd, *tmp;
772 cwd = g_get_current_dir ();
773 tmp = g_strdup_printf ("%s_%d.sch",
774 toplevel->untitled_name,
775 ++w_current->num_untitled);
776 fn = g_build_filename (cwd, tmp, NULL);
777 g_free(cwd);
778 g_free(tmp);
779 } else {
780 fn = g_strdup (filename);
783 /* Return existing page if it is already loaded */
784 page = s_page_search (toplevel, fn);
785 if ( page != NULL ) {
786 g_free(fn);
787 return page;
790 old_current = toplevel->page_current;
791 page = s_page_new (toplevel, fn);
792 page->width = w_current->win_width;
793 page->height = w_current->win_height;
794 s_toplevel_goto_page(toplevel, page);
795 set_window(toplevel, page,
796 toplevel->init_left, toplevel->init_right,
797 toplevel->init_top, toplevel->init_bottom);
799 /* Load from file if necessary, otherwise just print a message */
800 if (filename != NULL) {
801 GError *err = NULL;
802 if (!quiet_mode)
803 s_log_message (_("Loading schematic [%s]\n"), fn);
805 if (!f_open(toplevel, page, (gchar *) fn, &err)) {
806 GtkWidget *dialog;
808 g_warning ("%s\n", err->message);
809 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
810 GTK_DIALOG_DESTROY_WITH_PARENT,
811 GTK_MESSAGE_ERROR,
812 GTK_BUTTONS_CLOSE,
813 "%s",
814 err->message);
815 gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to load file"));
816 gtk_dialog_run (GTK_DIALOG (dialog));
817 gtk_widget_destroy (dialog);
818 g_error_free (err);
819 } else {
820 recent_files_add (fn);
822 } else {
823 if (!quiet_mode)
824 s_log_message (_("New file [%s]\n"),
825 toplevel->page_current->page_filename);
828 if (scm_hook_empty_p (new_page_hook) == SCM_BOOL_F)
829 scm_run_hook (new_page_hook,
830 scm_cons (g_make_page_smob (toplevel, page), SCM_EOL));
832 a_zoom_extents(w_current, toplevel->page_current, A_PAN_DONT_REDRAW);
834 o_undo_savestate (w_current, UNDO_ALL);
836 if ( old_current != NULL )
837 s_toplevel_goto_page(toplevel, old_current);
839 /* This line is generally un-needed, however if some code
840 * wants to open a page, yet not bring it to the front, it is
841 * needed needed to add it into the page manager. Otherwise,
842 * it will get done in x_window_set_current_page(...)
844 x_pagesel_update (w_current); /* ??? */
846 g_free (fn);
848 return page;
851 /*! \brief Changes the current page.
852 * \par Function Description
853 * This function displays the specified page <B>page</B> in the
854 * window attached to <B>toplevel</B>.
856 * It changes the <B>toplevel</B>'s current page to <B>page</B>,
857 * draws it and updates the user interface.
859 * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>.
861 * \param [in] toplevel The toplevel environment.
862 * \param [in] page The page to become current page.
864 void
865 x_window_set_current_page (GSCHEM_TOPLEVEL *w_current, PAGE *page)
867 TOPLEVEL *toplevel = w_current->toplevel;
869 g_return_if_fail (toplevel != NULL);
870 g_return_if_fail (page != NULL);
872 s_toplevel_goto_page(toplevel, page);
874 i_update_menus (w_current);
875 i_set_filename (w_current, page->page_filename);
877 x_pagesel_update (w_current);
878 x_multiattrib_update(w_current,
879 w_current->toplevel->page_current->selection_list);
881 toplevel->DONT_REDRAW = 1;
882 x_repaint_background (w_current);
883 x_manual_resize (w_current);
884 x_hscrollbar_update (w_current);
885 x_vscrollbar_update (w_current);
886 toplevel->DONT_REDRAW = 0;
888 o_redraw_all (w_current);
891 /*! \brief Saves a page to a file.
892 * \par Function Description
893 * This function saves the page <B>page</B> to a file named
894 * <B>filename</B>.
896 * It returns the value returned by function <B>f_save()</B> trying
897 * to save page <B>page</B> to file <B>filename</B> (1 on success, 0
898 * on failure).
900 * <B>page</B> may not be the current page of <B>toplevel</B>. The
901 * current page of <B>toplevel</B> is not affected by this function.
903 * \param [in] toplevel The toplevel environment.
904 * \param [in] page The page to save.
905 * \param [in] filename The name of the file in which to save page.
906 * \returns 1 on success, 0 otherwise.
908 gint
909 x_window_save_page (GSCHEM_TOPLEVEL *w_current, PAGE *page, const gchar *filename)
911 TOPLEVEL *toplevel = w_current->toplevel;
912 PAGE *old_current;
913 const gchar *log_msg, *state_msg;
914 gint ret;
916 g_return_val_if_fail (toplevel != NULL, 0);
917 g_return_val_if_fail (page != NULL, 0);
918 g_return_val_if_fail (filename != NULL, 0);
920 /* save current page for restore after opening */
921 old_current = toplevel->page_current;
923 /* change to page */
924 s_toplevel_goto_page(toplevel, page);
925 /* and try saving current page to filename */
926 ret = f_save(toplevel, page, filename);
927 if (ret != 1) {
928 /* an error occurred when saving page to file */
929 log_msg = _("Could NOT save page [%s]\n");
930 state_msg = _("Error while trying to save");
932 } else {
933 /* successful save of page to file, update page... */
934 /* change page name if necessary and prepare log message */
935 if (g_ascii_strcasecmp (page->page_filename, filename) != 0) {
936 g_free (page->page_filename);
937 page->page_filename = g_strdup (filename);
939 log_msg = _("Saved as [%s]\n");
940 } else {
941 log_msg = _("Saved [%s]\n");
943 state_msg = _("Saved");
945 /* reset page CHANGED flag */
946 page->CHANGED = 0;
948 /* update recent file list */
949 recent_files_add(filename);
952 /* log status of operation */
953 s_log_message (log_msg, filename);
955 /* update display and page manager */
956 x_window_set_current_page (w_current, old_current);
958 i_set_state_msg (w_current, SELECT, state_msg);
959 i_update_toolbar (w_current);
961 return ret;
964 /*! \brief Closes a page.
965 * \par Function Description
966 * This function closes the page <B>page</B> of toplevel
967 * <B>toplevel</B>.
969 * If necessary, the current page of <B>toplevel</B> is changed to
970 * the next valid page or to a new untitled page.
972 * \param [in] toplevel The toplevel environment.
973 * \param [in] page The page to close.
975 void
976 x_window_close_page (GSCHEM_TOPLEVEL *w_current, PAGE *page)
978 TOPLEVEL *toplevel = w_current->toplevel;
979 PAGE *new_current = NULL;
980 GList *iter;
982 g_return_if_fail (toplevel != NULL);
983 g_return_if_fail (page != NULL);
985 if (page == toplevel->page_current) {
986 /* as it will delete current page, select new current page */
987 /* first look up in page hierarchy */
988 new_current = page->up_page;
990 if (new_current == NULL) {
991 /* no up in hierarchy, choice is prev, next, new page */
992 iter = g_list_find( geda_list_get_glist( toplevel->pages ), page );
994 if ( g_list_previous( iter ) ) {
995 new_current = (PAGE *)g_list_previous( iter )->data;
996 } else if ( g_list_next( iter ) ) {
997 new_current = (PAGE *)g_list_next( iter )->data;
998 } else {
999 /* need to add a new untitled page */
1000 new_current = NULL;
1003 /* new_current will be the new current page at the end of the function */
1006 s_log_message (page->CHANGED ?
1007 _("Discarding page [%s]\n") : _("Closing [%s]\n"),
1008 page->page_filename);
1009 /* remove page from toplevel list of page and free */
1010 s_page_delete (toplevel, page);
1012 /* Switch to a different page if we just removed the current */
1013 if (toplevel->page_current == NULL) {
1015 /* Create a new page if there wasn't another to switch to */
1016 if (new_current == NULL) {
1017 new_current = x_window_open_page (w_current, NULL);
1020 /* change to new_current and update display */
1021 x_window_set_current_page (w_current, new_current);
1026 /*! \brief Setup default icon for GTK windows
1028 * \par Function Description
1029 * Sets the default window icon by name, to be found in the current icon
1030 * theme. The name used is #defined above as GSCHEM_THEME_ICON_NAME.
1032 void x_window_set_default_icon( void )
1034 gtk_window_set_default_icon_name( GSCHEM_THEME_ICON_NAME );