Statistics
| Revision:

root / branches / nntpgrab-0.7 / client / gui / queue.c @ 1918

History | View | Annotate | Download (75.6 KB)

1
/* 
2
    Copyright (C) 2005-2010  Erik van Pienbroek
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
*/
18

                
19
#ifdef WIN32
20
#include 
21
#include 
22
#endif
23
#include 
24
#include 
25
#include "gui.h"
26
#include 
27
#include "config_gui.h"
28
#include "nntpgrab_utils.h"
29

                
30
#ifdef HAVE_LIBNOTIFY
31
#include 
32
#endif
33

                
34
/* GTK3 compatibility */
35
#if GTK_CHECK_VERSION(2,90,0)
36
#define GDK_Delete GDK_KEY_Delete
37
#endif
38

                
39
static gboolean find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter);
40
static gboolean find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter);
41

                
42
static NGSchedularState status_flag = SCHEDULAR_STATE_RUNNING;     // Schedular is running by default
43

                
44
enum {
45
    FIELD_COLLECTION_NAME,
46
    FIELD_COLLECTION_NAME_HASH,
47
    FIELD_SUBJECT,
48
    FIELD_SUBJECT_HASH,
49
    FIELD_POSTER,
50
    FIELD_GROUPS,
51
    FIELD_STAMP_STR,
52
    FIELD_PROGRESS,
53
    FIELD_FILESIZE,
54
    FIELD_FILESIZE_STR,
55
    FIELD_FILESIZE_REMAINING,
56
    FIELD_FILESIZE_REMAINING_STR,
57
    FIELD_NUM_PARTS,
58
    FIELD_PARTS_DONE,
59
    FIELD_PARTS_FAILED,
60
    FIELD_STATE_STR,
61
    FIELD_ESTIMATED_TIME_REMAINING,
62
    FIELD_ESTIMATED_TIME_TO_FINISH,
63
    FIELD_POSITION,
64
    FIELD_REAL_FILENAME,
65
    LAST_FIELD
66
};
67
static GtkTreeViewColumn *columns[LAST_FIELD] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0 };
68

                
69
#ifdef WIN32
70
static void
71
open_file(const char *file, gboolean open_folder)
72
{
73
    char *mode;
74
    char *file_localized;
75
    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
76

                
77
    if (open_folder) {
78
        mode = "explore";
79
    } else {
80
        mode = "open";
81
    }
82

                
83
    file_localized = g_win32_locale_filename_from_utf8(file);
84
    g_return_if_fail(file_localized != NULL);
85

                
86
    if ((intptr_t) ShellExecuteA((HWND) GDK_WINDOW_HWND(gtk_widget_get_window(windowMain)), mode, file_localized, NULL, file_localized, SW_SHOW) < 32) {
87
        char *msg;
88
        GtkWidget *windowMain;
89

                
90
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
91
        if (open_folder) {
92
            msg = g_strdup_printf(_("Unable to navigate to the download folder\n"));    // TODO: Add GetLastError message
93
        } else {
94
            msg = g_strdup_printf(_("Unable to execute file\n%s"), file);    // TODO: Add GetLastError message
95
        }
96
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
97
        g_free(msg);
98
    }
99

                
100
    g_free(file_localized);
101
}
102
#else
103
static void
104
open_file(const char *folder_unquoted, gboolean open_folder)
105
{
106
    char *cmd = NULL;
107
    GError *err = NULL;
108
    char *folder = g_shell_quote(folder_unquoted);
109
    gboolean ret;
110

                
111
#if GTK_CHECK_VERSION(2,14,0) && !defined(DARWIN)
112
    char *uri = g_strdup_printf("file://%s", folder_unquoted);
113
    ret = gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &err);
114
    g_free(uri);
115
#else
116
#ifdef DARWIN
117
    cmd = g_strdup_printf("open %s", folder);
118
#else
119
    cmd = g_strdup_printf("xdg-open %s", folder);
120
#endif
121

                
122
    ret = g_spawn_command_line_async(cmd, &err);
123
#endif /* GTK_CHECK_VERSION(2,14,0) */
124

                
125
    if (!ret) {
126
        char *msg;
127
        GtkWidget *windowMain;
128

                
129
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
130
        if (open_folder) {
131
            msg = g_strdup_printf(_("Unable to navigate to the download folder:\n%s"), err->message);
132
        } else {
133
            msg = g_strdup_printf(_("Unable to execute file\n%s:\n%s"), folder, err->message);
134
        }
135
        g_error_free(err);
136
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
137
        g_free(msg);
138
    }
139

                
140
    g_free(cmd);
141
}
142
#endif
143

                
144
G_MODULE_EXPORT gboolean
145
on_treeDownloadQueue_button_press_event(GtkWidget *caller, GdkEventButton *event, gpointer data)
146
{
147
    GtkWidget *menuDownloadQueue;
148
    GtkTreeModel *model;
149
    GtkTreeSelection *selection;
150
    GValue val;
151
#if GTK_CHECK_VERSION(2,12,0)
152
    GObject *menuitemExecuteItem;
153
    GObject *menuitemOpenDownloadFolder;
154
    GObject *menuitemMarkOptional;
155
#else
156
    GtkWidget *menuitemExecuteItem;
157
    GtkWidget *menuitemOpenDownloadFolder;
158
    GtkWidget *menuitemMarkOptional;
159
#endif
160
    const char *str_optional;
161
    gboolean show_optional_button;
162
    char *progress_str = NULL;
163
    GtkTreeIter iter;
164
    GtkTreeIter parent;
165
    GList *list;
166

                
167
#ifdef DARWIN
168
    // Catch both the right mouse button and CTRL + left mouse button
169
    if (!(event->button == 3 || (event->button == 1 && event->state == GDK_CONTROL_MASK))) {
170
        return FALSE;
171
    }
172
#else
173
    // We only want to catch the right mouse button
174
    if (event->button != 3) {
175
        return FALSE;
176
    }
177
#endif
178

                
179
    // Is at least one item selected?
180
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(caller));
181
    if (gtk_tree_selection_count_selected_rows(selection) == 0)
182
    {
183
        GtkTreePath *path;
184

                
185
        /* Get tree path for row that was clicked */
186
        if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(caller),
187
                                            (gint) event->x,
188
                                            (gint) event->y,
189
                                            &path, NULL, NULL, NULL))
190
        {
191
            // No item(s) selected and no item under the cursor. Ignore
192
            return FALSE;
193
        }
194

                
195
        gtk_tree_selection_unselect_all(selection);
196
        gtk_tree_selection_select_path(selection, path);
197
        gtk_tree_path_free(path);
198
    }
199

                
200
    // Do we have exactly one selected item and is this item already downloaded?
201
#if GTK_CHECK_VERSION(2,12,0)
202
    menuitemExecuteItem = nntpgrab_gui_base_get_object("menuitemExecuteItem");
203
    memset(&val, 0, sizeof(val));
204
    g_value_init(&val, G_TYPE_BOOLEAN);
205
    g_value_set_boolean (&val, FALSE);
206
    g_object_set_property(G_OBJECT(menuitemExecuteItem), "sensitive", &val);
207
#else
208
    menuitemExecuteItem = nntpgrab_gui_base_get_widget("menuitemExecuteItem");
209
    gtk_widget_set_sensitive(menuitemExecuteItem, FALSE);
210
#endif
211

                
212
    if (!nntpgrab_glue_get_is_standalone(glue)) {
213
#if GTK_CHECK_VERSION(2,12,0)
214
        menuitemOpenDownloadFolder = nntpgrab_gui_base_get_object("menuitemOpenDownloadFolder");
215
        memset(&val, 0, sizeof(val));
216
        g_value_init(&val, G_TYPE_BOOLEAN);
217
        g_value_set_boolean (&val, FALSE);
218
        g_object_set_property(G_OBJECT(menuitemOpenDownloadFolder), "sensitive", &val);
219
#else
220
        menuitemOpenDownloadFolder = nntpgrab_gui_base_get_widget("menuitemOpenDownloadFolder");
221
        gtk_widget_set_sensitive(menuitemOpenDownloadFolder, FALSE);
222
#endif
223
    }
224

                
225
    list = gtk_tree_selection_get_selected_rows(selection, &model);
226

                
227
    gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
228
    gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &progress_str, -1);
