* plugins/symbol-browser/plugin.c,
[anjuta-git-plugin.git] / plugins / symbol-browser / plugin.c
blobe9c7c67fe431a7d3d98d9bc4c59006aa70e39ee7
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 plugin.c
4 Copyright (C) Naba Kumar <naba@gnome.org>
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 #include <config.h>
22 #include <libgnomevfs/gnome-vfs-utils.h>
23 #include <libanjuta/anjuta-shell.h>
24 #include <libanjuta/anjuta-debug.h>
25 #include <libanjuta/interfaces/ianjuta-help.h>
26 #include <libanjuta/interfaces/ianjuta-document-manager.h>
27 #include <libanjuta/interfaces/ianjuta-project-manager.h>
28 #include <libanjuta/interfaces/ianjuta-file-manager.h>
29 #include <libanjuta/interfaces/ianjuta-file.h>
30 #include <libanjuta/interfaces/ianjuta-file-loader.h>
31 #include <libanjuta/interfaces/ianjuta-editor.h>
32 #include <libanjuta/interfaces/ianjuta-markable.h>
33 #include <libanjuta/interfaces/ianjuta-symbol-manager.h>
34 #include <libanjuta/interfaces/ianjuta-preferences.h>
35 #include <libegg/menu/egg-combo-action.h>
37 #include <tm_tagmanager.h>
38 #include "an_symbol_view.h"
39 #include "an_symbol_search.h"
40 #include "anjuta-symbol-locals.h"
41 #include "an_symbol_info.h"
42 #include "an_symbol_prefs.h"
43 #include "an_symbol_iter.h"
44 #include "plugin.h"
46 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-symbol-browser-plugin.ui"
47 #define PREFS_GLADE PACKAGE_DATA_DIR"/glade/anjuta-symbol-browser-plugin.glade"
48 #define ICON_FILE "anjuta-symbol-browser-plugin-48.png"
50 #define TIMEOUT_INTERVAL_SYMBOLS_UPDATE 10000
52 static gpointer parent_class = NULL;
53 static gboolean need_symbols_update;
54 static gint timeout_id;
55 static gchar prev_char_added = ' ';
57 /* these will block signals on treeview and treesearch callbacks functions */
58 static void trees_signals_block (SymbolBrowserPlugin *sv_plugin);
59 static void trees_signals_unblock (SymbolBrowserPlugin *sv_plugin);
61 static void on_treesearch_symbol_selected_event (AnjutaSymbolSearch *search,
62 AnjutaSymbolInfo *sym,
63 SymbolBrowserPlugin *sv_plugin);
64 static void update_editor_symbol_model (SymbolBrowserPlugin *sv_plugin);
66 static void on_editor_update_ui (IAnjutaEditor *editor,
67 SymbolBrowserPlugin *sv_plugin);
68 static void on_char_added (IAnjutaEditor *editor, IAnjutaIterable *position, gchar ch,
69 SymbolBrowserPlugin *sv_plugin);
71 static void
72 register_stock_icons (AnjutaPlugin *plugin)
74 static gboolean registered = FALSE;
76 if (registered)
77 return;
78 registered = TRUE;
80 /* Register stock icons */
81 BEGIN_REGISTER_ICON (plugin);
82 REGISTER_ICON (ICON_FILE, "symbol-browser-plugin-icon");
83 END_REGISTER_ICON;
86 static void
87 goto_file_line (AnjutaPlugin *plugin, const gchar *filename, gint lineno)
89 gchar *uri;
90 IAnjutaDocumentManager *docman;
92 g_return_if_fail (filename != NULL);
94 /* Go to file and line number */
95 docman = anjuta_shell_get_interface (plugin->shell, IAnjutaDocumentManager,
96 NULL);
98 uri = gnome_vfs_get_uri_from_local_path (filename);
99 ianjuta_document_manager_goto_file_line (docman, uri, lineno, NULL);
100 g_free (uri);
103 static void
104 goto_file_tag (SymbolBrowserPlugin *sv_plugin, const char *symbol,
105 gboolean prefer_definition)
107 const gchar *file;
108 gint line;
109 gboolean ret;
111 ret = anjuta_symbol_view_get_file_symbol (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
112 symbol, prefer_definition,
113 &file, &line);
114 if (ret)
116 goto_file_line (ANJUTA_PLUGIN (sv_plugin), file, line);
120 static void
121 on_goto_def_activate (GtkAction *action, SymbolBrowserPlugin *sv_plugin)
123 gchar *file;
124 gint line;
125 gboolean ret;
127 ret = anjuta_symbol_view_get_current_symbol_def (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
128 &file, &line);
129 if (ret)
131 goto_file_line (ANJUTA_PLUGIN (sv_plugin), file, line);
132 g_free (file);
136 static void
137 on_goto_decl_activate (GtkAction *action, SymbolBrowserPlugin *sv_plugin)
139 gchar *file;
140 gint line;
141 gboolean ret;
143 ret = anjuta_symbol_view_get_current_symbol_decl (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
144 &file, &line);
145 if (ret)
147 goto_file_line (ANJUTA_PLUGIN (sv_plugin), file, line);
148 g_free (file);
152 static void
153 on_goto_file_tag_decl_activate (GtkAction * action,
154 SymbolBrowserPlugin *sv_plugin)
156 IAnjutaEditor *ed;
157 gchar *word;
159 if (sv_plugin->current_editor)
161 ed = IANJUTA_EDITOR (sv_plugin->current_editor);
162 word = ianjuta_editor_get_current_word (ed, NULL);
163 if (word)
165 goto_file_tag (sv_plugin, word, FALSE);
166 g_free (word);
171 static void
172 on_goto_file_tag_def_activate (GtkAction * action,
173 SymbolBrowserPlugin *sv_plugin)
175 IAnjutaEditor *ed;
176 gchar *word;
178 if (sv_plugin->current_editor)
180 ed = IANJUTA_EDITOR (sv_plugin->current_editor);
181 word = ianjuta_editor_get_current_word (ed, NULL);
182 if (word)
184 goto_file_tag (sv_plugin, word, TRUE);
185 g_free (word);
190 static void
191 on_find_activate (GtkAction *action, SymbolBrowserPlugin *sv_plugin)
193 gchar *symbol;
194 symbol = anjuta_symbol_view_get_current_symbol (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree));
195 if (symbol)
197 g_warning ("TODO: Unimplemented");
198 g_free (symbol);
202 static gboolean
203 on_refresh_idle (gpointer user_data)
205 IAnjutaProjectManager *pm;
206 GList *source_uris;
207 GList *source_files;
208 AnjutaStatus *status;
209 SymbolBrowserPlugin *sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (user_data);
211 /* FIXME: There should be a way to ensure that this project manager
212 * is indeed the one that has opened the project_uri
214 pm = anjuta_shell_get_interface (ANJUTA_PLUGIN (sv_plugin)->shell,
215 IAnjutaProjectManager, NULL);
216 g_return_val_if_fail (pm != NULL, FALSE);
218 status = anjuta_shell_get_status (ANJUTA_PLUGIN (sv_plugin)->shell, NULL);
219 anjuta_status_push (status, "Refreshing symbol tree...");
220 anjuta_status_busy_push (status);
222 source_uris = source_files = NULL;
223 source_uris = ianjuta_project_manager_get_elements (pm,
224 IANJUTA_PROJECT_MANAGER_SOURCE,
225 NULL);
226 if (source_uris)
228 const gchar *uri;
229 GList *node;
230 node = source_uris;
232 while (node)
234 gchar *file_path;
236 uri = (const gchar *)node->data;
237 file_path = gnome_vfs_get_local_path_from_uri (uri);
238 if (file_path)
239 source_files = g_list_prepend (source_files, file_path);
240 node = g_list_next (node);
242 source_files = g_list_reverse (source_files);
244 anjuta_symbol_view_update (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
245 source_files);
246 g_list_foreach (source_files, (GFunc)g_free, NULL);
247 g_list_foreach (source_uris, (GFunc)g_free, NULL);
248 g_list_free (source_files);
249 g_list_free (source_uris);
251 /* Current editor symbol model may have changed */
252 update_editor_symbol_model (sv_plugin);
254 anjuta_status_busy_pop (status);
255 anjuta_status_pop (status);
256 return FALSE;
259 static void
260 on_refresh_activate (GtkAction *action, SymbolBrowserPlugin *sv_plugin)
262 if (!sv_plugin->project_root_uri)
263 return;
264 g_idle_add (on_refresh_idle, sv_plugin);
267 static GtkActionEntry actions[] =
269 { "ActionMenuGoto", NULL, N_("_Goto"), NULL, NULL, NULL},
271 "ActionSymbolBrowserGotoDef",
272 NULL,
273 N_("Tag _Definition"),
274 "<control>d",
275 N_("Goto symbol definition"),
276 G_CALLBACK (on_goto_file_tag_def_activate)
279 "ActionSymbolBrowserGotoDecl",
280 NULL,
281 N_("Tag De_claration"),
282 "<shift><control>d",
283 N_("Goto symbol declaration"),
284 G_CALLBACK (on_goto_file_tag_decl_activate)
288 static GtkActionEntry popup_actions[] =
291 "ActionPopupSymbolBrowserGotoDef",
292 NULL,
293 N_("Goto _Definition"),
294 NULL,
295 N_("Goto symbol definition"),
296 G_CALLBACK (on_goto_def_activate)
299 "ActionPopupSymbolBrowserGotoDecl",
300 NULL,
301 N_("Goto De_claration"),
302 NULL,
303 N_("Goto symbol declaration"),
304 G_CALLBACK (on_goto_decl_activate)
307 "ActionPopupSymbolBrowserFind",
308 GTK_STOCK_FIND,
309 N_("_Find Usage"),
310 NULL,
311 N_("Find usage of symbol in project"),
312 G_CALLBACK (on_find_activate)
315 "ActionPopupSymbolBrowserRefresh",
316 GTK_STOCK_REFRESH,
317 N_("_Refresh"),
318 NULL,
319 N_("Refresh symbol browser tree"),
320 G_CALLBACK (on_refresh_activate)
324 static void
325 on_project_element_added (IAnjutaProjectManager *pm, const gchar *uri,
326 SymbolBrowserPlugin *sv_plugin)
328 gchar *filename;
330 if (!sv_plugin->project_root_uri)
331 return;
333 filename = gnome_vfs_get_local_path_from_uri (uri);
334 if (filename)
336 anjuta_symbol_view_add_source (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
337 filename);
338 g_free (filename);
342 static void
343 on_project_element_removed (IAnjutaProjectManager *pm, const gchar *uri,
344 SymbolBrowserPlugin *sv_plugin)
346 gchar *filename;
348 if (!sv_plugin->project_root_uri)
349 return;
351 filename = gnome_vfs_get_local_path_from_uri (uri);
352 if (filename)
354 anjuta_symbol_view_remove_source (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
355 filename);
356 g_free (filename);
360 // add a new project
361 static void
362 project_root_added (AnjutaPlugin *plugin, const gchar *name,
363 const GValue *value, gpointer user_data)
365 AnjutaStatus *status;
366 IAnjutaProjectManager *pm;
367 SymbolBrowserPlugin *sv_plugin;
368 const gchar *root_uri;
370 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
372 g_free (sv_plugin->project_root_uri);
373 sv_plugin->project_root_uri = NULL;
374 root_uri = g_value_get_string (value);
375 if (root_uri)
377 gchar *root_dir = gnome_vfs_get_local_path_from_uri (root_uri);
378 if (root_dir)
380 status = anjuta_shell_get_status (plugin->shell, NULL);
381 anjuta_status_progress_add_ticks (status, 1);
382 trees_signals_block (sv_plugin);
383 anjuta_symbol_view_open (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
384 root_dir);
386 /* Current editor symbol model may have changed */
387 update_editor_symbol_model (sv_plugin);
388 anjuta_status_progress_tick (status, NULL, _("Created symbols..."));
389 trees_signals_unblock (sv_plugin);
390 g_free (root_dir);
392 sv_plugin->project_root_uri = g_strdup (root_uri);
394 /* FIXME: There should be a way to ensure that this project manager
395 * is indeed the one that has opened the project_uri
397 pm = anjuta_shell_get_interface (ANJUTA_PLUGIN (sv_plugin)->shell,
398 IAnjutaProjectManager, NULL);
399 g_signal_connect (G_OBJECT (pm), "element_added",
400 G_CALLBACK (on_project_element_added), sv_plugin);
401 g_signal_connect (G_OBJECT (pm), "element_removed",
402 G_CALLBACK (on_project_element_removed), sv_plugin);
405 static void
406 project_root_removed (AnjutaPlugin *plugin, const gchar *name,
407 gpointer user_data)
409 IAnjutaProjectManager *pm;
410 SymbolBrowserPlugin *sv_plugin;
412 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
414 /* Disconnect events from project manager */
416 /* FIXME: There should be a way to ensure that this project manager
417 * is indeed the one that has opened the project_uri
419 pm = anjuta_shell_get_interface (ANJUTA_PLUGIN (sv_plugin)->shell,
420 IAnjutaProjectManager, NULL);
421 g_signal_handlers_disconnect_by_func (G_OBJECT (pm),
422 on_project_element_added,
423 sv_plugin);
424 g_signal_handlers_disconnect_by_func (G_OBJECT (pm),
425 on_project_element_removed,
426 sv_plugin);
428 /* clear anjuta_symbol_search side */
429 anjuta_symbol_search_clear(ANJUTA_SYMBOL_SEARCH(sv_plugin->ss));
431 /* clear glist's sfiles */
432 anjuta_symbol_view_clear (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree));
434 g_free (sv_plugin->project_root_uri);
435 sv_plugin->project_root_uri = NULL;
438 static gboolean
439 on_treeview_event (GtkWidget *widget,
440 GdkEvent *event,
441 SymbolBrowserPlugin *sv_plugin)
443 GtkTreeView *view;
444 GtkTreeModel *model;
445 GtkTreeSelection *selection;
447 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
449 view = GTK_TREE_VIEW (widget);
450 model = gtk_tree_view_get_model (view);
451 selection = gtk_tree_view_get_selection (view);
453 if (!event)
454 return FALSE;
456 if (event->type == GDK_BUTTON_PRESS) {
457 GdkEventButton *e = (GdkEventButton *) event;
459 if (e->button == 3) {
460 GtkWidget *menu;
462 /* Popup project menu */
463 menu = gtk_ui_manager_get_widget (GTK_UI_MANAGER (sv_plugin->ui),
464 "/PopupSymbolBrowser");
465 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
466 NULL, NULL, e->button, e->time);
467 return TRUE;
469 } else if (event->type == GDK_KEY_PRESS) {
470 GdkEventKey *e = (GdkEventKey *) event;
472 switch (e->keyval) {
473 case GDK_Return:
474 anjuta_ui_activate_action_by_group (sv_plugin->ui,
475 sv_plugin->popup_action_group,
476 "ActionPopupSymbolBrowserGotoDef");
477 return TRUE;
478 default:
479 return FALSE;
482 return FALSE;
485 static void
486 on_treeview_row_activated (GtkTreeView *view, GtkTreePath *arg1,
487 GtkTreeViewColumn *arg2,
488 SymbolBrowserPlugin *sv_plugin)
490 GtkTreeModel *model;
491 GtkTreeSelection *selection;
492 GtkTreeIter iter;
494 selection = gtk_tree_view_get_selection (view);
495 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
496 return;
497 anjuta_ui_activate_action_by_group (sv_plugin->ui,
498 sv_plugin->popup_action_group,
499 "ActionPopupSymbolBrowserGotoDef");
502 static void
503 trees_signals_block (SymbolBrowserPlugin *sv_plugin)
505 g_signal_handlers_block_by_func (G_OBJECT (sv_plugin->sv_tree),
506 G_CALLBACK (on_treeview_event), NULL);
508 g_signal_handlers_block_by_func (G_OBJECT (sv_plugin->ss),
509 G_CALLBACK (on_treesearch_symbol_selected_event),
510 NULL);
514 static void
515 trees_signals_unblock (SymbolBrowserPlugin *sv_plugin)
517 g_signal_handlers_unblock_by_func (G_OBJECT (sv_plugin->sv_tree),
518 G_CALLBACK (on_treeview_event), NULL);
520 g_signal_handlers_unblock_by_func (G_OBJECT (sv_plugin->ss),
521 G_CALLBACK (on_treesearch_symbol_selected_event),
522 NULL);
525 static void
526 goto_tree_iter (SymbolBrowserPlugin *sv_plugin, GtkTreeIter *iter)
528 gint line;
530 /* FIXME: */
531 line = anjuta_symbol_view_workspace_get_line (ANJUTA_SYMBOL_VIEW
532 (sv_plugin->sv_tree),
533 iter);
535 if (line > 0 && sv_plugin->current_editor)
537 /* Goto line number */
538 ianjuta_editor_goto_line (IANJUTA_EDITOR (sv_plugin->current_editor),
539 line, NULL);
540 if (IANJUTA_IS_MARKABLE (sv_plugin->current_editor))
542 ianjuta_markable_delete_all_markers (IANJUTA_MARKABLE (sv_plugin->current_editor),
543 IANJUTA_MARKABLE_LINEMARKER,
544 NULL);
546 ianjuta_markable_mark (IANJUTA_MARKABLE (sv_plugin->current_editor),
547 line, IANJUTA_MARKABLE_LINEMARKER, NULL);
552 static void
553 on_symbol_selected (GtkAction *action, SymbolBrowserPlugin *sv_plugin)
555 GtkTreeIter iter;
557 if (egg_combo_action_get_active_iter (EGG_COMBO_ACTION (action), &iter))
559 goto_tree_iter (sv_plugin, &iter);
563 static void
564 on_local_treeview_row_activated (GtkTreeView *view, GtkTreePath *arg1,
565 GtkTreeViewColumn *arg2,
566 SymbolBrowserPlugin *sv_plugin)
568 GtkTreeModel *model;
569 GtkTreeSelection *selection;
570 GtkTreeIter iter;
572 selection = gtk_tree_view_get_selection (view);
573 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
574 return;
575 goto_tree_iter (sv_plugin, &iter);
578 /* -----------------------------------------------------------------------------
579 * will manage the click of mouse and other events on search->hitlist treeview
581 static void
582 on_treesearch_symbol_selected_event (AnjutaSymbolSearch *search,
583 AnjutaSymbolInfo *sym,
584 SymbolBrowserPlugin *sv_plugin) {
585 gboolean ret;
586 gint line;
587 const gchar *file;
589 ret = anjuta_symbol_view_get_file_symbol (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
590 sym->sym_name, TRUE,
591 &file, &line);
592 if (ret)
594 goto_file_line (ANJUTA_PLUGIN (sv_plugin), file, line);
598 static void
599 on_editor_destroy (SymbolBrowserPlugin *sv_plugin, IAnjutaEditor *editor)
601 const gchar *uri;
603 if (!sv_plugin->editor_connected || !sv_plugin->sv_tree)
604 return;
605 uri = g_hash_table_lookup (sv_plugin->editor_connected, G_OBJECT (editor));
606 if (uri && strlen (uri) > 0)
608 DEBUG_PRINT ("Removing file tags of %s", uri);
609 anjuta_symbol_view_workspace_remove_file (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
610 uri);
612 g_hash_table_remove (sv_plugin->editor_connected, G_OBJECT (editor));
615 static void
616 on_editor_saved (IAnjutaEditor *editor, const gchar *saved_uri,
617 SymbolBrowserPlugin *sv_plugin)
619 const gchar *old_uri;
620 gboolean tags_update;
621 GtkTreeModel *file_symbol_model;
622 GtkAction *action;
623 AnjutaUI *ui;
625 /* FIXME: Do this only if automatic tags update is enabled */
626 /* tags_update =
627 anjuta_preferences_get_int (te->preferences, AUTOMATIC_TAGS_UPDATE);
629 tags_update = TRUE;
630 if (tags_update)
632 gchar *local_filename;
634 /* Verify that it's local file */
635 local_filename = gnome_vfs_get_local_path_from_uri (saved_uri);
636 g_return_if_fail (local_filename != NULL);
637 g_free (local_filename);
639 if (!sv_plugin->editor_connected)
640 return;
642 old_uri = g_hash_table_lookup (sv_plugin->editor_connected, editor);
644 if (old_uri && strlen (old_uri) <= 0)
645 old_uri = NULL;
646 anjuta_symbol_view_workspace_update_file (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
647 old_uri, saved_uri);
648 g_hash_table_insert (sv_plugin->editor_connected, editor,
649 g_strdup (saved_uri));
651 /* Update File symbol view */
652 ui = anjuta_shell_get_ui (ANJUTA_PLUGIN (sv_plugin)->shell, NULL);
653 action = anjuta_ui_get_action (ui, "ActionGroupSymbolNavigation",
654 "ActionGotoSymbol");
657 file_symbol_model =
658 anjuta_symbol_view_get_file_symbol_model (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree));
659 g_object_set_data (G_OBJECT (editor), "tm_file",
660 g_object_get_data (G_OBJECT (file_symbol_model),
661 "tm_file"));
662 /* Set toolbar version */
663 egg_combo_action_set_model (EGG_COMBO_ACTION (action), file_symbol_model);
664 /* Set local view version */
665 gtk_tree_view_set_model (GTK_TREE_VIEW (sv_plugin->sl_tree),
666 file_symbol_model);
667 sv_plugin->locals_line_number = 0;
668 on_editor_update_ui (editor, sv_plugin);
670 if (gtk_tree_model_iter_n_children (file_symbol_model, NULL) > 0)
671 g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
672 else
673 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
675 #if 0
676 /* FIXME: Re-hilite all editors on tags update */
677 if(update)
679 for (tmp = app->text_editor_list; tmp; tmp = g_list_next(tmp))
681 te1 = TEXT_EDITOR (tmp->data);
682 text_editor_set_hilite_type(te1);
685 #endif
689 static void
690 on_editor_foreach_disconnect (gpointer key, gpointer value, gpointer user_data)
692 g_signal_handlers_disconnect_by_func (G_OBJECT(key),
693 G_CALLBACK (on_editor_saved),
694 user_data);
695 g_signal_handlers_disconnect_by_func (G_OBJECT(key),
696 G_CALLBACK (on_editor_update_ui),
697 user_data);
698 g_signal_handlers_disconnect_by_func (G_OBJECT(key),
699 G_CALLBACK (on_char_added),
700 user_data);
701 g_object_weak_unref (G_OBJECT(key),
702 (GWeakNotify) (on_editor_destroy),
703 user_data);
706 static void
707 on_editor_foreach_clear (gpointer key, gpointer value, gpointer user_data)
709 const gchar *uri;
710 SymbolBrowserPlugin *sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (user_data);
712 uri = (const gchar *)value;
713 if (uri && strlen (uri) > 0)
715 DEBUG_PRINT ("Removing file tags of %s", uri);
716 anjuta_symbol_view_workspace_remove_file (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
717 uri);
721 static void
722 update_editor_symbol_model (SymbolBrowserPlugin *sv_plugin)
724 AnjutaUI *ui;
725 gchar *uri;
726 GObject *editor = sv_plugin->current_editor;
728 if (!editor)
729 return;
731 ui = anjuta_shell_get_ui (ANJUTA_PLUGIN (sv_plugin)->shell, NULL);
732 uri = ianjuta_file_get_uri (IANJUTA_FILE (editor), NULL);
733 if (uri)
735 gchar *local_filename;
736 GtkTreeModel *file_symbol_model;
737 GtkAction *action;
739 /* Verify that it's local file */
740 local_filename = gnome_vfs_get_local_path_from_uri (uri);
741 g_return_if_fail (local_filename != NULL);
742 g_free (local_filename);
744 anjuta_symbol_view_workspace_add_file (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree), uri);
745 action = anjuta_ui_get_action (ui, "ActionGroupSymbolNavigation",
746 "ActionGotoSymbol");
747 g_free (uri);
749 file_symbol_model =
750 anjuta_symbol_view_get_file_symbol_model (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree));
751 if (file_symbol_model)
753 g_object_set_data (G_OBJECT (editor), "tm_file",
754 g_object_get_data (G_OBJECT (file_symbol_model),
755 "tm_file"));
756 /* Set toolbar version */
757 egg_combo_action_set_model (EGG_COMBO_ACTION (action), file_symbol_model);
759 /* Set local view version */
760 gtk_tree_view_set_model (GTK_TREE_VIEW (sv_plugin->sl_tree),
761 file_symbol_model);
762 sv_plugin->locals_line_number = 0;
763 on_editor_update_ui (IANJUTA_EDITOR (editor), sv_plugin);
765 if (gtk_tree_model_iter_n_children (file_symbol_model, NULL) > 0)
766 g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
767 else
768 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
773 static gboolean
774 on_editor_buffer_symbols_update_timeout (gpointer user_data)
776 SymbolBrowserPlugin *sv_plugin;
777 IAnjutaEditor *ed;
778 gchar *current_buffer = NULL;
779 gint buffer_size = 0;
780 gchar *uri = NULL;
782 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (user_data);
784 if (sv_plugin->current_editor == NULL)
785 return FALSE;
787 /* we won't proceed with the updating of the symbols if we didn't type in
788 anything */
789 if (!need_symbols_update)
790 return TRUE;
792 if (sv_plugin->current_editor) {
793 ed = IANJUTA_EDITOR (sv_plugin->current_editor);
795 buffer_size = ianjuta_editor_get_length (ed, NULL);
796 current_buffer = ianjuta_editor_get_text_all (ed, NULL);
798 uri = ianjuta_file_get_uri (IANJUTA_FILE (ed), NULL);
801 else
802 return FALSE;
804 if (uri) {
805 /* FIXME: Only uncomment after investigating bug 395362 */
807 anjuta_symbol_view_update_source_from_buffer (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree),
808 uri, current_buffer, buffer_size);
810 g_free (uri);
813 if (current_buffer)
814 g_free (current_buffer);
816 need_symbols_update = FALSE;
818 return TRUE;
821 static gboolean
822 iter_matches (SymbolBrowserPlugin *sv_plugin, GtkTreeIter* iter,
823 GtkTreeModel* model, gint lineno)
825 gint line;
826 gtk_tree_model_get (model, iter, COL_LINE, &line, -1);
827 if (line == lineno)
829 GtkTreePath* path = gtk_tree_model_get_path (model, iter);
830 GtkAction* action = anjuta_ui_get_action (sv_plugin->ui,
831 "ActionGroupSymbolNavigation",
832 "ActionGotoSymbol");
834 egg_combo_action_set_active_iter (EGG_COMBO_ACTION (action), iter);
835 gtk_tree_view_set_cursor (GTK_TREE_VIEW (sv_plugin->sl_tree), path, NULL,
836 FALSE);
837 gtk_tree_path_free (path);
838 return TRUE;
840 return FALSE;
843 static void
844 on_editor_update_ui (IAnjutaEditor *editor, SymbolBrowserPlugin *sv_plugin)
846 gint lineno = ianjuta_editor_get_lineno (editor, NULL);
848 GtkTreeModel* model = anjuta_symbol_view_get_file_symbol_model
849 (ANJUTA_SYMBOL_VIEW(sv_plugin->sv_tree));
850 GtkTreeIter iter;
851 gboolean found = FALSE;
853 if (sv_plugin->locals_line_number == lineno)
854 return;
855 sv_plugin->locals_line_number = lineno;
857 if (!gtk_tree_model_get_iter_first (model, &iter))
858 return;
859 while (!found && lineno >= 0)
861 gtk_tree_model_get_iter_first (model, &iter);
864 found = iter_matches (sv_plugin, &iter, model, lineno);
865 if (found)
866 break;
868 while (gtk_tree_model_iter_next (model, &iter));
869 lineno--;
873 static void
874 on_char_added (IAnjutaEditor *editor, IAnjutaIterable *position, gchar ch,
875 SymbolBrowserPlugin *sv_plugin)
877 DEBUG_PRINT ("char added: %c [int %d]", ch, ch);
879 /* try to force the update if a "." or a "->" is pressed */
880 if ((ch == '.') || (prev_char_added == '-' && ch == '>'))
881 on_editor_buffer_symbols_update_timeout (sv_plugin);
883 need_symbols_update = TRUE;
885 prev_char_added = ch;
888 static void
889 value_added_current_editor (AnjutaPlugin *plugin, const char *name,
890 const GValue *value, gpointer data)
892 gchar *uri;
893 GObject *editor;
894 SymbolBrowserPlugin *sv_plugin;
896 editor = g_value_get_object (value);
898 if (!IANJUTA_IS_EDITOR(editor))
899 return;
901 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
903 if (!sv_plugin->editor_connected)
905 sv_plugin->editor_connected = g_hash_table_new_full (g_direct_hash,
906 g_direct_equal,
907 NULL, g_free);
909 sv_plugin->current_editor = editor;
911 update_editor_symbol_model (sv_plugin);
913 uri = ianjuta_file_get_uri (IANJUTA_FILE (editor), NULL);
914 if (g_hash_table_lookup (sv_plugin->editor_connected, editor) == NULL)
916 g_object_weak_ref (G_OBJECT (editor),
917 (GWeakNotify) (on_editor_destroy),
918 sv_plugin);
919 if (uri)
921 g_hash_table_insert (sv_plugin->editor_connected, editor, uri); //g_strdup (uri));
922 //g_free (uri);
924 else
926 g_hash_table_insert (sv_plugin->editor_connected, editor,
927 g_strdup (""));
929 g_signal_connect (G_OBJECT (editor), "saved",
930 G_CALLBACK (on_editor_saved),
931 sv_plugin);
933 g_signal_connect (G_OBJECT (editor), "char-added",
934 G_CALLBACK (on_char_added),
935 sv_plugin);
936 g_signal_connect (G_OBJECT(editor), "update_ui",
937 G_CALLBACK (on_editor_update_ui),
938 sv_plugin);
941 /* add a default timeout to the updating of buffer symbols */
942 timeout_id = g_timeout_add (TIMEOUT_INTERVAL_SYMBOLS_UPDATE,
943 on_editor_buffer_symbols_update_timeout,
944 plugin);
945 need_symbols_update = FALSE;
949 static void
950 value_removed_current_editor (AnjutaPlugin *plugin,
951 const char *name, gpointer data)
953 AnjutaUI *ui;
954 SymbolBrowserPlugin *sv_plugin;
955 GtkAction *action;
957 /* let's remove the timeout for symbols refresh */
958 g_source_remove (timeout_id);
959 need_symbols_update = FALSE;
961 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
962 ui = anjuta_shell_get_ui (plugin->shell, NULL);
963 action = anjuta_ui_get_action (ui, "ActionGroupSymbolNavigation",
964 "ActionGotoSymbol");
965 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
966 sv_plugin->current_editor = NULL;
969 static gboolean
970 activate_plugin (AnjutaPlugin *plugin)
972 GtkActionGroup *group;
973 GtkAction *action;
974 SymbolBrowserPlugin *sv_plugin;
976 DEBUG_PRINT ("SymbolBrowserPlugin: Activating Symbol Manager plugin...");
978 register_stock_icons (plugin);
980 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
981 sv_plugin->ui = anjuta_shell_get_ui (plugin->shell, NULL);
982 sv_plugin->prefs = anjuta_shell_get_preferences (plugin->shell, NULL);
984 /* Create widgets */
985 sv_plugin->sw = gtk_notebook_new();
987 /* Local symbols */
988 sv_plugin->sl = gtk_scrolled_window_new (NULL, NULL);
989 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sv_plugin->sl),
990 GTK_SHADOW_IN);
991 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sv_plugin->sl),
992 GTK_POLICY_AUTOMATIC,
993 GTK_POLICY_AUTOMATIC);
995 sv_plugin->sl_tab_label = gtk_label_new (_("Local" ));
996 sv_plugin->sl_tree = anjuta_symbol_locals_new ();
997 g_object_add_weak_pointer (G_OBJECT (sv_plugin->sl_tree),
998 (gpointer)&sv_plugin->sl_tree);
999 g_signal_connect (G_OBJECT (sv_plugin->sl_tree), "row_activated",
1000 G_CALLBACK (on_local_treeview_row_activated), plugin);
1001 gtk_container_add (GTK_CONTAINER(sv_plugin->sl), sv_plugin->sl_tree);
1003 /* add the scrolled window to the notebook */
1004 gtk_notebook_append_page (GTK_NOTEBOOK(sv_plugin->sw),
1005 sv_plugin->sl, sv_plugin->sl_tab_label );
1007 /* Global symbols */
1008 sv_plugin->sv = gtk_scrolled_window_new (NULL, NULL);
1009 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sv_plugin->sv),
1010 GTK_SHADOW_IN);
1011 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sv_plugin->sv),
1012 GTK_POLICY_AUTOMATIC,
1013 GTK_POLICY_AUTOMATIC);
1015 sv_plugin->sv_tab_label = gtk_label_new (_("Global" ));
1016 sv_plugin->sv_tree = anjuta_symbol_view_new ();
1017 symbol_browser_load_global_tags (plugin);
1019 g_object_add_weak_pointer (G_OBJECT (sv_plugin->sv_tree),
1020 (gpointer)&sv_plugin->sv_tree);
1022 g_signal_connect (G_OBJECT (sv_plugin->sv_tree), "event-after",
1023 G_CALLBACK (on_treeview_event), plugin);
1024 g_signal_connect (G_OBJECT (sv_plugin->sv_tree), "row_activated",
1025 G_CALLBACK (on_treeview_row_activated), plugin);
1027 gtk_container_add (GTK_CONTAINER(sv_plugin->sv), sv_plugin->sv_tree);
1029 /* add the scrolled window to the notebook */
1030 gtk_notebook_append_page (GTK_NOTEBOOK(sv_plugin->sw),
1031 sv_plugin->sv, sv_plugin->sv_tab_label );
1033 /* anjuta symbol search */
1034 sv_plugin->ss =
1035 anjuta_symbol_search_new (ANJUTA_SYMBOL_VIEW (sv_plugin->sv_tree));
1036 sv_plugin->ss_tab_label = gtk_label_new (_("Search" ));
1038 g_object_add_weak_pointer (G_OBJECT (sv_plugin->ss),
1039 (gpointer)&sv_plugin->ss);
1041 gtk_notebook_append_page (GTK_NOTEBOOK(sv_plugin->sw), sv_plugin->ss,
1042 sv_plugin->ss_tab_label );
1044 gtk_widget_show_all (sv_plugin->sw);
1046 /* connect some signals */
1047 g_signal_connect (G_OBJECT (sv_plugin->ss), "symbol_selected",
1048 G_CALLBACK (on_treesearch_symbol_selected_event),
1049 plugin);
1052 /* setting focus to the tree_view*/
1053 gtk_notebook_set_current_page (GTK_NOTEBOOK (sv_plugin->sw), 0);
1055 /* Add action group */
1056 sv_plugin->action_group =
1057 anjuta_ui_add_action_group_entries (sv_plugin->ui,
1058 "ActionGroupSymbolBrowser",
1059 _("Symbol browser actions"),
1060 actions,
1061 G_N_ELEMENTS (actions),
1062 GETTEXT_PACKAGE, TRUE, plugin);
1063 sv_plugin->popup_action_group =
1064 anjuta_ui_add_action_group_entries (sv_plugin->ui,
1065 "ActionGroupPopupSymbolBrowser",
1066 _("Symbol browser popup actions"),
1067 popup_actions,
1068 G_N_ELEMENTS (popup_actions),
1069 GETTEXT_PACKAGE, FALSE, plugin);
1070 group = gtk_action_group_new ("ActionGroupSymbolNavigation");
1072 /* create a new combobox in style of libegg... */
1073 action = g_object_new (EGG_TYPE_COMBO_ACTION,
1074 "name", "ActionGotoSymbol",
1075 "label", _("Goto symbol"),
1076 "tooltip", _("Select the symbol to go"),
1077 "stock_id", GTK_STOCK_JUMP_TO,
1078 NULL);
1080 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
1081 g_signal_connect (action, "activate",
1082 G_CALLBACK (on_symbol_selected), sv_plugin);
1083 gtk_action_group_add_action (group, action);
1084 anjuta_ui_add_action_group (sv_plugin->ui, "ActionGroupSymbolNavigation",
1085 N_("Symbol navigations"), group, FALSE);
1086 sv_plugin->action_group_nav = group;
1088 /* Add UI */
1089 sv_plugin->merge_id =
1090 anjuta_ui_merge (sv_plugin->ui, UI_FILE);
1092 /* Added widgets */
1093 anjuta_shell_add_widget (plugin->shell, sv_plugin->sw,
1094 "AnjutaSymbolBrowser", _("Symbols"),
1095 "symbol-browser-plugin-icon",
1096 ANJUTA_SHELL_PLACEMENT_LEFT, NULL);
1098 /* set up project directory watch */
1099 sv_plugin->root_watch_id = anjuta_plugin_add_watch (plugin,
1100 "project_root_uri",
1101 project_root_added,
1102 project_root_removed, NULL);
1103 sv_plugin->editor_watch_id =
1104 anjuta_plugin_add_watch (plugin, "document_manager_current_editor",
1105 value_added_current_editor,
1106 value_removed_current_editor, NULL);
1107 return TRUE;
1110 static gboolean
1111 deactivate_plugin (AnjutaPlugin *plugin)
1113 SymbolBrowserPlugin *sv_plugin;
1114 sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (plugin);
1116 /* Ensure all editor cached info are released */
1117 if (sv_plugin->editor_connected)
1119 g_hash_table_foreach (sv_plugin->editor_connected,
1120 on_editor_foreach_disconnect, plugin);
1121 g_hash_table_foreach (sv_plugin->editor_connected,
1122 on_editor_foreach_clear, plugin);
1123 g_hash_table_destroy (sv_plugin->editor_connected);
1124 sv_plugin->editor_connected = NULL;
1126 /* Remove watches */
1127 anjuta_plugin_remove_watch (plugin, sv_plugin->root_watch_id, FALSE);
1128 anjuta_plugin_remove_watch (plugin, sv_plugin->editor_watch_id, TRUE);
1130 /* Remove widgets: Widgets will be destroyed when sw is removed */
1131 anjuta_shell_remove_widget (plugin->shell, sv_plugin->sw, NULL);
1133 /* Remove UI */
1134 anjuta_ui_unmerge (sv_plugin->ui, sv_plugin->merge_id);
1136 /* Remove action group */
1137 anjuta_ui_remove_action_group (sv_plugin->ui, sv_plugin->action_group);
1138 anjuta_ui_remove_action_group (sv_plugin->ui, sv_plugin->popup_action_group);
1139 anjuta_ui_remove_action_group (sv_plugin->ui, sv_plugin->action_group_nav);
1141 sv_plugin->root_watch_id = 0;
1142 sv_plugin->editor_watch_id = 0;
1143 sv_plugin->merge_id = 0;
1144 sv_plugin->sw = NULL;
1145 sv_plugin->sl = NULL;
1146 sv_plugin->sl_tree = NULL;
1147 sv_plugin->sv = NULL;
1148 sv_plugin->sv_tree = NULL;
1149 sv_plugin->ss = NULL;
1150 return TRUE;
1153 static void
1154 dispose (GObject *obj)
1156 SymbolBrowserPlugin *sv_plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (obj);
1157 /* Ensure all editors are disconnected */
1158 if (sv_plugin->editor_connected)
1160 g_hash_table_foreach (sv_plugin->editor_connected,
1161 on_editor_foreach_disconnect,
1162 sv_plugin);
1163 g_hash_table_destroy (sv_plugin->editor_connected);
1164 sv_plugin->editor_connected = NULL;
1167 if (plugin->sw)
1169 g_object_unref (G_OBJECT (plugin->sw));
1170 plugin->sw = NULL;
1174 g_object_remove_weak_pointer (G_OBJECT (sv_plugin->ss),
1175 (gpointer)&sv_plugin->ss);
1178 GNOME_CALL_PARENT (G_OBJECT_CLASS, dispose, (obj));
1181 static void
1182 finalize (GObject *obj)
1184 /* SymbolBrowserPlugin *plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (obj); */
1185 GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (obj));
1188 static void
1189 symbol_browser_plugin_instance_init (GObject *obj)
1191 SymbolBrowserPlugin *plugin = ANJUTA_PLUGIN_SYMBOL_BROWSER (obj);
1192 plugin->current_editor = NULL;
1193 plugin->editor_connected = NULL;
1194 plugin->sw = NULL;
1195 plugin->sv = NULL;
1196 plugin->gconf_notify_ids = NULL;
1197 plugin->locals_line_number = 0;
1198 plugin->launcher = NULL;
1201 static void
1202 symbol_browser_plugin_class_init (SymbolBrowserPluginClass *klass)
1204 GObjectClass *object_class;
1205 AnjutaPluginClass *plugin_class;
1207 parent_class = g_type_class_peek_parent (klass);
1208 object_class = G_OBJECT_CLASS (klass);
1209 plugin_class = ANJUTA_PLUGIN_CLASS (klass);
1211 plugin_class->activate = activate_plugin;
1212 plugin_class->deactivate = deactivate_plugin;
1214 object_class->dispose = dispose;
1215 object_class->finalize = finalize;
1218 static IAnjutaIterable*
1219 isymbol_manager_search (IAnjutaSymbolManager *sm,
1220 IAnjutaSymbolType match_types,
1221 const gchar *match_name,
1222 gboolean partial_name_match,
1223 gboolean global_search,
1224 GError **err)
1226 const GPtrArray *tags_array;
1227 AnjutaSymbolIter *iter = NULL;
1228 const gchar *name;
1230 if (match_name && strlen (match_name) > 0)
1231 name = match_name;
1232 else
1233 name = NULL;
1235 tags_array = tm_workspace_find (name, match_types, NULL,
1236 partial_name_match, global_search);
1237 if (tags_array && tags_array->len)
1239 iter = anjuta_symbol_iter_new (tags_array);
1240 return IANJUTA_ITERABLE (iter);
1242 return NULL;
1245 static IAnjutaIterable*
1246 isymbol_manager_get_members (IAnjutaSymbolManager *sm,
1247 const gchar *symbol_name,
1248 gboolean global_search,
1249 GError **err)
1251 const GPtrArray *tags_array;
1252 AnjutaSymbolIter *iter = NULL;
1254 tags_array = tm_workspace_find_scope_members (NULL, symbol_name,
1255 global_search, TRUE);
1258 if (tags_array && tags_array->len)
1260 iter = anjuta_symbol_iter_new (tags_array);
1261 return IANJUTA_ITERABLE (iter);
1263 return NULL;
1266 static IAnjutaIterable*
1267 isymbol_manager_get_parents (IAnjutaSymbolManager *sm,
1268 const gchar *symbol_name,
1269 GError **err)
1271 const GPtrArray *tags_array;
1272 AnjutaSymbolIter *iter = NULL;
1274 tags_array = tm_workspace_get_parents (symbol_name);
1275 if (tags_array && tags_array->len)
1277 iter = anjuta_symbol_iter_new (tags_array);
1278 return IANJUTA_ITERABLE (iter);
1280 return NULL;
1283 static void
1284 isymbol_manager_iface_init (IAnjutaSymbolManagerIface *iface)
1286 iface->search = isymbol_manager_search;
1287 iface->get_members = isymbol_manager_get_members;
1288 iface->get_parents = isymbol_manager_get_parents;
1291 static void
1292 ipreferences_merge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** e)
1294 symbol_browser_prefs_init(ANJUTA_PLUGIN_SYMBOL_BROWSER (ipref));
1297 static void
1298 ipreferences_unmerge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** e)
1300 symbol_browser_prefs_finalize (ANJUTA_PLUGIN_SYMBOL_BROWSER (ipref));
1303 static void
1304 ipreferences_iface_init(IAnjutaPreferencesIface* iface)
1306 iface->merge = ipreferences_merge;
1307 iface->unmerge = ipreferences_unmerge;
1310 ANJUTA_PLUGIN_BEGIN (SymbolBrowserPlugin, symbol_browser_plugin);
1311 ANJUTA_PLUGIN_ADD_INTERFACE (isymbol_manager, IANJUTA_TYPE_SYMBOL_MANAGER);
1312 ANJUTA_PLUGIN_ADD_INTERFACE (ipreferences, IANJUTA_TYPE_PREFERENCES);
1313 ANJUTA_PLUGIN_END;
1315 ANJUTA_SIMPLE_PLUGIN (SymbolBrowserPlugin, symbol_browser_plugin);