~ubuntu-branches/debian/sid/geany-plugins/sid

« back to all changes in this revision

Viewing changes to gproject/src/gproject-project.c

  • Committer: Package Import Robot
  • Author(s): Evgeni Golov, Evgeni Golov
  • Date: 2011-11-17 20:03:24 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20111117200324-8qmu6tuwjtfpv3n7
Tags: 0.21.1-1
[ Evgeni Golov ]
* [f4007f2] Imported Upstream version 0.21.1
* [5ff622d] install plugins from/to /usr/lib/<arch-triplet>/geany
* [75411d7] bump build-dep to geany 0.21
* [db92155] depend on the virtual geany-abi-XX package
* [dd84769] switch to 3.0 (quilt)
* [3ab76d7] drop readme.source
* [233d44c] refresh patch against 0.21.1
* [030bec1] add lintian override for
            documentation-package-not-architecture-independent
* [aba43d7] use debhelper compat level 9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2010 Jiri Techet <techet@gmail.com>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
17
 */
 
18
 
 
19
#include <sys/time.h>
 
20
#include <gdk/gdkkeysyms.h>
 
21
#include <glib/gstdio.h>
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
        #include "config.h"
 
25
#endif
 
26
#include <geanyplugin.h>
 
27
 
 
28
#include "gproject-utils.h"
 
29
#include "gproject-project.h"
 
30
 
 
31
extern GeanyPlugin *geany_plugin;
 
32
extern GeanyData *geany_data;
 
33
extern GeanyFunctions *geany_functions;
 
34
 
 
35
typedef struct
 
36
{
 
37
        GtkWidget *source_patterns;
 
38
        GtkWidget *header_patterns;
 
39
        GtkWidget *ignored_dirs_patterns;
 
40
        GtkWidget *generate_tags;
 
41
} PropertyDialogElements;
 
42
 
 
43
GPrj *g_prj = NULL;
 
44
 
 
45
static PropertyDialogElements *e;
 
46
 
 
47
typedef enum {DeferredTagOpAdd, DeferredTagOpRemove} DeferredTagOpType;
 
48
 
 
49
typedef struct
 
50
{
 
51
        gchar * filename;
 
52
        DeferredTagOpType type;
 
53
} DeferredTagOp;
 
54
 
 
55
static GSList *file_tag_deferred_op_queue = NULL;
 
56
static gboolean flush_queued = FALSE;
 
57
 
 
58
 
 
59
static void deferred_op_free(DeferredTagOp* op, G_GNUC_UNUSED gpointer user_data)
 
60
{
 
61
        g_free(op->filename);
 
62
        g_free(op);
 
63
}
 
64
 
 
65
 
 
66
static void deferred_op_queue_clean()
 
67
{
 
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;
 
71
        flush_queued = FALSE;
 
72
}
 
73
 
 
74
 
 
75
static void workspace_add_tag(gchar *filename, TagObject *obj, gpointer foo)
 
76
{
 
77
        TMWorkObject *tm_obj = NULL;
 
78
 
 
79
        if (!document_find_by_filename(filename))
 
80
        {
 
81
                gchar *locale_filename;
 
82
 
 
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);
 
86
 
 
87
                tm_workspace_add_object(tm_obj);
 
88
                tm_source_file_update(tm_obj, TRUE, FALSE, TRUE);
 
89
        }
 
90
 
 
91
        if (obj->tag)
 
92
                tm_workspace_remove_object(obj->tag, TRUE, TRUE);
 
93
 
 
94
        obj->tag = tm_obj;
 
95
}
 
96
 
 
97
 
 
98
static void workspace_add_file_tag(gchar *filename)
 
99
{
 
100
        TagObject *obj;
 
101
 
 
102
        obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
 
103
        if (obj)
 
104
                workspace_add_tag(filename, obj, NULL);
 
105
}
 
106
 
 
107
 
 
108
static void workspace_remove_tag(gchar *filename, TagObject *obj, gpointer foo)
 
109
{
 
110
        if (obj->tag)
 
111
        {
 
112
                tm_workspace_remove_object(obj->tag, TRUE, TRUE);
 
113
                obj->tag = NULL;
 
114
        }
 
115
}
 
116
 
 
117
 
 
118
static void workspace_remove_file_tag(gchar *filename)
 
119
{
 
120
        TagObject *obj;
 
121
 
 
122
        obj = g_hash_table_lookup(g_prj->file_tag_table, filename);
 
123
        if (obj)
 
124
                workspace_remove_tag(filename, obj, NULL);
 
125
}
 
126
 
 
127
 
 
128
static void deferred_op_queue_dispatch(DeferredTagOp* op, G_GNUC_UNUSED gpointer user_data)
 
129
{
 
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);
 
134
}
 
135
 
 
136
 
 
137
static gboolean deferred_op_queue_flush(G_GNUC_UNUSED gpointer data)
 
