ui_filters.c 17.7 KB
Newer Older
winckel's avatar
winckel committed
1
2
3
4
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>

winckel's avatar
winckel committed
5
6
7
#include <libxml/parser.h>
#include <libxml/tree.h>

winckel's avatar
winckel committed
8
#include "ui_callbacks.h"
9
#include "ui_main_screen.h"
winckel's avatar
winckel committed
10
#include "ui_filters.h"
winckel's avatar
winckel committed
11
12
#include "ui_tree_view.h"
#include "ui_notif_dlg.h"
winckel's avatar
winckel committed
13
14
15
#include "rc.h"

const uint32_t FILTER_ALLOC_NUMBER = 100;
winckel's avatar
winckel committed
16
const uint32_t FILTER_ID_UNDEFINED = ~0;
17

18
const char * const COLOR_WHITE = "#ffffff";
19
20
21
22
23
const char * const COLOR_DARK_GREY = "#585858";

#define ENABLED_NAME    "enabled"
#define FOREGROUND_NAME "foreground_color"
#define BACKGROUND_NAME "background_color"
winckel's avatar
winckel committed
24
25
26

ui_filters_t ui_filters;

winckel's avatar
winckel committed
27
static int ui_init_filter(ui_filter_t *filter, int reset, int clear_ids, char *name)
winckel's avatar
winckel committed
28
29
30
{
    if (filter->items == NULL)
    {
winckel's avatar
winckel committed
31
32
        filter->name = name;

winckel's avatar
winckel committed
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        /* Allocate some filter entries */
        filter->items = malloc (FILTER_ALLOC_NUMBER * sizeof(ui_filter_item_t));
        filter->allocated = FILTER_ALLOC_NUMBER;
    }
    if (reset)
    {
        /* Reset number of used filter entries */
        filter->used = 0;
    }
    else
    {
        if (clear_ids)
        {
            /* Clear entries IDs */
            int item;

            for (item = 0; item < filter->used; item++)
            {
winckel's avatar
winckel committed
51
                filter->items[item].id = FILTER_ID_UNDEFINED;
winckel's avatar
winckel committed
52
53
54
55
56
57
58
59
60
            }
        }
    }

    return (RC_OK);
}

int ui_init_filters(int reset, int clear_ids)
{
winckel's avatar
winckel committed
61
62
63
    ui_init_filter (&ui_filters.messages, reset, clear_ids, "messages");
    ui_init_filter (&ui_filters.origin_tasks, reset, clear_ids, "origin_tasks");
    ui_init_filter (&ui_filters.destination_tasks, reset, clear_ids, "destination_tasks");
winckel's avatar
winckel committed
64
    ui_init_filter (&ui_filters.instances, reset, clear_ids, "instances");
winckel's avatar
winckel committed
65

66
    ui_destroy_filter_menus ();
winckel's avatar
winckel committed
67

winckel's avatar
winckel committed
68
69
70
    return (RC_OK);
}

71
72
73
74
75
76
77
78
79
80
81
82
gboolean ui_filters_enable(gboolean enabled)
{
    gboolean changed = ui_filters.filters_enabled != enabled;

    if (changed)
    {
        ui_filters.filters_enabled = enabled;
    }

    return changed;
}

83
static int ui_filters_search_name(ui_filter_t *filter, const char *name)
winckel's avatar
winckel committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
{
    int item;

    for (item = 0; item < filter->used; item++)
    {
        if (strncmp (name, filter->items[item].name, SIGNAL_NAME_LENGTH) == 0)
        {
            return (item);
        }
    }

    return (item);
}

98
int ui_filters_search_id(ui_filter_t *filter, uint32_t value)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{
    int item;

    for (item = 0; item < filter->used; item++)
    {
        if (filter->items[item].id == value)
        {
            return (item);
        }
    }

    return (item);
}

winckel's avatar
winckel committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
static void ui_filter_set_enabled(uint8_t *enabled, ui_entry_enabled_e entry_enabled, gboolean new)
{
    if (entry_enabled == ENTRY_ENABLED_UNDEFINED)
    {
        if (new)
        {
            *enabled = TRUE;
        }
    }
    else
    {
        if (entry_enabled == ENTRY_ENABLED_FALSE)
        {
            *enabled = FALSE;
        }
        else
        {
            *enabled = TRUE;
        }
    }
}

135
static int ui_filter_add(ui_filter_t *filter, uint32_t value, const char *name, ui_entry_enabled_e entry_enabled,
136
                         const char *foreground, const char *background)
