2008-07-20 [ticho] 3.5.0cvs29
[claws.git] / src / news_gtk.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto & the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28
29 #include <gtk/gtk.h>
30
31 #include "utils.h"
32 #include "folder.h"
33 #include "folderview.h"
34 #include "menu.h"
35 #include "account.h"
36 #include "alertpanel.h"
37 #include "grouplistdialog.h"
38 #include "prefs_common.h"
39 #include "news_gtk.h"
40 #include "common/hooks.h"
41 #include "inc.h"
42 #include "news.h"
43 #include "statusbar.h"
44 #include "inputdialog.h"
45
46 static void subscribe_newsgroup_cb(FolderView *folderview, guint action, GtkWidget *widget);
47 static void unsubscribe_newsgroup_cb(FolderView *folderview, guint action, GtkWidget *widget);
48 static void rename_newsgroup_cb(FolderView *folderview, guint action, GtkWidget *widget);
49 static void update_tree_cb(FolderView *folderview, guint action, GtkWidget *widget);
50 static void download_cb(FolderView *folderview, guint action, GtkWidget *widget);
51 static void sync_cb(FolderView *folderview, guint action, GtkWidget *widget);
52
53 static GtkItemFactoryEntry news_popup_entries[] =
54 {
55         {N_("/_Subscribe to newsgroup..."),     NULL, subscribe_newsgroup_cb,    0, NULL},
56         {N_("/_Unsubscribe newsgroup"),         NULL, unsubscribe_newsgroup_cb,  0, NULL},
57         {"/---",                                NULL, NULL,                      0, "<Separator>"},
58         {N_("/Synchronise"),                    NULL, sync_cb,          0, NULL},
59         {N_("/Down_load messages"),             NULL, download_cb,               0, NULL},
60         {N_("/_Rename folder..."),              NULL, rename_newsgroup_cb,       0, NULL},
61         {"/---",                                NULL, NULL,                      0, "<Separator>"},
62         {N_("/_Check for new messages"),        NULL, update_tree_cb,            0, NULL},
63         {"/---",                                NULL, NULL,                      0, "<Separator>"},
64 };
65
66 static void set_sensitivity(GtkItemFactory *factory, FolderItem *item);
67
68 static FolderViewPopup news_popup =
69 {
70         "news",
71         "<NewsFolder>",
72         NULL,
73         set_sensitivity
74 };
75
76 void news_gtk_init(void)
77 {
78         guint i, n_entries;
79
80         n_entries = sizeof(news_popup_entries) /
81                 sizeof(news_popup_entries[0]);
82         for (i = 0; i < n_entries; i++)
83                 news_popup.entries = g_slist_append(news_popup.entries, &news_popup_entries[i]);
84
85         folderview_register_popup(&news_popup);
86 }
87
88 static void set_sensitivity(GtkItemFactory *factory, FolderItem *item)
89 {
90         MainWindow *mainwin = mainwindow_get_mainwindow();
91         
92 #define SET_SENS(name, sens) \
93         menu_set_sensitive(factory, name, sens)
94
95         SET_SENS("/Subscribe to newsgroup...", 
96                         folder_item_parent(item) == NULL 
97                         && mainwin->lock_count == 0
98                         && news_folder_locked(item->folder) == 0);
99         SET_SENS("/Unsubscribe newsgroup",     
100                         folder_item_parent(item) != NULL 
101                         && mainwin->lock_count == 0
102                         && news_folder_locked(item->folder) == 0);
103         SET_SENS("/Check for new messages",    
104                         folder_item_parent(item) == NULL 
105                         && mainwin->lock_count == 0
106                         && news_folder_locked(item->folder) == 0);
107         SET_SENS("/Synchronise",    
108                         item ? (folder_item_parent(item) != NULL
109                         && folder_want_synchronise(item->folder))
110                         : FALSE);
111         SET_SENS("/Download messages",
112                         item ? (folder_item_parent(item) != NULL
113                         && !item->no_select)
114                         : FALSE);
115         SET_SENS("/Rename folder...",
116                         folder_item_parent(item) != NULL 
117                         && mainwin->lock_count == 0
118                         && news_folder_locked(item->folder) == 0);
119 #undef SET_SENS
120 }
121
122 static void subscribe_newsgroup_cb(FolderView *folderview, guint action, GtkWidget *widget)
123 {
124         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
125         GtkCTreeNode *servernode, *node;
126         Folder *folder;
127         FolderItem *item;
128         FolderItem *rootitem;
129         FolderItem *newitem;
130         GSList *new_subscr;
131         GSList *cur;
132         GNode *gnode;
133         MainWindow *mainwin = mainwindow_get_mainwindow();
134         
135         if (!folderview->selected) return;
136
137         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
138         g_return_if_fail(item != NULL);
139
140         if (mainwin->lock_count || news_folder_locked(item->folder))
141                 return;
142
143         folder = item->folder;
144         g_return_if_fail(folder != NULL);
145         g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS);
146         g_return_if_fail(folder->account != NULL);
147
148         if (GTK_CTREE_ROW(folderview->selected)->parent != NULL)
149                 servernode = GTK_CTREE_ROW(folderview->selected)->parent;
150         else
151                 servernode = folderview->selected;
152
153         rootitem = gtk_ctree_node_get_row_data(ctree, servernode);
154
155         new_subscr = grouplist_dialog(folder);
156
157         /* remove unsubscribed newsgroups */
158         for (gnode = folder->node->children; gnode != NULL; ) {
159                 GNode *next = gnode->next;
160
161                 item = FOLDER_ITEM(gnode->data);
162                 if (g_slist_find_custom(new_subscr, item->path,
163                                         (GCompareFunc)g_ascii_strcasecmp) != NULL) {
164                         gnode = next;
165                         continue;
166                 }
167
168                 node = gtk_ctree_find_by_row_data(ctree, servernode, item);
169                 if (!node) {
170                         gnode = next;
171                         continue;
172                 }
173
174                 if (folderview->opened == node) {
175                         summary_clear_all(folderview->summaryview);
176                         folderview->opened = NULL;
177                 }
178
179                 gtk_ctree_remove_node(ctree, node);
180                 folder_item_remove(item);
181
182                 gnode = next;
183         }
184
185         gtk_clist_freeze(GTK_CLIST(ctree));
186
187         /* add subscribed newsgroups */
188         for (cur = new_subscr; cur != NULL; cur = cur->next) {
189                 gchar *name = (gchar *)cur->data;
190                 FolderUpdateData hookdata;
191
192                 if (folder_find_child_item_by_name(rootitem, name) != NULL)
193                         continue;
194
195                 newitem = folder_item_new(folder, name, name);
196                 folder_item_append(rootitem, newitem);
197
198                 hookdata.folder = newitem->folder;
199                 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
200                 hookdata.item = newitem;
201                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
202         }
203
204         gtk_clist_thaw(GTK_CLIST(ctree));
205
206         slist_free_strings(new_subscr);
207         g_slist_free(new_subscr);
208
209         folder_write_list();
210 }
211
212 static void unsubscribe_newsgroup_cb(FolderView *folderview, guint action,
213                                      GtkWidget *widget)
214 {
215         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
216         FolderItem *item;
217         gchar *name;
218         gchar *message;
219         gchar *old_id;
220         AlertValue avalue;
221         MainWindow *mainwin = mainwindow_get_mainwindow();
222         
223         if (!folderview->selected) return;
224
225         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
226         g_return_if_fail(item != NULL);
227
228         if (mainwin->lock_count || news_folder_locked(item->folder))
229                 return;
230
231         g_return_if_fail(item->folder != NULL);
232         g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
233         g_return_if_fail(item->folder->account != NULL);
234
235         old_id = folder_item_get_identifier(item);
236
237         name = trim_string(item->path, 32);
238         message = g_strdup_printf(_("Really unsubscribe newsgroup '%s'?"), name);
239         avalue = alertpanel_full(_("Unsubscribe newsgroup"), message,
240                                  GTK_STOCK_CANCEL, _("_Unsubscribe"), NULL, FALSE,
241                                  NULL, ALERT_WARNING, G_ALERTDEFAULT);
242         g_free(message);
243         g_free(name);
244         if (avalue != G_ALERTALTERNATE) return;
245
246         if (folderview->opened == folderview->selected) {
247                 summary_clear_all(folderview->summaryview);
248                 folderview->opened = NULL;
249         }
250
251         if(item->folder->klass->remove_folder(item->folder, item) < 0) {
252                 folder_item_scan(item);
253                 alertpanel_error(_("Can't remove the folder '%s'."), name);
254                 g_free(old_id);
255                 return;
256         }
257         
258         folder_write_list();
259         
260         prefs_filtering_delete_path(old_id);
261         g_free(old_id);
262 }
263
264 static FolderItem *find_child_by_name(FolderItem *item, const gchar *name)
265 {
266         GNode *node;
267         FolderItem *child;
268
269         for (node = item->node->children; node != NULL; node = node->next) {
270                 child = FOLDER_ITEM(node->data);
271                 if (strcmp2(child->name, name) == 0) {
272                         return child;
273                 }
274         }
275
276         return NULL;
277 }
278
279 static void rename_newsgroup_cb(FolderView *folderview, guint action,
280                                 GtkWidget *widget)
281 {
282         FolderItem *item;
283         gchar *new_folder;
284         gchar *name;
285         gchar *message;
286
287         item = folderview_get_selected_item(folderview);
288         g_return_if_fail(item != NULL);
289         g_return_if_fail(item->path != NULL);
290         g_return_if_fail(item->folder != NULL);
291
292         name = trim_string(item->name, 32);
293         message = g_strdup_printf(_("Input new name for '%s':"), name);
294         new_folder = input_dialog(_("Rename newsgroup folder"), message, item->name);
295         g_free(message);
296         g_free(name);
297
298         if (!new_folder) return;
299         AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
300
301         if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
302                 alertpanel_error(_("'%c' can't be included in folder name."),
303                                  G_DIR_SEPARATOR);
304                 return;
305         }
306
307         if (find_child_by_name(folder_item_parent(item), new_folder)) {
308                 name = trim_string(new_folder, 32);
309                 alertpanel_error(_("The folder '%s' already exists."), name);
310                 g_free(name);
311                 return;
312         }
313
314         if (folder_item_rename(item, new_folder) < 0) {
315                 alertpanel_error(_("The folder could not be renamed.\n"
316                                    "The new folder name is not allowed."));
317                 return;
318         }
319
320         folder_write_list();
321 }
322
323 static void update_tree_cb(FolderView *folderview, guint action,
324                            GtkWidget *widget)
325 {
326         FolderItem *item;
327         MainWindow *mainwin = mainwindow_get_mainwindow();
328         
329         item = folderview_get_selected_item(folderview);
330         g_return_if_fail(item != NULL);
331
332         if (mainwin->lock_count || news_folder_locked(item->folder))
333                 return;
334
335         summary_show(folderview->summaryview, NULL);
336
337         g_return_if_fail(item->folder != NULL);
338
339         folderview_check_new(item->folder);
340 }
341
342 static void sync_cb(FolderView *folderview, guint action,
343                            GtkWidget *widget)
344 {
345         FolderItem *item;
346
347         item = folderview_get_selected_item(folderview);
348         g_return_if_fail(item != NULL);
349         folder_synchronise(item->folder);
350 }
351
352 void news_gtk_synchronise(FolderItem *item, gint days)
353 {
354         MainWindow *mainwin = mainwindow_get_mainwindow();
355         FolderView *folderview = mainwin->folderview;
356         GSList *mlist;
357         GSList *cur;
358         gint num = 0;
359         gint total = 0;
360         time_t t = time(NULL);
361
362         g_return_if_fail(item != NULL);
363         g_return_if_fail(item->folder != NULL);
364
365         if (mainwin->lock_count || news_folder_locked(item->folder))
366                 return;
367
368         total = item->total_msgs;
369
370         main_window_cursor_wait(mainwin);
371         inc_lock();
372         main_window_lock(mainwin);
373         gtk_widget_set_sensitive(folderview->ctree, FALSE);
374         main_window_progress_on(mainwin);
375         GTK_EVENTS_FLUSH();
376
377         mlist = folder_item_get_msg_list(item);
378         for (cur = mlist; cur != NULL; cur = cur->next) {
379                 MsgInfo *msginfo = (MsgInfo *)cur->data;
380                 gint age = (t - msginfo->date_t) / (60*60*24);
381                 if (days == 0 || age <= days)
382                         folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum, TRUE, TRUE);
383                 statusbar_progress_all(num++,total, 100);
384                 if (num % 100 == 0)
385                         GTK_EVENTS_FLUSH();
386         }
387
388         statusbar_progress_all(0,0,0);
389         procmsg_msg_list_free(mlist);
390         folder_set_ui_func(item->folder, NULL, NULL);
391         main_window_progress_off(mainwin);
392         gtk_widget_set_sensitive(folderview->ctree, TRUE);
393         main_window_unlock(mainwin);
394         inc_unlock();
395         main_window_cursor_normal(mainwin);
396 }
397
398 static void download_cb(FolderView *folderview, guint action,
399                         GtkWidget *widget)
400 {
401         GtkCTree *ctree = GTK_CTREE(folderview->ctree);
402         FolderItem *item;
403
404         if (!folderview->selected) return;
405
406         item = gtk_ctree_node_get_row_data(ctree, folderview->selected);
407         news_gtk_synchronise(item, 0);
408 }