Statistics
| Revision:

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

History | View | Annotate | Download (75.3 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 ((int) ShellExecuteA((HWND) GDK_WINDOW_HWND(windowMain->window), mode, file_localized, NULL, file_localized, SW_SHOW) < 32) {
87
        char *msg;
88
        GtkWidget *windowMain;
89

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                
235

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

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

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

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

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

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

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

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

                
288
    return TRUE;
289
}
290

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

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

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

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

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

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

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

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

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

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

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

                
348
        list = g_list_next(list);
349
    }
350

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

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

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

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

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

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

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

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

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

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

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

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

                
418
        list = g_list_next(list);
419
    }
420

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

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

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

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

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

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

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

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

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

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

                
465
        g_return_if_fail(name != NULL);
466

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

                
476
        g_free(name);
477

                
478
        list = g_list_next(list);
479
    }
480

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

                
484
    opts = nntpgrab_glue_config_get_opts(glue);
485

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

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

                
500
        g_free(folder);
501
    }
502

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

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

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

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

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

                
531
    path = selectedRows->data;
532

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

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

                
541
    g_return_if_fail(real_filename != NULL);
542

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

                
546
    open_file(full_filename, FALSE);
547

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                
695
        list = g_list_next(list);
696
    }
697

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

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

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

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

                
719
        if (subject) {
720
            g_free(subject);
721
            subject = NULL;
722
        }
723

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

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

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

                
744
        g_strfreev(values);
745
        g_free(path_str);
746

                
747
        if (collection_name == NULL || strlen(collection_name) == 0) {
748
            GtkTreeIter parent;
749

                
750
            // Look at the parent
751
            gtk_tree_model_iter_parent(model, &parent, &iter);
752
            gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
753

                
754
            is_child = TRUE;
755
        }
756

                
757
        g_assert(collection_name);
758

                
759
        switch (direction) {
760
            case MOVE_DIRECTION_TOP:
761
                if (is_child) {
762
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, 0));
763
                } else {
764
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, 0));
765
                }
766
                break;
767

                
768
            case MOVE_DIRECTION_UP:
769
                if (position == 0) {
770
                    break;
771
                }
772
                if (is_child) {
773
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position - 1));
774
                } else {
775
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position - 1));
776
                }
777
                break;
778

                
779
            case MOVE_DIRECTION_DOWN:
780
                if (is_child) {
781
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, position + 1));
782
                } else {
783
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, position + 1));
784
                }
785
                break;
786

                
787
            case MOVE_DIRECTION_BOTTOM:
788
                if (is_child) {
789
                    g_return_if_fail(nntpgrab_glue_schedular_move_task(glue, collection_name, subject, collection_name, -1));
790
                } else {
791
                    g_return_if_fail(nntpgrab_glue_schedular_move_collection(glue, collection_name, -1));
792
                }
793
                break;
794

                
795
            default:
796
                break;
797
        }
798

                
799
        list = g_list_next(list);
800
        gtk_tree_path_free(path);
801
        gtk_tree_row_reference_free(ref);
802
    }
803

                
804
    g_list_free(references);
805

                
806
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
807
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
808
        char msg[1024];
809

                
810
        memset(&msg, 0, sizeof(msg));
811
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
812
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
813
        g_free(errmsg);
814
    }
815

                
816
    if (collection_name) {
817
        g_free(collection_name);
818
    }
819

                
820
    if (subject) {
821
        g_free(subject);
822
    }
823
}
824

                
825
G_MODULE_EXPORT void
826
on_menuitemMoveToTop_activate(GtkWidget *caller, gpointer data)
827
{
828
    move_item(MOVE_DIRECTION_TOP);
829
}
830

                
831
G_MODULE_EXPORT void
832
on_menuitemMoveUp_activate(GtkWidget *caller, gpointer data)
833
{
834
    move_item(MOVE_DIRECTION_UP);
835
}
836

                
837
G_MODULE_EXPORT void
838
on_menuitemMoveDown_activate(GtkWidget *caller, gpointer data)
839
{
840
    move_item(MOVE_DIRECTION_DOWN);
841
}
842

                
843
G_MODULE_EXPORT void
844
on_menuitemMoveToBottom_activate(GtkWidget *caller, gpointer data)
845
{
846
    move_item(MOVE_DIRECTION_BOTTOM);
847
}
848

                
849
G_MODULE_EXPORT void
850
on_btnRemoveItemFromQueue_clicked(GtkWidget *caller, gpointer data)
851
{
852
    GtkWidget *treeDownloadQueue;
853
    GtkTreeSelection *selection;
854
    GList *list, *selectedRows, *list_new;
855
    GtkTreeModel *model;
856
    char *errmsg = NULL;
857

                
858
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
859
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
860

                
861
    g_return_if_fail(gtk_tree_selection_count_selected_rows(selection) > 0);
862

                
863
    selectedRows = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(selection), &model);
864

                
865
    // Duplicate the list and transform the items to GtkTreeRowReferences
866
    // This is because we are going to delete items in the tree
867
    list = selectedRows;
868
    list_new = NULL;
869
    while (list) {
870
        GtkTreePath *path = list->data;
871

                
872
        list_new = g_list_append(list_new, gtk_tree_row_reference_new(model, path));
873

                
874
        list = g_list_next(list);
875
    }
876

                
877
    g_list_foreach (selectedRows, (GFunc) gtk_tree_path_free, NULL);
878
    g_list_free (selectedRows);
879

                
880
    // Now loop trough all the items and remove the downloads tasks
881
    list = list_new;
