1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2001-2003 CodeFactory AB
4 * Copyright (C) 2001-2003 Mikael Hallendal <micke@imendio.com>
5 * Copyright (C) 2005 Massimo CorĂ <maxcvs@email.it>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include <gdk/gdkkeysyms.h>
27 #include <gtk/gtkaccessible.h>
28 #include <gtk/gtkcellrenderertext.h>
29 #include <gtk/gtkentry.h>
30 #include <gtk/gtkframe.h>
31 #include <gtk/gtkhbox.h>
32 #include <gtk/gtkvbox.h>
33 #include <gtk/gtklabel.h>
34 #include <gtk/gtkscrolledwindow.h>
35 #include <gtk/gtktreeview.h>
36 #include <gtk/gtktreeselection.h>
37 #include <glib/gi18n.h>
38 #include <libanjuta/anjuta-debug.h>
40 #include "an_symbol_search.h"
41 #include "an_symbol_info.h"
42 #include "an_symbol_view.h"
47 struct _AnjutaSymbolSearchPriv
50 GtkTreeModel
*model
; /* AnSymbolView's one [gtk_tree_store] */
52 GtkWidget
*entry
; /* entrybox */
53 GtkWidget
*hitlist
; /* treeview */
55 GCompletion
*completion
;
61 static void an_symbol_search_init (AnjutaSymbolSearch
* search
);
62 static void an_symbol_search_class_init (AnjutaSymbolSearchClass
* klass
);
63 static void an_symbol_search_finalize (GObject
* object
);
64 static gboolean
an_symbol_search_on_tree_row_activate(GtkTreeView
* view
,
66 GtkTreeViewColumn
* arg2
,
67 AnjutaSymbolSearch
* search
);
69 static gboolean
an_symbol_search_on_entry_key_press_event (GtkEntry
* entry
,
75 static void an_symbol_search_on_entry_changed (GtkEntry
* entry
,
76 AnjutaSymbolSearch
* search
);
77 static void an_symbol_search_on_entry_activated (GtkEntry
* entry
,
78 AnjutaSymbolSearch
* search
);
79 static void an_symbol_search_on_entry_text_inserted (GtkEntry
* entry
,
85 static gboolean
an_symbol_search_complete_idle (AnjutaSymbolSearch
* search
);
86 static gboolean
an_symbol_search_filter_idle (AnjutaSymbolSearch
* search
);
88 static AnjutaSymbolInfo
*an_symbol_search_model_filter (AnjutaSymbolSearch
*
90 const gchar
* string
);
106 /* max hits to display on the search tab */
109 static GtkVBox
*parent_class
;
110 static gint signals
[LAST_SIGNAL
] = { 0 };
112 /*---------------------------------------------------------------------------*/
114 an_symbol_search_dispose (GObject
* obj
)
116 AnjutaSymbolSearch
*search
= ANJUTA_SYMBOL_SEARCH (obj
);
117 AnjutaSymbolSearchPriv
*priv
= search
->priv
;
119 DEBUG_PRINT("Destroying symbolsearch");
121 /* anjuta_symbol_view's dispose should manage it's freeing */
124 anjuta_symbol_search_clear(search
);
125 g_object_unref (priv
->model
);
133 priv
->hitlist
= NULL
;
135 G_OBJECT_CLASS (parent_class
)->dispose (obj
);
138 /*---------------------------------------------------------------------------*/
140 an_symbol_search_finalize (GObject
* obj
)
142 AnjutaSymbolSearch
*search
= ANJUTA_SYMBOL_SEARCH (obj
);
143 AnjutaSymbolSearchPriv
*priv
= search
->priv
;
145 DEBUG_PRINT ("Finalizing symbolsearch widget");
147 g_list_foreach (priv
->completion
->items
, (GFunc
)g_free
, NULL
);
148 g_completion_free (priv
->completion
);
151 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
154 /*-----------------------------------------------------------------------------
155 * Cleaning issues. This function must be called when a project is removed.
157 void anjuta_symbol_search_clear (AnjutaSymbolSearch
*search
) {
159 AnjutaSymbolSearchPriv
*priv
;
162 /* set entry text to a NULL string */
163 gtk_entry_set_text (GTK_ENTRY (priv
->entry
), "");
165 /* thrown away the g_completion words */
166 g_list_foreach (priv
->completion
->items
, (GFunc
)g_free
, NULL
);
167 g_completion_clear_items (priv
->completion
);
169 /* clean the gtk_tree_store */
170 gtk_tree_store_clear (GTK_TREE_STORE(gtk_tree_view_get_model
171 (GTK_TREE_VIEW (priv
->hitlist
))));
175 anjuta_symbol_search_get_type (void)
177 static GType type
= 0;
181 static const GTypeInfo info
= {
182 sizeof (AnjutaSymbolSearchClass
),
185 (GClassInitFunc
) an_symbol_search_class_init
,
188 sizeof (AnjutaSymbolSearch
),
190 (GInstanceInitFunc
) an_symbol_search_init
,
193 type
= g_type_register_static (GTK_TYPE_VBOX
,
194 "AnjutaSymbolSearch", &info
,
201 an_symbol_search_class_init (AnjutaSymbolSearchClass
* klass
)
203 GObjectClass
*object_class
;
205 object_class
= G_OBJECT_CLASS (klass
);
206 parent_class
= g_type_class_peek_parent (klass
);
208 object_class
->finalize
= an_symbol_search_finalize
;
209 object_class
->dispose
= an_symbol_search_dispose
;
211 signals
[SYM_SELECTED
] =
212 g_signal_new ("symbol-selected",
213 G_TYPE_FROM_CLASS (klass
),
215 G_STRUCT_OFFSET (AnjutaSymbolSearchClass
,
216 symbol_selected
), NULL
, NULL
,
217 g_cclosure_marshal_VOID__POINTER
, G_TYPE_NONE
,
221 /*--------------------------------------------------------------------------*/
223 an_symbol_search_init (AnjutaSymbolSearch
* search
)
226 AnjutaSymbolSearchPriv
*priv
;
227 GtkTreeViewColumn
*column
;
228 GtkCellRenderer
*renderer
;
229 GtkWidget
*frame
, *list_sw
;
231 /* allocate space for a AnjutaSymbolSearchPriv class. */
232 priv
= g_new0 (AnjutaSymbolSearchPriv
, 1);
235 priv
->idle_complete
= 0;
236 priv
->idle_filter
= 0;
240 g_completion_new (NULL
);
242 priv
->hitlist
= gtk_tree_view_new ();
244 priv
->model
= GTK_TREE_MODEL (gtk_tree_store_new (COLUMNS_NB
,
247 ANJUTA_TYPE_SYMBOL_INFO
));
249 gtk_tree_view_set_model (GTK_TREE_VIEW (priv
->hitlist
),
250 GTK_TREE_MODEL (priv
->model
));
251 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv
->hitlist
), TRUE
);
253 /* column initialization */
254 column
= gtk_tree_view_column_new ();
255 gtk_tree_view_column_set_sizing (column
,
256 GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
258 renderer
= gtk_cell_renderer_pixbuf_new ();
259 gtk_tree_view_column_pack_start (column
, renderer
, FALSE
);
260 gtk_tree_view_column_add_attribute (column
, renderer
, "pixbuf",
263 renderer
= gtk_cell_renderer_text_new ();
264 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
265 gtk_tree_view_column_add_attribute (column
, renderer
, "text",
268 gtk_tree_view_append_column (GTK_TREE_VIEW (priv
->hitlist
), column
);
269 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (priv
->hitlist
),
272 gtk_box_set_spacing (GTK_BOX (search
), 2);
274 gtk_container_set_border_width (GTK_CONTAINER (search
), 2);
276 /* creating entry box, where we'll type the keyword to look for */
277 priv
->entry
= gtk_entry_new ();
279 /* set up some signals */
280 g_signal_connect (priv
->entry
, "key_press_event",
281 G_CALLBACK (an_symbol_search_on_entry_key_press_event
),
284 g_signal_connect (priv
->hitlist
, "row_activated",
285 G_CALLBACK (an_symbol_search_on_tree_row_activate
),
288 g_signal_connect (priv
->entry
, "changed",
289 G_CALLBACK (an_symbol_search_on_entry_changed
),
292 g_signal_connect (priv
->entry
, "activate",
293 G_CALLBACK (an_symbol_search_on_entry_activated
),
296 g_signal_connect (priv
->entry
, "insert_text",
297 G_CALLBACK (an_symbol_search_on_entry_text_inserted
), search
);
299 gtk_box_pack_start (GTK_BOX (search
), priv
->entry
, FALSE
, FALSE
, 0);
301 frame
= gtk_frame_new (NULL
);
302 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_IN
);
304 list_sw
= gtk_scrolled_window_new (NULL
, NULL
);
305 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (list_sw
),
306 GTK_POLICY_AUTOMATIC
,
307 GTK_POLICY_AUTOMATIC
);
309 gtk_container_add (GTK_CONTAINER (frame
), list_sw
);
310 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv
->hitlist
), FALSE
);
312 gtk_container_add (GTK_CONTAINER (list_sw
), priv
->hitlist
);
313 gtk_box_pack_end_defaults (GTK_BOX (search
), frame
);
315 gtk_widget_show_all (GTK_WIDGET (search
));
319 an_symbol_search_on_tree_row_activate (GtkTreeView
* view
,
321 GtkTreeViewColumn
* arg2
,
322 AnjutaSymbolSearch
* search
)
326 AnjutaSymbolSearchPriv
*priv
;
327 AnjutaSymbolInfo
*sym
;
328 GtkTreeSelection
*selection
;
332 selection
= gtk_tree_view_get_selection (view
);
334 if (!gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
337 ("an_symbol_search_on_tree_row_activate: error getting selected row");
341 gtk_tree_model_get (GTK_TREE_MODEL (priv
->model
),
342 &iter
, SVFILE_ENTRY_COLUMN
, &sym
, -1);
344 g_signal_emit (search
, signals
[SYM_SELECTED
], 0, sym
);
345 anjuta_symbol_info_free (sym
);
346 /* Always return FALSE so the tree view gets the event and can update
353 an_symbol_search_on_entry_key_press_event (GtkEntry
* entry
,
355 AnjutaSymbolSearch
* search
)
357 AnjutaSymbolSearchPriv
*priv
;
361 DEBUG_PRINT ("key_press event");
362 if (event
->keyval
== GDK_Tab
)
364 DEBUG_PRINT ("tab key pressed");
365 if (event
->state
& GDK_CONTROL_MASK
)
367 gtk_widget_grab_focus (priv
->hitlist
);
371 gtk_editable_set_position (GTK_EDITABLE (entry
), -1);
372 gtk_editable_select_region (GTK_EDITABLE (entry
), -1,
378 if (event
->keyval
== GDK_Return
|| event
->keyval
== GDK_KP_Enter
)
381 AnjutaSymbolInfo
*sym
;
384 DEBUG_PRINT("enter key pressed: getting the first entry found");
386 /* Get the first entry found. */
387 if (gtk_tree_model_get_iter_first
388 (GTK_TREE_MODEL (priv
->model
), &iter
))
391 gtk_tree_model_get (GTK_TREE_MODEL (priv
->model
),
394 SVFILE_ENTRY_COLUMN
, &sym
, -1);
396 g_return_val_if_fail (&iter
!= NULL
, FALSE
);
398 DEBUG_PRINT ("got -----> sym_name: %s ", sym
->sym_name
);
399 gtk_entry_set_text (GTK_ENTRY (entry
), name
);
401 gtk_editable_set_position (GTK_EDITABLE (entry
), -1);
402 gtk_editable_select_region (GTK_EDITABLE (entry
), -1, -1);
404 g_signal_emit (search
, signals
[SYM_SELECTED
], 0, sym
);
406 anjuta_symbol_info_free (sym
);
416 an_symbol_search_on_entry_changed (GtkEntry
* entry
,
417 AnjutaSymbolSearch
* search
)
419 AnjutaSymbolSearchPriv
*priv
;
421 g_return_if_fail (GTK_IS_ENTRY (entry
));
422 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
));
426 DEBUG_PRINT("Entry changed");
428 if (!priv
->idle_filter
)
431 g_idle_add ((GSourceFunc
)
432 an_symbol_search_filter_idle
, search
);
437 an_symbol_search_on_entry_activated (GtkEntry
* entry
,
438 AnjutaSymbolSearch
* search
)
440 AnjutaSymbolInfo
*info
;
441 AnjutaSymbolSearchPriv
*priv
;
444 g_return_if_fail (GTK_IS_ENTRY (entry
));
445 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
));
449 str
= (gchar
*) gtk_entry_get_text (GTK_ENTRY (priv
->entry
));
451 /* parse the string typed in the entry */
452 info
= an_symbol_search_model_filter (search
, str
);
454 anjuta_symbol_info_free (info
);
458 an_symbol_search_on_entry_text_inserted (GtkEntry
* entry
,
462 AnjutaSymbolSearch
* search
)
464 AnjutaSymbolSearchPriv
*priv
;
465 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
));
469 if (!priv
->idle_complete
)
471 priv
->idle_complete
=
472 g_idle_add ((GSourceFunc
)
473 an_symbol_search_complete_idle
, search
);
478 an_symbol_search_complete_idle (AnjutaSymbolSearch
* search
)
480 AnjutaSymbolSearchPriv
*priv
;
482 gchar
*completed
= NULL
;
486 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
), FALSE
);
490 text
= gtk_entry_get_text (GTK_ENTRY (priv
->entry
));
492 list
= g_completion_complete (priv
->completion
, (gchar
*) text
,
497 text_length
= strlen (text
);
498 gtk_entry_set_text (GTK_ENTRY (priv
->entry
), completed
);
500 gtk_editable_set_position (GTK_EDITABLE (priv
->entry
),
503 gtk_editable_select_region (GTK_EDITABLE (priv
->entry
),
507 priv
->idle_complete
= 0;
512 an_symbol_search_filter_idle (AnjutaSymbolSearch
* search
)
514 AnjutaSymbolSearchPriv
*priv
;
516 AnjutaSymbolInfo
*sym
;
518 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
), FALSE
);
522 str
= (gchar
*) gtk_entry_get_text (GTK_ENTRY (priv
->entry
));
523 sym
= an_symbol_search_model_filter (search
, str
);
525 priv
->idle_filter
= 0;
527 /* if you wanna that on word completion event we [open file->goto symbol line]
528 just uncomment this part. Anyway this could cause some involountary editing/tampering
529 with just opened files */
533 /* g_signal_emit (search, signals[SYM_SELECTED], 0, sym); */
534 anjuta_symbol_info_free (sym
);
540 an_symbol_search_model_filter (AnjutaSymbolSearch
* search
,
541 const gchar
* string
)
543 AnjutaSymbolSearchPriv
*priv
;
546 const GPtrArray
*tags
;
547 AnjutaSymbolInfo
*exactsym
= NULL
;
550 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
), NULL
);
551 g_return_val_if_fail (string
!= NULL
, NULL
);
555 /* get the tree store model */
556 store
= GTK_TREE_STORE (gtk_tree_view_get_model
557 (GTK_TREE_VIEW (priv
->hitlist
)));
559 /* let's clean up rows from store model */
560 g_list_foreach (priv
->completion
->items
, (GFunc
)g_free
, NULL
);
561 g_completion_clear_items (priv
->completion
);
562 gtk_tree_store_clear (GTK_TREE_STORE (store
));
566 tags
= tm_workspace_find (string
, tm_tag_max_t
, NULL
, TRUE
, TRUE
);
567 if (tags
&& (tags
->len
> 0))
569 GList
*completion_list
;
573 max_hits
= (tags
->len
< MAX_HITS
)? tags
->len
: MAX_HITS
;
575 completion_list
= NULL
;
577 for (i
= 0; i
< max_hits
; ++i
)
582 AnjutaSymbolInfo
*sym
= NULL
;
584 tag
= (TMTag
*) tags
->pdata
[i
];
585 symbol
= g_new0 (TMSymbol
, 1);
588 sym
= anjuta_symbol_info_new (symbol
,
589 anjuta_symbol_info_get_node_type (symbol
, NULL
));
594 gtk_tree_store_append (GTK_TREE_STORE (store
), &iter
, NULL
);
596 gtk_tree_store_set (GTK_TREE_STORE (store
), &iter
,
598 anjuta_symbol_info_get_pixbuf (sym
->node_type
),
599 NAME_COLUMN
, tag
->name
,
600 SVFILE_ENTRY_COLUMN
, sym
, -1);
602 completion_list
= g_list_prepend (completion_list
,
603 g_strdup (sym
->sym_name
));
606 (!exactsym
&& strcmp (tag
->name
, string
) == 0))
609 anjuta_symbol_info_free (exactsym
);
615 anjuta_symbol_info_free (sym
);
619 completion_list
= g_list_reverse (completion_list
);
620 g_completion_add_items (priv
->completion
, completion_list
);
621 g_list_free (completion_list
);
628 /*--------------------------------------------------------------------------*/
630 anjuta_symbol_search_new (AnjutaSymbolView
*symbol_view
)
632 AnjutaSymbolSearch
*search
;
633 /* create a new object */
634 search
= g_object_new (ANJUTA_TYPE_SYMBOL_SEARCH
, NULL
);
635 search
->priv
->sv
= symbol_view
;
636 return GTK_WIDGET (search
);
641 anjuta_symbol_search_set_search_string (AnjutaSymbolSearch
* search
,
644 /* FIXME: untested function. Leave this here for a future feature? */
645 AnjutaSymbolSearchPriv
*priv
;
647 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
));
651 gtk_entry_set_text (GTK_ENTRY (priv
->entry
), str
);
653 gtk_editable_set_position (GTK_EDITABLE (priv
->entry
), -1);
654 gtk_editable_select_region (GTK_EDITABLE (priv
->entry
), -1, -1);
658 anjuta_symbol_search_grab_focus (AnjutaSymbolSearch
* search
)
660 /* FIXME: untested function. Leave this here for a future feature? */
661 AnjutaSymbolSearchPriv
*priv
;
663 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search
));
667 gtk_widget_grab_focus (priv
->entry
);