winckel's avatar
winckel committed
137
{
138
    int item = ui_filters_search_name (filter, name);
winckel's avatar
winckel committed
139
140
141
142
143
144
145
146

    if (item >= filter->allocated)
    {
        /* Increase number of filter entries */
        filter->items = realloc (filter->items, (filter->allocated + FILTER_ALLOC_NUMBER) * sizeof(ui_filter_item_t));
        filter->allocated += FILTER_ALLOC_NUMBER;
    }

winckel's avatar
winckel committed
147
148
149
150
    if (value != FILTER_ID_UNDEFINED)
    {
        filter->items[item].id = value;
    }
winckel's avatar
winckel committed
151
152
153
    if (item >= filter->used)
    {
        /* New entry */
154
        strncpy (filter->items[item].name, name, SIGNAL_NAME_LENGTH);
winckel's avatar
winckel committed
155
        ui_filter_set_enabled (&filter->items[item].enabled, entry_enabled, TRUE);
156
157
        strncpy (filter->items[item].foreground, foreground != NULL ? foreground : COLOR_DARK_GREY, COLOR_SIZE);
        strncpy (filter->items[item].background, background != NULL ? background : COLOR_WHITE, COLOR_SIZE);
winckel's avatar
winckel committed
158
159
160

        filter->used++;
    }
winckel's avatar
winckel committed
161
162
163
    else
    {
        ui_filter_set_enabled (&filter->items[item].enabled, entry_enabled, FALSE);
164
165
166
167
        if (foreground != NULL)
        {
            strncpy (filter->items[item].foreground, foreground, COLOR_SIZE);
        }
168
169
        if (background != NULL)
        {
170
            strncpy (filter->items[item].background, background, COLOR_SIZE);
171
        }
winckel's avatar
winckel committed
172
    }
winckel's avatar
winckel committed
173

winckel's avatar
winckel committed
174
175
    g_debug("filter \"%s\" add %d \"%s\" %d", filter->name, value, name, entry_enabled);

winckel's avatar
winckel committed
176
177
178
    return (item);
}

179
void ui_filters_add(ui_filter_e filter, uint32_t value, const char *name, ui_entry_enabled_e entry_enabled,
180
                    const char *foreground, const char *background)
winckel's avatar
winckel committed
181
182
183
184
{
    switch (filter)
    {
        case FILTER_MESSAGES:
185
            ui_filter_add (&ui_filters.messages, value, name, entry_enabled, foreground, background);
winckel's avatar
winckel committed
186
187
188
            break;

        case FILTER_ORIGIN_TASKS:
189
            ui_filter_add (&ui_filters.origin_tasks, value, name, entry_enabled, foreground, background);
winckel's avatar
winckel committed
190
191
192
            break;

        case FILTER_DESTINATION_TASKS:
193
            ui_filter_add (&ui_filters.destination_tasks, value, name, entry_enabled, foreground, background);
winckel's avatar
winckel committed
194
195
            break;

winckel's avatar
winckel committed
196
        case FILTER_INSTANCES:
197
            ui_filter_add (&ui_filters.instances, value, name, entry_enabled, foreground, background);
winckel's avatar
winckel committed
198
199
            break;

winckel's avatar
winckel committed
200
        default:
201
            g_warning("unknown filter type %d", filter);
winckel's avatar
winckel committed
202
203
204
205
            break;
    }
}

206
static gboolean ui_item_enabled(ui_filter_t *filter, const uint32_t value)
207
{
208
    int item;
209

210
    if (value != (uint32_t) ~0)
211
    {
212
        item = ui_filters_search_id (filter, value);
213

214
215
216
217
218
        if (item < filter->used)
        {
            return (filter->items[item].enabled ? TRUE : FALSE);
        }
    }
219
220
221
    return (FALSE);
}

222
223
gboolean ui_filters_message_enabled(const uint32_t message, const uint32_t origin_task, const uint32_t destination_task,
                                    const uint32_t instance)
224
225
226
{
    gboolean result;

227
228
229
    result = (ui_item_enabled (&ui_filters.messages, message) && ui_item_enabled (&ui_filters.origin_tasks, origin_task)
            && ui_item_enabled (&ui_filters.destination_tasks, destination_task)
            && ui_item_enabled (&ui_filters.instances, instance));
230
231
232
233

    return result;
}