229

                
230
    if (progress_str != NULL) {
231
        if (gtk_tree_selection_count_selected_rows(selection) == 1) {
232
            if (!strcmp(progress_str, _("Done")) ||
233
                !strcmp(progress_str, _("Incomplete"))) {
234

                
235

                
236
                if (nntpgrab_glue_get_is_standalone(glue)) {
237
#if GTK_CHECK_VERSION(2,12,0)
238
                    g_value_set_boolean (&val, TRUE);
239
                    g_object_set_property(G_OBJECT(menuitemExecuteItem), "sensitive", &val);
240
#else
241
                    gtk_widget_set_sensitive(menuitemExecuteItem, TRUE);
242
#endif
243
                }
244
            }
245
        }
246
    }
247

                
248
    if (progress_str && !strcmp(progress_str, _("Skipped"))) {
249
        str_optional = _("Forcefully download file(s)");
250
    } else {
251
        str_optional = _("Don't download file(s)");
252
    }
253

                
254
    menuitemMarkOptional = nntpgrab_gui_base_get_object("menuitemMarkOptional");
255
    g_value_unset(&val);
256
    g_value_init(&val, G_TYPE_STRING);
257
    g_value_set_string(&val, str_optional);
258
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "label", &val);
259

                
260
    if (gtk_tree_model_iter_parent(model, &parent, &iter) &&
261
        (!progress_str || strcmp(progress_str, _("Done"))) &&
262
        (!progress_str || strcmp(progress_str, _("Incomplete"))) &&
263
        (!progress_str || strcmp(progress_str, _("Not available")))) {
264

                
265
        show_optional_button = TRUE;
266
    } else {
267
        show_optional_button = FALSE;
268
    }
269
#if GTK_CHECK_VERSION(2,12,0)
270
    g_value_unset(&val);
271
    g_value_init(&val, G_TYPE_BOOLEAN);
272
    g_value_set_boolean (&val, show_optional_button);
273
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "sensitive", &val);
274
#else
275
    gtk_widget_set_sensitive(menuitemMarkOptional, show_optional_button);
276
#endif
277

                
278
    g_free(progress_str);
279
    g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
280
    g_list_free (list);
281

                
282
    // Do we have multiple items selected and do these all belong to the same collection ?
283
    // TODO
284

                
285
    menuDownloadQueue = nntpgrab_gui_base_get_widget("menuDownloadQueue");
286
    gtk_menu_popup(GTK_MENU(menuDownloadQueue), NULL, NULL, NULL, NULL, event->button, event->time);
287

                
288
    return TRUE;
289
}
290

                
291
G_MODULE_EXPORT void
292
on_menuitemRestartTask_activate(GtkWidget *caller, gpointer data)
293
{
294
    GtkWidget *treeDownloadQueue;
295
    GtkTreeSelection *selection;
296
    GList *list, *selectedRows;
297
    GtkTreeModel *model;
298
    GtkTreeIter iter, parent;
299
    char *collection_name = NULL;
300
    char *subject = NULL;
301
    char *errmsg = NULL;
302

                
303
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
304
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
305

                
306
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
307

                
308
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
309

                
310
    list = selectedRows;
311
    while (list) {
312
        GtkTreePath *path = list->data;
313

                
314
        gtk_tree_model_get_iter(model, &iter, path);
315
        if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
316
            // Item contains no parent, restart the whole collection
317
            gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, -1);
318
            if (!nntpgrab_glue_schedular_restart_task(glue, collection_name, NULL, &errmsg)) {
319
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
320
                char msg[1024];
321

                
322
                memset(&msg, 0, sizeof(msg));
323
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart collection due to an error:\n%s"), errmsg);
324
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
325
                g_free(errmsg);
326
            }
327

                
328
            g_free(collection_name);
329
        } else {
330
            // Restart one item from the collection
331
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
332
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
333

                
334
            if (!nntpgrab_glue_schedular_restart_task(glue, collection_name, subject, &errmsg)) {
335
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
336
                char msg[1024];
337

                
338
                memset(&msg, 0, sizeof(msg));
339
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart task due to an error:\n%s"), errmsg);
340
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
341
                g_free(errmsg);
342
            }
343

                
344
            g_free(collection_name);
345
            g_free(subject);
346
        }
347

                
348
        list = g_list_next(list);
349
    }
350

                
351
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
352
    g_list_free (selectedRows);
353

                
354
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
355
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
356
        char msg[1024];
357

                
358
        memset(&msg, 0, sizeof(msg));
359
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
360
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
361
        g_free(errmsg);
362
    }
363
}
364

                
365
G_MODULE_EXPORT void
366
on_menuitemMarkOptional_activate(GtkWidget *caller, gpointer data)
367
{
368
    GtkWidget *treeDownloadQueue;
369
    GtkTreeSelection *selection;
370
    GList *list, *selectedRows;
371
    GtkTreeModel *model;
372
    GtkTreeIter iter, parent;
373
    char *collection_name = NULL;
374
    char *subject = NULL;
375
    gboolean is_optional = FALSE;
376
    gboolean flag_set = FALSE;
377

                
378
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
379
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
380

                
381
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
382

                
383
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
384

                
385
    list = selectedRows;
386
    while (list) {
387
        GtkTreePath *path = list->data;
388

                
389
        gtk_tree_model_get_iter(model, &iter, path);
390
        if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
391
            // Item contains no parent
392
        } else {
393
            // Restart one item from the collection
394
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
395
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
396

                
397
            if (!flag_set) {
398
                char *state = NULL;
399
                gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &state, -1);
400
                if (state && !strcmp(state, _("Skipped"))) {
401
                    is_optional = FALSE;
402
                } else {
403
                    is_optional = TRUE;
404
                }
405
                flag_set = TRUE;
406
                g_free(state);
407
            }
408

                
409
            if (!nntpgrab_glue_schedular_mark_task_optional(glue, collection_name, subject, is_optional)) {
410
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
411
                nntpgrab_gui_base_dialog_show(windowMain, _("Unable to change the status of the selected file(s)"), GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
412
            }
413

                
414
            g_free(collection_name);
415
            g_free(subject);
416
        }
417

                
418
        list = g_list_next(list);
419
    }
420

                
421
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
422
    g_list_free (selectedRows);
423
}
424

                
425
G_MODULE_EXPORT void
426
on_menuitemOpenDownloadFolder_activate(GtkWidget *caller, gpointer data)
427
{
428
    // Is only one collection selected or more ?
429
    gboolean multiple_collections = FALSE;
430
    char *collection_name = NULL;
431
    GtkWidget *treeDownloadQueue;
432
    GtkTreeSelection *selection;
433
    GList *list, *selectedRows;
434
    GtkTreeModel *model;
435
    NGConfigOpts opts;
436

                
437
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
438
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
439

                
440
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
441

                
442
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
443

                
444
    list = selectedRows;
445
    while (list) {
446
        GtkTreePath *path = list->data;
447
        GtkTreeIter iter;
448
        char *name = NULL;
449

                
450
        gtk_tree_model_get_iter(model, &iter, path);
451
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &name, -1);
452

                
453
        if (name == NULL || strlen(name) == 0) {
454
            GtkTreeIter parent;
455

                
456
            if (name) {
457
                g_free(name);
458
            }
459

                
460
            // Look at the parent
461
            gtk_tree_model_iter_parent(model, &parent, &iter);
462
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &name, -1);
463
        }
464

                
465
        g_return_if_fail(name != NULL);
466

                
467
        if (!collection_name) {
468
            collection_name = g_strdup(name);
469
        } else {
470
            if (strcmp(name, collection_name)) {
471
                // A previous found collection name and this collection name differ!
472
                multiple_collections = TRUE;
473
            }
474
        }
475

                
476
        g_free(name);
477

                
478
        list = g_list_next(list);
479
    }
480

                
481
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
482
    g_list_free (selectedRows);
483

                
484
    opts = nntpgrab_glue_config_get_opts(glue);
485

                
486
    if (multiple_collections) {
487
        // Open the default download folder
488
        open_file(opts.download_directory, TRUE);
489
    } else {
490
        // Open the download folder of the active collection
491
        char *folder = g_strdup_printf("%s%s%s", opts.download_directory, G_DIR_SEPARATOR_S, collection_name);
492

                
493
        if (!g_file_test(folder, G_FILE_TEST_IS_DIR)) {
494
            // Folder doesn't exist yet, fall back to default download directory
495
            open_file(opts.download_directory, TRUE);
496
        } else {
497
            open_file(folder, TRUE);
498
        }
499

                
500
        g_free(folder);
501
    }
502

                
503
    if (collection_name) {
504
        g_free(collection_name);
505
    }
