Statistics
| Revision:

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

History | View | Annotate | Download (74.7 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
#ifdef DARWIN
112
    cmd = g_strdup_printf("open %s", folder);
113
    ret = g_spawn_command_line_async(cmd, &err);
114
#else
115
    char *uri = g_strdup_printf("file://%s", folder_unquoted);
116
    ret = gtk_show_uri(NULL, uri, GDK_CURRENT_TIME, &err);
117
    g_free(uri);
118
#endif
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
    GObject *menuitemExecuteItem;
147
    GObject *menuitemOpenDownloadFolder;
148
    GObject *menuitemMarkOptional;
149
    const char *str_optional;
150
    gboolean show_optional_button;
151
    char *progress_str = NULL;
152
    GtkTreeIter iter;
153
    GtkTreeIter parent;
154
    GList *list;
155

                
156
#ifdef DARWIN
157
    // Catch both the right mouse button and CTRL + left mouse button
158
    if (!(event->button == 3 || (event->button == 1 && event->state == GDK_CONTROL_MASK))) {
159
        return FALSE;
160
    }
161
#else
162
    // We only want to catch the right mouse button
163
    if (event->button != 3) {
164
        return FALSE;
165
    }
166
#endif
167

                
168
    // Is at least one item selected?
169
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(caller));
170
    if (gtk_tree_selection_count_selected_rows(selection) == 0)
171
    {
172
        GtkTreePath *path;
173

                
174
        /* Get tree path for row that was clicked */
175
        if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(caller),
176
                                            (gint) event->x,
177
                                            (gint) event->y,
178
                                            &path, NULL, NULL, NULL))
179
        {
180
            // No item(s) selected and no item under the cursor. Ignore
181
            return FALSE;
182
        }
183

                
184
        gtk_tree_selection_unselect_all(selection);
185
        gtk_tree_selection_select_path(selection, path);
186
        gtk_tree_path_free(path);
187
    }
188

                
189
    // Do we have exactly one selected item and is this item already downloaded?
190
    menuitemExecuteItem = nntpgrab_gui_base_get_object("menuitemExecuteItem");
191
    memset(&val, 0, sizeof(val));
192
    g_value_init(&val, G_TYPE_BOOLEAN);
193
    g_value_set_boolean (&val, FALSE);
194
    g_object_set_property(G_OBJECT(menuitemExecuteItem), "sensitive", &val);
195

                
196
    if (!nntpgrab_glue_get_is_standalone(glue)) {
197
        menuitemOpenDownloadFolder = nntpgrab_gui_base_get_object("menuitemOpenDownloadFolder");
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(menuitemOpenDownloadFolder), "sensitive", &val);
202
    }
203

                
204
    list = gtk_tree_selection_get_selected_rows(selection, &model);
205

                
206
    gtk_tree_model_get_iter(model, &iter, (GtkTreePath *) list->data);
207
    gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &progress_str, -1);
208

                
209
    if (progress_str != NULL) {
210
        if (gtk_tree_selection_count_selected_rows(selection) == 1) {
211
            if (!strcmp(progress_str, _("Done")) ||
212
                !strcmp(progress_str, _("Incomplete"))) {
213

                
214
                if (nntpgrab_glue_get_is_standalone(glue)) {
215
                    g_value_set_boolean (&val, TRUE);
216
                    g_object_set_property(G_OBJECT(menuitemExecuteItem), "sensitive", &val);
217
                }
218
            }
219
        }
220
    }
221

                
222
    if (progress_str && !strcmp(progress_str, _("Skipped"))) {
223
        str_optional = _("Forcefully download file(s)");
224
    } else {
225
        str_optional = _("Don't download file(s)");
226
    }
227

                
228
    menuitemMarkOptional = nntpgrab_gui_base_get_object("menuitemMarkOptional");
229
    g_value_unset(&val);
230
    g_value_init(&val, G_TYPE_STRING);
231
    g_value_set_string(&val, str_optional);
232
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "label", &val);
233

                
234
    if (gtk_tree_model_iter_parent(model, &parent, &iter) &&
235
        (!progress_str || strcmp(progress_str, _("Done"))) &&
236
        (!progress_str || strcmp(progress_str, _("Incomplete"))) &&
237
        (!progress_str || strcmp(progress_str, _("Not available")))) {
238

                
239
        show_optional_button = TRUE;
240
    } else {
241
        show_optional_button = FALSE;
242
    }
243
    g_value_unset(&val);
244
    g_value_init(&val, G_TYPE_BOOLEAN);
245
    g_value_set_boolean (&val, show_optional_button);
246
    g_object_set_property(G_OBJECT(menuitemMarkOptional), "sensitive", &val);
247

                
248
    g_free(progress_str);
249
    g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
250
    g_list_free (list);
251

                
252
    // Do we have multiple items selected and do these all belong to the same collection ?
253
    // TODO
254

                
255
    menuDownloadQueue = nntpgrab_gui_base_get_widget("menuDownloadQueue");
256
    gtk_menu_popup(GTK_MENU(menuDownloadQueue), NULL, NULL, NULL, NULL, event->button, event->time);
257

                
258
    return TRUE;
259
}
260

                
261
G_MODULE_EXPORT void
262
on_menuitemRestartTask_activate(GtkWidget *caller, gpointer data)
263
{
264
    GtkWidget *treeDownloadQueue;
265
    GtkTreeSelection *selection;
266
    GList *list, *selectedRows;
267
    GtkTreeModel *model;
268
    GtkTreeIter iter, parent;
269
    char *collection_name = NULL;
270
    char *subject = NULL;
271
    char *errmsg = NULL;
272

                
273
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
274
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
275

                
276
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
277

                
278
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
279

                
280
    list = selectedRows;
281
    while (list) {
282
        GtkTreePath *path = list->data;
283

                
284
        gtk_tree_model_get_iter(model, &iter, path);
285
        if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
286
            // Item contains no parent, restart the whole collection
287
            gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, -1);
288
            if (!nntpgrab_glue_schedular_restart_task(glue, collection_name, NULL, &errmsg)) {
289
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
290
                char msg[1024];
291

                
292
                memset(&msg, 0, sizeof(msg));
293
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart collection due to an error:\n%s"), errmsg);
294
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
295
                g_free(errmsg);
296
            }
297

                
298
            g_free(collection_name);
299
        } else {
300
            // Restart one item from the collection
301
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
302
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
303

                
304
            if (!nntpgrab_glue_schedular_restart_task(glue, collection_name, subject, &errmsg)) {
305
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
306
                char msg[1024];
307

                
308
                memset(&msg, 0, sizeof(msg));
309
                snprintf(msg, sizeof(msg) - 1, _("Unable to restart task due to an error:\n%s"), errmsg);
310
                nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
311
                g_free(errmsg);
312
            }
313

                
314
            g_free(collection_name);
315
            g_free(subject);
316
        }
