missing NULL terminator in set_config_x
[geda-gaf.git] / gschem / src / gschem_find_text_dockable.c
blob74af4de77097caaf45386a861da821d01280c808
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
20 /*!
21 * \file gschem_find_text_dockable.c
23 * \brief Stores state of a find text operation
26 #include <config.h>
28 #include <stdio.h>
29 #ifdef HAVE_STDLIB_H
30 #include <stdlib.h>
31 #endif
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
36 #include "gschem.h"
38 #include "../include/gschem_find_text_dockable.h"
41 enum
43 COLUMN_FILENAME,
44 COLUMN_STRING,
45 COLUMN_OBJECT,
46 COLUMN_COUNT
50 typedef void (*NotifyFunc)(void*, void*);
53 static void
54 assign_store (GschemFindTextDockable *state, GSList *objects);
56 static void
57 class_init (GschemFindTextDockableClass *klass);
59 static void
60 clear_store (GschemFindTextDockable *state);
62 static void
63 dispose (GObject *object);
65 static void
66 finalize (GObject *object);
68 static GSList*
69 find_objects_using_pattern (GSList *pages, const char *text);
71 static GSList*
72 find_objects_using_regex (GSList *pages, const char *text, GError **error);
74 static GSList*
75 find_objects_using_substring (GSList *pages, const char *text);
77 static GSList*
78 get_pages (GList *pages, gboolean descend);
80 static void
81 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec);
83 static GList*
84 get_subpages (PAGE *page);
86 static void
87 instance_init (GschemFindTextDockable *state);
89 static GtkWidget *
90 create_widget (GschemDockable *parent);
92 static void
93 object_weakref_cb (OBJECT *object, GschemFindTextDockable *state);
95 static void
96 remove_object (GschemFindTextDockable *state, OBJECT *object);
98 static void
99 select_cb (GtkTreeSelection *selection, GschemFindTextDockable *state);
101 static void
102 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec);
105 static GObjectClass *gschem_find_text_dockable_parent_class = NULL;
108 /*! \brief find instances of a given string
110 * Finds instances of a given string and displays the result inside this
111 * widget.
113 * \param [in] state
114 * \param [in] pages a list of pages to search
115 * \param [in] type the type of find to perform
116 * \param [in] text the text to find
117 * \param [in] descend decend the page heirarchy
118 * \return the number of objects found
121 gschem_find_text_dockable_find (GschemFindTextDockable *state, GList *pages, int type, const char *text, gboolean descend)
123 int count;
124 GSList *objects = NULL;
125 GSList *all_pages;
127 all_pages = get_pages (pages, descend);
129 switch (type) {
130 case FIND_TYPE_SUBSTRING:
131 objects = find_objects_using_substring (all_pages, text);
132 break;
134 case FIND_TYPE_PATTERN:
135 objects = find_objects_using_pattern (all_pages, text);
136 break;
138 case FIND_TYPE_REGEX:
139 objects = find_objects_using_regex (all_pages, text, NULL);
140 break;
142 default:
143 break;
146 g_slist_free (all_pages);
148 assign_store (state, objects);
150 count = g_slist_length (objects);
151 g_slist_free (objects);
153 return count;
157 /*! \brief Get/register GschemFindTextDockable type.
159 GType
160 gschem_find_text_dockable_get_type ()
162 static GType type = 0;
164 if (type == 0) {
165 static const GTypeInfo info = {
166 sizeof(GschemFindTextDockableClass),
167 NULL, /* base_init */
168 NULL, /* base_finalize */
169 (GClassInitFunc) class_init,
170 NULL, /* class_finalize */
171 NULL, /* class_data */
172 sizeof(GschemFindTextDockable),
173 0, /* n_preallocs */
174 (GInstanceInitFunc) instance_init,
177 type = g_type_register_static (GSCHEM_TYPE_DOCKABLE,
178 "GschemFindTextDockable",
179 &info,
183 return type;
193 /*! \brief places object in the store so the user can see them
195 * \param [in] state
196 * \param [in] objects the list of objects to put in the store
198 static void
199 assign_store (GschemFindTextDockable *state, GSList *objects)
201 GSList *object_iter;
203 g_return_if_fail (state != NULL);
204 g_return_if_fail (state->store != NULL);
206 clear_store (state);
208 object_iter = objects;
210 while (object_iter != NULL) {
211 char *basename;
212 OBJECT *object = (OBJECT*) object_iter->data;
213 const char *str;
214 GtkTreeIter tree_iter;
216 object_iter = g_slist_next (object_iter);
218 if (object == NULL) {
219 g_warning ("NULL object encountered");
220 continue;
223 if (object->page == NULL) {
224 g_warning ("NULL page encountered");
225 continue;
228 if (object->type != OBJ_TEXT) {
229 g_warning ("expecting a text object");
230 continue;
233 str = o_text_get_string (object->page->toplevel, object);
235 if (str == NULL) {
236 g_warning ("NULL string encountered");
237 continue;
240 s_object_weak_ref (object, (NotifyFunc) object_weakref_cb, state);
242 gtk_list_store_append (state->store, &tree_iter);
244 if (object->page->is_untitled)
245 basename = g_strdup (_("Untitled page"));
246 else
247 basename = g_path_get_basename (object->page->page_filename);
249 gtk_list_store_set (state->store,
250 &tree_iter,
251 COLUMN_FILENAME, basename,
252 COLUMN_STRING, str,
253 COLUMN_OBJECT, object,
254 -1);
256 g_free (basename);
261 /*! \brief initialize class
263 * \param [in] klass The class for initialization
265 static void
266 class_init (GschemFindTextDockableClass *klass)
268 gschem_find_text_dockable_parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
270 GSCHEM_DOCKABLE_CLASS (klass)->create_widget = create_widget;
272 G_OBJECT_CLASS (klass)->dispose = dispose;
273 G_OBJECT_CLASS (klass)->finalize = finalize;
275 G_OBJECT_CLASS (klass)->get_property = get_property;
276 G_OBJECT_CLASS (klass)->set_property = set_property;
278 g_signal_new ("select-object", /* signal_name */
279 G_OBJECT_CLASS_TYPE (klass), /* itype */
280 0, /* signal_flags */
281 0, /* class_offset */
282 NULL, /* accumulator */
283 NULL, /* accu_data */
284 g_cclosure_marshal_VOID__POINTER, /* c_marshaller */
285 G_TYPE_NONE, /* return_type */
286 1, /* n_params */
287 G_TYPE_POINTER
292 /*! \brief delete all items from the list store
294 * This function deletes all items in the list store and removes all the weak
295 * references to the objects.
297 * \param [in] state
299 static void
300 clear_store (GschemFindTextDockable *state)
302 GtkTreeIter iter;
303 gboolean valid;
305 g_return_if_fail (state != NULL);
306 g_return_if_fail (state->store != NULL);
308 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->store), &iter);
310 while (valid) {
311 GValue value = G_VALUE_INIT;
313 gtk_tree_model_get_value (GTK_TREE_MODEL (state->store),
314 &iter,
315 COLUMN_OBJECT,
316 &value);
318 if (G_VALUE_HOLDS_POINTER (&value)) {
319 OBJECT *object = g_value_get_pointer (&value);
321 s_object_weak_unref (object, (NotifyFunc) object_weakref_cb, state);
324 g_value_unset (&value);
326 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->store), &iter);
329 gtk_list_store_clear (state->store);
333 /*! \brief Dispose of the object
335 static void
336 dispose (GObject *object)
338 GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (object);
340 if (state->store) {
341 clear_store (state);
342 g_object_unref (state->store);
343 state->store = NULL;
346 /* lastly, chain up to the parent dispose */
348 g_return_if_fail (gschem_find_text_dockable_parent_class != NULL);
349 gschem_find_text_dockable_parent_class->dispose (object);
353 /*! \brief Finalize object
355 static void
356 finalize (GObject *object)
358 /* lastly, chain up to the parent finalize */
360 g_return_if_fail (gschem_find_text_dockable_parent_class != NULL);
361 gschem_find_text_dockable_parent_class->finalize (object);
365 /*! \brief Find all text objects that match a pattern
367 * \param pages the list of pages to search
368 * \param text the pattern to match
369 * \return a list of objects that match the given pattern
371 static GSList*
372 find_objects_using_pattern (GSList *pages, const char *text)
374 GSList *object_list = NULL;
375 GSList *page_iter = pages;
376 GPatternSpec *pattern;
378 g_return_val_if_fail (text != NULL, NULL);
380 pattern = g_pattern_spec_new (text);
382 while (page_iter != NULL) {
383 const GList *object_iter;
384 PAGE *page = (PAGE*) page_iter->data;
386 page_iter = g_slist_next (page_iter);
388 if (page == NULL) {
389 g_warning ("NULL page encountered");
390 continue;
393 object_iter = s_page_objects (page);
395 while (object_iter != NULL) {
396 OBJECT *object = (OBJECT*) object_iter->data;
397 const char *str;
399 object_iter = g_list_next (object_iter);
401 if (object == NULL) {
402 g_warning ("NULL object encountered");
403 continue;
406 if (object->type != OBJ_TEXT) {
407 continue;
410 if (!(o_is_visible (object) || page->toplevel->show_hidden_text)) {
411 continue;
414 str = o_text_get_string (object->page->toplevel, object);
416 if (str == NULL) {
417 g_warning ("NULL string encountered");
418 continue;
421 if (g_pattern_match_string (pattern, str)) {
422 object_list = g_slist_prepend (object_list, object);
427 g_pattern_spec_free (pattern);
429 return g_slist_reverse (object_list);
433 /*! \brief Find all text objects that match a regex
435 * \param pages the list of pages to search
436 * \param text the regex to match
437 * \return a list of objects that match the given regex
439 static GSList*
440 find_objects_using_regex (GSList *pages, const char *text, GError **error)
442 GError *ierror = NULL;
443 GSList *object_list = NULL;
444 GSList *page_iter = pages;
445 GRegex *regex;
447 g_return_val_if_fail (text != NULL, NULL);
449 regex = g_regex_new (text,
452 &ierror);
454 if (ierror != NULL) {
455 g_propagate_error (error, ierror);
456 return NULL;
459 while (page_iter != NULL) {
460 const GList *object_iter;
461 PAGE *page = (PAGE*) page_iter->data;
463 page_iter = g_slist_next (page_iter);
465 if (page == NULL) {
466 g_warning ("NULL page encountered");
467 continue;
470 object_iter = s_page_objects (page);
472 while (object_iter != NULL) {
473 OBJECT *object = (OBJECT*) object_iter->data;
474 const char *str;
476 object_iter = g_list_next (object_iter);
478 if (object == NULL) {
479 g_warning ("NULL object encountered");
480 continue;
483 if (object->type != OBJ_TEXT) {
484 continue;
487 if (!(o_is_visible (object) || page->toplevel->show_hidden_text)) {
488 continue;
491 str = o_text_get_string (object->page->toplevel, object);
493 if (str == NULL) {
494 g_warning ("NULL string encountered");
495 continue;
498 if (g_regex_match (regex, str, 0, NULL)) {
499 object_list = g_slist_prepend (object_list, object);
504 g_regex_unref (regex);
506 return g_slist_reverse (object_list);
510 /*! \brief Find all text objects that contain a substring
512 * \param pages the list of pages to search
513 * \param text the substring to find
514 * \return a list of objects that contain the given substring
516 static GSList*
517 find_objects_using_substring (GSList *pages, const char *text)
519 GSList *object_list = NULL;
520 GSList *page_iter = pages;
522 g_return_val_if_fail (text != NULL, NULL);
524 while (page_iter != NULL) {
525 const GList *object_iter;
526 PAGE *page = (PAGE*) page_iter->data;
528 page_iter = g_slist_next (page_iter);
530 if (page == NULL) {
531 g_warning ("NULL page encountered");
532 continue;
535 object_iter = s_page_objects (page);
537 while (object_iter != NULL) {
538 OBJECT *object = (OBJECT*) object_iter->data;
539 const char *str;
541 object_iter = g_list_next (object_iter);
543 if (object == NULL) {
544 g_warning ("NULL object encountered");
545 continue;
548 if (object->type != OBJ_TEXT) {
549 continue;
552 if (!(o_is_visible (object) || page->toplevel->show_hidden_text)) {
553 continue;
556 str = o_text_get_string (object->page->toplevel, object);
558 if (str == NULL) {
559 g_warning ("NULL string encountered");
560 continue;
563 if (strstr (str, text) != NULL) {
564 object_list = g_slist_prepend (object_list, object);
569 return g_slist_reverse (object_list);
574 /*! \brief obtain a list of pages for an operation
576 * Descends the heirarchy of pages, if selected, and removes duplicate pages.
578 * \param [in] pages the list of pages to begin search
579 * \param [in] descend alose locates subpages
580 * \return a list of all the pages
582 static GSList*
583 get_pages (GList *pages, gboolean descend)
585 GList *input_list = g_list_copy (pages);
586 GSList *output_list = NULL;
587 GHashTable *visit_list = g_hash_table_new (NULL, NULL);
589 while (input_list != NULL) {
590 PAGE *page = (PAGE*) input_list->data;
592 input_list = g_list_delete_link (input_list, input_list);
594 if (page == NULL) {
595 g_warning ("NULL page encountered");
596 continue;
599 /** \todo the following function becomes available in glib 2.32 */
600 /* if (g_hash_table_contains (visit_list, page)) { */
602 if (g_hash_table_lookup_extended (visit_list, page, NULL, NULL)) {
603 continue;
606 output_list = g_slist_prepend (output_list, page);
607 g_hash_table_insert (visit_list, page, NULL);
609 if (descend) {
610 input_list = g_list_concat (input_list, get_subpages (page));
614 g_hash_table_destroy (visit_list);
616 return g_slist_reverse (output_list);
620 /*! \brief Get a property
622 * \param [in] object
623 * \param [in] param_id
624 * \param [in,out] value
625 * \param [in] pspec
627 static void
628 get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec)
630 /* GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (object); */
632 switch (param_id) {
633 default:
634 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
639 /*! \brief get the subpages of a schematic page
641 * if any subpages are not loaded, this function will load them.
643 * \param [in] page the parent page
644 * \return a list of all the subpages
646 static GList*
647 get_subpages (PAGE *page)
649 const GList *object_iter;
650 GList *page_list = NULL;
652 g_return_val_if_fail (page != NULL, NULL);
654 object_iter = s_page_objects (page);
656 while (object_iter != NULL) {
657 char *attrib;
658 char **filenames;
659 char **iter;
660 OBJECT *object = (OBJECT*) object_iter->data;
662 object_iter = g_list_next (object_iter);
664 if (object == NULL) {
665 g_warning ("NULL object encountered");
666 continue;
669 if (object->type != OBJ_COMPLEX) {
670 continue;
673 attrib = o_attrib_search_attached_attribs_by_name (object,
674 "source",
677 if (attrib == NULL) {
678 attrib = o_attrib_search_inherited_attribs_by_name (object,
679 "source",
683 if (attrib == NULL) {
684 continue;
687 filenames = g_strsplit (attrib, ",", 0);
689 if (filenames == NULL) {
690 continue;
693 for (iter = filenames; *iter != NULL; iter++) {
694 PAGE *subpage = s_hierarchy_load_subpage (page, *iter, NULL);
696 if (subpage != NULL) {
697 page_list = g_list_prepend (page_list, subpage);
701 g_strfreev (filenames);
704 return g_list_reverse (page_list);
708 /*! \brief initialize a new instance
710 * \param [in] state the new instance
712 static void
713 instance_init (GschemFindTextDockable *state)
715 state->store = gtk_list_store_new(COLUMN_COUNT,
716 G_TYPE_STRING,
717 G_TYPE_STRING,
718 G_TYPE_POINTER);
721 static GtkWidget *
722 create_widget (GschemDockable *parent)
724 GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (parent);
726 GtkTreeViewColumn *column;
727 GtkCellRenderer *renderer;
728 GtkWidget *scrolled;
729 GtkTreeSelection *selection;
730 GtkWidget *tree_widget;
732 scrolled = gtk_scrolled_window_new (NULL, NULL);
734 tree_widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (state->store));
735 gtk_container_add (GTK_CONTAINER (scrolled), tree_widget);
737 /* filename column */
739 column = gtk_tree_view_column_new();
740 gtk_tree_view_column_set_resizable (column, TRUE);
741 gtk_tree_view_column_set_title (column, _("Filename"));
743 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_widget), column);
745 renderer = gtk_cell_renderer_text_new();
746 gtk_tree_view_column_pack_start(column, renderer, TRUE);
747 gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
749 /* text column */
751 column = gtk_tree_view_column_new();
752 gtk_tree_view_column_set_resizable (column, TRUE);
753 gtk_tree_view_column_set_title (column, _("Text"));
755 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_widget), column);
757 renderer = gtk_cell_renderer_text_new();
758 gtk_tree_view_column_pack_start(column, renderer, TRUE);
759 gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
761 /* attach signal to detect user selection */
763 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_widget));
764 g_signal_connect (selection, "changed", G_CALLBACK (select_cb), state);
766 gtk_widget_show_all (scrolled);
767 return scrolled;
771 /*! \brief callback for an object that has been destroyed
773 * \param [in] object the object that has been destroyed
774 * \param [in] state
776 static void
777 object_weakref_cb (OBJECT *object, GschemFindTextDockable *state)
779 g_return_if_fail (state != NULL);
781 remove_object (state, object);
785 /*! \brief remove an object from the store
787 * This function gets called in response to the object deletion. And, doesn't
788 * dereference the object.
790 * This function doesn't remove the weak reference, under the assumption that
791 * the object is being destroyed.
793 * \param [in] state
794 * \param [in] object the object to remove from the store
796 static void
797 remove_object (GschemFindTextDockable *state, OBJECT *object)
799 GtkTreeIter iter;
800 gboolean valid;
802 g_return_if_fail (object != NULL);
803 g_return_if_fail (state != NULL);
804 g_return_if_fail (state->store != NULL);
806 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->store), &iter);
808 while (valid) {
809 GValue value = G_VALUE_INIT;
811 gtk_tree_model_get_value (GTK_TREE_MODEL (state->store),
812 &iter,
813 COLUMN_OBJECT,
814 &value);
816 if (G_VALUE_HOLDS_POINTER (&value)) {
817 OBJECT *other = g_value_get_pointer (&value);
819 if (object == other) {
820 g_value_unset (&value);
821 valid = gtk_list_store_remove (state->store, &iter);
822 continue;
826 g_value_unset (&value);
827 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->store), &iter);
832 /*! \brief callback for user selecting an item
834 * \param [in] selection
835 * \param [in] state
837 static void
838 select_cb (GtkTreeSelection *selection, GschemFindTextDockable *state)
840 GtkTreeIter iter;
841 gboolean success;
843 g_return_if_fail (selection != NULL);
844 g_return_if_fail (state != NULL);
846 success = gtk_tree_selection_get_selected (selection, NULL, &iter);
848 if (success) {
849 GValue value = G_VALUE_INIT;
851 gtk_tree_model_get_value (GTK_TREE_MODEL (state->store),
852 &iter,
853 COLUMN_OBJECT,
854 &value);
856 if (G_VALUE_HOLDS_POINTER (&value)) {
857 OBJECT *object = g_value_get_pointer (&value);
859 if (object != NULL) {
860 g_signal_emit_by_name (state, "select-object", object);
861 } else {
862 g_warning ("NULL object encountered");
866 g_value_unset (&value);
871 /*! \brief Set a gobject property
873 * \param [in] object
874 * \param [in] param_id
875 * \param [in,out] value
876 * \param [in] pspec
878 static void
879 set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec)
881 /* GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (object); */
883 switch (param_id) {
884 default:
885 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);