Statistics
| Revision:

root / trunk / client / gui / queue.c @ 1777

History | View | Annotate | Download (74.5 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
static gboolean find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter);
35
static gboolean find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter);
36

                
37
static NGSchedularState status_flag = SCHEDULAR_STATE_RUNNING;     // Schedular is running by default
38

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

                
64
#ifdef WIN32
65
static void
66
open_file(const char *file, gboolean open_folder)
67
{
68
    char *mode;
69
    char *file_localized;
70
    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
71

                
72
    if (open_folder) {
73
        mode = "explore";
74
    } else {
75
        mode = "open";
76
    }
77

                
78
    file_localized = g_win32_locale_filename_from_utf8(file);
79
    g_return_if_fail(file_localized != NULL);
80

                
81
    if ((int) ShellExecuteA((HWND) GDK_WINDOW_HWND(windowMain->window), mode, file_localized, NULL, file_localized, SW_SHOW) < 32) {
82
        char *msg;
83
        GtkWidget *windowMain;
84

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

                
95
    g_free(file_localized);
96
}
97
#else
98
static void
99
open_file(const char *folder_unquoted, gboolean open_folder)
100
{
101
    char *cmd = NULL;
102
    GError *err = NULL;
103
    char *folder = g_shell_quote(folder_unquoted);
104
    gboolean ret;
105

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

                
117
    ret = g_spawn_command_line_async(cmd, &err);
118
#endif /* GTK_CHECK_VERSION(2,14,0) */
119

                
120
    if (!ret) {
121
        char *msg;
122
        GtkWidget *windowMain;
123

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

                
135
    g_free(cmd);
136
}
137
#endif
138

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

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

                
174
    // Is at least one item selected?
175
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(caller));
176
    if (gtk_tree_selection_count_selected_rows(selection) == 0)
177
    {
178
        GtkTreePath *path;
179

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

                
190
        gtk_tree_selection_unselect_all(selection);
191
        gtk_tree_selection_select_path(selection, path);
192
        gtk_tree_path_free(path);
193
    }
194

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

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

                
220
    list = gtk_tree_selection_get_selected_rows(selection, &model);
221

                
222
    gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
223
    gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &progress_str, -1);
224

                
225
    if (progress_str != NULL) {
226
        if (gtk_tree_selection_count_selected_rows(selection) == 1) {
227
            if (!strcmp(progress_str, _("Done")) ||
228
                !strcmp(progress_str, _("Incomplete"))) {
229

                
230

                
231
                if (nntpgrab_glue_get_is_standalone(glue)) {
232
#if GTK_CHECK_VERSION(2,12,0)
233
                    g_value_set_boolean (&val, TRUE);
234
                    g_object_set_property(G_OBJECT(menuitemExecuteItem), "sensitive", &val);
235
#else
236
                    gtk_widget_set_sensitive(menuitemExecuteItem, TRUE);
237
#endif
238
                }
239
            }
240
        }
241
    }
242

                
243
    if (progress_str && !strcmp(progress_str, _("Skipped"))) {
244
        str_optional = _("Forcefully download file(s)");
245
    } else {
246
        str_optional = _("Don't download file(s)");
247
    }
248

                
249
    menuitemMarkOptional = nntpgrab_gui_base_get_object("menuitemMarkOptional");
250
    g_value_unset(&val);
251
    g_value_init(&val, G_TYPE_STRING);
252
    g_value_set_string(&val, str_optional);
253
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "label", &val);
254

                
255
    if (gtk_tree_model_iter_parent(model, &parent, &iter) &&
256
        (!progress_str || strcmp(progress_str, _("Done"))) &&
257
        (!progress_str || strcmp(progress_str, _("Incomplete"))) &&
258
        (!progress_str || strcmp(progress_str, _("Not available")))) {
259

                
260
        show_optional_button = TRUE;
261
    } else {
262
        show_optional_button = FALSE;
263
    }
264
#if GTK_CHECK_VERSION(2,12,0)
265
    g_value_unset(&val);
266
    g_value_init(&val, G_TYPE_BOOLEAN);
267
    g_value_set_boolean (&val, show_optional_button);
268
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "sensitive", &val);
269
#else
270
    gtk_widget_set_sensitive(menuitemMarkOptional, show_optional_button);
271
#endif
272

                
273
    g_free(progress_str);
274
    g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
275
    g_list_free (list);
276

                
277
    // Do we have multiple items selected and do these all belong to the same collection ?
278
    // TODO
279

                
280
    menuDownloadQueue = nntpgrab_gui_base_get_widget("menuDownloadQueue");
281
    gtk_menu_popup(GTK_MENU(menuDownloadQueue), NULL, NULL, NULL, NULL, event->button, event->time);
282

                
283
    return TRUE;
284
}
285

                
286
G_MODULE_EXPORT void
287
on_menuitemRestartTask_activate(GtkWidget *caller, gpointer data)
288
{
289
    GtkWidget *treeDownloadQueue;
290
    GtkTreeSelection *selection;
291
    GList *list, *selectedRows;
292
    GtkTreeModel *model;
293
    GtkTreeIter iter, parent;
294
    char *collection_name = NULL;
295
    char *subject = NULL;
296
    char *errmsg = NULL;
297

                
298
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
299
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
300

                
301
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
302

                
303
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
304

                
305
    list = selectedRows;
306
    while (list) {
307
        GtkTreePath *path = list->data;
308

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

                
317
                memset(&msg, 0, sizeof(msg));
318
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart collection due to an error:\n%s"), errmsg);
319
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
320
                g_free(errmsg);
321
            }
322

                
323
            g_free(collection_name);
324
        } else {
325
            // Restart one item from the collection
326
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
327
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
328

                
329
            if (!nntpgrab_glue_schedular_restart_task(glue, collection_name, subject, &errmsg)) {
330
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
331
                char msg[1024];
332

                
333
                memset(&msg, 0, sizeof(msg));
334
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart task due to an error:\n%s"), errmsg);
335
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
336
                g_free(errmsg);
337
            }
338

                
339
            g_free(collection_name);
340
            g_free(subject);
341
        }
342

                
343
        list = g_list_next(list);
344
    }
345

                
346
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
347
    g_list_free (selectedRows);
348

                
349
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
350
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
351
        char msg[1024];
352

                
353
        memset(&msg, 0, sizeof(msg));
354
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
355
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
356
        g_free(errmsg);
357
    }
358
}
359

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

                
373
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
374
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
375

                
376
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
377

                
378
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
379

                
380
    list = selectedRows;
381
    while (list) {
382
        GtkTreePath *path = list->data;
383

                
384
        gtk_tree_model_get_iter(model, &iter, path);
385
        if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
386
            // Item contains no parent
387
        } else {
388
            // Restart one item from the collection
389
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
390
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
391

                
392
            if (!flag_set) {
393
                char *state = NULL;
394
                gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &state, -1);
395
                if (state && !strcmp(state, _("Skipped"))) {
396
                    is_optional = FALSE;
397
                } else {
398
                    is_optional = TRUE;
399
                }
400
                flag_set = TRUE;
401
                g_free(state);
402
            }