winckel's avatar
winckel committed
234
235
236
237
238
239
240
241
242
243
244
245
246
247
static ui_filter_e ui_filter_from_name(const char *name)
{
    if (strcmp (name, ui_filters.messages.name) == 0)
    {
        return FILTER_MESSAGES;
    }
    if (strcmp (name, ui_filters.origin_tasks.name) == 0)
    {
        return FILTER_ORIGIN_TASKS;
    }
    if (strcmp (name, ui_filters.destination_tasks.name) == 0)
    {
        return FILTER_DESTINATION_TASKS;
    }
winckel's avatar
winckel committed
248
249
250
251
    if (strcmp (name, ui_filters.instances.name) == 0)
    {
        return FILTER_INSTANCES;
    }
winckel's avatar
winckel committed
252
253
254
255
256
257
258
259
260
    return FILTER_UNKNOWN;
}

static int xml_parse_filters(xmlDocPtr doc)
{
    xmlNode *root_element = NULL;
    xmlNode *filter_node = NULL;
    xmlNode *cur_node = NULL;
    ui_filter_e filter;
261
    guint filters_entries = 0;
262
    int ret = RC_FAIL;
winckel's avatar
winckel committed
263
264
265
266
267
268
269

    /* Get the root element node */
    root_element = xmlDocGetRootElement (doc);

    if (root_element != NULL)
    {
        /* Search for the start of filters definition */
270
        for (cur_node = root_element; (cur_node != NULL) && (strcmp ((char *) cur_node->name, "filters") != 0);
winckel's avatar
winckel committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
                cur_node = cur_node->next)
            ;

        if (cur_node != NULL)
        {
            /* Search for filter header */
            for (filter_node = cur_node->children; filter_node != NULL;)
            {
                /* Search for next element node */
                for (; (filter_node != NULL) && (filter_node->type != XML_ELEMENT_NODE); filter_node =
                        filter_node->next)
                    ;

                if (filter_node != NULL)
                {
                    filter = ui_filter_from_name ((const char*) filter_node->name);
287
                    g_debug("Found filter %s %d", filter_node->name, filter);
winckel's avatar
winckel committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

                    if (filter == FILTER_UNKNOWN)
                    {
                        g_warning("Unknown filter \"%s\"", filter_node->name);
                    }
                    else
                    {
                        /* Search for entries */
                        for (cur_node = filter_node->children; cur_node != NULL;)
                        {
                            /* Search for next element node */
                            for (; (cur_node != NULL) && (cur_node->type != XML_ELEMENT_NODE); cur_node =
                                    cur_node->next)
                                ;

                            if (cur_node != NULL)
                            {
305
306
307
                                xmlAttr *prop_node;
                                ui_entry_enabled_e enabled = ENTRY_ENABLED_UNDEFINED;
                                char *foreground = NULL;
308
309
                                char *background = NULL;

310
                                for (prop_node = cur_node->properties; prop_node != NULL; prop_node = prop_node->next)
311
                                {
312
313
314
315
316
317
318
319
320
321
322
323
324
325
                                    if (strcmp ((char *) prop_node->name, ENABLED_NAME) == 0)
                                    {
                                        enabled =
                                                prop_node->children->content[0] == '0' ?
                                                        ENTRY_ENABLED_FALSE : ENTRY_ENABLED_TRUE;
                                    }
                                    if (strcmp ((char *) prop_node->name, FOREGROUND_NAME) == 0)
                                    {
                                        foreground = (char *) prop_node->children->content;
                                    }
                                    if (strcmp ((char *) prop_node->name, BACKGROUND_NAME) == 0)
                                    {
                                        background = (char *) prop_node->children->content;
                                    }
326
327
                                }

winckel's avatar
winckel committed
328
                                g_debug("  Found entry %s %s", cur_node->name, cur_node->properties->children->content);
329
330
                                ui_filters_add (filter, FILTER_ID_UNDEFINED, (const char*) cur_node->name, enabled,
                                                foreground, background);
winckel's avatar
winckel committed
331

332
                                filters_entries++;
winckel's avatar
winckel committed
333
334
335
336
337
338
339
                                cur_node = cur_node->next;
                            }
                        }
                    }
                    filter_node = filter_node->next;
                }
            }
340

winckel's avatar
winckel committed
341
342
            /* Filters have changed destroy filter menus and update tree view */
            ui_destroy_filter_menus ();
343
344
345

            /* Reactivate filtering */
            gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON(ui_main_data.filters_enabled), TRUE);
winckel's avatar
winckel committed
346
347
348
349
350
351
            ui_tree_view_refilter ();
        }
    }
    /* Free the document */
    xmlFreeDoc (doc);

352
353
354
355
356
    if (filters_entries > 0)
    {
        ret = RC_OK;
    }

