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