403

                
404
            if (!nntpgrab_glue_schedular_mark_task_optional(glue, collection_name, subject, is_optional)) {
405
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
406
                nntpgrab_gui_base_dialog_show(windowMain, _("Unable to change the status of the selected file(s)"), GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
407
            }
408

                
409
            g_free(collection_name);
410
            g_free(subject);
411
        }
412

                
413
        list = g_list_next(list);
414
    }
415

                
416
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
417
    g_list_free (selectedRows);
418
}
419

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

                
432
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
433
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
434

                
435
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
436

                
437
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
438

                
439
    list = selectedRows;
440
    while (list) {
441
        GtkTreePath *path = list->data;
442
        GtkTreeIter iter;
443
        char *name = NULL;
444

                
445
        gtk_tree_model_get_iter(model, &iter, path);
446
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &name, -1);
447

                
448
        if (name == NULL || strlen(name) == 0) {
449
            GtkTreeIter parent;
450

                
451
            if (name) {
452
                g_free(name);
453
            }
454

                
455
            // Look at the parent
456
            gtk_tree_model_iter_parent(model, &parent, &iter);
457
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &name, -1);
458
        }
459

                
460
        g_return_if_fail(name != NULL);
461

                
462
        if (!collection_name) {
463
            collection_name = g_strdup(name);
464
        } else {
465
            if (strcmp(name, collection_name)) {
466
                // A previous found collection name and this collection name differ!
467
                multiple_collections = TRUE;
468
            }
469
        }
470

                
471
        g_free(name);
472

                
473
        list = g_list_next(list);
474
    }
475

                
476
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
477
    g_list_free (selectedRows);
478

                
479
    opts = nntpgrab_glue_config_get_opts(glue);
480

                
481
    if (multiple_collections) {
482
        // Open the default download folder
483
        open_file(opts.download_directory, TRUE);
484
    } else {
485
        // Open the download folder of the active collection
486
        char *folder = g_strdup_printf("%s%s%s", opts.download_directory, G_DIR_SEPARATOR_S, collection_name);
487

                
488
        if (!g_file_test(folder, G_FILE_TEST_IS_DIR)) {
489
            // Folder doesn't exist yet, fall back to default download directory
490
            open_file(opts.download_directory, TRUE);
491
        } else {
492
            open_file(folder, TRUE);
493
        }
494

                
495
        g_free(folder);
496
    }
497

                
498
    if (collection_name) {
499
        g_free(collection_name);
500
    }
501
}
502

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

                
519
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
520
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
521

                
522
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) == 1);
523

                
524
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
525

                
526
    path = selectedRows->data;
527

                
528
    gtk_tree_model_get_iter(model, &iter, path);
529
    gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
530

                
531
    // Look at the parent
532
    gtk_tree_model_iter_parent(model, &parent, &iter);
533
    gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
534
    gtk_tree_model_get(model, &iter, FIELD_REAL_FILENAME, &real_filename, -1);
535

                
536
    g_return_if_fail(real_filename != NULL);
537

                
538
    opts = nntpgrab_glue_config_get_opts(glue);
539
    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);
540

                
541
    open_file(full_filename, FALSE);
542

                
543
    g_free(full_filename);
544
    g_free(subject);
545
    g_free(real_filename);
546
    g_free(collection_name);
547
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
548
    g_list_free (selectedRows);
549
}
550

                
551
typedef enum {
552
    MOVE_DIRECTION_TOP,
553
    MOVE_DIRECTION_UP,
554
    MOVE_DIRECTION_DOWN,
555
    MOVE_DIRECTION_BOTTOM
556
} move_direction;
557

                
558
static void
559
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)
560
{
561
    GtkWidget *treeDownloadQueue;
562
    GtkTreeModel *store;
563
    GtkTreeIter parent, orig_iter, new_iter;
564
    int i;
565
    int num_items;
566

                
567
#if 0 
568
    g_print("%s: orig_collection_name = %s\n", __PRETTY_FUNCTION__, orig_collection_name);
569
    g_print("%s: subject = %s\n", __PRETTY_FUNCTION__, subject);
570
    g_print("%s: new_collection_name = %s\n", __PRETTY_FUNCTION__, new_collection_name);
571
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
572
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
573
#endif
574

                
575
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
576
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
577

                
578
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), new_collection_name, &parent));
579
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &orig_iter));
580

                
581
    num_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &parent);
582
    if (num_items == 1) {
583
        /* List only contains one element so we don't need to move anything */
584
        return;
585
    }
586

                
587
    if (new_position == -1) {
588
        i = 1;
589
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
590
        while (i != num_items) {
591
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
592
            i++;
593
        }
594

                
595
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
596
    } else {
597
        i = 0;
598
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
599
        while (i != new_position) {
600
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
601
            i++;
602
        }
603

                
604
        if (old_position < new_position) {
605
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
606
        } else {
607
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
608
        }
609
    }
610
}
611

                
612
static void
613
collection_moved_cb(NntpgrabGlue *obj, const char *collection_name, int old_position, int new_position)
614
{
615
    GtkWidget *treeDownloadQueue;
616
    GtkTreeModel *store;
617
    GtkTreeIter orig_iter, new_iter;
618
    int i;
619

                
620
#if 0 
621
    g_print("%s: collection_name = %s\n", __PRETTY_FUNCTION__, collection_name);
622
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
623
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
624
#endif
625

                
626
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
627
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
628

                
629
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &orig_iter));
630
    g_return_if_fail(new_position <= (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL)));
631

                
632
    if (new_position == -1) {
633
        new_position = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
634

                
635
        i = 1;
636
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
637
        while (i != new_position) {
638
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
639
            i++;
640
        }
641

                
642
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
643
    } else {
644
        i = 0;
645
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
646
        while (i != new_position) {
647
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
648
            i++;
649
        }
650

                
651
        if (old_position < new_position) {
652
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
653
        } else {
654
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
655
        }
656
    }