317

                
318
        list = g_list_next(list);
319
    }
320

                
321
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
322
    g_list_free (selectedRows);
323

                
324
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
325
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
326
        char msg[1024];
327

                
328
        memset(&msg, 0, sizeof(msg));
329
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
330
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
331
        g_free(errmsg);
332
    }
333
}
334

                
335
G_MODULE_EXPORT void
336
on_menuitemMarkOptional_activate(GtkWidget *caller, gpointer data)
337
{
338
    GtkWidget *treeDownloadQueue;
339
    GtkTreeSelection *selection;
340
    GList *list, *selectedRows;
341
    GtkTreeModel *model;
342
    GtkTreeIter iter, parent;
343
    char *collection_name = NULL;
344
    char *subject = NULL;
345
    gboolean is_optional = FALSE;
346
    gboolean flag_set = FALSE;
347

                
348
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
349
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
350

                
351
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
352

                
353
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
354

                
355
    list = selectedRows;
356
    while (list) {
357
        GtkTreePath *path = list->data;
358

                
359
        gtk_tree_model_get_iter(model, &iter, path);
360
        if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
361
            // Item contains no parent
362
        } else {
363
            // Restart one item from the collection
364
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
365
            gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
366

                
367
            if (!flag_set) {
368
                char *state = NULL;
369
                gtk_tree_model_get(model, &iter, FIELD_STATE_STR, &state, -1);
370
                if (state && !strcmp(state, _("Skipped"))) {
371
                    is_optional = FALSE;
372
                } else {
373
                    is_optional = TRUE;
374
                }
375
                flag_set = TRUE;
376
                g_free(state);
377
            }
378

                
379
            if (!nntpgrab_glue_schedular_mark_task_optional(glue, collection_name, subject, is_optional)) {
380
                GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
381
                nntpgrab_gui_base_dialog_show(windowMain, _("Unable to change the status of the selected file(s)"), GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
382
            }
383

                
384
            g_free(collection_name);
385
            g_free(subject);
386
        }
387

                
388
        list = g_list_next(list);
389
    }
390

                
391
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
392
    g_list_free (selectedRows);
393
}
394

                
395
G_MODULE_EXPORT void
396
on_menuitemOpenDownloadFolder_activate(GtkWidget *caller, gpointer data)
397
{
398
    // Is only one collection selected or more ?
399
    gboolean multiple_collections = FALSE;
400
    char *collection_name = NULL;
401
    GtkWidget *treeDownloadQueue;
402
    GtkTreeSelection *selection;
403
    GList *list, *selectedRows;
404
    GtkTreeModel *model;
405
    NGConfigOpts opts;
406

                
407
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
408
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
409

                
410
    g_assert(gtk_tree_selection_count_selected_rows(selection) > 0);
411

                
412
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
413

                
414
    list = selectedRows;
415
    while (list) {
416
        GtkTreePath *path = list->data;
417
        GtkTreeIter iter;
418
        char *name = NULL;
419

                
420
        gtk_tree_model_get_iter(model, &iter, path);
421
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &name, -1);
422

                
423
        if (name == NULL || strlen(name) == 0) {
424
            GtkTreeIter parent;
425

                
426
            if (name) {
427
                g_free(name);
428
            }
429

                
430
            // Look at the parent
431
            gtk_tree_model_iter_parent(model, &parent, &iter);
432
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &name, -1);
433
        }
434

                
435
        g_return_if_fail(name != NULL);
436

                
437
        if (!collection_name) {
438
            collection_name = g_strdup(name);
439
        } else {
440
            if (strcmp(name, collection_name)) {
441
                // A previous found collection name and this collection name differ!
442
                multiple_collections = TRUE;
443
            }
444
        }
445

                
446
        g_free(name);
447

                
448
        list = g_list_next(list);
449
    }
450

                
451
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
452
    g_list_free (selectedRows);
453

                
454
    opts = nntpgrab_glue_config_get_opts(glue);
455

                
456
    if (multiple_collections) {
457
        // Open the default download folder
458
        open_file(opts.download_directory, TRUE);
459
    } else {
460
        // Open the download folder of the active collection
461
        char *folder = g_strdup_printf("%s%s%s", opts.download_directory, G_DIR_SEPARATOR_S, collection_name);
462

                
463
        if (!g_file_test(folder, G_FILE_TEST_IS_DIR)) {
464
            // Folder doesn't exist yet, fall back to default download directory
465
            open_file(opts.download_directory, TRUE);
466
        } else {
467
            open_file(folder, TRUE);
468
        }
469

                
470
        g_free(folder);
471
    }
472

                
473
    if (collection_name) {
474
        g_free(collection_name);
475
    }
476
}
477

                
478
G_MODULE_EXPORT void
479
on_menuitemExecuteItem_activate(GtkWidget *caller, gpointer data)
480
{
481
    char *collection_name = NULL;
482
    char *real_filename = NULL;
483
    GtkWidget *treeDownloadQueue;
484
    GtkTreeSelection *selection;
485
    GList *selectedRows;
486
    GtkTreeModel *model;
487
    NGConfigOpts opts;
488
    GtkTreePath *path;
489
    GtkTreeIter iter;
490
    char *subject;
491
    char *full_filename;
492
    GtkTreeIter parent;
493

                
494
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
495
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
496

                
497
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) == 1);
498

                
499
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
500

                
501
    path = selectedRows->data;
502

                
503
    gtk_tree_model_get_iter(model, &iter, path);
504
    gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
505

                
506
    // Look at the parent
507
    gtk_tree_model_iter_parent(model, &parent, &iter);
508
    gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
509
    gtk_tree_model_get(model, &iter, FIELD_REAL_FILENAME, &real_filename, -1);
510

                
511
    g_return_if_fail(real_filename != NULL);
512

                
513
    opts = nntpgrab_glue_config_get_opts(glue);
514
    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);
515

                
516
    open_file(full_filename, FALSE);
517

                
518
    g_free(full_filename);
519
    g_free(subject);
520
    g_free(real_filename);
521
    g_free(collection_name);
522
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
523
    g_list_free (selectedRows);
