1
/* Bluefish HTML Editor
4
* Copyright (C) 2002,2003,2004,2005,2006,2007 Olivier Sessink
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.
29
#include "filebrowser2.h"
31
#include "dialog_utils.h"
34
#include "file_dialogs.h"
35
#include "gtk_easy.h" /* destroy_disposable_menu_cb() */
36
#include "stringlist.h" /* count_array() */
40
GPatternSpec* patspec;
43
static Tfilterpattern *new_pattern(gchar *name) {
44
Tfilterpattern *pat = g_new(Tfilterpattern,1);
45
pat->pattern = g_strdup(name);
46
pat->patspec = g_pattern_spec_new(name);
51
static GList *remove_pattern_from_list(GList *list, const gchar *pattern) {
52
GList *tmplist = g_list_first(list);
54
Tfilterpattern *pat = (Tfilterpattern *)tmplist->data;
55
if (strcmp(pat->pattern, pattern)==0) {
57
g_pattern_spec_free(pat->patspec);
59
list = g_list_delete_link(list, tmplist);
63
tmplist = g_list_next(tmplist);
68
static gboolean filename_match(Tfilter *filter, const gchar *string) {
70
guint len = strlen(string);
71
const gchar *reversed = g_utf8_strreverse(string,-1);
72
for (tmplist = g_list_first(filter->patterns) ; tmplist ; tmplist = g_list_next(tmplist)) {
73
if (g_pattern_match(((Tfilterpattern *)tmplist->data)->patspec,len,string,reversed))
79
gboolean file_visible_in_filter(Tfilter *filter, const gchar *mime_type, const gchar *filename) {
82
return (((mime_type && GPOINTER_TO_INT(g_hash_table_lookup(filter->filetypes,mime_type)))
83
|| (filter->patterns && filename && filename_match(filter, filename))) ? filter->mode : !filter->mode);
88
Tfilter *find_filter_by_name(const gchar *name) {
89
GList *tmplist = g_list_first(main_v->filefilters);
91
Tfilter *filter = (Tfilter *)tmplist->data;
92
if (strcmp(filter->name, name)==0) {
95
tmplist = g_list_next(tmplist);
100
static GHashTable *hashtable_from_string(const gchar *mimetypes) {
101
GHashTable *filetypes = g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
103
gchar **types = g_strsplit(mimetypes, ":", 127);
104
gchar **type = types;
106
g_hash_table_replace(filetypes, g_strdup(*type), GINT_TO_POINTER(1));
114
static GList *patternlist_from_string(const gchar *patterns) {
117
gchar **pats = g_strsplit(patterns, ":", 127);
118
gchar **pattern = pats;
121
if (strlen(*pattern)>0) {
122
list = g_list_append(list, new_pattern(*pattern));
132
static Tfilter *new_filter(const gchar *name, const gchar *mode, const gchar *mimetypes, const gchar *patterns) {
133
Tfilter *filter = g_new(Tfilter,1);
134
filter->name = g_strdup(name);
135
filter->refcount = 1;
136
filter->mode = atoi(mode);
137
filter->filetypes = hashtable_from_string(mimetypes);
138
filter->patterns = patternlist_from_string(patterns);
142
static void filter_destroy(Tfilter *filter) {
144
g_free(filter->name);
145
g_hash_table_destroy(filter->filetypes);
146
for (tmplist=g_list_first(filter->patterns);tmplist;tmplist=g_list_next(tmplist)) {
147
Tfilterpattern *pat = tmplist->data;
148
g_pattern_spec_free(pat->patspec);
149
g_free(pat->pattern);
151
g_list_free(filter->patterns);
155
void filter_unref(Tfilter *filter) {
157
if (filter->refcount <= 0) {
158
filter_destroy(filter);
162
void filter_ref(Tfilter *filter) {
166
void filter_delete(Tfilter *filter) {
169
/* delete from config */
170
tmplist = g_list_first(main_v->globses.filefilters);
172
strarr = (gchar **) tmplist->data;
173
if (strarr && strcmp(strarr[0],filter->name)==0) {
174
/* config string found */
175
main_v->globses.filefilters = g_list_remove(main_v->globses.filefilters, strarr);
178
tmplist = g_list_next(tmplist);
180
/* delete from current list of filters, but we need to
181
make sure no window is actually using this filter! */
182
tmplist = g_list_first(main_v->bfwinlist);
184
Tbfwin *bfwin = BFWIN(tmplist->data);
185
/* test if the filter is named in the current session */
186
if (bfwin->session->last_filefilter && strcmp(bfwin->session->last_filefilter,filter->name)==0) {
187
g_free(bfwin->session->last_filefilter);
188
bfwin->session->last_filefilter = NULL;
191
fb2_unset_filter(bfwin, filter);
193
tmplist = g_list_next(tmplist);
195
/* now really remove the filter */
196
main_v->filefilters = g_list_remove(main_v->filefilters, filter);
197
filter_unref(filter);
201
* WARNING: these filter are also used in the filechooser dialog (file->open in the menu)
203
void filters_rebuild(void) {
205
/* free any existing filters */
206
tmplist = g_list_first(main_v->filefilters);
208
filter_unref(tmplist->data);
209
tmplist = g_list_next(tmplist);
211
g_list_free(main_v->filefilters);
212
main_v->filefilters = NULL;
214
/* build a list of filters */
215
main_v->filefilters = g_list_prepend(NULL, new_filter(_("All files"), "0", NULL, NULL));
216
tmplist = g_list_first(main_v->globses.filefilters);
218
gchar **strarr = (gchar **) tmplist->data;
219
Tfilter *filter = new_filter(strarr[0], strarr[1], strarr[2], strarr[3]);
220
main_v->filefilters = g_list_prepend(main_v->filefilters, filter);
221
tmplist = g_list_next(tmplist);
225
static void restore_filter_from_config(Tfilter *filter, const gchar *origname) {
230
if (!origname) return;
232
tmplist = g_list_first(main_v->globses.filefilters);
234
strarr = (gchar **) tmplist->data;
235
if (strarr && strcmp(strarr[0],origname)==0) {
236
/* config string found */
239
tmplist = g_list_next(tmplist);
243
if (strcmp(filter->name,origname)!=0) {
244
g_free(filter->name);
245
filter->name = g_strdup(origname);
247
mode = atoi(strarr[1]);
248
if (mode != filter->mode) {
252
g_hash_table_destroy(filter->filetypes);
253
filter->filetypes = hashtable_from_string(strarr[2]);
254
filter->patterns = patternlist_from_string(strarr[3]);
257
static void hashtable_to_string_lcb(gpointer key, gpointer value, gpointer data) {
258
g_string_append((GString *)data, (gchar *)key);
259
g_string_append_c((GString *)data,':');
262
static void apply_filter_to_config(Tfilter *filter, const gchar *origname) {
264
gchar **strarr = NULL;
267
/* find the config string, if it existed before */
268
tmplist = g_list_first(main_v->globses.filefilters);
270
strarr = (gchar **) tmplist->data;
271
if (strarr && strcmp(strarr[0],origname)==0) {
272
/* config string found */
275
tmplist = g_list_next(tmplist);
278
if (strarr == NULL) {
279
DEBUG_MSG("apply_filter_to_config, prepending new entry in config list\n");
280
/* no config string with this name, */
281
strarr = (gchar **)g_new0(gpointer, 5);
282
main_v->globses.filefilters = g_list_prepend(main_v->globses.filefilters, strarr);
284
if (origname == NULL) {
285
main_v->filefilters = g_list_prepend(main_v->filefilters, filter);
287
/* the config string has three entries: the name, inverse filtering, filetypes */
288
if (strarr[0]) g_free(strarr[0]);
289
strarr[0] = g_strdup(filter->name);
290
if (strarr[1]) g_free(strarr[1]);
291
strarr[1] = g_strdup_printf("%d",filter->mode);
292
gstr = g_string_new("");
293
g_hash_table_foreach(filter->filetypes,hashtable_to_string_lcb,gstr);
294
if (strarr[2]) g_free(strarr[2]);
295
strarr[2] = g_string_free(gstr,FALSE);
296
if (strarr[3]) g_free(strarr[3]);
297
gstr = g_string_new("");
300
for (tmplist = g_list_first(filter->patterns) ; tmplist ; tmplist = g_list_next(tmplist)) {
301
Tfilterpattern *pat = tmplist->data;
302
g_string_append(gstr, pat->pattern);
303
g_string_append_c(gstr,':');
305
strarr[3] = g_string_free(gstr,FALSE);
310
the filefilter gui has one listmodel with all filetypes currently known in bluefish,
311
and two filtermodels, in_model shows all types in the filter, out_model shows all other filetypes
318
GtkListStore *lstore;
319
GtkTreeModel *in_model; /* shows all the types IN the filter */
320
GtkTreeModel *out_model; /* shows all types OUTside the filter */
323
GtkWidget *nameentry;
324
GtkWidget *inversecheck;
331
static void filefiltergui_destroy_lcb(GtkWidget *widget, Tfilefiltergui *ffg) {
332
g_free(ffg->origname);
333
g_object_unref(ffg->lstore);
334
window_destroy(ffg->win);
336
DEBUG_MSG("filefiltergui_destroy_lcb, done\n");
339
static void filefiltergui_cancel_clicked(GtkWidget *widget, Tfilefiltergui *ffg) {
340
restore_filter_from_config(ffg->curfilter, ffg->origname);
341
filefiltergui_destroy_lcb(widget, ffg);
344
static void filefiltergui_ok_clicked(GtkWidget *widget, Tfilefiltergui *ffg) {
345
g_free(ffg->curfilter->name);
346
ffg->curfilter->name = gtk_editable_get_chars(GTK_EDITABLE(ffg->nameentry),0,-1);
347
ffg->curfilter->mode = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ffg->inversecheck));
348
DEBUG_MSG("filefiltergui_ok_clicked, filter '%s' has mode %d\n",ffg->curfilter->name,ffg->curfilter->mode);
349
apply_filter_to_config(ffg->curfilter, ffg->origname);
350
filefiltergui_destroy_lcb(widget, ffg);
353
static gboolean filefiltergui_infilter_visiblefunc(GtkTreeModel *model,GtkTreeIter *iter,gpointer data) {
354
Tfilefiltergui *ffg = data;
355
gboolean retval = FALSE;
358
gtk_tree_model_get(model, iter, 0, &name, 2, &type, -1);
361
retval = (g_hash_table_lookup(ffg->curfilter->filetypes, name) != NULL);
362
} else { /* type == 1*/
364
for (tmplist = g_list_first(ffg->curfilter->patterns) ; tmplist ; tmplist = g_list_next(tmplist)) {
365
Tfilterpattern *pat = tmplist->data;
366
if (strcmp(pat->pattern, name)==0) {
376
static gboolean filefiltergui_outfilter_visiblefunc(GtkTreeModel *model,GtkTreeIter *iter,gpointer data) {
377
return !filefiltergui_infilter_visiblefunc(model,iter,data);
380
static void filefiltergui_add_filetypes(gpointer key,gpointer value,gpointer data) {
381
Tfilefiltergui *ffg = data;
383
if (strlen(key)>0 && g_hash_table_lookup(main_v->filetypetable, key) == NULL) {
385
gtk_list_store_prepend(ffg->lstore,&it);
386
gtk_list_store_set(ffg->lstore,&it,0,key,2,0,-1);
390
static void filefiltergui_2right_clicked(GtkWidget *widget, Tfilefiltergui *ffg) {
392
GtkTreeSelection *select;
395
/* get the selection */
396
select = gtk_tree_view_get_selection(GTK_TREE_VIEW(ffg->out_view));
397
if (gtk_tree_selection_get_selected(select, &model, &iter)) {
400
gtk_tree_model_get(model, &iter, 0, &name, 2, &type, -1);
401
/* add the selection to the filter */
402
DEBUG_MSG("filefiltergui_2right_clicked, adding %s\n",name);
404
g_hash_table_replace(ffg->curfilter->filetypes, name, GINT_TO_POINTER(1));
406
ffg->curfilter->patterns = g_list_append(ffg->curfilter->patterns, new_pattern(name));
409
DEBUG_MSG("filefiltergui_2right_clicked, refilter\n");
411
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(ffg->in_model));
412
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(ffg->out_model));
414
DEBUG_MSG("filefiltergui_2right_clicked, nothing selected\n");
418
static void filefiltergui_2left_clicked(GtkWidget *widget, Tfilefiltergui *ffg) {
420
GtkTreeSelection *select;
423
/* get the selection */
424
select = gtk_tree_view_get_selection(GTK_TREE_VIEW (ffg->in_view));
425
if (gtk_tree_selection_get_selected(select, &model, &iter)) {
428
gtk_tree_model_get(model, &iter, 0, &name,2,&type, -1);
429
/* add the selection to the filter */
431
DEBUG_MSG("filefiltergui_2left_clicked, removing %s\n",name);
432
g_hash_table_remove(ffg->curfilter->filetypes, name);
434
/* remove from the list of the filter */
435
ffg->curfilter->patterns = remove_pattern_from_list(ffg->curfilter->patterns, name);
438
DEBUG_MSG("filefiltergui_2left_clicked, refilter\n");
440
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(ffg->in_model));
441
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(ffg->out_model));
443
DEBUG_MSG("filefiltergui_2left_clicked, nothing selected\n");
447
static void filefiltergui_addpattern_clicked_lcb(GtkWidget *widget, Tfilefiltergui *ffg) {
450
pat = g_new(Tfilterpattern,1);
451
pat->pattern = g_strdup(gtk_entry_get_text(GTK_ENTRY(ffg->patentry)));
452
pat->patspec = g_pattern_spec_new(pat->pattern);
453
ffg->curfilter->patterns = g_list_append(ffg->curfilter->patterns, pat);
454
gtk_list_store_prepend(ffg->lstore,&it);
455
gtk_list_store_set(ffg->lstore,&it,0,pat->pattern,1,NULL,2,1,-1);
458
void filefilter_gui(Tfilter *filter) {
459
GtkCellRenderer *renderer;
460
GtkTreeViewColumn *column;
461
GList *tmplist, *reglist;
462
GtkWidget *table,*hbox,*but,*vbox,*scrolwin;
464
GList *mimelist=NULL;
465
gchar *last_mime=NULL;
468
Tfilefiltergui *ffg = g_new0(Tfilefiltergui,1);
469
ffg->curfilter = filter;
471
ffg->origname = g_strdup(filter->name);
474
ffg->curfilter = g_new0(Tfilter,1);
475
ffg->curfilter->name = g_strdup(_("New filter"));
476
ffg->curfilter->filetypes = g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
479
DEBUG_MSG("filefilter_gui, editing filter %p\n",ffg->curfilter);
480
ffg->win = window_full2(_("Edit filter"), GTK_WIN_POS_MOUSE, 10, G_CALLBACK(filefiltergui_destroy_lcb),ffg, TRUE, NULL);
481
gtk_window_set_default_size(GTK_WINDOW(ffg->win),400,400);
482
ffg->lstore = gtk_list_store_new(3, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
484
/* fill the list model from the currently known filetypes */
485
reglist = g_content_types_get_registered();
488
tmplist = g_list_first(reglist);
490
mimelist = g_list_prepend(mimelist, g_content_type_get_mime_type(tmplist->data));
491
tmplist = g_list_next(tmplist);
493
mimelist = g_list_reverse(g_list_sort(mimelist, (GCompareFunc) g_strcmp0));
494
tmplist = g_list_first(mimelist);
496
if (!last_mime || g_strcmp0(last_mime, tmplist->data)!=0) {
498
last_mime=tmplist->data;
499
if (MIME_ISDIR(tmplist->data)) {
500
gtk_list_store_prepend(ffg->lstore,&it);
501
gtk_list_store_set(ffg->lstore,&it,0,tmplist->data,2,0, -1);
504
tmplist = g_list_next(tmplist);
506
/* GList *winlist = NULL;
510
mimetype = g_content_type_get_mime_type(reglist->data);
511
if ((llen = g_list_length(winlist))) {
512
tmplist = g_list_copy(winlist);
513
for (lpos = 0; llen != -1 && lpos < llen; lpos++) {
514
if (!g_strcmp0(mimetype, tmplist->data))
517
tmplist = g_list_next(tmplist);
519
g_list_free(tmplist);
522
winlist = g_list_append(winlist, mimetype);
523
reglist = g_list_next(reglist);
525
tmplist = g_list_first(g_list_reverse(g_list_sort(winlist, (GCompareFunc) g_strcmp0)));*/
526
free_stringlist(mimelist);
528
tmplist = g_list_first(g_list_sort(reglist, (GCompareFunc) g_strcmp0));
531
if (MIME_ISDIR(tmplist->data)) {
532
gtk_list_store_prepend(ffg->lstore,&it);
533
gtk_list_store_set(ffg->lstore,&it,0,tmplist->data,2,0, -1);
535
tmplist = g_list_next(tmplist);
538
g_list_free(reglist);
539
/* make sure that all filetypes that exist in the current filter are shown */
540
/*g_hash_table_foreach(ffg->curfilter->filetypes,filefiltergui_add_filetypes,ffg);*/
541
/* add the patterns from the current filter */
542
tmplist = g_list_first(ffg->curfilter->patterns);
545
Tfilterpattern *pat = (Tfilterpattern *)tmplist->data;
546
gtk_list_store_prepend(ffg->lstore,&it);
547
gtk_list_store_set(ffg->lstore,&it,0,pat->pattern,1,NULL,2,1,-1);
548
tmplist = g_list_next(tmplist);
551
ffg->in_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(ffg->lstore), NULL);
552
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(ffg->in_model),filefiltergui_infilter_visiblefunc,ffg,NULL);
553
ffg->out_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(ffg->lstore), NULL);
554
gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(ffg->out_model),filefiltergui_outfilter_visiblefunc,ffg,NULL);
556
table = gtk_table_new(5,4,FALSE);
557
gtk_table_set_row_spacings(GTK_TABLE(table), 5);
559
ffg->nameentry = entry_with_text(ffg->curfilter->name, 50);
560
gtk_table_attach(GTK_TABLE(table),ffg->nameentry,0,1,0,1,GTK_FILL,GTK_FILL,0,0);
562
ffg->inversecheck = checkbut_with_value(_("Hide files that match the filter"), !ffg->curfilter->mode);
563
gtk_table_attach(GTK_TABLE(table),ffg->inversecheck,0,1,1,2,GTK_FILL,GTK_FILL,0,0);
565
ffg->patentry = entry_with_text("*.*", 20);
566
gtk_table_attach(GTK_TABLE(table),ffg->patentry,2,3,1,2,GTK_FILL,GTK_FILL,0,0);
567
but = gtk_button_new_with_label(_("Add pattern"));
568
g_signal_connect(but,"clicked",G_CALLBACK(filefiltergui_addpattern_clicked_lcb),ffg);
569
gtk_table_attach(GTK_TABLE(table),but,3,4,1,2,GTK_FILL,GTK_FILL,0,0);
571
ffg->in_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ffg->in_model));
572
renderer = gtk_cell_renderer_text_new();
573
column = gtk_tree_view_column_new_with_attributes(_("Mime type"),renderer,"text", 0,NULL);
574
gtk_tree_view_append_column (GTK_TREE_VIEW(ffg->in_view), column);
575
renderer = gtk_cell_renderer_pixbuf_new();
576
column = gtk_tree_view_column_new_with_attributes(_("Icon"),renderer,"pixbuf", 1,NULL);
577
gtk_tree_view_append_column (GTK_TREE_VIEW(ffg->in_view), column);
578
scrolwin = gtk_scrolled_window_new(NULL, NULL);
579
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
580
gtk_container_add(GTK_CONTAINER(scrolwin),ffg->in_view);
581
gtk_table_attach_defaults(GTK_TABLE(table),scrolwin,2,4,2,3);
583
ffg->out_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ffg->out_model));
584
renderer = gtk_cell_renderer_text_new();
585
column = gtk_tree_view_column_new_with_attributes(_("Mime type"),renderer,"text", 0,NULL);
586
gtk_tree_view_append_column (GTK_TREE_VIEW(ffg->out_view), column);
587
renderer = gtk_cell_renderer_pixbuf_new();
588
column = gtk_tree_view_column_new_with_attributes(_("Icon"),renderer,"pixbuf", 1,NULL);
589
gtk_tree_view_append_column (GTK_TREE_VIEW(ffg->out_view), column);
590
scrolwin = gtk_scrolled_window_new(NULL, NULL);
591
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
592
gtk_container_add(GTK_CONTAINER(scrolwin),ffg->out_view);
593
gtk_table_attach_defaults(GTK_TABLE(table),scrolwin,0,1,2,3);
595
vbox = gtk_vbox_new(TRUE,5);
596
but = gtk_button_new_with_label("->");
597
g_signal_connect(but,"clicked",G_CALLBACK(filefiltergui_2right_clicked),ffg);
598
gtk_box_pack_start(GTK_BOX(vbox),but,TRUE,TRUE,0);
599
but = gtk_button_new_with_label("<-");
600
g_signal_connect(but,"clicked",G_CALLBACK(filefiltergui_2left_clicked),ffg);
601
gtk_box_pack_start(GTK_BOX(vbox),but,TRUE,TRUE,0);
602
gtk_table_attach(GTK_TABLE(table),vbox,1,2,2,3,GTK_EXPAND|GTK_FILL,GTK_EXPAND,5,5);
604
hbox = gtk_hbutton_box_new();
605
gtk_hbutton_box_set_layout_default(GTK_BUTTONBOX_END);
606
gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbox), 12);
607
but = bf_stock_cancel_button(G_CALLBACK(filefiltergui_cancel_clicked), ffg);
608
gtk_box_pack_start(GTK_BOX(hbox),but, FALSE, FALSE, 0);
609
but = bf_stock_ok_button(G_CALLBACK(filefiltergui_ok_clicked), ffg);
610
gtk_box_pack_start(GTK_BOX(hbox),but, FALSE, FALSE, 0);
612
gtk_table_attach(GTK_TABLE(table), hbox, 0, 3, 4, 5,GTK_FILL,GTK_FILL,0,0);
614
gtk_container_add(GTK_CONTAINER(ffg->win), table);
615
gtk_widget_show_all(ffg->win);