657
}
658

                
659
static void
660
move_item(move_direction direction)
661
{
662
    char *collection_name = NULL;
663
    char *subject = NULL;
664
    int position = -1;
665
    GtkWidget *treeDownloadQueue;
666
    GtkTreeSelection *selection;
667
    GList *list, *selectedRows;
668
    GtkTreeModel *model;
669
    GList *references = NULL;
670
    char *errmsg = NULL;
671

                
672
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
673
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
674

                
675
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
676

                
677
    // Duplicate the list with selected rows into GtkTreeRowReference's as the position of
678
    // selected rows can change during the loop later on
679

                
680
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
681

                
682
    list = selectedRows;
683
    while (list) {
684
        GtkTreePath *path = list->data;
685
        GtkTreeRowReference *ref;
686

                
687
        ref = gtk_tree_row_reference_new(model, path);
688
        references = g_list_append(references, ref);
689

                
690
        list = g_list_next(list);
691
    }
692

                
693
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
694
    g_list_free (selectedRows);
695

                
696
    if (direction == MOVE_DIRECTION_DOWN || direction == MOVE_DIRECTION_TOP) {
697
        references = g_list_reverse(references);
698
    }
699

                
700
    list = references;
701
    while (list) {
702
        GtkTreeRowReference *ref = list->data;
703
        GtkTreePath *path;
704
        GtkTreeIter iter;
705
        char *path_str;
706
        char **values;
707
        gboolean is_child = FALSE;
708

                
709
        if (collection_name) {
710
            g_free(collection_name);
711
            collection_name = NULL;
712
        }
713

                
714
        if (subject) {
715
            g_free(subject);
716
            subject = NULL;
717
        }
718

                
719
        path = gtk_tree_row_reference_get_path(ref);
720
        if (!path) {
721
            list = g_list_next(list);
722
            gtk_tree_row_reference_free(ref);
723
            continue;
724
        }
725

                
726
        gtk_tree_model_get_iter(model, &iter, path);
727
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, FIELD_SUBJECT, &subject, -1);
728

                
729
        path_str = gtk_tree_path_to_string(path);
730
        values = g_strsplit(path_str, ":", 0);
731
        g_return_if_fail(values != NULL);
732
        if (values[0]) {
733
            position = atoi(values[0]);
734
            if (values[1]) {
735
                position = atoi(values[1]);
736
            }
737
        }
738

                
739
        g_strfreev(values);
740
        g_free(path_str);
741

                
742
        if (collection_name == NULL || strlen(collection_name) == 0) {
743
            GtkTreeIter parent;
744

                
745
            // Look at the parent
746
            gtk_tree_model_iter_parent(model, &parent, &iter);
747
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
748

                
749
            is_child = TRUE;
750
        }
751

                
752
        g_assert(collection_name);
753

                
754
        switch (direction) {
755
            case MOVE_DIRECTION_TOP:
756
                if (is_child) {
757
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, 0));
758
                } else {
759
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, 0));
760
                }
761
                break;
762

                
763
            case MOVE_DIRECTION_UP:
764
                if (position == 0) {
765
                    break;
766
                }
767
                if (is_child) {
768
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position - 1));
769
                } else {
770
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position - 1));
771
                }
772
                break;
773

                
774
            case MOVE_DIRECTION_DOWN:
775
                if (is_child) {
776
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position + 1));
777
                } else {
778
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position + 1));
779
                }
780
                break;
781

                
782
            case MOVE_DIRECTION_BOTTOM:
783
                if (is_child) {
784
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, -1));
785
                } else {
786
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, -1));
787
                }
788
                break;
789

                
790
            default:
791
                break;
792
        }
793

                
794
        list = g_list_next(list);
795
        gtk_tree_path_free(path);
796
        gtk_tree_row_reference_free(ref);
797
    }
798

                
799
    g_list_free(references);
800

                
801
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
802
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
803
        char msg[1024];
804

                
805
        memset(&msg, 0, sizeof(msg));
806
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
807
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
808
        g_free(errmsg);
809
    }
810

                
811
    if (collection_name) {
812
        g_free(collection_name);
813
    }
814

                
815
    if (subject) {
816
        g_free(subject);
817
    }
818
}
819

                
820
G_MODULE_EXPORT void
821
on_menuitemMoveToTop_activate(GtkWidget *caller, gpointer data)
822
{
823
    move_item(MOVE_DIRECTION_TOP);
824
}
825

                
826
G_MODULE_EXPORT void
827
on_menuitemMoveUp_activate(GtkWidget *caller, gpointer data)
828
{
829
    move_item(MOVE_DIRECTION_UP);
830
}
831

                
832
G_MODULE_EXPORT void
833
on_menuitemMoveDown_activate(GtkWidget *caller, gpointer data)
834
{
835
    move_item(MOVE_DIRECTION_DOWN);
836
}
837

                
838
G_MODULE_EXPORT void
839
on_menuitemMoveToBottom_activate(GtkWidget *caller, gpointer data)
840
{
841
    move_item(MOVE_DIRECTION_BOTTOM);
842
}
843

                
844
G_MODULE_EXPORT void
845
on_btnRemoveItemFromQueue_clicked(GtkWidget *caller, gpointer data)
846
{
847
    GtkWidget *treeDownloadQueue;
848
    GtkTreeSelection *selection;
849
    GList *list, *selectedRows, *list_new;
850
    GtkTreeModel *model;
851
    char *errmsg = NULL;
852

                
853
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
854
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
855

                
856
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
857

                
858
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
859

                
860
    // Duplicate the list and transform the items to GtkTreeRowReferences
861
    // This is because we are going to delete items in the tree
862
    list = selectedRows;
863
    list_new = NULL;
864
    while (list) {
865
        GtkTreePath *path = list->data;
866

                
867
        list_new = g_list_append(list_new, gtk_tree_row_reference_new(model, path));
868

                
869
        list = g_list_next(list);
870
    }
871

                
872
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
873
    g_list_free (selectedRows);
874

                
875
    // Now loop trough all the items and remove the downloads tasks
876
    list = list_new;
877
    while (list) {
878
        GtkTreeRowReference *ref = list->data;
879

                
880
        if (gtk_tree_row_reference_valid(ref)) {
881
            GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
882
            GtkTreeIter iter, parent;
883
            GList *list_tmp;
884
            char *collection_name = NULL;
885
            char *subject = NULL;
886
            gboolean item_is_already_removed = FALSE;
887

                
888
            /* Did we already try to remove the parent of the active item? */
889
            list_tmp = g_list_previous(list);
890
            while (list_tmp) {
891
                GtkTreeRowReference *ref_tmp = list_tmp->data;
892
                GtkTreePath *path_tmp = gtk_tree_row_reference_get_path(ref_tmp);
893

                
894
                if (gtk_tree_path_is_ancestor(path_tmp, path)) {
895
                    /* Hey, the collection itself is already removed! */
896
                    item_is_already_removed = TRUE;
897
                }
898

                
899
                gtk_tree_path_free(path_tmp);
900

                
901
                list_tmp = g_list_previous(list_tmp);
902
            }
903

                
904
            gtk_tree_model_get_iter(model, &iter, path);
905
            gtk_tree_path_free(path);
906

                
907
            if (item_is_already_removed) {
908
                list = g_list_next(list);
909
                continue;
910
            }
911

                
912
            if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
913
                // Item contains no parent, delete the whole collection
914
                gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, -1);
915
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, NULL, &errmsg)) {
916
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
917
                    char msg[1024];
918

                
919
                    memset(&msg, 0, sizeof(msg));
920
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing collection(s):\n%s"), errmsg);
921
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
922
                    g_free(errmsg);
923
                }
924

                
925
                g_free(collection_name);
926
            } else {
927
                // Delete one item from the collection
928
                gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
929
                gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
930

                
931
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, subject, &errmsg)) {
932
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
933
                    char msg[1024];
934

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

                
941
                g_free(collection_name);
942
                g_free(subject);
943
            }
944
        }
945

                
946
        list = g_list_next(list);
947
    }
948

                
949
    /* Traverse the list again to free all the row references */
950
    list = list_new;
951
    while (list) {
952
        gtk_tree_row_reference_free((GtkTreeRowReference*) list->data);
953
        list = g_list_next(list);
954
    }
955
    g_list_free(list_new);
956

                
957
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
958
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
959
        char msg[1024];
960

                
961
        memset(&msg, 0, sizeof(msg));
962
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
963
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
964
        g_free(errmsg);
965
    }