524
}
525

                
526
typedef enum {
527
    MOVE_DIRECTION_TOP,
528
    MOVE_DIRECTION_UP,
529
    MOVE_DIRECTION_DOWN,
530
    MOVE_DIRECTION_BOTTOM
531
} move_direction;
532

                
533
static void
534
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)
535
{
536
    GtkWidget *treeDownloadQueue;
537
    GtkTreeModel *store;
538
    GtkTreeIter parent, orig_iter, new_iter;
539
    int i;
540
    int num_items;
541

                
542
#if 0 
543
    g_print("%s: orig_collection_name = %s\n", __PRETTY_FUNCTION__, orig_collection_name);
544
    g_print("%s: subject = %s\n", __PRETTY_FUNCTION__, subject);
545
    g_print("%s: new_collection_name = %s\n", __PRETTY_FUNCTION__, new_collection_name);
546
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
547
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
548
#endif
549

                
550
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
551
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
552

                
553
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), new_collection_name, &parent));
554
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &orig_iter));
555

                
556
    num_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &parent);
557
    if (num_items == 1) {
558
        /* List only contains one element so we don't need to move anything */
559
        return;
560
    }
561

                
562
    if (new_position == -1) {
563
        i = 1;
564
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
565
        while (i != num_items) {
566
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
567
            i++;
568
        }
569

                
570
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
571
    } else {
572
        i = 0;
573
        gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &new_iter, &parent);
574
        while (i != new_position) {
575
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
576
            i++;
577
        }
578

                
579
        if (old_position < new_position) {
580
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
581
        } else {
582
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
583
        }
584
    }
585
}
586

                
587
static void
588
collection_moved_cb(NntpgrabGlue *obj, const char *collection_name, int old_position, int new_position)
589
{
590
    GtkWidget *treeDownloadQueue;
591
    GtkTreeModel *store;
592
    GtkTreeIter orig_iter, new_iter;
593
    int i;
594

                
595
#if 0 
596
    g_print("%s: collection_name = %s\n", __PRETTY_FUNCTION__, collection_name);
597
    g_print("%s: old_position = %i\n", __PRETTY_FUNCTION__, old_position);
598
    g_print("%s: new_position = %i\n", __PRETTY_FUNCTION__, new_position);
599
#endif
600

                
601
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
602
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
603

                
604
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &orig_iter));
605
    g_return_if_fail(new_position <= (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL)));
606

                
607
    if (new_position == -1) {
608
        new_position = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
609

                
610
        i = 1;
611
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
612
        while (i != new_position) {
613
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
614
            i++;
615
        }
616

                
617
        gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
618
    } else {
619
        i = 0;
620
        gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &new_iter);
621
        while (i != new_position) {
622
            gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &new_iter);
623
            i++;
624
        }
625

                
626
        if (old_position < new_position) {
627
            gtk_tree_store_move_after(GTK_TREE_STORE(store), &orig_iter, &new_iter);
628
        } else {
629
            gtk_tree_store_move_before(GTK_TREE_STORE(store), &orig_iter, &new_iter);
630
        }
631
    }
632
}
633

                
634
static void
635
move_item(move_direction direction)
636
{
637
    char *collection_name = NULL;
638
    char *subject = NULL;
639
    int position = -1;
640
    GtkWidget *treeDownloadQueue;
641
    GtkTreeSelection *selection;
642
    GList *list, *selectedRows;
643
    GtkTreeModel *model;
644
    GList *references = NULL;
645
    char *errmsg = NULL;
646

                
647
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
648
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
649

                
650
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
651

                
652
    // Duplicate the list with selected rows into GtkTreeRowReference's as the position of
653
    // selected rows can change during the loop later on
654

                
655
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
656

                
657
    list = selectedRows;
658
    while (list) {
659
        GtkTreePath *path = list->data;
660
        GtkTreeRowReference *ref;
661

                
662
        ref = gtk_tree_row_reference_new(model, path);
663
        references = g_list_append(references, ref);
664

                
665
        list = g_list_next(list);
666
    }
667

                
668
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
669
    g_list_free (selectedRows);
670

                
671
    if (direction == MOVE_DIRECTION_DOWN || direction == MOVE_DIRECTION_TOP) {
672
        references = g_list_reverse(references);
673
    }
674

                
675
    list = references;
676
    while (list) {
677
        GtkTreeRowReference *ref = list->data;
678
        GtkTreePath *path;
679
        GtkTreeIter iter;
680
        char *path_str;
681
        char **values;
682
        gboolean is_child = FALSE;
683
        int num_items;
684
        GtkTreeIter parent;
685

                
686
        if (collection_name) {
687
            g_free(collection_name);
688
            collection_name = NULL;
689
        }
690

                
691
        if (subject) {
692
            g_free(subject);
693
            subject = NULL;
694
        }
695

                
696
        path = gtk_tree_row_reference_get_path(ref);
697
        if (!path) {
698
            list = g_list_next(list);
699
            gtk_tree_row_reference_free(ref);
700
            continue;
701
        }
702

                
703
        gtk_tree_model_get_iter(model, &iter, path);
704
        gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, FIELD_SUBJECT, &subject, -1);
705

                
706
        path_str = gtk_tree_path_to_string(path);
707
        values = g_strsplit(path_str, ":", 0);
708
        g_return_if_fail(values != NULL);
709
        if (values[0]) {
710
            position = atoi(values[0]);
711
            if (values[1]) {
712
                position = atoi(values[1]);
713
            }
714
        }
715

                
716
        g_strfreev(values);
717
        g_free(path_str);
718

                
719
        if (gtk_tree_model_iter_parent(model, &parent, &iter)) {
720
            num_items = gtk_tree_model_iter_n_children(model, &parent);
721
        } else {
722
            num_items = gtk_tree_model_iter_n_children(model, NULL);
723
        }
724

                
725
        if (collection_name == NULL || strlen(collection_name) == 0) {
726
            // Look at the parent
727
            gtk_tree_model_iter_parent(model, &parent, &iter);
728
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
729

                
730
            is_child = TRUE;
731
        }
732

                
733
        g_assert(collection_name);
734

                
735
        switch (direction) {
736
            case MOVE_DIRECTION_TOP:
737
                if (is_child) {
738
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, 0));
739
                } else {
740
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, 0));
741
                }
742
                break;
743

                
744
            case MOVE_DIRECTION_UP:
745
                if (position == 0) {
746
                    break;
747
                }
748
                if (is_child) {
749
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position - 1));
750
                } else {
751
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position - 1));
752
                }
753
                break;
754

                
755
            case MOVE_DIRECTION_DOWN:
756
                if (position + 1 >= num_items) {
757
                    break;
758
                }
