2
* Copyright 2010 Jiri Techet <techet@gmail.com>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
#include <gdk/gdkkeysyms.h>
21
#include <glib/gstdio.h>
26
#include <geanyplugin.h>
28
#include "gproject-utils.h"
29
#include "gproject-project.h"
31
extern GeanyPlugin *geany_plugin;
32
extern GeanyData *geany_data;
33
extern GeanyFunctions *geany_functions;
37
GtkWidget *source_patterns;
38
GtkWidget *header_patterns;
39
GtkWidget *ignored_dirs_patterns;
40
GtkWidget *generate_tags;
41
} PropertyDialogElements;
45
static PropertyDialogElements *e;
47
typedef enum {DeferredTagOpAdd, DeferredTagOpRemove} DeferredTagOpType;
52
DeferredTagOpType type;
55
static GSList *file_tag_deferred_op_queue = NULL;
56
static gboolean flush_queued = FALSE;
59
static void deferred_op_free(DeferredTagOp* op, G_GNUC_UNUSED gpointer user_data)
66
static void deferred_op_queue_clean()
68
g_slist_foreach(file_tag_deferred_op_queue, (GFunc)deferred_op_free, NULL);
69
g_slist_free(file_tag_deferred_op_queue);
70
file_tag_deferred_op_queue = NULL;
75
static void workspace_add_tag(gchar *filename, TagObject *obj, gpointer foo)
77
TMWorkObject *tm_obj = NULL;
79
if (!document_find_by_filename(filename))
81
gchar *locale_filename;
83
locale_filename = utils_get_locale_from_utf8(filename);
84
tm_obj = tm_source_file_new(locale_filename, FALSE, filetypes_detect_from_file(filename)->name);
85
g_free(locale_filename);
87
tm_workspace_add_object(tm_obj);
88
tm_source_file_update(tm_obj, TRUE, FALSE, TRUE);
92
tm_workspace_remove_object(obj->tag, TRUE, TRUE);
98
static void workspace_add_file_tag(gchar *filename)
102
obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
104
workspace_add_tag(filename, obj, NULL);
108
static void workspace_remove_tag(gchar *filename, TagObject *obj, gpointer foo)
112
tm_workspace_remove_object(obj->tag, TRUE, TRUE);
118
static void workspace_remove_file_tag(gchar *filename)
122
obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
124
workspace_remove_tag(filename, obj, NULL);
128
static void deferred_op_queue_dispatch(DeferredTagOp* op, G_GNUC_UNUSED gpointer user_data)
130
if (op->type == DeferredTagOpAdd)
131
workspace_add_file_tag(op->filename);
132
else if (op->type == DeferredTagOpRemove)
133
workspace_remove_file_tag(op->filename);
137
static gboolean deferred_op_queue_flush(G_GNUC_UNUSED gpointer data)
139
g_slist_foreach(file_tag_deferred_op_queue,
140
(GFunc)deferred_op_queue_dispatch, NULL);
141
deferred_op_queue_clean();
142
flush_queued = FALSE;
144
return FALSE; // returning false removes this callback; it is a one-shot
148
static void deferred_op_queue_enqueue(gchar* filename, DeferredTagOpType type)
152
op = (DeferredTagOp *) g_new0(DeferredTagOp, 1);
154
op->filename = g_strdup(filename);
156
file_tag_deferred_op_queue = g_slist_prepend(file_tag_deferred_op_queue,op);
161
plugin_idle_add(geany_plugin, (GSourceFunc)deferred_op_queue_flush, NULL);
166
/* path - absolute path in locale, returned list in locale */
167
static GSList *get_file_list(const gchar * path, GSList *patterns, GSList *ignored_dirs_patterns)
172
dir = g_dir_open(path, 0, NULL);
181
name = g_dir_read_name(dir);
185
filename = g_build_filename(path, name, NULL);
187
if (g_file_test(filename, G_FILE_TEST_IS_DIR))
191
if (patterns_match(ignored_dirs_patterns, name))
197
lst = get_file_list(filename, patterns, ignored_dirs_patterns);
199
list = g_slist_concat(list, lst);
202
else if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
204
if (patterns_match(patterns, name))
205
list = g_slist_prepend(list, filename);
217
void gprj_project_rescan()
219
GSList *pattern_list = NULL;
220
GSList *ignored_dirs_list = NULL;
227
if (g_prj->generate_tags)
228
g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_remove_tag, NULL);
229
g_hash_table_destroy(g_prj->file_tag_table);
230
g_prj->file_tag_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
232
deferred_op_queue_clean();
234
pattern_list = get_precompiled_patterns(geany_data->app->project->file_patterns);
236
ignored_dirs_list = get_precompiled_patterns(g_prj->ignored_dirs_patterns);
238
lst = get_file_list(geany_data->app->project->base_path, pattern_list, ignored_dirs_list);
240
for (elem = lst; elem != NULL; elem = g_slist_next(elem))
245
obj = g_new0(TagObject, 1);
248
path = tm_get_real_path(elem->data);
249
setptr(path, utils_get_utf8_from_locale(path));
250
g_hash_table_insert(g_prj->file_tag_table, path, obj);
253
if (g_prj->generate_tags)
254
g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_add_tag, NULL);
256
g_slist_foreach(lst, (GFunc) g_free, NULL);
259
g_slist_foreach(pattern_list, (GFunc) g_pattern_spec_free, NULL);
260
g_slist_free(pattern_list);
262
g_slist_foreach(ignored_dirs_list, (GFunc) g_pattern_spec_free, NULL);
263
g_slist_free(ignored_dirs_list);
267
static void update_project(
268
gchar **source_patterns,
269
gchar **header_patterns,
270
gchar **ignored_dirs_patterns,
271
gboolean generate_tags)
273
if (g_prj->source_patterns)
274
g_strfreev(g_prj->source_patterns);
275
g_prj->source_patterns = g_strdupv(source_patterns);
277
if (g_prj->header_patterns)
278
g_strfreev(g_prj->header_patterns);
279
g_prj->header_patterns = g_strdupv(header_patterns);
281
if (g_prj->ignored_dirs_patterns)
282
g_strfreev(g_prj->ignored_dirs_patterns);
283
g_prj->ignored_dirs_patterns = g_strdupv(ignored_dirs_patterns);
285
g_prj->generate_tags = generate_tags;
287
gprj_project_rescan();
291
void gprj_project_save(GKeyFile * key_file)
296
g_key_file_set_string_list(key_file, "gproject", "source_patterns",
297
(const gchar**) g_prj->source_patterns, g_strv_length(g_prj->source_patterns));
298
g_key_file_set_string_list(key_file, "gproject", "header_patterns",
299
(const gchar**) g_prj->header_patterns, g_strv_length(g_prj->header_patterns));
300
g_key_file_set_string_list(key_file, "gproject", "ignored_dirs_patterns",
301
(const gchar**) g_prj->ignored_dirs_patterns, g_strv_length(g_prj->ignored_dirs_patterns));
302
g_key_file_set_boolean(key_file, "gproject", "generate_tags", g_prj->generate_tags);
306
void gprj_project_open(GKeyFile * key_file)
308
gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
309
gboolean generate_tags;
312
gprj_project_close();
314
g_prj = (GPrj *) g_new0(GPrj, 1);
316
g_prj->source_patterns = NULL;
317
g_prj->header_patterns = NULL;
318
g_prj->ignored_dirs_patterns = NULL;
319
g_prj->generate_tags = FALSE;
321
g_prj->file_tag_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
323
deferred_op_queue_clean();
325
source_patterns = g_key_file_get_string_list(key_file, "gproject", "source_patterns", NULL, NULL);
326
if (!source_patterns)
327
source_patterns = g_strsplit("*.c *.C *.cpp *.cxx *.c++ *.cc", " ", -1);
328
header_patterns = g_key_file_get_string_list(key_file, "gproject", "header_patterns", NULL, NULL);
329
if (!header_patterns)
330
header_patterns = g_strsplit("*.h *.H *.hpp *.hxx *.h++ *.hh *.m", " ", -1);
331
ignored_dirs_patterns = g_key_file_get_string_list(key_file, "gproject", "ignored_dirs_patterns", NULL, NULL);
332
if (!ignored_dirs_patterns)
333
ignored_dirs_patterns = g_strsplit(".* CVS", " ", -1);
334
generate_tags = utils_get_setting_boolean(key_file, "gproject", "generate_tags", FALSE);
339
ignored_dirs_patterns,
342
g_free(source_patterns);
343
g_free(header_patterns);
344
g_free(ignored_dirs_patterns);
348
static gchar **split_patterns(const gchar *str)
354
input = g_strdup(str);
357
tmp = g_string_new(input);
359
do {} while (utils_string_replace_all(tmp, " ", " "));
360
ret = g_strsplit(tmp->str, " ", -1);
361
g_string_free(tmp, TRUE);
366
void gprj_project_read_properties_tab()
368
gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
370
source_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->source_patterns)));
371
header_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->header_patterns)));
372
ignored_dirs_patterns = split_patterns(gtk_entry_get_text(GTK_ENTRY(e->ignored_dirs_patterns)));
375
source_patterns, header_patterns, ignored_dirs_patterns,
376
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(e->generate_tags)));
378
g_free(source_patterns);
379
g_free(header_patterns);
380
g_free(ignored_dirs_patterns);
384
void gprj_project_add_properties_tab(GtkWidget *notebook)
386
GtkWidget *vbox, *hbox, *hbox1;
391
e = g_new0(PropertyDialogElements, 1);
393
vbox = gtk_vbox_new(FALSE, 0);
395
table = gtk_table_new(3, 2, FALSE);
396
gtk_table_set_row_spacings(GTK_TABLE(table), 5);
397
gtk_table_set_col_spacings(GTK_TABLE(table), 10);
399
label = gtk_label_new(_("Source patterns:"));
400
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
401
e->source_patterns = gtk_entry_new();
402
ui_table_add_row(GTK_TABLE(table), 0, label, e->source_patterns, NULL);
403
ui_entry_add_clear_icon(GTK_ENTRY(e->source_patterns));
404
ui_widget_set_tooltip_text(e->source_patterns,
405
_("Space separated list of patterns that are used to identify source files."));
406
str = g_strjoinv(" ", g_prj->source_patterns);
407
gtk_entry_set_text(GTK_ENTRY(e->source_patterns), str);
410
label = gtk_label_new(_("Header patterns:"));
411
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
412
e->header_patterns = gtk_entry_new();
413
ui_entry_add_clear_icon(GTK_ENTRY(e->header_patterns));
414
ui_table_add_row(GTK_TABLE(table), 1, label, e->header_patterns, NULL);
415
ui_widget_set_tooltip_text(e->header_patterns,
416
_("Space separated list of patterns that are used to identify headers. "
417
"Used mainly for header/source swapping."));
418
str = g_strjoinv(" ", g_prj->header_patterns);
419
gtk_entry_set_text(GTK_ENTRY(e->header_patterns), str);
422
label = gtk_label_new(_("Ignored dirs patterns:"));
423
gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
424
e->ignored_dirs_patterns = gtk_entry_new();
425
ui_entry_add_clear_icon(GTK_ENTRY(e->ignored_dirs_patterns));
426
ui_table_add_row(GTK_TABLE(table), 2, label, e->ignored_dirs_patterns, NULL);
427
ui_widget_set_tooltip_text(e->ignored_dirs_patterns,
428
_("Space separated list of patterns that are used to identify directories "
429
"that are not scanned for source files."));
430
str = g_strjoinv(" ", g_prj->ignored_dirs_patterns);
431
gtk_entry_set_text(GTK_ENTRY(e->ignored_dirs_patterns), str);
434
gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 6);
436
e->generate_tags = gtk_check_button_new_with_label(_("Generate tags for all project files"));
437
ui_widget_set_tooltip_text(e->generate_tags,
438
_("Generate tag list for all project files instead of only for the currently opened files. "
439
"Too slow for big projects (>1000 files) and should be disabled in this case."));
440
gtk_box_pack_start(GTK_BOX(vbox), e->generate_tags, FALSE, FALSE, 6);
441
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e->generate_tags), g_prj->generate_tags);
443
hbox1 = gtk_hbox_new(FALSE, 0);
444
label = gtk_label_new(_("Note: set the patterns of files belonging to the project under the Project tab."));
445
gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
446
gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 6);
448
label = gtk_label_new(_("GProject"));
450
hbox = gtk_hbox_new(FALSE, 0);
451
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 6);
453
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
457
void gprj_project_close()
459
g_return_if_fail(g_prj);
461
if (g_prj->generate_tags)
462
g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_remove_tag, NULL);
464
deferred_op_queue_clean();
466
g_free(g_prj->source_patterns);
467
g_free(g_prj->header_patterns);
468
g_free(g_prj->ignored_dirs_patterns);
470
g_hash_table_destroy(g_prj->file_tag_table);
477
void gprj_project_add_file_tag(gchar *filename)
479
deferred_op_queue_enqueue(filename, DeferredTagOpAdd);
483
void gprj_project_remove_file_tag(gchar *filename)
485
deferred_op_queue_enqueue(filename, DeferredTagOpRemove);
489
gboolean gprj_project_is_in_project(const gchar * filename)
491
return filename && g_prj && geany_data->app->project &&
492
g_hash_table_lookup(g_prj->file_tag_table, filename) != NULL;