966
}
967

                
968
static void
969
generate_stamp(time_t stamp, char *buf, int buf_len)
970
{
971
    struct tm *now;
972

                
973
    if ((now = localtime(&stamp)) == NULL) {
974
        // Date could not be parted
975
        buf[0] = '\0';
976
    }
977

                
978
    strftime(buf, buf_len, "%c", now);
979
}
980

                
981
static int
982
calculate_progress(guint64 file_size, guint64 file_size_remaining)
983
{
984
    double progress;
985
    int progress_int;
986

                
987
    // Prevent a division by zero
988
    if (file_size_remaining == 0) {
989
        return 100;
990
    }
991

                
992
    progress = (double) file_size - file_size_remaining;
993
    progress /= file_size;
994
    progress *= 100;
995
    progress_int = (int) progress;
996

                
997
    if (progress_int > 100) {
998
        g_print(__FILE__ ":%i progress overflow!\n", __LINE__);
999
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1000
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1001
        progress_int = 100;
1002
    }
1003

                
1004
    if (progress < 0) {
1005
        g_print(__FILE__ ":%i progress underflow!\n", __LINE__);
1006
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1007
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1008
        progress_int = 0;
1009
    }
1010

                
1011
    return progress_int;
1012
}
1013

                
1014
static void
1015
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)
1016
{
1017
    GtkWidget *treeDownloadQueue;
1018
    GtkTreeModel *store;
1019
    GtkTreeIter iter, parent;
1020
    char *state;
1021
    int progress;
1022
    char size_remaining[64];
1023
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1024
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1025

                
1026
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1027
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1028

                
1029
    /* If the file has just been skipped, ignore this event */
1030
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_STATE_STR, &state, -1);
1031
    if (state && !strcmp(state, _("Skipped"))) {
1032
        g_free(state);
1033
        return;
1034
    }
1035
    g_free(state);
1036

                
1037
    // Update the parent
1038
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1039

                
1040
    memset(&size_remaining, 0, sizeof(size_remaining));
1041
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1042

                
1043
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1044
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1045
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1046

                
1047
    // Update the child
1048
    progress = calculate_progress(file_size, file_size_remaining);
1049
    memset(&size_remaining, 0, sizeof(size_remaining));
1050
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1051

                
1052
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, progress,
1053
                                                        FIELD_PARTS_DONE, num_parts_done,
1054
                                                        FIELD_PARTS_FAILED, num_parts_failed,
1055
                                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1056
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1057
}
1058

                
1059
static void
1060
collection_added (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1061
{
1062
    GtkWidget *treeDownloadQueue;
1063
    GtkTreeModel *store;
1064
    GtkTreeIter iter;
1065

                
1066
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1067
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1068

                
1069
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, NULL);
1070
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_COLLECTION_NAME, collection_name,
1071
                                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1072
                                                        FIELD_SUBJECT, collection_name,
1073
                                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1074
                                                        FIELD_POSTER, poster,
1075
                                                        FIELD_STATE_STR, NULL, -1);
1076
}
1077

                
1078
static void
1079
collection_removed (NntpgrabGlue *obj, const char *collection_name)
1080
{
1081
    GtkWidget *treeDownloadQueue;
1082
    GtkTreeModel *store;
1083
    GtkTreeIter iter;
1084

                
1085
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1086
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1087

                
1088
    g_return_if_fail(find_iter_by_collection_name(store, collection_name, &iter));
1089

                
1090
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1091
}
1092

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

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

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

                
1105
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FIELD_POSTER, poster, -1);
1106
}
1107

                
1108
static void
1109
file_added (NntpgrabGlue *obj, const char *collection_name, const char *subject, const char *poster, guint64 stamp, guint64 file_size, guint64 total_file_size, guint64 total_file_size_remaining, NGTaskState state, int num_parts, GList *groups)
1110
{
1111
    GtkWidget *treeDownloadQueue;
1112
    GtkTreeModel *store;
1113
    GtkTreeIter parent, iter;
1114
    char size[64];
1115
    char size_remaining[64];
1116
    char total_groups[256];
1117
    char stamp_str[64];
1118
    GList *list;
1119
    int progress;
1120

                
1121
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1122
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1123

                
1124
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1125
    g_return_if_fail(file_size > 0);
1126

                
1127
    list = groups;
1128
    memset(&total_groups, 0, sizeof(total_groups));
1129
    while (list) {
1130
        if (total_groups[0] != '\0') {
1131
            char tmp[256];
1132

                
1133
            memset(tmp, 0, sizeof(tmp));
1134
            snprintf(tmp, sizeof(tmp) - 1, "%s, %s", total_groups, (const char *) list->data);
1135
            strcpy(total_groups, tmp);
1136
        } else {
1137
            strncpy(total_groups, list->data, sizeof(total_groups) - 1);
1138
        }
1139

                
1140
        list = g_list_next(list);
1141
    }
1142

                
1143
    memset(size, 0, sizeof(size));
1144
    nntpgrab_utils_calculate_file_size(file_size, size, sizeof(size) - 1);
1145

                
1146
    memset(&stamp_str, 0, sizeof(stamp_str));
1147
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1148

                
1149
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, &parent);
1150
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_SUBJECT, subject,
1151
                                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1152
                                                        FIELD_POSTER, poster,
1153
                                                        FIELD_GROUPS, total_groups,
1154
                                                        FIELD_STAMP_STR, stamp_str,
1155
                                                        FIELD_FILESIZE, file_size,
1156
                                                        FIELD_FILESIZE_STR, size,
1157
                                                        FIELD_FILESIZE_REMAINING, file_size,
1158
                                                        FIELD_FILESIZE_REMAINING_STR, size,
1159
                                                        FIELD_NUM_PARTS, num_parts,
1160
                                                        FIELD_STATE_STR, NULL, -1);
1161

                
1162
    if (state == TASK_STATE_SKIPPED) {
1163
        gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1164
                                                            FIELD_FILESIZE_REMAINING, (guint64) 0,
1165
                                                            FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1166
    }
1167

                
1168
    // Update the parent
1169
    memset(size, 0, sizeof(size));
1170
    nntpgrab_utils_calculate_file_size(total_file_size, size, sizeof(size) - 1);
1171

                
1172
    memset(size_remaining, 0, sizeof(size_remaining));
1173
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1174
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1175

                
1176
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1177
                                                        FIELD_FILESIZE, total_file_size,
1178
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1179
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1180
                                                        FIELD_PROGRESS, progress, -1);
1181
}
1182

                
1183
static void
1184
file_removed (NntpgrabGlue *obj, const char *collection_name, const char *subject, guint64 total_file_size, guint64 total_file_size_remaining, gpointer data)
1185
{
1186
    GtkWidget *treeDownloadQueue;
1187
    GtkTreeModel *store;
1188
    GtkTreeIter iter, parent;
1189
    int progress;
1190
    char size[64];
1191
    char size_remaining[64];
1192

                
1193
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1194
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1195

                
1196
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1197
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1198

                
1199
    memset(&size, 0, sizeof(size));
1200
    nntpgrab_utils_calculate_file_size(total_file_size, size, sizeof(size) - 1);
1201

                
1202
    memset(size_remaining, 0, sizeof(size_remaining));
1203
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1204

                
1205
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1206

                
1207
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1208
                                                        FIELD_FILESIZE, total_file_size,
1209
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1210
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1211
                                                        FIELD_PROGRESS, progress, -1);
