2 * Copyright 2005 Timo Hirvonen
13 #include <ui_curses.h>
18 struct window
*filters_win
;
19 struct searchable
*filters_searchable
;
20 LIST_HEAD(filters_head
);
22 static const char *recursive_filter
;
24 static inline void filter_entry_to_iter(struct filter_entry
*e
, struct iter
*iter
)
26 iter
->data0
= &filters_head
;
31 static GENERIC_ITER_PREV(filters_get_prev
, struct filter_entry
, node
)
32 static GENERIC_ITER_NEXT(filters_get_next
, struct filter_entry
, node
)
34 static int filters_search_get_current(void *data
, struct iter
*iter
)
36 return window_get_sel(filters_win
, iter
);
39 static int filters_search_matches(void *data
, struct iter
*iter
, const char *text
)
41 char **words
= get_words(text
);
44 if (words
[0] != NULL
) {
45 struct filter_entry
*e
;
48 e
= iter_to_filter_entry(iter
);
50 if (words
[i
] == NULL
) {
51 window_set_sel(filters_win
, iter
);
55 if (u_strcasestr(e
->name
, words
[i
]) == NULL
)
59 free_str_array(words
);
63 static const struct searchable_ops filters_search_ops
= {
64 .get_prev
= filters_get_prev
,
65 .get_next
= filters_get_next
,
66 .get_current
= filters_search_get_current
,
67 .matches
= filters_search_matches
70 static void free_filter(struct filter_entry
*e
)
77 static struct filter_entry
*find_filter(const char *name
)
79 struct filter_entry
*e
;
81 list_for_each_entry(e
, &filters_head
, node
) {
82 if (strcmp(e
->name
, name
) == 0)
88 static const char *get_filter(const char *name
)
90 struct filter_entry
*e
= find_filter(name
);
94 recursive_filter
= e
->name
;
103 void filters_activate(void)
105 struct filter_entry
*f
;
106 struct expr
*e
, *expr
= NULL
;
108 /* mark visited and AND together all selected filters
109 * mark any other filters unvisited */
110 list_for_each_entry(f
, &filters_head
, node
) {
116 e
= expr_parse(f
->filter
);
118 error_msg("error parsing filter %s: %s", f
->name
, expr_error());
127 struct expr
*and = xnew(struct expr
, 1);
129 and->type
= EXPR_AND
;
139 recursive_filter
= NULL
;
140 if (expr
&& expr_check_leaves(&expr
, get_filter
)) {
141 if (recursive_filter
) {
142 error_msg("recursion detected in filter %s", recursive_filter
);
144 error_msg("error parsing filter: %s", expr_error());
150 /* update active flag */
151 list_for_each_entry(f
, &filters_head
, node
) {
156 lib_set_filter(expr
);
157 filters_win
->changed
= 1;
160 static int for_each_name(const char *str
, int (*cb
)(const char *name
))
168 while (str
[s
] == ' ')
171 while (str
[e
] && str
[e
] != ' ')
177 if (len
>= sizeof(buf
)) {
178 error_msg("filter name too long");
182 memcpy(buf
, str
+ s
, len
);
190 static int ensure_filter_name(const char *name
)
192 if (find_filter(name
) == NULL
) {
193 error_msg("no such filter %s", name
);
199 static int select_filter(const char *name
)
201 struct filter_entry
*e
= find_filter(name
);
207 void filters_activate_names(const char *str
)
209 struct filter_entry
*f
;
211 /* first validate all filter names */
212 if (str
&& for_each_name(str
, ensure_filter_name
))
215 /* mark all filters unselected */
216 list_for_each_entry(f
, &filters_head
, node
)
219 /* select the filters */
221 for_each_name(str
, select_filter
);
223 /* activate selected */
227 void filters_toggle_filter(void)
231 if (window_get_sel(filters_win
, &iter
)) {
232 struct filter_entry
*e
;
234 e
= iter_to_filter_entry(&iter
);
236 filters_win
->changed
= 1;
240 void filters_delete_filter(void)
244 if (window_get_sel(filters_win
, &iter
)) {
245 struct filter_entry
*e
;
247 e
= iter_to_filter_entry(&iter
);
248 if (yes_no_query("Delete filter '%s'? [y/N]", e
->name
)) {
249 window_row_vanishes(filters_win
, &iter
);
256 static int validate_filter_name(const char *name
)
260 for (i
= 0; name
[i
]; i
++) {
261 if (isalnum(name
[i
]))
263 if (name
[i
] == '_' || name
[i
] == '-')
270 static void do_filters_set_filter(const char *keyval
, int active
)
272 const char *eq
= strchr(keyval
, '=');
275 struct filter_entry
*new;
276 struct list_head
*item
;
280 error_msg("invalid argument ('key=value' expected)");
283 key
= xstrndup(keyval
, eq
- keyval
);
284 val
= xstrdup(eq
+ 1);
285 if (!validate_filter_name(key
)) {
287 error_msg("invalid filter name (can only contain 'a-zA-Z0-9_-' characters)");
292 expr
= expr_parse(val
);
295 error_msg("error parsing filter %s: %s", val
, expr_error());
302 new = xnew(struct filter_entry
, 1);
305 new->active
= active
;
306 new->selected
= active
;
308 /* add or replace filter */
309 list_for_each(item
, &filters_head
) {
310 struct filter_entry
*e
= container_of(item
, struct filter_entry
, node
);
311 int res
= strcmp(key
, e
->name
);
319 if (ui_initialized
) {
320 filter_entry_to_iter(e
, &iter
);
321 window_row_vanishes(filters_win
, &iter
);
329 /* add before item */
330 list_add_tail(&new->node
, item
);
332 window_changed(filters_win
);
335 void filters_init(void)
339 filters_win
= window_new(filters_get_prev
, filters_get_next
);
340 window_set_contents(filters_win
, &filters_head
);
341 window_changed(filters_win
);
343 iter
.data0
= &filters_head
;
346 filters_searchable
= searchable_new(NULL
, &iter
, &filters_search_ops
);
349 void filters_exit(void)
351 searchable_free(filters_searchable
);
352 window_free(filters_win
);
355 void filters_set_filter(const char *keyval
)
357 do_filters_set_filter(keyval
, 0);
360 struct expr
*parse_filter(const char *val
)
362 struct expr
*e
= NULL
;
363 struct filter_entry
*f
;
368 error_msg("error parsing filter %s: %s", val
, expr_error());
373 /* mark all unvisited so that we can check recursion */
374 list_for_each_entry(f
, &filters_head
, node
)
377 recursive_filter
= NULL
;
378 if (e
&& expr_check_leaves(&e
, get_filter
)) {
379 if (recursive_filter
) {
380 error_msg("recursion detected in filter %s", recursive_filter
);
382 error_msg("error parsing filter: %s", expr_error());
390 void filters_set_anonymous(const char *val
)
392 struct filter_entry
*f
;
393 struct expr
*e
= NULL
;
396 e
= parse_filter(val
);
401 /* deactive all filters */
402 list_for_each_entry(f
, &filters_head
, node
)
407 filters_win
->changed
= 1;