Use a text object as context for calculating size.
[geda-gaf/berndj.git] / gschem / src / o_misc.c
blobc2808ffd5f57ca47fb680113615108df8a54fade
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 "gschem.h"
35 #ifdef HAVE_LIBDMALLOC
36 #include <dmalloc.h>
37 #endif
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
43 * o_delete...
45 /*! \todo Finish function documentation!!!
46 * \brief
47 * \par Function Description
50 void o_edit(GSCHEM_TOPLEVEL *w_current, SELECTION *selection)
52 OBJECT *o_current;
53 const gchar *str = NULL;
54 GList *list;
56 if (selection == NULL) {
57 g_critical(_("Got NULL selection in o_edit\n"));
58 return;
61 list = geda_list_get_glist(selection);
62 if (list == NULL) {
63 w_current->inside_action = 0;
64 i_set_state(w_current, SELECT);
65 return;
68 o_current = list->data;
69 if (o_current == NULL) {
70 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
71 exit(-1);
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 */
78 case(OBJ_COMPLEX):
79 case(OBJ_PLACEHOLDER):
80 case(OBJ_NET):
81 case(OBJ_PIN):
82 case(OBJ_BUS):
83 x_multiattrib_open (w_current, selection);
84 break;
86 case(OBJ_PICTURE):
87 picture_change_filename_dialog(w_current);
88 break;
89 case(OBJ_ARC):
90 arc_angle_dialog(w_current, o_current);
91 break;
92 case(OBJ_TEXT):
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);
98 } else {
99 o_text_edit(w_current, o_current);
101 break;
104 /* has to be more extensive in the future */
105 /* some sort of redrawing? */
108 /*! \todo Finish function documentation!!!
109 * \brief
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;
121 /* skip over head */
122 s_current = geda_list_get_glist( w_current->toplevel->page_current->selection_list );
124 while(s_current != NULL) {
125 object = s_current->data;
126 if (object) {
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;
133 } else {
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!!!
147 * \brief
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
153 something.. :-) */
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;
164 if (object) {
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;
170 } else {
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;
200 OBJECT *o_current;
201 GList const *o_iter;
202 GList *other_objects=NULL;
203 GList *connected_objects=NULL;
205 /* this is okay if you just hit rotate and have nothing selected */
206 if (list == NULL) {
207 w_current->inside_action = 0;
208 i_set_state(w_current, SELECT);
209 return;
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. */
221 o_iter = list;
222 while (o_iter != NULL) {
223 o_current = o_iter->data;
224 switch (o_current->type) {
225 case OBJ_COMPLEX:
226 case OBJ_PLACEHOLDER:
227 other_objects =
228 s_conn_return_complex_others(other_objects, o_current);
229 s_conn_remove_complex(o_current);
230 break;
231 case OBJ_NET:
232 case OBJ_PIN:
233 case OBJ_BUS:
234 other_objects = s_conn_return_others(other_objects, o_current);
235 s_conn_remove(o_current);
236 break;
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. */
247 o_iter = list;
248 while (o_iter != NULL) {
249 o_current = o_iter->data;
250 switch (o_current->type) {
251 case OBJ_COMPLEX:
252 case OBJ_PLACEHOLDER:
253 s_conn_update_object(toplevel->page_current, o_current);
254 connected_objects =
255 s_conn_return_complex_others(connected_objects, o_current);
256 break;
257 case OBJ_NET:
258 case OBJ_PIN:
259 case OBJ_BUS:
260 s_conn_update_object(toplevel->page_current, o_current);
261 connected_objects = s_conn_return_others(connected_objects, o_current);
262 break;
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) */
285 o_iter = list;
286 while (o_iter != NULL) {
287 o_current = o_iter->data;
289 switch(o_current->type) {
290 case(OBJ_PIN):
291 /* Run the rotate pin hook */
292 if (scm_hook_empty_p(rotate_pin_hook) == SCM_BOOL_F &&
293 o_current != NULL) {
294 scm_run_hook(rotate_pin_hook,
295 scm_cons(g_make_object_smob(toplevel, o_current),
296 SCM_EOL));
298 break;
300 case (OBJ_COMPLEX):
301 /* Run the rotate hook */
302 if (scm_hook_empty_p(rotate_component_object_hook) == SCM_BOOL_F &&
303 o_current != NULL) {
304 scm_run_hook(rotate_component_object_hook,
305 scm_cons(g_make_object_smob(toplevel, o_current),
306 SCM_EOL));
308 break;
309 default:
310 break;
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!!!
326 * \brief
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;
333 OBJECT *o_current;
334 GList *o_iter;
335 GList *other_objects=NULL;
336 GList *connected_objects=NULL;
338 if (list == NULL) {
339 w_current->inside_action = 0;
340 i_set_state(w_current, SELECT);
341 return;
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. */
351 o_iter = list;
352 while (o_iter != NULL) {
353 o_current = o_iter->data;
354 switch (o_current->type) {
355 case OBJ_COMPLEX:
356 case OBJ_PLACEHOLDER:
357 other_objects =
358 s_conn_return_complex_others(other_objects, o_current);
359 s_conn_remove_complex(o_current);
360 break;
361 case OBJ_NET:
362 case OBJ_PIN:
363 case OBJ_BUS:
364 other_objects = s_conn_return_others(other_objects, o_current);
365 s_conn_remove(o_current);
366 break;
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. */
377 o_iter = list;
378 while (o_iter != NULL) {
379 o_current = o_iter->data;
380 switch (o_current->type) {
381 case OBJ_COMPLEX:
382 case OBJ_PLACEHOLDER:
383 s_conn_update_object(toplevel->page_current, o_current);
384 connected_objects =
385 s_conn_return_complex_others(connected_objects, o_current);
386 break;
387 case OBJ_NET:
388 case OBJ_PIN:
389 case OBJ_BUS:
390 s_conn_update_object(toplevel->page_current, o_current);
391 connected_objects = s_conn_return_others(connected_objects, o_current);
392 break;
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) */
413 o_iter = list;
414 while (o_iter != NULL) {
415 o_current = o_iter->data;
417 switch(o_current->type) {
418 case(OBJ_PIN):
419 /* Run the mirror pin hook */
420 if (scm_hook_empty_p(mirror_pin_hook) == SCM_BOOL_F &&
421 o_current != NULL) {
422 scm_run_hook(mirror_pin_hook,
423 scm_cons(g_make_object_smob(toplevel, o_current),
424 SCM_EOL));
426 break;
428 case (OBJ_COMPLEX):
429 /* Run the mirror pin hook */
430 if (scm_hook_empty_p(mirror_component_object_hook) == SCM_BOOL_F &&
431 o_current != NULL) {
432 scm_run_hook(mirror_component_object_hook,
433 scm_cons(g_make_object_smob(toplevel, o_current),
434 SCM_EOL));
436 break;
437 default:
438 break;
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 if (o_current->text->prim_objs == NULL) {
459 o_text_recreate(o_current);
461 o_recalc_single_object(o_current);
462 o_text_draw(w_current, o_current);
463 } else {
464 /* object is hidden and we are now NOT drawing it, so */
465 /* get rid of the extra primitive data */
466 o_text_recreate(o_current);
467 o_recalc_single_object(o_current);
468 /* unfortunately, you cannot erase the old visible text here */
469 /* because o_text_draw will just return */
473 if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
474 o_edit_show_hidden_lowlevel(w_current, o_current->complex->prim_objs);
475 o_recalc_single_object(o_current);
478 return VISIT_RES_OK;
481 /*! \todo Finish function documentation!!!
482 * \brief
483 * \par Function Description
486 static void o_edit_show_hidden_lowlevel(GSCHEM_TOPLEVEL *w_current, OBJECT *o_list)
488 s_visit_list(o_list, LIST_KIND_HEAD,
489 &show_hidden_lowlevel_one, w_current, VISIT_ANY, 1);
492 static enum visit_result
493 show_hidden_one(OBJECT *o_current, void *userdata)
495 TOPLEVEL *toplevel = userdata;
497 o_current->show_hidden = toplevel->show_hidden_default;
499 return VISIT_RES_OK;
502 /*! \todo Finish function documentation!!!
503 * \brief
504 * \par Function Description
507 void o_edit_show_hidden(GSCHEM_TOPLEVEL *w_current, PAGE *page)
509 /* this function just shows the hidden text, but doesn't toggle it */
510 /* this function does not change the CHANGED bit, no real changes are */
511 /* made to the schematic */
512 TOPLEVEL *toplevel = w_current->toplevel;
514 /* toggle show_hidden_default variable, which when it is true */
515 /* means that hidden text IS drawn */
516 toplevel->show_hidden_default = !toplevel->show_hidden_default;
517 i_show_state(w_current, NULL); /* update screen status */
519 /* Tell all objects to show themselves despite really being invisible. */
520 s_visit_page(page, &show_hidden_one, toplevel, VISIT_ANY, 1);
522 o_edit_show_hidden_lowlevel(w_current, page->object_head);
523 o_redraw_all(w_current);
525 if (toplevel->show_hidden_default) {
526 s_log_message(_("Hidden text is now visible\n"));
527 } else {
528 s_log_message(_("Hidden text is now invisible\n"));
532 static enum visit_result make_visible_one(OBJECT *o_current, void *userdata)
534 GSCHEM_TOPLEVEL *w_current = userdata;
536 if (o_current->type == OBJ_TEXT) {
537 if (o_current->visibility == INVISIBLE) {
538 o_current->visibility = VISIBLE;
540 if (o_current->text->prim_objs == NULL) {
541 o_text_recreate(o_current);
544 o_text_draw(w_current, o_current);
546 /* XXX No, don't use page_current when an explicit PAGE * is nearby. */
547 w_current->toplevel->page_current->CHANGED = 1;
551 return VISIT_RES_OK;
554 /*! \todo Finish function documentation!!!
555 * \brief
556 * \par Function Description
559 void o_edit_make_visible(GSCHEM_TOPLEVEL *w_current, PAGE *page)
561 /* this function actually changes the visibility flag */
562 s_visit_page(page, &make_visible_one, w_current, VISIT_ANY, 1);
563 o_undo_savestate(w_current, UNDO_ALL);
566 #define FIND_WINDOW_HALF_SIZE (5000)
568 OBJECT *last_o = NULL;
569 int skiplast;
571 /*! \todo Finish function documentation!!!
572 * \brief
573 * \par Function Description
576 int o_edit_find_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list, char *stext,
577 int descend, int skip)
579 TOPLEVEL *toplevel = w_current->toplevel;
580 PAGE *page = toplevel->page_current;
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) {
600 if (descend) {
601 if (o_current->type == OBJ_COMPLEX) {
602 parent = page;
603 attrib = o_attrib_search_name_single_count(o_current, "source", count);
605 /* if above is null, then look inside symbol */
606 if (attrib == NULL) {
607 attrib = o_attrib_search_object(o_current, "source", count);
608 /* looking_inside = TRUE; */
611 if (attrib) {
612 pcount = 0;
613 current_filename = u_basic_breakup_string(attrib, ',', pcount);
614 if (current_filename != NULL) {
615 page_control =
616 s_hierarchy_down_schematic_single(toplevel,
617 current_filename,
618 parent,
619 page_control,
620 HIERARCHY_NORMAL_LOAD);
621 /* o_redraw_all(w_current); */
623 rv = o_edit_find_text(w_current,
624 page->object_head,
625 stext, descend, skiplast);
626 if (!rv) {
627 return 0;
629 s_toplevel_goto_page(toplevel, parent);
630 page = parent;
636 if (o_current->type == OBJ_TEXT) {
637 const gchar *str = o_text_get_string(o_current);
638 /* replaced strcmp with strstr to simplify the search */
639 if (strstr (str,stext)) {
640 if (!skiplast) {
641 a_zoom(w_current, page, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
642 text_screen_height =
643 SCREENabs(page, o_text_height(o_current, str));
644 /* this code will zoom/pan till the text screen height is about */
645 /* 50 pixels high, perhaps a future enhancement will be to make */
646 /* this number configurable */
647 while (text_screen_height < 50) {
648 a_zoom(w_current, page, ZOOM_IN, DONTCARE, A_PAN_DONT_REDRAW);
649 text_screen_height =
650 SCREENabs(page, o_text_height(o_current, str));
652 a_pan_general(w_current, page,
653 o_current->text->x, o_current->text->y,
654 page->to_screen_y_constant, A_PAN_FULL);
656 /* Make sure the titlebar and scrollbars are up-to-date */
657 x_window_set_current_page(w_current, page);
659 last_o = o_current;
660 break;
662 if (last_o == o_current) {
663 skiplast = 0;
666 } /* if (o_current->type == OBJ_TEXT) */
667 o_current = o_current->next;
669 if (o_current == NULL) {
670 return 1;
673 return (o_current == NULL);
677 /*! \todo Finish function documentation!!!
678 * \brief
679 * \par Function Description
682 void o_edit_hide_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
683 char *stext)
685 TOPLEVEL *toplevel = w_current->toplevel;
686 OBJECT *o_current = NULL;
688 if (o_list == NULL)
689 return;
691 o_current = o_list;
693 while (o_current != NULL) {
694 if (o_current->type == OBJ_TEXT) {
695 const gchar *str = o_text_get_string(o_current);
696 if (!strncmp (stext, str, strlen (stext))) {
697 if (o_current->visibility == VISIBLE) {
698 o_current->visibility = INVISIBLE;
700 if (o_current->text->prim_objs == NULL) {
701 o_text_recreate(o_current);
703 toplevel->page_current->CHANGED = 1;
707 o_current = o_current->next;
709 o_undo_savestate(w_current, UNDO_ALL);
710 o_redraw_all(w_current);
713 /*! \todo Finish function documentation!!!
714 * \brief
715 * \par Function Description
718 void o_edit_show_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
719 char *stext)
721 TOPLEVEL *toplevel = w_current->toplevel;
722 OBJECT *o_current = NULL;
724 if (o_list == NULL)
725 return;
727 o_current = o_list;
729 while (o_current != NULL) {
730 if (o_current->type == OBJ_TEXT) {
731 const gchar *str = o_text_get_string(o_current);
732 if (!strncmp (stext, str, strlen (stext))) {
733 if (o_current->visibility == INVISIBLE) {
734 o_current->visibility = VISIBLE;
736 if (o_current->text->prim_objs == NULL) {
737 o_text_recreate(o_current);
739 o_text_draw(w_current, o_current);
740 toplevel->page_current->CHANGED = 1;
744 o_current = o_current->next;
746 o_undo_savestate(w_current, UNDO_ALL);
749 /*! \todo Finish function documentation!!!
750 * \brief
751 * \par Function Description
754 void o_update_component(GSCHEM_TOPLEVEL *w_current, PAGE *page, OBJECT *o_current)
756 TOPLEVEL *toplevel = w_current->toplevel;
757 OBJECT *tmp_list, *new_complex;
758 OBJECT *a_current;
759 OBJECT *tmp;
760 GList *a_iter;
761 gboolean is_embedded;
762 const CLibSymbol *clib;
764 g_return_if_fail (o_current != NULL);
766 is_embedded = o_complex_is_embedded (o_current);
768 g_assert (o_current->complex_basename != NULL);
770 /* This should be replaced with API to invalidate only the specific
771 * symbol name we want to update */
772 s_clib_flush_symbol_cache ();
773 clib = s_clib_get_symbol_by_name (o_current->complex_basename);
775 if (clib == NULL) {
776 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
777 o_current->complex_basename);
778 return;
781 /* erase the complex object */
782 o_erase_single (w_current, o_current);
783 /* delete its connections */
784 s_conn_remove_complex(o_current);
785 /* and unselect it */
786 o_selection_remove(page->selection_list, o_current);
788 /* build a temporary list and add a complex to this list */
789 tmp_list = s_toplevel_new_object(toplevel, OBJ_HEAD, "update component");
790 new_complex = o_complex_new (toplevel, OBJ_COMPLEX, WHITE,
791 o_current->complex->x,
792 o_current->complex->y,
793 o_current->complex->angle,
794 o_current->complex->mirror,
795 clib, o_current->complex_basename,
797 tmp_list = s_basic_link_object(new_complex, tmp_list);
798 o_complex_promote_attribs(toplevel, NULL, new_complex);
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;
808 /* set the parent field now */
809 for (tmp = o_current->complex->prim_objs; tmp != NULL; tmp = tmp->next) {
810 tmp->complex_parent = o_current;
813 /* - reset the new complex prim_objs */
814 new_complex->complex->prim_objs = NULL;
816 /* then process the attributes: */
817 /* - check each attrib of the new complex */
818 a_iter = new_complex->attribs;
819 while (a_iter != NULL) {
820 a_current = a_iter->data;
821 OBJECT *o_attrib;
822 gchar *name;
823 char *attrfound;
824 g_assert (a_current->type == OBJ_TEXT);
825 o_attrib_get_name_value(o_text_get_string(a_current), &name, NULL);
827 attrfound = o_attrib_search_name_single(o_current, name, NULL);
829 /* free these now since they are no longer being used */
830 g_free(name);
832 if (attrfound == NULL) {
833 /* attribute with same name not found in old component: */
834 /* add new attribute to old component */
836 /* make a copy of the attribute object */
837 o_list_copy_to (toplevel, o_current,
838 a_current, NORMAL_FLAG, &o_attrib);
839 g_signal_emit_by_name(page, "add-object", a_current);
840 /* add the attribute to old */
841 o_attrib_add (toplevel, o_current, o_attrib);
842 /* redraw the attribute object */
843 o_redraw_single (w_current, o_attrib);
844 /* note: this object is unselected (not added to selection). */
846 else
848 g_free(attrfound);
852 a_iter = g_list_next (a_iter);
855 /* finally delete the temp list with the updated complex */
856 s_delete_list_fromstart (toplevel, tmp_list);
858 /* update the pinnumbers to the current slot */
859 o_attrib_slot_update(toplevel, o_current);
861 /* Recalculate the bounds of the object */
862 o_recalc_single_object(o_current);
864 /* reconnect, re-select and redraw */
865 s_conn_update_object(page, o_current);
866 o_selection_add(page->selection_list, o_current);
867 o_redraw_single (w_current, o_current);
869 /* Re-flag as embedded if necessary */
870 (*o_current->embed_func)(toplevel, o_current, is_embedded);
872 /* mark the page as modified */
873 page->CHANGED = 1;
874 o_undo_savestate (w_current, UNDO_ALL);
878 /*! \brief Do autosave on all pages that are marked.
879 * \par Function Description
880 * Looks for pages with the do_autosave_backup flag activated and
881 * autosaves them.
883 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
885 void o_autosave_backups(GSCHEM_TOPLEVEL *w_current)
887 TOPLEVEL *toplevel = w_current->toplevel;
888 GList *iter;
889 PAGE *p_save, *p_current;
890 gchar *backup_filename;
891 gchar *real_filename;
892 gchar *only_filename;
893 gchar *dirname;
894 struct stat st;
896 /* save current page */
897 p_save = toplevel->page_current;
899 for ( iter = geda_list_get_glist( toplevel->pages );
900 iter != NULL;
901 iter = g_list_next( iter ) ) {
903 p_current = (PAGE *)iter->data;
905 if (p_current->do_autosave_backup == 0) {
906 continue;
908 if (p_current->ops_since_last_backup != 0) {
909 /* make p_current the current page of toplevel */
910 s_toplevel_goto_page(toplevel, p_current);
912 /* Get the real filename and file permissions */
913 real_filename = follow_symlinks (p_current->page_filename, NULL);
915 if (real_filename == NULL) {
916 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current->page_filename);
917 } else {
918 /* Get the directory in which the real filename lives */
919 dirname = g_path_get_dirname (real_filename);
920 only_filename = g_path_get_basename(real_filename);
922 backup_filename = g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING,
923 dirname, G_DIR_SEPARATOR, only_filename);
925 /* If there is not an existing file with that name, compute the
926 * permissions and uid/gid that we will use for the newly-created file.
929 if (stat (real_filename, &st) != 0) {
930 mode_t saved_umask;
931 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
932 struct stat dir_st;
933 int result;
934 #endif
936 /* Use default permissions */
937 saved_umask = umask(0);
938 st.st_mode = 0666 & ~saved_umask;
939 umask(saved_umask);
940 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
941 st.st_uid = getuid ();
943 result = stat (dirname, &dir_st);
945 if (result == 0 && (dir_st.st_mode & S_ISGID))
946 st.st_gid = dir_st.st_gid;
947 else
948 st.st_gid = getgid ();
949 #endif
951 g_free (dirname);
952 g_free (only_filename);
953 g_free (real_filename);
955 /* Make the backup file writable before saving a new one */
956 if ( g_file_test (backup_filename, G_FILE_TEST_EXISTS) &&
957 (! g_file_test (backup_filename, G_FILE_TEST_IS_DIR))) {
958 if (chmod(backup_filename, st.st_mode | S_IWRITE)) {
959 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
960 backup_filename);
964 if (o_save(toplevel, p_current, backup_filename)) {
965 mode_t mask;
967 p_current->ops_since_last_backup = 0;
968 p_current->do_autosave_backup = 0;
970 /* Make the backup file readonly so a 'rm *' command will ask
971 the user before deleting it. Remove x bits for good measure. */
972 mask = ~(S_IWRITE|S_IWGRP|S_IEXEC|S_IXGRP|S_IXOTH);
973 if (chmod(backup_filename, st.st_mode & ~mask) != 0) {
974 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
975 backup_filename);
977 } else {
978 s_log_message (_("Could NOT save backup file [%s]\n"),
979 backup_filename);
981 g_free (backup_filename);
985 /* restore current page */
986 s_toplevel_goto_page(toplevel, p_save);