1212

                
1213
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1214
}
1215

                
1216
static void
1217
file_state_changed (NntpgrabGlue *obj, const char *collection_name, const char *subject, const char *real_filename, NGTaskState old_state, NGTaskState new_state)
1218
{
1219
    GtkWidget *treeDownloadQueue;
1220
    GtkTreeModel *store;
1221
    GtkTreeIter iter, parent;
1222
    int progress;
1223
    guint64 file_size;
1224
    guint64 file_size_remaining;
1225
    guint64 total_size;
1226
    guint64 total_size_remaining;
1227
    char size_remaining[64];
1228

                
1229
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1230
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1231

                
1232
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1233
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1234

                
1235
    switch(new_state) {
1236
        case TASK_STATE_WAITING_FOR_DOWNLOAD:       // The file was restarted
1237
            // Update the row itself
1238
            gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,    FIELD_FILESIZE, &file_size,
1239
                                                                FIELD_FILESIZE_REMAINING, &file_size_remaining, -1);
1240

                
1241
            memset(size_remaining, 0, sizeof(size_remaining));
1242
            nntpgrab_utils_calculate_file_size(file_size, size_remaining, sizeof(size_remaining) - 1);
1243

                
1244
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PARTS_DONE, (int) 0,
1245
                                                                FIELD_PARTS_FAILED, (int) 0,
1246
                                                                FIELD_PROGRESS, (int) 0,
1247
                                                                FIELD_STATE_STR, NULL,
1248
                                                                FIELD_FILESIZE_REMAINING, file_size,
1249
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1250

                
1251
            // Update the parent
1252
            gtk_tree_model_get(GTK_TREE_MODEL(store), &parent,  FIELD_FILESIZE, &total_size,
1253
                                                                FIELD_FILESIZE_REMAINING, &total_size_remaining, -1);
1254

                
1255
            total_size_remaining += file_size - file_size_remaining;
1256
            progress = calculate_progress(total_size, total_size_remaining);
1257
            memset(size_remaining, 0, sizeof(size_remaining));
1258
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1259

                
1260
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1261
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1262
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1263

                
1264
            break;
1265

                
1266
        case TASK_STATE_DOWNLOADING:
1267
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 0,
1268
                                                                FIELD_STATE_STR, NULL, -1);     // The state message should be NULL. In that case the value of the progress bar will be shown
1269
            break;
1270

                
1271
        case TASK_STATE_WAITING_FOR_DECODE:
1272
            memset(size_remaining, 0, sizeof(size_remaining));
1273
            nntpgrab_utils_calculate_file_size((guint64) 0, size_remaining, sizeof(size_remaining) - 1);
1274
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1275
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining,
1276
                                                                FIELD_STATE_STR, _("Waiting"), -1);
1277

                
1278
            break;
1279

                
1280
        case TASK_STATE_DECODING:
1281
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1282
                                                                FIELD_STATE_STR, _("Decoding"), -1);
1283
            break;
1284

                
1285
        case TASK_STATE_FINISHED_COMPLETE:
1286
            // Do we need to update the parent
1287
            g_return_if_fail(real_filename != NULL);
1288
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Done"),
1289
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1290
            break;
1291

                
1292
        case TASK_STATE_FINISHED_INCOMPLETE:
1293
            g_return_if_fail(real_filename != NULL);
1294
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Incomplete"),
1295
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1296
            break;
1297

                
1298
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1299
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Not available"), -1);
1300
            break;
1301

                
1302
        case TASK_STATE_SKIPPED:
1303
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1304
                                                                FIELD_FILESIZE_REMAINING, (guint64) 0,
1305
                                                                FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1306
            break;
1307
    };
1308
}
1309

                
1310
static void
1311
foreach_collection_func (const char *collection_name, const char *poster, nguint64 total_size, nguint64 total_size_remaining, int position, gpointer data)
1312
{
1313
    GtkTreeStore *store = GTK_TREE_STORE(data);
1314
    GtkTreeIter iter;
1315
    char size[64];
1316
    char size_remaining[64];
1317
    int progress;
1318

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

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

                
1325
    progress = calculate_progress(total_size, total_size_remaining);
1326

                
1327
    gtk_tree_store_append(store, &iter, NULL);
1328
    gtk_tree_store_set(store, &iter,    FIELD_COLLECTION_NAME, collection_name,
1329
                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1330
                                        FIELD_SUBJECT, collection_name,
1331
                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1332
                                        FIELD_POSTER, poster,
1333
                                        FIELD_FILESIZE_STR, size,
1334
                                        FIELD_FILESIZE, total_size,
1335
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1336
                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1337
                                        FIELD_STATE_STR, NULL,
1338
                                        FIELD_PROGRESS, progress,
1339
                                        FIELD_POSITION, position, -1);
1340
}
1341

                
1342
static gboolean
1343
find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter)
1344
{
1345
    unsigned int hash1, hash2;
1346

                
1347
    hash1 = g_str_hash(collection_name);
1348

                
1349
    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), iter)) {
1350
        do {
1351
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME_HASH, &hash2, -1);
1352

                
1353
            if (hash1 == hash2) {
1354
                char *name = NULL;
1355

                
1356
                gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME, &name, -1);
1357

                
1358
                if (!strcmp(collection_name, name)) {
1359
                    g_free(name);
1360
                    return TRUE;
1361
                }
1362

                
1363
                g_free(name);
1364
            }
1365
        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1366
    }
1367

                
1368
    return FALSE;
1369
}
1370

                
1371
static gboolean
1372
find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter)
1373
{
1374
    unsigned int hash1, hash2;
1375

                
1376
    if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), iter, &parent)) {
1377
        // Node contains no children. Shouldn't happen
1378
        return FALSE;
1379
    }
1380

                
1381
    hash1 = g_str_hash(subject);
1382

                
1383
    do {
1384
        gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT_HASH, &hash2, -1);
1385

                
1386
        if (hash1 == hash2) {
1387
            char *name = NULL;
1388

                
1389
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT, &name, -1);
1390

                
1391
            if (!strcmp(subject, name)) {
1392
                g_free(name);
1393
                return TRUE;
1394
            }
1395

                
1396
            g_free(name);
1397
        }
1398
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1399

                
1400
    return FALSE;
1401
}
1402

                
1403
static void
1404
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)
1405
{
1406
    GtkTreeStore *store = GTK_TREE_STORE(data);
1407
    GtkTreeIter iter, parent;
1408
    char size_str[64];
1409
    char size_remaining_str[64];
1410
    char stamp_str[64];
1411
    int progress;
1412
    char *state_str;
1413

                
1414
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1415
    g_return_if_fail(file_size_remaining >= 0);
1416

                
1417
    memset(&size_str, 0, sizeof(size_str));
1418
    nntpgrab_utils_calculate_file_size(file_size, size_str, sizeof(size_str) - 1);
1419

                
1420
    memset(&size_remaining_str, 0, sizeof(size_remaining_str));
1421
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining_str, sizeof(size_remaining_str) - 1);
1422

                
1423
    memset(&stamp_str, 0, sizeof(stamp_str));