357
358
    g_message(
            "Parsed XML filters definition found %d entries (%d messages to display)", filters_entries, ui_tree_view_get_filtered_number());
winckel's avatar
winckel committed
359
360
361
362
363
364
365

    return ret;
}

int ui_filters_read(const char *file_name)
{
    xmlDocPtr doc; /* the resulting document tree */
366
    int ret;
winckel's avatar
winckel committed
367
368
369
370
371
372
373
374
375
376

    if (file_name == NULL)
    {
        g_warning("No name for filters file");
        return RC_FAIL;
    }

    doc = xmlReadFile (file_name, NULL, 0);
    if (doc == NULL)
    {
winckel's avatar
winckel committed
377
        ui_notification_dialog (GTK_MESSAGE_ERROR, "open filters", "Failed to parse file \"%s\"", file_name);
winckel's avatar
winckel committed
378
379
380
        return RC_FAIL;
    }

381
382
383
    ret = xml_parse_filters (doc);
    if (ret != RC_OK)
    {
384
385
        ui_notification_dialog (GTK_MESSAGE_WARNING, "open filters", "Found no filter definitions in \"%s\"",
                                file_name);
386
387
388
389
        return RC_FAIL;
    }

    return ret;
winckel's avatar
winckel committed
390
391
}

392
static void write_filter(FILE *filter_file, ui_filter_t *filter, gboolean save_colors)
winckel's avatar
winckel committed
393
394
395
396
397
398
{
    int item;

    fprintf (filter_file, "  <%s>\n", filter->name);
    for (item = 0; item < filter->used; item++)
    {
399
400
401
402
403
404
405
406
407
408
409
410
        if (save_colors)
        {
            fprintf (filter_file,
                     "    <%s " ENABLED_NAME "=\"%d\" " FOREGROUND_NAME "=\"%s\" " BACKGROUND_NAME "=\"%s\"/>\n",
                     filter->items[item].name, filter->items[item].enabled ? 1 : 0, filter->items[item].foreground,
                     filter->items[item].background);
        }
        else
        {
            fprintf (filter_file, "    <%s " ENABLED_NAME "=\"%d\"/>\n", filter->items[item].name,
                     filter->items[item].enabled ? 1 : 0);
        }
winckel's avatar
winckel committed
411
412
413
414
    }
    fprintf (filter_file, "  </%s>\n", filter->name);
}

winckel's avatar
winckel committed
415
int ui_filters_file_write(const char *file_name)
winckel's avatar
winckel committed
416
417
418
{
    FILE *filter_file;

winckel's avatar
winckel committed
419
420
421
422
423
    if (file_name == NULL)
    {
        g_warning("No name for filters file");
        return RC_FAIL;
    }
winckel's avatar
winckel committed
424

winckel's avatar
winckel committed
425
    filter_file = fopen (file_name, "w");
winckel's avatar
winckel committed
426
427
    if (filter_file == NULL)
    {
winckel's avatar
winckel committed
428
        g_warning("Failed to open file \"%s\": %s", file_name, g_strerror (errno));
winckel's avatar
winckel committed
429
430
431
        return RC_FAIL;
    }

432
433
    fprintf (filter_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
             "<filters>\n");
winckel's avatar
winckel committed
434

435
436
437
    write_filter (filter_file, &ui_filters.messages, TRUE);
    write_filter (filter_file, &ui_filters.origin_tasks, FALSE);
    write_filter (filter_file, &ui_filters.destination_tasks, FALSE);
winckel's avatar
winckel committed
438

winckel's avatar
winckel committed
439
440
441
442
443
    fprintf (filter_file, "</filters>\n");

    fclose (filter_file);
    return RC_OK;
}
444