759
                if (is_child) {
760
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position + 1));
761
                } else {
762
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position + 1));
763
                }
764
                break;
765

                
766
            case MOVE_DIRECTION_BOTTOM:
767
                if (is_child) {
768
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, -1));
769
                } else {
770
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, -1));
771
                }
772
                break;
773

                
774
            default:
775
                break;
776
        }
777

                
778
        list = g_list_next(list);
779
        gtk_tree_path_free(path);
780
        gtk_tree_row_reference_free(ref);
781
    }
782

                
783
    g_list_free(references);
784

                
785
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
786
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
787
        char msg[1024];
788

                
789
        memset(&msg, 0, sizeof(msg));
790
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
791
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
792
        g_free(errmsg);
793
    }
794

                
795
    if (collection_name) {
796
        g_free(collection_name);
797
    }
798

                
799
    if (subject) {
800
        g_free(subject);
801
    }
802
}
803

                
804
G_MODULE_EXPORT void
805
on_menuitemMoveToTop_activate(GtkWidget *caller, gpointer data)
806
{
807
    move_item(MOVE_DIRECTION_TOP);
808
}
809

                
810
G_MODULE_EXPORT void
811
on_menuitemMoveUp_activate(GtkWidget *caller, gpointer data)
812
{
813
    move_item(MOVE_DIRECTION_UP);
814
}
815

                
816
G_MODULE_EXPORT void
817
on_menuitemMoveDown_activate(GtkWidget *caller, gpointer data)
818
{
819
    move_item(MOVE_DIRECTION_DOWN);
820
}
821

                
822
G_MODULE_EXPORT void
823
on_menuitemMoveToBottom_activate(GtkWidget *caller, gpointer data)
824
{
825
    move_item(MOVE_DIRECTION_BOTTOM);
826
}
827

                
828
G_MODULE_EXPORT void
829
on_btnRemoveItemFromQueue_clicked(GtkWidget *caller, gpointer data)
830
{
831
    GtkWidget *treeDownloadQueue;
832
    GtkTreeSelection *selection;
833
    GList *list, *selectedRows, *list_new;
834
    GtkTreeModel *model;
835
    char *errmsg = NULL;
836

                
837
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
838
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
839

                
840
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
841

                
842
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
843

                
844
    // Duplicate the list and transform the items to GtkTreeRowReferences
845
    // This is because we are going to delete items in the tree
846
    list = selectedRows;
847
    list_new = NULL;
848
    while (list) {
849
        GtkTreePath *path = list->data;
850

                
851
        list_new = g_list_append(list_new, gtk_tree_row_reference_new(model, path));
852

                
853
        list = g_list_next(list);
854
    }
855

                
856
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
857
    g_list_free (selectedRows);
858

                
859
    // Now loop trough all the items and remove the downloads tasks
860
    list = list_new;
861
    while (list) {
862
        GtkTreeRowReference *ref = list->data;
863

                
864
        if (gtk_tree_row_reference_valid(ref)) {
865
            GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
866
            GtkTreeIter iter, parent;
867
            GList *list_tmp;
868
            char *collection_name = NULL;
869
            char *subject = NULL;
870
            gboolean item_is_already_removed = FALSE;
871

                
872
            /* Did we already try to remove the parent of the active item? */
873
            list_tmp = g_list_previous(list);
874
            while (list_tmp) {
875
                GtkTreeRowReference *ref_tmp = list_tmp->data;
876
                GtkTreePath *path_tmp = gtk_tree_row_reference_get_path(ref_tmp);
877

                
878
                if (gtk_tree_path_is_ancestor(path_tmp, path)) {
879
                    /* Hey, the collection itself is already removed! */
880
                    item_is_already_removed = TRUE;
881
                }
882

                
883
                gtk_tree_path_free(path_tmp);
884

                
885
                list_tmp = g_list_previous(list_tmp);
886
            }
887

                
888
            gtk_tree_model_get_iter(model, &iter, path);
889
            gtk_tree_path_free(path);
890

                
891
            if (item_is_already_removed) {
892
                list = g_list_next(list);
893
                continue;
894
            }
895

                
896
            if (!gtk_tree_model_iter_parent(model, &parent, &iter)) {
897
                // Item contains no parent, delete the whole collection
898
                gtk_tree_model_get(model, &iter, FIELD_COLLECTION_NAME, &collection_name, -1);
899
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, NULL, &errmsg)) {
900
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
901
                    char msg[1024];
902

                
903
                    memset(&msg, 0, sizeof(msg));
904
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing collection(s):\n%s"), errmsg);
905
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
906
                    g_free(errmsg);
907
                }
908

                
909
                g_free(collection_name);
910
            } else {
911
                // Delete one item from the collection
912
                gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
913
                gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
914

                
915
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, subject, &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 file(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
                g_free(subject);
927
            }
928
        }
929

                
930
        list = g_list_next(list);
931
    }
932

                
933
    /* Traverse the list again to free all the row references */
934
    list = list_new;
935
    while (list) {
936
        gtk_tree_row_reference_free((GtkTreeRowReference*) list->data);
937
        list = g_list_next(list);
938
    }
939
    g_list_free(list_new);
940

                
941
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
942
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
943
        char msg[1024];
944

                
945
        memset(&msg, 0, sizeof(msg));
946
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
947
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
948
        g_free(errmsg);
949
    }
950
}
951

                
952
static void
953
generate_stamp(time_t stamp, char *buf, int buf_len)
954
{
955
    struct tm *now;
956

                
957
    if ((now = localtime(&stamp)) == NULL) {
958
        // Date could not be parted
959
        buf[0] = '\0';
960
    }
961

                
962
    strftime(buf, buf_len, "%c", now);
963
}
964

                
965
static int
966
calculate_progress(guint64 file_size, guint64 file_size_remaining)
967
{
968
    double progress;
969
    int progress_int;
970

                
971
    // Prevent a division by zero
972
    if (file_size_remaining == 0) {
973
        return 100;
974
    }
975

                
976
    progress = (double) file_size - file_size_remaining;
977
    progress /= file_size;
978
    progress *= 100;
979
    progress_int = (int) progress;
980

                
981
    if (progress_int > 100) {
982
        g_print(__FILE__ ":%i progress overflow!\n", __LINE__);
983
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
984
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
985
        progress_int = 100;
986
    }
987

                
988
    if (progress < 0) {
989
        g_print(__FILE__ ":%i progress underflow!\n", __LINE__);
990
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
991
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
992
        progress_int = 0;
993
    }
994

                
995
    return progress_int;
996
}
997

                
998
static void
999
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)
1000
{
1001
    GtkWidget *treeDownloadQueue;
1002
    GtkTreeModel *store;
1003
    GtkTreeIter iter, parent;
1004
    char *state;
1005
    int progress;
1006
    char size_remaining[64];
1007
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1008
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1009

                
1010
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1011
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1012

                
1013
    /* If the file has just been skipped, ignore this event */
1014
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_STATE_STR, &state, -1);
1015
    if (state && !strcmp(state, _("Skipped"))) {
1016
        g_free(state);
1017
        return;
1018
    }