1424
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1425

                
1426
    if (status == TASK_STATE_SKIPPED) {
1427
        progress = 0;
1428
    } else {
1429
        progress = calculate_progress(file_size, file_size_remaining);
1430
    }
1431

                
1432
    switch(status) {
1433
        case TASK_STATE_WAITING_FOR_DOWNLOAD:
1434
        case TASK_STATE_DOWNLOADING:
1435
            state_str = NULL;
1436
            break;
1437

                
1438
        case TASK_STATE_WAITING_FOR_DECODE:
1439
            state_str = _("Waiting");
1440
            break;
1441

                
1442
        case TASK_STATE_DECODING:
1443
            state_str = _("Decoding");
1444
            break;
1445

                
1446
        case TASK_STATE_FINISHED_COMPLETE:
1447
            state_str = _("Done");
1448
            break;
1449

                
1450
        case TASK_STATE_FINISHED_INCOMPLETE:
1451
            state_str = _("Incomplete");
1452
            break;
1453

                
1454
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1455
            state_str = _("Not available");
1456
            break;
1457

                
1458
        case TASK_STATE_SKIPPED:
1459
            state_str = _("Skipped");
1460
            break;
1461

                
1462
        default:
1463
            state_str = NULL;
1464
            break;
1465
    };
1466

                
1467
    gtk_tree_store_append(store, &iter, &parent);
1468
    gtk_tree_store_set(store, &iter,    FIELD_SUBJECT, subject,
1469
                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1470
                                        FIELD_POSTER, poster,
1471
                                        FIELD_STAMP_STR, stamp_str,
1472
                                        FIELD_FILESIZE, file_size,
1473
                                        FIELD_FILESIZE_STR, size_str,
1474
                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1475
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining_str,
1476
                                        FIELD_NUM_PARTS, num_parts,
1477
                                        FIELD_PARTS_DONE, num_parts_downloaded,
1478
                                        FIELD_PARTS_FAILED, num_parts_failed,
1479
                                        FIELD_PROGRESS, progress,
1480
                                        FIELD_STATE_STR, state_str,
1481
                                        FIELD_POSITION, position,
1482
                                        FIELD_REAL_FILENAME, filename, -1);
1483
}
1484

                
1485
static void
1486
foreach_group_func (const char *collection_name, const char *subject, const char *group, gpointer data)
1487
{
1488
    GtkTreeStore *store = GTK_TREE_STORE(data);
1489
    GtkTreeIter iter, parent;
1490
    char *old_groups;
1491
    char *new_groups;
1492

                
1493
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1494
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1495

                
1496
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_GROUPS, &old_groups, -1);
1497
    if (old_groups && strlen(old_groups) > 0) {
1498
        new_groups = g_strdup_printf("%s, %s", old_groups, group);
1499
    } else {
1500
        new_groups = g_strdup(group);
1501
    }
1502

                
1503
    gtk_tree_store_set(store, &iter, FIELD_GROUPS, new_groups, -1);
1504

                
1505
    g_free(new_groups);
1506
    g_free(old_groups);
1507
}
1508

                
1509
static void
1510
on_tree_view_select (GtkTreeSelection *selection, gpointer user_data)
1511
{
1512
    GtkWidget *btnRemoveItemFromQueue = nntpgrab_gui_base_get_widget("btnRemoveItemFromQueue");
1513
    GtkWidget *btnQueueMoveToTop = nntpgrab_gui_base_get_widget("btnQueueMoveToTop");
1514
    GtkWidget *btnQueueMoveUp = nntpgrab_gui_base_get_widget("btnQueueMoveUp");
1515
    GtkWidget *btnQueueMoveDown = nntpgrab_gui_base_get_widget("btnQueueMoveDown");
1516
    GtkWidget *btnQueueMoveToBottom = nntpgrab_gui_base_get_widget("btnQueueMoveToBottom");
1517

                
1518
    if (gtk_tree_selection_count_selected_rows(selection) == 0) {
1519
        // No items selected, disable the remove button and the move buttons
1520
        gtk_widget_set_sensitive(btnRemoveItemFromQueue, FALSE);
1521
        gtk_widget_set_sensitive(btnQueueMoveToTop, FALSE);
1522
        gtk_widget_set_sensitive(btnQueueMoveUp, FALSE);
1523
        gtk_widget_set_sensitive(btnQueueMoveDown, FALSE);
1524
        gtk_widget_set_sensitive(btnQueueMoveToBottom, FALSE);
1525
        return;
1526
    }
1527

                
1528
    // Enable the remove button and the move buttons
1529
    gtk_widget_set_sensitive(btnRemoveItemFromQueue, TRUE);
1530
    gtk_widget_set_sensitive(btnQueueMoveToTop, TRUE);
1531
    gtk_widget_set_sensitive(btnQueueMoveUp, TRUE);
1532
    gtk_widget_set_sensitive(btnQueueMoveDown, TRUE);
1533
    gtk_widget_set_sensitive(btnQueueMoveToBottom, TRUE);
1534
}
1535

                
1536
G_MODULE_EXPORT void
1537
on_btnPauseQueue_clicked(GtkWidget *caller, gpointer data)
1538
{
1539
    NGSchedularState active_state = nntpgrab_glue_schedular_get_state(glue);
1540

                
1541
    if (status_flag == SCHEDULAR_STATE_STOPPED && active_state == SCHEDULAR_STATE_STOPPING) {
1542
        GtkWidget *windowMain;
1543

                
1544
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
1545

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

                
1548
        g_signal_handlers_block_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1549

                
1550
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(caller), TRUE);
1551

                
1552
        g_signal_handlers_unblock_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1553

                
1554
        return;
1555
    }
1556

                
1557
    if (status_flag == SCHEDULAR_STATE_RUNNING) {
1558
        status_flag = SCHEDULAR_STATE_STOPPED;
1559
        nntpgrab_glue_schedular_stop(glue, FALSE);
1560
    } else {
1561
        status_flag = SCHEDULAR_STATE_RUNNING;
1562
        nntpgrab_glue_schedular_start(glue);
1563
    }
1564
}
1565

                
1566
static gboolean
1567
on_key_press_event(GtkWidget *widget, GdkEventKey *key, gpointer data)
1568
{
1569
    if (key->keyval == GDK_Delete) {
1570
        GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1571
        if (gtk_tree_selection_count_selected_rows(selection) > 0) {
1572
            on_btnRemoveItemFromQueue_clicked(widget, data);
1573
            return TRUE;
1574
        }
1575
    }
1576

                
1577
    return FALSE;
1578
}
1579

                
1580
static void
1581
schedular_state_changed_cb(NntpgrabGlue *obj, NGSchedularState state, const char *reason)
1582
{
1583
    GtkWidget *btnPauseQueue;
1584

                
1585
    if (state == SCHEDULAR_STATE_STOPPING) {
1586
        status_flag = SCHEDULAR_STATE_STOPPED;
1587
    } else {
1588
        status_flag = state;
1589
    }
1590

                
1591
    if (state == SCHEDULAR_STATE_STOPPING && reason != NULL) {
1592
        char *msg;
1593
        GtkWidget *windowMain;
1594
#ifdef HAVE_LIBNOTIFY
1595
        NotifyNotification *notification;
1596
#endif
1597

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

                
1601
#ifdef HAVE_LIBNOTIFY
1602
#ifdef HAVE_LIBNOTIFY_0_7
1603
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL);
1604
#else
1605
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL, NULL);
1606
#endif
1607
        if (nntpgrab_gui_base_tray_icon_get_is_shown()) {
1608
#if GTK_CHECK_VERSION(2,10,0)
1609
#ifndef HAVE_LIBNOTIFY_0_7
1610
            notify_notification_attach_to_status_icon(notification, nntpgrab_gui_base_tray_get_status_icon());
1611
#endif
1612
#endif
1613
        }