445
static void ui_create_filter_menu(GtkWidget **menu, ui_filter_t *filter)
446
{
447
448
449
450
451
    if (*menu == NULL)
    {
        GtkWidget *menu_items;
        int item;
        gpointer data;
452

453
        *menu = gtk_menu_new ();
454

455
456
457
458
        /* Create the "NONE" menu-item */
        {
            /* Create a new menu-item with a name */
            menu_items = gtk_menu_item_new_with_label ("NONE");
winckel's avatar
winckel committed
459

460
461
            /* Add it to the menu. */
            gtk_menu_shell_append (GTK_MENU_SHELL(*menu), menu_items);
winckel's avatar
winckel committed
462

463
464
            g_debug("ui_create_filter_menu %lx", (long) menu_items);
            g_signal_connect(G_OBJECT(menu_items), "activate", G_CALLBACK(ui_callback_on_menu_none), *menu);
winckel's avatar
winckel committed
465

466
467
468
            /* Show the widget */
            gtk_widget_show (menu_items);
        }
winckel's avatar
winckel committed
469

470
471
472
473
        /* Create the "ALL" menu-item */
        {
            /* Create a new menu-item with a name */
            menu_items = gtk_menu_item_new_with_label ("ALL");
winckel's avatar
winckel committed
474

475
476
            /* Add it to the menu. */
            gtk_menu_shell_append (GTK_MENU_SHELL(*menu), menu_items);
winckel's avatar
winckel committed
477

478
479
            g_debug("ui_create_filter_menu %lx", (long) menu_items);
            g_signal_connect(G_OBJECT(menu_items), "activate", G_CALLBACK(ui_callback_on_menu_all), *menu);
winckel's avatar
winckel committed
480

481
482
483
            /* Show the widget */
            gtk_widget_show (menu_items);
        }
winckel's avatar
winckel committed
484

485
486
487
        /* Create separator */
        {
            menu_items = gtk_menu_item_new ();
winckel's avatar
winckel committed
488

489
490
            /* Add it to the menu. */
            gtk_menu_shell_append (GTK_MENU_SHELL(*menu), menu_items);
winckel's avatar
winckel committed
491

492
493
494
            /* Show the widget */
            gtk_widget_show (menu_items);
        }
winckel's avatar
winckel committed
495

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        /* Creates menu-items */
        for (item = 0; item < filter->used; item++)
        {
            /* Create a new menu-item with a name */
            menu_items = gtk_check_menu_item_new_with_label (filter->items[item].name);

            /* Add it to the menu. */
            gtk_menu_shell_append (GTK_MENU_SHELL(*menu), menu_items);

            gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_items), filter->items[item].enabled);

            /* Connect function to be called when the menu item is selected */
            data = &filter->items[item];
            g_debug("ui_create_filter_menu %lx %lx", (long) menu_items, (long) data);
            g_signal_connect(G_OBJECT(menu_items), "activate", G_CALLBACK(ui_callback_on_menu_item_selected), data);
            /* Save the menu_item reference */
            filter->items[item].menu_item = menu_items;

            /* Show the widget */
            gtk_widget_show (menu_items);
        }
517
518
519
    }
}

520
521
522
523
524
525
526
527
void ui_create_filter_menus(void)
{
    ui_create_filter_menu (&ui_main_data.menu_filter_messages, &ui_filters.messages);
    ui_create_filter_menu (&ui_main_data.menu_filter_origin_tasks, &ui_filters.origin_tasks);
    ui_create_filter_menu (&ui_main_data.menu_filter_destination_tasks, &ui_filters.destination_tasks);
    ui_create_filter_menu (&ui_main_data.menu_filter_instances, &ui_filters.instances);
}

winckel's avatar
winckel committed
528
static void ui_destroy_filter_menu_item(GtkWidget *widget, gpointer data)
529
{
winckel's avatar
winckel committed
530
531
    if (GTK_IS_MENU_ITEM(widget))
    {
532
        gtk_widget_destroy (widget);
winckel's avatar
winckel committed
533
534
    }
}
535

winckel's avatar
winckel committed
536
static void ui_destroy_filter_menu_widget(GtkWidget **menu)
winckel's avatar
winckel committed
537
{
538
539
    if (*menu != NULL)
    {
540
        gtk_container_foreach (GTK_CONTAINER(*menu), ui_destroy_filter_menu_item, NULL);
winckel's avatar
winckel committed
541

542
        gtk_widget_destroy (*menu);
543
544
545
546
547
548
        *menu = NULL;
    }
}

void ui_destroy_filter_menus(void)
{
winckel's avatar
winckel committed
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_messages);
    ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_origin_tasks);
    ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_destination_tasks);
    ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_instances);
}

void ui_destroy_filter_menu(ui_filter_e filter)
{
    switch (filter)
    {
        case FILTER_MESSAGES:
            ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_messages);
            break;

        case FILTER_ORIGIN_TASKS:
            ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_origin_tasks);
            break;

        case FILTER_DESTINATION_TASKS:
            ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_destination_tasks);
            break;

        case FILTER_INSTANCES:
            ui_destroy_filter_menu_widget (&ui_main_data.menu_filter_instances);
            break;

        default:
            g_warning("unknown filter type %d", filter);
            break;
    }
579
580
581
582
}

void ui_show_filter_menu(GtkWidget **menu, ui_filter_t *filter)
{
583
    ui_create_filter_menu (menu, filter);
584
585
586

    gtk_menu_popup (GTK_MENU (*menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
}