1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Massimo Cora' 2008 <maxcvs@email.it>
6 * anjuta_trunk is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * anjuta_trunk is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
28 #include <gtk/gtktreeview.h>
29 #include <gtk/gtkliststore.h>
30 #include <libanjuta/anjuta-debug.h>
31 #include <libanjuta/anjuta-launcher.h>
32 #include <libanjuta/interfaces/ianjuta-language.h>
34 #include "symbol-db-prefs.h"
36 #define GLADE_FILE PACKAGE_DATA_DIR"/glade/anjuta-symbol-db.glade"
37 #define GLADE_ROOT "symbol_prefs"
38 #define ICON_FILE "anjuta-symbol-db-plugin-48.png"
40 #define CTAGS_PREFS_KEY "ctags.executable"
41 #define CHOOSER_WIDGET "preferences_file:text:/usr/bin/ctags:0:symboldb.ctags"
57 static unsigned int signals
[LAST_SIGNAL
] = { 0 };
59 struct _SymbolDBPrefsPriv
{
60 GtkListStore
*prefs_list_store
;
62 AnjutaLauncher
*pkg_config_launcher
;
63 AnjutaPreferences
*prefs
;
66 SymbolDBEngine
*sdbe_project
;
67 SymbolDBEngine
*sdbe_globals
;
70 GHashTable
*enabled_packages_hash
;
76 typedef struct _ParseableData
{
84 destroy_parseable_data (ParseableData
*pdata
)
86 g_free (pdata
->path_str
);
91 G_DEFINE_TYPE (SymbolDBPrefs
, sdb_prefs
, G_TYPE_OBJECT
);
94 on_prefs_executable_changed (GtkFileChooser
*chooser
,
99 SymbolDBPrefsPriv
*priv
;
101 sdbp
= SYMBOL_DB_PREFS (user_data
);
104 new_file
= gtk_file_chooser_get_filename (chooser
);
105 DEBUG_PRINT ("on_prefs_executable_changed (): new executable selected %s",
107 if (new_file
!= NULL
)
110 fchooser
= glade_xml_get_widget (priv
->prefs_gxml
, CHOOSER_WIDGET
);
111 gtk_widget_set_sensitive (fchooser
, TRUE
);
113 anjuta_preferences_set (priv
->prefs
, CTAGS_PREFS_KEY
,
116 /* remember to set the new ctags path into various symbol engines */
117 symbol_db_engine_set_ctags_path (priv
->sdbe_project
, new_file
);
118 symbol_db_engine_set_ctags_path (priv
->sdbe_globals
, new_file
);
125 on_gconf_notify_prefs (GConfClient
*gclient
, guint cnxn_id
,
126 GConfEntry
*entry
, gpointer user_data
)
128 DEBUG_PRINT ("on_gconf_notify_prefs ()");
132 pkg_list_compare (gconstpointer a
, gconstpointer b
)
134 return strcmp ((const gchar
*)a
, (const gchar
*)b
);
138 on_listall_output (AnjutaLauncher
* launcher
,
139 AnjutaLauncherOutputType output_type
,
140 const gchar
* chars
, gpointer user_data
)
143 const gchar
*curr_line
;
146 SymbolDBPrefsPriv
*priv
;
149 if (output_type
== ANJUTA_LAUNCHER_OUTPUT_STDERR
)
151 /* no way. We don't like errors on stderr... */
155 sdbp
= SYMBOL_DB_PREFS (user_data
);
158 store
= priv
->prefs_list_store
;
159 lines
= g_strsplit (chars
, "\n", -1);
161 while ((curr_line
= lines
[i
++]) != NULL
)
165 pkgs
= g_strsplit (curr_line
, " ", -1);
167 /* just take the first token as it's the package-name */
171 if (pkgs
[0] == NULL
) {
175 priv
->pkg_list
= g_list_prepend (priv
->pkg_list
, g_strdup (pkgs
[0]));
183 on_listall_exit (AnjutaLauncher
* launcher
, int child_pid
,
184 int exit_status
, gulong time_taken_in_seconds
,
188 SymbolDBPrefsPriv
*priv
;
193 sdbp
= SYMBOL_DB_PREFS (user_data
);
195 store
= priv
->prefs_list_store
;
197 DEBUG_PRINT ("on_listall_exit ()");
199 g_signal_handlers_disconnect_by_func (launcher
, on_listall_exit
,
202 treeview
= glade_xml_get_widget (priv
->prefs_gxml
, "tags_treeview");
203 gtk_widget_set_sensitive (treeview
, TRUE
);
205 /* we should have pkg_list filled with packages names
206 * It's not enough anyway: we have to sort alphabetically the list.
207 * The implementation done before required the single scan of every package,
208 * for instance 'pkg-config --cflags pkg_name', but this was really
209 * unefficent when a lot of packages were found on /usr/lib/pkg-config.
210 * Let then the user click on the toggle checkbox. We'll notify her whether
211 * there are no good cflags for that package.
213 if (priv
->pkg_list
== NULL
)
215 g_warning ("No packages found");
219 priv
->pkg_list
= g_list_sort (priv
->pkg_list
, pkg_list_compare
);
220 item
= priv
->pkg_list
;
225 gboolean enabled
= FALSE
;
226 /* that's good. We can add the package to the GtkListStore */
227 gtk_list_store_append (GTK_LIST_STORE (store
), &iter
);
229 /* check if we should enable or not the checkbox */
230 if (g_hash_table_lookup (priv
->enabled_packages_hash
, item
->data
) == NULL
)
235 gtk_list_store_set (store
, &iter
, COLUMN_LOAD
, enabled
,
236 COLUMN_NAME
, g_strdup (item
->data
), -1);
243 on_tag_load_toggled_parseable_cb (SymbolDBSystem
*sdbs
,
244 gboolean is_parseable
,
247 GtkWidget
*treeview
, *prefs_progressbar
;
248 GtkWindow
*prefs_window
;
249 ParseableData
*pdata
;
251 SymbolDBPrefsPriv
*priv
;
252 const gchar
*path_str
;
257 gchar
*curr_package_name
;
259 pdata
= (ParseableData
*)user_data
;
260 path_str
= pdata
->path_str
;
264 DEBUG_PRINT ("on_tag_load_toggled_parseable_cb %d", is_parseable
);
265 prefs_window
= GTK_WINDOW (glade_xml_get_widget (priv
->prefs_gxml
, "symbol_db_pref_window"));
266 treeview
= glade_xml_get_widget (priv
->prefs_gxml
, "tags_treeview");
267 prefs_progressbar
= glade_xml_get_widget (priv
->prefs_gxml
, "prefs_progressbar");
269 store
= priv
->prefs_list_store
;
270 path
= gtk_tree_path_new_from_string (path_str
);
271 gtk_tree_model_get_iter (GTK_TREE_MODEL (store
), &iter
, path
);
272 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
,
273 COLUMN_LOAD
, &enabled
,
274 COLUMN_NAME
, &curr_package_name
,
277 if (is_parseable
== FALSE
)
279 GtkWidget
*wid
= gtk_message_dialog_new (prefs_window
, GTK_DIALOG_MODAL
,
281 GTK_BUTTONS_OK
, _("Package is not parseable"));
282 gtk_dialog_run (GTK_DIALOG (wid
));
283 gtk_widget_destroy (wid
);
285 /* we for sure don't want this package on list next time */
286 gtk_list_store_set (store
, &iter
, COLUMN_LOAD
, FALSE
, -1);
288 /* emit the package-remove signal */
289 g_signal_emit (sdbp
, signals
[PACKAGE_REMOVE
], 0, curr_package_name
);
293 /* we have a good parseable package. Let's mark the check enabled/disabled */
295 gtk_list_store_set (store
, &iter
, COLUMN_LOAD
, enabled
, -1);
297 /* good, should we scan the packages? */
300 symbol_db_system_scan_package (priv
->sdbs
, curr_package_name
);
302 /* emit the package-add signal */
303 g_signal_emit (sdbp
, signals
[PACKAGE_ADD
], 0, curr_package_name
);
307 /* emit the package-remove signal */
308 g_signal_emit (sdbp
, signals
[PACKAGE_REMOVE
], 0, curr_package_name
);
312 gtk_widget_set_sensitive (treeview
, TRUE
);
313 gtk_widget_hide (prefs_progressbar
);
314 gtk_tree_path_free (path
);
316 destroy_parseable_data (pdata
);
320 on_tag_load_toggled (GtkCellRendererToggle
*cell
, char *path_str
,
325 gchar
*curr_package_name
;
327 GtkWidget
*prefs_progressbar
;
328 GtkWidget
* treeview
;
329 ParseableData
*pdata
;
330 SymbolDBPrefsPriv
*priv
;
334 DEBUG_PRINT ("on_tag_load_toggled ()");
336 store
= priv
->prefs_list_store
;
337 path
= gtk_tree_path_new_from_string (path_str
);
338 gtk_tree_model_get_iter (GTK_TREE_MODEL (store
), &iter
, path
);
339 gtk_tree_model_get (GTK_TREE_MODEL (store
), &iter
,
340 COLUMN_NAME
, &curr_package_name
,
342 gtk_tree_path_free (path
);
344 prefs_progressbar
= glade_xml_get_widget (priv
->prefs_gxml
, "prefs_progressbar");
345 gtk_widget_show_all (prefs_progressbar
);
347 gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (prefs_progressbar
), 1.0);
348 gtk_progress_bar_pulse (GTK_PROGRESS_BAR (prefs_progressbar
));
350 treeview
= glade_xml_get_widget (priv
->prefs_gxml
, "tags_treeview");
351 gtk_widget_set_sensitive (treeview
, FALSE
);
353 pdata
= g_new0 (ParseableData
, 1);
355 pdata
->path_str
= g_strdup (path_str
);
357 symbol_db_system_is_package_parseable (priv
->sdbs
, curr_package_name
,
358 on_tag_load_toggled_parseable_cb
,
363 sdb_prefs_init1 (SymbolDBPrefs
*sdbp
)
365 SymbolDBPrefsPriv
*priv
;
371 fchooser
= glade_xml_get_widget (priv
->prefs_gxml
, CHOOSER_WIDGET
);
372 /* we will reactivate it after the listall has been finished */
373 gtk_widget_set_sensitive (fchooser
, FALSE
);
375 anjuta_preferences_add_page (priv
->prefs
,
378 _("Symbol Database"),
381 ctags_value
= anjuta_preferences_get (priv
->prefs
, CTAGS_PREFS_KEY
);
383 if (ctags_value
== NULL
|| strlen (ctags_value
) <= 0)
385 ctags_value
= g_strdup (CTAGS_PATH
);
388 DEBUG_PRINT ("select ->%s<-", ctags_value
);
390 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fchooser
), ctags_value
);
391 gtk_file_chooser_select_filename (GTK_FILE_CHOOSER (fchooser
), ctags_value
);
393 g_signal_connect (G_OBJECT (fchooser
), "selection-changed",
394 G_CALLBACK (on_prefs_executable_changed
), sdbp
);
396 priv
->prefs_notify_id
= anjuta_preferences_notify_add (priv
->prefs
,
398 on_gconf_notify_prefs
,
401 g_free (ctags_value
);
405 sdb_prefs_init (SymbolDBPrefs
*object
)
408 SymbolDBPrefsPriv
*priv
;
410 GtkCellRenderer
*renderer
;
411 GtkTreeViewColumn
*column
;
412 gchar
* exe_string
= NULL
;
413 gboolean require_scan
= FALSE
; /* scan for packages */
415 sdbp
= SYMBOL_DB_PREFS (object
);
416 sdbp
->priv
= g_new0 (SymbolDBPrefsPriv
, 1);
419 priv
->pkg_list
= NULL
;
421 DEBUG_PRINT ("symbol_db_prefs_init ()");
423 if (priv
->prefs_gxml
== NULL
)
425 /* Create the preferences page */
426 priv
->prefs_gxml
= glade_xml_new (GLADE_FILE
, GLADE_ROOT
, NULL
);
429 /* init GtkListStore */
430 if (priv
->prefs_list_store
== NULL
)
432 priv
->prefs_list_store
= gtk_list_store_new (COLUMN_MAX
, G_TYPE_BOOLEAN
,
437 treeview
= glade_xml_get_widget (priv
->prefs_gxml
, "tags_treeview");
438 /* on_listall_exit will reactivate this */
439 gtk_widget_set_sensitive (treeview
, FALSE
);
440 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview
),
441 GTK_TREE_MODEL (priv
->prefs_list_store
));
443 /* Add the column for stock treeview */
444 renderer
= gtk_cell_renderer_toggle_new ();
445 g_signal_connect (G_OBJECT (renderer
), "toggled",
446 G_CALLBACK (on_tag_load_toggled
), sdbp
);
447 column
= gtk_tree_view_column_new_with_attributes (_("Load"),
452 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
454 renderer
= gtk_cell_renderer_text_new ();
455 column
= gtk_tree_view_column_new_with_attributes (_("API Tags"),
459 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
460 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
461 gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview
),
464 /* frame3 show all */
466 frame3
= glade_xml_get_widget (priv
->prefs_gxml
, "frame3");
467 gtk_widget_show_all (frame3
);
468 GtkWidget
*prefs_progressbar
= glade_xml_get_widget (priv
->prefs_gxml
,
469 "prefs_progressbar");
470 gtk_widget_hide (prefs_progressbar
);
472 /* listall launcher thing */
473 if (require_scan
== TRUE
)
475 priv
->pkg_config_launcher
= anjuta_launcher_new ();
477 anjuta_launcher_set_check_passwd_prompt (priv
->pkg_config_launcher
,
480 g_signal_connect (G_OBJECT (priv
->pkg_config_launcher
), "child-exited",
481 G_CALLBACK (on_listall_exit
), sdbp
);
483 exe_string
= g_strdup ("pkg-config --list-all");
485 anjuta_launcher_execute (priv
->pkg_config_launcher
,
486 exe_string
, on_listall_output
,
490 /* unrefs unused memory objects */
495 sdb_prefs_finalize (GObject
*object
)
498 SymbolDBPrefsPriv
*priv
;
500 sdbp
= SYMBOL_DB_PREFS (object
);
503 DEBUG_PRINT ("symbol_db_prefs_finalize ()");
505 anjuta_preferences_notify_remove(priv
->prefs
, priv
->prefs_notify_id
);
506 anjuta_preferences_remove_page(priv
->prefs
, _("Symbol Database"));
508 if (priv
->pkg_config_launcher
!= NULL
)
509 g_object_unref (priv
->pkg_config_launcher
);
510 priv
->pkg_config_launcher
= NULL
;
513 g_list_foreach (priv
->pkg_list
, (GFunc
)g_free
, NULL
);
514 g_list_free (priv
->pkg_list
);
515 priv
->pkg_list
= NULL
;
517 if (priv
->prefs_gxml
!= NULL
)
518 g_object_unref (priv
->prefs_gxml
);
520 if (priv
->prefs_list_store
!= NULL
)
521 g_object_unref (priv
->prefs_list_store
);
523 if (priv
->enabled_packages_hash
)
525 g_hash_table_destroy (priv
->enabled_packages_hash
);
528 G_OBJECT_CLASS (sdb_prefs_parent_class
)->finalize (object
);
532 sdb_prefs_class_init (SymbolDBPrefsClass
*klass
)
534 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
537 = g_signal_new ("package-add",
538 G_OBJECT_CLASS_TYPE (object_class
),
540 G_STRUCT_OFFSET (SymbolDBPrefsClass
, package_add
),
542 g_cclosure_marshal_VOID__STRING
, G_TYPE_NONE
,
546 signals
[PACKAGE_REMOVE
]
547 = g_signal_new ("package-remove",
548 G_OBJECT_CLASS_TYPE (object_class
),
550 G_STRUCT_OFFSET (SymbolDBPrefsClass
, package_remove
),
552 g_cclosure_marshal_VOID__STRING
, G_TYPE_NONE
,
556 object_class
->finalize
= sdb_prefs_finalize
;
560 symbol_db_prefs_new (SymbolDBSystem
*sdbs
, SymbolDBEngine
*sdbe_project
,
561 SymbolDBEngine
*sdbe_globals
, AnjutaPreferences
*prefs
,
562 GList
*enabled_packages
)
565 SymbolDBPrefsPriv
*priv
;
567 sdbp
= g_object_new (SYMBOL_TYPE_DB_PREFS
, NULL
);
573 priv
->sdbe_project
= sdbe_project
;
574 priv
->sdbe_globals
= sdbe_globals
;
575 priv
->enabled_packages_hash
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
578 /* we'll convert the list of strings in input into an hash table, so that
579 * a lookup there will be done quicker
581 GList
*item
= enabled_packages
;
584 g_hash_table_insert (priv
->enabled_packages_hash
, (gpointer
)g_strdup (item
->data
),
589 sdb_prefs_init1 (sdbp
);