1019
    g_free(state);
1020

                
1021
    // Update the parent
1022
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1023

                
1024
    memset(&size_remaining, 0, sizeof(size_remaining));
1025
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1026

                
1027
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1028
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1029
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1030

                
1031
    // Update the child
1032
    progress = calculate_progress(file_size, file_size_remaining);
1033
    memset(&size_remaining, 0, sizeof(size_remaining));
1034
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1035

                
1036
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, progress,
1037
                                                        FIELD_PARTS_DONE, num_parts_done,
1038
                                                        FIELD_PARTS_FAILED, num_parts_failed,
1039
                                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1040
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1041
}
1042

                
1043
static void
1044
collection_added (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1045
{
1046
    GtkWidget *treeDownloadQueue;
1047
    GtkTreeModel *store;
1048
    GtkTreeIter iter;
1049

                
1050
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1051
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1052

                
1053
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, NULL);
1054
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_COLLECTION_NAME, collection_name,
1055
                                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1056
                                                        FIELD_SUBJECT, collection_name,
1057
                                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1058
                                                        FIELD_POSTER, poster,
1059
                                                        FIELD_STATE_STR, NULL, -1);
1060
}
1061

                
1062
static void
1063
collection_removed (NntpgrabGlue *obj, const char *collection_name)
1064
{
1065
    GtkWidget *treeDownloadQueue;
1066
    GtkTreeModel *store;
1067
    GtkTreeIter iter;
1068

                
1069
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1070
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1071

                
1072
    g_return_if_fail(find_iter_by_collection_name(store, collection_name, &iter));
1073

                
1074
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1075
}
1076

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

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

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

                
1089
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FIELD_POSTER, poster, -1);
1090
}
1091

                
1092
static void
1093
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)
1094
{
1095
    GtkWidget *treeDownloadQueue;
1096
    GtkTreeModel *store;
1097
    GtkTreeIter parent, iter;
1098
    char size[64];
1099
    char size_remaining[64];
1100
    char total_groups[256];
1101
    char stamp_str[64];
1102
    GList *list;
1103
    int progress;
1104

                
1105
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1106
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1107

                
1108
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1109
    g_return_if_fail(file_size > 0);
1110

                
1111
    list = groups;
1112
    memset(&total_groups, 0, sizeof(total_groups));
1113
    while (list) {
1114
        if (total_groups[0] != '\0') {
1115
            char tmp[256];
1116

                
1117
            memset(tmp, 0, sizeof(tmp));
1118
            snprintf(tmp, sizeof(tmp) - 1, "%s, %s", total_groups, (const char *) list->data);
1119
            strcpy(total_groups, tmp);
1120
        } else {
1121
            strncpy(total_groups, list->data, sizeof(total_groups) - 1);
1122
        }
1123

                
1124
        list = g_list_next(list);
1125
    }
1126

                
1127
    memset(size, 0, sizeof(size));
1128
    nntpgrab_utils_calculate_file_size(file_size, size, sizeof(size) - 1);
1129

                
1130
    memset(&stamp_str, 0, sizeof(stamp_str));
1131
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1132

                
1133
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, &parent);
1134
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_SUBJECT, subject,
1135
                                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1136
                                                        FIELD_POSTER, poster,
1137
                                                        FIELD_GROUPS, total_groups,
1138
                                                        FIELD_STAMP_STR, stamp_str,
1139
                                                        FIELD_FILESIZE, file_size,
1140
                                                        FIELD_FILESIZE_STR, size,
1141
                                                        FIELD_FILESIZE_REMAINING, file_size,
1142
                                                        FIELD_FILESIZE_REMAINING_STR, size,
1143
                                                        FIELD_NUM_PARTS, num_parts,
1144
                                                        FIELD_STATE_STR, NULL, -1);
1145

                
1146
    if (state == TASK_STATE_SKIPPED) {
1147
        gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1148
                                                            FIELD_FILESIZE_REMAINING, (guint64) 0,
1149
                                                            FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1150
    }
1151

                
1152
    // Update the parent
1153
    memset(size, 0, sizeof(size));
1154
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1155

                
1156
    memset(size_remaining, 0, sizeof(size_remaining));
1157
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1158
    progress = calculate_progress(total_size, total_size_remaining);
1159

                
1160
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1161
                                                        FIELD_FILESIZE, total_size,
1162
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1163
                                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1164
                                                        FIELD_PROGRESS, progress, -1);
1165
}
1166

                
1167
static void
1168
file_removed (NntpgrabGlue *obj, const char *collection_name, const char *subject, guint64 total_size, guint64 total_size_remaining, gpointer data)
1169
{
1170
    GtkWidget *treeDownloadQueue;
1171
    GtkTreeModel *store;
1172
    GtkTreeIter iter, parent;
1173
    int progress;
1174
    char size[64];
1175
    char size_remaining[64];
1176

                
1177
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1178
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1179

                
1180
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1181
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1182

                
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

                
1189
    progress = calculate_progress(total_size, total_size_remaining);
1190

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

                
1197
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1198
}
1199

                
1200
static void
1201
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)
1202
{
1203
    GtkWidget *treeDownloadQueue;
1204
    GtkTreeModel *store;
1205
    GtkTreeIter iter, parent;
1206
    int progress;
1207
    char size[64];
1208
    char size_remaining[64];
1209

                
1210
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1211
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1212

                
1213
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1214
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1215

                
1216
    switch(new_state) {
1217
        case TASK_STATE_WAITING_FOR_DOWNLOAD:       // The file was restarted
1218
            // Update the row itself
1219
            memset(size_remaining, 0, sizeof(size_remaining));
1220
            nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1221

                
1222
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PARTS_DONE, (int) 0,
1223
                                                                FIELD_PARTS_FAILED, (int) 0,
1224
                                                                FIELD_PROGRESS, (int) 0,
1225
                                                                FIELD_STATE_STR, NULL,
1226
                                                                FIELD_FILESIZE_REMAINING, file_size_remaining,
1227
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1228

                
1229
            // Update the parent
1230
            progress = calculate_progress(total_size, total_size_remaining);
1231

                
1232
            memset(size, 0, sizeof(size));
1233
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1234

                
1235
            memset(size_remaining, 0, sizeof(size_remaining));
1236
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1237

                
1238
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1239
                                                                FIELD_FILESIZE, total_size,
1240
                                                                FIELD_FILESIZE_STR, size,
1241
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1242
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1243

                
1244
            break;
1245

                
1246
        case TASK_STATE_DOWNLOADING:
1247
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 0,
1248
                                                                FIELD_STATE_STR, NULL, -1);     // The state message should be NULL. In that case the value of the progress bar will be shown
1249
            break;
1250

                
1251
        case TASK_STATE_WAITING_FOR_DECODE:
1252
            memset(size_remaining, 0, sizeof(size_remaining));
1253
            nntpgrab_utils_calculate_file_size((guint64) 0, size_remaining, sizeof(size_remaining) - 1);
1254
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1255
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining,
1256
                                                                FIELD_STATE_STR, _("Waiting"), -1);