1614
        notify_notification_show(notification, NULL);
1615
#endif
1616

                
1617
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK);
1618
        g_free(msg);
1619
    }
1620

                
1621
    btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1622
    g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1623

                
1624
    if (state == SCHEDULAR_STATE_RUNNING) {
1625
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), FALSE);
1626
    } else {
1627
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1628
    }
1629

                
1630
    g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1631

                
1632
}
1633

                
1634
static void
1635
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)
1636
{
1637
    static int times_been_here = 10;
1638
    gboolean no_data_received = FALSE;
1639
    GtkTreeModel *model;
1640
    GtkWidget *treeDownloadQueue;
1641
    GtkTreeIter iter, parent;
1642
    guint64 total_estimated_time_remaining;
1643

                
1644
    if (bytes_received1 == 0 && bytes_received2 == 0 && bytes_received3 == 0 && bytes_received4 == 0 && bytes_received5 == 0 &&
1645
        bytes_received6 == 0 && bytes_received7 == 0 && bytes_received8 == 0 && bytes_received9 == 0 && bytes_received10 == 0) {
1646
            no_data_received = TRUE;
1647
    }
1648

                
1649
    // Only update on startup, on every 3 seconds, and when there has no data been received in the last 10 seconds
1650
    if (times_been_here < 3 && !no_data_received) {
1651
        times_been_here += 1;
1652
        return;
1653
    }
1654

                
1655
    if (no_data_received) {
1656
        times_been_here = 3;    // update the time remaining the next time we get here
1657
    } else {
1658
        times_been_here = 0;    // perform the next update in 10 seconds
1659
    }
1660

                
1661
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1662
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1663

                
1664
    if (!gtk_tree_model_get_iter_first(model, &parent)) {
1665
        // Nothing in the download queue
1666
        return;
1667
    }
1668

                
1669
    total_estimated_time_remaining = 0;
1670

                
1671
    do {
1672
        guint64 filesize;
1673
        int estimated_time_remaining;
1674
        char str_estimated_time_remaining[64];
1675
        char str_estimated_time_to_finish[64];
1676

                
1677
        gtk_tree_model_get(model, &parent, FIELD_FILESIZE_REMAINING, &filesize, -1);
1678

                
1679
        estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1680
                                                                                        bytes_received3, bytes_received4,
1681
                                                                                        bytes_received5, bytes_received6,
1682
                                                                                        bytes_received7, bytes_received8,
1683
                                                                                        bytes_received9, bytes_received10,
1684
                                                                                        filesize);
1685

                
1686
        if (estimated_time_remaining == -1) {
1687
            // Not enough data received to make an estimation
1688
            return;
1689
        }
1690

                
1691
        if (estimated_time_remaining == 0) {
1692
            // File is already downloaded
1693
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, "",
1694
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1695
        } else {
1696
            memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1697
            nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining + estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1698

                
1699
            memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1700
            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);
1701

                
1702
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1703
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1704
        }
1705

                
1706
        // Walk through all the children
1707
        if (gtk_tree_model_iter_children(model, &iter, &parent)) {
1708
            do {
1709
                gtk_tree_model_get(model, &iter, FIELD_FILESIZE_REMAINING, &filesize, -1);
1710

                
1711
                if (filesize > 0) {
1712
                    estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1713
                                                                                                    bytes_received3, bytes_received4,
1714
                                                                                                    bytes_received5, bytes_received6,
1715
                                                                                                    bytes_received7, bytes_received8,
1716
                                                                                                    bytes_received9, bytes_received10,
1717
                                                                                                    filesize);
1718

                
1719
                    total_estimated_time_remaining += estimated_time_remaining;
1720

                
1721
                    memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1722
                    nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1723

                
1724
                    memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1725
                    nntpgrab_utils_get_readable_finished_time(total_estimated_time_remaining, str_estimated_time_to_finish, sizeof(str_estimated_time_to_finish));
1726

                
1727
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1728
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1729
                } else {
1730
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, "",
1731
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1732
                }
1733
            } while (gtk_tree_model_iter_next(model, &iter));
1734
        }
1735
    } while (gtk_tree_model_iter_next(model, &parent));
1736
}
1737

                
1738
static void
1739
config_changed(ConfigGUI *config, gpointer data)
1740
{
1741
    ConfigGUIOpts opts = config_gui_get_opts();
1742

                
1743
    gtk_tree_view_column_set_visible(columns[FIELD_PROGRESS], opts.show_column_progress);
1744
    gtk_tree_view_column_set_visible(columns[FIELD_SUBJECT], opts.show_column_subject);
1745
    gtk_tree_view_column_set_visible(columns[FIELD_POSTER], opts.show_column_poster);
1746
    gtk_tree_view_column_set_visible(columns[FIELD_GROUPS], opts.show_column_newsgroups);
1747
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_STR], opts.show_column_filesize);
1748
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_REMAINING_STR], opts.show_column_filesize_remaining);
1749
    gtk_tree_view_column_set_visible(columns[FIELD_STAMP_STR], opts.show_column_stamp);
1750
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_REMAINING], opts.show_column_time_remaining);
1751
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_TO_FINISH], opts.show_column_time_to_finish);
1752
}
1753

                
1754
void
1755
queue_initialize()
1756
{
1757
    GtkWidget *treeDownloadQueue;
1758
    GtkTreeStore *store;
1759
    GtkCellRenderer *cellRenderer;
1760
    GtkTreeSelection *selection;
1761

                
1762
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1763

                
1764
    cellRenderer = gtk_cell_renderer_progress_new();
1765
    columns[FIELD_PROGRESS] = gtk_tree_view_column_new_with_attributes(_("Progress"), cellRenderer, "value", FIELD_PROGRESS, "text", FIELD_STATE_STR, NULL);
1766
    gtk_tree_view_column_set_resizable(columns[FIELD_PROGRESS], TRUE);
1767
    gtk_tree_view_column_set_reorderable(columns[FIELD_PROGRESS], TRUE);
1768
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_PROGRESS], -1);
1769

                
1770
    cellRenderer = gtk_cell_renderer_text_new();
1771
    columns[FIELD_SUBJECT] = gtk_tree_view_column_new_with_attributes(_("Subject"), cellRenderer, "text", FIELD_SUBJECT, NULL);
1772
    gtk_tree_view_column_set_resizable(columns[FIELD_SUBJECT], TRUE);
1773
    gtk_tree_view_column_set_reorderable(columns[FIELD_SUBJECT], TRUE);
1774
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_SUBJECT], -1);
1775

                
1776
    cellRenderer = gtk_cell_renderer_text_new();
1777
    columns[FIELD_POSTER] = gtk_tree_view_column_new_with_attributes(_("Poster"), cellRenderer, "text", FIELD_POSTER, NULL);