882
    while (list) {
883
        GtkTreeRowReference *ref = list->data;
884

                
885
        if (gtk_tree_row_reference_valid(ref)) {
886
            GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
887
            GtkTreeIter iter, parent;
888
            GList *list_tmp;
889
            char *collection_name = NULL;
890
            char *subject = NULL;
891
            gboolean item_is_already_removed = FALSE;
892

                
893
            /* Did we already try to remove the parent of the active item? */
894
            list_tmp = g_list_previous(list);
895
            while (list_tmp) {
896
                GtkTreeRowReference *ref_tmp = list_tmp->data;
897
                GtkTreePath *path_tmp = gtk_tree_row_reference_get_path(ref_tmp);
898

                
899
                if (gtk_tree_path_is_ancestor(path_tmp, path)) {
900
                    /* Hey, the collection itself is already removed! */
901
                    item_is_already_removed = TRUE;
902
                }
903

                
904
                gtk_tree_path_free(path_tmp);
905

                
906
                list_tmp = g_list_previous(list_tmp);
907
            }
908

                
909
            gtk_tree_model_get_iter(model, &iter, path);
910
            gtk_tree_path_free(path);
911

                
912
            if (item_is_already_removed) {
913
                list = g_list_next(list);
914
                continue;
915
            }
916

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

                
924
                    memset(&msg, 0, sizeof(msg));
925
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing collection(s):\n%s"), errmsg);
926
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
927
                    g_free(errmsg);
928
                }
929

                
930
                g_free(collection_name);
931
            } else {
932
                // Delete one item from the collection
933
                gtk_tree_model_get(model, &parent, FIELD_COLLECTION_NAME, &collection_name, -1);
934
                gtk_tree_model_get(model, &iter, FIELD_SUBJECT, &subject, -1);
935

                
936
                if (!nntpgrab_glue_schedular_del_task_from_queue(glue, collection_name, subject, &errmsg)) {
937
                    GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
938
                    char msg[1024];
939

                
940
                    memset(&msg, 0, sizeof(msg));
941
                    snprintf(msg, sizeof(msg) - 1, _("Error occured while removing file(s):\n%s"), errmsg);
942
                    nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
943
                    g_free(errmsg);
944
                }
945

                
946
                g_free(collection_name);
947
                g_free(subject);
948
            }
949
        }
950

                
951
        list = g_list_next(list);
952
    }
953

                
954
    /* Traverse the list again to free all the row references */
955
    list = list_new;
956
    while (list) {
957
        gtk_tree_row_reference_free((GtkTreeRowReference*) list->data);
958
        list = g_list_next(list);
959
    }
960
    g_list_free(list_new);
961

                
962
    if (!nntpgrab_glue_schedular_save_queue(glue, &errmsg)) {
963
        GtkWidget *windowMain = nntpgrab_gui_base_get_widget("windowMain");
964
        char msg[1024];
965

                
966
        memset(&msg, 0, sizeof(msg));
967
        snprintf(msg, sizeof(msg) - 1, _("Download queue could not be saved:\n%s"), errmsg);
968
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
969
        g_free(errmsg);
970
    }
971
}
972

                
973
static void
974
generate_stamp(time_t stamp, char *buf, int buf_len)
975
{
976
    struct tm *now;
977

                
978
    if ((now = localtime(&stamp)) == NULL) {
979
        // Date could not be parted
980
        buf[0] = '\0';
981
    }
982

                
983
    strftime(buf, buf_len, "%c", now);
984
}
985

                
986
static int
987
calculate_progress(guint64 file_size, guint64 file_size_remaining)
988
{
989
    double progress;
990
    int progress_int;
991

                
992
    // Prevent a division by zero
993
    if (file_size_remaining == 0) {
994
        return 100;
995
    }
996

                
997
    progress = (double) file_size - file_size_remaining;
998
    progress /= file_size;
999
    progress *= 100;
1000
    progress_int = (int) progress;
1001

                
1002
    if (progress_int > 100) {
1003
        g_print(__FILE__ ":%i progress overflow!\n", __LINE__);
1004
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1005
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1006
        progress_int = 100;
1007
    }
1008

                
1009
    if (progress < 0) {
1010
        g_print(__FILE__ ":%i progress underflow!\n", __LINE__);
1011
        g_print("file_size = %"G_GUINT64_FORMAT"\n", file_size);
1012
        g_print("file_size_remaining = %"G_GUINT64_FORMAT"\n", file_size_remaining);
1013
        progress_int = 0;
1014
    }
1015

                
1016
    return progress_int;
1017
}
1018

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

                
1031
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1032
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1033

                
1034
    /* If the file has just been skipped, ignore this event */
1035
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_STATE_STR, &state, -1);
1036
    if (state && !strcmp(state, _("Skipped"))) {
1037
        g_free(state);
1038
        return;
1039
    }
1040
    g_free(state);
1041

                
1042
    // Update the parent
1043
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1044

                
1045
    memset(&size_remaining, 0, sizeof(size_remaining));
1046
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1047

                
1048
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1049
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1050
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1051

                
1052
    // Update the child
1053
    progress = calculate_progress(file_size, file_size_remaining);
1054
    memset(&size_remaining, 0, sizeof(size_remaining));
1055
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1056

                
1057
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_PROGRESS, progress,
1058
                                                        FIELD_PARTS_DONE, num_parts_done,
1059
                                                        FIELD_PARTS_FAILED, num_parts_failed,