1257

                
1258
            break;
1259

                
1260
        case TASK_STATE_DECODING:
1261
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, (int) 100,
1262
                                                                FIELD_STATE_STR, _("Decoding"), -1);
1263
            break;
1264

                
1265
        case TASK_STATE_FINISHED_COMPLETE:
1266
            // Do we need to update the parent
1267
            g_return_if_fail(real_filename != NULL);
1268
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Done"),
1269
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1270
            break;
1271

                
1272
        case TASK_STATE_FINISHED_INCOMPLETE:
1273
            g_return_if_fail(real_filename != NULL);
1274
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Incomplete"),
1275
                                                                FIELD_REAL_FILENAME, real_filename, -1);
1276
            break;
1277

                
1278
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1279
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Not available"), -1);
1280
            break;
1281

                
1282
        case TASK_STATE_SKIPPED:
1283
            gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1284
                                                                FIELD_FILESIZE_REMAINING, (guint64) 0,
1285
                                                                FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1286

                
1287
            // Update the parent
1288
            progress = calculate_progress(total_size, total_size_remaining);
1289

                
1290
            memset(size, 0, sizeof(size));
1291
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1292

                
1293
            memset(size_remaining, 0, sizeof(size_remaining));
1294
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1295

                
1296
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1297
                                                                FIELD_FILESIZE, total_size,
1298
                                                                FIELD_FILESIZE_REMAINING, size,
1299
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1300
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1301

                
1302
            break;
1303
    };
1304
}
1305

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

                
1315
    memset(&size, 0, sizeof(size));
1316
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1317

                
1318
    memset(&size_remaining, 0, sizeof(size_remaining));
1319
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1320

                
1321
    progress = calculate_progress(total_size, total_size_remaining);
1322

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

                
1338
static gboolean
1339
find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter)
1340
{
1341
    unsigned int hash1, hash2;
1342

                
1343
    hash1 = g_str_hash(collection_name);
1344

                
1345
    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), iter)) {
1346
        do {
1347
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME_HASH, &hash2, -1);
1348

                
1349
            if (hash1 == hash2) {
1350
                char *name = NULL;
1351

                
1352
                gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME, &name, -1);
1353

                
1354
                if (!strcmp(collection_name, name)) {
1355
                    g_free(name);
1356
                    return TRUE;
1357
                }
1358

                
1359
                g_free(name);
1360
            }
1361
        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1362
    }
1363

                
1364
    return FALSE;
1365
}
1366

                
1367
static gboolean
1368
find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter)
1369
{
1370
    unsigned int hash1, hash2;
1371

                
1372
    if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), iter, &parent)) {
1373
        // Node contains no children. Shouldn't happen
1374
        return FALSE;
1375
    }
1376

                
1377
    hash1 = g_str_hash(subject);
1378

                
1379
    do {
1380
        gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT_HASH, &hash2, -1);
1381

                
1382
        if (hash1 == hash2) {
1383
            char *name = NULL;
1384

                
1385
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT, &name, -1);
1386

                
1387
            if (!strcmp(subject, name)) {
1388
                g_free(name);
1389
                return TRUE;
1390
            }
1391

                
1392
            g_free(name);
1393
        }
1394
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1395

                
1396
    return FALSE;
1397
}
1398

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

                
1410
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1411
    g_return_if_fail(file_size_remaining >= 0);
1412

                
1413
    memset(&size_str, 0, sizeof(size_str));
1414
    nntpgrab_utils_calculate_file_size(file_size, size_str, sizeof(size_str) - 1);
1415

                
1416
    memset(&size_remaining_str, 0, sizeof(size_remaining_str));
1417
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining_str, sizeof(size_remaining_str) - 1);
1418

                
1419
    memset(&stamp_str, 0, sizeof(stamp_str));
1420
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1421

                
1422
    if (status == TASK_STATE_SKIPPED) {
1423
        progress = 0;
1424
    } else {
1425
        progress = calculate_progress(file_size, file_size_remaining);
1426
    }
1427

                
1428
    switch(status) {
1429
        case TASK_STATE_WAITING_FOR_DOWNLOAD:
1430
        case TASK_STATE_DOWNLOADING:
1431
            state_str = NULL;
1432
            break;
1433

                
1434
        case TASK_STATE_WAITING_FOR_DECODE:
1435
            state_str = _("Waiting");
1436
            break;
1437

                
1438
        case TASK_STATE_DECODING:
1439
            state_str = _("Decoding");
1440
            break;
1441

                
1442
        case TASK_STATE_FINISHED_COMPLETE:
1443
            state_str = _("Done");
1444
            break;
1445

                
1446
        case TASK_STATE_FINISHED_INCOMPLETE:
1447
            state_str = _("Incomplete");
1448
            break;
1449

                
1450
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1451
            state_str = _("Not available");
1452
            break;
1453

                
1454
        case TASK_STATE_SKIPPED:
1455
            state_str = _("Skipped");
1456
            break;
1457

                
1458
        default:
1459
            state_str = NULL;
1460
            break;
1461
    };
1462

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

                
1481
static void
1482
foreach_group_func (const char *collection_name, const char *subject, const char *group, gpointer data)
1483
{
1484
    GtkTreeStore *store = GTK_TREE_STORE(data);
1485
    GtkTreeIter iter, parent;
1486
    char *old_groups;
1487
    char *new_groups;
1488

                
1489
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1490
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1491

                
1492
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_GROUPS, &old_groups, -1);
1493
    if (old_groups && strlen(old_groups) > 0) {
1494
        new_groups = g_strdup_printf("%s, %s", old_groups, group);
1495
    } else {
1496
        new_groups = g_strdup(group);
1497
    }