506
}
507

                
508
G_MODULE_EXPORT void
509
on_menuitemExecuteItem_activate(GtkWidget *caller, gpointer data)
510
{
511
    char *collection_name = NULL;
512
    char *real_filename = NULL;
513
    GtkWidget *treeDownloadQueue;
514
    GtkTreeSelection *selection;
515
    GList *selectedRows;
516
    GtkTreeModel *model;
517
    NGConfigOpts opts;
518
    GtkTreePath *path;
519
    GtkTreeIter iter;
520
    char *subject;
521
    char *full_filename;
522
    GtkTreeIter parent;
523

                
524
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
525
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
526

                
527
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) == 1);
528

                
529
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
530

                
531
    path = selectedRows->data;
532

                
533
    gtk_tree_model_get_iter(model, &iter, path);
534
    gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
535

                
536
    // Look at the parent
537
    gtk_tree_model_iter_parent(model, &parent, &iter);
538
    gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
539
    gtk_tree_model_get(model, &iter, FIELD_REAL_FILENAME, &real_filename, -1);
540

                
541
    g_return_if_fail(real_filename != NULL);
542

                
543
    opts = nntpgrab_glue_config_get_opts(glue);
544
    full_filename = g_strdup_printf("%s%s%s%s%s", opts.download_directory, G_DIR_SEPARATOR_S, collection_name, G_DIR_SEPARATOR_S, real_filename);
545

                
546
    open_file(full_filename, FALSE);
547

                
548
    g_free(full_filename);
549
    g_free(subject);
550
    g_free(real_filename);
551
    g_free(collection_name);
552
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
553
    g_list_free (selectedRows);
554
}
555

                
556
typedef enum {
557
    MOVE_DIRECTION_TOP,
558
    MOVE_DIRECTION_UP,
559
    MOVE_DIRECTION_DOWN,
560
    MOVE_DIRECTION_BOTTOM
561
} move_direction;
562

                
563
static void
564
task_moved_cb(NntpgrabGlue *obj, const char *orig_collection_name, const char *subject, const char *new_collection_name, int old_position, int new_position, gpointer data)
565
{
566
    GtkWidget *treeDownloadQueue;
567
    GtkTreeModel *store;
568
    GtkTreeIter parent, orig_iter, new_iter;
569
    int i;
570
    int num_items;
571

                
572
#if 0 
573
    g_print("%s: orig_collection_name = %s\n", __PRETTY_FUNCTION__, orig_collection_name);
574
    g_print("%s: subject = %s\n", __PRETTY_FUNCTION__, subject);
575
    g_print("%s: new_collection_name = %s\n", __PRETTY_FUNCTION__, new_collection_name);
576
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
577
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
578
#endif
579

                
580
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
581
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
582

                
583
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), new_collection_name, &parent));
584
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &orig_iter));
585

                
586
    num_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &parent);
587
    if (num_items == 1) {
588
        /* List only contains one element so we don't need to move anything */
589
        return;
590
    }
591

                
592
    if (new_position == -1) {
593
        i = 1;
594
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
595
        while (i != num_items) {
596
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
597
            i++;
598
        }
599

                
600
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
601
    } else {
602
        i = 0;
603
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
604
        while (i != new_position) {
605
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
606
            i++;
607
        }
608

                
609
        if (old_position < new_position) {
610
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
611
        } else {
612
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
613
        }
614
    }
615
}
616

                
617
static void
618
collection_moved_cb(NntpgrabGlue *obj, const char *collection_name, int old_position, int new_position)
619
{
620
    GtkWidget *treeDownloadQueue;
621
    GtkTreeModel *store;
622
    GtkTreeIter orig_iter, new_iter;
623
    int i;
624

                
625
#if 0 
626
    g_print("%s: collection_name = %s\n", __PRETTY_FUNCTION__, collection_name);
627
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
628
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
629
#endif
630

                
631
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
632
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
633

                
634
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &orig_iter));
635
    g_return_if_fail(new_position <= (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL)));
636

                
637
    if (new_position == -1) {
638
        new_position = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
639

                
640
        i = 1;
641
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
642
        while (i != new_position) {
643
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
644
            i++;
645
        }
646

                
647
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
648
    } else {
649
        i = 0;
650
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
651
        while (i != new_position) {
652
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
653
            i++;
654
        }
655

                
656
        if (old_position < new_position) {
657
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
658
        } else {
659
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
660
        }
661
    }
662
}
663

                
664
static void
665
move_item(move_direction direction)
666
{
667
    char *collection_name = NULL;
668
    char *subject = NULL;
669
    int position = -1;
670
    GtkWidget *treeDownloadQueue;
671
    GtkTreeSelection *selection;
672
    GList *list, *selectedRows;
673
    GtkTreeModel *model;
674
    GList *references = NULL;
675
    char *errmsg = NULL;
676

                
677
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
678
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
679

                
680
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
681

                
682
    // Duplicate the list with selected rows into GtkTreeRowReference's as the position of
683
    // selected rows can change during the loop later on
684

                
685
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
686

                
687
    list = selectedRows;
688
    while (list) {
689
        GtkTreePath *path = list->data;
690
        GtkTreeRowReference *ref;
691

                
692
        ref = gtk_tree_row_reference_new(model, path);
693
        references = g_list_append(references, ref);
694

                
695
        list = g_list_next(list);
696
    }
697

                
698
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
699
    g_list_free (selectedRows);
700

                
701
    if (direction == MOVE_DIRECTION_DOWN || direction == MOVE_DIRECTION_TOP) {
702
        references = g_list_reverse(references);
703
    }
704

                
705
    list = references;
706
    while (list) {
707
        GtkTreeRowReference *ref = list->data;
708
        GtkTreePath *path;
709
        GtkTreeIter iter;
710
        char *path_str;
711
        char **values;
712
        gboolean is_child = FALSE;
713
        int num_items;
714
        GtkTreeIter parent;
715

                
716
        if (collection_name) {
717
            g_free(collection_name);
718
            collection_name = NULL;
719
        }
720

                
721
        if (subject) {
722
            g_free(subject);
723
            subject = NULL;
724
        }
725

                
726
        path = gtk_tree_row_reference_get_path(ref);
727
        if (!path) {
728
            list = g_list_next(list);
729
            gtk_tree_row_reference_free(ref);
730
            continue;
731
        }
732

                
733
        gtk_tree_model_get_iter(model, &iter, path);
734
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, FIELD_SUBJECT, &subject, -1);
735

                
736
        path_str = gtk_tree_path_to_string(path);
737
        values = g_strsplit(path_str, ":", 0);
738
        g_return_if_fail(values != NULL);
739
        if (values[0]) {
740
            position = atoi(values[0]);
741
            if (values[1]) {
742
                position = atoi(values[1]);
743
            }
744
        }
745

                
746
        g_strfreev(values);
747
        g_free(path_str);
748

                
749
        if (gtk_tree_model_iter_parent(model, &parent, &iter)) {
750
            num_items = gtk_tree_model_iter_n_children(model, &parent);
751
        } else {
752
            num_items = gtk_tree_model_iter_n_children(model, NULL);
753
        }
754

                
755
        if (collection_name == NULL || strlen(collection_name) == 0) {
756
            // Look at the parent
757
            gtk_tree_model_iter_parent(model, &parent, &iter);
758
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
759

                
760
            is_child = TRUE;
761
        }
762

                
763
        g_assert(collection_name);
764

                
765
        switch (direction) {
766
            case MOVE_DIRECTION_TOP:
767
                if (is_child) {
768
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, 0));
769
                } else {
770
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, 0));
771
                }
772
                break;
773

                
774
            case MOVE_DIRECTION_UP:
775
                if (position == 0) {
776
                    break;
777
                }
778
                if (is_child) {
779
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position - 1));
780
                } else {
781
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position - 1));
782
                }
783
                break;
784

                
785
            case MOVE_DIRECTION_DOWN:
786
                if (position + 1 >= num_items) {
787
                    break;
788
                }
789
                if (is_child) {
790
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position + 1));
791
                } else {
792
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position + 1));
793
                }
794
                break;
795

                
796
            case MOVE_DIRECTION_BOTTOM:
797
                if (is_child) {
798
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, -1));
799
                } else {
800
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, -1));
801
                }
802
                break;
803

                
804
            default:
805
                break;
806
        }
807

                
808
        list = g_list_next(list);
809
        gtk_tree_path_free(path);
810
        gtk_tree_row_reference_free(ref);
811
    }
812

                
813
    g_list_free(references);
814

                
815
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
816
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
817
        char msg[1024];
818

                
819
        memset(&msg, 0, sizeof(msg));