1060
                                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1061
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1062
}
1063

                
1064
static void
1065
collection_added (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1066
{
1067
    GtkWidget *treeDownloadQueue;
1068
    GtkTreeModel *store;
1069
    GtkTreeIter iter;
1070

                
1071
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1072
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1073

                
1074
    gtk_tree_store_append(GTK_TREE_STORE(store), &iter, NULL);
1075
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_COLLECTION_NAME, collection_name,
1076
                                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1077
                                                        FIELD_SUBJECT, collection_name,
1078
                                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1079
                                                        FIELD_POSTER, poster,
1080
                                                        FIELD_STATE_STR, NULL, -1);
1081
}
1082

                
1083
static void
1084
collection_removed (NntpgrabGlue *obj, const char *collection_name)
1085
{
1086
    GtkWidget *treeDownloadQueue;
1087
    GtkTreeModel *store;
1088
    GtkTreeIter iter;
1089

                
1090
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1091
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1092

                
1093
    g_return_if_fail(find_iter_by_collection_name(store, collection_name, &iter));
1094

                
1095
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1096
}
1097

                
1098
static void
1099
collection_modified (NntpgrabGlue *obj, const char *collection_name, const char *poster)
1100
{
1101
    GtkWidget *treeDownloadQueue;
1102
    GtkTreeModel *store;
1103
    GtkTreeIter iter;
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(store, collection_name, &iter));
1109

                
1110
    gtk_tree_store_set(GTK_TREE_STORE(store), &iter, FIELD_POSTER, poster, -1);
1111
}
1112

                
1113
static void
1114
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)
1115
{
1116
    GtkWidget *treeDownloadQueue;
1117
    GtkTreeModel *store;
1118
    GtkTreeIter parent, iter;
1119
    char size[64];
1120
    char size_remaining[64];
1121
    char total_groups[256];
1122
    char stamp_str[64];
1123
    GList *list;
1124
    int progress;
1125

                
1126
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1127
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1128

                
1129
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1130
    g_return_if_fail(file_size > 0);
1131

                
1132
    list = groups;
1133
    memset(&total_groups, 0, sizeof(total_groups));
1134
    while (list) {
1135
        if (total_groups[0] != '\0') {
1136
            char tmp[256];
1137

                
1138
            memset(tmp, 0, sizeof(tmp));
1139
            snprintf(tmp, sizeof(tmp) - 1, "%s, %s", total_groups, (const char *) list->data);
1140
            strcpy(total_groups, tmp);
1141
        } else {
1142
            strncpy(total_groups, list->data, sizeof(total_groups) - 1);
1143
        }
1144

                
1145
        list = g_list_next(list);
1146
    }
1147

                
1148
    memset(size, 0, sizeof(size));
1149
    nntpgrab_utils_calculate_file_size(file_size, size, sizeof(size) - 1);
1150

                
1151
    memset(&stamp_str, 0, sizeof(stamp_str));
1152
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1153

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

                
1167
    if (state == TASK_STATE_SKIPPED) {
1168
        gtk_tree_store_set(GTK_TREE_STORE(store), &iter,    FIELD_STATE_STR, _("Skipped"),
1169
                                                            FIELD_FILESIZE_REMAINING, (guint64) 0,
1170
                                                            FIELD_FILESIZE_REMAINING_STR, NULL, -1);
1171
    }
1172

                
1173
    // Update the parent
1174
    memset(size, 0, sizeof(size));
1175
    nntpgrab_utils_calculate_file_size(total_file_size, size, sizeof(size) - 1);
1176

                
1177
    memset(size_remaining, 0, sizeof(size_remaining));
1178
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1179
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1180

                
1181
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1182
                                                        FIELD_FILESIZE, total_file_size,
1183
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1184
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1185
                                                        FIELD_PROGRESS, progress, -1);