138
{
 
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;
 
143
 
 
144
        return FALSE; // returning false removes this callback; it is a one-shot
 
145
}
 
146
 
 
147
 
 
148
static void deferred_op_queue_enqueue(gchar* filename, DeferredTagOpType type)
 
149
{
 
150
        DeferredTagOp * op;
 
151
 
 
152
        op = (DeferredTagOp *) g_new0(DeferredTagOp, 1);
 
153
        op->type = type;
 
154
        op->filename = g_strdup(filename);
 
155
 
 
156
        file_tag_deferred_op_queue = g_slist_prepend(file_tag_deferred_op_queue,op);
 
157
 
 
158
        if (!flush_queued)
 
159
        {
 
160
                flush_queued = TRUE;
 
161
                plugin_idle_add(geany_plugin, (GSourceFunc)deferred_op_queue_flush, NULL);
 
162
        }
 
163
}
 
164
 
 
165
 
 
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)
 
168
{
 
169
        GSList *list = NULL;
 
170
        GDir *dir;
 
171
 
 
172
        dir = g_dir_open(path, 0, NULL);
 
173
        if (!dir)
 
174
                return NULL;
 
175
 
 
176
        while (TRUE)
 
177
        {
 
178
                const gchar *name;
 
179
                gchar *filename;
 
180
 
 
181
                name = g_dir_read_name(dir);
 
182
                if (!name)
 
183
                        break;
 
184
 
 
185
                filename = g_build_filename(path, name, NULL);
 
186
 
 
187
                if (g_file_test(filename, G_FILE_TEST_IS_DIR))
 
188
                {
 
189
                        GSList *lst;
 
190
 
 
191
                        if (patterns_match(ignored_dirs_patterns, name))
 
192
                        {
 
193
                                g_free(filename);
 
194
                                continue;
 
195
                        }
 
196
 
 
197
                        lst = get_file_list(filename, patterns, ignored_dirs_patterns);
 
198
                        if (lst)
 
199
                                list = g_slist_concat(list, lst);
 
200
                        g_free(filename);
 
201
                }
 
202
                else if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
 
203
                {
 
204
                        if (patterns_match(patterns, name))
 
205
                                list = g_slist_prepend(list, filename);
 
206
                        else
 
207
                                g_free(filename);
 
208
                }
 
209
        }
 
210
 
 
211
        g_dir_close(dir);
 
212
 
 
213
        return list;
 
214
}
 
215
 
 
216
 
 
217
void gprj_project_rescan()
 
218
{
 
219
        GSList *pattern_list = NULL;
 
220
        GSList *ignored_dirs_list = NULL;
 
221
        GSList *lst;
 
222
        GSList *elem;
 
223
 
 
224
        if (!g_prj)
 
225
                return;
 
226
 
 
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);
 
231
 
 
232
        deferred_op_queue_clean();
 
233
 
 
234
        pattern_list = get_precompiled_patterns(geany_data->app->project->file_patterns);
 
235
 
 
236
        ignored_dirs_list = get_precompiled_patterns(g_prj->ignored_dirs_patterns);
 
237
 
 
238
        lst = get_file_list(geany_data->app->project->base_path, pattern_list, ignored_dirs_list);
 
239
 
 
240
        for (elem = lst; elem != NULL; elem = g_slist_next(elem))
 
241
        {
 
242
                char *path;
 
243
                TagObject *obj;
 
244
 
 
245
                obj = g_new0(TagObject, 1);
 
246
                obj->tag = NULL;
 
247
 
 
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);
 
251
        }
 
252
 
 
253
        if (g_prj->generate_tags)
 
254
                g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_add_tag, NULL);
 
255
 
 
256
        g_slist_foreach(lst, (GFunc) g_free, NULL);
 
257
        g_slist_free(lst);
 
258
 
 
259
        g_slist_foreach(pattern_list, (GFunc) g_pattern_spec_free, NULL);
 
260
        g_slist_free(pattern_list);
 
261
 
 
262
        g_slist_foreach(ignored_dirs_list, (GFunc) g_pattern_spec_free, NULL);
 
263
        g_slist_free(ignored_dirs_list);
 
264
}
 
265
 
 
266
 
 
267
static void update_project(
 
268
        gchar **source_patterns,
 
269
        gchar **header_patterns,
 
270
        gchar **ignored_dirs_patterns,
 
271
        gboolean generate_tags)
 
272
{
 
273
        if (g_prj->source_patterns)
 
274
                g_strfreev(g_prj->source_patterns);
 
275
        g_prj->source_patterns = g_strdupv(source_patterns);
 
276
 
 
277
        if (g_prj->header_patterns)
 
278
                g_strfreev(g_prj->header_patterns);
 
279
        g_prj->header_patterns = g_strdupv(header_patterns);
 
280
 
 
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);
 
