1 /* Lepton EDA Schematic Capture
2 * Copyright (C) 1998-2010 Ales Hvezda
3 * Copyright (C) 1998-2016 gEDA Contributors
4 * Copyright (C) 2016 Peter Brett <peter@peter-b.co.uk>
5 * Copyright (C) 2017-2021 Lepton EDA Contributors
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <config.h>
23 #include "gschem.h"
24
25
26 static void
27 create_menubar (GschemToplevel *w_current,
28 GtkWidget *main_box,
29 GtkWidget *menubar);
30
31 static void
32 create_toolbar_button (GschemToplevel *w_current,
33 GtkWidget *toolbar,
34 const gchar *pixmap_name,
35 const gchar *label,
36 const gchar *tooltip,
37 GCallback callback,
38 gint pos);
39
40 static GtkWidget*
41 create_toolbar_radio_button (GSList** group,
42 GschemToplevel *w_current,
43 GtkWidget *toolbar,
44 const gchar *pixmap_name,
45 const gchar *label,
46 const gchar *tooltip,
47 GCallback callback,
48 gint pos);
49
50 static void
51 create_toolbar_separator (GtkWidget *toolbar, gint pos);
52
53 static void
54 create_toolbar (GschemToplevel *w_current, GtkWidget *main_box);
55
56
57 static void
58 create_find_text_widget (GschemToplevel *w_current, GtkWidget *work_box);
59
60 static void
61 create_hide_text_widget (GschemToplevel *w_current, GtkWidget *work_box);
62
63 static void
64 create_show_text_widget (GschemToplevel *w_current, GtkWidget *work_box);
65
66 static void
67 create_macro_widget (GschemToplevel *w_current, GtkWidget *work_box);
68
69 static void
70 create_translate_widget (GschemToplevel *w_current, GtkWidget *work_box);
71
72 static void
73 create_bottom_widget (GschemToplevel *w_current, GtkWidget *main_box);
74
75
76 static GtkWidget*
77 create_notebook_right (GschemToplevel *w_current);
78
79 static GtkWidget*
80 create_notebook_bottom (GschemToplevel *w_current);
81
82
83 static void
84 geometry_save (GschemToplevel* w_current);
85
86 static void
87 geometry_restore (GschemToplevel* w_current);
88
89
90 static void
91 open_page_error_dialog (GschemToplevel* w_current,
92 const gchar* filename,
93 GError* err);
94
95 static void
96 recent_manager_add (GschemToplevel* w_current,
97 const gchar* filename);
98
99 static int
100 untitled_next_index (GschemToplevel* w_current);
101
102 static gchar*
103 untitled_filename (GschemToplevel* w_current, gboolean log_skipped);
104
105 static LeptonPage*
106 x_window_new_page (GschemToplevel* w_current);
107
108
109
110 /*! \todo Finish function documentation!!!
111 * \brief
112 * \par Function Description
113 *
114 */
115 GschemToplevel*
x_window_setup(GschemToplevel * w_current)116 x_window_setup (GschemToplevel *w_current)
117 {
118 LeptonToplevel *toplevel = gschem_toplevel_get_toplevel (w_current);
119
120 /* immediately setup user params */
121 i_vars_set(w_current);
122
123 /* Initialize the autosave callback */
124 lepton_toplevel_init_autosave (toplevel);
125
126 /* Initialize the clipboard callback */
127 x_clipboard_init (w_current);
128
129 /* Add to the list of windows */
130 global_window_list = g_list_append (global_window_list, w_current);
131
132 return w_current;
133 }
134
135 /*! \todo Finish function documentation!!!
136 * \brief
137 * \par Function Description
138 *
139 */
x_window_create_drawing(GtkWidget * scrolled,GschemToplevel * w_current)140 void x_window_create_drawing(GtkWidget *scrolled, GschemToplevel *w_current)
141 {
142 LeptonPage* page = schematic_window_get_active_page (w_current);
143 GschemPageView* view = gschem_page_view_new_with_page (page);
144
145 #ifdef ENABLE_GTK3
146 gtk_widget_set_hexpand (GTK_WIDGET (view), TRUE);
147 gtk_widget_set_vexpand (GTK_WIDGET (view), TRUE);
148 gtk_widget_set_halign (GTK_WIDGET (view), GTK_ALIGN_FILL);
149 gtk_widget_set_valign (GTK_WIDGET (view), GTK_ALIGN_FILL);
150 #endif
151
152 w_current->drawing_area = GTK_WIDGET (view);
153
154 gtk_container_add(GTK_CONTAINER(scrolled), w_current->drawing_area);
155
156 gtk_widget_set_can_focus (w_current->drawing_area, TRUE);
157 gtk_widget_grab_focus (w_current->drawing_area);
158 gtk_widget_show (w_current->drawing_area);
159 }
160
161
162
163 /*! \brief Set up callbacks for window events that affect drawing.
164 * \par Function Description
165 *
166 * Installs GTK+ callback handlers for the main window
167 * that affect the drawing area.
168 *
169 * \param [in] w_current The toplevel environment.
170 * \param [in] main_window The main window.
171 */
x_window_setup_draw_events_main_wnd(GschemToplevel * w_current,GtkWidget * main_window)172 void x_window_setup_draw_events_main_wnd (GschemToplevel* w_current,
173 GtkWidget* main_window)
174 {
175 struct event_reg_t
176 {
177 const gchar* detailed_signal;
178 GCallback c_handler;
179 };
180
181 struct event_reg_t main_window_events[] =
182 {
183 { "enter_notify_event", G_CALLBACK(x_event_enter) },
184 { NULL, NULL }
185 };
186
187 struct event_reg_t* tmp = NULL;
188
189 for (tmp = main_window_events; tmp->detailed_signal != NULL; tmp++)
190 {
191 g_signal_connect (main_window,
192 tmp->detailed_signal,
193 tmp->c_handler,
194 w_current);
195 }
196
197 } /* x_window_setup_draw_events_main_wnd() */
198
199
200
201 /*! \brief Set up callbacks for the drawing area.
202 * \par Function Description
203 *
204 * Installs GTK+ callback handlers for signals that are emitted by
205 * the drawing area
206 *
207 * \param [in] w_current The toplevel environment.
208 * \param [in] drawing_area The drawing area (page view).
209 */
x_window_setup_draw_events_drawing_area(GschemToplevel * w_current,GschemPageView * drawing_area)210 void x_window_setup_draw_events_drawing_area (GschemToplevel* w_current,
211 GschemPageView* drawing_area)
212 {
213 struct event_reg_t
214 {
215 const gchar* detailed_signal;
216 GCallback c_handler;
217 };
218
219 struct event_reg_t drawing_area_events[] =
220 {
221 #ifdef ENABLE_GTK3
222 { "draw", G_CALLBACK(x_event_draw) },
223 #else
224 { "expose_event", G_CALLBACK(x_event_expose) },
225 #endif
226 { "button_press_event", G_CALLBACK(x_event_button_pressed) },
227 { "button_release_event", G_CALLBACK(x_event_button_released) },
228 { "motion_notify_event", G_CALLBACK(x_event_motion) },
229 { "configure_event", G_CALLBACK(x_event_configure) },
230 { "key_press_event", G_CALLBACK(x_event_key) },
231 { "key_release_event", G_CALLBACK(x_event_key) },
232 { "scroll_event", G_CALLBACK(x_event_scroll) },
233 { "update-grid-info", G_CALLBACK(i_update_grid_info_callback) },
234 { "notify::page", G_CALLBACK(gschem_toplevel_notify_page_callback) },
235 { NULL, NULL }
236 };
237
238
239 /* is the configure event type missing here? hack */
240
241
242 /* gtk_widget_set_events() can be called on unrealized widgets only.
243 * Since with tabbed GUI (see x_tabs.c) we need to setup events
244 * for already created page view widgets, use
245 * gtk_widget_add_events() instead.
246 */
247 gtk_widget_add_events (GTK_WIDGET (drawing_area),
248 GDK_EXPOSURE_MASK |
249 GDK_POINTER_MOTION_MASK |
250 GDK_BUTTON_PRESS_MASK |
251 #if GTK_CHECK_VERSION(3,4,0)
252 GDK_SMOOTH_SCROLL_MASK |
253 #endif
254 GDK_ENTER_NOTIFY_MASK |
255 GDK_KEY_PRESS_MASK |
256 GDK_BUTTON_RELEASE_MASK
257 #ifdef ENABLE_GTK3
258 | GDK_SCROLL_MASK
259 #endif
260 );
261
262 struct event_reg_t* tmp = NULL;
263
264 for (tmp = drawing_area_events; tmp->detailed_signal != NULL; tmp++)
265 {
266 g_signal_connect (drawing_area,
267 tmp->detailed_signal,
268 tmp->c_handler,
269 w_current);
270 }
271
272 } /* x_window_setup_draw_events_drawing_area() */
273
274
275
276 /*! \brief Creates a new GtkImage displaying a GTK stock icon if available.
277 *
278 * If a stock GTK icon with the requested name was not found, this function
279 * falls back to the bitmap icons provided in the distribution.
280 *
281 * \param stock Name of the stock icon ("new", "open", etc.)
282 * \param w_current Schematic top level
283 * \return Pointer to the new GtkImage object.
284 */
x_window_stock_pixmap(const char * stock,GschemToplevel * w_current)285 static GtkWidget *x_window_stock_pixmap(const char *stock, GschemToplevel *w_current)
286 {
287 GtkWidget *wpixmap = NULL;
288 GtkStockItem item;
289
290 gchar *stockid=g_strconcat("gtk-", stock, NULL);
291
292 /* First check if GTK knows this icon */
293 if(gtk_stock_lookup(stockid, &item)) {
294 wpixmap = gtk_image_new_from_stock(stockid,
295 GTK_ICON_SIZE_LARGE_TOOLBAR);
296 } else {
297 /* Look up the icon in the icon theme */
298 wpixmap = gtk_image_new_from_icon_name (stock,
299 GTK_ICON_SIZE_LARGE_TOOLBAR);
300 }
301
302 g_free(stockid);
303
304 return wpixmap;
305 }
306
307
308 static void
x_window_find_text(GtkWidget * widget,gint response,GschemToplevel * w_current)309 x_window_find_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
310 {
311 gint close = FALSE;
312 int count;
313
314 g_return_if_fail (w_current != NULL);
315 g_return_if_fail (w_current->toplevel != NULL);
316
317 gboolean show_hidden_text =
318 gschem_toplevel_get_show_hidden_text (w_current);
319
320 switch (response) {
321 case GTK_RESPONSE_OK:
322 count = gschem_find_text_state_find (
323 w_current,
324 GSCHEM_FIND_TEXT_STATE (w_current->find_text_state),
325 lepton_list_get_glist (w_current->toplevel->pages),
326 gschem_find_text_widget_get_find_type (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
327 gschem_find_text_widget_get_find_text_string (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
328 gschem_find_text_widget_get_descend (GSCHEM_FIND_TEXT_WIDGET (w_current->find_text_widget)),
329 show_hidden_text);
330
331 if (count > 0)
332 {
333 x_widgets_show_find_text_state (w_current);
334 close = TRUE;
335 }
336
337 break;
338
339 case GTK_RESPONSE_CANCEL:
340 case GTK_RESPONSE_DELETE_EVENT:
341 close = TRUE;
342 break;
343
344 default:
345 printf("x_window_find_text(): strange signal %d\n", response);
346 }
347
348 if (close) {
349 gtk_widget_grab_focus (w_current->drawing_area);
350 gtk_widget_hide (GTK_WIDGET (widget));
351 }
352 }
353
354
355 static void
x_window_hide_text(GtkWidget * widget,gint response,GschemToplevel * w_current)356 x_window_hide_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
357 {
358 LeptonPage *page = NULL;
359
360 g_return_if_fail (w_current != NULL);
361
362 if (response == GTK_RESPONSE_OK) {
363 page = schematic_window_get_active_page (w_current);
364 o_edit_hide_specific_text (w_current,
365 lepton_page_objects (page),
366 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
367 }
368
369 gtk_widget_grab_focus (w_current->drawing_area);
370 gtk_widget_hide (GTK_WIDGET (widget));
371 }
372
373
374 static void
x_window_show_text(GtkWidget * widget,gint response,GschemToplevel * w_current)375 x_window_show_text (GtkWidget *widget, gint response, GschemToplevel *w_current)
376 {
377 LeptonPage *page = NULL;
378
379 g_return_if_fail (w_current != NULL);
380
381 if (response == GTK_RESPONSE_OK) {
382 page = schematic_window_get_active_page (w_current);
383 o_edit_show_specific_text (w_current,
384 lepton_page_objects (page),
385 gschem_show_hide_text_widget_get_text_string (GSCHEM_SHOW_HIDE_TEXT_WIDGET (widget)));
386 }
387
388 gtk_widget_grab_focus (w_current->drawing_area);
389 gtk_widget_hide (GTK_WIDGET (widget));
390 }
391
392
393 void
x_window_select_object(GschemFindTextState * state,LeptonObject * object,GschemToplevel * w_current)394 x_window_select_object (GschemFindTextState *state,
395 LeptonObject *object,
396 GschemToplevel *w_current)
397 {
398 GschemPageView *view = gschem_toplevel_get_current_page_view (w_current);
399 g_return_if_fail (view != NULL);
400
401 LeptonPage *page = gschem_page_view_get_page (view);
402 g_return_if_fail (page != NULL);
403
404 g_return_if_fail (object != NULL);
405 g_return_if_fail (object->page != NULL);
406
407 if (page != object->page)
408 {
409 /* open object's page: */
410 x_window_set_current_page (w_current, object->page);
411
412 /* tabbed GUI: current page view may be different here: */
413 view = gschem_toplevel_get_current_page_view (w_current);
414 }
415
416 gschem_page_view_zoom_object (view, object);
417 }
418
419 static void
x_window_translate_response(GschemTranslateWidget * widget,gint response,GschemToplevel * w_current)420 x_window_translate_response (GschemTranslateWidget *widget, gint response, GschemToplevel *w_current)
421 {
422 if (response == GTK_RESPONSE_OK) {
423 o_component_translate_all (w_current,
424 gschem_translate_widget_get_value (widget));
425 }
426
427 i_set_state (w_current, SELECT);
428 gtk_widget_grab_focus (w_current->drawing_area);
429 gtk_widget_hide (GTK_WIDGET (widget));
430 }
431
432
433
434 /*! \todo Finish function documentation!!!
435 * \brief
436 * \par Function Description
437 *
438 */
439 GschemToplevel*
x_window_create_main(GschemToplevel * w_current,GtkWidget * menubar)440 x_window_create_main (GschemToplevel *w_current, GtkWidget *menubar)
441 {
442 GtkWidget *main_box = NULL;
443 GtkWidget *hpaned = NULL;
444 GtkWidget *vpaned = NULL;
445 GtkWidget *work_box = NULL;
446 GtkWidget *scrolled = NULL;
447
448 w_current->main_window = GTK_WIDGET (gschem_main_window_new ());
449
450 gtk_widget_set_name (w_current->main_window, "lepton-schematic");
451 gtk_window_set_resizable (GTK_WINDOW (w_current->main_window), TRUE);
452
453 /* We want the widgets to flow around the drawing area, so we don't
454 * set a size of the main window. The drawing area's size is fixed,
455 * see below
456 */
457
458 /* this should work fine */
459 g_signal_connect (G_OBJECT (w_current->main_window), "delete_event",
460 G_CALLBACK (i_callback_close_wm),
461 w_current);
462
463
464 /*
465 * top level container:
466 */
467 #ifdef ENABLE_GTK3
468 main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
469 #else
470 main_box = gtk_vbox_new (FALSE, 1);
471 #endif
472 gtk_container_set_border_width (GTK_CONTAINER (main_box), 0);
473 gtk_container_add (GTK_CONTAINER (w_current->main_window), main_box);
474
475
476 /*
477 * main menu:
478 */
479 create_menubar (w_current, main_box, menubar);
480
481
482 /*
483 * toolbar:
484 */
485 create_toolbar (w_current, main_box);
486
487
488 /*
489 * popup menu:
490 */
491 w_current->popup_menu = (GtkWidget*) get_main_popup (w_current);
492
493
494 /*
495 * container for scrolled window and bottom infowidgets;
496 * when tabbed GUI is enabled, it will contain the notebook:
497 */
498 #ifdef ENABLE_GTK3
499 work_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
500 #else
501 work_box = gtk_vbox_new (FALSE, 0);
502 #endif
503
504 if (x_tabs_enabled())
505 {
506 x_tabs_create (w_current, work_box);
507 }
508 else
509 {
510 /* scrolled window (parent of page view): */
511 scrolled = gtk_scrolled_window_new (NULL, NULL);
512 gtk_container_add (GTK_CONTAINER (work_box), scrolled);
513
514 /* create page view: */
515 x_window_create_drawing (scrolled, w_current);
516 x_window_setup_scrolling (w_current, scrolled);
517
518 /* setup callbacks for draw events - page view: */
519 GschemPageView* pview = GSCHEM_PAGE_VIEW (w_current->drawing_area);
520 x_window_setup_draw_events_drawing_area (w_current, pview);
521 }
522
523
524 /* setup callbacks for draw events - main window: */
525 x_window_setup_draw_events_main_wnd (w_current, w_current->main_window);
526
527
528 /*
529 * hidden infowidgets:
530 */
531 create_find_text_widget (w_current, work_box);
532 create_hide_text_widget (w_current, work_box);
533 create_show_text_widget (w_current, work_box);
534 create_macro_widget (w_current, work_box);
535 create_translate_widget (w_current, work_box);
536
537
538 /*
539 * widgets:
540 */
541 x_widgets_init();
542 x_widgets_create (w_current);
543
544
545 /*
546 * windows layout:
547 */
548 #ifdef ENABLE_GTK3
549 vpaned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
550 hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
551 #else
552 vpaned = gtk_vpaned_new ();
553 hpaned = gtk_hpaned_new ();
554 #endif
555
556 w_current->right_notebook = create_notebook_right (w_current);
557 w_current->bottom_notebook = create_notebook_bottom (w_current);
558
559
560 gtk_container_add (GTK_CONTAINER (main_box), vpaned);
561
562
563 gtk_paned_pack1 (GTK_PANED (vpaned), hpaned,
564 TRUE, TRUE);
565
566 gtk_paned_pack2 (GTK_PANED (vpaned), w_current->bottom_notebook,
567 FALSE, TRUE);
568
569
570 gtk_paned_pack1 (GTK_PANED (hpaned), work_box,
571 TRUE, TRUE);
572
573 gtk_paned_pack2 (GTK_PANED (hpaned), w_current->right_notebook,
574 FALSE, TRUE);
575
576
577 /*
578 * status bar aka 'bottom widget':
579 */
580 create_bottom_widget (w_current, main_box);
581
582 geometry_restore (w_current);
583
584 /* show all widgets: */
585 gtk_widget_show_all (w_current->main_window);
586
587
588 if ( !x_widgets_use_docks() )
589 {
590 gtk_widget_set_visible (GTK_WIDGET (w_current->right_notebook), FALSE);
591 gtk_widget_set_visible (GTK_WIDGET (w_current->bottom_notebook), FALSE);
592 }
593
594
595 /* focus page view: */
596 gtk_widget_grab_focus (w_current->drawing_area);
597
598 return w_current;
599 } /* x_window_create_main() */
600
601
602
603 /*! \todo Finish function documentation!!!
604 * \brief
605 * \par Function Description
606 *
607 */
x_window_close(GschemToplevel * w_current)608 void x_window_close(GschemToplevel *w_current)
609 {
610 gboolean last_window = FALSE;
611
612 /* If we're closing whilst inside an action, re-wind the
613 * page contents back to their state before we started */
614 if (w_current->inside_action) {
615 i_callback_cancel (NULL, w_current);
616 }
617
618 /* last chance to save possible unsaved pages */
619 if (!x_dialog_close_window (w_current)) {
620 /* user somehow cancelled the close */
621 return;
622 }
623
624 x_clipboard_finish (w_current);
625
626 w_current->dont_invalidate = TRUE;
627
628 x_widgets_destroy_dialogs (w_current);
629
630 /* close all the dialog boxes */
631 if (w_current->cswindow)
632 gtk_widget_destroy(w_current->cswindow);
633
634 if (w_current->tiwindow)
635 gtk_widget_destroy(w_current->tiwindow);
636
637 if (w_current->aawindow)
638 gtk_widget_destroy(w_current->aawindow);
639
640 x_multiattrib_close (w_current);
641
642 if (w_current->aewindow)
643 gtk_widget_destroy(w_current->aewindow);
644
645 if (w_current->hkwindow)
646 gtk_widget_destroy(w_current->hkwindow);
647
648 if (w_current->cowindow)
649 gtk_widget_destroy(w_current->cowindow);
650
651 if (w_current->sewindow)
652 gtk_widget_destroy(w_current->sewindow);
653
654 if (g_list_length (global_window_list) == 1) {
655 /* no more window after this one, remember to quit */
656 last_window = TRUE;
657 }
658
659 if (last_window)
660 {
661 geometry_save (w_current);
662 }
663
664 /* stuff that has to be done before we free w_current */
665 if (last_window) {
666 /* close the log file */
667 s_log_close ();
668 /* free the buffers */
669 o_buffer_free (w_current);
670 }
671
672 /* Allow Scheme value for this window to be garbage-collected */
673 if (!scm_is_eq (w_current->smob, SCM_UNDEFINED)) {
674 scm_gc_unprotect_object (w_current->smob);
675 w_current->smob = SCM_UNDEFINED;
676 }
677
678 /* finally close the main window */
679 gtk_widget_destroy(w_current->main_window);
680
681 global_window_list = g_list_remove (global_window_list, w_current);
682 gschem_toplevel_free (w_current);
683
684 /* just closed last window, so quit */
685 if (last_window) {
686 gschem_quit();
687 }
688 }
689
690 /*! \todo Finish function documentation!!!
691 * \brief
692 * \par Function Description
693 *
694 */
x_window_close_all(GschemToplevel * w_current)695 void x_window_close_all(GschemToplevel *w_current)
696 {
697 GschemToplevel *current;
698 GList *list_copy, *iter;
699
700 iter = list_copy = g_list_copy (global_window_list);
701 while (iter != NULL ) {
702 current = (GschemToplevel *)iter->data;
703 iter = g_list_next (iter);
704 x_window_close (current);
705 }
706 g_list_free (list_copy);
707 }
708
709
710
711 /*! \brief Opens a new page from a file.
712 * \private
713 * \par Function Description
714 * This function opens the file whose name is <B>filename</B> in a
715 * new LeptonPage of <B>toplevel</B>.
716 *
717 * If there is no page for <B>filename</B> in <B>toplevel</B>'s
718 * list of pages, it creates a new LeptonPage, loads the file in
719 * it and returns a pointer on the new page. Otherwise it returns
720 * a pointer on the existing page.
721 *
722 * If the filename passed is NULL, this function creates an empty,
723 * untitled page. The name of the untitled page is build from
724 * configuration data ('untitled-name') and a counter for uniqueness.
725 *
726 * The opened page becomes the current page of <B>toplevel</B>.
727 *
728 * \param [in] w_current The toplevel environment.
729 * \param [in] filename The name of the file to open or NULL for a blank page.
730 * \returns A pointer on the new page.
731 *
732 * \bug This code should check to make sure any untitled filename
733 * does not conflict with a file on disk.
734 */
735 LeptonPage*
x_window_open_page_impl(GschemToplevel * w_current,const gchar * filename)736 x_window_open_page_impl (GschemToplevel *w_current,
737 const gchar *filename)
738 {
739 LeptonToplevel *toplevel = gschem_toplevel_get_toplevel (w_current);
740 g_return_val_if_fail (toplevel != NULL, NULL);
741
742 /* New blank page requested: */
743 if (filename == NULL)
744 return x_window_new_page (w_current);
745
746
747 /* Return existing page if it is already loaded: */
748 LeptonPage* page = lepton_toplevel_search_page (toplevel, filename);
749 if (page != NULL)
750 return page;
751
752
753 /* Create a new page: */
754 page = lepton_page_new (toplevel, filename);
755
756 /* Switch to a new page: */
757 lepton_toplevel_goto_page (toplevel, page); /* NOTE: sets current active page of toplevel */
758 gschem_toplevel_page_changed (w_current);
759
760 if (!quiet_mode)
761 g_message (_("Loading schematic [%1$s]"), filename);
762
763
764 /* Try to load [filename]: */
765 GError* err = NULL;
766 if (!schematic_file_open (w_current, page, filename, &err))
767 {
768 g_warning ("%s\n", err->message);
769 open_page_error_dialog (w_current, filename, err);
770 g_clear_error (&err);
771
772 /* Loading failed: delete page and open a blank one: */
773 lepton_page_delete (toplevel, page);
774 return x_window_new_page (w_current);
775 }
776
777
778 /* Run hook: */
779 g_run_hook_page (w_current, "%open-page-hook", page);
780
781 /* Add page file name to the recent file list: */
782 recent_manager_add (w_current, filename);
783
784 /* Save current state of the page: */
785 o_undo_savestate (w_current, page, UNDO_ALL);
786
787 return page;
788
789 } /* x_window_open_page_impl() */
790
791
792
793 /*! \brief Changes the current page.
794 * \private
795 * \par Function Description
796 * This function displays the specified page <B>page</B> in the
797 * window attached to <B>toplevel</B>.
798 *
799 * It changes the <B>toplevel</B>'s current page to <B>page</B>,
800 * draws it and updates the user interface.
801 *
802 * <B>page</B> has to be in the list of PAGEs attached to <B>toplevel</B>.
803 *
804 * \param [in] w_current The toplevel environment.
805 * \param [in] page The page to become current page.
806 */
807 void
x_window_set_current_page_impl(GschemToplevel * w_current,LeptonPage * page)808 x_window_set_current_page_impl (GschemToplevel *w_current,
809 LeptonPage *page)
810 {
811 GschemPageView *page_view = gschem_toplevel_get_current_page_view (w_current);
812 g_return_if_fail (page_view != NULL);
813
814 g_return_if_fail (page != NULL);
815
816 o_redraw_cleanstates (w_current);
817
818 gschem_page_view_set_page (page_view, page);
819
820 i_update_menus (w_current);
821 /* i_set_filename (w_current, page->page_filename); */
822
823 page_select_widget_update (w_current);
824 x_multiattrib_update (w_current);
825
826 } /* x_window_set_current_page_impl() */
827
828
829
830 /*! \brief Saves a page to a file.
831 * \par Function Description
832 * This function saves the page <B>page</B> to a file named
833 * <B>filename</B>.
834 *
835 * It returns the value returned by function <B>f_save()</B> trying
836 * to save page <B>page</B> to file <B>filename</B> (1 on success, 0
837 * on failure).
838 *
839 * \param [in] page The page to save.
840 * \param [in] filename The name of the file in which to save page.
841 * \returns 1 on success, 0 otherwise.
842 */
843 gint
x_window_save_page(GschemToplevel * w_current,LeptonPage * page,const gchar * filename)844 x_window_save_page (GschemToplevel *w_current,
845 LeptonPage *page,
846 const gchar *filename)
847 {
848 const gchar *log_msg, *state_msg;
849 gint ret;
850 GError *err = NULL;
851
852 g_return_val_if_fail (page != NULL, 0);
853 g_return_val_if_fail (filename != NULL, 0);
854
855 /* try saving page to filename */
856 ret = (gint)f_save (page, filename, &err);
857
858 if (ret != 1) {
859 log_msg = _("Could NOT save page [%1$s]\n");
860 state_msg = _("Error while trying to save");
861
862 GtkWidget *dialog;
863
864 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
865 GTK_DIALOG_DESTROY_WITH_PARENT,
866 GTK_MESSAGE_ERROR,
867 GTK_BUTTONS_CLOSE,
868 "%s",
869 err->message);
870 gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to save file"));
871 gtk_dialog_run (GTK_DIALOG (dialog));
872 gtk_widget_destroy (dialog);
873 g_clear_error (&err);
874 } else {
875 /* successful save of page to file, update page... */
876 /* change page name if necessary and prepare log message */
877 if (g_ascii_strcasecmp (lepton_page_get_filename (page), filename) != 0)
878 {
879 lepton_page_set_filename (page, filename);
880
881 log_msg = _("Saved as [%1$s]");
882 } else {
883 log_msg = _("Saved [%1$s]");
884 }
885 state_msg = _("Saved");
886
887 /* reset page CHANGED flag */
888 lepton_page_set_changed (page, 0);
889
890 /* add to recent file list */
891 recent_manager_add (w_current, filename);
892
893 page_select_widget_update (w_current);
894 }
895
896 /* log status of operation */
897 g_message (log_msg, filename);
898
899 i_set_state_msg (w_current, SELECT, state_msg);
900
901 return ret;
902
903 } /* x_window_save_page() */
904
905
906
907 /*! \brief Closes a page.
908 * \private
909 * \par Function Description
910 * This function closes the page <B>page</B> of toplevel
911 * <B>toplevel</B>.
912 *
913 * The current page of <B>toplevel</B> is changed to
914 * the next valid page.
915 * If necessary, a new untitled page is created
916 * (unless tabbed GUI is enabled: return NULL in that case).
917 *
918 * \param [in] w_current The toplevel environment.
919 * \param [in] page The page to close.
920 * \return Pointer to a new current LeptonPage object.
921 */
922 LeptonPage*
x_window_close_page_impl(GschemToplevel * w_current,LeptonPage * page)923 x_window_close_page_impl (GschemToplevel *w_current,
924 LeptonPage *page)
925 {
926 LeptonToplevel *toplevel = gschem_toplevel_get_toplevel (w_current);
927 LeptonPage *new_current = NULL;
928 GList *iter;
929
930 g_return_val_if_fail (toplevel != NULL, NULL);
931 g_return_val_if_fail (page != NULL, NULL);
932
933 g_assert (page->pid != -1);
934
935 /* If we're closing whilst inside an action, re-wind the
936 * page contents back to their state before we started */
937 if (w_current->inside_action) {
938 i_callback_cancel (NULL, w_current);
939 }
940
941 if (page == lepton_toplevel_get_page_current (toplevel))
942 {
943 /* as it will delete current page, select new current page */
944 /* first look up in page hierarchy */
945 new_current = lepton_toplevel_search_page_by_id (toplevel->pages, page->up);
946
947 if (new_current == NULL) {
948 /* no up in hierarchy, choice is prev, next, new page */
949 iter = g_list_find( lepton_list_get_glist( toplevel->pages ), page );
950
951 if ( g_list_previous( iter ) ) {
952 new_current = (LeptonPage *)g_list_previous( iter )->data;
953 } else if ( g_list_next( iter ) ) {
954 new_current = (LeptonPage *)g_list_next( iter )->data;
955 } else {
956 /* need to add a new untitled page */
957 new_current = NULL;
958 }
959 }
960 /* new_current will be the new current page at the end of the function */
961 }
962
963 g_message (lepton_page_get_changed (page) ?
964 _("Discarding page [%1$s]") : _("Closing [%1$s]"),
965 lepton_page_get_filename (page));
966 /* remove page from toplevel list of page and free */
967 lepton_page_delete (toplevel, page);
968 gschem_toplevel_page_changed (w_current);
969
970 /* Switch to a different page if we just removed the current */
971 if (lepton_toplevel_get_page_current (toplevel) == NULL)
972 {
973
974 /* Create a new page if there wasn't another to switch to */
975 if (new_current == NULL && !x_tabs_enabled())
976 {
977 new_current = x_window_open_page_impl (w_current, NULL);
978 }
979
980 /* change to new_current and update display */
981 if (!x_tabs_enabled())
982 {
983 x_window_set_current_page_impl (w_current, new_current);
984 }
985
986 }
987
988 return new_current;
989
990 } /* x_window_close_page_impl() */
991
992
993 /*! \brief Creates and initializes a new lepton-schematic window.
994 *
995 * \return Pointer to the new GschemToplevel object.
996 */
x_window_new()997 GschemToplevel* x_window_new ()
998 {
999 LeptonToplevel *toplevel = s_toplevel_new ();
1000
1001 /* Load old (*rc files) and new (*.conf) configuration: */
1002 x_rc_parse_gschem (toplevel, NULL);
1003
1004 GschemToplevel *w_current = gschem_toplevel_new ();
1005 gschem_toplevel_set_toplevel (w_current, toplevel);
1006
1007 /* Damage notifications should invalidate the object on screen */
1008 lepton_object_add_change_notify (toplevel,
1009 (ChangeNotifyFunc) o_invalidate,
1010 (ChangeNotifyFunc) o_invalidate,
1011 w_current);
1012
1013 /* Initialize tabbed GUI: */
1014 x_tabs_init();
1015
1016 return w_current;
1017 }
1018
1019
1020
1021 static void
create_menubar(GschemToplevel * w_current,GtkWidget * main_box,GtkWidget * menubar)1022 create_menubar (GschemToplevel *w_current, GtkWidget *main_box, GtkWidget *menubar)
1023 {
1024
1025 #ifdef ENABLE_GTK3
1026 gtk_box_pack_start (GTK_BOX (main_box), menubar, FALSE, FALSE, 0);
1027 #else
1028 if (w_current->handleboxes)
1029 {
1030 GtkWidget *handlebox = gtk_handle_box_new ();
1031 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
1032 gtk_container_add (GTK_CONTAINER (handlebox), menubar);
1033 }
1034 else
1035 {
1036 gtk_box_pack_start (GTK_BOX (main_box), menubar, FALSE, FALSE, 0);
1037 }
1038 #endif
1039
1040 w_current->menubar = menubar;
1041 }
1042
1043
1044
1045 static void
create_toolbar_button(GschemToplevel * w_current,GtkWidget * toolbar,const gchar * pixmap_name,const gchar * label,const gchar * tooltip,GCallback callback,gint pos)1046 create_toolbar_button (GschemToplevel *w_current,
1047 GtkWidget *toolbar,
1048 const gchar *pixmap_name,
1049 const gchar *label,
1050 const gchar *tooltip,
1051 GCallback callback,
1052 gint pos)
1053 {
1054 GtkWidget *pixmap = x_window_stock_pixmap (pixmap_name, w_current);
1055
1056 GtkToolButton *button = (GtkToolButton*) gtk_tool_button_new (pixmap, label);
1057
1058 gtk_widget_set_tooltip_text (GTK_WIDGET (button), tooltip);
1059
1060 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), pos);
1061
1062 g_signal_connect (button, "clicked", callback, w_current);
1063 }
1064
1065
1066
1067 static GtkWidget*
create_toolbar_radio_button(GSList ** group,GschemToplevel * w_current,GtkWidget * toolbar,const gchar * pixmap_name,const gchar * label,const gchar * tooltip,GCallback callback,gint pos)1068 create_toolbar_radio_button (GSList** group,
1069 GschemToplevel *w_current,
1070 GtkWidget *toolbar,
1071 const gchar *pixmap_name,
1072 const gchar *label,
1073 const gchar *tooltip,
1074 GCallback callback,
1075 gint pos)
1076 {
1077 GtkWidget *button = GTK_WIDGET (gtk_radio_tool_button_new (*group));
1078
1079 gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), label);
1080 gtk_widget_set_tooltip_text (GTK_WIDGET (button), tooltip);
1081
1082 GtkWidget *pixmap = x_window_stock_pixmap (pixmap_name, w_current);
1083 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (button), pixmap);
1084
1085 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), pos);
1086
1087 g_signal_connect (button, "toggled", callback, w_current);
1088
1089 *group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (button));
1090
1091 return button;
1092 }
1093
1094
1095
1096 static void
create_toolbar_separator(GtkWidget * toolbar,gint pos)1097 create_toolbar_separator (GtkWidget *toolbar, gint pos)
1098 {
1099 gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
1100 GTK_TOOL_ITEM (gtk_separator_tool_item_new ()),
1101 pos);
1102 }
1103
1104
1105
1106 static void
create_toolbar(GschemToplevel * w_current,GtkWidget * main_box)1107 create_toolbar( GschemToplevel *w_current, GtkWidget *main_box )
1108 {
1109 if (w_current->toolbars == 0)
1110 {
1111 return;
1112 }
1113
1114 GtkWidget *toolbar = gtk_toolbar_new ();
1115
1116 gtk_orientable_set_orientation (GTK_ORIENTABLE (toolbar),
1117 GTK_ORIENTATION_HORIZONTAL);
1118 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
1119
1120 #ifdef ENABLE_GTK3
1121 gtk_box_pack_start (GTK_BOX (main_box), toolbar, FALSE, FALSE, 0);
1122 #else
1123 if (w_current->handleboxes)
1124 {
1125 GtkWidget *handlebox = gtk_handle_box_new ();
1126 gtk_box_pack_start (GTK_BOX (main_box), handlebox, FALSE, FALSE, 0);
1127 gtk_container_add (GTK_CONTAINER (handlebox), toolbar);
1128 }
1129 else
1130 {
1131 gtk_box_pack_start (GTK_BOX (main_box), toolbar, FALSE, FALSE, 0);
1132 }
1133 #endif
1134
1135 create_toolbar_button (w_current, toolbar,
1136 "new", _("New"), _("New file"),
1137 G_CALLBACK (&i_callback_file_new), 0);
1138
1139 create_toolbar_button (w_current, toolbar,
1140 "open", _("Open"), _("Open file"),
1141 G_CALLBACK (&i_callback_file_open), 1);
1142
1143 create_toolbar_button (w_current, toolbar,
1144 "save", _("Save"), _("Save file"),
1145 G_CALLBACK (&i_callback_file_save), 2);
1146
1147 create_toolbar_separator (toolbar, 3);
1148
1149 create_toolbar_button (w_current, toolbar,
1150 "undo", _("Undo"), _("Undo last operation"),
1151 G_CALLBACK (&i_callback_edit_undo), 4);
1152
1153 create_toolbar_button (w_current, toolbar,
1154 "redo", _("Redo"), _("Redo last undo"),
1155 G_CALLBACK (&i_callback_edit_redo), 5);
1156
1157 create_toolbar_separator (toolbar, 6);
1158
1159 const gchar *text = _("Add component...\n"
1160 "Select library and component from list, move the mouse into main window, click to place\n"
1161 "Right mouse button to cancel");
1162
1163 create_toolbar_button (w_current, toolbar,
1164 "insert-symbol", _("Component"), text,
1165 G_CALLBACK (&i_callback_add_component), 7);
1166
1167
1168 GSList *radio_group = NULL;
1169
1170 text = _("Add nets mode\n"
1171 "Right mouse button to cancel");
1172
1173 w_current->toolbar_net =
1174 create_toolbar_radio_button (&radio_group, w_current, toolbar,
1175 "insert-net", _("Nets"), text,
1176 G_CALLBACK (&i_callback_toolbar_add_net), 8);
1177
1178 text = _("Add buses mode\n"
1179 "Right mouse button to cancel");
1180
1181 w_current->toolbar_bus =
1182 create_toolbar_radio_button (&radio_group, w_current, toolbar,
1183 "insert-bus", _("Bus"), text,
1184 G_CALLBACK (&i_callback_toolbar_add_bus), 9);
1185
1186 create_toolbar_button (w_current, toolbar,
1187 "insert-text", _("Text"), _("Add Text..."),
1188 G_CALLBACK (&i_callback_add_text), 10);
1189
1190 create_toolbar_separator (toolbar, 11);
1191
1192 w_current->toolbar_select =
1193 create_toolbar_radio_button (&radio_group, w_current, toolbar,
1194 "select", _("Select"), _("Select mode"),
1195 G_CALLBACK (&i_callback_toolbar_edit_select), 12);
1196
1197 create_toolbar_separator (toolbar, 13);
1198
1199
1200 /* activate 'select' button at start-up */
1201 gtk_toggle_tool_button_set_active(
1202 GTK_TOGGLE_TOOL_BUTTON (w_current->toolbar_select), TRUE);
1203
1204 } /* create_toolbar() */
1205
1206
1207
1208 static void
create_find_text_widget(GschemToplevel * w_current,GtkWidget * work_box)1209 create_find_text_widget (GschemToplevel *w_current, GtkWidget *work_box)
1210 {
1211 gpointer obj = g_object_new (GSCHEM_TYPE_FIND_TEXT_WIDGET, NULL);
1212
1213 w_current->find_text_widget = GTK_WIDGET (obj);
1214
1215 gtk_box_pack_start (GTK_BOX (work_box),
1216 w_current->find_text_widget,
1217 FALSE, FALSE, 0);
1218
1219 g_signal_connect (w_current->find_text_widget, "response",
1220 G_CALLBACK (&x_window_find_text), w_current);
1221 }
1222
1223
1224
1225 static void
create_hide_text_widget(GschemToplevel * w_current,GtkWidget * work_box)1226 create_hide_text_widget (GschemToplevel *w_current, GtkWidget *work_box)
1227 {
1228 gpointer obj = g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
1229 "button-text", _("Hide"),
1230 "label-text", _("Hide text starting with:"),
1231 NULL);
1232
1233 w_current->hide_text_widget = GTK_WIDGET (obj);
1234
1235 gtk_box_pack_start (GTK_BOX (work_box),
1236 w_current->hide_text_widget,
1237 FALSE, FALSE, 0);
1238
1239 g_signal_connect (w_current->hide_text_widget, "response",
1240 G_CALLBACK (&x_window_hide_text), w_current);
1241 }
1242
1243
1244
1245 static void
create_show_text_widget(GschemToplevel * w_current,GtkWidget * work_box)1246 create_show_text_widget (GschemToplevel *w_current, GtkWidget *work_box)
1247 {
1248 gpointer obj = g_object_new (GSCHEM_TYPE_SHOW_HIDE_TEXT_WIDGET,
1249 "button-text", _("Show"),
1250 "label-text", _("Show text starting with:"),
1251 NULL);
1252
1253 w_current->show_text_widget = GTK_WIDGET (obj);
1254
1255 gtk_box_pack_start (GTK_BOX (work_box),
1256 w_current->show_text_widget,
1257 FALSE, FALSE, 0);
1258
1259 g_signal_connect (w_current->show_text_widget, "response",
1260 G_CALLBACK (&x_window_show_text), w_current);
1261 }
1262
1263
1264
1265 static void
create_macro_widget(GschemToplevel * w_current,GtkWidget * work_box)1266 create_macro_widget (GschemToplevel *w_current, GtkWidget *work_box)
1267 {
1268 w_current->macro_widget = macro_widget_new (w_current);
1269
1270 gtk_box_pack_start (GTK_BOX (work_box),
1271 w_current->macro_widget,
1272 FALSE, FALSE, 0);
1273 }
1274
1275
1276
1277 static void
create_translate_widget(GschemToplevel * w_current,GtkWidget * work_box)1278 create_translate_widget (GschemToplevel *w_current, GtkWidget *work_box)
1279 {
1280 gpointer obj = g_object_new (GSCHEM_TYPE_TRANSLATE_WIDGET, NULL);
1281
1282 w_current->translate_widget = GTK_WIDGET (obj);
1283
1284 gtk_box_pack_start( GTK_BOX (work_box),
1285 w_current->translate_widget,
1286 FALSE, FALSE, 0 );
1287
1288 g_signal_connect (w_current->translate_widget, "response",
1289 G_CALLBACK (&x_window_translate_response), w_current);
1290 }
1291
1292
1293
1294 static void
create_bottom_widget(GschemToplevel * w_current,GtkWidget * main_box)1295 create_bottom_widget (GschemToplevel *w_current, GtkWidget *main_box)
1296 {
1297 const char* text_mid_button = _("none");
1298
1299 #ifdef HAVE_LIBSTROKE
1300 if (w_current->middle_button == MOUSEBTN_DO_STROKE)
1301 text_mid_button = _("Stroke");
1302 #endif
1303
1304 if (w_current->middle_button == MOUSEBTN_DO_ACTION)
1305 text_mid_button = _("Action");
1306 else
1307 if (w_current->middle_button == MOUSEBTN_DO_REPEAT)
1308 text_mid_button = _("Repeat");
1309 else
1310 if (w_current->middle_button == MOUSEBTN_DO_PAN)
1311 text_mid_button = _("Pan");
1312 else
1313 if (w_current->middle_button == MOUSEBTN_DO_POPUP)
1314 text_mid_button = _("Menu");
1315
1316
1317 const char* text_right_button_action = NULL;
1318 const char* text_right_button_cancel = NULL;
1319 char* text_right_button = NULL;
1320
1321 if (w_current->third_button == MOUSEBTN_DO_POPUP)
1322 {
1323 text_right_button_action = _("Menu");
1324 }
1325 else
1326 if (w_current->third_button == MOUSEBTN_DO_PAN)
1327 {
1328 text_right_button_action = _("Pan");
1329 }
1330 else
1331 {
1332 text_right_button_action = _("none");
1333 }
1334
1335 if (w_current->third_button_cancel)
1336 {
1337 text_right_button_cancel = _("/Cancel");
1338 }
1339 else
1340 {
1341 text_right_button_cancel = "";
1342 }
1343
1344 text_right_button = g_strdup_printf ("%s%s",
1345 text_right_button_action,
1346 text_right_button_cancel);
1347
1348 gpointer obj = g_object_new (GSCHEM_TYPE_BOTTOM_WIDGET,
1349 "toplevel",
1350 w_current,
1351 "grid-mode",
1352 gschem_options_get_grid_mode (w_current->options),
1353 "grid-size",
1354 gschem_options_get_snap_size (w_current->options),
1355 /* x_grid_query_drawn_spacing (w_current), -- occurs before the page is set */
1356 "left-button-text",
1357 _("Pick"),
1358 "middle-button-text",
1359 text_mid_button,
1360 "right-button-text",
1361 text_right_button,
1362 "snap-mode",
1363 gschem_options_get_snap_mode (w_current->options),
1364 "snap-size",
1365 gschem_options_get_snap_size (w_current->options),
1366 "status-text",
1367 _("Select Mode"),
1368 "net-rubber-band-mode",
1369 gschem_options_get_net_rubber_band_mode (w_current->options),
1370 "magnetic-net-mode",
1371 gschem_options_get_magnetic_net_mode (w_current->options),
1372 NULL);
1373
1374 g_free (text_right_button);
1375
1376 w_current->bottom_widget = GTK_WIDGET (obj);
1377
1378 gtk_box_pack_start (GTK_BOX (main_box),
1379 w_current->bottom_widget,
1380 FALSE, FALSE, 0);
1381
1382 } /* create_bottom_widget */
1383
1384
1385
1386 /*! \brief Setup scrolling parameters
1387 *
1388 * \param [in] scrolled Scrolled widget - a parent of page view widget
1389 */
1390 void
x_window_setup_scrolling(GschemToplevel * w_current,GtkWidget * scrolled)1391 x_window_setup_scrolling (GschemToplevel *w_current, GtkWidget *scrolled)
1392 {
1393 GtkAdjustment *hadjustment = GTK_ADJUSTMENT(
1394 gtk_adjustment_new (0.0,
1395 WORLD_DEFAULT_LEFT,
1396 WORLD_DEFAULT_RIGHT,
1397 100.0,
1398 100.0,
1399 10.0));
1400
1401 GtkAdjustment *vadjustment = GTK_ADJUSTMENT(
1402 gtk_adjustment_new (WORLD_DEFAULT_BOTTOM,
1403 0.0,
1404 WORLD_DEFAULT_BOTTOM - WORLD_DEFAULT_TOP,
1405 100.0,
1406 100.0,
1407 10.0));
1408
1409 gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (scrolled), hadjustment);
1410 gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (scrolled), vadjustment);
1411
1412 GtkPolicyType policy = w_current->scrollbars_flag ?
1413 GTK_POLICY_ALWAYS :
1414 GTK_POLICY_NEVER;
1415
1416 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
1417 policy, policy);
1418
1419 } /* x_window_setup_scrolling() */
1420
1421
1422
1423 static GtkWidget*
create_notebook_right(GschemToplevel * w_current)1424 create_notebook_right (GschemToplevel* w_current)
1425 {
1426 GtkWidget *notebook = gtk_notebook_new ();
1427
1428 if ( x_widgets_use_docks() )
1429 {
1430 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1431 GTK_WIDGET (w_current->object_properties),
1432 gtk_label_new(_("Object")));
1433
1434 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1435 GTK_WIDGET (w_current->text_properties),
1436 gtk_label_new(_("Text")));
1437
1438 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1439 GTK_WIDGET (w_current->options_widget),
1440 gtk_label_new(_("Options")));
1441
1442
1443
1444 gtk_container_set_border_width (GTK_CONTAINER (notebook),
1445 DIALOG_BORDER_SPACING);
1446 }
1447
1448 return notebook;
1449 }
1450
1451
1452
1453 static GtkWidget*
create_notebook_bottom(GschemToplevel * w_current)1454 create_notebook_bottom (GschemToplevel* w_current)
1455 {
1456 GtkWidget *notebook = gtk_notebook_new ();
1457
1458 if ( x_widgets_use_docks() )
1459 {
1460 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1461 GTK_WIDGET (w_current->find_text_state),
1462 gtk_label_new(_("Find Text")));
1463
1464 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1465 GTK_WIDGET (w_current->log_widget),
1466 gtk_label_new(_("Log")));
1467
1468
1469 gtk_container_set_border_width (GTK_CONTAINER (notebook),
1470 DIALOG_BORDER_SPACING);
1471 }
1472
1473 return notebook;
1474 }
1475
1476
1477
1478 /*! \brief Opens a new page from a file or a blank one if \a filename is NULL.
1479 *
1480 * \see x_window_open_page_impl()
1481 * \see x_tabs_page_open()
1482 */
1483 LeptonPage*
x_window_open_page(GschemToplevel * w_current,const gchar * filename)1484 x_window_open_page (GschemToplevel* w_current, const gchar* filename)
1485 {
1486 LeptonPage* page = NULL;
1487
1488 if (x_tabs_enabled())
1489 {
1490 page = x_tabs_page_open (w_current, filename);
1491 }
1492 else
1493 {
1494 page = x_window_open_page_impl (w_current, filename);
1495 }
1496
1497 if (filename != NULL && page != NULL)
1498 {
1499 /* check for symbol version changes, display
1500 * an error dialog box, if necessary:
1501 */
1502 major_changed_dialog (w_current);
1503 }
1504
1505 return page;
1506 }
1507
1508
1509
1510 /*! \brief Changes the current page.
1511 *
1512 * \see x_window_set_current_page_impl()
1513 * \see x_tabs_page_set_cur()
1514 */
1515 void
x_window_set_current_page(GschemToplevel * w_current,LeptonPage * page)1516 x_window_set_current_page (GschemToplevel* w_current,
1517 LeptonPage* page)
1518 {
1519 if (x_tabs_enabled())
1520 {
1521 x_tabs_page_set_cur (w_current, page);
1522 }
1523 else
1524 {
1525 x_window_set_current_page_impl (w_current, page);
1526 }
1527 }
1528
1529
1530
1531 /*! \brief Closes a page.
1532 *
1533 * \see x_window_close_page_impl()
1534 * \see x_tabs_page_close()
1535 */
1536 void
x_window_close_page(GschemToplevel * w_current,LeptonPage * page)1537 x_window_close_page (GschemToplevel* w_current,
1538 LeptonPage* page)
1539 {
1540 if (x_tabs_enabled())
1541 {
1542 x_tabs_page_close (w_current, page);
1543 }
1544 else
1545 {
1546 x_window_close_page_impl (w_current, page);
1547 }
1548 }
1549
1550
1551
1552 /*! \brief Create new blank page.
1553 *
1554 * \todo Do further refactoring: this function should be used
1555 * instead of x_window_open_page() when a new page is reqested.
1556 *
1557 * \param w_current The toplevel environment.
1558 */
1559 static LeptonPage*
x_window_new_page(GschemToplevel * w_current)1560 x_window_new_page (GschemToplevel* w_current)
1561 {
1562 g_return_val_if_fail (w_current != NULL, NULL);
1563
1564 LeptonToplevel* toplevel = gschem_toplevel_get_toplevel (w_current);
1565 g_return_val_if_fail (toplevel != NULL, NULL);
1566
1567 /* New page file name: */
1568 gchar* filename = untitled_filename (w_current, TRUE);
1569
1570 /* Create a new page: */
1571 LeptonPage* page = lepton_page_new (toplevel, filename);
1572
1573 /* Switch to a new page: */
1574 lepton_toplevel_goto_page (toplevel, page);
1575 gschem_toplevel_page_changed (w_current);
1576
1577 if (!quiet_mode)
1578 g_message (_("New file [%s]"), filename);
1579
1580 g_free (filename);
1581
1582 /* Run hook: */
1583 g_run_hook_page (w_current, "%new-page-hook", page);
1584
1585 /* Save current state of the page: */
1586 o_undo_savestate (w_current, page, UNDO_ALL);
1587
1588 return page;
1589
1590 } /* x_window_new_page() */
1591
1592
1593
1594 /*! \brief Show "Failed to load file" dialog.
1595 *
1596 * \param w_current The toplevel environment.
1597 * \param filename File path that failed to load.
1598 * \param err Associated GError.
1599 */
1600 static void
open_page_error_dialog(GschemToplevel * w_current,const gchar * filename,GError * err)1601 open_page_error_dialog (GschemToplevel* w_current,
1602 const gchar* filename,
1603 GError* err)
1604 {
1605 g_return_if_fail (w_current != NULL);
1606
1607 const gchar* msg =
1608 _("<b>An error occurred while loading the requested file.</b>"
1609 "\n\n"
1610 "Loading from '%1$s' failed. Error message:"
1611 "\n\n"
1612 "%2$s."
1613 "\n\n"
1614 "The lepton-schematic log may contain more information.\n"
1615 "You may also launch lepton-schematic with --verbose command"
1616 " line switch and monitor program's output in terminal window.");
1617
1618 GtkWidget* dialog = gtk_message_dialog_new_with_markup
1619 (GTK_WINDOW (w_current->main_window),
1620 GTK_DIALOG_DESTROY_WITH_PARENT,
1621 GTK_MESSAGE_ERROR,
1622 GTK_BUTTONS_CLOSE,
1623 msg,
1624 filename,
1625 err != NULL ? err->message : "");
1626
1627 gtk_window_set_title (GTK_WINDOW (dialog), _("Failed to load file"));
1628
1629 gtk_dialog_run (GTK_DIALOG (dialog));
1630 gtk_widget_destroy (dialog);
1631
1632 } /* open_page_error_dialog() */
1633
1634
1635
1636 /*! \brief Add \a filename to the recent files list.
1637 *
1638 * \todo gtk_recent_manager_add_item() also used in x_menus.c.
1639 * Consider making this function public.
1640 *
1641 * \param w_current The toplevel environment.
1642 * \param filename File name to add.
1643 */
1644 static void
recent_manager_add(GschemToplevel * w_current,const gchar * filename)1645 recent_manager_add (GschemToplevel* w_current,
1646 const gchar* filename)
1647 {
1648 g_return_if_fail (w_current != NULL);
1649
1650 GtkRecentManager* manager = w_current->recent_manager;
1651 if (manager != NULL)
1652 {
1653 gchar* uri = g_filename_to_uri (filename, NULL, NULL);
1654 gtk_recent_manager_add_item (manager, uri);
1655 g_free (uri);
1656 }
1657
1658 } /* recent_manager_add() */
1659
1660
1661
1662 /*! \brief Get next number to be part of the untitled file name.
1663 */
1664 static int
untitled_next_index(GschemToplevel * w_current)1665 untitled_next_index (GschemToplevel* w_current)
1666 {
1667 return ++w_current->num_untitled;
1668 }
1669
1670
1671
1672 /*! \brief Get untitled file name.
1673 * \par Function Description
1674 *
1675 * Determine "untitled" schematic file name (used for new pages)
1676 * and build full path from this name and current working directory.
1677 * When constructing this name, avoid reusing names of already opened
1678 * files and existing files in current directory; if \a log_skipped
1679 * is TRUE, report such (avoided) names to the log.
1680 *
1681 * \param w_current The toplevel environment.
1682 * \param log_skipped Print skipped file names to the log.
1683 * \return Newly-allocated untitled file path.
1684 */
1685 static gchar*
untitled_filename(GschemToplevel * w_current,gboolean log_skipped)1686 untitled_filename (GschemToplevel* w_current, gboolean log_skipped)
1687 {
1688 g_return_val_if_fail (w_current != NULL, NULL);
1689
1690 /* Determine default file name (without a number appended)
1691 * for a new page:
1692 */
1693 gchar* cwd = g_get_current_dir ();
1694 EdaConfig* cfg = eda_config_get_context_for_path (cwd);
1695
1696 gchar* name = eda_config_get_string (cfg,
1697 "schematic",
1698 "default-filename",
1699 NULL);
1700
1701 gchar* fname = NULL;
1702 gchar* fpath = NULL;
1703
1704 LeptonToplevel* toplevel = gschem_toplevel_get_toplevel (w_current);
1705
1706 for (;;)
1707 {
1708 /* Build file name (default name + number appended):
1709 */
1710 fname = g_strdup_printf ("%s_%d.sch",
1711 name ? name : UNTITLED_FILENAME_PREFIX,
1712 untitled_next_index (w_current));
1713
1714 /* Build full path for file name:
1715 */
1716 fpath = g_build_filename (cwd, fname, NULL);
1717
1718 /* Avoid reusing names of already opened files:
1719 * Avoid reusing names of existing files in current directory:
1720 */
1721 if ( lepton_toplevel_search_page_by_basename (toplevel, fname) ||
1722 g_file_test (fpath, G_FILE_TEST_EXISTS) )
1723 {
1724 if (log_skipped)
1725 {
1726 g_message (_("Skipping existing file [%s]"), fname);
1727 }
1728
1729 g_free (fname);
1730 g_free (fpath);
1731 }
1732 else
1733 {
1734 break;
1735 }
1736 }
1737
1738 g_free (cwd);
1739 g_free (name);
1740 g_free (fname);
1741
1742 return fpath;
1743
1744 } /* untitled_filename() */
1745
1746
1747
1748 /*! \brief Determine if a given \a page is "untitled" one.
1749 * \par Function Description
1750 *
1751 *"Untitled" pages are newly created pages with the default
1752 * file name and not yet saved to disk.
1753 * This function check if a \a page meets these conditions.
1754 *
1755 * \param w_current Page to check.
1756 * \return TRUE if a \a page looks like "untitled" one.
1757 */
1758 gboolean
x_window_untitled_page(LeptonPage * page)1759 x_window_untitled_page (LeptonPage* page)
1760 {
1761 g_return_val_if_fail (page != NULL, TRUE);
1762
1763 const gchar* fname = lepton_page_get_filename (page);
1764 gchar* uname = NULL;
1765
1766 EdaConfig* cfg = eda_config_get_context_for_path (fname);
1767 if (cfg != NULL)
1768 {
1769 uname = eda_config_get_string (cfg,
1770 "schematic",
1771 "default-filename",
1772 NULL);
1773 }
1774
1775 if (uname == NULL)
1776 {
1777 uname = g_strdup (UNTITLED_FILENAME_PREFIX);
1778 }
1779
1780 gboolean named_like_untitled = strstr (fname, uname) != NULL;
1781 gboolean file_exists = g_file_test (fname, G_FILE_TEST_EXISTS);
1782
1783 g_free (uname);
1784
1785 /*
1786 * consider page as "untitled" if it is named like untitled
1787 * and associated file does not exist:
1788 */
1789 return named_like_untitled && !file_exists;
1790
1791 } /* untitled_page() */
1792
1793
1794
1795 /*! \brief Save main window's geometry to the CACHE config context.
1796 *
1797 * \param w_current The toplevel environment.
1798 */
1799 static void
geometry_save(GschemToplevel * w_current)1800 geometry_save (GschemToplevel* w_current)
1801 {
1802 gint x = 0;
1803 gint y = 0;
1804 gtk_window_get_position (GTK_WINDOW (w_current->main_window), &x, &y);
1805
1806 gint width = 0;
1807 gint height = 0;
1808 gtk_window_get_size (GTK_WINDOW (w_current->main_window), &width, &height);
1809
1810 EdaConfig* cfg = eda_config_get_cache_context();
1811
1812 eda_config_set_int (cfg, "schematic.window-geometry", "x", x);
1813 eda_config_set_int (cfg, "schematic.window-geometry", "y", y);
1814 eda_config_set_int (cfg, "schematic.window-geometry", "width", width);
1815 eda_config_set_int (cfg, "schematic.window-geometry", "height", height);
1816
1817 eda_config_save (cfg, NULL);
1818 }
1819
1820
1821
1822 /*! \brief Restore main window's geometry.
1823 *
1824 * \par Function Description
1825 * If [schematic.gui]::restore-window-geometry configuration key is
1826 * set to true, read main window's geometry from the CACHE config
1827 * context and restore it.
1828 * Unless valid configuration values are read, use default width
1829 * and height.
1830 *
1831 * \param w_current The toplevel environment.
1832 */
1833 static void
geometry_restore(GschemToplevel * w_current)1834 geometry_restore (GschemToplevel* w_current)
1835 {
1836 gchar* cwd = g_get_current_dir();
1837 EdaConfig* cfg = eda_config_get_context_for_path (cwd);
1838 g_free (cwd);
1839
1840 gboolean restore = TRUE; /* by default, restore geometry */
1841 GError* err = NULL;
1842 gboolean val = eda_config_get_boolean (cfg,
1843 "schematic.gui",
1844 "restore-window-geometry",
1845 &err);
1846 if (err == NULL)
1847 {
1848 restore = val;
1849 }
1850
1851 g_clear_error (&err);
1852
1853
1854 gint width = -1;
1855 gint height = -1;
1856
1857 if (restore)
1858 {
1859 EdaConfig* ccfg = eda_config_get_cache_context();
1860
1861 gint x = eda_config_get_int (ccfg, "schematic.window-geometry", "x", NULL);
1862 gint y = eda_config_get_int (ccfg, "schematic.window-geometry", "y", NULL);
1863
1864 if (x > 0 && y > 0)
1865 {
1866 gtk_window_move (GTK_WINDOW (w_current->main_window), x, y);
1867 }
1868
1869 width = eda_config_get_int (ccfg, "schematic.window-geometry", "width", NULL);
1870 height = eda_config_get_int (ccfg, "schematic.window-geometry", "height", NULL);
1871 }
1872
1873
1874 if (width <= 0 || height <= 0)
1875 {
1876 width = default_width;
1877 height = default_height;
1878 }
1879
1880 gtk_window_resize (GTK_WINDOW (w_current->main_window), width, height);
1881
1882
1883 if (x_widgets_use_docks())
1884 {
1885 gtk_widget_set_size_request (w_current->find_text_state,
1886 -1,
1887 height / 4);
1888 }
1889 }
1890