1186
}
1187

                
1188
static void
1189
file_removed (NntpgrabGlue *obj, const char *collection_name, const char *subject, guint64 total_file_size, guint64 total_file_size_remaining, gpointer data)
1190
{
1191
    GtkWidget *treeDownloadQueue;
1192
    GtkTreeModel *store;
1193
    GtkTreeIter iter, parent;
1194
    int progress;
1195
    char size[64];
1196
    char size_remaining[64];
1197

                
1198
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1199
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1200

                
1201
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1202
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1203

                
1204
    memset(&size, 0, sizeof(size));
1205
    nntpgrab_utils_calculate_file_size(total_file_size, size, sizeof(size) - 1);
1206

                
1207
    memset(size_remaining, 0, sizeof(size_remaining));
1208
    nntpgrab_utils_calculate_file_size(total_file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1209

                
1210
    progress = calculate_progress(total_file_size, total_file_size_remaining);
1211

                
1212
    gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_FILESIZE_STR, size,
1213
                                                        FIELD_FILESIZE, total_file_size,
1214
                                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1215
                                                        FIELD_FILESIZE_REMAINING, total_file_size_remaining,
1216
                                                        FIELD_PROGRESS, progress, -1);
1217

                
1218
    gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
1219
}
1220

                
1221
static void
1222
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)
1223
{
1224
    GtkWidget *treeDownloadQueue;
1225
    GtkTreeModel *store;
1226
    GtkTreeIter iter, parent;
1227
    int progress;
1228
    char size[64];
1229
    char size_remaining[64];
1230

                
1231
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1232
    store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1233

                
1234
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1235
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1236

                
1237
    switch(new_state) {
1238
        case TASK_STATE_WAITING_FOR_DOWNLOAD:       // The file was restarted
1239
            // Update the row itself
1240
            memset(size_remaining, 0, sizeof(size_remaining));
1241
            nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1242

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

                
1250
            // Update the parent
1251
            progress = calculate_progress(total_size, total_size_remaining);
1252

                
1253
            memset(size, 0, sizeof(size));
1254
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1255

                
1256
            memset(size_remaining, 0, sizeof(size_remaining));
1257
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1258

                
1259
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1260
                                                                FIELD_FILESIZE, total_size,
1261
                                                                FIELD_FILESIZE_STR, size,
1262
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1263
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1264

                
1265
            break;
1266

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

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

                
1279
            break;
1280

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

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

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

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

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

                
1308
            // Update the parent
1309
            progress = calculate_progress(total_size, total_size_remaining);
1310

                
1311
            memset(size, 0, sizeof(size));
1312
            nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1313

                
1314
            memset(size_remaining, 0, sizeof(size_remaining));
1315
            nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1316

                
1317
            gtk_tree_store_set(GTK_TREE_STORE(store), &parent,  FIELD_PROGRESS, progress,
1318
                                                                FIELD_FILESIZE, total_size,
1319
                                                                FIELD_FILESIZE_REMAINING, size,
1320
                                                                FIELD_FILESIZE_REMAINING, total_size_remaining,
1321
                                                                FIELD_FILESIZE_REMAINING_STR, size_remaining, -1);
1322

                
1323
            break;
1324
    };
1325
}
1326

                
1327
static void
1328
foreach_collection_func (const char *collection_name, const char *poster, nguint64 total_size, nguint64 total_size_remaining, int position, gpointer data)
1329
{
1330
    GtkTreeStore *store = GTK_TREE_STORE(data);
1331
    GtkTreeIter iter;
1332
    char size[64];
1333
    char size_remaining[64];
1334
    int progress;
1335

                
1336
    memset(&size, 0, sizeof(size));
1337
    nntpgrab_utils_calculate_file_size(total_size, size, sizeof(size) - 1);
1338

                
1339
    memset(&size_remaining, 0, sizeof(size_remaining));
1340
    nntpgrab_utils_calculate_file_size(total_size_remaining, size_remaining, sizeof(size_remaining) - 1);
1341

                
1342
    progress = calculate_progress(total_size, total_size_remaining);
1343

                
1344
    gtk_tree_store_append(store, &iter, NULL);
1345
    gtk_tree_store_set(store, &iter,    FIELD_COLLECTION_NAME, collection_name,
1346
                                        FIELD_COLLECTION_NAME_HASH, g_str_hash(collection_name),
1347
                                        FIELD_SUBJECT, collection_name,
1348
                                        FIELD_SUBJECT_HASH, g_str_hash(collection_name),
1349
                                        FIELD_POSTER, poster,
1350
                                        FIELD_FILESIZE_STR, size,
1351
                                        FIELD_FILESIZE, total_size,
1352
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining,
1353
                                        FIELD_FILESIZE_REMAINING, total_size_remaining,
1354
                                        FIELD_STATE_STR, NULL,
1355
                                        FIELD_PROGRESS, progress,
1356
                                        FIELD_POSITION, position, -1);
1357
}
1358

                
1359
static gboolean
1360
find_iter_by_collection_name(GtkTreeModel *model, const char *collection_name, GtkTreeIter *iter)
1361
{
1362
    unsigned int hash1, hash2;
1363

                
1364
    hash1 = g_str_hash(collection_name);
1365

                
1366
    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), iter)) {
1367
        do {
1368
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME_HASH, &hash2, -1);
1369

                
1370
            if (hash1 == hash2) {
1371
                char *name = NULL;
1372

                
1373
                gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_COLLECTION_NAME, &name, -1);
1374

                
1375
                if (!strcmp(collection_name, name)) {
1376
                    g_free(name);
1377
                    return TRUE;
1378
                }
1379

                
1380
                g_free(name);
1381
            }
1382
        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1383
    }
1384

                
1385
    return FALSE;
1386
}
1387

                
1388
static gboolean
1389
find_iter_by_subject(GtkTreeModel *model, GtkTreeIter parent, const char *subject, GtkTreeIter *iter)
1390
{
1391
    unsigned int hash1, hash2;
1392

                
1393
    if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(model), iter, &parent)) {
1394
        // Node contains no children. Shouldn't happen
1395
        return FALSE;
1396
    }
1397

                
1398
    hash1 = g_str_hash(subject);
1399

                
1400
    do {
1401
        gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT_HASH, &hash2, -1);
1402

                
1403
        if (hash1 == hash2) {
1404
            char *name = NULL;
1405

                
1406
            gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FIELD_SUBJECT, &name, -1);
1407

                
1408
            if (!strcmp(subject, name)) {
1409
                g_free(name);
1410
                return TRUE;
1411
            }
1412

                
1413
            g_free(name);
1414
        }
1415
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), iter));
1416

                
1417
    return FALSE;