1498

                
1499
    gtk_tree_store_set(store, &iter, FIELD_GROUPS, new_groups, -1);
1500

                
1501
    g_free(new_groups);
1502
    g_free(old_groups);
1503
}
1504

                
1505
static void
1506
on_tree_view_select (GtkTreeSelection *selection, gpointer user_data)
1507
{
1508
    GtkWidget *btnRemoveItemFromQueue = nntpgrab_gui_base_get_widget("btnRemoveItemFromQueue");
1509
    GtkWidget *btnQueueMoveToTop = nntpgrab_gui_base_get_widget("btnQueueMoveToTop");
1510
    GtkWidget *btnQueueMoveUp = nntpgrab_gui_base_get_widget("btnQueueMoveUp");
1511
    GtkWidget *btnQueueMoveDown = nntpgrab_gui_base_get_widget("btnQueueMoveDown");
1512
    GtkWidget *btnQueueMoveToBottom = nntpgrab_gui_base_get_widget("btnQueueMoveToBottom");
1513

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

                
1524
    // Enable the remove button and the move buttons
1525
    gtk_widget_set_sensitive(btnRemoveItemFromQueue, TRUE);
1526
    gtk_widget_set_sensitive(btnQueueMoveToTop, TRUE);
1527
    gtk_widget_set_sensitive(btnQueueMoveUp, TRUE);
1528
    gtk_widget_set_sensitive(btnQueueMoveDown, TRUE);
1529
    gtk_widget_set_sensitive(btnQueueMoveToBottom, TRUE);
1530
}
1531

                
1532
G_MODULE_EXPORT void
1533
on_btnPauseQueue_clicked(GtkWidget *caller, gpointer data)
1534
{
1535
    NGSchedularState active_state = nntpgrab_glue_schedular_get_state(glue);
1536

                
1537
    if (status_flag == SCHEDULAR_STATE_STOPPED && active_state == SCHEDULAR_STATE_STOPPING) {
1538
        GtkWidget *windowMain;
1539

                
1540
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
1541

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

                
1544
        g_signal_handlers_block_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1545

                
1546
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(caller), TRUE);
1547

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

                
1550
        return;
1551
    }
1552

                
1553
    if (status_flag == SCHEDULAR_STATE_RUNNING) {
1554
        status_flag = SCHEDULAR_STATE_STOPPED;
1555
        nntpgrab_glue_schedular_stop(glue, FALSE);
1556
    } else {
1557
        status_flag = SCHEDULAR_STATE_RUNNING;
1558
        nntpgrab_glue_schedular_start(glue);
1559
    }
1560
}
1561

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

                
1573
    return FALSE;
1574
}
1575

                
1576
static void
1577
schedular_state_changed_cb(NntpgrabGlue *obj, NGSchedularState state, const char *reason)
1578
{
1579
    GtkWidget *btnPauseQueue;
1580

                
1581
    if (state == SCHEDULAR_STATE_STOPPING) {
1582
        status_flag = SCHEDULAR_STATE_STOPPED;
1583
    } else {
1584
        status_flag = state;
1585
    }
1586

                
1587
    if (state == SCHEDULAR_STATE_STOPPING && reason != NULL) {
1588
        char *msg;
1589
        GtkWidget *windowMain;
1590
#ifdef HAVE_LIBNOTIFY
1591
        NotifyNotification *notification;
1592
#endif
1593

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

                
1597
#ifdef HAVE_LIBNOTIFY
1598
#ifdef HAVE_LIBNOTIFY_0_7
1599
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL);
1600
#else
1601
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL, NULL);
1602
#endif
1603
        if (nntpgrab_gui_base_tray_icon_get_is_shown()) {
1604
#ifndef HAVE_LIBNOTIFY_0_7
1605
            notify_notification_attach_to_status_icon(notification, nntpgrab_gui_base_tray_get_status_icon());
1606
#endif
1607
        }
1608
        notify_notification_show(notification, NULL);
1609
#endif
1610

                
1611
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK);
1612
        g_free(msg);
1613
    }
1614

                
1615
    btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1616
    g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1617

                
1618
    if (state == SCHEDULAR_STATE_RUNNING) {
1619
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), FALSE);
1620
    } else {
1621
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1622
    }
1623

                
1624
    g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1625

                
1626
}
1627

                
1628
static void
1629
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)
1630
{
1631
    static int times_been_here = 10;
1632
    gboolean no_data_received = FALSE;
1633
    GtkTreeModel *model;
1634
    GtkWidget *treeDownloadQueue;
1635
    GtkTreeIter iter, parent;
1636
    guint64 total_estimated_time_remaining;
1637

                
1638
    if (bytes_received1 == 0 && bytes_received2 == 0 && bytes_received3 == 0 && bytes_received4 == 0 && bytes_received5 == 0 &&
1639
        bytes_received6 == 0 && bytes_received7 == 0 && bytes_received8 == 0 && bytes_received9 == 0 && bytes_received10 == 0) {
1640
            no_data_received = TRUE;
1641
    }
1642

                
1643
    // Only update on startup, on every 3 seconds, and when there has no data been received in the last 10 seconds
1644
    if (times_been_here < 3 && !no_data_received) {
1645
        times_been_here += 1;
1646
        return;
1647
    }
1648

                
1649
    if (no_data_received) {
1650
        times_been_here = 3;    // update the time remaining the next time we get here
1651
    } else {
1652
        times_been_here = 0;    // perform the next update in 10 seconds
1653
    }
1654

                
1655
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1656
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1657

                
1658
    if (!gtk_tree_model_get_iter_first(model, &parent)) {
1659
        // Nothing in the download queue
1660
        return;
1661
    }
1662

                
1663
    total_estimated_time_remaining = 0;
1664

                
1665
    do {
1666
        guint64 filesize;
1667
        int estimated_time_remaining;
1668
        char str_estimated_time_remaining[64];
1669
        char str_estimated_time_to_finish[64];
1670

                
1671
        gtk_tree_model_get(model, &parent, FIELD_FILESIZE_REMAINING, &filesize, -1);
1672

                
1673
        estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1674
                                                                                        bytes_received3, bytes_received4,
1675
                                                                                        bytes_received5, bytes_received6,
1676
                                                                                        bytes_received7, bytes_received8,
1677
                                                                                        bytes_received9, bytes_received10,
1678
                                                                                        filesize);
1679

                
1680
        if (estimated_time_remaining == -1) {
1681
            // Not enough data received to make an estimation
1682
            return;
1683
        }
1684

                
1685
        if (estimated_time_remaining == 0) {
1686
            // File is already downloaded
1687
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, "",
1688
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1689
        } else {
1690
            memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1691
            nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining + estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1692

                
1693
            memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1694
            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);
1695

                
1696
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1697
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1698
        }
