2007-10-13 Johannes Schmid <jhs@gnome.org>
[anjuta-git-plugin.git] / plugins / symbol-db / symbol-db-view-search.c
blob6d5bc628a35c5a9780ba2899bb3a14d663f0a0f9
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) 2001-2003 CodeFactory AB
5 * Copyright (C) 2001-2003 Mikael Hallendal <micke@imendio.com>
6 * Copyright (C) 2005-2007 Massimo CorĂ  <maxcvs@email.it>
7 *
8 * anjuta is free software.
9 *
10 * You may redistribute it and/or modify it under the terms of the
11 * GNU General Public License, as published by the Free Software
12 * Foundation; either version 2 of the License, or (at your option)
13 * any later version.
15 * anjuta is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 * See the GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with anjuta. If not, write to:
22 * The Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02110-1301, USA.
27 #include <config.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <glib-object.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <gtk/gtk.h>
33 #include <gtk/gtkaccessible.h>
34 #include <gtk/gtkcellrenderertext.h>
35 #include <gtk/gtkentry.h>
36 #include <gtk/gtkframe.h>
37 #include <gtk/gtkhbox.h>
38 #include <gtk/gtkvbox.h>
39 #include <gtk/gtklabel.h>
40 #include <gtk/gtkscrolledwindow.h>
41 #include <gtk/gtktreeview.h>
42 #include <gtk/gtktreeselection.h>
43 #include <libgnome/gnome-i18n.h>
44 #include <libanjuta/anjuta-debug.h>
46 #include "symbol-db-view-search.h"
47 #include "symbol-db-engine.h"
48 #include "symbol-db-engine-iterator.h"
49 #include "symbol-db-engine-iterator-node.h"
50 #include "symbol-db-view.h"
52 /* private class */
53 struct _SymbolDBViewSearchPriv
55 SymbolDBEngine *sdbe;
56 GtkTreeModel *model;
58 GtkWidget *entry; /* entrybox */
59 GtkWidget *hitlist; /* treeview */
61 GCompletion *completion;
63 guint idle_complete;
64 guint idle_filter;
67 enum
69 SYM_SELECTED,
70 LAST_SIGNAL
73 enum {
74 COLUMN_PIXBUF,
75 COLUMN_NAME,
76 COLUMN_LINE,
77 COLUMN_FILE,
78 COLUMN_SYMBOL_ID,
79 COLUMN_MAX
82 /* max hits to display on the search tab */
83 #define MAX_HITS 100
85 static GtkVBox *parent_class;
86 static gint signals[LAST_SIGNAL] = { 0 };
88 static void
89 sdb_view_search_model_filter (SymbolDBViewSearch * search,
90 const gchar * string)
92 SymbolDBViewSearchPriv *priv;
93 gint i;
94 GtkTreeStore *store;
95 SymbolDBEngineIterator *iterator;
96 gint hits = 0;
98 g_return_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search));
99 g_return_if_fail (string != NULL);
101 priv = search->priv;
103 /* get the tree store model */
104 store = GTK_TREE_STORE (gtk_tree_view_get_model
105 (GTK_TREE_VIEW (priv->hitlist)));
107 /* let's clean up rows from store model */
108 g_list_foreach (priv->completion->items, (GFunc)g_free, NULL);
109 g_completion_clear_items (priv->completion);
111 gtk_tree_store_clear (GTK_TREE_STORE (store));
113 if (strlen (string))
115 iterator = symbol_db_engine_find_symbol_by_name_pattern (priv->sdbe,
116 string, SYMINFO_SIMPLE| SYMINFO_FILE_PATH |
117 SYMINFO_ACCESS | SYMINFO_KIND);
118 if (iterator)
120 GList *completion_list;
121 gint max_hits;
122 SymbolDBEngineIteratorNode *iter_node;
124 /* max number of hits to take care of */
125 hits = symbol_db_engine_iterator_get_n_items (iterator);
126 max_hits = (hits < MAX_HITS)? hits : MAX_HITS;
128 completion_list = NULL;
130 for (i = 0; i < max_hits; ++i)
132 GtkTreeIter iter;
134 iter_node = SYMBOL_DB_ENGINE_ITERATOR_NODE (iterator);
136 const gchar *sym_name =
137 symbol_db_engine_iterator_node_get_symbol_name (iter_node);
139 if (sym_name)
141 /* get the full file path instead of a database-oriented one. */
142 gchar *file_path =
143 symbol_db_engine_get_full_local_path (priv->sdbe,
144 symbol_db_engine_iterator_node_get_symbol_extra_string (
145 iter_node, SYMINFO_FILE_PATH));
147 /* add a new iter */
148 gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL);
150 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
151 COLUMN_PIXBUF, symbol_db_view_get_pixbuf (
152 symbol_db_engine_iterator_node_get_symbol_extra_string (
153 iter_node, SYMINFO_KIND),
154 symbol_db_engine_iterator_node_get_symbol_extra_string (
155 iter_node, SYMINFO_ACCESS)
157 COLUMN_NAME, sym_name,
158 COLUMN_LINE,
159 symbol_db_engine_iterator_node_get_symbol_file_pos (
160 iter_node),
161 COLUMN_FILE, file_path,
162 COLUMN_SYMBOL_ID,
163 symbol_db_engine_iterator_node_get_symbol_id (iter_node),
164 -1);
166 completion_list = g_list_prepend (completion_list,
167 g_strdup (sym_name));
168 g_free (file_path);
171 symbol_db_engine_iterator_move_next (iterator);
173 if (completion_list)
175 completion_list = g_list_reverse (completion_list);
176 g_completion_add_items (priv->completion, completion_list);
177 g_list_free (completion_list);
181 if (iterator)
182 g_object_unref (iterator);
186 static gboolean
187 sdb_view_search_filter_idle (SymbolDBViewSearch * search)
189 SymbolDBViewSearchPriv *priv;
190 gchar *str;
192 g_return_val_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search), FALSE);
194 priv = search->priv;
196 str = (gchar *) gtk_entry_get_text (GTK_ENTRY (priv->entry));
197 sdb_view_search_model_filter (search, str);
199 priv->idle_filter = 0;
200 return FALSE;
204 static void
205 sdb_view_search_on_entry_changed (GtkEntry * entry,
206 SymbolDBViewSearch * search)
208 SymbolDBViewSearchPriv *priv;
210 g_return_if_fail (GTK_IS_ENTRY (entry));
211 g_return_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search));
213 priv = search->priv;
215 DEBUG_PRINT("Entry changed");
217 if (!priv->idle_filter)
219 priv->idle_filter =
220 g_idle_add ((GSourceFunc)
221 sdb_view_search_filter_idle, search);
225 static void
226 sdb_view_search_on_entry_activated (GtkEntry * entry,
227 SymbolDBViewSearch * search)
229 SymbolDBViewSearchPriv *priv;
230 gchar *str;
232 g_return_if_fail (GTK_IS_ENTRY (entry));
233 g_return_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search));
235 priv = search->priv;
237 str = (gchar *) gtk_entry_get_text (GTK_ENTRY (priv->entry));
239 /* parse the string typed in the entry */
240 sdb_view_search_model_filter (search, str);
245 static gboolean
246 sdb_view_search_on_tree_row_activate (GtkTreeView * view,
247 GtkTreePath * arg1,
248 GtkTreeViewColumn * arg2,
249 SymbolDBViewSearch * search)
252 GtkTreeIter iter;
253 SymbolDBViewSearchPriv *priv;
254 gint line;
255 gchar *file;
256 GtkTreeSelection *selection;
258 priv = search->priv;
260 selection = gtk_tree_view_get_selection (view);
262 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
264 DEBUG_PRINT
265 ("sdb_view_search_on_tree_row_activate: error getting selected row");
266 return FALSE;
269 gtk_tree_model_get (GTK_TREE_MODEL (priv->model),
270 &iter,
271 COLUMN_LINE, &line,
272 COLUMN_FILE, &file,
273 -1);
275 DEBUG_PRINT ("sdb_view_search_on_tree_row_activate: file %s", file);
277 g_signal_emit (search, signals[SYM_SELECTED], 0, line, file);
279 g_free (file);
281 /* Always return FALSE so the tree view gets the event and can update
282 * the selection etc.
284 return FALSE;
289 static gboolean
290 sdb_view_search_on_entry_key_press_event (GtkEntry * entry,
291 GdkEventKey * event,
292 SymbolDBViewSearch * search)
294 SymbolDBViewSearchPriv *priv;
296 priv = search->priv;
298 DEBUG_PRINT ("key_press event");
299 if (event->keyval == GDK_Tab)
301 DEBUG_PRINT ("tab key pressed");
302 if (event->state & GDK_CONTROL_MASK)
304 gtk_widget_grab_focus (priv->hitlist);
306 else
308 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
309 gtk_editable_select_region (GTK_EDITABLE (entry), -1,
310 -1);
312 return TRUE;
315 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
317 GtkTreeIter iter;
318 gchar *name;
319 gint line;
320 gchar *file;
322 DEBUG_PRINT("enter key pressed: getting the first entry found");
324 /* Get the first entry found. */
325 if (gtk_tree_model_get_iter_first
326 (GTK_TREE_MODEL (priv->model), &iter))
329 gtk_tree_model_get (GTK_TREE_MODEL (priv->model),
330 &iter,
331 COLUMN_NAME, &name,
332 COLUMN_LINE, &line,
333 COLUMN_FILE, &file,
334 -1);
336 g_return_val_if_fail (&iter != NULL, FALSE);
338 gtk_entry_set_text (GTK_ENTRY (entry), name);
340 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
341 gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
343 g_signal_emit (search, signals[SYM_SELECTED], 0, line, file);
345 g_free (name);
346 g_free (file);
348 return TRUE;
351 return FALSE;
354 static gboolean
355 sdb_view_search_complete_idle (SymbolDBViewSearch * search)
357 SymbolDBViewSearchPriv *priv;
358 const gchar *text;
359 gchar *completed = NULL;
360 GList *list;
361 gint text_length;
363 g_return_val_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search), FALSE);
365 priv = search->priv;
367 text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
369 list = g_completion_complete (priv->completion, (gchar *) text,
370 &completed);
372 if (completed)
374 text_length = strlen (text);
375 gtk_entry_set_text (GTK_ENTRY (priv->entry), completed);
377 gtk_editable_set_position (GTK_EDITABLE (priv->entry),
378 text_length);
380 gtk_editable_select_region (GTK_EDITABLE (priv->entry),
381 text_length, -1);
382 g_free (completed);
384 priv->idle_complete = 0;
385 return FALSE;
388 static void
389 sdb_view_search_on_entry_text_inserted (GtkEntry * entry,
390 const gchar * text,
391 gint length,
392 gint * position,
393 SymbolDBViewSearch * search)
395 SymbolDBViewSearchPriv *priv;
396 g_return_if_fail (SYMBOL_IS_DB_VIEW_SEARCH (search));
398 priv = search->priv;
400 if (!priv->idle_complete)
402 priv->idle_complete =
403 g_idle_add ((GSourceFunc)
404 sdb_view_search_complete_idle, search);
408 static void
409 sdb_view_search_init (SymbolDBViewSearch * search)
412 SymbolDBViewSearchPriv *priv;
413 GtkTreeViewColumn *column;
414 GtkCellRenderer *renderer;
415 GtkWidget *frame, *list_sw;
418 /* allocate space for a SymbolDBViewSearchPriv class. */
419 priv = g_new0 (SymbolDBViewSearchPriv, 1);
420 search->priv = priv;
422 priv->idle_complete = 0;
423 priv->idle_filter = 0;
425 priv->completion = g_completion_new (NULL);
427 priv->hitlist = gtk_tree_view_new ();
430 priv->model = GTK_TREE_MODEL (gtk_tree_store_new (COLUMN_MAX, GDK_TYPE_PIXBUF,
431 G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT));
433 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->hitlist), GTK_TREE_MODEL (priv->model));
434 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->hitlist), FALSE);
436 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->hitlist),
437 GTK_TREE_MODEL (priv->model));
438 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->hitlist), TRUE);
440 /* column initialization */
441 column = gtk_tree_view_column_new ();
442 gtk_tree_view_column_set_sizing (column,
443 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
445 renderer = gtk_cell_renderer_pixbuf_new ();
446 gtk_tree_view_column_pack_start (column, renderer, FALSE);
447 gtk_tree_view_column_add_attribute (column, renderer, "pixbuf",
448 COLUMN_PIXBUF);
450 renderer = gtk_cell_renderer_text_new ();
451 gtk_tree_view_column_pack_start (column, renderer, TRUE);
452 gtk_tree_view_column_add_attribute (column, renderer, "text",
453 COLUMN_NAME);
455 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->hitlist), column);
456 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (priv->hitlist),
457 column);
459 gtk_box_set_spacing (GTK_BOX (search), 2);
461 gtk_container_set_border_width (GTK_CONTAINER (search), 2);
463 /* creating entry box, where we'll type the keyword to look for */
464 priv->entry = gtk_entry_new ();
466 /* set up some signals */
467 g_signal_connect (priv->entry, "key_press_event",
468 G_CALLBACK (sdb_view_search_on_entry_key_press_event),
469 search);
471 g_signal_connect (priv->hitlist, "row_activated",
472 G_CALLBACK (sdb_view_search_on_tree_row_activate),
473 search);
475 g_signal_connect (priv->entry, "changed",
476 G_CALLBACK (sdb_view_search_on_entry_changed),
477 search);
479 g_signal_connect (priv->entry, "activate",
480 G_CALLBACK (sdb_view_search_on_entry_activated),
481 search);
483 g_signal_connect (priv->entry, "insert_text",
484 G_CALLBACK (sdb_view_search_on_entry_text_inserted), search);
486 gtk_box_pack_start (GTK_BOX (search), priv->entry, FALSE, FALSE, 0);
488 frame = gtk_frame_new (NULL);
489 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
491 list_sw = gtk_scrolled_window_new (NULL, NULL);
492 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (list_sw),
493 GTK_POLICY_AUTOMATIC,
494 GTK_POLICY_AUTOMATIC);
496 gtk_container_add (GTK_CONTAINER (frame), list_sw);
497 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->hitlist), FALSE);
499 gtk_container_add (GTK_CONTAINER (list_sw), priv->hitlist);
500 gtk_box_pack_end_defaults (GTK_BOX (search), frame);
502 gtk_widget_show_all (GTK_WIDGET (search));
505 static void
506 sdb_view_search_dispose (GObject * obj)
508 SymbolDBViewSearch *search = SYMBOL_DB_VIEW_SEARCH (obj);
509 SymbolDBViewSearchPriv *priv = search->priv;
511 DEBUG_PRINT("Destroying symbolsearch");
513 /* anjuta_symbol_view's dispose should manage it's freeing */
514 if (priv->model)
516 symbol_db_view_search_clear(search);
517 g_object_unref (priv->model);
518 priv->model = NULL;
521 if (priv->entry)
522 priv->entry = NULL;
524 if (priv->hitlist)
525 priv->hitlist = NULL;
527 G_OBJECT_CLASS (parent_class)->dispose (obj);
531 static void
532 sdb_view_search_finalize (GObject * obj)
534 SymbolDBViewSearch *search = SYMBOL_DB_VIEW_SEARCH (obj);
535 SymbolDBViewSearchPriv *priv = search->priv;
537 DEBUG_PRINT ("Finalizing symbolsearch widget");
539 g_list_foreach (priv->completion->items, (GFunc)g_free, NULL);
540 g_completion_free (priv->completion);
541 g_free (priv);
543 G_OBJECT_CLASS (parent_class)->finalize (obj);
547 static void
548 sdb_view_search_class_init (SymbolDBViewSearchClass * klass)
550 GObjectClass *object_class;
552 object_class = G_OBJECT_CLASS (klass);
553 parent_class = g_type_class_peek_parent (klass);
555 object_class->finalize = sdb_view_search_finalize;
556 object_class->dispose = sdb_view_search_dispose;
558 signals[SYM_SELECTED] =
559 g_signal_new ("symbol-selected",
560 G_TYPE_FROM_CLASS (klass),
561 G_SIGNAL_RUN_LAST,
562 G_STRUCT_OFFSET (SymbolDBViewSearchClass,
563 symbol_selected), NULL, NULL,
564 g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE,
565 2, G_TYPE_INT, G_TYPE_STRING);
569 * Cleaning issues. This function must be called when a project is removed.
571 void
572 symbol_db_view_search_clear (SymbolDBViewSearch *search)
574 SymbolDBViewSearchPriv *priv;
575 priv = search->priv;
577 /* set entry text to a NULL string */
578 gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
580 /* thrown away the g_completion words */
581 g_list_foreach (priv->completion->items, (GFunc)g_free, NULL);
582 g_completion_clear_items (priv->completion);
584 /* clean the gtk_tree_store */
585 gtk_tree_store_clear (GTK_TREE_STORE(gtk_tree_view_get_model
586 (GTK_TREE_VIEW (priv->hitlist))));
589 GType
590 sdb_view_search_get_type (void)
592 static GType type = 0;
594 if (!type)
596 static const GTypeInfo info = {
597 sizeof (SymbolDBViewSearchClass),
598 NULL,
599 NULL,
600 (GClassInitFunc) sdb_view_search_class_init,
601 NULL,
602 NULL,
603 sizeof (SymbolDBViewSearch),
605 (GInstanceInitFunc) sdb_view_search_init,
608 type = g_type_register_static (GTK_TYPE_VBOX,
609 "SymbolDBViewSearch", &info, 0);
611 return type;
614 GtkWidget *
615 symbol_db_view_search_new (SymbolDBEngine *dbe)
617 SymbolDBViewSearch *search;
618 SymbolDBViewSearchPriv *priv;
620 /* create a new object */
621 search = g_object_new (SYMBOL_TYPE_DB_VIEW_SEARCH, NULL);
623 /* store the engine pointer */
624 priv = search->priv;
625 priv->sdbe = dbe;
627 return GTK_WIDGET (search);