1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <sys/types.h>
35 #ifdef HAVE_LIBDMALLOC
39 /* break with the tradition here and input a list */
40 /*! \todo probably should go back and do the same for o_copy o_move
43 /*! \todo Finish function documentation!!!
45 * \par Function Description
48 void o_edit(GSCHEM_TOPLEVEL
*w_current
, GList
*list
)
51 const gchar
*str
= NULL
;
54 w_current
->inside_action
= 0;
55 i_set_state(w_current
, SELECT
);
59 o_current
= (OBJECT
*) list
->data
;
60 if (o_current
== NULL
) {
61 fprintf(stderr
, _("Got an unexpected NULL in o_edit\n"));
65 /* for now deal with only the first item */
66 switch(o_current
->type
) {
68 /* also add the ability to multi attrib edit: nets, busses, pins */
70 case(OBJ_PLACEHOLDER
):
74 x_multiattrib_open (w_current
);
78 picture_change_filename_dialog(w_current
);
81 arc_angle_dialog(w_current
, o_current
);
84 str
= o_text_get_string (w_current
->toplevel
, o_current
);
85 if (o_attrib_get_name_value (o_current
, NULL
, NULL
) &&
86 /* attribute editor only accept 1-line values for attribute */
87 o_text_num_lines (str
) == 1) {
88 attrib_edit_dialog(w_current
,o_current
, FROM_MENU
);
90 o_text_edit(w_current
, o_current
);
95 /* has to be more extensive in the future */
96 /* some sort of redrawing? */
99 /*! \todo Finish function documentation!!!
101 * \par Function Description
104 /* This locks the entire selected list. It does lock components, but does NOT
105 * change the color (of primatives of the components) though
106 * this cannot be called recursively */
107 void o_lock(GSCHEM_TOPLEVEL
*w_current
)
109 OBJECT
*object
= NULL
;
110 GList
*s_current
= NULL
;
113 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
115 while(s_current
!= NULL
) {
116 object
= (OBJECT
*) s_current
->data
;
118 /* check to see if locked_color is already being used */
119 if (object
->locked_color
== -1) {
120 object
->selectable
= FALSE
;
121 object
->locked_color
= object
->color
;
122 object
->color
= LOCK_COLOR
;
123 w_current
->toplevel
->page_current
->CHANGED
=1;
125 s_log_message(_("Object already locked\n"));
129 s_current
= g_list_next(s_current
);
132 if (!w_current
->SHIFTKEY
) o_select_unselect_all(w_current
);
133 o_undo_savestate(w_current
, UNDO_ALL
);
134 i_update_menus(w_current
);
137 /*! \todo Finish function documentation!!!
139 * \par Function Description
142 /* You can unlock something by selecting it with a bounding box... */
143 /* this will probably change in the future, but for now it's a
145 /* this cannot be called recursively */
146 void o_unlock(GSCHEM_TOPLEVEL
*w_current
)
148 OBJECT
*object
= NULL
;
149 GList
*s_current
= NULL
;
151 s_current
= geda_list_get_glist( w_current
->toplevel
->page_current
->selection_list
);
153 while(s_current
!= NULL
) {
154 object
= (OBJECT
*) s_current
->data
;
156 /* only unlock if the object is locked */
157 if (object
->selectable
== FALSE
) {
158 object
->selectable
= TRUE
;
159 object
->color
= object
->locked_color
;
160 object
->locked_color
= -1;
161 w_current
->toplevel
->page_current
->CHANGED
= 1;
163 s_log_message(_("Object already unlocked\n"));
167 s_current
= g_list_next(s_current
);
169 o_undo_savestate(w_current
, UNDO_ALL
);
172 /*! \brief Rotate all objects in list.
173 * \par Function Description
174 * Given an object <B>list</B>, and the center of rotation
175 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
176 * list, rotating each object through angle <B>angle</B>.
177 * The list contains a given object and all its attributes
178 * (refdes, pinname, pinlabel, ...).
179 * There is a second pass to run the rotate hooks of non-simple objects,
180 * like pin or complex objects, for example.
182 * \param [in] w_current The GSCHEM_TOPLEVEL object.
183 * \param [in] centerx Center x coordinate of rotation.
184 * \param [in] centery Center y coordinate of rotation.
185 * \param [in] angle Angle to rotate the objects through.
186 * \param [in] list The list of objects to rotate.
188 void o_rotate_world_update(GSCHEM_TOPLEVEL
*w_current
,
189 int centerx
, int centery
, int angle
, GList
*list
)
191 TOPLEVEL
*toplevel
= w_current
->toplevel
;
195 /* this is okay if you just hit rotate and have nothing selected */
197 w_current
->inside_action
= 0;
198 i_set_state(w_current
, SELECT
);
202 o_invalidate_glist (w_current
, list
);
204 /* Find connected objects, removing each object in turn from the
205 * connection list. We only _really_ want those objects connected
206 * to the selection, not those within in it.
208 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
209 o_current
= o_iter
->data
;
211 s_conn_remove_object (toplevel
, o_current
);
214 o_glist_rotate_world( toplevel
, centerx
, centery
, angle
, list
);
216 /* Find connected objects, adding each object in turn back to the
217 * connection list. We only _really_ want those objects connected
218 * to the selection, not those within in it.
220 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
221 o_current
= o_iter
->data
;
223 s_conn_update_object (toplevel
, o_current
);
226 o_invalidate_glist (w_current
, list
);
228 /* Run rotate-objects-hook */
229 g_run_hook_object_list ("%rotate-objects-hook", list
);
231 /* Don't save the undo state if we are inside an action */
232 /* This is useful when rotating the selection while moving, for example */
233 toplevel
->page_current
->CHANGED
= 1;
234 if (!w_current
->inside_action
) {
235 o_undo_savestate(w_current
, UNDO_ALL
);
239 /*! \todo Finish function documentation!!!
241 * \par Function Description
244 void o_mirror_world_update(GSCHEM_TOPLEVEL
*w_current
, int centerx
, int centery
, GList
*list
)
246 TOPLEVEL
*toplevel
= w_current
->toplevel
;
251 w_current
->inside_action
= 0;
252 i_set_state(w_current
, SELECT
);
256 o_invalidate_glist (w_current
, list
);
258 /* Find connected objects, removing each object in turn from the
259 * connection list. We only _really_ want those objects connected
260 * to the selection, not those within in it.
262 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
263 o_current
= o_iter
->data
;
265 s_conn_remove_object (toplevel
, o_current
);
268 o_glist_mirror_world( toplevel
, centerx
, centery
, list
);
270 /* Find connected objects, adding each object in turn back to the
271 * connection list. We only _really_ want those objects connected
272 * to the selection, not those within in it.
274 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
275 o_current
= o_iter
->data
;
277 s_conn_update_object (toplevel
, o_current
);
280 o_invalidate_glist (w_current
, list
);
282 /* Run mirror-objects-hook */
283 g_run_hook_object_list ("%mirror-objects-hook", list
);
285 toplevel
->page_current
->CHANGED
=1;
286 o_undo_savestate(w_current
, UNDO_ALL
);
289 /*! \todo Finish function documentation!!!
291 * \par Function Description
294 void o_edit_show_hidden_lowlevel (GSCHEM_TOPLEVEL
*w_current
,
297 TOPLEVEL
*toplevel
= w_current
->toplevel
;
302 while (iter
!= NULL
) {
303 o_current
= (OBJECT
*)iter
->data
;
304 if (o_current
->type
== OBJ_TEXT
&& !o_is_visible (toplevel
, o_current
)) {
306 /* don't toggle the visibility flag */
307 o_text_recreate (toplevel
, o_current
);
310 if (o_current
->type
== OBJ_COMPLEX
|| o_current
->type
== OBJ_PLACEHOLDER
) {
311 o_edit_show_hidden_lowlevel(w_current
, o_current
->complex->prim_objs
);
312 o_recalc_single_object(toplevel
, o_current
);
315 iter
= g_list_next (iter
);
319 /*! \todo Finish function documentation!!!
321 * \par Function Description
324 void o_edit_show_hidden (GSCHEM_TOPLEVEL
*w_current
, const GList
*o_list
)
326 /* this function just shows the hidden text, but doesn't toggle it */
327 /* this function does not change the CHANGED bit, no real changes are */
328 /* made to the schematic */
330 /* toggle show_hidden_text variable, which when it is true */
331 /* means that hidden text IS drawn */
332 w_current
->toplevel
->show_hidden_text
= !w_current
->toplevel
->show_hidden_text
;
333 i_show_state(w_current
, NULL
); /* update screen status */
335 o_edit_show_hidden_lowlevel(w_current
, o_list
);
336 o_invalidate_all (w_current
);
338 if (w_current
->toplevel
->show_hidden_text
) {
339 s_log_message(_("Hidden text is now visible\n"));
341 s_log_message(_("Hidden text is now invisible\n"));
345 #define FIND_WINDOW_HALF_SIZE (5000)
347 OBJECT
*last_o
= NULL
;
350 /*! \todo Finish function documentation!!!
352 * \par Function Description
354 * \todo Only descends into the first source schematic
357 int o_edit_find_text (GSCHEM_TOPLEVEL
*w_current
, const GList
*o_list
,
358 char *stext
, int descend
, int skip
)
360 TOPLEVEL
*toplevel
= w_current
->toplevel
;
364 char *current_filename
= NULL
;
365 int page_control
= 0;
369 int text_screen_height
;
377 while (iter
!= NULL
) {
378 o_current
= (OBJECT
*)iter
->data
;
381 if (o_current
->type
== OBJ_COMPLEX
) {
382 parent
= toplevel
->page_current
;
383 attrib
= o_attrib_search_attached_attribs_by_name (o_current
,
386 /* if above is null, then look inside symbol */
387 if (attrib
== NULL
) {
388 attrib
= o_attrib_search_inherited_attribs_by_name (o_current
,
390 /* looking_inside = TRUE; */
395 current_filename
= u_basic_breakup_string(attrib
, ',', pcount
);
396 if (current_filename
!= NULL
) {
398 s_hierarchy_down_schematic_single(toplevel
,
402 HIERARCHY_NORMAL_LOAD
);
404 if (child_page
!= NULL
) {
405 page_control
= child_page
->page_control
;
406 rv
= o_edit_find_text (w_current
,
407 s_page_objects (child_page
),
408 stext
, descend
, skiplast
);
410 s_page_goto( toplevel
, child_page
);
419 if (o_current
->type
== OBJ_TEXT
&&
420 (o_is_visible (toplevel
, o_current
) || toplevel
->show_hidden_text
)) {
422 const gchar
*str
= o_text_get_string (toplevel
, o_current
);
423 /* replaced strcmp with strstr to simplify the search */
424 if (strstr (str
,stext
)) {
426 a_zoom(w_current
, ZOOM_FULL
, DONTCARE
, A_PAN_DONT_REDRAW
);
427 g_assert( world_get_single_object_bounds (toplevel
, o_current
, &x1
, &y1
, &x2
, &y2
) );
428 text_screen_height
= SCREENabs (w_current
, y2
- y1
);
429 /* this code will zoom/pan till the text screen height is about */
430 /* 50 pixels high, perhaps a future enhancement will be to make */
431 /* this number configurable */
432 while (text_screen_height
< 50) {
433 a_zoom(w_current
, ZOOM_IN
, DONTCARE
, A_PAN_DONT_REDRAW
);
434 text_screen_height
= SCREENabs (w_current
, y2
- y1
);
436 a_pan_general(w_current
,
437 o_current
->text
->x
, o_current
->text
->y
,
440 /* Make sure the titlebar and scrollbars are up-to-date */
441 x_window_set_current_page(w_current
,
442 w_current
->toplevel
->page_current
);
447 if (last_o
== o_current
) {
451 } /* if (strstr(o_current->text->string,stext)) */
452 } /* if (o_current->type == OBJ_TEXT) */
453 iter
= g_list_next (iter
);
459 return (iter
== NULL
);
463 /*! \todo Finish function documentation!!!
465 * \par Function Description
468 void o_edit_hide_specific_text (GSCHEM_TOPLEVEL
*w_current
,
472 TOPLEVEL
*toplevel
= w_current
->toplevel
;
477 while (iter
!= NULL
) {
478 o_current
= (OBJECT
*)iter
->data
;
480 if (o_current
->type
== OBJ_TEXT
) {
481 const gchar
*str
= o_text_get_string (w_current
->toplevel
, o_current
);
482 if (!strncmp (stext
, str
, strlen (stext
))) {
483 if (o_is_visible (toplevel
, o_current
)) {
484 o_set_visibility (toplevel
, o_current
, INVISIBLE
);
485 o_text_recreate(toplevel
, o_current
);
487 toplevel
->page_current
->CHANGED
= 1;
491 iter
= g_list_next (iter
);
493 o_undo_savestate(w_current
, UNDO_ALL
);
494 o_invalidate_all (w_current
);
497 /*! \todo Finish function documentation!!!
499 * \par Function Description
502 void o_edit_show_specific_text (GSCHEM_TOPLEVEL
*w_current
,
506 TOPLEVEL
*toplevel
= w_current
->toplevel
;
511 while (iter
!= NULL
) {
512 o_current
= (OBJECT
*)iter
->data
;
514 if (o_current
->type
== OBJ_TEXT
) {
515 const gchar
*str
= o_text_get_string (w_current
->toplevel
, o_current
);
516 if (!strncmp (stext
, str
, strlen (stext
))) {
517 if (!o_is_visible (toplevel
, o_current
)) {
518 o_set_visibility (toplevel
, o_current
, VISIBLE
);
519 o_text_recreate(toplevel
, o_current
);
521 toplevel
->page_current
->CHANGED
= 1;
525 iter
= g_list_next (iter
);
527 o_undo_savestate(w_current
, UNDO_ALL
);
531 /*! \brief Update a component.
533 * \par Function Description
534 * Updates \a o_current to the latest version of the symbol available
535 * in the symbol library, while preserving any attributes set in the
536 * current schematic. On success, returns the new OBJECT which
537 * replaces \a o_current on the page; \a o_current is deleted. On
538 * failure, returns NULL, and \a o_current is left unchanged.
540 * \param [in] w_current The GSCHEM_TOPLEVEL object.
541 * \param [in,out] o_current The OBJECT to be updated.
543 * \return the new OBJECT that replaces \a o_current.
546 o_update_component (GSCHEM_TOPLEVEL
*w_current
, OBJECT
*o_current
)
548 TOPLEVEL
*toplevel
= w_current
->toplevel
;
554 const CLibSymbol
*clib
;
556 g_return_val_if_fail (o_current
!= NULL
, NULL
);
557 g_return_val_if_fail (o_current
->type
== OBJ_COMPLEX
, NULL
);
558 g_return_val_if_fail (o_current
->complex_basename
!= NULL
, NULL
);
560 page
= o_get_page (toplevel
, o_current
);
562 /* Force symbol data to be reloaded from source */
563 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
564 s_clib_symbol_invalidate_data (clib
);
567 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
568 o_current
->complex_basename
);
572 /* Unselect the old object. */
573 o_selection_remove (toplevel
, page
->selection_list
, o_current
);
575 /* Create new object and set embedded */
576 o_new
= o_complex_new (toplevel
, OBJ_COMPLEX
, DEFAULT_COLOR
,
577 o_current
->complex->x
,
578 o_current
->complex->y
,
579 o_current
->complex->angle
,
580 o_current
->complex->mirror
,
581 clib
, o_current
->complex_basename
,
583 if (o_complex_is_embedded (o_current
)) {
584 o_embed (toplevel
, o_new
);
587 new_attribs
= o_complex_promote_attribs (toplevel
, o_new
);
589 /* Cull any attributes from new COMPLEX that are already attached to
590 * old COMPLEX. Note that the new_attribs list is kept consistent by
591 * setting GList data pointers to NULL if their OBJECTs are
592 * culled. At the end, the new_attribs list is updated by removing
593 * all list items with NULL data. This is slightly magic, but
595 for (iter
= new_attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
596 OBJECT
*attr_new
= iter
->data
;
600 g_assert (attr_new
->type
== OBJ_TEXT
);
602 o_attrib_get_name_value (attr_new
, &name
, NULL
);
604 value
= o_attrib_search_attached_attribs_by_name (o_current
, name
, 0);
606 o_attrib_remove (toplevel
, &o_new
->attribs
, attr_new
);
607 s_delete_object (toplevel
, attr_new
);
614 new_attribs
= g_list_remove_all (new_attribs
, NULL
);
616 /* Detach attributes from old OBJECT and attach to new OBJECT */
617 old_attribs
= g_list_copy (o_current
->attribs
);
618 o_attrib_detach_all (toplevel
, o_current
);
619 o_attrib_attach_list (toplevel
, old_attribs
, o_new
, 1);
620 g_list_free (old_attribs
);
622 /* Add new attributes to page */
623 s_page_append_list (toplevel
, page
, new_attribs
);
625 /* Update pinnumbers for current slot */
626 s_slot_update_object (toplevel
, o_new
);
628 /* Replace old OBJECT with new OBJECT */
629 s_page_replace (toplevel
, page
, o_current
, o_new
);
630 s_delete_object (toplevel
, o_current
);
632 /* Select new OBJECT */
633 o_selection_add (toplevel
, page
->selection_list
, o_new
);
635 /* mark the page as modified */
636 toplevel
->page_current
->CHANGED
= 1;
637 o_undo_savestate (w_current
, UNDO_ALL
);
642 /*! \brief Do autosave on all pages that are marked.
643 * \par Function Description
644 * Looks for pages with the do_autosave_backup flag activated and
647 * \param [in] w_current The GSCHEM_TOPLEVEL object to search for autosave's.
649 void o_autosave_backups(GSCHEM_TOPLEVEL
*w_current
)
651 TOPLEVEL
*toplevel
= w_current
->toplevel
;
653 PAGE
*p_save
, *p_current
;
654 gchar
*backup_filename
;
655 gchar
*real_filename
;
656 gchar
*only_filename
;
662 /* save current page */
663 p_save
= toplevel
->page_current
;
665 for ( iter
= geda_list_get_glist( toplevel
->pages
);
667 iter
= g_list_next( iter
) ) {
669 p_current
= (PAGE
*)iter
->data
;
671 if (p_current
->do_autosave_backup
== 0) {
674 if (p_current
->ops_since_last_backup
!= 0) {
675 /* make p_current the current page of toplevel */
676 s_page_goto (toplevel
, p_current
);
678 /* Get the real filename and file permissions */
679 real_filename
= follow_symlinks (p_current
->page_filename
, NULL
);
681 if (real_filename
== NULL
) {
682 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current
->page_filename
);
684 /* Get the directory in which the real filename lives */
685 dirname
= g_path_get_dirname (real_filename
);
686 only_filename
= g_path_get_basename(real_filename
);
688 backup_filename
= g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING
,
689 dirname
, G_DIR_SEPARATOR
, only_filename
);
691 /* If there is not an existing file with that name, compute the
692 * permissions and uid/gid that we will use for the newly-created file.
695 if (stat (real_filename
, &st
) != 0) {
696 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
701 /* Use default permissions */
702 saved_umask
= umask(0);
703 st
.st_mode
= 0666 & ~saved_umask
;
705 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
706 st
.st_uid
= getuid ();
708 result
= stat (dirname
, &dir_st
);
710 if (result
== 0 && (dir_st
.st_mode
& S_ISGID
))
711 st
.st_gid
= dir_st
.st_gid
;
713 st
.st_gid
= getgid ();
717 g_free (only_filename
);
718 g_free (real_filename
);
720 /* Make the backup file writable before saving a new one */
721 if ( g_file_test (backup_filename
, G_FILE_TEST_EXISTS
) &&
722 (! g_file_test (backup_filename
, G_FILE_TEST_IS_DIR
))) {
723 saved_umask
= umask(0);
724 if (chmod(backup_filename
, (S_IWRITE
|S_IWGRP
|S_IWOTH
) &
725 ((~saved_umask
) & 0777)) != 0) {
726 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
732 if (o_save (toplevel
,
733 s_page_objects (toplevel
->page_current
),
734 backup_filename
, NULL
)) {
736 p_current
->ops_since_last_backup
= 0;
737 p_current
->do_autosave_backup
= 0;
739 /* Make the backup file readonly so a 'rm *' command will ask
740 the user before deleting it */
741 saved_umask
= umask(0);
742 mask
= (S_IWRITE
|S_IWGRP
|S_IEXEC
|S_IXGRP
|S_IXOTH
);
744 mask
&= ((~saved_umask
) & 0777);
745 if (chmod(backup_filename
,mask
) != 0) {
746 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
751 s_log_message (_("Could NOT save backup file [%s]\n"),
754 g_free (backup_filename
);
758 /* restore current page */
759 s_page_goto (toplevel
, p_save
);