820
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
821
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
822
        g_free(errmsg);
823
    }
824

                
825
    if (collection_name) {
826
        g_free(collection_name);
827
    }
828

                
829
    if (subject) {
830
        g_free(subject);
831
    }
832
}
833

                
834
G_MODULE_EXPORT void
835
on_menuitemMoveToTop_activate(GtkWidget *caller, gpointer data)
836
{
837
    move_item(MOVE_DIRECTION_TOP);
838
}
839

                
840
G_MODULE_EXPORT void
841
on_menuitemMoveUp_activate(GtkWidget *caller, gpointer data)
842
{
843
    move_item(MOVE_DIRECTION_UP);
844
}
845

                
846
G_MODULE_EXPORT void
847
on_menuitemMoveDown_activate(GtkWidget *caller, gpointer data)
848
{
849
    move_item(MOVE_DIRECTION_DOWN);
850
}
851

                
852
G_MODULE_EXPORT void
853
on_menuitemMoveToBottom_activate(GtkWidget *caller, gpointer data)
854
{
855
    move_item(MOVE_DIRECTION_BOTTOM);
856
}
857

                
858
G_MODULE_EXPORT void
859
on_btnRemoveItemFromQueue_clicked(GtkWidget *caller, gpointer data)
860
{
861
    GtkWidget *treeDownloadQueue;
862
    GtkTreeSelection *selection;
863
    GList *list, *selectedRows, *list_new;
864
    GtkTreeModel *model;
865
    char *errmsg = NULL;
866

                
867
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
868
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
869

                
870
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
871

                
872
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
873

                
874
    // Duplicate the list and transform the items to GtkTreeRowReferences
875
    // This is because we are going to delete items in the tree
876
    list = selectedRows;
877
    list_new = NULL;
878
    while (list) {
879
        GtkTreePath *path = list->data;
880

                
881
        list_new = g_list_append(list_new, gtk_tree_row_reference_new(model, path));
882

                
883
        list = g_list_next(list);
884
    }
885

                
886
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
887
    g_list_free (selectedRows);
888

                
889
    // Now loop trough all the items and remove the downloads tasks
890
    list = list_new;
891
    while (list) {
892
        GtkTreeRowReference *ref = list->data;
893

                
894
        if (gtk_tree_row_reference_valid(ref)) {
895
            GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
896
            GtkTreeIter iter, parent;
897
            GList *list_tmp;
898
            char *collection_name = NULL;
899
            char *subject = NULL;
900
            gboolean item_is_already_removed = FALSE;
901

                
902
            /* Did we already try to remove the parent of the active item? */
903
            list_tmp = g_list_previous(list);
904
            while (list_tmp) {
905
                GtkTreeRowReference *ref_tmp = list_tmp->data;
906
                GtkTreePath *path_tmp = gtk_tree_row_reference_get_path(ref_tmp);
907

                
908
                if (gtk_tree_path_is_ancestor(path_tmp, path)) {
909
                    /* Hey, the collection itself is already removed! */
910
                    item_is_already_removed = TRUE;
911
                }
912

                
913
                gtk_tree_path_free(path_tmp);
914

                
915
                list_tmp = g_list_previous(list_tmp);
916
            }
917

                
918
            gtk_tree_model_get_iter(model, &iter, path);
919
            gtk_tree_path_free(path);
920

                
921
            if (item_is_already_removed) {
922
                list = g_list_next(list);
923
                continue;
924
            }
925

                
926
            if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
927
                // Item contains no parent, delete the whole collection
928
                gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, -1);
929
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, NULL, &errmsg)) {
930
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
931
                    char msg[1024];
932

                
933
                    memset(&msg, 0, sizeof(msg));
934
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing collection(s):\n%s"), errmsg);
935
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
936
                    g_free(errmsg);
937
                }
938

                
939
                g_free(collection_name);
940
            } else {
941
                // Delete one item from the collection
942
                gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
943
                gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
944

                
945
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, subject, &errmsg)) {
946
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
947
                    char msg[1024];
948

                
949
                    memset(&msg, 0, sizeof(msg));
950
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing file(s):\n%s"), errmsg);
951
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
952
                    g_free(errmsg);
953
                }
954

                
955
                g_free(collection_name);
956
                g_free(subject);
957
            }
958
        }
959

                
960
        list = g_list_next(list);
961
    }
962

                
963
    /* Traverse the list again to free all the row references */
964
    list = list_new;
965
    while (list) {
966
        gtk_tree_row_reference_free((GtkTreeRowReference*) list->data);
967
        list = g_list_next(list);
968
    }
969
    g_list_free(list_new);
970

                
971
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
972
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
973
        char msg[1024];
974

                
975
        memset(&msg, 0, sizeof(msg));
976
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
977
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
978
        g_free(errmsg);
979
    }
980
}
981

                
982
static void
983
generate_stamp(time_t stamp, char *buf, int buf_len)
984
{
985
    struct tm *now;
986

                
987
    if ((now = localtime(&stamp)) == NULL) {
988
        // Date could not be parted
989
        buf[0] = '\0';
990
    }
991

                
992
    strftime(buf, buf_len, "%c", now);
993
}
994

                
995
static int
996
calculate_progress(guint64 file_size, guint64 file_size_remaining)
997
{
998
    double progress;
999
    int progress_int;
1000

                
1001
    // Prevent a division by zero
1002
    if (file_size_remaining == 0) {
1003
        return 100;
1004
    }
1005

                
1006
    progress = (double) file_size - file_size_remaining;
1007
    progress /= file_size;
1008
    progress *= 100;
1009
    progress_int = (int) progress;
1010

                
1011
    if (progress_int > 100) {
1012
        g_print(__FILE__ ":%i progress overflow!\n", __LINE__);
1013
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1014
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1015
        progress_int = 100;
1016
    }
1017

                
1018
    if (progress < 0) {
1019
        g_print(__FILE__ ":%i progress underflow!\n", __LINE__);
1020
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1021
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1022
        progress_int = 0;
1023
    }
1024

                
1025
    return progress_int;
1026
}
1027

                
1028
static void
1029
file_download_state_update (NntpgrabGlue *obj, const char *collection_name, const char *subject, int num_parts_total, int num_parts_done, int num_parts_failed, guint64 file_size, guint64 file_size_remaining, guint64 total_file_size, guint64 total_file_size_remaining, gpointer data)
1030
{
1031
    GtkWidget *treeDownloadQueue;
1032
    GtkTreeModel *store;
1033
    GtkTreeIter iter, parent;
1034
    char *state;
1035
    int progress;
1036
    char size_remaining[64];
1037
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1038
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1039

                
1040
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1041
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1042

                
1043
    /* If the file has just been skipped, ignore this event */
1044
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_STATE_STR, &state, -1);
1045
    if (state && !strcmp(state, _("Skipped"))) {
1046
        g_free(state);
1047
        return;
1048
    }
1049
    g_free(state);
1050

                
1051
    // Update the parent
1052
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1053

                
1054
    memset(&size_remaining, 0, sizeof(size_remaining));
1055
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1056

                
1057
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1058
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1059
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1060

                
1061
    // Update the child
1062
    progress = calculate_progress(file_size, file_size_remaining);
1063
    memset(&size_remaining, 0, sizeof(size_remaining));
1064
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1065

                
1066
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, progress,
1067
                                                        FIELD_PARTS_DONE, num_parts_done,
1068
                                                        FIELD_PARTS_FAILED, num_parts_failed,
1069
                                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1070
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1071
}
1072

                
1073
static void
1074
collection_added (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1075
{
1076
    GtkWidget *treeDownloadQueue;
1077
    GtkTreeModel *store;
1078
    GtkTreeIter iter;
1079

                
1080
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1081
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1082

                
1083
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, NULL);
1084
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_COLLECTION_NAME, collection_name,
1085
                                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1086
                                                        FIELD_SUBJECT, collection_name,
1087
                                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1088
                                                        FIELD_POSTER, poster,
1089
                                                        FIELD_STATE_STR, NULL, -1);
