Updated Spanish translation
[anjuta-git-plugin.git] / plugins / symbol-browser / an_symbol_search.c
blob0b41ea07ab9565873ad160a43f67be23fdb584c6
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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.
23 #include <config.h>
24 #include <string.h>
25 #include <gdk/gdkkeysyms.h>
26 #include <gtk/gtk.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"
44 #include "plugin.h"
46 /* private class */
47 struct _AnjutaSymbolSearchPriv
49 AnjutaSymbolView *sv;
50 GtkTreeModel *model; /* AnSymbolView's one [gtk_tree_store] */
52 GtkWidget *entry; /* entrybox */
53 GtkWidget *hitlist; /* treeview */
55 GCompletion *completion;
57 guint idle_complete;
58 guint idle_filter;
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,
65 GtkTreePath * arg1,
66 GtkTreeViewColumn * arg2,
67 AnjutaSymbolSearch * search);
69 static gboolean an_symbol_search_on_entry_key_press_event (GtkEntry * entry,
70 GdkEventKey *
71 event,
72 AnjutaSymbolSearch
73 * search);
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,
80 const gchar * text,
81 gint length,
82 gint * position,
83 AnjutaSymbolSearch *
84 search);
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 *
89 model,
90 const gchar * string);
92 enum
94 SYM_SELECTED,
95 LAST_SIGNAL
98 enum
100 PIXBUF_COLUMN,
101 NAME_COLUMN,
102 SVFILE_ENTRY_COLUMN,
103 COLUMNS_NB
106 /* max hits to display on the search tab */
107 #define MAX_HITS 100
109 static GtkVBox *parent_class;
110 static gint signals[LAST_SIGNAL] = { 0 };
112 /*---------------------------------------------------------------------------*/
113 static void
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 */
122 if (priv->model)
124 anjuta_symbol_search_clear(search);
125 g_object_unref (priv->model);
126 priv->model = NULL;
129 if (priv->entry)
130 priv->entry = NULL;
132 if (priv->hitlist)
133 priv->hitlist = NULL;
135 G_OBJECT_CLASS (parent_class)->dispose (obj);
138 /*---------------------------------------------------------------------------*/
139 static void
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);
149 g_free (priv);
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;
160 priv=search->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))));
174 GType
175 anjuta_symbol_search_get_type (void)
177 static GType type = 0;
179 if (!type)
181 static const GTypeInfo info = {
182 sizeof (AnjutaSymbolSearchClass),
183 NULL,
184 NULL,
185 (GClassInitFunc) an_symbol_search_class_init,
186 NULL,
187 NULL,
188 sizeof (AnjutaSymbolSearch),
190 (GInstanceInitFunc) an_symbol_search_init,
193 type = g_type_register_static (GTK_TYPE_VBOX,
194 "AnjutaSymbolSearch", &info,
197 return type;
200 static void
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),
214 G_SIGNAL_RUN_LAST,
215 G_STRUCT_OFFSET (AnjutaSymbolSearchClass,
216 symbol_selected), NULL, NULL,
217 g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE,
218 1, G_TYPE_POINTER);
221 /*--------------------------------------------------------------------------*/
222 static void
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);
233 search->priv = priv;
235 priv->idle_complete = 0;
236 priv->idle_filter = 0;
237 priv->sv = NULL;
239 priv->completion =
240 g_completion_new (NULL);
242 priv->hitlist = gtk_tree_view_new ();
244 priv->model = GTK_TREE_MODEL (gtk_tree_store_new (COLUMNS_NB,
245 GDK_TYPE_PIXBUF,
246 G_TYPE_STRING,
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",
261 PIXBUF_COLUMN);
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",
266 NAME_COLUMN);
268 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->hitlist), column);
269 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (priv->hitlist),
270 column);
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),
282 search);
284 g_signal_connect (priv->hitlist, "row_activated",
285 G_CALLBACK (an_symbol_search_on_tree_row_activate),
286 search);
288 g_signal_connect (priv->entry, "changed",
289 G_CALLBACK (an_symbol_search_on_entry_changed),
290 search);
292 g_signal_connect (priv->entry, "activate",
293 G_CALLBACK (an_symbol_search_on_entry_activated),
294 search);
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));
318 static gboolean
319 an_symbol_search_on_tree_row_activate (GtkTreeView * view,
320 GtkTreePath * arg1,
321 GtkTreeViewColumn * arg2,
322 AnjutaSymbolSearch * search)
325 GtkTreeIter iter;
326 AnjutaSymbolSearchPriv *priv;
327 AnjutaSymbolInfo *sym;
328 GtkTreeSelection *selection;
330 priv = search->priv;
332 selection = gtk_tree_view_get_selection (view);
334 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
336 DEBUG_PRINT
337 ("an_symbol_search_on_tree_row_activate: error getting selected row");
338 return FALSE;
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
347 * the selection etc.
349 return FALSE;
352 static gboolean
353 an_symbol_search_on_entry_key_press_event (GtkEntry * entry,
354 GdkEventKey * event,
355 AnjutaSymbolSearch * search)
357 AnjutaSymbolSearchPriv *priv;
359 priv = search->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);
369 else
371 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
372 gtk_editable_select_region (GTK_EDITABLE (entry), -1,
373 -1);
375 return TRUE;
378 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
380 GtkTreeIter iter;
381 AnjutaSymbolInfo *sym;
382 gchar *name;
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),
392 &iter,
393 NAME_COLUMN, &name,
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);
407 g_free (name);
409 return TRUE;
412 return FALSE;
415 static void
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));
424 priv = search->priv;
426 DEBUG_PRINT("Entry changed");
428 if (!priv->idle_filter)
430 priv->idle_filter =
431 g_idle_add ((GSourceFunc)
432 an_symbol_search_filter_idle, search);
436 static void
437 an_symbol_search_on_entry_activated (GtkEntry * entry,
438 AnjutaSymbolSearch * search)
440 AnjutaSymbolInfo *info;
441 AnjutaSymbolSearchPriv *priv;
442 gchar *str;
444 g_return_if_fail (GTK_IS_ENTRY (entry));
445 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search));
447 priv = search->priv;
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);
453 if (info)
454 anjuta_symbol_info_free (info);
457 static void
458 an_symbol_search_on_entry_text_inserted (GtkEntry * entry,
459 const gchar * text,
460 gint length,
461 gint * position,
462 AnjutaSymbolSearch * search)
464 AnjutaSymbolSearchPriv *priv;
465 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search));
467 priv = search->priv;
469 if (!priv->idle_complete)
471 priv->idle_complete =
472 g_idle_add ((GSourceFunc)
473 an_symbol_search_complete_idle, search);
477 static gboolean
478 an_symbol_search_complete_idle (AnjutaSymbolSearch * search)
480 AnjutaSymbolSearchPriv *priv;
481 const gchar *text;
482 gchar *completed = NULL;
483 GList *list;
484 gint text_length;
486 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search), FALSE);
488 priv = search->priv;
490 text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
492 list = g_completion_complete (priv->completion, (gchar *) text,
493 &completed);
495 if (completed)
497 text_length = strlen (text);
498 gtk_entry_set_text (GTK_ENTRY (priv->entry), completed);
500 gtk_editable_set_position (GTK_EDITABLE (priv->entry),
501 text_length);
503 gtk_editable_select_region (GTK_EDITABLE (priv->entry),
504 text_length, -1);
505 g_free (completed);
507 priv->idle_complete = 0;
508 return FALSE;
511 static gboolean
512 an_symbol_search_filter_idle (AnjutaSymbolSearch * search)
514 AnjutaSymbolSearchPriv *priv;
515 gchar *str;
516 AnjutaSymbolInfo *sym;
518 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search), FALSE);
520 priv = search->priv;
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 */
531 if (sym)
533 /* g_signal_emit (search, signals[SYM_SELECTED], 0, sym); */
534 anjuta_symbol_info_free (sym);
536 return FALSE;
539 AnjutaSymbolInfo *
540 an_symbol_search_model_filter (AnjutaSymbolSearch * search,
541 const gchar * string)
543 AnjutaSymbolSearchPriv *priv;
544 gint i;
545 GtkTreeStore *store;
546 const GPtrArray *tags;
547 AnjutaSymbolInfo *exactsym = NULL;
548 gint hits = 0;
550 g_return_val_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search), NULL);
551 g_return_val_if_fail (string != NULL, NULL);
553 priv = search->priv;
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));
564 if (strlen (string))
566 tags = tm_workspace_find (string, tm_tag_max_t, NULL, TRUE, TRUE);
567 if (tags && (tags->len > 0))
569 GList *completion_list;
570 gint max_hits;
572 hits = tags->len;
573 max_hits = (tags->len < MAX_HITS)? tags->len : MAX_HITS;
575 completion_list = NULL;
577 for (i = 0; i < max_hits; ++i)
579 TMSymbol *symbol;
580 GtkTreeIter iter;
581 TMTag *tag;
582 AnjutaSymbolInfo *sym = NULL;
584 tag = (TMTag *) tags->pdata[i];
585 symbol = g_new0 (TMSymbol, 1);
586 symbol->tag = tag;
588 sym = anjuta_symbol_info_new (symbol,
589 anjuta_symbol_info_get_node_type (symbol, NULL));
591 if (sym->sym_name)
593 /* add a new iter */
594 gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL);
596 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
597 PIXBUF_COLUMN,
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));
605 if ((hits == 1) ||
606 (!exactsym && strcmp (tag->name, string) == 0))
608 if (exactsym)
609 anjuta_symbol_info_free (exactsym);
610 exactsym = sym;
613 g_free (symbol);
614 if (exactsym != sym)
615 anjuta_symbol_info_free (sym);
617 if (completion_list)
619 completion_list = g_list_reverse (completion_list);
620 g_completion_add_items (priv->completion, completion_list);
621 g_list_free (completion_list);
625 return exactsym;
628 /*--------------------------------------------------------------------------*/
629 GtkWidget *
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);
639 #if 0
640 void
641 anjuta_symbol_search_set_search_string (AnjutaSymbolSearch * search,
642 const gchar * str)
644 /* FIXME: untested function. Leave this here for a future feature? */
645 AnjutaSymbolSearchPriv *priv;
647 g_return_if_fail (ANJUTA_SYMBOL_IS_SEARCH (search));
649 priv = search->priv;
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);
657 void
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));
665 priv = search->priv;
667 gtk_widget_grab_focus (priv->entry);
669 #endif