1418
}
1419

                
1420
static void
1421
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)
1422
{
1423
    GtkTreeStore *store = GTK_TREE_STORE(data);
1424
    GtkTreeIter iter, parent;
1425
    char size_str[64];
1426
    char size_remaining_str[64];
1427
    char stamp_str[64];
1428
    int progress;
1429
    char *state_str;
1430

                
1431
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1432
    g_return_if_fail(file_size_remaining >= 0);
1433

                
1434
    memset(&size_str, 0, sizeof(size_str));
1435
    nntpgrab_utils_calculate_file_size(file_size, size_str, sizeof(size_str) - 1);
1436

                
1437
    memset(&size_remaining_str, 0, sizeof(size_remaining_str));
1438
    nntpgrab_utils_calculate_file_size(file_size_remaining, size_remaining_str, sizeof(size_remaining_str) - 1);
1439

                
1440
    memset(&stamp_str, 0, sizeof(stamp_str));
1441
    generate_stamp(stamp, stamp_str, sizeof(stamp_str) - 1);
1442

                
1443
    if (status == TASK_STATE_SKIPPED) {
1444
        progress = 0;
1445
    } else {
1446
        progress = calculate_progress(file_size, file_size_remaining);
1447
    }
1448

                
1449
    switch(status) {
1450
        case TASK_STATE_WAITING_FOR_DOWNLOAD:
1451
        case TASK_STATE_DOWNLOADING:
1452
            state_str = NULL;
1453
            break;
1454

                
1455
        case TASK_STATE_WAITING_FOR_DECODE:
1456
            state_str = _("Waiting");
1457
            break;
1458

                
1459
        case TASK_STATE_DECODING:
1460
            state_str = _("Decoding");
1461
            break;
1462

                
1463
        case TASK_STATE_FINISHED_COMPLETE:
1464
            state_str = _("Done");
1465
            break;
1466

                
1467
        case TASK_STATE_FINISHED_INCOMPLETE:
1468
            state_str = _("Incomplete");
1469
            break;
1470

                
1471
        case TASK_STATE_FINISHED_NO_PARTS_AVAIL:
1472
            state_str = _("Not available");
1473
            break;
1474

                
1475
        case TASK_STATE_SKIPPED:
1476
            state_str = _("Skipped");
1477
            break;
1478

                
1479
        default:
1480
            state_str = NULL;
1481
            break;
1482
    };
1483

                
1484
    gtk_tree_store_append(store, &iter, &parent);
1485
    gtk_tree_store_set(store, &iter,    FIELD_SUBJECT, subject,
1486
                                        FIELD_SUBJECT_HASH, g_str_hash(subject),
1487
                                        FIELD_POSTER, poster,
1488
                                        FIELD_STAMP_STR, stamp_str,
1489
                                        FIELD_FILESIZE, file_size,
1490
                                        FIELD_FILESIZE_STR, size_str,
1491
                                        FIELD_FILESIZE_REMAINING, file_size_remaining,
1492
                                        FIELD_FILESIZE_REMAINING_STR, size_remaining_str,
1493
                                        FIELD_NUM_PARTS, num_parts,
1494
                                        FIELD_PARTS_DONE, num_parts_downloaded,
1495
                                        FIELD_PARTS_FAILED, num_parts_failed,
1496
                                        FIELD_PROGRESS, progress,
1497
                                        FIELD_STATE_STR, state_str,
1498
                                        FIELD_POSITION, position,
1499
                                        FIELD_REAL_FILENAME, filename, -1);
1500
}
1501

                
1502
static void
1503
foreach_group_func (const char *collection_name, const char *subject, const char *group, gpointer data)
1504
{
1505
    GtkTreeStore *store = GTK_TREE_STORE(data);
1506
    GtkTreeIter iter, parent;
1507
    char *old_groups;
1508
    char *new_groups;
1509

                
1510
    g_return_if_fail(find_iter_by_collection_name(GTK_TREE_MODEL(store), collection_name, &parent));
1511
    g_return_if_fail(find_iter_by_subject(GTK_TREE_MODEL(store), parent, subject, &iter));
1512

                
1513
    gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FIELD_GROUPS, &old_groups, -1);
1514
    if (old_groups && strlen(old_groups) > 0) {
1515
        new_groups = g_strdup_printf("%s, %s", old_groups, group);
1516
    } else {
1517
        new_groups = g_strdup(group);
1518
    }
1519

                
1520
    gtk_tree_store_set(store, &iter, FIELD_GROUPS, new_groups, -1);
1521

                
1522
    g_free(new_groups);
1523
    g_free(old_groups);
1524
}
1525

                
1526
static void
1527
on_tree_view_select (GtkTreeSelection *selection, gpointer user_data)
1528
{
1529
    GtkWidget *btnRemoveItemFromQueue = nntpgrab_gui_base_get_widget("btnRemoveItemFromQueue");
1530
    GtkWidget *btnQueueMoveToTop = nntpgrab_gui_base_get_widget("btnQueueMoveToTop");
1531
    GtkWidget *btnQueueMoveUp = nntpgrab_gui_base_get_widget("btnQueueMoveUp");
1532
    GtkWidget *btnQueueMoveDown = nntpgrab_gui_base_get_widget("btnQueueMoveDown");
1533
    GtkWidget *btnQueueMoveToBottom = nntpgrab_gui_base_get_widget("btnQueueMoveToBottom");
1534

                
1535
    if (gtk_tree_selection_count_selected_rows(selection) == 0) {
1536
        // No items selected, disable the remove button and the move buttons
1537
        gtk_widget_set_sensitive(btnRemoveItemFromQueue, FALSE);
1538
        gtk_widget_set_sensitive(btnQueueMoveToTop, FALSE);
1539
        gtk_widget_set_sensitive(btnQueueMoveUp, FALSE);
1540
        gtk_widget_set_sensitive(btnQueueMoveDown, FALSE);
1541
        gtk_widget_set_sensitive(btnQueueMoveToBottom, FALSE);
1542
        return;
1543
    }
1544

                
1545
    // Enable the remove button and the move buttons
1546
    gtk_widget_set_sensitive(btnRemoveItemFromQueue, TRUE);
1547
    gtk_widget_set_sensitive(btnQueueMoveToTop, TRUE);
1548
    gtk_widget_set_sensitive(btnQueueMoveUp, TRUE);
1549
    gtk_widget_set_sensitive(btnQueueMoveDown, TRUE);
1550
    gtk_widget_set_sensitive(btnQueueMoveToBottom, TRUE);
1551
}
1552

                
1553
G_MODULE_EXPORT void
1554
on_btnPauseQueue_clicked(GtkWidget *caller, gpointer data)
1555
{
1556
    NGSchedularState active_state = nntpgrab_glue_schedular_get_state(glue);
1557

                
1558
    if (status_flag == SCHEDULAR_STATE_STOPPED && active_state == SCHEDULAR_STATE_STOPPING) {
1559
        GtkWidget *windowMain;
1560

                
1561
        windowMain = nntpgrab_gui_base_get_widget("windowMain");
1562

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

                
1565
        g_signal_handlers_block_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1566

                
1567
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(caller), TRUE);
1568

                
1569
        g_signal_handlers_unblock_by_func (G_OBJECT (caller), G_CALLBACK (on_btnPauseQueue_clicked), data);
1570

                
1571
        return;
1572
    }
