1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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
29 #include <sys/types.h>
33 #include <libgeda/libgeda.h>
35 #include "../include/gschem_struct.h"
36 #include "../include/globals.h"
37 #include "../include/prototype.h"
39 #ifdef HAVE_LIBDMALLOC
43 /* break with the tradition here and input a list */
44 /*! \todo probably should go back and do the same for o_copy o_move
47 /*! \todo Finish function documentation!!!
49 * \par Function Description
52 void o_edit(GSCHEM_TOPLEVEL
*w_current
, GList
*list
)
59 w_current
->inside_action
= 0;
60 i_set_state(w_current
, SELECT
);
64 o_current
= (OBJECT
*) list
->data
;
65 if (o_current
== NULL
) {
66 fprintf(stderr
, _("Got an unexpected NULL in o_edit\n"));
70 /* for now deal with only the first item */
71 switch(o_current
->type
) {
73 /* also add the ability to multi attrib edit: nets, busses, pins */
75 case(OBJ_PLACEHOLDER
):
79 x_multiattrib_open (w_current
);
83 picture_change_filename_dialog(w_current
);
86 if(strchr(o_current
->text
->string
,'=')) {
88 /* now really make sure it's an attribute by
89 * checking that there are NO spaces around the ='s
91 equal_ptr
= strchr(o_current
->text
->string
, '=');
93 /* and also make sure it is only a single line */
94 num_lines
= o_text_num_lines(o_current
->text
->string
);
96 /* there is a possiblity for core dump yes? */
97 /* by exceeding the size of the text_string? */
98 /* or maybe not, since if the ='s is at the end of */
99 /* the string, there better be a null after it! */
100 if ( (*(equal_ptr
+ 1) != ' ') &&
101 (*(equal_ptr
- 1) != ' ') &&
103 attrib_edit_dialog(w_current
,o_current
, FROM_MENU
);
104 /* multi_attrib_edit(w_current, o_current); */
107 o_text_edit(w_current
, o_current
);
110 o_text_edit(w_current
, o_current
);
115 /* has to be more extensive in the future */
116 /* some sort of redrawing? */
119 /*! \todo Finish function documentation!!!
121 * \par Function Description
124 /* This locks the entire selected list. It does lock components, but does NOT
125 * change the color (of primatives of the components) though
126 * this cannot be called recursively */
127 void o_lock(GSCHEM_TOPLEVEL
*w_current
)
129 OBJECT
*object
= NULL
;
130 GList
*s_current
= NULL
;
133 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
135 while(s_current
!= NULL
) {
136 object
= (OBJECT
*) s_current
->data
;
138 /* check to see if locked_color is already being used */
139 if (object
->locked_color
== -1) {
140 object
->sel_func
= NULL
;
141 object
->locked_color
= object
->color
;
142 object
->color
= w_current
->lock_color
;
143 w_current
->toplevel
->page_current
->CHANGED
=1;
145 s_log_message(_("Object already locked\n"));
149 s_current
= g_list_next(s_current
);
152 if (!w_current
->SHIFTKEY
) o_select_unselect_all(w_current
);
153 o_undo_savestate(w_current
, UNDO_ALL
);
154 i_update_menus(w_current
);
157 /*! \todo Finish function documentation!!!
159 * \par Function Description
162 /* You can unlock something by selecting it with a bounding box... */
163 /* this will probably change in the future, but for now it's a
165 /* this cannot be called recursively */
166 void o_unlock(GSCHEM_TOPLEVEL
*w_current
)
168 OBJECT
*object
= NULL
;
169 GList
*s_current
= NULL
;
171 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
173 while(s_current
!= NULL
) {
174 object
= (OBJECT
*) s_current
->data
;
176 /* only unlock if sel_func is not set to something */
177 if (object
->sel_func
== NULL
) {
178 object
->sel_func
= select_func
;
179 object
->color
= object
->locked_color
;
180 object
->locked_color
= -1;
181 w_current
->toplevel
->page_current
->CHANGED
= 1;
183 s_log_message(_("Object already unlocked\n"));
187 s_current
= g_list_next(s_current
);
189 o_undo_savestate(w_current
, UNDO_ALL
);
192 /*! \brief Rotate all objects in list.
193 * \par Function Description
194 * Given an object <B>list</B>, and the center of rotation
195 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
196 * list, rotating each object through angle <B>angle</B>.
197 * The list contains a given object and all its attributes
198 * (refdes, pinname, pinlabel, ...).
199 * There is a second pass to run the rotate hooks of non-simple objects,
200 * like pin or complex objects, for example.
202 * \param [in] w_current The GSCHEM_TOPLEVEL object.
203 * \param [in] centerx Center x coordinate of rotation.
204 * \param [in] centery Center y coordinate of rotation.
205 * \param [in] angle Angle to rotate the objects through.
206 * \param [in] list The list of objects to rotate.
208 void o_rotate_world_update(GSCHEM_TOPLEVEL
*w_current
,
209 int centerx
, int centery
, int angle
, GList
*list
)
211 TOPLEVEL
*toplevel
= w_current
->toplevel
;
214 GList
*other_objects
=NULL
;
215 GList
*connected_objects
=NULL
;
217 /* this is okay if you just hit rotate and have nothing selected */
219 w_current
->inside_action
= 0;
220 i_set_state(w_current
, SELECT
);
224 if (!toplevel
->DONT_REDRAW
) {
225 o_cue_undraw_list (w_current
, list
);
226 o_erase_list (w_current
, list
);
229 /* Find connected objects, removing each object in turn from the
230 * connection list. We only _really_ want those objects connected
231 * to the selection, not those within in it. The extra redraws
232 * don't _really_ hurt though. */
234 while (o_iter
!= NULL
) {
235 o_current
= o_iter
->data
;
236 switch (o_current
->type
) {
238 case OBJ_PLACEHOLDER
:
240 s_conn_return_complex_others(other_objects
, o_current
);
241 s_conn_remove_complex (toplevel
, o_current
);
246 other_objects
= s_conn_return_others(other_objects
, o_current
);
247 s_conn_remove (toplevel
, o_current
);
250 o_iter
= g_list_next (o_iter
);
253 o_glist_rotate_world( toplevel
, centerx
, centery
, angle
, list
);
255 /* Find connected objects, adding each object in turn back to the
256 * connection list. We only _really_ want those objects connected
257 * to the selection, not those within in it. The extra redraws dont
258 * _really_ hurt though. */
260 while (o_iter
!= NULL
) {
261 o_current
= o_iter
->data
;
262 switch (o_current
->type
) {
264 case OBJ_PLACEHOLDER
:
265 s_conn_update_complex(toplevel
, o_current
->complex->prim_objs
);
267 s_conn_return_complex_others(connected_objects
, o_current
);
272 s_conn_update_object(toplevel
, o_current
);
273 connected_objects
= s_conn_return_others(connected_objects
, o_current
);
276 o_iter
= g_list_next (o_iter
);
279 if (!toplevel
->DONT_REDRAW
) {
280 o_draw_list (w_current
, list
);
281 o_cue_undraw_list(w_current
, other_objects
);
282 o_cue_draw_list(w_current
, other_objects
);
283 o_cue_undraw_list(w_current
, connected_objects
);
284 o_cue_draw_list(w_current
, connected_objects
);
285 o_cue_draw_list(w_current
, list
);
288 g_list_free (other_objects
);
289 other_objects
= NULL
;
290 g_list_free (connected_objects
);
291 connected_objects
= NULL
;
293 /* All objects were rotated. Do a 2nd pass to run the rotate hooks */
294 /* Do not run any hooks for simple objects here, like text, since they
295 were rotated in the previous pass, and the selection list can contain
296 an object and all its attributes (text) */
298 while (o_iter
!= NULL
) {
299 o_current
= o_iter
->data
;
301 switch(o_current
->type
) {
303 /* Run the rotate pin hook */
304 if (scm_hook_empty_p(rotate_pin_hook
) == SCM_BOOL_F
&&
306 scm_run_hook(rotate_pin_hook
,
307 scm_cons(g_make_object_smob(toplevel
, o_current
),
313 /* Run the rotate hook */
314 if (scm_hook_empty_p(rotate_component_object_hook
) == SCM_BOOL_F
&&
316 scm_run_hook(rotate_component_object_hook
,
317 scm_cons(g_make_object_smob(toplevel
, o_current
),
325 o_iter
= g_list_next(o_iter
);
328 /* Don't save the undo state if we are inside an action */
329 /* This is useful when rotating the selection while moving, for example */
330 toplevel
->page_current
->CHANGED
= 1;
331 if (!w_current
->inside_action
) {
332 o_undo_savestate(w_current
, UNDO_ALL
);
337 /*! \todo Finish function documentation!!!
339 * \par Function Description
342 void o_mirror_world_update(GSCHEM_TOPLEVEL
*w_current
, int centerx
, int centery
, GList
*list
)
344 TOPLEVEL
*toplevel
= w_current
->toplevel
;
347 GList
*other_objects
=NULL
;
348 GList
*connected_objects
=NULL
;
351 w_current
->inside_action
= 0;
352 i_set_state(w_current
, SELECT
);
356 o_cue_undraw_list (w_current
, list
);
357 o_erase_list (w_current
, list
);
359 /* Find connected objects, removing each object in turn from the
360 * connection list. We only _really_ want those objects connected
361 * to the selection, not those within in it. The extra redraws
362 * don't _really_ hurt though. */
364 while (o_iter
!= NULL
) {
365 o_current
= o_iter
->data
;
366 switch (o_current
->type
) {
368 case OBJ_PLACEHOLDER
:
370 s_conn_return_complex_others(other_objects
, o_current
);
371 s_conn_remove_complex (toplevel
, o_current
);
376 other_objects
= s_conn_return_others(other_objects
, o_current
);
377 s_conn_remove (toplevel
, o_current
);
380 o_iter
= g_list_next (o_iter
);
383 o_glist_mirror_world( toplevel
, centerx
, centery
, list
);
385 /* Find connected objects, adding each object in turn back to the
386 * connection list. We only _really_ want those objects connected
387 * to the selection, not those within in it. The extra redraws dont
388 * _really_ hurt though. */
390 while (o_iter
!= NULL
) {
391 o_current
= o_iter
->data
;
392 switch (o_current
->type
) {
394 case OBJ_PLACEHOLDER
:
395 s_conn_update_complex(toplevel
, o_current
->complex->prim_objs
);
397 s_conn_return_complex_others(connected_objects
, o_current
);
402 s_conn_update_object(toplevel
, o_current
);
403 connected_objects
= s_conn_return_others(connected_objects
, o_current
);
406 o_iter
= g_list_next (o_iter
);
409 o_draw_list (w_current
, list
);
410 o_cue_undraw_list(w_current
, other_objects
);
411 o_cue_draw_list(w_current
, other_objects
);
412 o_cue_undraw_list(w_current
, connected_objects
);
413 o_cue_draw_list(w_current
, connected_objects
);
414 o_cue_draw_list(w_current
, list
);
416 g_list_free (other_objects
);
417 other_objects
= NULL
;
418 g_list_free (connected_objects
);
419 connected_objects
= NULL
;
421 /* All objects were mirrored. Do a 2nd pass to run the mirror hooks */
422 /* Do not run any hooks for simple objects here, like text, since they
423 were mirrored in the previous pass, and the selection list can contain
424 an object and all its attributes (text) */
426 while (o_iter
!= NULL
) {
427 o_current
= (OBJECT
*) o_iter
->data
;
429 switch(o_current
->type
) {
431 /* Run the mirror pin hook */
432 if (scm_hook_empty_p(mirror_pin_hook
) == SCM_BOOL_F
&&
434 scm_run_hook(mirror_pin_hook
,
435 scm_cons(g_make_object_smob(toplevel
, o_current
),
441 /* Run the mirror pin hook */
442 if (scm_hook_empty_p(mirror_component_object_hook
) == SCM_BOOL_F
&&
444 scm_run_hook(mirror_component_object_hook
,
445 scm_cons(g_make_object_smob(toplevel
, o_current
),
453 o_iter
= g_list_next(o_iter
);
456 toplevel
->page_current
->CHANGED
=1;
457 o_undo_savestate(w_current
, UNDO_ALL
);
460 /*! \todo Finish function documentation!!!
462 * \par Function Description
465 void o_edit_show_hidden_lowlevel(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_list
)
467 TOPLEVEL
*toplevel
= w_current
->toplevel
;
468 OBJECT
*o_current
= o_list
;
470 if (o_current
== NULL
) {
474 while(o_current
!= NULL
) {
475 if (o_current
->type
== OBJ_TEXT
&& o_current
->visibility
== INVISIBLE
) {
477 /* don't toggle the visibility flag */
479 if (toplevel
->show_hidden_text
) {
480 /* draw the text object if it hidden */
481 if (o_current
->text
->prim_objs
== NULL
) {
482 o_text_recreate(toplevel
, o_current
);
484 o_text_recalc(toplevel
, o_current
);
485 o_text_draw(w_current
, o_current
);
487 /* object is hidden and we are now NOT drawing it, so */
488 /* get rid of the extra primitive data */
489 o_text_recreate(toplevel
, o_current
);
490 o_text_recalc(toplevel
, o_current
);
491 /* unfortunately, you cannot erase the old visible text here */
492 /* because o_text_draw will just return */
496 if (o_current
->type
== OBJ_COMPLEX
|| o_current
->type
== OBJ_PLACEHOLDER
) {
497 o_edit_show_hidden_lowlevel(w_current
, o_current
->complex->prim_objs
);
498 o_complex_recalc(toplevel
, o_current
);
501 o_current
= o_current
->next
;
505 /*! \todo Finish function documentation!!!
507 * \par Function Description
510 void o_edit_show_hidden(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_list
)
512 /* this function just shows the hidden text, but doesn't toggle it */
513 /* this function does not change the CHANGED bit, no real changes are */
514 /* made to the schematic */
516 /* toggle show_hidden_text variable, which when it is true */
517 /* means that hidden text IS drawn */
518 w_current
->toplevel
->show_hidden_text
= !w_current
->toplevel
->show_hidden_text
;
519 i_show_state(w_current
, NULL
); /* update screen status */
521 o_edit_show_hidden_lowlevel(w_current
, o_list
);
522 o_redraw_all(w_current
);
524 if (w_current
->toplevel
->show_hidden_text
) {
525 s_log_message(_("Hidden text is now visible\n"));
527 s_log_message(_("Hidden text is now invisible\n"));
531 /*! \todo Finish function documentation!!!
533 * \par Function Description
536 void o_edit_make_visible(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_list
)
538 /* this function actually changes the visibility flag */
539 TOPLEVEL
*toplevel
= w_current
->toplevel
;
540 OBJECT
*o_current
= NULL
;
546 while(o_current
!= NULL
) {
548 if (o_current
->type
== OBJ_TEXT
) {
549 if (o_current
->visibility
== INVISIBLE
) {
550 o_current
->visibility
= VISIBLE
;
552 if (o_current
->text
->prim_objs
== NULL
) {
553 o_text_recreate(toplevel
, o_current
);
556 o_text_draw(w_current
, o_current
);
558 toplevel
->page_current
->CHANGED
= 1;
561 o_current
= o_current
->next
;
563 o_undo_savestate(w_current
, UNDO_ALL
);
567 #define FIND_WINDOW_HALF_SIZE (5000)
569 OBJECT
*last_o
= NULL
;
572 /*! \todo Finish function documentation!!!
574 * \par Function Description
577 int o_edit_find_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
, char *stext
,
578 int descend
, int skip
)
580 TOPLEVEL
*toplevel
= w_current
->toplevel
;
584 char *current_filename
= NULL
;
585 int page_control
= 0;
588 int text_screen_height
;
590 OBJECT
*o_current
= NULL
;
595 if (o_current
== NULL
) {
599 while (o_current
!= NULL
) {
602 if (o_current
->type
== OBJ_COMPLEX
) {
603 parent
= toplevel
->page_current
;
604 attrib
= o_attrib_search_name_single_count(o_current
, "source", count
);
606 /* if above is null, then look inside symbol */
607 if (attrib
== NULL
) {
608 attrib
= o_attrib_search_name(o_current
->complex->prim_objs
,
610 /* looking_inside = TRUE; */
615 current_filename
= u_basic_breakup_string(attrib
, ',', pcount
);
616 if (current_filename
!= NULL
) {
618 s_hierarchy_down_schematic_single(toplevel
,
622 HIERARCHY_NORMAL_LOAD
);
623 /* o_redraw_all(w_current); */
625 rv
= o_edit_find_text(w_current
,
626 toplevel
->page_current
->object_head
,
627 stext
, descend
, skiplast
);
631 s_page_goto( toplevel
, parent
);
637 if (o_current
->type
== OBJ_TEXT
) {
638 /* replaced strcmp with strstr to simplify the search */
639 if (strstr(o_current
->text
->string
,stext
)) {
641 a_zoom(w_current
, ZOOM_FULL
, DONTCARE
, A_PAN_DONT_REDRAW
);
643 SCREENabs(toplevel
, o_text_height(o_current
->text
->string
,
644 o_current
->text
->size
));
645 /* this code will zoom/pan till the text screen height is about */
646 /* 50 pixels high, perhaps a future enhancement will be to make */
647 /* this number configurable */
648 while (text_screen_height
< 50) {
649 a_zoom(w_current
, ZOOM_IN
, DONTCARE
, A_PAN_DONT_REDRAW
);
651 SCREENabs(toplevel
, o_text_height(o_current
->text
->string
,
652 o_current
->text
->size
));
654 a_pan_general(w_current
,
655 o_current
->text
->x
, o_current
->text
->y
,
658 /* Make sure the titlebar and scrollbars are up-to-date */
659 x_window_set_current_page(w_current
,
660 w_current
->toplevel
->page_current
);
665 if (last_o
== o_current
) {
669 } /* if (strstr(o_current->text->string,stext)) */
670 } /* if (o_current->type == OBJ_TEXT) */
671 o_current
= o_current
->next
;
673 if (o_current
== NULL
) {
677 return (o_current
== NULL
);
681 /*! \todo Finish function documentation!!!
683 * \par Function Description
686 void o_edit_hide_specific_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
,
689 TOPLEVEL
*toplevel
= w_current
->toplevel
;
690 OBJECT
*o_current
= NULL
;
697 while (o_current
!= NULL
) {
699 if (o_current
->type
== OBJ_TEXT
) {
700 if (!strncmp(stext
, o_current
->text
->string
, strlen(stext
))) {
701 if (o_current
->visibility
== VISIBLE
) {
702 o_current
->visibility
= INVISIBLE
;
704 if (o_current
->text
->prim_objs
== NULL
) {
705 o_text_recreate(toplevel
, o_current
);
707 toplevel
->page_current
->CHANGED
= 1;
711 o_current
= o_current
->next
;
713 o_undo_savestate(w_current
, UNDO_ALL
);
714 o_redraw_all(w_current
);
717 /*! \todo Finish function documentation!!!
719 * \par Function Description
722 void o_edit_show_specific_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
,
725 TOPLEVEL
*toplevel
= w_current
->toplevel
;
726 OBJECT
*o_current
= NULL
;
733 while (o_current
!= NULL
) {
735 if (o_current
->type
== OBJ_TEXT
) {
736 if (!strncmp(stext
, o_current
->text
->string
, strlen(stext
))) {
737 if (o_current
->visibility
== INVISIBLE
) {
738 o_current
->visibility
= VISIBLE
;
740 if (o_current
->text
->prim_objs
== NULL
) {
741 o_text_recreate(toplevel
, o_current
);
743 o_text_draw(w_current
, o_current
);
744 toplevel
->page_current
->CHANGED
= 1;
748 o_current
= o_current
->next
;
750 o_undo_savestate(w_current
, UNDO_ALL
);
753 /*! \todo Finish function documentation!!!
755 * \par Function Description
758 void o_update_component(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
760 TOPLEVEL
*toplevel
= w_current
->toplevel
;
761 OBJECT
*tmp_list
, *new_complex
;
764 gboolean is_embedded
;
765 const CLibSymbol
*clib
;
767 g_return_if_fail (o_current
!= NULL
);
769 is_embedded
= o_complex_is_embedded (o_current
);
771 g_assert (o_current
->complex_basename
!= NULL
);
772 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
775 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
776 o_current
->complex_basename
);
780 /* erase the complex object */
781 o_erase_single (w_current
, o_current
);
782 /* delete its connections */
783 s_conn_remove_complex (toplevel
, o_current
);
784 /* and unselect it */
785 o_selection_remove( toplevel
->page_current
->selection_list
, o_current
);
787 /* build a temporary list and add a complex to this list */
788 tmp_list
= s_basic_init_object ("update component");
789 new_complex
= o_complex_add (toplevel
,
793 o_current
->complex->x
,
794 o_current
->complex->y
,
795 o_current
->complex->angle
,
796 o_current
->complex->mirror
,
797 clib
, o_current
->complex_basename
,
800 /* updating the old complex with data from the new one */
801 /* first process the prim_objs: */
802 /* - delete the prim_objs of the old component */
803 s_delete_list_fromstart (toplevel
,
804 o_current
->complex->prim_objs
);
805 /* - put the prim_objs of the new component in the old one */
806 o_current
->complex->prim_objs
= new_complex
->complex->prim_objs
;
807 /* - reset the new complex prim_objs */
808 new_complex
->complex->prim_objs
= NULL
;
810 /* then process the attributes: */
811 /* - check each attrib of the new complex */
812 a_iter
= new_complex
->attribs
;
813 while (a_iter
!= NULL
) {
814 a_current
= a_iter
->data
;
818 g_assert (a_current
->object
->type
== OBJ_TEXT
);
819 o_attrib_get_name_value (a_current
->object
->text
->string
,
822 attrfound
= o_attrib_search_name_single(o_current
, name
, NULL
);
824 /* free these now since they are no longer being used */
825 if (name
) { g_free(name
); }
826 if (value
) { g_free(value
); }
828 if (attrfound
== NULL
) {
829 /* attribute with same name not found in old component: */
830 /* add new attribute to old component */
832 /* make a copy of the attribute object */
833 o_list_copy_to (toplevel
, o_current
,
834 a_current
->object
, NORMAL_FLAG
, &o_attrib
);
835 /* add the attribute to old */
836 o_attrib_add (toplevel
, o_current
, o_attrib
);
837 /* redraw the attribute object */
838 o_redraw_single (w_current
, o_attrib
);
839 /* note: this object is unselected (not added to selection). */
847 a_iter
= g_list_next (a_iter
);
850 /* finally delete the temp list with the updated complex */
851 s_delete_list_fromstart (toplevel
, tmp_list
);
853 /* Recalculate the bounds of the object */
854 o_complex_recalc(toplevel
, o_current
);
856 /* reconnect, re-select and redraw */
857 s_conn_update_complex (toplevel
, o_current
->complex->prim_objs
);
858 o_selection_add( toplevel
->page_current
->selection_list
, o_current
);
859 o_redraw_single (w_current
, o_current
);
861 /* Re-flag as embedded if necessary */
862 o_current
->complex_embedded
= is_embedded
;
864 /* mark the page as modified */
865 toplevel
->page_current
->CHANGED
= 1;
866 o_undo_savestate (w_current
, UNDO_ALL
);
870 /*! \brief Do autosave on all pages that are marked.
871 * \par Function Description
872 * Looks for pages with the do_autosave_backup flag activated and
875 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
877 void o_autosave_backups(GSCHEM_TOPLEVEL
*w_current
)
879 TOPLEVEL
*toplevel
= w_current
->toplevel
;
881 PAGE
*p_save
, *p_current
;
882 gchar
*backup_filename
;
883 gchar
*real_filename
;
884 gchar
*only_filename
;
890 /* save current page */
891 p_save
= toplevel
->page_current
;
893 for ( iter
= geda_list_get_glist( toplevel
->pages
);
895 iter
= g_list_next( iter
) ) {
897 p_current
= (PAGE
*)iter
->data
;
899 if (p_current
->do_autosave_backup
== 0) {
902 if (p_current
->ops_since_last_backup
!= 0) {
903 /* make p_current the current page of toplevel */
904 s_page_goto (toplevel
, p_current
);
906 /* Get the real filename and file permissions */
907 real_filename
= follow_symlinks (p_current
->page_filename
, NULL
);
909 if (real_filename
== NULL
) {
910 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current
->page_filename
);
912 /* Get the directory in which the real filename lives */
913 dirname
= g_path_get_dirname (real_filename
);
914 only_filename
= g_path_get_basename(real_filename
);
916 backup_filename
= g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING
,
917 dirname
, G_DIR_SEPARATOR
, only_filename
);
919 /* If there is not an existing file with that name, compute the
920 * permissions and uid/gid that we will use for the newly-created file.
923 if (stat (real_filename
, &st
) != 0) {
924 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
929 /* Use default permissions */
930 saved_umask
= umask(0);
931 st
.st_mode
= 0666 & ~saved_umask
;
933 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
934 st
.st_uid
= getuid ();
936 result
= stat (dirname
, &dir_st
);
938 if (result
== 0 && (dir_st
.st_mode
& S_ISGID
))
939 st
.st_gid
= dir_st
.st_gid
;
941 st
.st_gid
= getgid ();
945 g_free (only_filename
);
946 g_free (real_filename
);
948 /* Make the backup file writable before saving a new one */
949 if ( g_file_test (backup_filename
, G_FILE_TEST_EXISTS
) &&
950 (! g_file_test (backup_filename
, G_FILE_TEST_IS_DIR
))) {
951 saved_umask
= umask(0);
952 if (chmod(backup_filename
, (S_IWRITE
|S_IWGRP
|S_IWOTH
) &
953 ((~saved_umask
) & 0777)) != 0) {
954 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
960 if (o_save (toplevel
, backup_filename
)) {
962 p_current
->ops_since_last_backup
= 0;
963 p_current
->do_autosave_backup
= 0;
965 /* Make the backup file readonly so a 'rm *' command will ask
966 the user before deleting it */
967 saved_umask
= umask(0);
968 mask
= (S_IWRITE
|S_IWGRP
|S_IEXEC
|S_IXGRP
|S_IXOTH
);
970 mask
&= ((~saved_umask
) & 0777);
971 if (chmod(backup_filename
,mask
) != 0) {
972 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
977 s_log_message (_("Could NOT save backup file [%s]\n"),
980 g_free (backup_filename
);
984 /* restore current page */
985 s_page_goto (toplevel
, p_save
);