When doing a find_text into hierarchy, the titlebar was not being updated
[geda-gaf/peter-b.git] / gschem / src / o_misc.c
blob056752feb807546081ad7aa26ce2fddad90047bd
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
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 <libgeda/libgeda.h>
35 #include "../include/gschem_struct.h"
36 #include "../include/globals.h"
37 #include "../include/prototype.h"
39 #ifdef HAVE_LIBDMALLOC
40 #include <dmalloc.h>
41 #endif
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
45 * o_delete...
47 /*! \todo Finish function documentation!!!
48 * \brief
49 * \par Function Description
52 void o_edit(GSCHEM_TOPLEVEL *w_current, GList *list)
54 char *equal_ptr;
55 OBJECT *o_current;
56 int num_lines = 0;
58 if (list == NULL) {
59 w_current->inside_action = 0;
60 i_set_state(w_current, SELECT);
61 return;
64 o_current = (OBJECT *) list->data;
65 if (o_current == NULL) {
66 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
67 exit(-1);
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 */
74 case(OBJ_COMPLEX):
75 case(OBJ_PLACEHOLDER):
76 case(OBJ_NET):
77 case(OBJ_PIN):
78 case(OBJ_BUS):
79 x_multiattrib_open (w_current);
80 break;
82 case(OBJ_PICTURE):
83 picture_change_filename_dialog(w_current);
84 break;
85 case(OBJ_TEXT):
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) != ' ') &&
102 (num_lines == 1) ) {
103 attrib_edit_dialog(w_current,o_current, FROM_MENU);
104 /* multi_attrib_edit(w_current, o_current); */
106 } else {
107 o_text_edit(w_current, o_current);
109 } else {
110 o_text_edit(w_current, o_current);
112 break;
115 /* has to be more extensive in the future */
116 /* some sort of redrawing? */
119 /*! \todo Finish function documentation!!!
120 * \brief
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;
132 /* skip over head */
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;
137 if (object) {
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;
144 } else {
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!!!
158 * \brief
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
164 something.. :-) */
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;
175 if (object) {
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;
182 } else {
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;
212 OBJECT *o_current;
213 GList *o_iter;
214 GList *other_objects=NULL;
215 GList *connected_objects=NULL;
217 /* this is okay if you just hit rotate and have nothing selected */
218 if (list == NULL) {
219 w_current->inside_action = 0;
220 i_set_state(w_current, SELECT);
221 return;
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. */
233 o_iter = list;
234 while (o_iter != NULL) {
235 o_current = o_iter->data;
236 switch (o_current->type) {
237 case OBJ_COMPLEX:
238 case OBJ_PLACEHOLDER:
239 other_objects =
240 s_conn_return_complex_others(other_objects, o_current);
241 s_conn_remove_complex (toplevel, o_current);
242 break;
243 case OBJ_NET:
244 case OBJ_PIN:
245 case OBJ_BUS:
246 other_objects = s_conn_return_others(other_objects, o_current);
247 s_conn_remove (toplevel, o_current);
248 break;
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. */
259 o_iter = list;
260 while (o_iter != NULL) {
261 o_current = o_iter->data;
262 switch (o_current->type) {
263 case OBJ_COMPLEX:
264 case OBJ_PLACEHOLDER:
265 s_conn_update_complex(toplevel, o_current->complex->prim_objs);
266 connected_objects =
267 s_conn_return_complex_others(connected_objects, o_current);
268 break;
269 case OBJ_NET:
270 case OBJ_PIN:
271 case OBJ_BUS:
272 s_conn_update_object(toplevel, o_current);
273 connected_objects = s_conn_return_others(connected_objects, o_current);
274 break;
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) */
297 o_iter = list;
298 while (o_iter != NULL) {
299 o_current = o_iter->data;
301 switch(o_current->type) {
302 case(OBJ_PIN):
303 /* Run the rotate pin hook */
304 if (scm_hook_empty_p(rotate_pin_hook) == SCM_BOOL_F &&
305 o_current != NULL) {
306 scm_run_hook(rotate_pin_hook,
307 scm_cons(g_make_object_smob(toplevel, o_current),
308 SCM_EOL));
310 break;
312 case (OBJ_COMPLEX):
313 /* Run the rotate hook */
314 if (scm_hook_empty_p(rotate_component_object_hook) == SCM_BOOL_F &&
315 o_current != NULL) {
316 scm_run_hook(rotate_component_object_hook,
317 scm_cons(g_make_object_smob(toplevel, o_current),
318 SCM_EOL));
320 break;
321 default:
322 break;
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!!!
338 * \brief
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;
345 OBJECT *o_current;
346 GList *o_iter;
347 GList *other_objects=NULL;
348 GList *connected_objects=NULL;
350 if (list == NULL) {
351 w_current->inside_action = 0;
352 i_set_state(w_current, SELECT);
353 return;
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. */
363 o_iter = list;
364 while (o_iter != NULL) {
365 o_current = o_iter->data;
366 switch (o_current->type) {
367 case OBJ_COMPLEX:
368 case OBJ_PLACEHOLDER:
369 other_objects =
370 s_conn_return_complex_others(other_objects, o_current);
371 s_conn_remove_complex (toplevel, o_current);
372 break;
373 case OBJ_NET:
374 case OBJ_PIN:
375 case OBJ_BUS:
376 other_objects = s_conn_return_others(other_objects, o_current);
377 s_conn_remove (toplevel, o_current);
378 break;
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. */
389 o_iter = list;
390 while (o_iter != NULL) {
391 o_current = o_iter->data;
392 switch (o_current->type) {
393 case OBJ_COMPLEX:
394 case OBJ_PLACEHOLDER:
395 s_conn_update_complex(toplevel, o_current->complex->prim_objs);
396 connected_objects =
397 s_conn_return_complex_others(connected_objects, o_current);
398 break;
399 case OBJ_NET:
400 case OBJ_PIN:
401 case OBJ_BUS:
402 s_conn_update_object(toplevel, o_current);
403 connected_objects = s_conn_return_others(connected_objects, o_current);
404 break;
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) */
425 o_iter = list;
426 while (o_iter != NULL) {
427 o_current = (OBJECT *) o_iter->data;
429 switch(o_current->type) {
430 case(OBJ_PIN):
431 /* Run the mirror pin hook */
432 if (scm_hook_empty_p(mirror_pin_hook) == SCM_BOOL_F &&
433 o_current != NULL) {
434 scm_run_hook(mirror_pin_hook,
435 scm_cons(g_make_object_smob(toplevel, o_current),
436 SCM_EOL));
438 break;
440 case (OBJ_COMPLEX):
441 /* Run the mirror pin hook */
442 if (scm_hook_empty_p(mirror_component_object_hook) == SCM_BOOL_F &&
443 o_current != NULL) {
444 scm_run_hook(mirror_component_object_hook,
445 scm_cons(g_make_object_smob(toplevel, o_current),
446 SCM_EOL));
448 break;
449 default:
450 break;
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!!!
461 * \brief
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) {
471 return;
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);
486 } else {
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!!!
506 * \brief
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"));
526 } else {
527 s_log_message(_("Hidden text is now invisible\n"));
531 /*! \todo Finish function documentation!!!
532 * \brief
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;
542 if (o_list == NULL)
543 return;
544 o_current = o_list;
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;
570 int skiplast;
572 /*! \todo Finish function documentation!!!
573 * \brief
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;
581 char *attrib = NULL;
582 int count = 0;
583 PAGE *parent = NULL;
584 char *current_filename = NULL;
585 int page_control = 0;
586 int pcount = 0;
587 int rv;
588 int text_screen_height;
590 OBJECT *o_current = NULL;
592 skiplast = skip;
593 o_current = o_list;
595 if (o_current == NULL) {
596 return 1;
599 while (o_current != NULL) {
601 if (descend) {
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,
609 "source", count);
610 /* looking_inside = TRUE; */
613 if (attrib) {
614 pcount = 0;
615 current_filename = u_basic_breakup_string(attrib, ',', pcount);
616 if (current_filename != NULL) {
617 page_control =
618 s_hierarchy_down_schematic_single(toplevel,
619 current_filename,
620 parent,
621 page_control,
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);
628 if (!rv) {
629 return 0;
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)) {
640 if (!skiplast) {
641 a_zoom(w_current, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
642 text_screen_height =
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);
650 text_screen_height =
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,
656 1, 0);
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 );
662 last_o = o_current;
663 break;
665 if (last_o == o_current) {
666 skiplast = 0;
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) {
674 return 1;
677 return (o_current == NULL);
681 /*! \todo Finish function documentation!!!
682 * \brief
683 * \par Function Description
686 void o_edit_hide_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
687 char *stext)
689 TOPLEVEL *toplevel = w_current->toplevel;
690 OBJECT *o_current = NULL;
692 if (o_list == NULL)
693 return;
695 o_current = o_list;
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!!!
718 * \brief
719 * \par Function Description
722 void o_edit_show_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
723 char *stext)
725 TOPLEVEL *toplevel = w_current->toplevel;
726 OBJECT *o_current = NULL;
728 if (o_list == NULL)
729 return;
731 o_current = o_list;
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!!!
754 * \brief
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;
762 ATTRIB *a_current;
763 GList *a_iter;
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);
774 if (clib == NULL) {
775 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
776 o_current->complex_basename);
777 return;
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,
790 tmp_list, NULL,
791 OBJ_COMPLEX,
792 WHITE,
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,
798 1, TRUE);
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;
815 OBJECT *o_attrib;
816 gchar *name, *value;
817 char *attrfound;
818 g_assert (a_current->object->type == OBJ_TEXT);
819 o_attrib_get_name_value (a_current->object->text->string,
820 &name, &value);
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). */
841 else
843 g_free(attrfound);
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
873 * autosaves them.
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;
880 GList *iter;
881 PAGE *p_save, *p_current;
882 gchar *backup_filename;
883 gchar *real_filename;
884 gchar *only_filename;
885 gchar *dirname;
886 mode_t saved_umask;
887 mode_t mask;
888 struct stat st;
890 /* save current page */
891 p_save = toplevel->page_current;
893 for ( iter = geda_list_get_glist( toplevel->pages );
894 iter != NULL;
895 iter = g_list_next( iter ) ) {
897 p_current = (PAGE *)iter->data;
899 if (p_current->do_autosave_backup == 0) {
900 continue;
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);
911 } else {
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)
925 struct stat dir_st;
926 int result;
927 #endif
929 /* Use default permissions */
930 saved_umask = umask(0);
931 st.st_mode = 0666 & ~saved_umask;
932 umask(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;
940 else
941 st.st_gid = getgid ();
942 #endif
944 g_free (dirname);
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"),
955 backup_filename);
957 umask(saved_umask);
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);
969 mask = (~mask)&0777;
970 mask &= ((~saved_umask) & 0777);
971 if (chmod(backup_filename,mask) != 0) {
972 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
973 backup_filename);
975 umask(saved_umask);
976 } else {
977 s_log_message (_("Could NOT save backup file [%s]\n"),
978 backup_filename);
980 g_free (backup_filename);
984 /* restore current page */
985 s_page_goto (toplevel, p_save);