1573

                
1574
    if (status_flag == SCHEDULAR_STATE_RUNNING) {
1575
        status_flag = SCHEDULAR_STATE_STOPPED;
1576
        nntpgrab_glue_schedular_stop(glue, FALSE);
1577
    } else {
1578
        status_flag = SCHEDULAR_STATE_RUNNING;
1579
        nntpgrab_glue_schedular_start(glue);
1580
    }
1581
}
1582

                
1583
static gboolean
1584
on_key_press_event(GtkWidget *widget, GdkEventKey *key, gpointer data)
1585
{
1586
    if (key->keyval == GDK_Delete) {
1587
        GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1588
        if (gtk_tree_selection_count_selected_rows(selection) > 0) {
1589
            on_btnRemoveItemFromQueue_clicked(widget, data);
1590
            return TRUE;
1591
        }
1592
    }
1593

                
1594
    return FALSE;
1595
}
1596

                
1597
static void
1598
schedular_state_changed_cb(NntpgrabGlue *obj, NGSchedularState state, const char *reason)
1599
{
1600
    GtkWidget *btnPauseQueue;
1601

                
1602
    if (state == SCHEDULAR_STATE_STOPPING) {
1603
        status_flag = SCHEDULAR_STATE_STOPPED;
1604
    } else {
1605
        status_flag = state;
1606
    }
1607

                
1608
    if (state == SCHEDULAR_STATE_STOPPING && reason != NULL) {
1609
        char *msg;
1610
        GtkWidget *windowMain;
1611
#ifdef HAVE_LIBNOTIFY
1612
        NotifyNotification *notification;
1613
#endif
1614

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

                
1618
#ifdef HAVE_LIBNOTIFY
1619
#ifdef HAVE_LIBNOTIFY_0_7
1620
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL);
1621
#else
1622
        notification = notify_notification_new(_("Schedular has been paused"), msg, NULL, NULL);
1623
#endif
1624
        if (nntpgrab_gui_base_tray_icon_get_is_shown()) {
1625
#if GTK_CHECK_VERSION(2,10,0)
1626
#ifndef HAVE_LIBNOTIFY_0_7
1627
            notify_notification_attach_to_status_icon(notification, nntpgrab_gui_base_tray_get_status_icon());
1628
#endif
1629
#endif
1630
        }
1631
        notify_notification_show(notification, NULL);
1632
#endif
1633

                
1634
        nntpgrab_gui_base_dialog_show(windowMain, msg, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK);
1635
        g_free(msg);
1636
    }
1637

                
1638
    btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1639
    g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1640

                
1641
    if (state == SCHEDULAR_STATE_RUNNING) {
1642
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), FALSE);
1643
    } else {
1644
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1645
    }
1646

                
1647
    g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1648

                
1649
}
1650

                
1651
static void
1652
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)
1653
{
1654
    static int times_been_here = 10;
1655
    gboolean no_data_received = FALSE;
1656
    GtkTreeModel *model;
1657
    GtkWidget *treeDownloadQueue;
1658
    GtkTreeIter iter, parent;
1659
    guint64 total_estimated_time_remaining;
1660

                
1661
    if (bytes_received1 == 0 && bytes_received2 == 0 && bytes_received3 == 0 && bytes_received4 == 0 && bytes_received5 == 0 &&
1662
        bytes_received6 == 0 && bytes_received7 == 0 && bytes_received8 == 0 && bytes_received9 == 0 && bytes_received10 == 0) {
1663
            no_data_received = TRUE;
1664
    }
1665

                
1666
    // Only update on startup, on every 3 seconds, and when there has no data been received in the last 10 seconds
1667
    if (times_been_here < 3 && !no_data_received) {
1668
        times_been_here += 1;
1669
        return;
1670
    }
1671

                
1672
    if (no_data_received) {
1673
        times_been_here = 3;    // update the time remaining the next time we get here
1674
    } else {
1675
        times_been_here = 0;    // perform the next update in 10 seconds
1676
    }
1677

                
1678
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1679
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeDownloadQueue));
1680

                
1681
    if (!gtk_tree_model_get_iter_first(model, &parent)) {
1682
        // Nothing in the download queue
1683
        return;
1684
    }
1685

                
1686
    total_estimated_time_remaining = 0;
1687

                
1688
    do {
1689
        guint64 filesize;
1690
        int estimated_time_remaining;
1691
        char str_estimated_time_remaining[64];
1692
        char str_estimated_time_to_finish[64];
1693

                
1694
        gtk_tree_model_get(model, &parent, FIELD_FILESIZE_REMAINING, &filesize, -1);
1695

                
1696
        estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1697
                                                                                        bytes_received3, bytes_received4,
1698
                                                                                        bytes_received5, bytes_received6,
1699
                                                                                        bytes_received7, bytes_received8,
1700
                                                                                        bytes_received9, bytes_received10,
1701
                                                                                        filesize);
1702

                
1703
        if (estimated_time_remaining == -1) {
1704
            // Not enough data received to make an estimation
1705
            return;
1706
        }
1707

                
1708
        if (estimated_time_remaining == 0) {
1709
            // File is already downloaded
1710
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, "",
1711
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1712
        } else {
1713
            memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1714
            nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining + estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1715

                
1716
            memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1717
            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);
1718

                
1719
            gtk_tree_store_set(GTK_TREE_STORE(model), &parent,  FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1720
                                                                FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1721
        }
1722

                
1723
        // Walk through all the children
