1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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>
34 #include "actions.decl.x"
36 /* break with the tradition here and input a list */
37 /*! \todo probably should go back and do the same for o_copy o_move
40 /*! \todo Finish function documentation!!!
42 * \par Function Description
45 void o_edit(GschemToplevel
*w_current
, GList
*list
, gboolean double_click
)
48 const gchar
*str
= NULL
;
54 o_current
= (OBJECT
*) list
->data
;
55 if (o_current
== NULL
) {
56 fprintf(stderr
, _("Got an unexpected NULL in o_edit\n"));
60 /* on double-click, see if we can enter a subschematic */
61 if (double_click
&& !w_current
->SHIFTKEY
&&
62 (o_current
->type
== OBJ_COMPLEX
||
63 o_current
->type
== OBJ_PLACEHOLDER
) &&
64 x_hierarchy_down_schematic (w_current
, o_current
))
67 /* for now deal with only the first item */
68 switch(o_current
->type
) {
70 /* also add the ability to multi attrib edit: nets, busses, pins */
72 case(OBJ_PLACEHOLDER
):
76 gschem_dockable_present (w_current
->multiattrib_dockable
);
80 picture_change_filename_dialog(w_current
);
83 arc_angle_dialog(w_current
, o_current
);
86 str
= o_text_get_string (w_current
->toplevel
, o_current
);
87 if (o_attrib_get_name_value (o_current
, NULL
, NULL
) &&
88 /* attribute editor only accept 1-line values for attribute */
89 o_text_num_lines (str
) == 1) {
90 x_multiattrib_edit_attribute (w_current
, o_current
);
92 gschem_dockable_present (w_current
->text_properties_dockable
);
97 /* has to be more extensive in the future */
98 /* some sort of redrawing? */
101 /*! \brief Rotate all objects in list.
102 * \par Function Description
103 * Given an object <B>list</B>, and the center of rotation
104 * (<B>centerx</B>,<B>centery</B>, this function traverses all the selection
105 * list, rotating each object through angle <B>angle</B>.
106 * The list contains a given object and all its attributes
107 * (refdes, pinname, pinlabel, ...).
108 * There is a second pass to run the rotate hooks of non-simple objects,
109 * like pin or complex objects, for example.
111 * \param [in] w_current The GschemToplevel object.
112 * \param [in] centerx Center x coordinate of rotation.
113 * \param [in] centery Center y coordinate of rotation.
114 * \param [in] angle Angle to rotate the objects through.
115 * \param [in] list The list of objects to rotate.
117 void o_rotate_world_update(GschemToplevel
*w_current
,
118 int centerx
, int centery
, int angle
, GList
*list
)
120 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
124 /* this is okay if you just hit rotate and have nothing selected */
126 i_action_stop (w_current
);
127 i_set_state(w_current
, SELECT
);
131 o_invalidate_glist (w_current
, list
);
133 /* Find connected objects, removing each object in turn from the
134 * connection list. We only _really_ want those objects connected
135 * to the selection, not those within in it.
137 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
138 o_current
= o_iter
->data
;
140 s_conn_remove_object_connections (toplevel
, o_current
);
143 o_glist_rotate_world( toplevel
, centerx
, centery
, angle
, list
);
145 /* Find connected objects, adding each object in turn back to the
146 * connection list. We only _really_ want those objects connected
147 * to the selection, not those within in it.
149 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
150 o_current
= o_iter
->data
;
152 s_conn_update_object (o_current
->page
, o_current
);
155 o_invalidate_glist (w_current
, list
);
157 /* Run rotate-objects-hook */
158 g_run_hook_object_list (w_current
, "%rotate-objects-hook", list
);
160 /* Don't save the undo state if we are inside an action */
161 /* This is useful when rotating the selection while moving, for example */
162 gschem_toplevel_page_content_changed (w_current
, toplevel
->page_current
);
163 if (!w_current
->inside_action
) {
164 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Rotate"));
167 if (w_current
->event_state
== ROTATEMODE
) {
168 i_set_state(w_current
, SELECT
);
172 /*! \todo Finish function documentation!!!
174 * \par Function Description
177 void o_mirror_world_update(GschemToplevel
*w_current
, int centerx
, int centery
, GList
*list
)
179 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
184 i_action_stop (w_current
);
185 i_set_state(w_current
, SELECT
);
189 o_invalidate_glist (w_current
, list
);
191 /* Find connected objects, removing each object in turn from the
192 * connection list. We only _really_ want those objects connected
193 * to the selection, not those within in it.
195 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
196 o_current
= o_iter
->data
;
198 s_conn_remove_object_connections (toplevel
, o_current
);
201 o_glist_mirror_world( toplevel
, centerx
, centery
, list
);
203 /* Find connected objects, adding each object in turn back to the
204 * connection list. We only _really_ want those objects connected
205 * to the selection, not those within in it.
207 for (o_iter
= list
; o_iter
!= NULL
; o_iter
= g_list_next (o_iter
)) {
208 o_current
= o_iter
->data
;
210 s_conn_update_object (o_current
->page
, o_current
);
213 o_invalidate_glist (w_current
, list
);
215 /* Run mirror-objects-hook */
216 g_run_hook_object_list (w_current
, "%mirror-objects-hook", list
);
218 gschem_toplevel_page_content_changed (w_current
, toplevel
->page_current
);
219 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Mirror"));
221 if (w_current
->event_state
== MIRRORMODE
) {
222 i_set_state(w_current
, SELECT
);
226 /*! \todo Finish function documentation!!!
228 * \par Function Description
231 void o_edit_show_hidden_lowlevel (GschemToplevel
*w_current
,
234 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
239 while (iter
!= NULL
) {
240 o_current
= (OBJECT
*)iter
->data
;
241 if (o_current
->type
== OBJ_TEXT
&& !o_is_visible (o_current
)) {
243 /* don't toggle the visibility flag */
244 o_text_recreate (toplevel
, o_current
);
247 if (o_current
->type
== OBJ_COMPLEX
|| o_current
->type
== OBJ_PLACEHOLDER
) {
248 o_edit_show_hidden_lowlevel(w_current
, o_current
->complex->prim_objs
);
249 o_current
->w_bounds_valid_for
= NULL
;
252 iter
= g_list_next (iter
);
256 /*! \todo Finish function documentation!!!
258 * \par Function Description
261 void o_edit_show_hidden (GschemToplevel
*w_current
, const GList
*o_list
)
263 /* this function just shows the hidden text, but doesn't toggle it */
264 /* this function does not change the CHANGED bit, no real changes are */
265 /* made to the schematic */
267 /* toggle show_hidden_text variable, which when it is true */
268 /* means that hidden text IS drawn */
269 w_current
->toplevel
->show_hidden_text
= !w_current
->toplevel
->show_hidden_text
;
270 i_show_state(w_current
, NULL
); /* update screen status */
272 o_edit_show_hidden_lowlevel(w_current
, o_list
);
273 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current
));
275 if (w_current
->toplevel
->show_hidden_text
) {
276 s_log_message(_("Hidden text is now visible\n"));
278 s_log_message(_("Hidden text is now invisible\n"));
281 gschem_action_set_active (action_edit_show_hidden
,
282 w_current
->toplevel
->show_hidden_text
,
286 /*! \todo Finish function documentation!!!
288 * \par Function Description
291 void o_edit_hide_specific_text (GschemToplevel
*w_current
,
295 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
298 gboolean changed
= FALSE
;
301 while (iter
!= NULL
) {
302 o_current
= (OBJECT
*)iter
->data
;
304 if (o_current
->type
== OBJ_TEXT
) {
305 const gchar
*str
= o_text_get_string (w_current
->toplevel
, o_current
);
306 if (!strncmp (stext
, str
, strlen (stext
))) {
307 if (o_is_visible (o_current
)) {
308 o_set_visibility (toplevel
, o_current
, INVISIBLE
);
309 o_text_recreate(toplevel
, o_current
);
315 iter
= g_list_next (iter
);
318 gschem_toplevel_page_content_changed (w_current
, toplevel
->page_current
);
319 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Hide Specific Text"));
321 gschem_page_view_invalidate_all (gschem_toplevel_get_current_page_view (w_current
));
324 /*! \todo Finish function documentation!!!
326 * \par Function Description
329 void o_edit_show_specific_text (GschemToplevel
*w_current
,
333 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
336 gboolean changed
= FALSE
;
339 while (iter
!= NULL
) {
340 o_current
= (OBJECT
*)iter
->data
;
342 if (o_current
->type
== OBJ_TEXT
) {
343 const gchar
*str
= o_text_get_string (w_current
->toplevel
, o_current
);
344 if (!strncmp (stext
, str
, strlen (stext
))) {
345 if (!o_is_visible (o_current
)) {
346 o_set_visibility (toplevel
, o_current
, VISIBLE
);
347 o_text_recreate(toplevel
, o_current
);
353 iter
= g_list_next (iter
);
356 gschem_toplevel_page_content_changed (w_current
, toplevel
->page_current
);
357 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Show Specific Text"));
362 /*! \brief Update a component.
364 * \par Function Description
365 * Updates \a o_current to the latest version of the symbol available
366 * in the symbol library, while preserving any attributes set in the
367 * current schematic. On success, returns the new OBJECT which
368 * replaces \a o_current on the page; \a o_current is deleted. On
369 * failure, returns NULL, and \a o_current is left unchanged.
371 * \param [in] w_current The GschemToplevel object.
372 * \param [in,out] o_current The OBJECT to be updated.
374 * \return the new OBJECT that replaces \a o_current.
377 o_update_component (GschemToplevel
*w_current
, OBJECT
*o_current
)
379 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
385 const CLibSymbol
*clib
;
387 g_return_val_if_fail (o_current
!= NULL
, NULL
);
388 g_return_val_if_fail (o_current
->type
== OBJ_COMPLEX
, NULL
);
389 g_return_val_if_fail (o_current
->complex_basename
!= NULL
, NULL
);
391 page
= o_get_page (toplevel
, o_current
);
393 /* Force symbol data to be reloaded from source */
394 clib
= s_clib_get_symbol_by_name (o_current
->complex_basename
);
395 s_clib_symbol_invalidate_data (clib
);
398 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
399 o_current
->complex_basename
);
403 /* Unselect the old object. */
404 o_selection_remove (toplevel
, page
->selection_list
, o_current
);
406 /* Create new object and set embedded */
407 o_new
= o_complex_new (toplevel
, OBJ_COMPLEX
, DEFAULT_COLOR
,
408 o_current
->complex->x
,
409 o_current
->complex->y
,
410 o_current
->complex->angle
,
411 o_current
->complex->mirror
,
412 clib
, o_current
->complex_basename
,
414 if (o_complex_is_embedded (o_current
)) {
415 o_embed (toplevel
, o_new
);
418 new_attribs
= o_complex_promote_attribs (toplevel
, o_new
);
420 /* Cull any attributes from new COMPLEX that are already attached to
421 * old COMPLEX. Note that the new_attribs list is kept consistent by
422 * setting GList data pointers to NULL if their OBJECTs are
423 * culled. At the end, the new_attribs list is updated by removing
424 * all list items with NULL data. This is slightly magic, but
426 for (iter
= new_attribs
; iter
!= NULL
; iter
= g_list_next (iter
)) {
427 OBJECT
*attr_new
= iter
->data
;
431 g_assert (attr_new
->type
== OBJ_TEXT
);
433 o_attrib_get_name_value (attr_new
, &name
, NULL
);
435 value
= o_attrib_search_attached_attribs_by_name (o_current
, name
, 0);
437 o_attrib_remove (toplevel
, &o_new
->attribs
, attr_new
);
438 s_delete_object (toplevel
, attr_new
);
445 new_attribs
= g_list_remove_all (new_attribs
, NULL
);
447 /* Detach attributes from old OBJECT and attach to new OBJECT */
448 old_attribs
= g_list_copy (o_current
->attribs
);
449 o_attrib_detach_all (toplevel
, o_current
);
450 o_attrib_attach_list (toplevel
, old_attribs
, o_new
, 1);
451 g_list_free (old_attribs
);
453 /* Add new attributes to page */
454 s_page_append_list (toplevel
, page
, new_attribs
);
456 /* Update pinnumbers for current slot */
457 s_slot_update_object (toplevel
, o_new
);
459 /* Replace old OBJECT with new OBJECT */
460 s_page_replace (toplevel
, page
, o_current
, o_new
);
461 s_delete_object (toplevel
, o_current
);
463 /* Select new OBJECT */
464 o_selection_add (toplevel
, page
->selection_list
, o_new
);
466 /* Can't undo this, so there's no point in adding an undo step. */
471 /*! \brief Do autosave on all pages that are marked.
472 * \par Function Description
473 * Looks for pages with the do_autosave_backup flag activated and
476 * \param [in] w_current The GschemToplevel object to search for autosave's.
478 void o_autosave_backups(GschemToplevel
*w_current
)
480 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
482 PAGE
*p_save
, *p_current
;
483 gchar
*backup_filename
;
484 gchar
*real_filename
;
485 gchar
*only_filename
;
491 /* save current page */
492 p_save
= toplevel
->page_current
;
494 for ( iter
= geda_list_get_glist( toplevel
->pages
);
496 iter
= g_list_next( iter
) ) {
498 p_current
= (PAGE
*)iter
->data
;
500 if (p_current
->do_autosave_backup
== 0) {
503 if (p_current
->ops_since_last_backup
!= 0) {
504 /* make p_current the current page of toplevel */
505 s_page_goto (toplevel
, p_current
);
506 gschem_toplevel_page_changed (w_current
);
508 /* Get the real filename and file permissions */
509 real_filename
= follow_symlinks (p_current
->page_filename
, NULL
);
511 if (real_filename
== NULL
) {
512 s_log_message (_("o_autosave_backups: Can't get the real filename of %s."), p_current
->page_filename
);
514 /* Get the directory in which the real filename lives */
515 dirname
= g_path_get_dirname (real_filename
);
516 only_filename
= g_path_get_basename(real_filename
);
518 backup_filename
= g_strdup_printf("%s%c"AUTOSAVE_BACKUP_FILENAME_STRING
,
519 dirname
, G_DIR_SEPARATOR
, only_filename
);
521 /* If there is not an existing file with that name, compute the
522 * permissions and uid/gid that we will use for the newly-created file.
525 if (stat (real_filename
, &st
) != 0) {
526 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
531 /* Use default permissions */
532 saved_umask
= umask(0);
533 st
.st_mode
= 0666 & ~saved_umask
;
535 #if defined(HAVE_GETUID) && defined(HAVE_GETGID)
536 st
.st_uid
= getuid ();
538 result
= stat (dirname
, &dir_st
);
540 if (result
== 0 && (dir_st
.st_mode
& S_ISGID
))
541 st
.st_gid
= dir_st
.st_gid
;
543 st
.st_gid
= getgid ();
547 g_free (only_filename
);
548 g_free (real_filename
);
550 /* Make the backup file writable before saving a new one */
551 if ( g_file_test (backup_filename
, G_FILE_TEST_EXISTS
) &&
552 (! g_file_test (backup_filename
, G_FILE_TEST_IS_DIR
))) {
553 saved_umask
= umask(0);
554 if (chmod(backup_filename
, (S_IWRITE
|S_IWGRP
|S_IWOTH
) &
555 ((~saved_umask
) & 0777)) != 0) {
556 s_log_message (_("Could NOT set previous backup file [%s] read-write\n"),
562 if (o_save (toplevel
,
563 s_page_objects (toplevel
->page_current
),
564 backup_filename
, NULL
)) {
566 p_current
->ops_since_last_backup
= 0;
567 p_current
->do_autosave_backup
= 0;
569 /* Make the backup file readonly so a 'rm *' command will ask
570 the user before deleting it */
571 saved_umask
= umask(0);
572 mask
= (S_IWRITE
|S_IWGRP
|S_IEXEC
|S_IXGRP
|S_IXOTH
);
574 mask
&= ((~saved_umask
) & 0777);
575 if (chmod(backup_filename
,mask
) != 0) {
576 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
581 s_log_message (_("Could NOT save backup file [%s]\n"),
584 g_free (backup_filename
);
588 /* restore current page */
589 s_page_goto (toplevel
, p_save
);
590 gschem_toplevel_page_changed (w_current
);