1090
}
1091

                
1092
static void
1093
collection_removed (NntpgrabGlue *obj, const char *collection_name)
1094
{
1095
    GtkWidget *treeDownloadQueue;
1096
    GtkTreeModel *store;
1097
    GtkTreeIter iter;
1098

                
1099
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1100
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1101

                
1102
    g_return_if_fail(find_iter_by_collection_name(store, collection_name, &iter));
1103

                
1104
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1105
}
1106

                
1107
static void
1108
collection_modified (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1109
{
1110
    GtkWidget *treeDownloadQueue;
1111
    GtkTreeModel *store;
1112
    GtkTreeIter iter;
1113

                
1114
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1115
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1116

                
1117
    g_return_if_fail(find_iter_by_collection_name(store, collection_name, &iter));
1118

                
1119
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FIELD_POSTER, poster, -1);
1120
}
1121

                
1122
static void
1123
file_added (NntpgrabGlue *obj, const char *collection_name, const char *subject, const char *poster, guint64 stamp, guint64 file_size, guint64 total_size, guint64 total_size_remaining, NGTaskState state, int num_parts, GList *groups)
1124
{
1125
    GtkWidget *treeDownloadQueue;
1126
    GtkTreeModel *store;
1127
    GtkTreeIter parent, iter;
1128
    char size[64];
1129
    char size_remaining[64];
1130
    char total_groups[256];
1131
    char stamp_str[64];
1132
    GList *list;
1133
    int progress;
1134

                
1135
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1136
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1137

                
1138
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1139
    g_return_if_fail(file_size > 0);
1140

                
1141
    list = groups;
1142
    memset(&total_groups, 0, sizeof(total_groups));
1143
    while (list) {
1144
        if (total_groups[0] != '\0') {
1145
            char tmp[256];
1146

                
1147
            memset(tmp, 0, sizeof(tmp));
1148
            snprintf(tmp, sizeof(tmp) - 1, "%s, %s", total_groups, (const char *) list->data);
1149
            strcpy(total_groups, tmp);
1150
        } else {
1151
            strncpy(total_groups, list->data, sizeof(total_groups) - 1);
1152
        }
1153

                
1154
        list = g_list_next(list);
1155
    }
1156

                
1157
    memset(size, 0, sizeof(size));
1158
    nntpgrab_utils_calculate_file_size(file_size, size, sizeof(size) - 1);
1159

                
1160
    memset(&stamp_str, 0, sizeof(stamp_str));
1161
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1162

                
1163
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, &parent);
1164
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_SUBJECT, subject,
1165
                                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1166
                                                        FIELD_POSTER, poster,
1167
                                                        FIELD_GROUPS, total_groups,
1168
                                                        FIELD_STAMP_STR, stamp_str,
1169
                                                        FIELD_FILESIZE, file_size,
1170
                                                        FIELD_FILESIZE_STR, size,
1171
                                                        FIELD_FILESIZE_REMAINING, file_size,
1172
                                                        FIELD_FILESIZE_REMAINING_STR, size,
1173
                                                        FIELD_NUM_PARTS, num_parts,
1174
                                                        FIELD_STATE_STR, NULL, -1);
1175

                
1176
    if (state == TASK_STATE_SKIPPED) {
1177
        gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1178
                                                            FIELD_FILESIZE_REMAINING, (guint64) 0,
1179
                                                            FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1180
    }
1181

                
1182
    // Update the parent
1183
    memset(size, 0, sizeof(size));
1184
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1185

                
1186
    memset(size_remaining, 0, sizeof(size_remaining));
1187
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1188
    progress = calculate_progress(total_size, total_size_remaining);
1189

                
1190
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1191
                                                        FIELD_FILESIZE, total_size,
1192
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1193
                                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1194
                                                        FIELD_PROGRESS, progress, -1);
1195
}
1196

                
1197
static void
1198
file_removed (NntpgrabGlue *obj, const char *collection_name, const char *subject, guint64 total_size, guint64 total_size_remaining, gpointer data)
1199
{
1200
    GtkWidget *treeDownloadQueue;
1201
    GtkTreeModel *store;
1202
    GtkTreeIter iter, parent;
1203
    int progress;
1204
    char size[64];
1205
    char size_remaining[64];
1206

                
1207
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1208
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1209

                
1210
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1211
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1212

                
1213
    memset(&size, 0, sizeof(size));
1214
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1215

                
1216
    memset(size_remaining, 0, sizeof(size_remaining));
1217
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1218

                
1219
    progress = calculate_progress(total_size, total_size_remaining);
1220

                
1221
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1222
                                                        FIELD_FILESIZE, total_size,
1223
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1224
                                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1225
                                                        FIELD_PROGRESS, progress, -1);
1226

                
1227
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1228
}
1229

                
1230
static void
1231
file_state_changed (NntpgrabGlue *obj, const char *collection_name, const char *subject, const char *real_filename, NGTaskState old_state, NGTaskState new_state, guint64 file_size_remaining, guint64 total_size, guint64 total_size_remaining)
1232
{
1233
    GtkWidget *treeDownloadQueue;
1234
    GtkTreeModel *store;
1235
    GtkTreeIter iter, parent;
1236
    int progress;
1237
    char size[64];
1238
    char size_remaining[64];
1239

                
1240
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1241
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1242

                
1243
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1244
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1245

                
1246
    switch(new_state) {
1247
        case TASK_STATE_WAITING_FOR_DOWNLOAD:       // The file was restarted
1248
            // Update the row itself
1249
            memset(size_remaining, 0, sizeof(size_remaining));
1250
            nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1251

                
1252
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PARTS_DONE, (int) 0,
1253
                                                                FIELD_PARTS_FAILED, (int) 0,
1254
                                                                FIELD_PROGRESS, (int) 0,
1255
                                                                FIELD_STATE_STR, NULL,
1256
                                                                FIELD_FILESIZE_REMAINING, file_size_remaining,
1257
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1258

                
1259
            // Update the parent
1260
            progress = calculate_progress(total_size, total_size_remaining);
1261

                
1262
            memset(size, 0, sizeof(size));
1263
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1264

                
1265
            memset(size_remaining, 0, sizeof(size_remaining));
1266
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1267

                
1268
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1269
                                                                FIELD_FILESIZE, total_size,
1270
                                                                FIELD_FILESIZE_STR, size,
1271
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1272
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1273

                
1274
            break;
1275

                
1276
        case TASK_STATE_DOWNLOADING:
1277
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 0,
1278
                                                                FIELD_STATE_STR, NULL, -1);     // The state message should be NULL. In that case the value of the progress bar will be shown
1279
            break;
1280

                
1281
        case TASK_STATE_WAITING_FOR_DECODE:
1282
            memset(size_remaining, 0, sizeof(size_remaining));
1283
            nntpgrab_utils_calculate_file_size((guint64) 0, size_remaining, sizeof(size_remaining) - 1);
1284
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1285
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining,
1286
                                                                FIELD_STATE_STR, _("Waiting"), -1);
1287

                
1288
            break;
1289

                
1290
        case TASK_STATE_DECODING:
1291
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1292
                                                                FIELD_STATE_STR, _("Decoding"), -1);
1293
            break;
1294

                
1295
        case TASK_STATE_FINISHED_COMPLETE:
1296
            // Do we need to update the parent
1297
            g_return_if_fail(real_filename != NULL);
1298
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Done"),
1299
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1300
            break;
1301

                
1302
        case TASK_STATE_FINISHED_INCOMPLETE:
1303
            g_return_if_fail(real_filename != NULL);
1304
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Incomplete"),
1305
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1306
            break;
1307

                
1308
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1309
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Not available"), -1);
1310
            break;
1311

                
1312
        case TASK_STATE_SKIPPED:
1313
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1314
                                                                FIELD_FILESIZE_REMAINING, (guint64) 0,
1315
                                                                FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1316

                
1317
            // Update the parent
1318
            progress = calculate_progress(total_size, total_size_remaining);
1319

                
1320
            memset(size, 0, sizeof(size));
1321
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1322

                
1323
            memset(size_remaining, 0, sizeof(size_remaining));
1324
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1325

                
1326
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1327
                                                                FIELD_FILESIZE, total_size,
1328
                                                                FIELD_FILESIZE_REMAINING, size,
1329
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1330
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1331

                
1332
            break;
1333
    };