1724
        if (gtk_tree_model_iter_children(model, &iter, &parent)) {
1725
            do {
1726
                gtk_tree_model_get(model, &iter, FIELD_FILESIZE_REMAINING, &filesize, -1);
1727

                
1728
                if (filesize > 0) {
1729
                    estimated_time_remaining = nntpgrab_utils_calculate_estimated_time_remaining(   bytes_received1, bytes_received2,
1730
                                                                                                    bytes_received3, bytes_received4,
1731
                                                                                                    bytes_received5, bytes_received6,
1732
                                                                                                    bytes_received7, bytes_received8,
1733
                                                                                                    bytes_received9, bytes_received10,
1734
                                                                                                    filesize);
1735

                
1736
                    total_estimated_time_remaining += estimated_time_remaining;
1737

                
1738
                    memset(&str_estimated_time_remaining, 0, sizeof(str_estimated_time_remaining));
1739
                    nntpgrab_utils_get_readable_time_remaining(total_estimated_time_remaining, str_estimated_time_remaining, sizeof(str_estimated_time_remaining) - 1);
1740

                
1741
                    memset(&str_estimated_time_to_finish, 0, sizeof(str_estimated_time_to_finish));
1742
                    nntpgrab_utils_get_readable_finished_time(total_estimated_time_remaining, str_estimated_time_to_finish, sizeof(str_estimated_time_to_finish));
1743

                
1744
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, str_estimated_time_remaining,
1745
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, str_estimated_time_to_finish, -1);
1746
                } else {
1747
                    gtk_tree_store_set(GTK_TREE_STORE(model), &iter,    FIELD_ESTIMATED_TIME_REMAINING, "",
1748
                                                                        FIELD_ESTIMATED_TIME_TO_FINISH, "", -1);
1749
                }
1750
            } while (gtk_tree_model_iter_next(model, &iter));
1751
        }
1752
    } while (gtk_tree_model_iter_next(model, &parent));
1753
}
1754

                
1755
static void
1756
config_changed(ConfigGUI *config, gpointer data)
1757
{
1758
    ConfigGUIOpts opts = config_gui_get_opts();
1759

                
1760
    gtk_tree_view_column_set_visible(columns[FIELD_PROGRESS], opts.show_column_progress);
1761
    gtk_tree_view_column_set_visible(columns[FIELD_SUBJECT], opts.show_column_subject);
1762
    gtk_tree_view_column_set_visible(columns[FIELD_POSTER], opts.show_column_poster);
1763
    gtk_tree_view_column_set_visible(columns[FIELD_GROUPS], opts.show_column_newsgroups);
1764
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_STR], opts.show_column_filesize);
1765
    gtk_tree_view_column_set_visible(columns[FIELD_FILESIZE_REMAINING_STR], opts.show_column_filesize_remaining);
1766
    gtk_tree_view_column_set_visible(columns[FIELD_STAMP_STR], opts.show_column_stamp);
1767
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_REMAINING], opts.show_column_time_remaining);
1768
    gtk_tree_view_column_set_visible(columns[FIELD_ESTIMATED_TIME_TO_FINISH], opts.show_column_time_to_finish);
1769
}
1770

                
1771
void
1772
queue_initialize()
1773
{
1774
    GtkWidget *treeDownloadQueue;
1775
    GtkTreeStore *store;
1776
    GtkCellRenderer *cellRenderer;
1777
    GtkTreeSelection *selection;
1778

                
1779
    treeDownloadQueue = nntpgrab_gui_base_get_widget("treeDownloadQueue");
1780

                
1781
    cellRenderer = gtk_cell_renderer_progress_new();
1782
    columns[FIELD_PROGRESS] = gtk_tree_view_column_new_with_attributes(_("Progress"), cellRenderer, "value", FIELD_PROGRESS, "text", FIELD_STATE_STR, NULL);
1783
    gtk_tree_view_column_set_resizable(columns[FIELD_PROGRESS], TRUE);
1784
    gtk_tree_view_column_set_reorderable(columns[FIELD_PROGRESS], TRUE);
1785
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_PROGRESS], -1);
1786

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

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

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

                
1805
    cellRenderer = gtk_cell_renderer_text_new();
1806
    columns[FIELD_FILESIZE_STR] = gtk_tree_view_column_new_with_attributes(_("File size"), cellRenderer, "text", FIELD_FILESIZE_STR, NULL);
1807
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_STR], TRUE);
1808
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_STR], TRUE);
1809
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_STR], -1);
1810

                
1811
    cellRenderer = gtk_cell_renderer_text_new();
1812
    columns[FIELD_FILESIZE_REMAINING_STR] = gtk_tree_view_column_new_with_attributes(_("File size remaining"), cellRenderer, "text", FIELD_FILESIZE_REMAINING_STR, NULL);
1813
    gtk_tree_view_column_set_resizable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1814
    gtk_tree_view_column_set_reorderable(columns[FIELD_FILESIZE_REMAINING_STR], TRUE);
1815
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_FILESIZE_REMAINING_STR], -1);
1816

                
1817
    cellRenderer = gtk_cell_renderer_text_new();
1818
    columns[FIELD_STAMP_STR] = gtk_tree_view_column_new_with_attributes( _("Stamp"), cellRenderer, "text", FIELD_STAMP_STR, NULL);
1819
    gtk_tree_view_column_set_resizable(columns[FIELD_STAMP_STR], TRUE);
1820
    gtk_tree_view_column_set_reorderable(columns[FIELD_STAMP_STR], TRUE);
1821
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_STAMP_STR], -1);
1822

                
1823
    cellRenderer = gtk_cell_renderer_text_new();
