Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / o_misc.c
blob011079cbeb0cb178e34fbb54b4bbb374aae5004e
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111 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"
35 #ifdef HAVE_LIBDMALLOC
36 #include <dmalloc.h>
37 #endif
39 /* break with the tradition here and input a list */
40 /*! \todo probably should go back and do the same for o_copy o_move
41 * o_delete...
43 /*! \todo Finish function documentation!!!
44 * \brief
45 * \par Function Description
48 void o_edit(GSCHEM_TOPLEVEL *w_current, GList *list)
50 OBJECT *o_current;
51 const gchar *str = NULL;
53 if (list == NULL) {
54 w_current->inside_action = 0;
55 i_set_state(w_current, SELECT);
56 return;
59 o_current = (OBJECT *) list->data;
60 if (o_current == NULL) {
61 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
62 exit(-1);
65 /* for now deal with only the first item */
66 switch(o_current->type) {
68 /* also add the ability to multi attrib edit: nets, busses, pins */
69 case(OBJ_COMPLEX):
70 case(OBJ_PLACEHOLDER):
71 case(OBJ_NET):
72 case(OBJ_PIN):
73 case(OBJ_BUS):
74 x_multiattrib_open (w_current);
75 break;
77 case(OBJ_PICTURE):
78 picture_change_filename_dialog(w_current);
79 break;
80 case(OBJ_ARC):
81 arc_angle_dialog(w_current, o_current);
82 break;
83 case(OBJ_TEXT):
84 str = o_text_get_string (w_current->toplevel, o_current);
85 if (o_attrib_get_name_value (o_current, NULL, NULL) &&
86 /* attribute editor only accept 1-line values for attribute */
87 o_text_num_lines (str) == 1) {
88 attrib_edit_dialog(w_current,o_current, FROM_MENU);
89 } else {
90 o_text_edit(w_current, o_current);
92 break;
95 /* has to be more extensive in the future */
96 /* some sort of redrawing? */
99 /*! \todo Finish function documentation!!!
100 * \brief
101 * \par Function Description
104 /* This locks the entire selected list. It does lock components, but does NOT
105 * change the color (of primatives of the components) though
106 * this cannot be called recursively */
107 void o_lock(GSCHEM_TOPLEVEL *w_current)
109 OBJECT *object = NULL;
110 GList *s_current = NULL;
112 /* skip over head */
113 s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
115 while(s_current != NULL) {
116 object = (OBJECT *) s_current->data;
117 if (object) {
118 /* check to see if locked_color is already being used */
119 if (object->locked_color == -1) {
120 object->sel_func = NULL;
121 object->locked_color = object->color;
122 object->color = LOCK_COLOR;
123 w_current->toplevel->page_current->CHANGED=1;
124 } else {
125 s_log_message(_("Object already locked\n"));
129 s_current = g_list_next(s_current);
132 if (!w_current->SHIFTKEY) o_select_unselect_all(w_current);
133 o_undo_savestate(w_current, UNDO_ALL);
134 i_update_menus(w_current);
137 /*! \todo Finish function documentation!!!
138 * \brief
139 * \par Function Description
142 /* You can unlock something by selecting it with a bounding box... */
143 /* this will probably change in the future, but for now it's a
144 something.. :-) */
145 /* this cannot be called recursively */
146 void o_unlock(GSCHEM_TOPLEVEL *w_current)
148 OBJECT *object = NULL;
149 GList *s_current = NULL;
151 s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
153 while(s_current != NULL) {
154 object = (OBJECT *) s_current->data;
155 if (object) {
156 /* only unlock if sel_func is not set to something */
157 if (object->sel_func == NULL) {
158 object->sel_func = select_func;
159 object->color = object->locked_color;
160 object->locked_color = -1;
161 w_current->toplevel->page_current->CHANGED = 1;
162 } else {
163 s_log_message(_("Object already unlocked\n"));
167 s_current = g_list_next(s_current);
169 o_undo_savestate(w_current, UNDO_ALL);
172 /*! \brief Rotate all objects in list.
173 * \par Function Description
174 * Given an object <B>list</B>, and the center of rotation
175 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
176 * list, rotating each object through angle <B>angle</B>.
177 * The list contains a given object and all its attributes
178 * (refdes, pinname, pinlabel, ...).
179 * There is a second pass to run the rotate hooks of non-simple objects,
180 * like pin or complex objects, for example.
182 * \param [in] w_current The GSCHEM_TOPLEVEL object.
183 * \param [in] centerx Center x coordinate of rotation.
184 * \param [in] centery Center y coordinate of rotation.
185 * \param [in] angle Angle to rotate the objects through.
186 * \param [in] list The list of objects to rotate.
188 void o_rotate_world_update(GSCHEM_TOPLEVEL *w_current,
189 int centerx, int centery, int angle, GList *list)
191 TOPLEVEL *toplevel = w_current->toplevel;
192 OBJECT *o_current;
193 GList *o_iter;
194 GList *prev_conn_objects=NULL;
195 GList *connected_objects=NULL;
197 /* this is okay if you just hit rotate and have nothing selected */
198 if (list == NULL) {
199 w_current->inside_action = 0;
200 i_set_state(w_current, SELECT);
201 return;
204 if (!toplevel->DONT_REDRAW) {
205 o_invalidate_glist (w_current, list);
208 /* Find connected objects, removing each object in turn from the
209 * connection list. We only _really_ want those objects connected
210 * to the selection, not those within in it. The extra redraws
211 * don't _really_ hurt though. */
212 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
213 o_current = o_iter->data;
215 prev_conn_objects = s_conn_return_others (prev_conn_objects, o_current);
216 s_conn_remove_object (toplevel, o_current);
219 o_glist_rotate_world( toplevel, centerx, centery, angle, list );
221 /* Find connected objects, adding each object in turn back to the
222 * connection list. We only _really_ want those objects connected
223 * to the selection, not those within in it. The extra redraws dont
224 * _really_ hurt though. */
225 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
226 o_current = o_iter->data;
228 s_conn_update_object (toplevel, o_current);
229 connected_objects = s_conn_return_others (connected_objects, o_current);
232 if (!toplevel->DONT_REDRAW) {
233 o_invalidate_glist (w_current, list);
234 o_invalidate_glist (w_current, prev_conn_objects);
235 o_invalidate_glist (w_current, connected_objects);
238 g_list_free (prev_conn_objects);
239 prev_conn_objects = NULL;
240 g_list_free (connected_objects);
241 connected_objects = NULL;
243 /* All objects were rotated. Run the rotate hooks */
244 o_rotate_call_hooks (w_current, list);
246 /* Don't save the undo state if we are inside an action */
247 /* This is useful when rotating the selection while moving, for example */
248 toplevel->page_current->CHANGED = 1;
249 if (!w_current->inside_action) {
250 o_undo_savestate(w_current, UNDO_ALL);
255 void o_rotate_call_hooks (GSCHEM_TOPLEVEL *w_current, GList *list)
257 TOPLEVEL *toplevel = w_current->toplevel;
258 OBJECT *o_current;
259 GList *iter;
261 /* Do not run any hooks for simple objects here, like text, since they
262 were rotated in the previous pass, and the selection list can contain
263 an object and all its attributes (text) */
264 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
265 o_current = iter->data;
267 switch (o_current->type) {
268 case OBJ_PIN:
269 /* Run the rotate pin hook */
270 if (scm_hook_empty_p (rotate_pin_hook) == SCM_BOOL_F) {
271 scm_run_hook (rotate_pin_hook,
272 scm_cons (g_make_object_smob (toplevel, o_current),
273 SCM_EOL));
275 break;
277 case OBJ_COMPLEX:
278 /* Run the rotate hook */
279 if (scm_hook_empty_p (rotate_component_object_hook) == SCM_BOOL_F) {
280 scm_run_hook (rotate_component_object_hook,
281 scm_cons (g_make_object_smob (toplevel, o_current),
282 SCM_EOL));
284 break;
286 default:
287 break;
292 /*! \todo Finish function documentation!!!
293 * \brief
294 * \par Function Description
297 void o_mirror_world_update(GSCHEM_TOPLEVEL *w_current, int centerx, int centery, GList *list)
299 TOPLEVEL *toplevel = w_current->toplevel;
300 OBJECT *o_current;
301 GList *o_iter;
302 GList *prev_conn_objects=NULL;
303 GList *connected_objects=NULL;
305 if (list == NULL) {
306 w_current->inside_action = 0;
307 i_set_state(w_current, SELECT);
308 return;
311 o_invalidate_glist (w_current, list);
313 /* Find connected objects, removing each object in turn from the
314 * connection list. We only _really_ want those objects connected
315 * to the selection, not those within in it. The extra redraws
316 * don't _really_ hurt though. */
317 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
318 o_current = o_iter->data;
320 prev_conn_objects = s_conn_return_others (prev_conn_objects, o_current);
321 s_conn_remove_object (toplevel, o_current);
324 o_glist_mirror_world( toplevel, centerx, centery, list );
326 /* Find connected objects, adding each object in turn back to the
327 * connection list. We only _really_ want those objects connected
328 * to the selection, not those within in it. The extra redraws dont
329 * _really_ hurt though. */
330 for (o_iter = list; o_iter != NULL; o_iter = g_list_next (o_iter)) {
331 o_current = o_iter->data;
333 s_conn_update_object (toplevel, o_current);
334 connected_objects = s_conn_return_others (connected_objects, o_current);
337 o_invalidate_glist (w_current, list);
338 o_invalidate_glist (w_current, prev_conn_objects);
339 o_invalidate_glist (w_current, connected_objects);
341 g_list_free (prev_conn_objects);
342 prev_conn_objects = NULL;
343 g_list_free (connected_objects);
344 connected_objects = NULL;
346 /* All objects were mirrored. Do a 2nd pass to run the mirror hooks */
347 /* Do not run any hooks for simple objects here, like text, since they
348 were mirrored in the previous pass, and the selection list can contain
349 an object and all its attributes (text) */
350 o_iter = list;
351 while (o_iter != NULL) {
352 o_current = (OBJECT *) o_iter->data;
354 switch(o_current->type) {
355 case(OBJ_PIN):
356 /* Run the mirror pin hook */
357 if (scm_hook_empty_p(mirror_pin_hook) == SCM_BOOL_F &&
358 o_current != NULL) {
359 scm_run_hook(mirror_pin_hook,
360 scm_cons(g_make_object_smob(toplevel, o_current),
361 SCM_EOL));
363 break;
365 case (OBJ_COMPLEX):
366 /* Run the mirror pin hook */
367 if (scm_hook_empty_p(mirror_component_object_hook) == SCM_BOOL_F &&
368 o_current != NULL) {
369 scm_run_hook(mirror_component_object_hook,
370 scm_cons(g_make_object_smob(toplevel, o_current),
371 SCM_EOL));
373 break;
374 default:
375 break;
378 o_iter = g_list_next(o_iter);
381 toplevel->page_current->CHANGED=1;
382 o_undo_savestate(w_current, UNDO_ALL);
385 /*! \todo Finish function documentation!!!
386 * \brief
387 * \par Function Description
390 void o_edit_show_hidden_lowlevel (GSCHEM_TOPLEVEL *w_current,
391 const GList *o_list)
393 TOPLEVEL *toplevel = w_current->toplevel;
394 OBJECT *o_current;
395 const GList *iter;
397 iter = o_list;
398 while (iter != NULL) {
399 o_current = (OBJECT *)iter->data;
400 if (o_current->type == OBJ_TEXT && o_current->visibility == INVISIBLE) {
402 /* don't toggle the visibility flag */
404 if (toplevel->show_hidden_text) {
405 /* draw the text object if it hidden */
406 o_text_recreate(toplevel, o_current);
407 o_recalc_single_object(toplevel, o_current);
408 o_invalidate (w_current, o_current);
409 } else {
410 /* object is hidden and we are now NOT drawing it, so */
411 /* get rid of the extra primitive data */
412 o_text_recreate(toplevel, o_current);
413 o_recalc_single_object(toplevel, o_current);
414 /* unfortunately, you cannot erase the old visible text here */
415 /* because o_text_draw will just return */
419 if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
420 o_edit_show_hidden_lowlevel(w_current, o_current->complex->prim_objs);
421 o_recalc_single_object(toplevel, o_current);
424 iter = g_list_next (iter);
428 /*! \todo Finish function documentation!!!
429 * \brief
430 * \par Function Description
433 void o_edit_show_hidden (GSCHEM_TOPLEVEL *w_current, const GList *o_list)
435 /* this function just shows the hidden text, but doesn't toggle it */
436 /* this function does not change the CHANGED bit, no real changes are */
437 /* made to the schematic */
439 /* toggle show_hidden_text variable, which when it is true */
440 /* means that hidden text IS drawn */
441 w_current->toplevel->show_hidden_text = !w_current->toplevel->show_hidden_text;
442 i_show_state(w_current, NULL); /* update screen status */
444 o_edit_show_hidden_lowlevel(w_current, o_list);
445 o_invalidate_all (w_current);
447 if (w_current->toplevel->show_hidden_text) {
448 s_log_message(_("Hidden text is now visible\n"));
449 } else {
450 s_log_message(_("Hidden text is now invisible\n"));
454 /*! \todo Finish function documentation!!!
455 * \brief
456 * \par Function Description
459 void o_edit_make_visible (GSCHEM_TOPLEVEL *w_current, const GList *o_list)
461 /* this function actually changes the visibility flag */
462 TOPLEVEL *toplevel = w_current->toplevel;
463 OBJECT *o_current;
464 const GList *iter;
466 iter = o_list;
467 while (iter != NULL) {
468 o_current = (OBJECT *)iter->data;
470 if (o_current->type == OBJ_TEXT) {
471 if (o_current->visibility == INVISIBLE) {
472 o_current->visibility = VISIBLE;
473 o_text_recreate(toplevel, o_current);
475 o_invalidate (w_current, o_current);
477 toplevel->page_current->CHANGED = 1;
480 iter = g_list_next (iter);
482 o_undo_savestate(w_current, UNDO_ALL);
486 #define FIND_WINDOW_HALF_SIZE (5000)
488 OBJECT *last_o = NULL;
489 int skiplast;
491 /*! \todo Finish function documentation!!!
492 * \brief
493 * \par Function Description
495 * \fixme Only descends into the first source schematic
498 int o_edit_find_text (GSCHEM_TOPLEVEL *w_current, const GList *o_list,
499 char *stext, int descend, int skip)
501 TOPLEVEL *toplevel = w_current->toplevel;
502 char *attrib = NULL;
503 int count = 0;
504 PAGE *parent = NULL;
505 char *current_filename = NULL;
506 int page_control = 0;
507 int pcount = 0;
508 int rv;
509 int x1, y1, x2, y2;
510 int text_screen_height;
511 const GList *iter;
513 OBJECT *o_current;
515 skiplast = skip;
517 iter = o_list;
518 while (iter != NULL) {
519 o_current = (OBJECT *)iter->data;
521 if (descend) {
522 if (o_current->type == OBJ_COMPLEX) {
523 parent = toplevel->page_current;
524 attrib = o_attrib_search_attached_attribs_by_name (o_current,
525 "source", count);
527 /* if above is null, then look inside symbol */
528 if (attrib == NULL) {
529 attrib = o_attrib_search_inherited_attribs_by_name (o_current,
530 "source", count);
531 /* looking_inside = TRUE; */
534 if (attrib) {
535 pcount = 0;
536 current_filename = u_basic_breakup_string(attrib, ',', pcount);
537 if (current_filename != NULL) {
538 page_control =
539 s_hierarchy_down_schematic_single(toplevel,
540 current_filename,
541 parent,
542 page_control,
543 HIERARCHY_NORMAL_LOAD);
544 /* o_invalidate_all (w_current); */
546 rv = o_edit_find_text (w_current,
547 s_page_objects (toplevel->page_current),
548 stext, descend, skiplast);
549 if (!rv) {
550 return 0;
552 s_page_goto( toplevel, parent );
558 if (o_current->type == OBJ_TEXT) {
559 const gchar *str = o_text_get_string (toplevel, o_current);
560 /* replaced strcmp with strstr to simplify the search */
561 if (strstr (str,stext)) {
562 if (!skiplast) {
563 a_zoom(w_current, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
564 world_get_single_object_bounds (toplevel, o_current, &x1, &y1, &x2, &y2);
565 text_screen_height = SCREENabs (w_current, y2 - y1);
566 /* this code will zoom/pan till the text screen height is about */
567 /* 50 pixels high, perhaps a future enhancement will be to make */
568 /* this number configurable */
569 while (text_screen_height < 50) {
570 a_zoom(w_current, ZOOM_IN, DONTCARE, A_PAN_DONT_REDRAW);
571 text_screen_height = SCREENabs (w_current, y2 - y1);
573 a_pan_general(w_current,
574 o_current->text->x, o_current->text->y,
575 1, 0);
577 /* Make sure the titlebar and scrollbars are up-to-date */
578 x_window_set_current_page(w_current,
579 w_current->toplevel->page_current );
581 last_o = o_current;
582 break;
584 if (last_o == o_current) {
585 skiplast = 0;
588 } /* if (strstr(o_current->text->string,stext)) */
589 } /* if (o_current->type == OBJ_TEXT) */
590 iter = g_list_next (iter);
592 if (iter == NULL) {
593 return 1;
596 return (iter == NULL);
600 /*! \todo Finish function documentation!!!
601 * \brief
602 * \par Function Description
605 void o_edit_hide_specific_text (GSCHEM_TOPLEVEL *w_current,
606 const GList *o_list,
607 char *stext)
609 TOPLEVEL *toplevel = w_current->toplevel;
610 OBJECT *o_current;
611 const GList *iter;
613 iter = o_list;
614 while (iter != NULL) {
615 o_current = (OBJECT *)iter->data;
617 if (o_current->type == OBJ_TEXT) {
618 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
619 if (!strncmp (stext, str, strlen (stext))) {
620 if (o_current->visibility == VISIBLE) {
621 o_current->visibility = INVISIBLE;
622 o_text_recreate(toplevel, o_current);
624 toplevel->page_current->CHANGED = 1;
628 iter = g_list_next (iter);
630 o_undo_savestate(w_current, UNDO_ALL);
631 o_invalidate_all (w_current);
634 /*! \todo Finish function documentation!!!
635 * \brief
636 * \par Function Description
639 void o_edit_show_specific_text (GSCHEM_TOPLEVEL *w_current,
640 const GList *o_list,
641 char *stext)
643 TOPLEVEL *toplevel = w_current->toplevel;
644 OBJECT *o_current;
645 const GList *iter;
647 iter = o_list;
648 while (iter != NULL) {
649 o_current = (OBJECT *)iter->data;
651 if (o_current->type == OBJ_TEXT) {
652 const gchar *str = o_text_get_string (w_current->toplevel, o_current);
653 if (!strncmp (stext, str, strlen (stext))) {
654 if (o_current->visibility == INVISIBLE) {
655 o_current->visibility = VISIBLE;
656 o_text_recreate(toplevel, o_current);
658 o_invalidate (w_current, o_current);
659 toplevel->page_current->CHANGED = 1;
663 iter = g_list_next (iter);
665 o_undo_savestate(w_current, UNDO_ALL);
668 /*! \todo Finish function documentation!!!
669 * \brief
670 * \par Function Description
673 void o_update_component(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
675 TOPLEVEL *toplevel = w_current->toplevel;
676 OBJECT *new_complex;
677 OBJECT *a_current;
678 GList *temp_list;
679 GList *a_iter;
680 GList *po_iter;
681 gboolean is_embedded;
682 const CLibSymbol *clib;
684 g_return_if_fail (o_current != NULL);
686 is_embedded = o_complex_is_embedded (o_current);
688 g_assert (o_current->complex_basename != NULL);
690 /* This shuold be replaced with API to invalidate only the specific
691 * symbol name we want to update */
692 s_clib_flush_symbol_cache ();
693 clib = s_clib_get_symbol_by_name (o_current->complex_basename);
695 if (clib == NULL) {
696 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
697 o_current->complex_basename);
698 return;
701 /* ensure we repaint where the complex object was */
702 o_invalidate (w_current, o_current);
703 /* delete its connections */
704 s_conn_remove_object (toplevel, o_current);
705 /* and unselect it */
706 o_selection_remove (toplevel,
707 toplevel->page_current->selection_list, o_current);
709 new_complex = o_complex_new (toplevel, OBJ_COMPLEX, DEFAULT_COLOR,
710 o_current->complex->x,
711 o_current->complex->y,
712 o_current->complex->angle,
713 o_current->complex->mirror,
714 clib, o_current->complex_basename,
717 temp_list = o_complex_promote_attribs (toplevel, new_complex);
718 temp_list = g_list_append (temp_list, new_complex);
720 /* updating the old complex with data from the new one */
721 /* first process the prim_objs: */
722 /* - delete the prim_objs of the old component */
723 s_delete_object_glist (toplevel, o_current->complex->prim_objs);
724 /* - put the prim_objs of the new component in the old one */
725 o_current->complex->prim_objs = new_complex->complex->prim_objs;
727 /* set the parent field now */
728 for (po_iter = o_current->complex->prim_objs;
729 po_iter != NULL;
730 po_iter = g_list_next (po_iter)) {
731 OBJECT *tmp = po_iter->data;
732 tmp->parent = o_current;
735 /* - reset the new complex prim_objs */
736 new_complex->complex->prim_objs = NULL;
738 /* then process the attributes: */
739 /* - check each attrib of the new complex */
740 a_iter = new_complex->attribs;
741 while (a_iter != NULL) {
742 OBJECT *o_attrib;
743 gchar *name;
744 char *attrfound;
746 a_current = a_iter->data;
747 g_assert (a_current->type == OBJ_TEXT);
749 o_attrib_get_name_value (a_current, &name, NULL);
751 /* We are only interested in the attributes which were promoted during
752 * load of the new complex. Any which aren't already promoted in the
753 * schematic are migrated.
755 attrfound = o_attrib_search_attached_attribs_by_name (o_current, name, 0);
757 /* free this now since it is no longer being used */
758 g_free(name);
760 if (attrfound == NULL) {
761 /* attribute with same name not found in old component: */
762 /* add new attribute to old component */
764 /* make a copy of the attribute object */
765 o_attrib = o_object_copy (toplevel, a_current, NORMAL_FLAG);
766 s_page_append (toplevel, toplevel->page_current, o_attrib);
767 /* add the attribute to old */
768 o_attrib_add (toplevel, o_current, o_attrib);
769 /* redraw the attribute object */
770 o_invalidate (w_current, o_attrib);
771 /* note: this object is unselected (not added to selection). */
773 else
775 g_free(attrfound);
778 a_iter = g_list_next (a_iter);
781 s_delete_object_glist (toplevel, temp_list);
783 /* update the pinnumbers to the current slot */
784 s_slot_update_object (toplevel, o_current);
786 /* Recalculate the bounds of the object */
787 o_recalc_single_object(toplevel, o_current);
789 /* reconnect, re-select and redraw */
790 s_conn_update_object (toplevel, o_current);
791 o_selection_add (toplevel, toplevel->page_current->selection_list, o_current);
792 o_invalidate (w_current, o_current);
794 /* Re-flag as embedded if necessary */
795 o_current->complex_embedded = is_embedded;
797 /* mark the page as modified */
798 toplevel->page_current->CHANGED = 1;
799 o_undo_savestate (w_current, UNDO_ALL);
803 /*! \brief Do autosave on all pages that are marked.
804 * \par Function Description
805 * Looks for pages with the do_autosave_backup flag activated and
806 * autosaves them.
808 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
810 void o_autosave_backups(GSCHEM_TOPLEVEL *w_current)
812 TOPLEVEL *toplevel = w_current->toplevel;
813 GList *iter;
814 PAGE *p_save, *p_current;
815 gchar *backup_filename;
816 gchar *real_filename;
817 gchar *only_filename;
818 gchar *dirname;
819 mode_t saved_umask;
820 mode_t mask;
821 struct stat st;
823 /* save current page */
824 p_save = toplevel->page_current;
826 for ( iter = geda_list_get_glist( toplevel->pages );
827 iter != NULL;
828 iter = g_list_next( iter ) ) {
830 p_current = (PAGE *)iter->data;
832 if (p_current->do_autosave_backup == 0) {
833 continue;
835 if (p_current->ops_since_last_backup != 0) {
836 /* make p_current the current page of toplevel */
837 s_page_goto (toplevel, p_current);
839 /* Get the real filename and file permissions */
840 real_filename = follow_symlinks (p_current->page_filename, NULL);
842 if (real_filename == NULL) {
843 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
844 } else {
845 /* Get the directory in which the real filename lives */
846 dirname = g_path_get_dirname (real_filename);
847 only_filename = g_path_get_basename(real_filename);
849 backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
850 dirname, G_DIR_SEPARATOR, only_filename);
852 /* If there is not an existing file with that name, compute the
853 * permissions and uid/gid that we will use for the newly-created file.
856 if (stat (real_filename, &st) != 0) {
857 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
858 struct stat dir_st;
859 int result;
860 #endif
862 /* Use default permissions */
863 saved_umask = umask(0);
864 st.st_mode = 0666 & ~saved_umask;
865 umask(saved_umask);
866 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
867 st.st_uid = getuid ();
869 result = stat (dirname, &dir_st);
871 if (result == 0 && (dir_st.st_mode & S_ISGID))
872 st.st_gid = dir_st.st_gid;
873 else
874 st.st_gid = getgid ();
875 #endif
877 g_free (dirname);
878 g_free (only_filename);
879 g_free (real_filename);
881 /* Make the backup file writable before saving a new one */
882 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
883 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
884 saved_umask = umask(0);
885 if (chmod(backup_filename, (S_IWRITE|S_IWGRP|S_IWOTH) &
886 ((~saved_umask) & 0777)) != 0) {
887 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
888 backup_filename);
890 umask(saved_umask);
893 if (o_save_curr_page (toplevel, backup_filename)) {
895 p_current->ops_since_last_backup = 0;
896 p_current->do_autosave_backup = 0;
898 /* Make the backup file readonly so a 'rm *' command will ask
899 the user before deleting it */
900 saved_umask = umask(0);
901 mask = (S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
902 mask = (~mask)&0777;
903 mask &= ((~saved_umask) & 0777);
904 if (chmod(backup_filename,mask) != 0) {
905 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
906 backup_filename);
908 umask(saved_umask);
909 } else {
910 s_log_message (_("Could NOT save backup file [%s]\n"),
911 backup_filename);
913 g_free (backup_filename);
917 /* restore current page */
918 s_page_goto (toplevel, p_save);