284
 
 
285
        g_prj->generate_tags = generate_tags;
 
286
 
 
287
        gprj_project_rescan();
 
288
}
 
289
 
 
290
 
 
291
void gprj_project_save(GKeyFile * key_file)
 
292
{
 
293
        if (!g_prj)
 
294
                return;
 
295
 
 
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);
 
303
}
 
304
 
 
305
 
 
306
void gprj_project_open(GKeyFile * key_file)
 
307
{
 
308
        gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
 
309
        gboolean generate_tags;
 
310
 
 
311
        if (g_prj != NULL)
 
312
                gprj_project_close();
 
313
 
 
314
        g_prj = (GPrj *) g_new0(GPrj, 1);
 
315
 
 
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;
 
320
 
 
321
        g_prj->file_tag_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 
322
 
 
323
        deferred_op_queue_clean();
 
324
 
 
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);
 
335
 
 
336
        update_project(
 
337
                source_patterns,
 
338
                header_patterns,
 
339
                ignored_dirs_patterns,
 
340
                generate_tags);
 
341
 
 
342
        g_free(source_patterns);
 
343
        g_free(header_patterns);
 
344
        g_free(ignored_dirs_patterns);
 
345
}
 
346
 
 
347
 
 
348
static gchar **split_patterns(const gchar *str)
 
349
{
 
350
        GString *tmp;
 
351
        gchar **ret;
 
352
        gchar *input;
 
353
 
 
354
        input = g_strdup(str);
 
355
 
 
356
        g_strstrip(input);
 
357
        tmp = g_string_new(input);
 
358
        g_free(input);
 
359
        do {} while (utils_string_replace_all(tmp, "  ", " "));
 
360
        ret = g_strsplit(tmp->str, " ", -1);
 
361
        g_string_free(tmp, TRUE);
 
362
        return ret;
 
363
}
 
364
 
 
365
 
 
366
void gprj_project_read_properties_tab()
 
367
{
 
368
        gchar **source_patterns, **header_patterns, **ignored_dirs_patterns;
 
369
 
 
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)));
 
373
 
 
374
        update_project(
 
375
                source_patterns, header_patterns, ignored_dirs_patterns,
 
376
                gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(e->generate_tags)));
 
377
 
 
378
        g_free(source_patterns);
 
379
        g_free(header_patterns);
 
380
        g_free(ignored_dirs_patterns);
 
381
}
 
382
 
 
383
 
 
384
void gprj_project_add_properties_tab(GtkWidget *notebook)
 
385
{
 
386
        GtkWidget *vbox, *hbox, *hbox1;
 
387
        GtkWidget *table;
 
388
        GtkWidget *label;
 
389
        gchar *str;
 
390
 
 
391
        e = g_new0(PropertyDialogElements, 1);
 
392
 
 
393
        vbox = gtk_vbox_new(FALSE, 0);
 
394
 
 
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);
 
398
 
 
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);
 
408
        g_free(str);
 
409
 
 
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);
 
420
        g_free(str);
 
421
 
 
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);
 
432
        g_free(str);
 
433
 
 
434
        gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 6);
 
435
 
 
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);
 
442
 
 
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);
 
447
 
 
448
        label = gtk_label_new(_("GProject"));
 
449
 
 
450
        hbox = gtk_hbox_new(FALSE, 0);
 
451
        gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 6);
 
452
 
 
453
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, label);
 
454
}
 
455
 
 
456
 
 
457
void gprj_project_close()
 
458
{
 
459
        g_return_if_fail(g_prj);
 
460
 
 
461
        if (g_prj->generate_tags)
 
462
                g_hash_table_foreach(g_prj->file_tag_table, (GHFunc)workspace_remove_tag, NULL);
 
463
 
 
464
        deferred_op_queue_clean();
 
465
 
 
466
        g_free(g_prj->source_patterns);
 
467
        g_free(g_prj->header_patterns);
 
468
        g_free(g_prj->ignored_dirs_patterns);
 
469
 
 
470
        g_hash_table_destroy(g_prj->file_tag_table);
 
471
 
 
472
        g_free(g_prj);
 
473
        g_prj = NULL;
 
474
}
 
475
 
 
476
 
 
477
void gprj_project_add_file_tag(gchar *filename)
 
478
{
 
479
        deferred_op_queue_enqueue(filename, DeferredTagOpAdd);
 
480
}
 
481
 
 
482
 
 
483
void gprj_project_remove_file_tag(gchar *filename)
 
484
{
 
485
        deferred_op_queue_enqueue(filename, DeferredTagOpRemove);
 
486
}
 
487
 
 
488
 
 
489
gboolean gprj_project_is_in_project(const gchar * filename)
 
490
{
 
491
        return filename && g_prj && geany_data->app->project &&
 
492
                g_hash_table_lookup(g_prj->file_tag_table, filename) != NULL;
 
493
}