1824
    columns[FIELD_ESTIMATED_TIME_REMAINING] = gtk_tree_view_column_new_with_attributes( _("Time remaining"), cellRenderer, "text", FIELD_ESTIMATED_TIME_REMAINING, NULL);
1825
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1826
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_REMAINING], TRUE);
1827
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_REMAINING], -1);
1828

                
1829
    cellRenderer = gtk_cell_renderer_text_new();
1830
    columns[FIELD_ESTIMATED_TIME_TO_FINISH] = gtk_tree_view_column_new_with_attributes( _("Time to finish"), cellRenderer, "text", FIELD_ESTIMATED_TIME_TO_FINISH, NULL);
1831
    gtk_tree_view_column_set_resizable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1832
    gtk_tree_view_column_set_reorderable(columns[FIELD_ESTIMATED_TIME_TO_FINISH], TRUE);
1833
    gtk_tree_view_insert_column(GTK_TREE_VIEW(treeDownloadQueue), columns[FIELD_ESTIMATED_TIME_TO_FINISH], -1);
1834

                
1835
    store = gtk_tree_store_new( LAST_FIELD,
1836
                                G_TYPE_STRING /* FIELD_COLLECTION_NAME */,
1837
                                G_TYPE_UINT   /* FIELD_COLLECTION_NAME_HASH */,
1838
                                G_TYPE_STRING /* FIELD_SUBJECT */,
1839
                                G_TYPE_UINT   /* FIELD_SUBJECT_HASH */,
1840
                                G_TYPE_STRING /* FIELD_POSTER */,
1841
                                G_TYPE_STRING /* FIELD_GROUPS */,
1842
                                G_TYPE_STRING /* FIELD_STAMP_STR */,
1843
                                G_TYPE_INT /* FIELD_PROGRESS */,
1844
                                G_TYPE_UINT64 /* FIELD_FILESIZE */,
1845
                                G_TYPE_STRING /* FIELD_FILESIZE_STR */,
1846
                                G_TYPE_UINT64 /* FIELD_FILESIZE_REMAINING */,
1847
                                G_TYPE_STRING /* FIELD_FILESIZE_REMAINING_STR */,
1848
                                G_TYPE_INT /* FIELD_NUM_PARTS */,
1849
                                G_TYPE_INT /* FIELD_PARTS_DONE */,
1850
                                G_TYPE_INT /* FIELD_PARTS_FAILED */,
1851
                                G_TYPE_STRING /* FIELD_STATE_STR */,
1852
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_REMAINING */,
1853
                                G_TYPE_STRING /* FIELD_ESTIMATED_TIME_TO_FINISH */,
1854
                                G_TYPE_INT /* FIELD_POSITION */,
1855
                                G_TYPE_STRING /* FIELD_REAL_FILENAME */);
1856

                
1857
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeDownloadQueue), GTK_TREE_MODEL(store));
1858

                
1859
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeDownloadQueue));
1860
    gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1861

                
1862
    g_signal_connect(selection, "changed", G_CALLBACK(on_tree_view_select), NULL);
1863
    on_tree_view_select(selection, NULL);
1864

                
1865
    nntpgrab_glue_schedular_foreach_task(glue, foreach_collection_func, foreach_file_func, foreach_group_func, store);
1866

                
1867
    nntpgrab_glue_signal_connect(glue, "collection_added", NG_CALLBACK(collection_added), NULL);
1868
    nntpgrab_glue_signal_connect(glue, "collection_removed", NG_CALLBACK(collection_removed), NULL);
1869
    nntpgrab_glue_signal_connect(glue, "collection_modified", NG_CALLBACK(collection_modified), NULL);
1870
    nntpgrab_glue_signal_connect(glue, "file_added", NG_CALLBACK(file_added), NULL);
1871
    nntpgrab_glue_signal_connect(glue, "file_removed", NG_CALLBACK(file_removed), NULL);
1872
    nntpgrab_glue_signal_connect(glue, "file_state_changed", NG_CALLBACK(file_state_changed), NULL);
1873
    nntpgrab_glue_signal_connect(glue, "file_download_state_update", NG_CALLBACK(file_download_state_update), NULL);
1874
    nntpgrab_glue_signal_connect(glue, "schedular_state_changed", NG_CALLBACK(schedular_state_changed_cb), NULL);
1875
    nntpgrab_glue_signal_connect(glue, "traffic_monitor_update", NG_CALLBACK(traffic_monitor_update_cb), NULL);
1876
    nntpgrab_glue_signal_connect(glue, "task_moved", NG_CALLBACK(task_moved_cb), NULL);
1877
    nntpgrab_glue_signal_connect(glue, "collection_moved", NG_CALLBACK(collection_moved_cb), NULL);
1878

                
1879
    g_signal_connect(config_gui_get_object(), "config_changed", G_CALLBACK(config_changed), NULL);
1880
    config_changed(NULL, NULL);
1881

                
1882
    g_signal_connect(treeDownloadQueue, "key-press-event", G_CALLBACK(on_key_press_event), NULL);
1883

                
1884
    if (nntpgrab_glue_get_is_standalone(glue)) {
1885
        nntpgrab_glue_schedular_start(glue);
1886
    }
1887

                
1888
    status_flag = nntpgrab_glue_schedular_get_state(glue);
1889
    if (status_flag == SCHEDULAR_STATE_STOPPING || status_flag == SCHEDULAR_STATE_STOPPED) {
1890
        GtkWidget *btnPauseQueue = nntpgrab_gui_base_get_widget("btnPauseQueue");
1891

                
1892
        g_signal_handlers_block_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1893
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(btnPauseQueue), TRUE);
1894
        g_signal_handlers_unblock_by_func (G_OBJECT (btnPauseQueue), G_CALLBACK (on_btnPauseQueue_clicked), NULL);
1895
    }
1896
}