1334
}
1335

                
1336
static void
1337
foreach_collection_func (const char *collection_name, const char *poster, nguint64 total_size, nguint64 total_size_remaining, int position, gpointer data)
1338
{
1339
    GtkTreeStore *store = GTK_TREE_STORE(data);
1340
    GtkTreeIter iter;
1341
    char size[64];
1342
    char size_remaining[64];
1343
    int progress;
1344

                
1345
    memset(&size, 0, sizeof(size));
1346
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1347

                
1348
    memset(&size_remaining, 0, sizeof(size_remaining));
1349
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1350

                
1351
    progress = calculate_progress(total_size, total_size_remaining);
1352

                
1353
    gtk_tree_store_append(store, &iter, NULL);
1354
    gtk_tree_store_set(store, &iter,    FIELD_COLLECTION_NAME, collection_name,
1355
                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1356
                                        FIELD_SUBJECT, collection_name,
1357
                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1358
                                        FIELD_POSTER, poster,
1359
                                        FIELD_FILESIZE_STR, size,
1360
                                        FIELD_FILESIZE, total_size,
1361
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1362
                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1363
                                        FIELD_STATE_STR, NULL,
1364
                                        FIELD_PROGRESS, progress,
1365
                                        FIELD_POSITION, position, -1);
1366
}
1367

                
1368
static gboolean
1369
find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter)
1370
{
1371
    unsigned int hash1, hash2;
1372

                
1373
    hash1 = g_str_hash(collection_name);
1374

                
1375
    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), iter)) {
1376
        do {
1377
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME_HASH, &hash2, -1);
1378

                
1379
            if (hash1 == hash2) {
1380
                char *name = NULL;
1381

                
1382
                gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME, &name, -1);
1383

                
1384
                if (!strcmp(collection_name, name)) {
1385
                    g_free(name);
1386
                    return TRUE;
1387
                }
1388

                
1389
                g_free(name);
1390
            }
1391
        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1392
    }
1393

                
1394
    return FALSE;
1395
}
1396

                
1397
static gboolean
1398
find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter)
1399
{
1400
    unsigned int hash1, hash2;
1401

                
1402
    if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), iter, &parent)) {
1403
        // Node contains no children. Shouldn't happen
1404
        return FALSE;
1405
    }
1406

                
1407
    hash1 = g_str_hash(subject);
1408

                
1409
    do {
1410
        gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT_HASH, &hash2, -1);
1411

                
1412
        if (hash1 == hash2) {
1413
            char *name = NULL;
1414

                
1415
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT, &name, -1);
1416

                
1417
            if (!strcmp(subject, name)) {
1418
                g_free(name);
1419
                return TRUE;
1420
            }
1421

                
1422
            g_free(name);
1423
        }
1424
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1425

                
1426
    return FALSE;
1427
}
1428

                
1429
static void
1430
foreach_file_func (const char *collection_name, const char *subject, const char *poster, ngint64 stamp, nguint64 file_size, nguint64 file_size_remaining, int position, int num_parts, int num_parts_downloaded, int num_parts_failed, NGTaskState status, const char *filename, gpointer data)
1431
{
1432
    GtkTreeStore *store = GTK_TREE_STORE(data);
1433
    GtkTreeIter iter, parent;
1434
    char size_str[64];
1435
    char size_remaining_str[64];
1436
    char stamp_str[64];
1437
    int progress;
1438
    char *state_str;
1439

                
1440
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1441
    g_return_if_fail(file_size_remaining >= 0);
1442

                
1443
    memset(&size_str, 0, sizeof(size_str));
1444
    nntpgrab_utils_calculate_file_size(file_size, size_str, sizeof(size_str) - 1);
1445

                
1446
    memset(&size_remaining_str, 0, sizeof(size_remaining_str));
1447
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining_str, sizeof(size_remaining_str) - 1);
1448

                
1449
    memset(&stamp_str, 0, sizeof(stamp_str));
1450
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1451

                
1452
    if (status == TASK_STATE_SKIPPED) {
1453
        progress = 0;
1454
    } else {
1455
        progress = calculate_progress(file_size, file_size_remaining);
1456
    }
1457

                
1458
    switch(status) {
1459
        case TASK_STATE_WAITING_FOR_DOWNLOAD:
1460
        case TASK_STATE_DOWNLOADING:
1461
            state_str = NULL;
1462
            break;
1463

                
1464
        case TASK_STATE_WAITING_FOR_DECODE:
1465
            state_str = _("Waiting");
1466
            break;
1467

                
1468
        case TASK_STATE_DECODING:
1469
            state_str = _("Decoding");
1470
            break;
1471

                
1472
        case TASK_STATE_FINISHED_COMPLETE:
1473
            state_str = _("Done");
1474
            break;
1475

                
1476
        case TASK_STATE_FINISHED_INCOMPLETE:
1477
            state_str = _("Incomplete");
1478
            break;
1479

                
1480
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1481
            state_str = _("Not available");
1482
            break;
1483

                
1484
        case TASK_STATE_SKIPPED:
1485
            state_str = _("Skipped");
1486
            break;
1487

                
1488
        default:
1489
            state_str = NULL;
1490
            break;
1491
    };
1492

                
1493
    gtk_tree_store_append(store, &iter, &parent);
1494
    gtk_tree_store_set(store, &iter,    FIELD_SUBJECT, subject,
1495
                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1496
                                        FIELD_POSTER, poster,
1497
                                        FIELD_STAMP_STR, stamp_str,
1498
                                        FIELD_FILESIZE, file_size,
1499
                                        FIELD_FILESIZE_STR, size_str,
1500
                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1501
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining_str,
1502
                                        FIELD_NUM_PARTS, num_parts,
1503
                                        FIELD_PARTS_DONE, num_parts_downloaded,
1504
                                        FIELD_PARTS_FAILED, num_parts_failed,
1505
                                        FIELD_PROGRESS, progress,
1506
                                        FIELD_STATE_STR, state_str,
1507
                                        FIELD_POSITION, position,
1508
                                        FIELD_REAL_FILENAME, filename, -1);
1509
}
1510

                
1511
static void
1512
foreach_group_func (const char *collection_name, const char *subject, const char *group, gpointer data)
1513
{
1514
    GtkTreeStore *store = GTK_TREE_STORE(data);
1515
    GtkTreeIter iter, parent;
1516
    char *old_groups;
1517
    char *new_groups;
1518

                
1519
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1520
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1521

                
1522
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_GROUPS, &old_groups, -1);
1523
    if (old_groups && strlen(old_groups) > 0) {
1524
        new_groups = g_strdup_printf("%s, %s", old_groups, group);
1525
    } else {
1526
        new_groups = g_strdup(group);
1527
    }
1528

                
1529
    gtk_tree_store_set(store, &iter, FIELD_GROUPS, new_groups, -1);
1530

                
1531
    g_free(new_groups);
1532
    g_free(old_groups);
