missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / x_lowlevel.c
blob594ca3a0b2d12890f625fd415ebf6c229c36e0cc
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
30 * be used instead.
32 #include <config.h>
34 #include "gschem.h"
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
52 PAGE *
53 x_lowlevel_new_page (GschemToplevel *w_current, const gchar *filename)
55 PAGE *page;
56 gchar *fn;
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;
64 EdaConfig *cfg;
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",
68 NULL);
69 fn = NULL;
70 do {
71 g_free (fn);
72 tmp = g_strdup_printf ("%s_%d.sch", untitled_name,
73 ++w_current->num_untitled);
74 fn = g_build_filename (cwd, tmp, NULL);
75 g_free (tmp);
76 } while (g_file_test (fn, G_FILE_TEST_EXISTS));
77 g_free (untitled_name);
78 g_free (cwd);
79 } else {
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,
84 GTK_MESSAGE_ERROR,
85 GTK_BUTTONS_CLOSE,
86 _("<b>Can't create \"%s\".</b>"),
87 filename);
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);
93 return NULL;
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 */
106 if (!quiet_mode)
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);
114 g_free (fn);
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);
122 return page;
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
130 * page instead.
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
138 * loaded
140 PAGE *
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;
148 gchar *furi;
150 PAGE *page;
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);
156 if (page != NULL) {
157 gtk_recent_manager_add_item (
158 recent_manager, g_filename_to_uri (page->page_filename, NULL, NULL));
159 return page;
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);
168 /* Load from file */
169 GError *err = NULL;
170 if (!quiet_mode)
171 s_log_message (_("Loading schematic [%s]\n"), filename);
173 if (!f_open (toplevel, page, (gchar *) filename, &err)) {
174 GtkWidget *dialog;
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,
180 GTK_MESSAGE_ERROR,
181 GTK_BUTTONS_CLOSE,
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);
195 g_error_free (err);
197 return NULL;
200 furi = g_filename_to_uri (page->page_filename, NULL, NULL);
201 gtk_recent_manager_add_item (recent_manager, furi);
202 g_free (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);
212 return page;
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
230 gboolean
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;
236 gboolean success;
237 GError *err = NULL;
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;
246 if (!success) {
247 log_msg = _("Could NOT save page [%s]\n");
248 state_msg = _("Error while trying to save");
250 GtkWidget *dialog;
252 dialog = gtk_message_dialog_new (GTK_WINDOW (w_current->main_window),
253 GTK_DIALOG_DESTROY_WITH_PARENT,
254 GTK_MESSAGE_ERROR,
255 GTK_BUTTONS_CLOSE,
256 "%s",
257 err->message);
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);
262 } else {
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");
270 } else
271 log_msg = _("Saved [%s]\n");
273 state_msg = _("Saved");
275 page->is_untitled = FALSE;
277 /* reset page CHANGED flag */
278 page->CHANGED = 0;
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);
299 return success;
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
317 gboolean
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 */
327 return FALSE;
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;
337 int up = page->up;
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 */
350 s_clib_refresh ();
352 page = x_lowlevel_open_page (w_current, filename);
353 g_free (filename);
355 if (page == NULL) {
356 /* don't leave without a current page set */
357 if (toplevel->page_current == NULL) {
358 GList *pages = geda_list_get_glist (toplevel->pages);
359 if (pages != NULL)
360 page = (PAGE *) pages->data;
362 /* create a new page if we closed the last one */
363 if (page == NULL)
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 */
370 return FALSE;
373 /* make sure we maintain the hierarchy info */
374 page->page_control = page_control;
375 page->up = up;
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);
384 return TRUE;
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
399 void
400 x_lowlevel_close_page (GschemToplevel *w_current, PAGE *page)
402 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
403 PAGE *new_current = NULL;
404 GList *iter;
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;
428 else
429 /* need to add a new untitled page */
430 new_current = NULL;
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);