1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /*! \file x_lowlevel.c
22 * \brief Low-level page-related functions.
24 * These functions don't usually interact with the user. In
25 * particular, they don't switch pages and don't ask for confirmation
26 * before carrying out potentially destructive actions. However, they
27 * *do* warn if an error occurred during an operation.
29 * For user-interface actions, the functions in x_highlevel.c should
35 #include "actions.decl.x"
38 /*! \brief Create a new untitled page with a titleblock.
40 * If \a filename is \c NULL, the name of the new page is build from
41 * configuration data ('untitled-name') and a counter for uniqueness.
43 * This function doesn't change the current page of \a w_current.
45 * \param [in] w_current the toplevel environment
46 * \param [in] filename the filename for the new page, or \c NULL to
47 * generate an untitled filename
49 * \returns a pointer to the new page, or \c NULL if a file with the
50 * specified filename already exists
53 x_lowlevel_new_page (GschemToplevel
*w_current
, const gchar
*filename
)
58 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
59 g_return_val_if_fail (toplevel
!= NULL
, NULL
);
61 if (filename
== NULL
) {
62 /* Generate untitled filename if none was specified */
63 gchar
*cwd
, *tmp
, *untitled_name
;
65 cwd
= g_get_current_dir ();
66 cfg
= eda_config_get_context_for_path (cwd
);
67 untitled_name
= eda_config_get_string (cfg
, "gschem", "default-filename",
72 tmp
= g_strdup_printf ("%s_%d.sch", untitled_name
,
73 ++w_current
->num_untitled
);
74 fn
= g_build_filename (cwd
, tmp
, NULL
);
76 } while (g_file_test (fn
, G_FILE_TEST_EXISTS
));
77 g_free (untitled_name
);
80 if (g_file_test (filename
, G_FILE_TEST_EXISTS
)) {
81 GtkWidget
*dialog
= gtk_message_dialog_new_with_markup (
82 GTK_WINDOW (w_current
->main_window
),
83 GTK_DIALOG_DESTROY_WITH_PARENT
,
86 _("<b>Can't create \"%s\".</b>"),
88 g_object_set (dialog
, "secondary-text",
89 _("A file with this name already exists."), NULL
);
90 gtk_window_set_title (GTK_WINDOW (dialog
), _("gschem"));
91 gtk_dialog_run (GTK_DIALOG (dialog
));
92 gtk_widget_destroy (dialog
);
95 fn
= g_strdup (filename
);
98 PAGE
*saved_page
= toplevel
->page_current
;
100 page
= s_page_new (toplevel
, fn
);
101 page
->is_untitled
= filename
== NULL
;
102 s_page_goto (toplevel
, page
);
103 gschem_toplevel_page_changed (w_current
);
105 /* print a message */
107 s_log_message (_("New file [%s]\n"),
108 toplevel
->page_current
->page_filename
);
110 g_run_hook_page (w_current
, "%new-page-hook", toplevel
->page_current
);
112 o_undo_savestate (w_current
, toplevel
->page_current
, UNDO_ALL
, NULL
);
116 if (saved_page
!= NULL
) {
117 s_page_goto (toplevel
, saved_page
);
118 gschem_toplevel_page_changed (w_current
);
121 x_pagesel_update (w_current
);
126 /*! \brief Open a new page from a file, or find an existing page.
128 * Creates a new page and loads the file in it. If there is already a
129 * matching page in \a w_current, returns a pointer to the existing
132 * This function doesn't change the current page of \a w_current.
134 * \param [in] w_current the toplevel environment
135 * \param [in] filename the name of the file to open
137 * \returns a pointer to the page, or \c NULL if the file couldn't be
141 x_lowlevel_open_page (GschemToplevel
*w_current
, const gchar
*filename
)
143 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
144 g_return_val_if_fail (toplevel
!= NULL
, NULL
);
145 g_return_val_if_fail (filename
!= NULL
, NULL
);
147 gchar
*full_filename
;
152 /* Return existing page if it is already loaded */
153 full_filename
= f_normalize_filename (filename
, NULL
);
154 page
= s_page_search (toplevel
, full_filename
);
155 g_free (full_filename
);
157 gtk_recent_manager_add_item (
158 recent_manager
, g_filename_to_uri (page
->page_filename
, NULL
, NULL
));
162 PAGE
*saved_page
= toplevel
->page_current
;
164 page
= s_page_new (toplevel
, filename
);
165 s_page_goto (toplevel
, page
);
166 gschem_toplevel_page_changed (w_current
);
171 s_log_message (_("Loading schematic [%s]\n"), filename
);
173 if (!f_open (toplevel
, page
, (gchar
*) filename
, &err
)) {
176 g_warning ("%s\n", err
->message
);
177 dialog
= gtk_message_dialog_new_with_markup (
178 GTK_WINDOW (w_current
->main_window
),
179 GTK_DIALOG_DESTROY_WITH_PARENT
,
182 _("<b>An error occurred while loading the requested file.</b>\n\n"
183 "Loading from '%s' failed: %s. "
184 "The gschem log may contain more information."),
185 page
->page_filename
, err
->message
);
186 gtk_window_set_title (GTK_WINDOW (dialog
), _("Failed to load file"));
188 s_page_delete (toplevel
, page
);
189 if (saved_page
!= NULL
)
190 s_page_goto (toplevel
, saved_page
);
191 gschem_toplevel_page_changed (w_current
);
193 gtk_dialog_run (GTK_DIALOG (dialog
));
194 gtk_widget_destroy (dialog
);
200 furi
= g_filename_to_uri (page
->page_filename
, NULL
, NULL
);
201 gtk_recent_manager_add_item (recent_manager
, furi
);
204 o_undo_savestate (w_current
, toplevel
->page_current
, UNDO_ALL
, NULL
);
206 if (saved_page
!= NULL
) {
207 s_page_goto (toplevel
, saved_page
);
208 gschem_toplevel_page_changed (w_current
);
211 x_pagesel_update (w_current
);
216 /*! \brief Save a page to a given filename.
218 * \a page doesn't have to be the current page of \a w_current.
219 * This function doesn't change the current page of \a w_current.
221 * If \a filename is different from the current filename of \a page,
222 * the page's filename is updated.
224 * \param [in] w_current the toplevel environment
225 * \param [in] page the page to save
226 * \param [in] filename the name of the file to which to save the page
228 * \returns \c TRUE if the page could be saved, \c FALSE otherwise
231 x_lowlevel_save_page (GschemToplevel
*w_current
, PAGE
*page
,
232 const gchar
*filename
)
234 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
235 const gchar
*log_msg
, *state_msg
;
239 g_return_val_if_fail (toplevel
!= NULL
, FALSE
);
240 g_return_val_if_fail (page
!= NULL
, FALSE
);
241 g_return_val_if_fail (filename
!= NULL
, FALSE
);
243 /* try saving page to filename */
244 success
= f_save (toplevel
, page
, filename
, &err
) == 1;
247 log_msg
= _("Could NOT save page [%s]\n");
248 state_msg
= _("Error while trying to save");
252 dialog
= gtk_message_dialog_new (GTK_WINDOW (w_current
->main_window
),
253 GTK_DIALOG_DESTROY_WITH_PARENT
,
258 gtk_window_set_title (GTK_WINDOW (dialog
), _("Failed to save file"));
259 gtk_dialog_run (GTK_DIALOG (dialog
));
260 gtk_widget_destroy (dialog
);
261 g_clear_error (&err
);
263 /* successful save of page to file, update page... */
264 /* change page name if necessary and prepare log message */
265 if (g_ascii_strcasecmp (page
->page_filename
, filename
) != 0) {
266 g_free (page
->page_filename
);
267 page
->page_filename
= g_strdup (filename
);
269 log_msg
= _("Saved as [%s]\n");
271 log_msg
= _("Saved [%s]\n");
273 state_msg
= _("Saved");
275 page
->is_untitled
= FALSE
;
277 /* reset page CHANGED flag */
280 /* add to recent file list */
281 gtk_recent_manager_add_item (recent_manager
,
282 g_filename_to_uri (filename
, NULL
, NULL
));
284 /* i_set_filename (w_current, page->page_filename); */
285 x_pagesel_update (w_current
);
287 if (page
== gschem_toplevel_get_toplevel (w_current
)->page_current
) {
288 gschem_action_set_sensitive (action_page_revert
, TRUE
, w_current
);
289 /* if the file change notification bar was visible, hide it */
290 x_window_update_file_change_notification (w_current
, page
);
294 /* log status of operation */
295 s_log_message (log_msg
, filename
);
297 i_set_state_msg (w_current
, SELECT
, state_msg
);
303 /*! \brief Revert a page, no questions asked.
305 * Closes the page, creates a new page, and reads the file back from
306 * disk. If the reverted page was the current page before, switches
307 * to the newly created page.
309 * Does not ask for confirmation. If the user should be asked for
310 * confirmation, use \ref x_highlevel_revert_page instead.
312 * \param [in] w_current the toplevel environment
313 * \param [in] page the page to revert
315 * \returns \c TRUE if the page was successfully reloaded, \c FALSE otherwise
318 x_lowlevel_revert_page (GschemToplevel
*w_current
, PAGE
*page
)
320 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
321 g_return_val_if_fail (toplevel
!= NULL
, FALSE
);
322 g_return_val_if_fail (page
!= NULL
, FALSE
);
323 g_return_val_if_fail (page
->pid
!= -1, FALSE
);
325 if (page
->is_untitled
)
326 /* can't revert untitled page */
329 /* If we're reverting whilst inside an action, re-wind the
330 page contents back to their state before we started */
331 if (w_current
->inside_action
)
332 i_cancel (w_current
);
334 /* save this for later */
335 gchar
*filename
= g_strdup (page
->page_filename
);
336 int page_control
= page
->page_control
;
338 gchar
*patch_filename
= g_strdup (page
->patch_filename
);
339 gboolean patch_descend
= page
->patch_descend
;
340 gboolean was_current_page
= toplevel
->page_current
== page
;
342 /* delete the page, then re-open the file as a new page */
343 s_log_message (page
->CHANGED
? _("Discarding page [%s]\n")
344 : _("Closing [%s]\n"),
345 page
->page_filename
);
346 s_page_delete (toplevel
, page
);
347 gschem_toplevel_page_changed (w_current
);
349 /* Force symbols to be re-loaded from disk */
352 page
= x_lowlevel_open_page (w_current
, filename
);
356 /* don't leave without a current page set */
357 if (toplevel
->page_current
== NULL
) {
358 GList
*pages
= geda_list_get_glist (toplevel
->pages
);
360 page
= (PAGE
*) pages
->data
;
362 /* create a new page if we closed the last one */
364 page
= x_lowlevel_new_page (w_current
, NULL
);
366 x_window_set_current_page (w_current
, page
);
369 /* x_lowlevel_open_page has already displayed an error message */
373 /* make sure we maintain the hierarchy info */
374 page
->page_control
= page_control
;
376 g_free (page
->patch_filename
);
377 page
->patch_filename
= patch_filename
;
378 page
->patch_descend
= patch_descend
;
380 if (was_current_page
)
381 x_window_set_current_page (w_current
, page
);
383 x_pagesel_update (w_current
);
388 /*! \brief Close a page, no questions asked.
390 * Switches to the next valid page if necessary. If this was the last
391 * page of the toplevel, a new untitled page is created.
393 * Does not ask for confirmation. If the user should be asked for
394 * confirmation, use \ref x_highlevel_close_page instead.
396 * \param [in] w_current the toplevel environment
397 * \param [in] page the page to close
400 x_lowlevel_close_page (GschemToplevel
*w_current
, PAGE
*page
)
402 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
403 PAGE
*new_current
= NULL
;
406 g_return_if_fail (toplevel
!= NULL
);
407 g_return_if_fail (page
!= NULL
);
408 g_return_if_fail (page
->pid
!= -1);
410 /* If we're closing whilst inside an action, re-wind the
411 * page contents back to their state before we started */
412 if (w_current
->inside_action
)
413 i_cancel (w_current
);
415 if (page
== toplevel
->page_current
) {
416 /* as it will delete current page, select new current page */
417 /* first look up in page hierarchy */
418 new_current
= s_page_search_by_page_id (toplevel
->pages
, page
->up
);
420 if (new_current
== NULL
) {
421 /* no up in hierarchy, choice is prev, next, new page */
422 iter
= g_list_find (geda_list_get_glist (toplevel
->pages
), page
);
424 if (g_list_previous (iter
))
425 new_current
= (PAGE
*) g_list_previous (iter
)->data
;
426 else if (g_list_next (iter
))
427 new_current
= (PAGE
*) g_list_next (iter
)->data
;
429 /* need to add a new untitled page */
432 /* new_current will be the new current page at the end of the function */
435 s_log_message (page
->CHANGED
? _("Discarding page [%s]\n")
436 : _("Closing [%s]\n"),
437 page
->page_filename
);
438 /* remove page from toplevel list of page and free */
439 s_page_delete (toplevel
, page
);
440 gschem_toplevel_page_changed (w_current
);
442 /* Switch to a different page if we just removed the current */
443 if (toplevel
->page_current
== NULL
) {
444 if (new_current
== NULL
)
445 /* Create a new page if there wasn't another to switch to */
446 new_current
= x_lowlevel_new_page (w_current
, NULL
);
448 /* change to new_current and update display */
449 x_window_set_current_page (w_current
, new_current
);
452 x_pagesel_update (w_current
);