1533
}
1534

                
1535
static void
1536
on_tree_view_select (GtkTreeSelection *selection, gpointer user_data)
1537
{
1538
    GtkWidget *btnRemoveItemFromQueue = nntpgrab_gui_base_get_widget("btnRemoveItemFromQueue");
1539
    GtkWidget *btnQueueMoveToTop = nntpgrab_gui_base_get_widget("btnQueueMoveToTop");
1540
    GtkWidget *btnQueueMoveUp = nntpgrab_gui_base_get_widget("btnQueueMoveUp");
1541
    GtkWidget *btnQueueMoveDown = nntpgrab_gui_base_get_widget("btnQueueMoveDown");
1542
    GtkWidget *btnQueueMoveToBottom = nntpgrab_gui_base_get_widget("btnQueueMoveToBottom");
1543

                
1544
    if (gtk_tree_selection_count_selected_rows(selection) == 0) {
1545
        // No items selected, disable the remove button and the move buttons
1546
        gtk_widget_set_sensitive(btnRemoveItemFromQueue, FALSE);
1547
        gtk_widget_set_sensitive(btnQueueMoveToTop, FALSE);
1548
        gtk_widget_set_sensitive(btnQueueMoveUp, FALSE);
1549
        gtk_widget_set_sensitive(btnQueueMoveDown, FALSE);
1550
        gtk_widget_set_sensitive(btnQueueMoveToBottom, FALSE);
1551
        return;
1552
    }
1553

                
1554
    // Enable the remove button and the move buttons
1555
    gtk_widget_set_sensitive(btnRemoveItemFromQueue, TRUE);
1556
    gtk_widget_set_sensitive(btnQueueMoveToTop, TRUE);
1557
    gtk_widget_set_sensitive(btnQueueMoveUp, TRUE);
1558
    gtk_widget_set_sensitive(btnQueueMoveDown, TRUE);
1559
    gtk_widget_set_sensitive(btnQueueMoveToBottom, TRUE);
1560
}
1561

                
1562
G_MODULE_EXPORT void
1563
on_btnPauseQueue_clicked(GtkWidget *caller, gpointer data)
1564
{
1565
    NGSchedularState active_state = nntpgrab_glue_schedular_get_state(glue);
1566

                
1567
    if (status_flag == SCHEDULAR_STATE_STOPPED && active_state == SCHEDULAR_STATE_STOPPING) {
1568
        GtkWidget *windowMain;
1569

                
1570
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
1571

                
1572
        nntpgrab_gui_base_dialog_show(windowMain, _("The schedular is currently being stopped"), GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
1573

                
1574
        g_signal_handlers_block_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1575

                
1576
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(caller), TRUE);
1577

                
1578
        g_signal_handlers_unblock_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1579

                
1580
        return;
1581
    }
1582

                
1583
    if (status_flag == SCHEDULAR_STATE_RUNNING) {
1584
        status_flag = SCHEDULAR_STATE_STOPPED;
1585
        nntpgrab_glue_schedular_stop(glue, FALSE);
1586
    } else {
1587
        status_flag = SCHEDULAR_STATE_RUNNING;
1588
        nntpgrab_glue_schedular_start(glue);
1589
    }
1590
}
1591

                
1592
static gboolean
1593
on_key_press_event(GtkWidget *widget, GdkEventKey *key, gpointer data)
1594
{
1595
    if (key->keyval == GDK_Delete) {
1596
        GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1597
        if (gtk_tree_selection_count_selected_rows(selection) > 0) {
1598
            on_btnRemoveItemFromQueue_clicked(widget, data);
1599
            return TRUE;
1600
        }
1601
    }
1602

                
1603
    return FALSE;
1604
}
1605

                
1606
static void
1607
schedular_state_changed_cb(NntpgrabGlue *obj, NGSchedularState state, const char *reason)
1608
{
1609
    GtkWidget *btnPauseQueue;
1610

                
1611
    if (state == SCHEDULAR_STATE_STOPPING) {
1612
        status_flag = SCHEDULAR_STATE_STOPPED;
1613
    } else {
1614
        status_flag = state;
1615
    }
1616

                
1617
    if (state == SCHEDULAR_STATE_STOPPING && reason != NULL) {
1618
        char *msg;
1619
        GtkWidget *windowMain;
1620
#ifdef HAVE_LIBNOTIFY
1621
        NotifyNotification *notification;
1622
#endif
1623

                
1624
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
1625
        msg = g_strdup_printf(_("The schedular has been paused due to an error:\n%s"), reason);
1626

                
1627
#ifdef HAVE_LIBNOTIFY
1628
#ifdef HAVE_LIBNOTIFY_0_7
1629
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL);
1630
#else
1631
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL, NULL);
1632
#endif
1633
        if (nntpgrab_gui_base_tray_icon_get_is_shown()) {
1634
#if GTK_CHECK_VERSION(2,10,0)
1635
#ifndef HAVE_LIBNOTIFY_0_7
1636
            notify_notification_attach_to_status_icon(notification, nntpgrab_gui_base_tray_get_status_icon());
1637
#endif
1638
#endif
1639
        }
1640
        notify_notification_show(notification, NULL);
1641
#endif
1642

                
1643
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK);
1644
        g_free(msg);
1645
    }
1646

                
1647
    btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1648
    g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1649

                
1650
    if (state == SCHEDULAR_STATE_RUNNING) {
1651
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), FALSE);
1652
    } else {
1653
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1654
    }
1655

                
1656
    g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1657

                
1658
}
1659

                
1660
static void
1661
traffic_monitor_update_cb (NntpgrabGlue *obj, int bytes_received1, int bytes_received2, int bytes_received3, int bytes_received4, int bytes_received5, int bytes_received6, int bytes_received7, int bytes_received8, int bytes_received9, int bytes_received10, guint64 last_update_stamp, double average)
1662
{
1663
    static int times_been_here = 10;
1664
    gboolean no_data_received = FALSE;
1665
    GtkTreeModel *model;
1666
    GtkWidget *treeDownloadQueue;
1667
    GtkTreeIter iter, parent;
1668
    guint64 total_estimated_time_remaining;
1669

                
1670
    if (bytes_received1 == 0 && bytes_received2 == 0 && bytes_received3 == 0 && bytes_received4 == 0 && bytes_received5 == 0 &&
1671
        bytes_received6 == 0 && bytes_received7 == 0 && bytes_received8 == 0 && bytes_received9 == 0 && bytes_received10 == 0) {
1672
            no_data_received = TRUE;
1673
    }
1674

                
1675
    // Only update on startup, on every 3 seconds, and when there has no data been received in the last 10 seconds
1676
    if (times_been_here < 3 && !no_data_received) {
1677
        times_been_here += 1;
1678
        return;
1679
    }
1680

                
1681
    if (no_data_received) {
1682
        times_been_here = 3;    // update the time remaining the next time we get here
1683
    } else {
1684
        times_been_here = 0;    // perform the next update in 10 seconds
1685
    }
1686

                
1687
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1688
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1689

                
1690
    if (!gtk_tree_model_get_iter_first(model, &parent)) {
1691
        // Nothing in the download queue
1692
        return;
1693
    }
1694

                
1695
    total_estimated_time_remaining = 0;
1696

                
1697
    do {
1698
        guint64 filesize;
1699
        int estimated_time_remaining;
1700
        char str_estimated_time_remaining[64];
1701
        char str_estimated_time_to_finish[64];
1702

                
1703
        gtk_tree_model_get(model, &parent, FIELD_FILESIZE_REMAINING, &filesize, -1);
1704

                
1705
        estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1706
                                                                                        bytes_received3, bytes_received4,
1707
                                                                                        bytes_received5, bytes_received6,
1708
                                                                                        bytes_received7, bytes_received8,
1709
                                                                                        bytes_received9, bytes_received10,
1710
                                                                                        filesize);
1711

                
1712
        if (estimated_time_remaining == -1) {
1713
            // Not enough data received to make an estimation
1714
            return;
1715
        }
1716

                
1717
        if (estimated_time_remaining == 0) {
1718
            // File is already downloaded
1719
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, "",
1720
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1721
        } else {
1722
            memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1723
            nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining + estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1724

                
1725
            memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1726
            nntpgrab_utils_get_readable_finished_time(total_estimated_time_remaining + estimated_time_remaining, str_estimated_time_to_finish, sizeof(str_estimated_time_to_finish) - 1);
1727

                
1728
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1729
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1730
        }
1731

                
1732
        // Walk through all the children
1733
        if (gtk_tree_model_iter_children(model, &iter, &parent)) {
1734
            do {
1735
                gtk_tree_model_get(model, &iter, FIELD_FILESIZE_REMAINING, &filesize, -1);
1736

                
1737
                if (filesize > 0) {
1738
                    estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1739
                                                                                                    bytes_received3, bytes_received4,
1740
                                                                                                    bytes_received5, bytes_received6,
1741
                                                                                                    bytes_received7, bytes_received8,
1742
                                                                                                    bytes_received9, bytes_received10,
1743
                                                                                                    filesize);
1744

                
1745
                    total_estimated_time_remaining += estimated_time_remaining;
1746

                
1747
                    memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1748
                    nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1749

                
1750
                    memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1751
                    nntpgrab_utils_get_readable_finished_time(total_estimated_time_remaining, str_estimated_time_to_finish, sizeof(str_estimated_time_to_finish));
1752

                
1753
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1754
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1755
                } else {
1756
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, "",
1757
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1758
                }
1759
            } while (gtk_tree_model_iter_next(model, &iter));
1760
        }
1761
    } while (gtk_tree_model_iter_next(model, &parent));
1762
}
1763

                
1764
static void
1765
config_changed(ConfigGUI *config, gpointer data)
1766
{
1767
    ConfigGUIOpts opts = config_gui_get_opts();
1768

                
1769
    gtk_tree_view_column_set_visible(columns[FIELD_PROGRESS], opts.show_column_progress);
1770
    gtk_tree_view_column_set_visible(columns[FIELD_SUBJECT], opts.show_column_subject);
1771
    gtk_tree_view_column_set_visible(columns[FIELD_POSTER], opts.show_column_poster);
1772
    gtk_tree_view_column_set_visible(columns[FIELD_GROUPS], opts.show_column_newsgroups);
1773
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_STR], opts.show_column_filesize);
1774
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_REMAINING_STR], opts.show_column_filesize_remaining);
1775
    gtk_tree_view_column_set_visible(columns[FIELD_STAMP_STR], opts.show_column_stamp);
