gschem: Fix searching for hidden text.
[geda-gaf/whiteaudio.git] / gschem / src / o_misc.c
blobc3d7750baadd84808a2ad74cbd8909cdebb2bacb
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
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 /* break with the tradition here and input a list */
40 /*! \todo probably should go back and do the same for o_copy o_move
41 * o_delete...
43 /*! \todo Finish function documentation!!!
44 * \brief
45 * \par Function Description
48 void o_edit(GSCHEM_TOPLEVEL *w_current, GList *list)
50 OBJECT *o_current;
51 const gchar *str = NULL;
53 if (list == NULL) {
54 w_current->inside_action = 0;
55 i_set_state(w_current, SELECT);
56 return;
59 o_current = (OBJECT *) list->data;
60 if (o_current == NULL) {
61 fprintf(stderr, _("Got an unexpected NULL in o_edit\n"));
62 exit(-1);
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 */
69 case(OBJ_COMPLEX):
70 case(OBJ_PLACEHOLDER):
71 case(OBJ_NET):
72 case(OBJ_PIN):
73 case(OBJ_BUS):
74 x_multiattrib_open (w_current);
75 break;
77 case(OBJ_PICTURE):
78 picture_change_filename_dialog(w_current);
79 break;
80 case(OBJ_ARC):
81 arc_angle_dialog(w_current, o_current);
82 break;
83 case(OBJ_TEXT):
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);
89 } else {
90 o_text_edit(w_current, o_current);
92 break;
95 /* has to be more extensive in the future */
96 /* some sort of redrawing? */
99 /*! \todo Finish function documentation!!!
100 * \brief
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;
112 /* skip over head */
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;
117 if (object) {
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;
124 } else {
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!!!
138 * \brief
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
144 something.. :-) */
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;
155 if (object) {
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;
162 } else {
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;
192 OBJECT *o_current;
193 GList *o_iter;
195 /* this is okay if you just hit rotate and have nothing selected */
196 if (list == NULL) {
197 w_current->inside_action = 0;
198 i_set_state(w_current, SELECT);
199 return;
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!!!
240 * \brief
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;
247 OBJECT *o_current;
248 GList *o_iter;
250 if (list == NULL) {
251 w_current->inside_action = 0;
252 i_set_state(w_current, SELECT);
253 return;
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!!!
290 * \brief
291 * \par Function Description
294 void o_edit_show_hidden_lowlevel (GSCHEM_TOPLEVEL *w_current,
295 const GList *o_list)
297 TOPLEVEL *toplevel = w_current->toplevel;
298 OBJECT *o_current;
299 const GList *iter;
301 iter = o_list;
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!!!
320 * \brief
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"));
340 } else {
341 s_log_message(_("Hidden text is now invisible\n"));
345 #define FIND_WINDOW_HALF_SIZE (5000)
347 OBJECT *last_o = NULL;
348 int skiplast;
350 /*! \todo Finish function documentation!!!
351 * \brief
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;
361 char *attrib = NULL;
362 int count = 0;
363 PAGE *parent = NULL;
364 char *current_filename = NULL;
365 int page_control = 0;
366 int pcount = 0;
367 int rv;
368 int x1, y1, x2, y2;
369 int text_screen_height;
370 const GList *iter;
372 OBJECT *o_current;
374 skiplast = skip;
376 iter = o_list;
377 while (iter != NULL) {
378 o_current = (OBJECT *)iter->data;
380 if (descend) {
381 if (o_current->type == OBJ_COMPLEX) {
382 parent = toplevel->page_current;
383 attrib = o_attrib_search_attached_attribs_by_name (o_current,
384 "source", count);
386 /* if above is null, then look inside symbol */
387 if (attrib == NULL) {
388 attrib = o_attrib_search_inherited_attribs_by_name (o_current,
389 "source", count);
390 /* looking_inside = TRUE; */
393 if (attrib) {
394 pcount = 0;
395 current_filename = u_basic_breakup_string(attrib, ',', pcount);
396 if (current_filename != NULL) {
397 PAGE *child_page =
398 s_hierarchy_down_schematic_single(toplevel,
399 current_filename,
400 parent,
401 page_control,
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);
409 if (!rv) {
410 s_page_goto( toplevel, child_page );
411 return 0;
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)) {
425 if (!skiplast) {
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,
438 1, 0);
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 );
444 last_o = o_current;
445 break;
447 if (last_o == o_current) {
448 skiplast = 0;
451 } /* if (strstr(o_current->text->string,stext)) */
452 } /* if (o_current->type == OBJ_TEXT) */
453 iter = g_list_next (iter);
455 if (iter == NULL) {
456 return 1;
459 return (iter == NULL);
463 /*! \todo Finish function documentation!!!
464 * \brief
465 * \par Function Description
468 void o_edit_hide_specific_text (GSCHEM_TOPLEVEL *w_current,
469 const GList *o_list,
470 char *stext)
472 TOPLEVEL *toplevel = w_current->toplevel;
473 OBJECT *o_current;
474 const GList *iter;
476 iter = o_list;
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!!!
498 * \brief
499 * \par Function Description
502 void o_edit_show_specific_text (GSCHEM_TOPLEVEL *w_current,
503 const GList *o_list,
504 char *stext)
506 TOPLEVEL *toplevel = w_current->toplevel;
507 OBJECT *o_current;
508 const GList *iter;
510 iter = o_list;
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.
545 OBJECT *
546 o_update_component (GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
548 TOPLEVEL *toplevel = w_current->toplevel;
549 OBJECT *o_new;
550 PAGE *page;
551 GList *new_attribs;
552 GList *old_attribs;
553 GList *iter;
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);
566 if (clib == NULL) {
567 s_log_message (_("Could not find symbol [%s] in library. Update failed.\n"),
568 o_current->complex_basename);
569 return NULL;
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
594 * works. */
595 for (iter = new_attribs; iter != NULL; iter = g_list_next (iter)) {
596 OBJECT *attr_new = iter->data;
597 gchar *name;
598 gchar *value;
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);
605 if (value != NULL) {
606 o_attrib_remove (toplevel, &o_new->attribs, attr_new);
607 s_delete_object (toplevel, attr_new);
608 iter->data = NULL;
611 g_free (name);
612 g_free (value);
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);
639 return o_new;
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
645 * autosaves them.
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;
652 GList *iter;
653 PAGE *p_save, *p_current;
654 gchar *backup_filename;
655 gchar *real_filename;
656 gchar *only_filename;
657 gchar *dirname;
658 mode_t saved_umask;
659 mode_t mask;
660 struct stat st;
662 /* save current page */
663 p_save = toplevel->page_current;
665 for ( iter = geda_list_get_glist( toplevel->pages );
666 iter != NULL;
667 iter = g_list_next( iter ) ) {
669 p_current = (PAGE *)iter->data;
671 if (p_current->do_autosave_backup == 0) {
672 continue;
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);
683 } else {
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)
697 struct stat dir_st;
698 int result;
699 #endif
701 /* Use default permissions */
702 saved_umask = umask(0);
703 st.st_mode = 0666 & ~saved_umask;
704 umask(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;
712 else
713 st.st_gid = getgid ();
714 #endif
716 g_free (dirname);
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"),
727 backup_filename);
729 umask(saved_umask);
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);
743 mask = (~mask)&0777;
744 mask &= ((~saved_umask) & 0777);
745 if (chmod(backup_filename,mask) != 0) {
746 s_log_message (_("Could NOT set backup file [%s] readonly\n"),
747 backup_filename);
749 umask(saved_umask);
750 } else {
751 s_log_message (_("Could NOT save backup file [%s]\n"),
752 backup_filename);
754 g_free (backup_filename);
758 /* restore current page */
759 s_page_goto (toplevel, p_save);