1699

                
1700
        // Walk through all the children
1701
        if (gtk_tree_model_iter_children(model, &iter, &parent)) {
1702
            do {
1703
                gtk_tree_model_get(model, &iter, FIELD_FILESIZE_REMAINING, &filesize, -1);
1704

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

                
1713
                    total_estimated_time_remaining += estimated_time_remaining;
1714

                
1715
                    memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1716
                    nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1717

                
1718
                    memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1719
                    nntpgrab_utils_get_readable_finished_time(total_estimated_time_remaining, str_estimated_time_to_finish, sizeof(str_estimated_time_to_finish));
1720

                
1721
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1722
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1723
                } else {
1724
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, "",
1725
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1726
                }
1727
            } while (gtk_tree_model_iter_next(model, &iter));
1728
        }
1729
    } while (gtk_tree_model_iter_next(model, &parent));
1730
}
1731

                
1732
static void
1733
config_changed(ConfigGUI *config, gpointer data)
1734
{
1735
    ConfigGUIOpts opts = config_gui_get_opts();
1736

                
1737
    gtk_tree_view_column_set_visible(columns[FIELD_PROGRESS], opts.show_column_progress);
1738
    gtk_tree_view_column_set_visible(columns[FIELD_SUBJECT], opts.show_column_subject);
1739
    gtk_tree_view_column_set_visible(columns[FIELD_POSTER], opts.show_column_poster);
1740
    gtk_tree_view_column_set_visible(columns[FIELD_GROUPS], opts.show_column_newsgroups);
1741
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_STR], opts.show_column_filesize);
1742
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_REMAINING_STR], opts.show_column_filesize_remaining);
1743
    gtk_tree_view_column_set_visible(columns[FIELD_STAMP_STR], opts.show_column_stamp);
1744
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_REMAINING], opts.show_column_time_remaining);
1745
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_TO_FINISH], opts.show_column_time_to_finish);
1746
}
1747

                
1748
void
1749
queue_initialize()
1750
{
1751
    GtkWidget *treeDownloadQueue;
1752
    GtkTreeStore *store;
1753
    GtkCellRenderer *cellRenderer;
1754
    GtkTreeSelection *selection;
1755

                
1756
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1757

                
1758
    cellRenderer = gtk_cell_renderer_progress_new();
1759
    columns[FIELD_PROGRESS] = gtk_tree_view_column_new_with_attributes(_("Progress"), cellRenderer, "value", FIELD_PROGRESS, "text", FIELD_STATE_STR, NULL);
1760
    gtk_tree_view_column_set_resizable(columns[FIELD_PROGRESS], TRUE);
1761
    gtk_tree_view_column_set_reorderable(columns[FIELD_PROGRESS], TRUE);
1762
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_PROGRESS], -1);
1763

                
1764
    cellRenderer = gtk_cell_renderer_text_new();
1765
    columns[FIELD_SUBJECT] = gtk_tree_view_column_new_with_attributes(_("Subject"), cellRenderer, "text", FIELD_SUBJECT, NULL);
1766
    gtk_tree_view_column_set_resizable(columns[FIELD_SUBJECT], TRUE);
1767
    gtk_tree_view_column_set_reorderable(columns[FIELD_SUBJECT], TRUE);
1768
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_SUBJECT], -1);
1769

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

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

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

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

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

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

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

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

                
1834
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeDownloadQueue), GTK_TREE_MODEL(store));
1835

                
1836
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
1837
    gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1838

                
1839
    g_signal_connect(selection, "changed", G_CALLBACK(on_tree_view_select), NULL);
1840
    on_tree_view_select(selection, NULL);
1841

                
1842
    nntpgrab_glue_schedular_foreach_task(glue, foreach_collection_func, foreach_file_func, foreach_group_func, store);
1843

                
1844
    nntpgrab_glue_signal_connect(glue, "collection_added", NG_CALLBACK(collection_added), NULL);
1845
    nntpgrab_glue_signal_connect(glue, "collection_removed", NG_CALLBACK(collection_removed), NULL);
1846
    nntpgrab_glue_signal_connect(glue, "collection_modified", NG_CALLBACK(collection_modified), NULL);
1847
    nntpgrab_glue_signal_connect(glue, "file_added", NG_CALLBACK(file_added), NULL);
1848
    nntpgrab_glue_signal_connect(glue, "file_removed", NG_CALLBACK(file_removed), NULL);
1849
    nntpgrab_glue_signal_connect(glue, "file_state_changed", NG_CALLBACK(file_state_changed), NULL);
1850
    nntpgrab_glue_signal_connect(glue, "file_download_state_update", NG_CALLBACK(file_download_state_update), NULL);
1851
    nntpgrab_glue_signal_connect(glue, "schedular_state_changed", NG_CALLBACK(schedular_state_changed_cb), NULL);
1852
    nntpgrab_glue_signal_connect(glue, "traffic_monitor_update", NG_CALLBACK(traffic_monitor_update_cb), NULL);
1853
    nntpgrab_glue_signal_connect(glue, "task_moved", NG_CALLBACK(task_moved_cb), NULL);
1854
    nntpgrab_glue_signal_connect(glue, "collection_moved", NG_CALLBACK(collection_moved_cb), NULL);
1855

                
1856
    g_signal_connect(config_gui_get_object(), "config_changed", G_CALLBACK(config_changed), NULL);
1857
    config_changed(NULL, NULL);
1858

                
1859
    g_signal_connect(treeDownloadQueue, "key-press-event", G_CALLBACK(on_key_press_event), NULL);
1860

                
1861
    if (nntpgrab_glue_get_is_standalone(glue)) {
1862
        nntpgrab_glue_schedular_start(glue);
1863
    }
1864

                
1865
    status_flag = nntpgrab_glue_schedular_get_state(glue);
1866
    if (status_flag == SCHEDULAR_STATE_STOPPING || status_flag == SCHEDULAR_STATE_STOPPED) {
1867
        GtkWidget *btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1868

                
1869
        g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1870
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1871
        g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1872
    }
1873
}