1776
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_REMAINING], opts.show_column_time_remaining);
1777
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_TO_FINISH], opts.show_column_time_to_finish);
1778
}
1779

                
1780
void
1781
queue_initialize()
1782
{
1783
    GtkWidget *treeDownloadQueue;
1784
    GtkTreeStore *store;
1785
    GtkCellRenderer *cellRenderer;
1786
    GtkTreeSelection *selection;
1787

                
1788
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1789

                
1790
    cellRenderer = gtk_cell_renderer_progress_new();
1791
    columns[FIELD_PROGRESS] = gtk_tree_view_column_new_with_attributes(_("Progress"), cellRenderer, "value", FIELD_PROGRESS, "text", FIELD_STATE_STR, NULL);
1792
    gtk_tree_view_column_set_resizable(columns[FIELD_PROGRESS], TRUE);
1793
    gtk_tree_view_column_set_reorderable(columns[FIELD_PROGRESS], TRUE);
1794
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_PROGRESS], -1);
1795

                
1796
    cellRenderer = gtk_cell_renderer_text_new();
1797
    columns[FIELD_SUBJECT] = gtk_tree_view_column_new_with_attributes(_("Subject"), cellRenderer, "text", FIELD_SUBJECT, NULL);
1798
    gtk_tree_view_column_set_resizable(columns[FIELD_SUBJECT], TRUE);
1799
    gtk_tree_view_column_set_reorderable(columns[FIELD_SUBJECT], TRUE);
1800
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_SUBJECT], -1);
1801

                
1802
    cellRenderer = gtk_cell_renderer_text_new();
1803
    columns[FIELD_POSTER] = gtk_tree_view_column_new_with_attributes(_("Poster"), cellRenderer, "text", FIELD_POSTER, NULL);
1804
    gtk_tree_view_column_set_resizable(columns[FIELD_POSTER], TRUE);
1805
    gtk_tree_view_column_set_reorderable(columns[FIELD_POSTER], TRUE);
1806
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_POSTER], -1);
1807

                
1808
    cellRenderer = gtk_cell_renderer_text_new();
1809
    columns[FIELD_GROUPS] = gtk_tree_view_column_new_with_attributes(_("Group"), cellRenderer, "text", FIELD_GROUPS, NULL);
1810
    gtk_tree_view_column_set_resizable(columns[FIELD_GROUPS], TRUE);
1811
    gtk_tree_view_column_set_reorderable(columns[FIELD_GROUPS], TRUE);
1812
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_GROUPS], -1);
1813

                
1814
    cellRenderer = gtk_cell_renderer_text_new();
1815
    columns[FIELD_FILESIZE_STR] = gtk_tree_view_column_new_with_attributes(_("File size"), cellRenderer, "text", FIELD_FILESIZE_STR, NULL);
1816
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_STR], TRUE);
1817
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_STR], TRUE);
1818
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_STR], -1);
1819

                
1820
    cellRenderer = gtk_cell_renderer_text_new();
1821
    columns[FIELD_FILESIZE_REMAINING_STR] = gtk_tree_view_column_new_with_attributes(_("File size remaining"), cellRenderer, "text", FIELD_FILESIZE_REMAINING_STR, NULL);
1822
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1823
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1824
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_REMAINING_STR], -1);
1825

                
1826
    cellRenderer = gtk_cell_renderer_text_new();
1827
    columns[FIELD_STAMP_STR] = gtk_tree_view_column_new_with_attributes( _("Stamp"), cellRenderer, "text", FIELD_STAMP_STR, NULL);
1828
    gtk_tree_view_column_set_resizable(columns[FIELD_STAMP_STR], TRUE);
1829
    gtk_tree_view_column_set_reorderable(columns[FIELD_STAMP_STR], TRUE);
1830
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_STAMP_STR], -1);
1831

                
1832
    cellRenderer = gtk_cell_renderer_text_new();
1833
    columns[FIELD_ESTIMATED_TIME_REMAINING] = gtk_tree_view_column_new_with_attributes( _("Time remaining"), cellRenderer, "text", FIELD_ESTIMATED_TIME_REMAINING, NULL);
1834
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1835
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1836
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_REMAINING], -1);
1837

                
1838
    cellRenderer = gtk_cell_renderer_text_new();
1839
    columns[FIELD_ESTIMATED_TIME_TO_FINISH] = gtk_tree_view_column_new_with_attributes( _("Time to finish"), cellRenderer, "text", FIELD_ESTIMATED_TIME_TO_FINISH, NULL);
1840
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1841
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1842
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_TO_FINISH], -1);
1843

                
1844
    store = gtk_tree_store_new( LAST_FIELD,
1845
                                G_TYPE_STRING /* FIELD_COLLECTION_NAME */,
1846
                                G_TYPE_UINT   /* FIELD_COLLECTION_NAME_HASH */,
1847
                                G_TYPE_STRING /* FIELD_SUBJECT */,
1848
                                G_TYPE_UINT   /* FIELD_SUBJECT_HASH */,
1849
                                G_TYPE_STRING /* FIELD_POSTER */,
1850
                                G_TYPE_STRING /* FIELD_GROUPS */,
1851
                                G_TYPE_STRING /* FIELD_STAMP_STR */,
1852
                                G_TYPE_INT /* FIELD_PROGRESS */,
1853
                                G_TYPE_UINT64 /* FIELD_FILESIZE */,
1854
                                G_TYPE_STRING /* FIELD_FILESIZE_STR */,
1855
                                G_TYPE_UINT64 /* FIELD_FILESIZE_REMAINING */,
1856
                                G_TYPE_STRING /* FIELD_FILESIZE_REMAINING_STR */,
1857
                                G_TYPE_INT /* FIELD_NUM_PARTS */,
1858
                                G_TYPE_INT /* FIELD_PARTS_DONE */,
1859
                                G_TYPE_INT /* FIELD_PARTS_FAILED */,
1860
                                G_TYPE_STRING /* FIELD_STATE_STR */,
1861
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_REMAINING */,
1862
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_TO_FINISH */,
1863
                                G_TYPE_INT /* FIELD_POSITION */,
1864
                                G_TYPE_STRING /* FIELD_REAL_FILENAME */);
1865

                
1866
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeDownloadQueue), GTK_TREE_MODEL(store));
1867

                
1868
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
1869
    gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1870

                
1871
    g_signal_connect(selection, "changed", G_CALLBACK(on_tree_view_select), NULL);
1872
    on_tree_view_select(selection, NULL);
1873

                
1874
    nntpgrab_glue_schedular_foreach_task(glue, foreach_collection_func, foreach_file_func, foreach_group_func, store);
1875

                
1876
    nntpgrab_glue_signal_connect(glue, "collection_added", NG_CALLBACK(collection_added), NULL);
1877
    nntpgrab_glue_signal_connect(glue, "collection_removed", NG_CALLBACK(collection_removed), NULL);
1878
    nntpgrab_glue_signal_connect(glue, "collection_modified", NG_CALLBACK(collection_modified), NULL);
1879
    nntpgrab_glue_signal_connect(glue, "file_added", NG_CALLBACK(file_added), NULL);
1880
    nntpgrab_glue_signal_connect(glue, "file_removed", NG_CALLBACK(file_removed), NULL);
1881
    nntpgrab_glue_signal_connect(glue, "file_state_changed", NG_CALLBACK(file_state_changed), NULL);
1882
    nntpgrab_glue_signal_connect(glue, "file_download_state_update", NG_CALLBACK(file_download_state_update), NULL);
1883
    nntpgrab_glue_signal_connect(glue, "schedular_state_changed", NG_CALLBACK(schedular_state_changed_cb), NULL);
1884
    nntpgrab_glue_signal_connect(glue, "traffic_monitor_update", NG_CALLBACK(traffic_monitor_update_cb), NULL);
1885
    nntpgrab_glue_signal_connect(glue, "task_moved", NG_CALLBACK(task_moved_cb), NULL);
1886
    nntpgrab_glue_signal_connect(glue, "collection_moved", NG_CALLBACK(collection_moved_cb), NULL);
1887

                
1888
    g_signal_connect(config_gui_get_object(), "config_changed", G_CALLBACK(config_changed), NULL);
1889
    config_changed(NULL, NULL);
1890

                
1891
    g_signal_connect(treeDownloadQueue, "key-press-event", G_CALLBACK(on_key_press_event), NULL);
1892

                
1893
    if (nntpgrab_glue_get_is_standalone(glue)) {
1894
        nntpgrab_glue_schedular_start(glue);
1895
    }
1896

                
1897
    status_flag = nntpgrab_glue_schedular_get_state(glue);
1898
    if (status_flag == SCHEDULAR_STATE_STOPPING || status_flag == SCHEDULAR_STATE_STOPPED) {
1899
        GtkWidget *btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1900

                
1901
        g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1902
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1903
        g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1904
    }
1905
}