missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / o_misc.c
blobcb525754d6ab0d52cba306770a0d4b935af7db34
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
20 #include <config.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #endif
27 #include <libgen.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
33 #include "gschem.h"
34 #include "actions.decl.x"
36 /* break with the tradition here and input a list */
37 /*! \todo probably should go back and do the same for o_copy o_move
38 * o_delete...
40 /*! \todo Finish function documentation!!!
41 * \brief
42 * \par Function Description
45 void o_edit(GschemToplevel *w_current, GList *list, gboolean double_click)
47 OBJECT *o_current;
48 const gchar *str = NULL;
50 if (list == NULL) {
51 return;
54 o_current = (OBJECT *) list->data;
55 if (o_current == NULL) {
56 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
57 exit(-1);
60 /* on double-click, see if we can enter a subschematic */
61 if (double_click && !w_current->SHIFTKEY &&
62 (o_current->type == OBJ_COMPLEX ||
63 o_current->type == OBJ_PLACEHOLDER) &&
64 x_hierarchy_down_schematic (w_current, o_current))
65 return;
67 /* for now deal with only the first item */
68 switch(o_current->type) {
70 /* also add the ability to multi attrib edit: nets, busses, pins */
71 case(OBJ_COMPLEX):
72 case(OBJ_PLACEHOLDER):
73 case(OBJ_NET):
74 case(OBJ_PIN):
75 case(OBJ_BUS):
76 gschem_dockable_present (w_current->multiattrib_dockable);
77 break;
79 case(OBJ_PICTURE):
80 picture_change_filename_dialog(w_current);
81 break;
82 case(OBJ_ARC):
83 arc_angle_dialog(w_current, o_current);
84 break;
85 case(OBJ_TEXT):
86 str = o_text_get_string (w_current->toplevel, o_current);
87 if (o_attrib_get_name_value (o_current, NULL, NULL) &&
88 /* attribute editor only accept 1-line values for attribute */
89 o_text_num_lines (str) == 1) {
90 x_multiattrib_edit_attribute (w_current, o_current);
91 } else {
92 gschem_dockable_present (w_current->text_properties_dockable);
94 break;
97 /* has to be more extensive in the future */
98 /* some sort of redrawing? */
101 /*! \brief Rotate all objects in list.
102 * \par Function Description
103 * Given an object <B>list</B>, and the center of rotation
104 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
105 * list, rotating each object through angle <B>angle</B>.
106 * The list contains a given object and all its attributes
107 * (refdes, pinname, pinlabel, ...).
108 * There is a second pass to run the rotate hooks of non-simple objects,
109 * like pin or complex objects, for example.
111 * \param [in] w_current The GschemToplevel object.
112 * \param [in] centerx Center x coordinate of rotation.
113 * \param [in] centery Center y coordinate of rotation.
114 * \param [in] angle Angle to rotate the objects through.
115 * \param [in] list The list of objects to rotate.
117 void o_rotate_world_update(GschemToplevel *w_current,
118 int centerx, int centery, int angle, GList *list)
120 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
121 OBJECT *o_current;
122 GList *o_iter;
124 /* this is okay if you just hit rotate and have nothing selected */
125 if (list == NULL) {
126 i_action_stop (w_current);
127 i_set_state(w_current, SELECT);
128 return;
131 o_invalidate_glist (w_current, list);
133 /* Find connected objects, removing each object in turn from the
134 * connection list. We only _really_ want those objects connected
135 * to the selection, not those within in it.
137 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
138 o_current = o_iter->data;
140 s_conn_remove_object_connections (toplevel, o_current);
143 o_glist_rotate_world( toplevel, centerx, centery, angle, list );
145 /* Find connected objects, adding each object in turn back to the
146 * connection list. We only _really_ want those objects connected
147 * to the selection, not those within in it.
149 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
150 o_current = o_iter->data;
152 s_conn_update_object (o_current->page, o_current);
155 o_invalidate_glist (w_current, list);
157 /* Run rotate-objects-hook */
158 g_run_hook_object_list (w_current, "%rotate-objects-hook", list);
160 /* Don't save the undo state if we are inside an action */
161 /* This is useful when rotating the selection while moving, for example */
162 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
163 if (!w_current->inside_action) {
164 o_undo_savestate_old (w_current, UNDO_ALL, _("Rotate"));
167 if (w_current->event_state == ROTATEMODE) {
168 i_set_state(w_current, SELECT);
172 /*! \todo Finish function documentation!!!
173 * \brief
174 * \par Function Description
177 void o_mirror_world_update(GschemToplevel *w_current, int centerx, int centery, GList *list)
179 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
180 OBJECT *o_current;
181 GList *o_iter;
183 if (list == NULL) {
184 i_action_stop (w_current);
185 i_set_state(w_current, SELECT);
186 return;
189 o_invalidate_glist (w_current, list);
191 /* Find connected objects, removing each object in turn from the
192 * connection list. We only _really_ want those objects connected
193 * to the selection, not those within in it.
195 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
196 o_current = o_iter->data;
198 s_conn_remove_object_connections (toplevel, o_current);
201 o_glist_mirror_world( toplevel, centerx, centery, list );
203 /* Find connected objects, adding each object in turn back to the
204 * connection list. We only _really_ want those objects connected
205 * to the selection, not those within in it.
207 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
208 o_current = o_iter->data;
210 s_conn_update_object (o_current->page, o_current);
213 o_invalidate_glist (w_current, list);
215 /* Run mirror-objects-hook */
216 g_run_hook_object_list (w_current, "%mirror-objects-hook", list);
218 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
219 o_undo_savestate_old (w_current, UNDO_ALL, _("Mirror"));
221 if (w_current->event_state == MIRRORMODE) {
222 i_set_state(w_current, SELECT);
226 /*! \todo Finish function documentation!!!
227 * \brief
228 * \par Function Description
231 void o_edit_show_hidden_lowlevel (GschemToplevel *w_current,
232 const GList *o_list)
234 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
235 OBJECT *o_current;
236 const GList *iter;
238 iter = o_list;
239 while (iter != NULL) {
240 o_current = (OBJECT *)iter->data;
241 if (o_current->type == OBJ_TEXT && !o_is_visible (o_current)) {
243 /* don't toggle the visibility flag */
244 o_text_recreate (toplevel, o_current);
247 if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
248 o_edit_show_hidden_lowlevel(w_current, o_current->complex->prim_objs);
249 o_current->w_bounds_valid_for = NULL;
252 iter = g_list_next (iter);
256 /*! \todo Finish function documentation!!!
257 * \brief
258 * \par Function Description
261 void o_edit_show_hidden (GschemToplevel *w_current, const GList *o_list)
263 /* this function just shows the hidden text, but doesn't toggle it */
264 /* this function does not change the CHANGED bit, no real changes are */
265 /* made to the schematic */
267 /* toggle show_hidden_text variable, which when it is true */
268 /* means that hidden text IS drawn */
269 w_current->toplevel->show_hidden_text = !w_current->toplevel->show_hidden_text;
270 i_show_state(w_current, NULL); /* update screen status */
272 o_edit_show_hidden_lowlevel(w_current, o_list);
273 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current));
275 if (w_current->toplevel->show_hidden_text) {
276 s_log_message(_("Hidden text is now visible\n"));
277 } else {
278 s_log_message(_("Hidden text is now invisible\n"));
281 gschem_action_set_active (action_edit_show_hidden,
282 w_current->toplevel->show_hidden_text,
283 w_current);
286 /*! \todo Finish function documentation!!!
287 * \brief
288 * \par Function Description
291 void o_edit_hide_specific_text (GschemToplevel *w_current,
292 const GList *o_list,
293 const char *stext)
295 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
296 OBJECT *o_current;
297 const GList *iter;
298 gboolean changed = FALSE;
300 iter = o_list;
301 while (iter != NULL) {
302 o_current = (OBJECT *)iter->data;
304 if (o_current->type == OBJ_TEXT) {
305 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
306 if (!strncmp (stext, str, strlen (stext))) {
307 if (o_is_visible (o_current)) {
308 o_set_visibility (toplevel, o_current, INVISIBLE);
309 o_text_recreate(toplevel, o_current);
311 changed = TRUE;
315 iter = g_list_next (iter);
317 if (changed) {
318 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
319 o_undo_savestate_old (w_current, UNDO_ALL, _("Hide Specific Text"));
321 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current));
324 /*! \todo Finish function documentation!!!
325 * \brief
326 * \par Function Description
329 void o_edit_show_specific_text (GschemToplevel *w_current,
330 const GList *o_list,
331 const char *stext)
333 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
334 OBJECT *o_current;
335 const GList *iter;
336 gboolean changed = FALSE;
338 iter = o_list;
339 while (iter != NULL) {
340 o_current = (OBJECT *)iter->data;
342 if (o_current->type == OBJ_TEXT) {
343 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
344 if (!strncmp (stext, str, strlen (stext))) {
345 if (!o_is_visible (o_current)) {
346 o_set_visibility (toplevel, o_current, VISIBLE);
347 o_text_recreate(toplevel, o_current);
349 changed = TRUE;
353 iter = g_list_next (iter);
355 if (changed) {
356 gschem_toplevel_page_content_changed (w_current, toplevel->page_current);
357 o_undo_savestate_old (w_current, UNDO_ALL, _("Show Specific Text"));
362 /*! \brief Update a component.
364 * \par Function Description
365 * Updates \a o_current to the latest version of the symbol available
366 * in the symbol library, while preserving any attributes set in the
367 * current schematic. On success, returns the new OBJECT which
368 * replaces \a o_current on the page; \a o_current is deleted. On
369 * failure, returns NULL, and \a o_current is left unchanged.
371 * \param [in] w_current The GschemToplevel object.
372 * \param [in,out] o_current The OBJECT to be updated.
374 * \return the new OBJECT that replaces \a o_current.
376 OBJECT *
377 o_update_component (GschemToplevel *w_current, OBJECT *o_current)
379 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
380 OBJECT *o_new;
381 PAGE *page;
382 GList *new_attribs;
383 GList *old_attribs;
384 GList *iter;
385 const CLibSymbol *clib;
387 g_return_val_if_fail (o_current != NULL, NULL);
388 g_return_val_if_fail (o_current->type == OBJ_COMPLEX, NULL);
389 g_return_val_if_fail (o_current->complex_basename != NULL, NULL);
391 page = o_get_page (toplevel, o_current);
393 /* Force symbol data to be reloaded from source */
394 clib = s_clib_get_symbol_by_name (o_current->complex_basename);
395 s_clib_symbol_invalidate_data (clib);
397 if (clib == NULL) {
398 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
399 o_current->complex_basename);
400 return NULL;
403 /* Unselect the old object. */
404 o_selection_remove (toplevel, page->selection_list, o_current);
406 /* Create new object and set embedded */
407 o_new = o_complex_new (toplevel, OBJ_COMPLEX, DEFAULT_COLOR,
408 o_current->complex->x,
409 o_current->complex->y,
410 o_current->complex->angle,
411 o_current->complex->mirror,
412 clib, o_current->complex_basename,
414 if (o_complex_is_embedded (o_current)) {
415 o_embed (toplevel, o_new);
418 new_attribs = o_complex_promote_attribs (toplevel, o_new);
420 /* Cull any attributes from new COMPLEX that are already attached to
421 * old COMPLEX. Note that the new_attribs list is kept consistent by
422 * setting GList data pointers to NULL if their OBJECTs are
423 * culled. At the end, the new_attribs list is updated by removing
424 * all list items with NULL data. This is slightly magic, but
425 * works. */
426 for (iter = new_attribs; iter != NULL; iter = g_list_next (iter)) {
427 OBJECT *attr_new = iter->data;
428 gchar *name;
429 gchar *value;
431 g_assert (attr_new->type == OBJ_TEXT);
433 o_attrib_get_name_value (attr_new, &name, NULL);
435 value = o_attrib_search_attached_attribs_by_name (o_current, name, 0);
436 if (value != NULL) {
437 o_attrib_remove (toplevel, &o_new->attribs, attr_new);
438 s_delete_object (toplevel, attr_new);
439 iter->data = NULL;
442 g_free (name);
443 g_free (value);
445 new_attribs = g_list_remove_all (new_attribs, NULL);
447 /* Detach attributes from old OBJECT and attach to new OBJECT */
448 old_attribs = g_list_copy (o_current->attribs);
449 o_attrib_detach_all (toplevel, o_current);
450 o_attrib_attach_list (toplevel, old_attribs, o_new, 1);
451 g_list_free (old_attribs);
453 /* Add new attributes to page */
454 s_page_append_list (toplevel, page, new_attribs);
456 /* Update pinnumbers for current slot */
457 s_slot_update_object (toplevel, o_new);
459 /* Replace old OBJECT with new OBJECT */
460 s_page_replace (toplevel, page, o_current, o_new);
461 s_delete_object (toplevel, o_current);
463 /* Select new OBJECT */
464 o_selection_add (toplevel, page->selection_list, o_new);
466 /* Can't undo this, so there's no point in adding an undo step. */
468 return o_new;
471 /*! \brief Do autosave on all pages that are marked.
472 * \par Function Description
473 * Looks for pages with the do_autosave_backup flag activated and
474 * autosaves them.
476 * \param [in] w_current The GschemToplevel object to search for autosave's.
478 void o_autosave_backups(GschemToplevel *w_current)
480 TOPLEVEL *toplevel = gschem_toplevel_get_toplevel (w_current);
481 GList *iter;
482 PAGE *p_save, *p_current;
483 gchar *backup_filename;
484 gchar *real_filename;
485 gchar *only_filename;
486 gchar *dirname;
487 mode_t saved_umask;
488 mode_t mask;
489 struct stat st;
491 /* save current page */
492 p_save = toplevel->page_current;
494 for ( iter = geda_list_get_glist( toplevel->pages );
495 iter != NULL;
496 iter = g_list_next( iter ) ) {
498 p_current = (PAGE *)iter->data;
500 if (p_current->do_autosave_backup == 0) {
501 continue;
503 if (p_current->ops_since_last_backup != 0) {
504 /* make p_current the current page of toplevel */
505 s_page_goto (toplevel, p_current);
506 gschem_toplevel_page_changed (w_current);
508 /* Get the real filename and file permissions */
509 real_filename = follow_symlinks (p_current->page_filename, NULL);
511 if (real_filename == NULL) {
512 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
513 } else {
514 /* Get the directory in which the real filename lives */
515 dirname = g_path_get_dirname (real_filename);
516 only_filename = g_path_get_basename(real_filename);
518 backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
519 dirname, G_DIR_SEPARATOR, only_filename);
521 /* If there is not an existing file with that name, compute the
522 * permissions and uid/gid that we will use for the newly-created file.
525 if (stat (real_filename, &st) != 0) {
526 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
527 struct stat dir_st;
528 int result;
529 #endif
531 /* Use default permissions */
532 saved_umask = umask(0);
533 st.st_mode = 0666 & ~saved_umask;
534 umask(saved_umask);
535 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
536 st.st_uid = getuid ();
538 result = stat (dirname, &dir_st);
540 if (result == 0 && (dir_st.st_mode & S_ISGID))
541 st.st_gid = dir_st.st_gid;
542 else
543 st.st_gid = getgid ();
544 #endif
546 g_free (dirname);
547 g_free (only_filename);
548 g_free (real_filename);
550 /* Make the backup file writable before saving a new one */
551 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
552 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
553 saved_umask = umask(0);
554 if (chmod(backup_filename, (S_IWRITE|S_IWGRP|S_IWOTH) &
555 ((~saved_umask) & 0777)) != 0) {
556 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
557 backup_filename);
559 umask(saved_umask);
562 if (o_save (toplevel,
563 s_page_objects (toplevel->page_current),
564 backup_filename, NULL)) {
566 p_current->ops_since_last_backup = 0;
567 p_current->do_autosave_backup = 0;
569 /* Make the backup file readonly so a 'rm *' command will ask
570 the user before deleting it */
571 saved_umask = umask(0);
572 mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
573 mask = (~mask)&0777;
574 mask &= ((~saved_umask) & 0777);
575 if (chmod(backup_filename,mask) != 0) {
576 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
577 backup_filename);
579 umask(saved_umask);
580 } else {
581 s_log_message (_("Could NOT save backup file [%s]\n"),
582 backup_filename);
584 g_free (backup_filename);
588 /* restore current page */
589 s_page_goto (toplevel, p_save);
590 gschem_toplevel_page_changed (w_current);