1778
    gtk_tree_view_column_set_resizable(columns[FIELD_POSTER], TRUE);
1779
    gtk_tree_view_column_set_reorderable(columns[FIELD_POSTER], TRUE);
1780
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_POSTER], -1);
1781

                
1782
    cellRenderer = gtk_cell_renderer_text_new();
1783
    columns[FIELD_GROUPS] = gtk_tree_view_column_new_with_attributes(_("Group"), cellRenderer, "text", FIELD_GROUPS, NULL);
1784
    gtk_tree_view_column_set_resizable(columns[FIELD_GROUPS], TRUE);
1785
    gtk_tree_view_column_set_reorderable(columns[FIELD_GROUPS], TRUE);
1786
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_GROUPS], -1);
1787

                
1788
    cellRenderer = gtk_cell_renderer_text_new();
1789
    columns[FIELD_FILESIZE_STR] = gtk_tree_view_column_new_with_attributes(_("File size"), cellRenderer, "text", FIELD_FILESIZE_STR, NULL);
1790
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_STR], TRUE);
1791
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_STR], TRUE);
1792
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_STR], -1);
1793

                
1794
    cellRenderer = gtk_cell_renderer_text_new();
1795
    columns[FIELD_FILESIZE_REMAINING_STR] = gtk_tree_view_column_new_with_attributes(_("File size remaining"), cellRenderer, "text", FIELD_FILESIZE_REMAINING_STR, NULL);
1796
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1797
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1798
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_REMAINING_STR], -1);
1799

                
1800
    cellRenderer = gtk_cell_renderer_text_new();
1801
    columns[FIELD_STAMP_STR] = gtk_tree_view_column_new_with_attributes( _("Stamp"), cellRenderer, "text", FIELD_STAMP_STR, NULL);
1802
    gtk_tree_view_column_set_resizable(columns[FIELD_STAMP_STR], TRUE);
1803
    gtk_tree_view_column_set_reorderable(columns[FIELD_STAMP_STR], TRUE);
1804
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_STAMP_STR], -1);
1805

                
1806
    cellRenderer = gtk_cell_renderer_text_new();
1807
    columns[FIELD_ESTIMATED_TIME_REMAINING] = gtk_tree_view_column_new_with_attributes( _("Time remaining"), cellRenderer, "text", FIELD_ESTIMATED_TIME_REMAINING, NULL);
1808
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1809
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1810
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_REMAINING], -1);
1811

                
1812
    cellRenderer = gtk_cell_renderer_text_new();
1813
    columns[FIELD_ESTIMATED_TIME_TO_FINISH] = gtk_tree_view_column_new_with_attributes( _("Time to finish"), cellRenderer, "text", FIELD_ESTIMATED_TIME_TO_FINISH, NULL);
1814
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1815
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1816
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_TO_FINISH], -1);
1817

                
1818
    store = gtk_tree_store_new( LAST_FIELD,
1819
                                G_TYPE_STRING /* FIELD_COLLECTION_NAME */,
1820
                                G_TYPE_UINT   /* FIELD_COLLECTION_NAME_HASH */,
1821
                                G_TYPE_STRING /* FIELD_SUBJECT */,
1822
                                G_TYPE_UINT   /* FIELD_SUBJECT_HASH */,
1823
                                G_TYPE_STRING /* FIELD_POSTER */,
1824
                                G_TYPE_STRING /* FIELD_GROUPS */,
1825
                                G_TYPE_STRING /* FIELD_STAMP_STR */,
1826
                                G_TYPE_INT /* FIELD_PROGRESS */,
1827
                                G_TYPE_UINT64 /* FIELD_FILESIZE */,
1828
                                G_TYPE_STRING /* FIELD_FILESIZE_STR */,
1829
                                G_TYPE_UINT64 /* FIELD_FILESIZE_REMAINING */,
1830
                                G_TYPE_STRING /* FIELD_FILESIZE_REMAINING_STR */,
1831
                                G_TYPE_INT /* FIELD_NUM_PARTS */,
1832
                                G_TYPE_INT /* FIELD_PARTS_DONE */,
1833
                                G_TYPE_INT /* FIELD_PARTS_FAILED */,
1834
                                G_TYPE_STRING /* FIELD_STATE_STR */,
1835
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_REMAINING */,
1836
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_TO_FINISH */,
1837
                                G_TYPE_INT /* FIELD_POSITION */,
1838
                                G_TYPE_STRING /* FIELD_REAL_FILENAME */);
1839

                
1840
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeDownloadQueue), GTK_TREE_MODEL(store));
1841

                
1842
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
1843
    gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1844

                
1845
    g_signal_connect(selection, "changed", G_CALLBACK(on_tree_view_select), NULL);
1846
    on_tree_view_select(selection, NULL);
1847

                
1848
    nntpgrab_glue_schedular_foreach_task(glue, foreach_collection_func, foreach_file_func, foreach_group_func, store);
1849

                
1850
    nntpgrab_glue_signal_connect(glue, "collection_added", NG_CALLBACK(collection_added), NULL);
1851
    nntpgrab_glue_signal_connect(glue, "collection_removed", NG_CALLBACK(collection_removed), NULL);
1852
    nntpgrab_glue_signal_connect(glue, "collection_modified", NG_CALLBACK(collection_modified), NULL);
1853
    nntpgrab_glue_signal_connect(glue, "file_added", NG_CALLBACK(file_added), NULL);
1854
    nntpgrab_glue_signal_connect(glue, "file_removed", NG_CALLBACK(file_removed), NULL);
1855
    nntpgrab_glue_signal_connect(glue, "file_state_changed", NG_CALLBACK(file_state_changed), NULL);
1856
    nntpgrab_glue_signal_connect(glue, "file_download_state_update", NG_CALLBACK(file_download_state_update), NULL);
1857
    nntpgrab_glue_signal_connect(glue, "schedular_state_changed", NG_CALLBACK(schedular_state_changed_cb), NULL);
1858
    nntpgrab_glue_signal_connect(glue, "traffic_monitor_update", NG_CALLBACK(traffic_monitor_update_cb), NULL);
1859
    nntpgrab_glue_signal_connect(glue, "task_moved", NG_CALLBACK(task_moved_cb), NULL);
1860
    nntpgrab_glue_signal_connect(glue, "collection_moved", NG_CALLBACK(collection_moved_cb), NULL);
1861

                
1862
    g_signal_connect(config_gui_get_object(), "config_changed", G_CALLBACK(config_changed), NULL);
1863
    config_changed(NULL, NULL);
1864

                
1865
    g_signal_connect(treeDownloadQueue, "key-press-event", G_CALLBACK(on_key_press_event), NULL);
1866

                
1867
    if (nntpgrab_glue_get_is_standalone(glue)) {
1868
        nntpgrab_glue_schedular_start(glue);
1869
    }
1870

                
1871
    status_flag = nntpgrab_glue_schedular_get_state(glue);
1872
    if (status_flag == SCHEDULAR_STATE_STOPPING || status_flag == SCHEDULAR_STATE_STOPPED) {
1873
        GtkWidget *btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1874

                
1875
        g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1876
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1877
        g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1878
    }
1879
}