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
21 * \file gschem_find_text_dockable.c
23 * \brief Stores state of a find text operation
38 #include "../include/gschem_find_text_dockable.h"
50 typedef void (*NotifyFunc
)(void*, void*);
54 assign_store (GschemFindTextDockable
*state
, GSList
*objects
);
57 class_init (GschemFindTextDockableClass
*klass
);
60 clear_store (GschemFindTextDockable
*state
);
63 dispose (GObject
*object
);
66 finalize (GObject
*object
);
69 find_objects_using_pattern (GSList
*pages
, const char *text
);
72 find_objects_using_regex (GSList
*pages
, const char *text
, GError
**error
);
75 find_objects_using_substring (GSList
*pages
, const char *text
);
78 get_pages (GList
*pages
, gboolean descend
);
81 get_property (GObject
*object
, guint param_id
, GValue
*value
, GParamSpec
*pspec
);
84 get_subpages (PAGE
*page
);
87 instance_init (GschemFindTextDockable
*state
);
90 create_widget (GschemDockable
*parent
);
93 object_weakref_cb (OBJECT
*object
, GschemFindTextDockable
*state
);
96 remove_object (GschemFindTextDockable
*state
, OBJECT
*object
);
99 select_cb (GtkTreeSelection
*selection
, GschemFindTextDockable
*state
);
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
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
)
124 GSList
*objects
= NULL
;
127 all_pages
= get_pages (pages
, descend
);
130 case FIND_TYPE_SUBSTRING
:
131 objects
= find_objects_using_substring (all_pages
, text
);
134 case FIND_TYPE_PATTERN
:
135 objects
= find_objects_using_pattern (all_pages
, text
);
138 case FIND_TYPE_REGEX
:
139 objects
= find_objects_using_regex (all_pages
, text
, NULL
);
146 g_slist_free (all_pages
);
148 assign_store (state
, objects
);
150 count
= g_slist_length (objects
);
151 g_slist_free (objects
);
157 /*! \brief Get/register GschemFindTextDockable type.
160 gschem_find_text_dockable_get_type ()
162 static GType 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
),
174 (GInstanceInitFunc
) instance_init
,
177 type
= g_type_register_static (GSCHEM_TYPE_DOCKABLE
,
178 "GschemFindTextDockable",
193 /*! \brief places object in the store so the user can see them
196 * \param [in] objects the list of objects to put in the store
199 assign_store (GschemFindTextDockable
*state
, GSList
*objects
)
203 g_return_if_fail (state
!= NULL
);
204 g_return_if_fail (state
->store
!= NULL
);
208 object_iter
= objects
;
210 while (object_iter
!= NULL
) {
212 OBJECT
*object
= (OBJECT
*) object_iter
->data
;
214 GtkTreeIter tree_iter
;
216 object_iter
= g_slist_next (object_iter
);
218 if (object
== NULL
) {
219 g_warning ("NULL object encountered");
223 if (object
->page
== NULL
) {
224 g_warning ("NULL page encountered");
228 if (object
->type
!= OBJ_TEXT
) {
229 g_warning ("expecting a text object");
233 str
= o_text_get_string (object
->page
->toplevel
, object
);
236 g_warning ("NULL string encountered");
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"));
247 basename
= g_path_get_basename (object
->page
->page_filename
);
249 gtk_list_store_set (state
->store
,
251 COLUMN_FILENAME
, basename
,
253 COLUMN_OBJECT
, object
,
261 /*! \brief initialize class
263 * \param [in] klass The class for initialization
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 */
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.
300 clear_store (GschemFindTextDockable
*state
)
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
);
311 GValue value
= G_VALUE_INIT
;
313 gtk_tree_model_get_value (GTK_TREE_MODEL (state
->store
),
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
336 dispose (GObject
*object
)
338 GschemFindTextDockable
*state
= GSCHEM_FIND_TEXT_DOCKABLE (object
);
342 g_object_unref (state
->store
);
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
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
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
);
389 g_warning ("NULL page encountered");
393 object_iter
= s_page_objects (page
);
395 while (object_iter
!= NULL
) {
396 OBJECT
*object
= (OBJECT
*) object_iter
->data
;
399 object_iter
= g_list_next (object_iter
);
401 if (object
== NULL
) {
402 g_warning ("NULL object encountered");
406 if (object
->type
!= OBJ_TEXT
) {
410 if (!(o_is_visible (object
) || page
->toplevel
->show_hidden_text
)) {
414 str
= o_text_get_string (object
->page
->toplevel
, object
);
417 g_warning ("NULL string encountered");
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
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
;
447 g_return_val_if_fail (text
!= NULL
, NULL
);
449 regex
= g_regex_new (text
,
454 if (ierror
!= NULL
) {
455 g_propagate_error (error
, ierror
);
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
);
466 g_warning ("NULL page encountered");
470 object_iter
= s_page_objects (page
);
472 while (object_iter
!= NULL
) {
473 OBJECT
*object
= (OBJECT
*) object_iter
->data
;
476 object_iter
= g_list_next (object_iter
);
478 if (object
== NULL
) {
479 g_warning ("NULL object encountered");
483 if (object
->type
!= OBJ_TEXT
) {
487 if (!(o_is_visible (object
) || page
->toplevel
->show_hidden_text
)) {
491 str
= o_text_get_string (object
->page
->toplevel
, object
);
494 g_warning ("NULL string encountered");
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
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
);
531 g_warning ("NULL page encountered");
535 object_iter
= s_page_objects (page
);
537 while (object_iter
!= NULL
) {
538 OBJECT
*object
= (OBJECT
*) object_iter
->data
;
541 object_iter
= g_list_next (object_iter
);
543 if (object
== NULL
) {
544 g_warning ("NULL object encountered");
548 if (object
->type
!= OBJ_TEXT
) {
552 if (!(o_is_visible (object
) || page
->toplevel
->show_hidden_text
)) {
556 str
= o_text_get_string (object
->page
->toplevel
, object
);
559 g_warning ("NULL string encountered");
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
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
);
595 g_warning ("NULL page encountered");
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
)) {
606 output_list
= g_slist_prepend (output_list
, page
);
607 g_hash_table_insert (visit_list
, page
, NULL
);
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
623 * \param [in] param_id
624 * \param [in,out] value
628 get_property (GObject
*object
, guint param_id
, GValue
*value
, GParamSpec
*pspec
)
630 /* GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (object); */
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
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
) {
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");
669 if (object
->type
!= OBJ_COMPLEX
) {
673 attrib
= o_attrib_search_attached_attribs_by_name (object
,
677 if (attrib
== NULL
) {
678 attrib
= o_attrib_search_inherited_attribs_by_name (object
,
683 if (attrib
== NULL
) {
687 filenames
= g_strsplit (attrib
, ",", 0);
689 if (filenames
== NULL
) {
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
713 instance_init (GschemFindTextDockable
*state
)
715 state
->store
= gtk_list_store_new(COLUMN_COUNT
,
722 create_widget (GschemDockable
*parent
)
724 GschemFindTextDockable
*state
= GSCHEM_FIND_TEXT_DOCKABLE (parent
);
726 GtkTreeViewColumn
*column
;
727 GtkCellRenderer
*renderer
;
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);
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
);
771 /*! \brief callback for an object that has been destroyed
773 * \param [in] object the object that has been destroyed
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.
794 * \param [in] object the object to remove from the store
797 remove_object (GschemFindTextDockable
*state
, OBJECT
*object
)
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
);
809 GValue value
= G_VALUE_INIT
;
811 gtk_tree_model_get_value (GTK_TREE_MODEL (state
->store
),
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
);
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
838 select_cb (GtkTreeSelection
*selection
, GschemFindTextDockable
*state
)
843 g_return_if_fail (selection
!= NULL
);
844 g_return_if_fail (state
!= NULL
);
846 success
= gtk_tree_selection_get_selected (selection
, NULL
, &iter
);
849 GValue value
= G_VALUE_INIT
;
851 gtk_tree_model_get_value (GTK_TREE_MODEL (state
->store
),
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
);
862 g_warning ("NULL object encountered");
866 g_value_unset (&value
);
871 /*! \brief Set a gobject property
874 * \param [in] param_id
875 * \param [in,out] value
879 set_property (GObject
*object
, guint param_id
, const GValue
*value
, GParamSpec
*pspec
)
881 /* GschemFindTextDockable *state = GSCHEM_FIND_TEXT_DOCKABLE (object); */
885 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);