Prefer o_text_change over random poking.
[geda-gaf/berndj.git] / gschem / src / o_misc.c
blob9eb250e2f7b70ac170ad0da51d0a415bdec33474
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 o_text_recreate(o_current);
459 o_recalc_single_object(o_current);
460 o_text_draw(w_current, o_current);
461 } else {
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);
476 return VISIT_RES_OK;
479 /*! \todo Finish function documentation!!!
480 * \brief
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;
497 return VISIT_RES_OK;
500 /*! \todo Finish function documentation!!!
501 * \brief
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"));
525 } else {
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;
547 return VISIT_RES_OK;
550 /*! \todo Finish function documentation!!!
551 * \brief
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;
565 int skiplast;
567 /*! \todo Finish function documentation!!!
568 * \brief
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;
577 char *attrib = NULL;
578 int count = 0;
579 PAGE *parent = NULL;
580 char *current_filename = NULL;
581 int page_control = 0;
582 int pcount = 0;
583 int rv;
584 int text_screen_height;
586 OBJECT *o_current = NULL;
588 skiplast = skip;
589 o_current = o_list;
591 if (o_current == NULL) {
592 return 1;
595 while (o_current != NULL) {
596 if (descend) {
597 if (o_current->type == OBJ_COMPLEX) {
598 parent = page;
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; */
607 if (attrib) {
608 pcount = 0;
609 current_filename = u_basic_breakup_string(attrib, ',', pcount);
610 if (current_filename != NULL) {
611 page_control =
612 s_hierarchy_down_schematic_single(toplevel,
613 current_filename,
614 parent,
615 page_control,
616 HIERARCHY_NORMAL_LOAD);
617 /* o_redraw_all(w_current); */
619 rv = o_edit_find_text(w_current,
620 page->object_head,
621 stext, descend, skiplast);
622 if (!rv) {
623 return 0;
625 s_toplevel_goto_page(toplevel, parent);
626 page = parent;
632 if (o_current->type == OBJ_TEXT) {
633 const gchar *str = o_text_get_string(o_current);
634 int x, y;
636 /* replaced strcmp with strstr to simplify the search */
637 if (strstr (str,stext)) {
638 if (!skiplast) {
639 a_zoom(w_current, page, ZOOM_FULL, DONTCARE, A_PAN_DONT_REDRAW);
640 text_screen_height =
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);
647 text_screen_height =
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);
657 last_o = o_current;
658 break;
660 if (last_o == o_current) {
661 skiplast = 0;
664 } /* if (o_current->type == OBJ_TEXT) */
665 o_current = o_current->next;
667 if (o_current == NULL) {
668 return 1;
671 return (o_current == NULL);
675 /*! \todo Finish function documentation!!!
676 * \brief
677 * \par Function Description
680 void o_edit_hide_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
681 char *stext)
683 TOPLEVEL *toplevel = w_current->toplevel;
684 OBJECT *o_current = NULL;
686 if (o_list == NULL)
687 return;
689 o_current = o_list;
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!!!
710 * \brief
711 * \par Function Description
714 void o_edit_show_specific_text(GSCHEM_TOPLEVEL *w_current, OBJECT * o_list,
715 char *stext)
717 TOPLEVEL *toplevel = w_current->toplevel;
718 OBJECT *o_current = NULL;
720 if (o_list == NULL)
721 return;
723 o_current = o_list;
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!!!
744 * \brief
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;
752 OBJECT *a_current;
753 OBJECT *tmp;
754 GList *a_iter;
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);
769 if (clib == NULL) {
770 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
771 o_current->complex_basename);
772 return;
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;
815 OBJECT *o_attrib;
816 gchar *name;
817 char *attrfound;
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 */
824 g_free(name);
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). */
840 else
842 g_free(attrfound);
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 */
867 page->CHANGED = 1;
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
875 * autosaves them.
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;
882 GList *iter;
883 PAGE *p_save, *p_current;
884 gchar *backup_filename;
885 gchar *real_filename;
886 gchar *only_filename;
887 gchar *dirname;
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_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);
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 mode_t saved_umask;
925 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
926 struct stat dir_st;
927 int result;
928 #endif
930 /* Use default permissions */
931 saved_umask = umask(0);
932 st.st_mode = 0666 & ~saved_umask;
933 umask(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;
941 else
942 st.st_gid = getgid ();
943 #endif
945 g_free (dirname);
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"),
954 backup_filename);
958 if (o_save(toplevel, p_current, backup_filename)) {
959 mode_t mask;
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"),
969 backup_filename);
971 } else {
972 s_log_message (_("Could NOT save backup file [%s]\n"),
973 backup_filename);
975 g_free (backup_filename);
979 /* restore current page */
980 s_toplevel_goto_page(toplevel, p_save);