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>
35 #ifdef HAVE_LIBDMALLOC
39 static void o_edit_show_hidden_lowlevel(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_list
);
41 /* break with the tradition here and input a list */
42 /*! \todo probably should go back and do the same for o_copy o_move
45 /*! \todo Finish function documentation!!!
47 * \par Function Description
50 void o_edit(GSCHEM_TOPLEVEL
*w_current
, SELECTION
*selection
)
53 const gchar
*str
= NULL
;
56 if (selection
== NULL
) {
57 g_critical(_("Got NULL selection in o_edit\n"));
61 list
= geda_list_get_glist(selection
);
63 w_current
->inside_action
= 0;
64 i_set_state(w_current
, SELECT
);
68 o_current
= list
->data
;
69 if (o_current
== NULL
) {
70 fprintf(stderr
, _("Got an unexpected NULL in o_edit\n"));
74 /* for now deal with only the first item */
75 switch(o_current
->type
) {
77 /* also add the ability to multi attrib edit: nets, busses, pins */
79 case(OBJ_PLACEHOLDER
):
83 x_multiattrib_open (w_current
, selection
);
87 picture_change_filename_dialog(w_current
);
90 arc_angle_dialog(w_current
, o_current
);
93 str
= o_text_get_string(o_current
);
94 if (o_attrib_get_name_value (str
, NULL
, NULL
) &&
95 /* attribute editor only accept 1-line values for attribute */
96 o_text_num_lines (str
) == 1) {
97 attrib_edit_dialog(w_current
,o_current
, FROM_MENU
);
99 o_text_edit(w_current
, o_current
);
104 /* has to be more extensive in the future */
105 /* some sort of redrawing? */
108 /*! \todo Finish function documentation!!!
110 * \par Function Description
113 /* This locks the entire selected list. It does lock components, but does NOT
114 * change the color (of primitives of the components) though
115 * this cannot be called recursively */
116 void o_lock(GSCHEM_TOPLEVEL
*w_current
)
118 OBJECT
*object
= NULL
;
119 GList
*s_current
= NULL
;
122 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
124 while(s_current
!= NULL
) {
125 object
= s_current
->data
;
127 /* check to see if locked_color is already being used */
128 if (object
->locked_color
== -1) {
129 object
->selectable
= 0;
130 object
->locked_color
= object
->color
;
131 object
->color
= w_current
->lock_color
;
132 w_current
->toplevel
->page_current
->CHANGED
=1;
134 s_log_message(_("Object already locked\n"));
138 s_current
= g_list_next(s_current
);
141 if (!w_current
->SHIFTKEY
) o_select_unselect_all(w_current
);
142 o_undo_savestate(w_current
, UNDO_ALL
);
143 i_update_menus(w_current
);
146 /*! \todo Finish function documentation!!!
148 * \par Function Description
151 /* You can unlock something by selecting it with a bounding box... */
152 /* this will probably change in the future, but for now it's a
154 /* this cannot be called recursively */
155 void o_unlock(GSCHEM_TOPLEVEL
*w_current
)
157 OBJECT
*object
= NULL
;
158 GList
*s_current
= NULL
;
160 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
162 while(s_current
!= NULL
) {
163 object
= s_current
->data
;
165 if (!object
->selectable
) {
166 object
->selectable
= 1;
167 object
->color
= object
->locked_color
;
168 object
->locked_color
= -1;
169 w_current
->toplevel
->page_current
->CHANGED
= 1;
171 s_log_message(_("Object already unlocked\n"));
175 s_current
= g_list_next(s_current
);
177 o_undo_savestate(w_current
, UNDO_ALL
);
180 /*! \brief Rotate all objects in list.
181 * \par Function Description
182 * Given an object <B>list</B>, and the center of rotation
183 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
184 * list, rotating each object through angle <B>angle</B>.
185 * The list contains a given object and all its attributes
186 * (refdes, pinname, pinlabel, ...).
187 * There is a second pass to run the rotate hooks of non-simple objects,
188 * like pin or complex objects, for example.
190 * \param [in] w_current The GSCHEM_TOPLEVEL object.
191 * \param [in] centerx Center x coordinate of rotation.
192 * \param [in] centery Center y coordinate of rotation.
193 * \param [in] angle Angle to rotate the objects through.
194 * \param [in] list The list of objects to rotate.
196 void o_rotate_world_update(GSCHEM_TOPLEVEL
*w_current
,
197 int centerx
, int centery
, int angle
, GList
const *list
)
199 TOPLEVEL
*toplevel
= w_current
->toplevel
;
202 GList
*other_objects
=NULL
;
203 GList
*connected_objects
=NULL
;
205 /* this is okay if you just hit rotate and have nothing selected */
207 w_current
->inside_action
= 0;
208 i_set_state(w_current
, SELECT
);
212 if (!toplevel
->DONT_REDRAW
) {
213 o_cue_undraw_list (w_current
, list
);
214 o_erase_list (w_current
, list
);
217 /* Find connected objects, removing each object in turn from the
218 * connection list. We only _really_ want those objects connected
219 * to the selection, not those within in it. The extra redraws
220 * don't _really_ hurt though. */
222 while (o_iter
!= NULL
) {
223 o_current
= o_iter
->data
;
224 switch (o_current
->type
) {
226 case OBJ_PLACEHOLDER
:
228 s_conn_return_complex_others(other_objects
, o_current
);
229 s_conn_remove_complex(o_current
);
234 other_objects
= s_conn_return_others(other_objects
, o_current
);
235 s_conn_remove(o_current
);
238 o_iter
= g_list_next (o_iter
);
241 o_glist_rotate_world(centerx
, centery
, angle
, list
);
243 /* Find connected objects, adding each object in turn back to the
244 * connection list. We only _really_ want those objects connected
245 * to the selection, not those within in it. The extra redraws dont
246 * _really_ hurt though. */
248 while (o_iter
!= NULL
) {
249 o_current
= o_iter
->data
;
250 switch (o_current
->type
) {
252 case OBJ_PLACEHOLDER
:
253 s_conn_update_object(toplevel
->page_current
, o_current
);
255 s_conn_return_complex_others(connected_objects
, o_current
);
260 s_conn_update_object(toplevel
->page_current
, o_current
);
261 connected_objects
= s_conn_return_others(connected_objects
, o_current
);
264 o_iter
= g_list_next (o_iter
);
267 if (!toplevel
->DONT_REDRAW
) {
268 o_draw_list (w_current
, list
);
269 o_cue_undraw_list(w_current
, other_objects
);
270 o_cue_draw_list(w_current
, other_objects
);
271 o_cue_undraw_list(w_current
, connected_objects
);
272 o_cue_draw_list(w_current
, connected_objects
);
273 o_cue_draw_list(w_current
, list
);
276 g_list_free (other_objects
);
277 other_objects
= NULL
;
278 g_list_free (connected_objects
);
279 connected_objects
= NULL
;
281 /* All objects were rotated. Do a 2nd pass to run the rotate hooks */
282 /* Do not run any hooks for simple objects here, like text, since they
283 were rotated in the previous pass, and the selection list can contain
284 an object and all its attributes (text) */
286 while (o_iter
!= NULL
) {
287 o_current
= o_iter
->data
;
289 switch(o_current
->type
) {
291 /* Run the rotate pin hook */
292 if (scm_hook_empty_p(rotate_pin_hook
) == SCM_BOOL_F
&&
294 scm_run_hook(rotate_pin_hook
,
295 scm_cons(g_make_object_smob(toplevel
, o_current
),
301 /* Run the rotate hook */
302 if (scm_hook_empty_p(rotate_component_object_hook
) == SCM_BOOL_F
&&
304 scm_run_hook(rotate_component_object_hook
,
305 scm_cons(g_make_object_smob(toplevel
, o_current
),
313 o_iter
= g_list_next(o_iter
);
316 /* Don't save the undo state if we are inside an action */
317 /* This is useful when rotating the selection while moving, for example */
318 toplevel
->page_current
->CHANGED
= 1;
319 if (!w_current
->inside_action
) {
320 o_undo_savestate(w_current
, UNDO_ALL
);
325 /*! \todo Finish function documentation!!!
327 * \par Function Description
330 void o_mirror_world_update(GSCHEM_TOPLEVEL
*w_current
, int centerx
, int centery
, GList
*list
)
332 TOPLEVEL
*toplevel
= w_current
->toplevel
;
335 GList
*other_objects
=NULL
;
336 GList
*connected_objects
=NULL
;
339 w_current
->inside_action
= 0;
340 i_set_state(w_current
, SELECT
);
344 o_cue_undraw_list (w_current
, list
);
345 o_erase_list (w_current
, list
);
347 /* Find connected objects, removing each object in turn from the
348 * connection list. We only _really_ want those objects connected
349 * to the selection, not those within in it. The extra redraws
350 * don't _really_ hurt though. */
352 while (o_iter
!= NULL
) {
353 o_current
= o_iter
->data
;
354 switch (o_current
->type
) {
356 case OBJ_PLACEHOLDER
:
358 s_conn_return_complex_others(other_objects
, o_current
);
359 s_conn_remove_complex(o_current
);
364 other_objects
= s_conn_return_others(other_objects
, o_current
);
365 s_conn_remove(o_current
);
368 o_iter
= g_list_next (o_iter
);
371 o_glist_mirror_world(centerx
, centery
, list
);
373 /* Find connected objects, adding each object in turn back to the
374 * connection list. We only _really_ want those objects connected
375 * to the selection, not those within in it. The extra redraws dont
376 * _really_ hurt though. */
378 while (o_iter
!= NULL
) {
379 o_current
= o_iter
->data
;
380 switch (o_current
->type
) {
382 case OBJ_PLACEHOLDER
:
383 s_conn_update_object(toplevel
->page_current
, o_current
);
385 s_conn_return_complex_others(connected_objects
, o_current
);
390 s_conn_update_object(toplevel
->page_current
, o_current
);
391 connected_objects
= s_conn_return_others(connected_objects
, o_current
);
394 o_iter
= g_list_next (o_iter
);
397 o_draw_list (w_current
, list
);
398 o_cue_undraw_list(w_current
, other_objects
);
399 o_cue_draw_list(w_current
, other_objects
);
400 o_cue_undraw_list(w_current
, connected_objects
);
401 o_cue_draw_list(w_current
, connected_objects
);
402 o_cue_draw_list(w_current
, list
);
404 g_list_free (other_objects
);
405 other_objects
= NULL
;
406 g_list_free (connected_objects
);
407 connected_objects
= NULL
;
409 /* All objects were mirrored. Do a 2nd pass to run the mirror hooks */
410 /* Do not run any hooks for simple objects here, like text, since they
411 were mirrored in the previous pass, and the selection list can contain
412 an object and all its attributes (text) */
414 while (o_iter
!= NULL
) {
415 o_current
= o_iter
->data
;
417 switch(o_current
->type
) {
419 /* Run the mirror pin hook */
420 if (scm_hook_empty_p(mirror_pin_hook
) == SCM_BOOL_F
&&
422 scm_run_hook(mirror_pin_hook
,
423 scm_cons(g_make_object_smob(toplevel
, o_current
),
429 /* Run the mirror pin hook */
430 if (scm_hook_empty_p(mirror_component_object_hook
) == SCM_BOOL_F
&&
432 scm_run_hook(mirror_component_object_hook
,
433 scm_cons(g_make_object_smob(toplevel
, o_current
),
441 o_iter
= g_list_next(o_iter
);
444 toplevel
->page_current
->CHANGED
=1;
445 o_undo_savestate(w_current
, UNDO_ALL
);
448 static enum visit_result
449 show_hidden_lowlevel_one(OBJECT
*o_current
, void *userdata
)
451 GSCHEM_TOPLEVEL
*w_current
= userdata
;
453 if (o_current
->type
== OBJ_TEXT
&& o_current
->visibility
== INVISIBLE
) {
454 /* don't toggle the visibility flag */
456 if (o_current
->show_hidden
) {
457 /* draw the text object if it hidden */
458 o_text_recreate(o_current
);
459 o_recalc_single_object(o_current
);
460 o_text_draw(w_current
, o_current
);
462 /* object is hidden and we are now NOT drawing it, so */
463 /* get rid of the extra primitive data */
464 o_text_recreate(o_current
);
465 o_recalc_single_object(o_current
);
466 /* unfortunately, you cannot erase the old visible text here */
467 /* because o_text_draw will just return */
471 if (o_current
->type
== OBJ_COMPLEX
|| o_current
->type
== OBJ_PLACEHOLDER
) {
472 o_edit_show_hidden_lowlevel(w_current
, o_current
->complex->prim_objs
);
473 o_recalc_single_object(o_current
);
479 /*! \todo Finish function documentation!!!
481 * \par Function Description
484 static void o_edit_show_hidden_lowlevel(GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_list
)
486 s_visit_list(o_list
, LIST_KIND_HEAD
,
487 &show_hidden_lowlevel_one
, w_current
, VISIT_ANY
, 1);
490 static enum visit_result
491 show_hidden_one(OBJECT
*o_current
, void *userdata
)
493 TOPLEVEL
*toplevel
= userdata
;
495 o_current
->show_hidden
= toplevel
->show_hidden_default
;
500 /*! \todo Finish function documentation!!!
502 * \par Function Description
505 void o_edit_show_hidden(GSCHEM_TOPLEVEL
*w_current
, PAGE
*page
)
507 /* this function just shows the hidden text, but doesn't toggle it */
508 /* this function does not change the CHANGED bit, no real changes are */
509 /* made to the schematic */
510 TOPLEVEL
*toplevel
= w_current
->toplevel
;
512 /* toggle show_hidden_default variable, which when it is true */
513 /* means that hidden text IS drawn */
514 toplevel
->show_hidden_default
= !toplevel
->show_hidden_default
;
515 i_show_state(w_current
, NULL
); /* update screen status */
517 /* Tell all objects to show themselves despite really being invisible. */
518 s_visit_page(page
, &show_hidden_one
, toplevel
, VISIT_ANY
, 1);
520 o_edit_show_hidden_lowlevel(w_current
, page
->object_head
);
521 o_redraw_all(w_current
);
523 if (toplevel
->show_hidden_default
) {
524 s_log_message(_("Hidden text is now visible\n"));
526 s_log_message(_("Hidden text is now invisible\n"));
530 static enum visit_result
make_visible_one(OBJECT
*o_current
, void *userdata
)
532 GSCHEM_TOPLEVEL
*w_current
= userdata
;
534 if (o_current
->type
== OBJ_TEXT
) {
535 if (o_current
->visibility
== INVISIBLE
) {
536 o_text_change(o_current
, NULL
, VISIBLE
, LEAVE_NAME_VALUE_ALONE
);
538 o_text_recreate(o_current
);
540 o_text_draw(w_current
, o_current
);
542 /* XXX No, don't use page_current when an explicit PAGE * is nearby. */
543 w_current
->toplevel
->page_current
->CHANGED
= 1;
550 /*! \todo Finish function documentation!!!
552 * \par Function Description
555 void o_edit_make_visible(GSCHEM_TOPLEVEL
*w_current
, PAGE
*page
)
557 /* this function actually changes the visibility flag */
558 s_visit_page(page
, &make_visible_one
, w_current
, VISIT_ANY
, 1);
559 o_undo_savestate(w_current
, UNDO_ALL
);
562 #define FIND_WINDOW_HALF_SIZE (5000)
564 OBJECT
*last_o
= NULL
;
567 /*! \todo Finish function documentation!!!
569 * \par Function Description
572 int o_edit_find_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
, char *stext
,
573 int descend
, int skip
)
575 TOPLEVEL
*toplevel
= w_current
->toplevel
;
576 PAGE
*page
= toplevel
->page_current
;
580 char *current_filename
= NULL
;
581 int page_control
= 0;
584 int text_screen_height
;
586 OBJECT
*o_current
= NULL
;
591 if (o_current
== NULL
) {
595 while (o_current
!= NULL
) {
597 if (o_current
->type
== OBJ_COMPLEX
) {
599 attrib
= o_attrib_search_name_single_count(o_current
, "source", count
);
601 /* if above is null, then look inside symbol */
602 if (attrib
== NULL
) {
603 attrib
= o_attrib_search_object(o_current
, "source", count
);
604 /* looking_inside = TRUE; */
609 current_filename
= u_basic_breakup_string(attrib
, ',', pcount
);
610 if (current_filename
!= NULL
) {
612 s_hierarchy_down_schematic_single(toplevel
,
616 HIERARCHY_NORMAL_LOAD
);
617 /* o_redraw_all(w_current); */
619 rv
= o_edit_find_text(w_current
,
621 stext
, descend
, skiplast
);
625 s_toplevel_goto_page(toplevel
, parent
);
632 if (o_current
->type
== OBJ_TEXT
) {
633 const gchar
*str
= o_text_get_string(o_current
);
636 /* replaced strcmp with strstr to simplify the search */
637 if (strstr (str
,stext
)) {
639 a_zoom(w_current
, page
, ZOOM_FULL
, DONTCARE
, A_PAN_DONT_REDRAW
);
641 SCREENabs(page
, o_text_height(o_current
, str
));
642 /* this code will zoom/pan till the text screen height is about */
643 /* 50 pixels high, perhaps a future enhancement will be to make */
644 /* this number configurable */
645 while (text_screen_height
< 50) {
646 a_zoom(w_current
, page
, ZOOM_IN
, DONTCARE
, A_PAN_DONT_REDRAW
);
648 SCREENabs(page
, o_text_height(o_current
, str
));
650 o_text_get_xy(o_current
, &x
, &y
);
651 a_pan_general(w_current
, page
, x
, y
,
652 page
->to_screen_y_constant
, A_PAN_FULL
);
654 /* Make sure the titlebar and scrollbars are up-to-date */
655 x_window_set_current_page(w_current
, page
);
660 if (last_o
== o_current
) {
664 } /* if (o_current->type == OBJ_TEXT) */
665 o_current
= o_current
->next
;
667 if (o_current
== NULL
) {
671 return (o_current
== NULL
);
675 /*! \todo Finish function documentation!!!
677 * \par Function Description
680 void o_edit_hide_specific_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
,
683 TOPLEVEL
*toplevel
= w_current
->toplevel
;
684 OBJECT
*o_current
= NULL
;
691 while (o_current
!= NULL
) {
692 if (o_current
->type
== OBJ_TEXT
) {
693 const gchar
*str
= o_text_get_string(o_current
);
694 if (!strncmp (stext
, str
, strlen (stext
))) {
695 if (o_current
->visibility
== VISIBLE
) {
696 o_text_change(o_current
, NULL
, INVISIBLE
, LEAVE_NAME_VALUE_ALONE
);
698 o_text_recreate(o_current
);
699 toplevel
->page_current
->CHANGED
= 1;
703 o_current
= o_current
->next
;
705 o_undo_savestate(w_current
, UNDO_ALL
);
706 o_redraw_all(w_current
);
709 /*! \todo Finish function documentation!!!
711 * \par Function Description
714 void o_edit_show_specific_text(GSCHEM_TOPLEVEL
*w_current
, OBJECT
* o_list
,
717 TOPLEVEL
*toplevel
= w_current
->toplevel
;
718 OBJECT
*o_current
= NULL
;
725 while (o_current
!= NULL
) {
726 if (o_current
->type
== OBJ_TEXT
) {
727 const gchar
*str
= o_text_get_string(o_current
);
728 if (!strncmp (stext
, str
, strlen (stext
))) {
729 if (o_current
->visibility
== INVISIBLE
) {
730 o_text_change(o_current
, NULL
, VISIBLE
, LEAVE_NAME_VALUE_ALONE
);
732 o_text_recreate(o_current
);
733 o_text_draw(w_current
, o_current
);
734 toplevel
->page_current
->CHANGED
= 1;
738 o_current
= o_current
->next
;
740 o_undo_savestate(w_current
, UNDO_ALL
);
743 /*! \todo Finish function documentation!!!
745 * \par Function Description
748 void o_update_component(GSCHEM_TOPLEVEL
*w_current
, PAGE
*page
, OBJECT
*o_current
)
750 TOPLEVEL
*toplevel
= w_current
->toplevel
;
751 OBJECT
*tmp_list
, *new_complex
;
755 gboolean is_embedded
;
756 const CLibSymbol
*clib
;
758 g_return_if_fail (o_current
!= NULL
);
760 is_embedded
= o_complex_is_embedded (o_current
);
762 g_assert (o_current
->complex_basename
!= NULL
);
764 /* This should be replaced with API to invalidate only the specific
765 * symbol name we want to update */
766 s_clib_flush_symbol_cache ();
767 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
770 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
771 o_current
->complex_basename
);
775 /* erase the complex object */
776 o_erase_single (w_current
, o_current
);
777 /* delete its connections */
778 s_conn_remove_complex(o_current
);
779 /* and unselect it */
780 o_selection_remove(page
->selection_list
, o_current
);
782 /* build a temporary list and add a complex to this list */
783 tmp_list
= s_toplevel_new_object(toplevel
, OBJ_HEAD
, "update component");
784 new_complex
= o_complex_new (toplevel
, OBJ_COMPLEX
, WHITE
,
785 o_current
->complex->x
,
786 o_current
->complex->y
,
787 o_current
->complex->angle
,
788 o_current
->complex->mirror
,
789 clib
, o_current
->complex_basename
,
791 tmp_list
= s_basic_link_object(new_complex
, tmp_list
);
792 o_complex_promote_attribs(toplevel
, NULL
, new_complex
);
794 /* updating the old complex with data from the new one */
795 /* first process the prim_objs: */
796 /* - delete the prim_objs of the old component */
797 s_delete_list_fromstart (toplevel
,
798 o_current
->complex->prim_objs
);
799 /* - put the prim_objs of the new component in the old one */
800 o_current
->complex->prim_objs
= new_complex
->complex->prim_objs
;
802 /* set the parent field now */
803 for (tmp
= o_current
->complex->prim_objs
; tmp
!= NULL
; tmp
= tmp
->next
) {
804 tmp
->complex_parent
= o_current
;
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
->type
== OBJ_TEXT
);
819 o_attrib_get_name_value(o_text_get_string(a_current
), &name
, NULL
);
821 attrfound
= o_attrib_search_name_single(o_current
, name
, NULL
);
823 /* free these now since they are no longer being used */
826 if (attrfound
== NULL
) {
827 /* attribute with same name not found in old component: */
828 /* add new attribute to old component */
830 /* make a copy of the attribute object */
831 o_list_copy_to (toplevel
, o_current
,
832 a_current
, NORMAL_FLAG
, &o_attrib
);
833 g_signal_emit_by_name(page
, "add-object", a_current
);
834 /* add the attribute to old */
835 o_attrib_add (toplevel
, o_current
, o_attrib
);
836 /* redraw the attribute object */
837 o_redraw_single (w_current
, o_attrib
);
838 /* note: this object is unselected (not added to selection). */
846 a_iter
= g_list_next (a_iter
);
849 /* finally delete the temp list with the updated complex */
850 s_delete_list_fromstart (toplevel
, tmp_list
);
852 /* update the pinnumbers to the current slot */
853 o_attrib_slot_update(toplevel
, o_current
);
855 /* Recalculate the bounds of the object */
856 o_recalc_single_object(o_current
);
858 /* reconnect, re-select and redraw */
859 s_conn_update_object(page
, o_current
);
860 o_selection_add(page
->selection_list
, o_current
);
861 o_redraw_single (w_current
, o_current
);
863 /* Re-flag as embedded if necessary */
864 (*o_current
->embed_func
)(toplevel
, o_current
, is_embedded
);
866 /* mark the page as modified */
868 o_undo_savestate (w_current
, UNDO_ALL
);
872 /*! \brief Do autosave on all pages that are marked.
873 * \par Function Description
874 * Looks for pages with the do_autosave_backup flag activated and
877 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
879 void o_autosave_backups(GSCHEM_TOPLEVEL
*w_current
)
881 TOPLEVEL
*toplevel
= w_current
->toplevel
;
883 PAGE
*p_save
, *p_current
;
884 gchar
*backup_filename
;
885 gchar
*real_filename
;
886 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_toplevel_goto_page(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) {
925 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
930 /* Use default permissions */
931 saved_umask
= umask(0);
932 st
.st_mode
= 0666 & ~saved_umask
;
934 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
935 st
.st_uid
= getuid ();
937 result
= stat (dirname
, &dir_st
);
939 if (result
== 0 && (dir_st
.st_mode
& S_ISGID
))
940 st
.st_gid
= dir_st
.st_gid
;
942 st
.st_gid
= getgid ();
946 g_free (only_filename
);
947 g_free (real_filename
);
949 /* Make the backup file writable before saving a new one */
950 if ( g_file_test (backup_filename
, G_FILE_TEST_EXISTS
) &&
951 (! g_file_test (backup_filename
, G_FILE_TEST_IS_DIR
))) {
952 if (chmod(backup_filename
, st
.st_mode
| S_IWRITE
)) {
953 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
958 if (o_save(toplevel
, p_current
, backup_filename
)) {
961 p_current
->ops_since_last_backup
= 0;
962 p_current
->do_autosave_backup
= 0;
964 /* Make the backup file readonly so a 'rm *' command will ask
965 the user before deleting it. Remove x bits for good measure. */
966 mask
= ~(S_IWRITE
|S_IWGRP
|S_IEXEC
|S_IXGRP
|S_IXOTH
);
967 if (chmod(backup_filename
, st
.st_mode
& ~mask
) != 0) {
968 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
972 s_log_message (_("Could NOT save backup file [%s]\n"),
975 g_free (backup_filename
);
979 /* restore current page */
980 s_toplevel_goto_page(toplevel
, p_save
);