1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
---|
2 | /* subscribe-dialog.c: Subscribe dialog */ |
---|
3 | /* |
---|
4 | * Authors: Chris Toshok <toshok@ximian.com> |
---|
5 | * Peter Williams <peterw@ximian.com> |
---|
6 | * |
---|
7 | * Copyright 2000 Ximian, Inc. (www.ximian.com) |
---|
8 | * |
---|
9 | * This program is free software; you can redistribute it and/or |
---|
10 | * modify it under the terms of version 2 of the GNU General Public |
---|
11 | * License as published by the Free Software Foundation. |
---|
12 | * |
---|
13 | * This program is distributed in the hope that it will be useful, |
---|
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
16 | * General Public License for more details. |
---|
17 | * |
---|
18 | * You should have received a copy of the GNU General Public |
---|
19 | * License along with this program; if not, write to the |
---|
20 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
21 | * Boston, MA 02111-1307, USA. |
---|
22 | * |
---|
23 | */ |
---|
24 | |
---|
25 | #ifdef HAVE_CONFIG_H |
---|
26 | #include <config.h> |
---|
27 | #endif |
---|
28 | |
---|
29 | #include <gal/util/e-util.h> |
---|
30 | #include <gal/widgets/e-unicode.h> |
---|
31 | |
---|
32 | #include <gal/e-table/e-cell-toggle.h> |
---|
33 | #include <gal/e-table/e-cell-text.h> |
---|
34 | #include <gal/e-table/e-cell-tree.h> |
---|
35 | |
---|
36 | #include <gal/e-table/e-tree-scrolled.h> |
---|
37 | #include <gal/e-table/e-tree-memory-callbacks.h> |
---|
38 | #include <gal/e-table/e-tree.h> |
---|
39 | |
---|
40 | #include <pthread.h> |
---|
41 | |
---|
42 | #include "evolution-shell-component-utils.h" |
---|
43 | #include "mail.h" |
---|
44 | #include "mail-tools.h" |
---|
45 | #include "mail-ops.h" |
---|
46 | #include "mail-mt.h" |
---|
47 | #include "mail-folder-cache.h" |
---|
48 | #include "camel/camel-exception.h" |
---|
49 | #include "camel/camel-store.h" |
---|
50 | #include "camel/camel-session.h" |
---|
51 | #include "subscribe-dialog.h" |
---|
52 | |
---|
53 | #include "art/empty.xpm" |
---|
54 | #include "art/mark.xpm" |
---|
55 | |
---|
56 | #define d(x) |
---|
57 | |
---|
58 | /* Things to test. |
---|
59 | * - Feature |
---|
60 | * + How to check that it works. |
---|
61 | * |
---|
62 | * - Proper stores displayed |
---|
63 | * + Skip stores that don't support subscriptions |
---|
64 | * + Skip disabled stores |
---|
65 | * - Changing subscription status |
---|
66 | * + Select single folder, double-click row -> toggled |
---|
67 | * + Select multiple folders, press subscribe -> all selected folders end up subscribed |
---|
68 | * - (un)Subscribing from/to already (un)subscribed folder |
---|
69 | * + Check that no IMAP command is sent |
---|
70 | * - Switching views between stores |
---|
71 | * + Proper tree shown |
---|
72 | * - No crashes when buttons are pressed with "No store" screen |
---|
73 | * + obvious |
---|
74 | * - Restoring filter settings when view switched |
---|
75 | * + Enter search, change view, change back -> filter checked and search entry set |
---|
76 | * + Clear search, change view, change back -> "all" checked |
---|
77 | * - Cancelling in middle of get_store |
---|
78 | * + Enter invalid hostname, open dialog, click Close |
---|
79 | * - Cancelling in middle if listing |
---|
80 | * + Open large directory, click Close |
---|
81 | * - Cancelling in middle of subscription op |
---|
82 | * + How to test? |
---|
83 | * - Test with both IMAP and NNTP |
---|
84 | * + obvious |
---|
85 | * - Verify that refresh view works |
---|
86 | * + obvious |
---|
87 | * - No unnecessary tree rebuilds |
---|
88 | * + Show All folders, change filter with empty search -> no tree rebuild |
---|
89 | * + Converse |
---|
90 | * - No out of date tree |
---|
91 | * + Show All Folders, change to filter with a search -> tree rebuild |
---|
92 | * - Tree construction logic (mostly IMAP-specific terminology) |
---|
93 | * + Tree is created progressively |
---|
94 | * + No wasted LIST responses |
---|
95 | * + No extraneous LIST commands |
---|
96 | * + Specifying "folder names begin with" works |
---|
97 | * + Always show folders below IMAP namespace (no escaping the namespace) |
---|
98 | * + Don't allow subscription to NoSelect folders |
---|
99 | * + IMAP accounts always show INBOX |
---|
100 | * - Shell interactions |
---|
101 | * + Folders are properly created / delete from folder tree when subscribed / unsubscribed |
---|
102 | * + Folders with spaces in names / 8bit chars |
---|
103 | * + Toplevel as well as subfolders |
---|
104 | * + Mail Folder Cache doesn't complain |
---|
105 | * - No ETable wackiness |
---|
106 | * + Verify columns cannot be DnD'd |
---|
107 | * + Alphabetical order always |
---|
108 | * - UI cleanliness |
---|
109 | * + Keybindings work |
---|
110 | * + Some widget has focus by default |
---|
111 | * + Escape / enter work |
---|
112 | * + Close button works |
---|
113 | */ |
---|
114 | |
---|
115 | /* FIXME: we should disable/enable the subscribe/unsubscribe buttons as |
---|
116 | * appropriate when only a single message is selected. We need a |
---|
117 | * mechanism to learn when the selected folder's subscription status |
---|
118 | * changes, so when the user double-clicks it (eg) the buttons can |
---|
119 | * (de)sensitize appropriately. See Ximian bug #7673. |
---|
120 | */ |
---|
121 | |
---|
122 | /*#define NEED_TOGGLE_SELECTION*/ |
---|
123 | |
---|
124 | typedef struct _FolderETree FolderETree; |
---|
125 | typedef struct _FolderETreeClass FolderETreeClass; |
---|
126 | |
---|
127 | struct _FolderETree { |
---|
128 | ETreeMemory parent; |
---|
129 | ETreePath root; |
---|
130 | |
---|
131 | GHashTable *scan_ops; |
---|
132 | GHashTable *subscribe_ops; |
---|
133 | |
---|
134 | CamelStore *store; |
---|
135 | EvolutionStorage *e_storage; |
---|
136 | char *service_name; |
---|
137 | char *search; |
---|
138 | }; |
---|
139 | |
---|
140 | struct _FolderETreeClass { |
---|
141 | ETreeMemoryClass parent; |
---|
142 | }; |
---|
143 | |
---|
144 | static GtkObjectClass *folder_etree_parent_class = NULL; |
---|
145 | |
---|
146 | typedef struct _FolderETreeExtras FolderETreeExtras; |
---|
147 | typedef struct _FolderETreeExtrasClass FolderETreeExtrasClass; |
---|
148 | |
---|
149 | enum { |
---|
150 | FOLDER_COL_SUBSCRIBED, |
---|
151 | FOLDER_COL_NAME, |
---|
152 | FOLDER_COL_LAST |
---|
153 | }; |
---|
154 | |
---|
155 | struct _FolderETreeExtras { |
---|
156 | ETableExtras parent; |
---|
157 | GdkPixbuf *toggles[2]; |
---|
158 | }; |
---|
159 | |
---|
160 | struct _FolderETreeExtrasClass { |
---|
161 | ETableExtrasClass parent; |
---|
162 | }; |
---|
163 | |
---|
164 | static GtkObjectClass *ftree_extras_parent_class = NULL; |
---|
165 | |
---|
166 | /* util */ |
---|
167 | |
---|
168 | static void |
---|
169 | recursive_add_folder (EvolutionStorage *storage, const char *path, const char *name, const char *url) |
---|
170 | { |
---|
171 | char *parent, *pname, *p; |
---|
172 | |
---|
173 | p = strrchr (path, '/'); |
---|
174 | if (p && p != path) { |
---|
175 | parent = g_strndup (path, p - path); |
---|
176 | if (!evolution_storage_folder_exists (storage, parent)) { |
---|
177 | p = strrchr (parent, '/'); |
---|
178 | if (p) |
---|
179 | pname = g_strdup (p + 1); |
---|
180 | else |
---|
181 | pname = g_strdup (""); |
---|
182 | recursive_add_folder (storage, parent, pname, ""); |
---|
183 | g_free (pname); |
---|
184 | } |
---|
185 | g_free (parent); |
---|
186 | } |
---|
187 | |
---|
188 | evolution_storage_new_folder (storage, path, name, "mail", url, name, FALSE); |
---|
189 | } |
---|
190 | |
---|
191 | /* ** Get one level of folderinfo ****************************************** */ |
---|
192 | |
---|
193 | typedef void (*SubscribeShortFolderinfoFunc) (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data); |
---|
194 | |
---|
195 | int subscribe_get_short_folderinfo (FolderETree *ftree, const char *prefix, |
---|
196 | SubscribeShortFolderinfoFunc func, gpointer user_data); |
---|
197 | |
---|
198 | struct _get_short_folderinfo_msg { |
---|
199 | struct _mail_msg msg; |
---|
200 | |
---|
201 | char *prefix; |
---|
202 | |
---|
203 | FolderETree *ftree; |
---|
204 | CamelFolderInfo *info; |
---|
205 | |
---|
206 | SubscribeShortFolderinfoFunc func; |
---|
207 | gpointer user_data; |
---|
208 | }; |
---|
209 | |
---|
210 | static char * |
---|
211 | get_short_folderinfo_desc (struct _mail_msg *mm, int done) |
---|
212 | { |
---|
213 | struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm; |
---|
214 | char *ret, *name; |
---|
215 | |
---|
216 | name = camel_service_get_name (CAMEL_SERVICE (m->ftree->store), TRUE); |
---|
217 | |
---|
218 | if (m->prefix) |
---|
219 | ret = g_strdup_printf (_("Scanning folders under %s on \"%s\""), m->prefix, name); |
---|
220 | else |
---|
221 | ret = g_strdup_printf (_("Scanning root-level folders on \"%s\""), name); |
---|
222 | |
---|
223 | g_free (name); |
---|
224 | return ret; |
---|
225 | } |
---|
226 | |
---|
227 | static void |
---|
228 | get_short_folderinfo_get (struct _mail_msg *mm) |
---|
229 | { |
---|
230 | struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm; |
---|
231 | |
---|
232 | m->info = camel_store_get_folder_info (m->ftree->store, m->prefix, CAMEL_STORE_FOLDER_INFO_FAST, &mm->ex); |
---|
233 | } |
---|
234 | |
---|
235 | static void |
---|
236 | get_short_folderinfo_got (struct _mail_msg *mm) |
---|
237 | { |
---|
238 | struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm; |
---|
239 | |
---|
240 | if (camel_exception_is_set (&mm->ex)) |
---|
241 | g_warning ("Error getting folder info from store at %s: %s", |
---|
242 | camel_service_get_url (CAMEL_SERVICE (m->ftree->store)), |
---|
243 | camel_exception_get_description (&mm->ex)); |
---|
244 | |
---|
245 | /* 'done' is probably guaranteed to fail, but... */ |
---|
246 | |
---|
247 | if (m->func) |
---|
248 | m->func (m->ftree->store, m->prefix, m->info, m->user_data); |
---|
249 | } |
---|
250 | |
---|
251 | static void |
---|
252 | get_short_folderinfo_free (struct _mail_msg *mm) |
---|
253 | { |
---|
254 | struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm; |
---|
255 | |
---|
256 | camel_store_free_folder_info (m->ftree->store, m->info); |
---|
257 | gtk_object_unref (GTK_OBJECT (m->ftree)); |
---|
258 | |
---|
259 | g_free (m->prefix); /* may be NULL but that's ok */ |
---|
260 | } |
---|
261 | |
---|
262 | static struct _mail_msg_op get_short_folderinfo_op = { |
---|
263 | get_short_folderinfo_desc, |
---|
264 | get_short_folderinfo_get, |
---|
265 | get_short_folderinfo_got, |
---|
266 | get_short_folderinfo_free, |
---|
267 | }; |
---|
268 | |
---|
269 | int |
---|
270 | subscribe_get_short_folderinfo (FolderETree *ftree, |
---|
271 | const char *prefix, |
---|
272 | SubscribeShortFolderinfoFunc func, |
---|
273 | gpointer user_data) |
---|
274 | { |
---|
275 | struct _get_short_folderinfo_msg *m; |
---|
276 | int id; |
---|
277 | |
---|
278 | m = mail_msg_new (&get_short_folderinfo_op, NULL, sizeof(*m)); |
---|
279 | |
---|
280 | m->ftree = ftree; |
---|
281 | gtk_object_ref (GTK_OBJECT (ftree)); |
---|
282 | |
---|
283 | if (prefix) |
---|
284 | m->prefix = g_strdup (prefix); |
---|
285 | else |
---|
286 | m->prefix = NULL; |
---|
287 | |
---|
288 | m->func = func; |
---|
289 | m->user_data = user_data; |
---|
290 | |
---|
291 | id = m->msg.seq; |
---|
292 | e_thread_put (mail_thread_queued, (EMsg *)m); |
---|
293 | return id; |
---|
294 | } |
---|
295 | |
---|
296 | /* ** Subscribe folder operation **************************************** */ |
---|
297 | |
---|
298 | typedef void (*SubscribeFolderCallback) (const char *, const char *, gboolean, gboolean, gpointer); |
---|
299 | |
---|
300 | struct _subscribe_msg { |
---|
301 | struct _mail_msg msg; |
---|
302 | |
---|
303 | CamelStore *store; |
---|
304 | gboolean subscribe; |
---|
305 | char *full_name; |
---|
306 | char *name; |
---|
307 | |
---|
308 | SubscribeFolderCallback cb; |
---|
309 | gpointer cb_data; |
---|
310 | }; |
---|
311 | |
---|
312 | static char * |
---|
313 | subscribe_folder_desc (struct _mail_msg *mm, int done) |
---|
314 | { |
---|
315 | struct _subscribe_msg *m = (struct _subscribe_msg *) mm; |
---|
316 | |
---|
317 | if (m->subscribe) |
---|
318 | return g_strdup_printf (_("Subscribing to folder \"%s\""), m->name); |
---|
319 | else |
---|
320 | return g_strdup_printf (_("Unsubscribing to folder \"%s\""), m->name); |
---|
321 | } |
---|
322 | |
---|
323 | static void |
---|
324 | subscribe_folder_subscribe (struct _mail_msg *mm) |
---|
325 | { |
---|
326 | struct _subscribe_msg *m = (struct _subscribe_msg *) mm; |
---|
327 | |
---|
328 | if (m->subscribe) |
---|
329 | camel_store_subscribe_folder (m->store, m->full_name, &mm->ex); |
---|
330 | else |
---|
331 | camel_store_unsubscribe_folder (m->store, m->full_name, &mm->ex); |
---|
332 | } |
---|
333 | |
---|
334 | static void |
---|
335 | subscribe_folder_subscribed (struct _mail_msg *mm) |
---|
336 | { |
---|
337 | struct _subscribe_msg *m = (struct _subscribe_msg *) mm; |
---|
338 | |
---|
339 | if (m->cb) |
---|
340 | (m->cb) (m->full_name, m->name, m->subscribe, |
---|
341 | !camel_exception_is_set (&mm->ex), m->cb_data); |
---|
342 | } |
---|
343 | |
---|
344 | static void |
---|
345 | subscribe_folder_free (struct _mail_msg *mm) |
---|
346 | { |
---|
347 | struct _subscribe_msg *m = (struct _subscribe_msg *) mm; |
---|
348 | |
---|
349 | g_free (m->name); |
---|
350 | g_free (m->full_name); |
---|
351 | |
---|
352 | camel_object_unref (CAMEL_OBJECT (m->store)); |
---|
353 | } |
---|
354 | |
---|
355 | static struct _mail_msg_op subscribe_folder_op = { |
---|
356 | subscribe_folder_desc, |
---|
357 | subscribe_folder_subscribe, |
---|
358 | subscribe_folder_subscribed, |
---|
359 | subscribe_folder_free, |
---|
360 | }; |
---|
361 | |
---|
362 | static int |
---|
363 | subscribe_do_subscribe_folder (CamelStore *store, const char *full_name, const char *name, |
---|
364 | gboolean subscribe, SubscribeFolderCallback cb, gpointer cb_data) |
---|
365 | { |
---|
366 | struct _subscribe_msg *m; |
---|
367 | int id; |
---|
368 | |
---|
369 | g_return_val_if_fail (CAMEL_IS_STORE (store), 0); |
---|
370 | g_return_val_if_fail (full_name, 0); |
---|
371 | |
---|
372 | m = mail_msg_new (&subscribe_folder_op, NULL, sizeof(*m)); |
---|
373 | m->store = store; |
---|
374 | m->subscribe = subscribe; |
---|
375 | m->name = g_strdup (name); |
---|
376 | m->full_name = g_strdup (full_name); |
---|
377 | m->cb = cb; |
---|
378 | m->cb_data = cb_data; |
---|
379 | |
---|
380 | camel_object_ref (CAMEL_OBJECT (store)); |
---|
381 | |
---|
382 | id = m->msg.seq; |
---|
383 | e_thread_put (mail_thread_queued, (EMsg *)m); |
---|
384 | return id; |
---|
385 | } |
---|
386 | |
---|
387 | /* ** FolderETree Extras *************************************************** */ |
---|
388 | |
---|
389 | static void |
---|
390 | fete_destroy (GtkObject *object) |
---|
391 | { |
---|
392 | FolderETreeExtras *extras = (FolderETreeExtras *) object; |
---|
393 | |
---|
394 | gdk_pixbuf_unref (extras->toggles[0]); |
---|
395 | gdk_pixbuf_unref (extras->toggles[1]); |
---|
396 | |
---|
397 | ftree_extras_parent_class->destroy (object); |
---|
398 | } |
---|
399 | |
---|
400 | static void |
---|
401 | fete_class_init (GtkObjectClass *object_class) |
---|
402 | { |
---|
403 | object_class->destroy = fete_destroy; |
---|
404 | |
---|
405 | ftree_extras_parent_class = gtk_type_class (E_TABLE_EXTRAS_TYPE); |
---|
406 | } |
---|
407 | |
---|
408 | static void |
---|
409 | fete_init (GtkObject *object) |
---|
410 | { |
---|
411 | FolderETreeExtras *extras = (FolderETreeExtras *) object; |
---|
412 | ECell *cell; |
---|
413 | ECell *text_cell; |
---|
414 | |
---|
415 | /* text column */ |
---|
416 | |
---|
417 | cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); |
---|
418 | text_cell = cell; |
---|
419 | gtk_object_set (GTK_OBJECT (cell), |
---|
420 | "bold_column", FOLDER_COL_SUBSCRIBED, |
---|
421 | NULL); |
---|
422 | e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_text", cell); |
---|
423 | |
---|
424 | /* toggle column */ |
---|
425 | |
---|
426 | extras->toggles[0] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm); |
---|
427 | extras->toggles[1] = gdk_pixbuf_new_from_xpm_data ((const char **)mark_xpm); |
---|
428 | cell = e_cell_toggle_new (0, 2, extras->toggles); |
---|
429 | e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_toggle", cell); |
---|
430 | |
---|
431 | /* tree cell */ |
---|
432 | |
---|
433 | cell = e_cell_tree_new (NULL, NULL, TRUE, text_cell); |
---|
434 | e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_tree", cell); |
---|
435 | |
---|
436 | /* misc */ |
---|
437 | |
---|
438 | e_table_extras_add_pixbuf (E_TABLE_EXTRAS (extras), "subscribed-image", extras->toggles[1]); |
---|
439 | } |
---|
440 | |
---|
441 | /* naughty! */ |
---|
442 | static |
---|
443 | E_MAKE_TYPE (fete, "FolderETreeExtras", FolderETreeExtras, fete_class_init, fete_init, E_TABLE_EXTRAS_TYPE); |
---|
444 | |
---|
445 | /* ** Global Extras ******************************************************** */ |
---|
446 | |
---|
447 | static FolderETreeExtras *global_extras = NULL; |
---|
448 | |
---|
449 | static void |
---|
450 | global_extras_destroyed (GtkObject *obj, gpointer user_data) |
---|
451 | { |
---|
452 | global_extras = NULL; |
---|
453 | } |
---|
454 | |
---|
455 | static ETableExtras * |
---|
456 | subscribe_get_global_extras (void) |
---|
457 | { |
---|
458 | if (global_extras == NULL) { |
---|
459 | global_extras = gtk_type_new (fete_get_type()); |
---|
460 | gtk_object_ref (GTK_OBJECT (global_extras)); |
---|
461 | gtk_object_sink (GTK_OBJECT (global_extras)); |
---|
462 | gtk_signal_connect (GTK_OBJECT (global_extras), "destroy", |
---|
463 | global_extras_destroyed, NULL); |
---|
464 | } |
---|
465 | |
---|
466 | gtk_object_ref (GTK_OBJECT (global_extras)); |
---|
467 | return E_TABLE_EXTRAS (global_extras); |
---|
468 | } |
---|
469 | |
---|
470 | /* ** Folder Tree Node ***************************************************** */ |
---|
471 | |
---|
472 | typedef struct _ftree_node ftree_node; |
---|
473 | |
---|
474 | struct _ftree_node { |
---|
475 | guint8 flags; |
---|
476 | char *cache; |
---|
477 | int uri_offset; |
---|
478 | int full_name_offset; |
---|
479 | |
---|
480 | /* format: {name}{\0}{uri}{\0}{full_name}{\0} |
---|
481 | * (No braces). */ |
---|
482 | char data[1]; |
---|
483 | }; |
---|
484 | |
---|
485 | #define FTREE_NODE_GOT_CHILDREN (1 << 0) |
---|
486 | #define FTREE_NODE_SUBSCRIBABLE (1 << 1) |
---|
487 | #define FTREE_NODE_SUBSCRIBED (1 << 2) |
---|
488 | #define FTREE_NODE_ROOT (1 << 3) |
---|
489 | |
---|
490 | static ftree_node * |
---|
491 | ftree_node_new_root (const char *prefix) |
---|
492 | { |
---|
493 | ftree_node *node; |
---|
494 | size_t size; |
---|
495 | |
---|
496 | if (prefix == NULL) |
---|
497 | prefix = ""; |
---|
498 | |
---|
499 | size = sizeof (ftree_node) + strlen (prefix) + 1; |
---|
500 | |
---|
501 | node = g_malloc (size); |
---|
502 | node->flags = FTREE_NODE_ROOT; |
---|
503 | node->uri_offset = 0; |
---|
504 | node->full_name_offset = 1; |
---|
505 | node->data[0] = '\0'; |
---|
506 | strcpy (node->data + 1, prefix); |
---|
507 | |
---|
508 | return node; |
---|
509 | } |
---|
510 | |
---|
511 | static ftree_node * |
---|
512 | ftree_node_new (CamelStore *store, CamelFolderInfo *fi) |
---|
513 | { |
---|
514 | ftree_node *node; |
---|
515 | int uri_offset, full_name_offset; |
---|
516 | size_t size; |
---|
517 | CamelURL *url; |
---|
518 | |
---|
519 | uri_offset = strlen (fi->name) + 1; |
---|
520 | full_name_offset = uri_offset + strlen (fi->url) + 1; |
---|
521 | size = full_name_offset + strlen (fi->full_name); |
---|
522 | |
---|
523 | /* - 1 for sizeof(node.data) but +1 for terminating \0 */ |
---|
524 | node = g_malloc (sizeof (*node) + size); |
---|
525 | |
---|
526 | node->cache = NULL; |
---|
527 | |
---|
528 | /* Noselect? */ |
---|
529 | |
---|
530 | url = camel_url_new (fi->url, NULL); |
---|
531 | if (camel_url_get_param (url, "noselect")) |
---|
532 | node->flags = 0; |
---|
533 | else |
---|
534 | node->flags = FTREE_NODE_SUBSCRIBABLE; |
---|
535 | camel_url_free (url); |
---|
536 | |
---|
537 | /* subscribed? */ |
---|
538 | |
---|
539 | if (camel_store_folder_subscribed (store, fi->full_name)) |
---|
540 | node->flags |= FTREE_NODE_SUBSCRIBED; |
---|
541 | |
---|
542 | /* Copy strings */ |
---|
543 | |
---|
544 | node->uri_offset = uri_offset; |
---|
545 | node->full_name_offset = full_name_offset; |
---|
546 | |
---|
547 | strcpy (node->data, fi->name); |
---|
548 | strcpy (node->data + uri_offset, fi->url); |
---|
549 | strcpy (node->data + full_name_offset, fi->full_name); |
---|
550 | |
---|
551 | /* Done */ |
---|
552 | |
---|
553 | return node; |
---|
554 | } |
---|
555 | |
---|
556 | #define ftree_node_subscribable(node) ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBABLE ) |
---|
557 | #define ftree_node_subscribed(node) ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBED ) |
---|
558 | #define ftree_node_get_name(node) ( ((ftree_node *) (node))->data ) |
---|
559 | #define ftree_node_get_full_name(node) ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->full_name_offset ) |
---|
560 | #define ftree_node_get_uri(node) ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->uri_offset ) |
---|
561 | |
---|
562 | /* ** Folder Tree Model **************************************************** */ |
---|
563 | |
---|
564 | /* A subscribe or scan operation */ |
---|
565 | |
---|
566 | typedef struct _ftree_op_data ftree_op_data; |
---|
567 | |
---|
568 | struct _ftree_op_data { |
---|
569 | FolderETree *ftree; |
---|
570 | ETreePath path; |
---|
571 | ftree_node *data; |
---|
572 | int handle; |
---|
573 | }; |
---|
574 | |
---|
575 | |
---|
576 | /* ETreeModel functions */ |
---|
577 | |
---|
578 | static int |
---|
579 | fe_column_count (ETreeModel *etm) |
---|
580 | { |
---|
581 | return FOLDER_COL_LAST; |
---|
582 | } |
---|
583 | |
---|
584 | static void * |
---|
585 | fe_duplicate_value (ETreeModel *etm, int col, const void *val) |
---|
586 | { |
---|
587 | return g_strdup (val); |
---|
588 | } |
---|
589 | |
---|
590 | static void |
---|
591 | fe_free_value (ETreeModel *etm, int col, void *val) |
---|
592 | { |
---|
593 | g_free (val); |
---|
594 | } |
---|
595 | |
---|
596 | static void* |
---|
597 | fe_init_value (ETreeModel *etm, int col) |
---|
598 | { |
---|
599 | return g_strdup (""); |
---|
600 | } |
---|
601 | |
---|
602 | static gboolean |
---|
603 | fe_value_is_empty (ETreeModel *etm, int col, const void *val) |
---|
604 | { |
---|
605 | return !(val && *(char *)val); |
---|
606 | } |
---|
607 | |
---|
608 | static char * |
---|
609 | fe_value_to_string (ETreeModel *etm, int col, const void *val) |
---|
610 | { |
---|
611 | return g_strdup (val); |
---|
612 | } |
---|
613 | |
---|
614 | static GdkPixbuf * |
---|
615 | fe_icon_at (ETreeModel *etree, ETreePath path) |
---|
616 | { |
---|
617 | return NULL; /* XXX no icons for now */ |
---|
618 | } |
---|
619 | |
---|
620 | static gpointer |
---|
621 | fe_root_value_at (FolderETree *ftree, int col) |
---|
622 | { |
---|
623 | switch (col) { |
---|
624 | case FOLDER_COL_NAME: |
---|
625 | return ftree->service_name; |
---|
626 | case FOLDER_COL_SUBSCRIBED: |
---|
627 | return GINT_TO_POINTER (0); |
---|
628 | default: |
---|
629 | printf ("Oh no, unimplemented column %d in subscribe dialog\n", col); |
---|
630 | } |
---|
631 | |
---|
632 | return NULL; |
---|
633 | } |
---|
634 | |
---|
635 | static gpointer |
---|
636 | fe_real_value_at (FolderETree *ftree, int col, gpointer data) |
---|
637 | { |
---|
638 | switch (col) { |
---|
639 | case FOLDER_COL_NAME: |
---|
640 | return ftree_node_get_name (data); |
---|
641 | case FOLDER_COL_SUBSCRIBED: |
---|
642 | if (ftree_node_subscribed (data)) |
---|
643 | return GINT_TO_POINTER (1); |
---|
644 | return GINT_TO_POINTER (0); |
---|
645 | default: |
---|
646 | printf ("Oh no, unimplemented column %d in subscribe dialog\n", col); |
---|
647 | } |
---|
648 | |
---|
649 | return NULL; |
---|
650 | } |
---|
651 | |
---|
652 | static void * |
---|
653 | fe_value_at (ETreeModel *etree, ETreePath path, int col) |
---|
654 | { |
---|
655 | FolderETree *ftree = (FolderETree *) etree; |
---|
656 | gpointer node_data; |
---|
657 | |
---|
658 | if (path == ftree->root) |
---|
659 | return fe_root_value_at (ftree, col); |
---|
660 | |
---|
661 | node_data = e_tree_memory_node_get_data (E_TREE_MEMORY (etree), path); |
---|
662 | return fe_real_value_at (ftree, col, node_data); |
---|
663 | } |
---|
664 | |
---|
665 | static void |
---|
666 | fe_set_value_at (ETreeModel *etree, ETreePath path, int col, const void *val) |
---|
667 | { |
---|
668 | /* nothing */ |
---|
669 | } |
---|
670 | |
---|
671 | static gboolean |
---|
672 | fe_return_false (void) |
---|
673 | { |
---|
674 | return FALSE; |
---|
675 | } |
---|
676 | |
---|
677 | static gint |
---|
678 | fe_sort_folder (ETreeMemory *etmm, ETreePath left, ETreePath right, gpointer user_data) |
---|
679 | { |
---|
680 | ftree_node *n_left, *n_right; |
---|
681 | |
---|
682 | n_left = e_tree_memory_node_get_data (etmm, left); |
---|
683 | n_right = e_tree_memory_node_get_data (etmm, right); |
---|
684 | |
---|
685 | return g_strcasecmp (ftree_node_get_name (n_left), ftree_node_get_name (n_right)); |
---|
686 | } |
---|
687 | |
---|
688 | /* scanning */ |
---|
689 | |
---|
690 | static void |
---|
691 | fe_got_children (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data) |
---|
692 | { |
---|
693 | ftree_op_data *closure = (ftree_op_data *) data; |
---|
694 | |
---|
695 | if (!info) /* cancelled */ |
---|
696 | return; |
---|
697 | |
---|
698 | if (!prefix) |
---|
699 | prefix = ""; |
---|
700 | |
---|
701 | for ( ; info; info = info->sibling) { |
---|
702 | ETreePath child_path; |
---|
703 | ftree_node *node; |
---|
704 | |
---|
705 | if (strcmp (info->full_name, prefix) == 0) |
---|
706 | continue; |
---|
707 | |
---|
708 | node = ftree_node_new (store, info); |
---|
709 | child_path = e_tree_memory_node_insert (E_TREE_MEMORY (closure->ftree), |
---|
710 | closure->path, |
---|
711 | 0, |
---|
712 | node); |
---|
713 | e_tree_memory_sort_node (E_TREE_MEMORY (closure->ftree), |
---|
714 | closure->path, |
---|
715 | fe_sort_folder, |
---|
716 | NULL); |
---|
717 | } |
---|
718 | |
---|
719 | if (closure->data) |
---|
720 | closure->data->flags |= FTREE_NODE_GOT_CHILDREN; |
---|
721 | |
---|
722 | g_hash_table_remove (closure->ftree->scan_ops, closure->path); |
---|
723 | g_free (closure); |
---|
724 | } |
---|
725 | |
---|
726 | static void |
---|
727 | fe_check_for_children (FolderETree *ftree, ETreePath path) |
---|
728 | { |
---|
729 | ftree_op_data *closure; |
---|
730 | ftree_node *node; |
---|
731 | char *prefix; |
---|
732 | |
---|
733 | node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path); |
---|
734 | |
---|
735 | /* have we already gotten these children? */ |
---|
736 | if (node->flags & FTREE_NODE_GOT_CHILDREN) |
---|
737 | return; |
---|
738 | |
---|
739 | /* or we're loading them right now? */ |
---|
740 | if (g_hash_table_lookup (ftree->scan_ops, path)) |
---|
741 | return; |
---|
742 | |
---|
743 | /* figure out our search prefix */ |
---|
744 | if (path == ftree->root) |
---|
745 | prefix = ftree->search; |
---|
746 | else |
---|
747 | prefix = ftree_node_get_full_name (node); |
---|
748 | |
---|
749 | closure = g_new (ftree_op_data, 1); |
---|
750 | closure->ftree = ftree; |
---|
751 | closure->path = path; |
---|
752 | closure->data = node; |
---|
753 | closure->handle = -1; |
---|
754 | |
---|
755 | g_hash_table_insert (ftree->scan_ops, path, closure); |
---|
756 | |
---|
757 | /* FIXME. Tiny race possiblity I guess. */ |
---|
758 | |
---|
759 | closure->handle = subscribe_get_short_folderinfo (ftree, prefix, fe_got_children, closure); |
---|
760 | } |
---|
761 | |
---|
762 | static void |
---|
763 | fe_create_root_node (FolderETree *ftree) |
---|
764 | { |
---|
765 | ftree_node *node; |
---|
766 | |
---|
767 | node = ftree_node_new_root (ftree->search); |
---|
768 | ftree->root = e_tree_memory_node_insert (E_TREE_MEMORY(ftree), NULL, 0, node); |
---|
769 | fe_check_for_children (ftree, ftree->root); |
---|
770 | } |
---|
771 | |
---|
772 | static ETreePath |
---|
773 | fe_get_first_child (ETreeModel *model, ETreePath path) |
---|
774 | { |
---|
775 | ETreePath child_path; |
---|
776 | |
---|
777 | child_path = E_TREE_MODEL_CLASS (folder_etree_parent_class)->get_first_child (model, path); |
---|
778 | if (child_path) |
---|
779 | fe_check_for_children ((FolderETree *) model, child_path); |
---|
780 | else |
---|
781 | fe_check_for_children ((FolderETree *) model, path); |
---|
782 | return child_path; |
---|
783 | } |
---|
784 | |
---|
785 | /* subscribing */ |
---|
786 | |
---|
787 | static char * |
---|
788 | fe_node_to_shell_path (ftree_node *node) |
---|
789 | { |
---|
790 | char *path = NULL; |
---|
791 | int name_len, full_name_len; |
---|
792 | |
---|
793 | name_len = strlen (ftree_node_get_name (node)); |
---|
794 | full_name_len = strlen (ftree_node_get_full_name (node)); |
---|
795 | |
---|
796 | if (name_len != full_name_len) { |
---|
797 | char *full_name; |
---|
798 | char *iter; |
---|
799 | char sep; |
---|
800 | |
---|
801 | /* so, we don't know the heirarchy separator. But |
---|
802 | * full_name = blahXblahXname, where X = separator |
---|
803 | * and name = .... name. So we can determine it. |
---|
804 | * (imap_store->dir_sep isn't really private, I guess, |
---|
805 | * so we could use that if we had the store. But also |
---|
806 | * we don't "know" that it is an IMAP store anyway.) |
---|
807 | */ |
---|
808 | |
---|
809 | full_name = ftree_node_get_full_name (node); |
---|
810 | sep = full_name[full_name_len - (name_len + 1)]; |
---|
811 | |
---|
812 | if (sep != '/') { |
---|
813 | path = g_malloc (full_name_len + 2); |
---|
814 | path[0] = '/'; |
---|
815 | strcpy (path + 1, full_name); |
---|
816 | while ((iter = strchr (path, sep)) != NULL) |
---|
817 | *iter = '/'; |
---|
818 | } |
---|
819 | } |
---|
820 | |
---|
821 | if (!path) |
---|
822 | path = g_strdup_printf ("/%s", ftree_node_get_full_name (node)); |
---|
823 | |
---|
824 | return path; |
---|
825 | } |
---|
826 | |
---|
827 | static void |
---|
828 | fe_done_subscribing (const char *full_name, const char *name, gboolean subscribe, gboolean success, gpointer user_data) |
---|
829 | { |
---|
830 | ftree_op_data *closure = (ftree_op_data *) user_data; |
---|
831 | |
---|
832 | if (success && closure->handle != -1) { |
---|
833 | char *path; |
---|
834 | |
---|
835 | path = fe_node_to_shell_path (closure->data); |
---|
836 | |
---|
837 | if (subscribe) { |
---|
838 | closure->data->flags |= FTREE_NODE_SUBSCRIBED; |
---|
839 | recursive_add_folder (closure->ftree->e_storage, |
---|
840 | path, name, |
---|
841 | ftree_node_get_uri (closure->data)); |
---|
842 | } else { |
---|
843 | closure->data->flags &= ~FTREE_NODE_SUBSCRIBED; |
---|
844 | |
---|
845 | /* FIXME: recursively remove folder as well? Possible? */ |
---|
846 | evolution_storage_removed_folder (closure->ftree->e_storage, path); |
---|
847 | } |
---|
848 | |
---|
849 | g_free (path); |
---|
850 | e_tree_model_node_data_changed (E_TREE_MODEL (closure->ftree), closure->path); |
---|
851 | } |
---|
852 | |
---|
853 | if (closure->handle != -1) |
---|
854 | g_hash_table_remove (closure->ftree->subscribe_ops, closure->path); |
---|
855 | |
---|
856 | g_free (closure); |
---|
857 | } |
---|
858 | |
---|
859 | /* cleanup */ |
---|
860 | |
---|
861 | static gboolean |
---|
862 | fe_cancel_op_foreach (gpointer key, gpointer value, gpointer user_data) |
---|
863 | { |
---|
864 | /*FolderETree *ftree = (FolderETree *) user_data;*/ |
---|
865 | ftree_op_data *closure = (ftree_op_data *) value; |
---|
866 | |
---|
867 | if (closure->handle != -1) |
---|
868 | mail_msg_cancel (closure->handle); |
---|
869 | |
---|
870 | closure->handle = -1; |
---|
871 | |
---|
872 | return TRUE; |
---|
873 | } |
---|
874 | |
---|
875 | static void |
---|
876 | fe_kill_current_tree (FolderETree *ftree) |
---|
877 | { |
---|
878 | g_hash_table_foreach_remove (ftree->scan_ops, fe_cancel_op_foreach, ftree); |
---|
879 | g_assert (g_hash_table_size (ftree->scan_ops) == 0); |
---|
880 | } |
---|
881 | |
---|
882 | static void |
---|
883 | fe_destroy (GtkObject *obj) |
---|
884 | { |
---|
885 | FolderETree *ftree = (FolderETree *) (obj); |
---|
886 | |
---|
887 | fe_kill_current_tree (ftree); |
---|
888 | |
---|
889 | g_hash_table_foreach_remove (ftree->subscribe_ops, fe_cancel_op_foreach, ftree); |
---|
890 | |
---|
891 | g_hash_table_destroy (ftree->scan_ops); |
---|
892 | g_hash_table_destroy (ftree->subscribe_ops); |
---|
893 | |
---|
894 | camel_object_unref (CAMEL_OBJECT (ftree->store)); |
---|
895 | bonobo_object_unref (BONOBO_OBJECT (ftree->e_storage)); |
---|
896 | |
---|
897 | g_free (ftree->search); |
---|
898 | g_free (ftree->service_name); |
---|
899 | } |
---|
900 | |
---|
901 | typedef gboolean (*bool_func_1) (ETreeModel *, ETreePath, int); |
---|
902 | typedef gboolean (*bool_func_2) (ETreeModel *); |
---|
903 | |
---|
904 | static void |
---|
905 | folder_etree_class_init (GtkObjectClass *klass) |
---|
906 | { |
---|
907 | ETreeModelClass *etree_model_class = E_TREE_MODEL_CLASS (klass); |
---|
908 | |
---|
909 | folder_etree_parent_class = gtk_type_class (E_TREE_MEMORY_TYPE); |
---|
910 | |
---|
911 | klass->destroy = fe_destroy; |
---|
912 | |
---|
913 | etree_model_class->value_at = fe_value_at; |
---|
914 | etree_model_class->set_value_at = fe_set_value_at; |
---|
915 | etree_model_class->column_count = fe_column_count; |
---|
916 | etree_model_class->duplicate_value = fe_duplicate_value; |
---|
917 | etree_model_class->free_value = fe_free_value; |
---|
918 | etree_model_class->initialize_value = fe_init_value; |
---|
919 | etree_model_class->value_is_empty = fe_value_is_empty; |
---|
920 | etree_model_class->value_to_string = fe_value_to_string; |
---|
921 | etree_model_class->icon_at = fe_icon_at; |
---|
922 | etree_model_class->is_editable = (bool_func_1) fe_return_false; |
---|
923 | etree_model_class->has_save_id = (bool_func_2) fe_return_false; |
---|
924 | etree_model_class->has_get_node_by_id = (bool_func_2) fe_return_false; |
---|
925 | etree_model_class->get_first_child = fe_get_first_child; |
---|
926 | } |
---|
927 | |
---|
928 | static void |
---|
929 | folder_etree_init (GtkObject *object) |
---|
930 | { |
---|
931 | FolderETree *ftree = (FolderETree *) object; |
---|
932 | |
---|
933 | e_tree_memory_set_node_destroy_func (E_TREE_MEMORY (ftree), (GFunc) g_free, ftree); |
---|
934 | |
---|
935 | ftree->scan_ops = g_hash_table_new (g_direct_hash, g_direct_equal); |
---|
936 | ftree->subscribe_ops = g_hash_table_new (g_direct_hash, g_direct_equal); |
---|
937 | |
---|
938 | ftree->search = g_strdup (""); |
---|
939 | } |
---|
940 | |
---|
941 | static FolderETree * |
---|
942 | folder_etree_construct (FolderETree *ftree, |
---|
943 | CamelStore *store) |
---|
944 | { |
---|
945 | e_tree_memory_construct (E_TREE_MEMORY (ftree)); |
---|
946 | |
---|
947 | ftree->store = store; |
---|
948 | camel_object_ref (CAMEL_OBJECT (store)); |
---|
949 | |
---|
950 | ftree->service_name = camel_service_get_name (CAMEL_SERVICE (store), FALSE); |
---|
951 | |
---|
952 | ftree->e_storage = mail_lookup_storage (store); /* this gives us a ref */ |
---|
953 | |
---|
954 | fe_create_root_node (ftree); |
---|
955 | |
---|
956 | return ftree; |
---|
957 | } |
---|
958 | |
---|
959 | static |
---|
960 | E_MAKE_TYPE (folder_etree, "FolderETree", FolderETree, folder_etree_class_init, folder_etree_init, E_TREE_MEMORY_TYPE); |
---|
961 | |
---|
962 | /* public */ |
---|
963 | |
---|
964 | static FolderETree * |
---|
965 | folder_etree_new (CamelStore *store) |
---|
966 | { |
---|
967 | FolderETree *ftree; |
---|
968 | |
---|
969 | ftree = gtk_type_new (folder_etree_get_type()); |
---|
970 | ftree = folder_etree_construct (ftree, store); |
---|
971 | return ftree; |
---|
972 | } |
---|
973 | |
---|
974 | static void |
---|
975 | folder_etree_clear_tree (FolderETree *ftree) |
---|
976 | { |
---|
977 | e_tree_memory_freeze (E_TREE_MEMORY (ftree)); |
---|
978 | e_tree_memory_node_remove (E_TREE_MEMORY (ftree), ftree->root); |
---|
979 | fe_create_root_node (ftree); |
---|
980 | e_tree_memory_thaw (E_TREE_MEMORY (ftree)); |
---|
981 | } |
---|
982 | |
---|
983 | static void |
---|
984 | folder_etree_set_search (FolderETree *ftree, const char *search) |
---|
985 | { |
---|
986 | if (!strcmp (search, ftree->search)) |
---|
987 | return; |
---|
988 | |
---|
989 | g_free (ftree->search); |
---|
990 | ftree->search = g_strdup (search); |
---|
991 | |
---|
992 | folder_etree_clear_tree (ftree); |
---|
993 | } |
---|
994 | |
---|
995 | |
---|
996 | static int |
---|
997 | folder_etree_path_set_subscription (FolderETree *ftree, ETreePath path, gboolean subscribe) |
---|
998 | { |
---|
999 | ftree_op_data *closure; |
---|
1000 | ftree_node *node; |
---|
1001 | |
---|
1002 | /* already in progress? */ |
---|
1003 | |
---|
1004 | if (g_hash_table_lookup (ftree->subscribe_ops, path)) |
---|
1005 | return 0; |
---|
1006 | |
---|
1007 | /* noselect? */ |
---|
1008 | |
---|
1009 | node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path); |
---|
1010 | |
---|
1011 | if (!ftree_node_subscribable (node)) |
---|
1012 | return -1; |
---|
1013 | |
---|
1014 | /* noop? */ |
---|
1015 | |
---|
1016 | /* uh, this should be a not XOR or something */ |
---|
1017 | if ((ftree_node_subscribed (node) && subscribe) || |
---|
1018 | (!ftree_node_subscribed (node) && !subscribe)) |
---|
1019 | return 0; |
---|
1020 | |
---|
1021 | closure = g_new (ftree_op_data, 1); |
---|
1022 | closure->ftree = ftree; |
---|
1023 | closure->path = path; |
---|
1024 | closure->data = node; |
---|
1025 | closure->handle = -1; |
---|
1026 | |
---|
1027 | g_hash_table_insert (ftree->subscribe_ops, path, closure); |
---|
1028 | |
---|
1029 | closure->handle = subscribe_do_subscribe_folder (ftree->store, |
---|
1030 | ftree_node_get_full_name (node), |
---|
1031 | ftree_node_get_name (node), |
---|
1032 | subscribe, |
---|
1033 | fe_done_subscribing, |
---|
1034 | closure); |
---|
1035 | return 0; |
---|
1036 | } |
---|
1037 | |
---|
1038 | static int |
---|
1039 | folder_etree_path_toggle_subscription (FolderETree *ftree, ETreePath path) |
---|
1040 | { |
---|
1041 | ftree_node *node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path); |
---|
1042 | |
---|
1043 | if (ftree_node_subscribed (node)) |
---|
1044 | return folder_etree_path_set_subscription (ftree, path, FALSE); |
---|
1045 | else |
---|
1046 | return folder_etree_path_set_subscription (ftree, path, TRUE); |
---|
1047 | } |
---|
1048 | |
---|
1049 | /* ** StoreData ************************************************************ */ |
---|
1050 | |
---|
1051 | typedef struct _StoreData StoreData; |
---|
1052 | |
---|
1053 | typedef void (*StoreDataStoreFunc) (StoreData *, CamelStore *, gpointer); |
---|
1054 | |
---|
1055 | struct _StoreData { |
---|
1056 | int refcount; |
---|
1057 | char *uri; |
---|
1058 | |
---|
1059 | FolderETree *ftree; |
---|
1060 | CamelStore *store; |
---|
1061 | |
---|
1062 | int request_id; |
---|
1063 | |
---|
1064 | GtkWidget *widget; |
---|
1065 | StoreDataStoreFunc store_func; |
---|
1066 | gpointer store_data; |
---|
1067 | }; |
---|
1068 | |
---|
1069 | static StoreData * |
---|
1070 | store_data_new (const char *uri) |
---|
1071 | { |
---|
1072 | StoreData *sd; |
---|
1073 | |
---|
1074 | sd = g_new0 (StoreData, 1); |
---|
1075 | sd->refcount = 1; |
---|
1076 | sd->uri = g_strdup (uri); |
---|
1077 | |
---|
1078 | return sd; |
---|
1079 | } |
---|
1080 | |
---|
1081 | static void |
---|
1082 | store_data_free (StoreData *sd) |
---|
1083 | { |
---|
1084 | if (sd->request_id) |
---|
1085 | mail_msg_cancel (sd->request_id); |
---|
1086 | |
---|
1087 | if (sd->widget) |
---|
1088 | gtk_object_unref (GTK_OBJECT (sd->widget)); |
---|
1089 | |
---|
1090 | if (sd->ftree) |
---|
1091 | gtk_object_unref (GTK_OBJECT (sd->ftree)); |
---|
1092 | |
---|
1093 | if (sd->store) |
---|
1094 | camel_object_unref (CAMEL_OBJECT (sd->store)); |
---|
1095 | |
---|
1096 | g_free (sd->uri); |
---|
1097 | g_free (sd); |
---|
1098 | } |
---|
1099 | |
---|
1100 | static void |
---|
1101 | store_data_ref (StoreData *sd) |
---|
1102 | { |
---|
1103 | sd->refcount++; |
---|
1104 | } |
---|
1105 | |
---|
1106 | static void |
---|
1107 | store_data_unref (StoreData *sd) |
---|
1108 | { |
---|
1109 | if (sd->refcount <= 1) { |
---|
1110 | store_data_free (sd); |
---|
1111 | } else { |
---|
1112 | sd->refcount--; |
---|
1113 | } |
---|
1114 | } |
---|
1115 | |
---|
1116 | static void |
---|
1117 | sd_got_store (char *uri, CamelStore *store, gpointer user_data) |
---|
1118 | { |
---|
1119 | StoreData *sd = (StoreData *) user_data; |
---|
1120 | |
---|
1121 | sd->store = store; |
---|
1122 | |
---|
1123 | if (store) /* we can have exceptions getting the store... server is down, eg */ |
---|
1124 | camel_object_ref (CAMEL_OBJECT (sd->store)); |
---|
1125 | |
---|
1126 | /* uh, so we might have a problem if this operation is cancelled. Unsure. */ |
---|
1127 | sd->request_id = 0; |
---|
1128 | |
---|
1129 | if (sd->store_func) |
---|
1130 | (sd->store_func) (sd, sd->store, sd->store_data); |
---|
1131 | |
---|
1132 | store_data_unref (sd); |
---|
1133 | } |
---|
1134 | |
---|
1135 | static void |
---|
1136 | store_data_async_get_store (StoreData *sd, StoreDataStoreFunc func, gpointer user_data) |
---|
1137 | { |
---|
1138 | if (sd->request_id) { |
---|
1139 | d(printf ("Already loading store, nooping\n")); |
---|
1140 | return; |
---|
1141 | } |
---|
1142 | |
---|
1143 | if (sd->store) { |
---|
1144 | /* um, is this the best behavior? */ |
---|
1145 | func (sd, sd->store, user_data); |
---|
1146 | return; |
---|
1147 | } |
---|
1148 | |
---|
1149 | sd->store_func = func; |
---|
1150 | sd->store_data = user_data; |
---|
1151 | store_data_ref (sd); |
---|
1152 | sd->request_id = mail_get_store (sd->uri, sd_got_store, sd); |
---|
1153 | } |
---|
1154 | |
---|
1155 | static void |
---|
1156 | store_data_cancel_get_store (StoreData *sd) |
---|
1157 | { |
---|
1158 | g_return_if_fail (sd->request_id); |
---|
1159 | |
---|
1160 | mail_msg_cancel (sd->request_id); |
---|
1161 | sd->request_id = 0; |
---|
1162 | } |
---|
1163 | |
---|
1164 | static void |
---|
1165 | sd_toggle_cb (ETree *tree, int row, ETreePath path, int col, GdkEvent *event, gpointer user_data) |
---|
1166 | { |
---|
1167 | StoreData *sd = (StoreData *) user_data; |
---|
1168 | |
---|
1169 | folder_etree_path_toggle_subscription (sd->ftree, path); |
---|
1170 | } |
---|
1171 | |
---|
1172 | static GtkWidget * |
---|
1173 | store_data_get_widget (StoreData *sd) |
---|
1174 | { |
---|
1175 | GtkWidget *tree; |
---|
1176 | |
---|
1177 | if (!sd->store) { |
---|
1178 | d(printf ("store data can't get widget before getting store.\n")); |
---|
1179 | return NULL; |
---|
1180 | } |
---|
1181 | |
---|
1182 | if (sd->widget) |
---|
1183 | return sd->widget; |
---|
1184 | |
---|
1185 | sd->ftree = folder_etree_new (sd->store); |
---|
1186 | |
---|
1187 | /* You annoy me, etree! */ |
---|
1188 | tree = gtk_widget_new (E_TREE_SCROLLED_TYPE, |
---|
1189 | "hadjustment", NULL, |
---|
1190 | "vadjustment", NULL, |
---|
1191 | NULL); |
---|
1192 | |
---|
1193 | tree = (GtkWidget *) e_tree_scrolled_construct_from_spec_file (E_TREE_SCROLLED (tree), |
---|
1194 | E_TREE_MODEL (sd->ftree), |
---|
1195 | subscribe_get_global_extras (), |
---|
1196 | EVOLUTION_ETSPECDIR "/subscribe-dialog.etspec", |
---|
1197 | NULL); |
---|
1198 | e_tree_root_node_set_visible (e_tree_scrolled_get_tree(E_TREE_SCROLLED(tree)), TRUE); |
---|
1199 | gtk_signal_connect (GTK_OBJECT (e_tree_scrolled_get_tree(E_TREE_SCROLLED (tree))), |
---|
1200 | "double_click", GTK_SIGNAL_FUNC (sd_toggle_cb), sd); |
---|
1201 | |
---|
1202 | gtk_object_unref (GTK_OBJECT (global_extras)); |
---|
1203 | |
---|
1204 | sd->widget = tree; |
---|
1205 | gtk_object_ref (GTK_OBJECT (sd->widget)); |
---|
1206 | |
---|
1207 | return sd->widget; |
---|
1208 | } |
---|
1209 | |
---|
1210 | typedef struct _selection_closure { |
---|
1211 | StoreData *sd; |
---|
1212 | enum { SET, CLEAR, TOGGLE } mode; |
---|
1213 | } selection_closure; |
---|
1214 | |
---|
1215 | static void |
---|
1216 | sd_subscribe_folder_foreach (int model_row, gpointer closure) |
---|
1217 | { |
---|
1218 | selection_closure *sc = (selection_closure *) closure; |
---|
1219 | StoreData *sd = sc->sd; |
---|
1220 | ETree *tree = e_tree_scrolled_get_tree(E_TREE_SCROLLED(sd->widget)); |
---|
1221 | ETreePath path = e_tree_node_at_row (tree, model_row); |
---|
1222 | |
---|
1223 | /* ignore results */ |
---|
1224 | switch (sc->mode) { |
---|
1225 | case SET: |
---|
1226 | folder_etree_path_set_subscription (sd->ftree, path, TRUE); |
---|
1227 | break; |
---|
1228 | case CLEAR: |
---|
1229 | folder_etree_path_set_subscription (sd->ftree, path, FALSE); |
---|
1230 | break; |
---|
1231 | case TOGGLE: |
---|
1232 | folder_etree_path_toggle_subscription (sd->ftree, path); |
---|
1233 | break; |
---|
1234 | } |
---|
1235 | } |
---|
1236 | |
---|
1237 | static void |
---|
1238 | store_data_selection_set_subscription (StoreData *sd, gboolean subscribe) |
---|
1239 | { |
---|
1240 | selection_closure sc; |
---|
1241 | ETree *tree; |
---|
1242 | |
---|
1243 | sc.sd = sd; |
---|
1244 | if (subscribe) |
---|
1245 | sc.mode = SET; |
---|
1246 | else |
---|
1247 | sc.mode = CLEAR; |
---|
1248 | |
---|
1249 | tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget)); |
---|
1250 | e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc); |
---|
1251 | } |
---|
1252 | |
---|
1253 | #ifdef NEED_TOGGLE_SELECTION |
---|
1254 | static void |
---|
1255 | store_data_selection_toggle_subscription (StoreData *sd) |
---|
1256 | { |
---|
1257 | selection_closure sc; |
---|
1258 | ETree *tree; |
---|
1259 | |
---|
1260 | sc.sd = sd; |
---|
1261 | sc.mode = TOGGLE; |
---|
1262 | |
---|
1263 | tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget)); |
---|
1264 | e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc); |
---|
1265 | } |
---|
1266 | #endif |
---|
1267 | |
---|
1268 | static gboolean |
---|
1269 | store_data_mid_request (StoreData *sd) |
---|
1270 | { |
---|
1271 | return (gboolean) sd->request_id; |
---|
1272 | } |
---|
1273 | |
---|
1274 | /* ** yaay, SubscribeDialog ******************************************************* */ |
---|
1275 | |
---|
1276 | #define PARENT_TYPE (gtk_object_get_type ()) |
---|
1277 | |
---|
1278 | #ifdef JUST_FOR_TRANSLATORS |
---|
1279 | static char *str = N_("Folder"); |
---|
1280 | #endif |
---|
1281 | |
---|
1282 | #define STORE_DATA_KEY "store-data" |
---|
1283 | |
---|
1284 | struct _SubscribeDialogPrivate { |
---|
1285 | GladeXML *xml; |
---|
1286 | GList *store_list; |
---|
1287 | |
---|
1288 | StoreData *current_store; |
---|
1289 | GtkWidget *current_widget; |
---|
1290 | |
---|
1291 | GtkWidget *default_widget; |
---|
1292 | GtkWidget *none_item; |
---|
1293 | GtkWidget *search_entry; |
---|
1294 | GtkWidget *hbox; |
---|
1295 | GtkWidget *filter_radio, *all_radio; |
---|
1296 | GtkWidget *sub_button, *unsub_button, *refresh_button; |
---|
1297 | }; |
---|
1298 | |
---|
1299 | static GtkObjectClass *subscribe_dialog_parent_class; |
---|
1300 | |
---|
1301 | static void |
---|
1302 | sc_refresh_pressed (GtkWidget *widget, gpointer user_data) |
---|
1303 | { |
---|
1304 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1305 | |
---|
1306 | if (sc->priv->current_store) |
---|
1307 | folder_etree_clear_tree (sc->priv->current_store->ftree); |
---|
1308 | } |
---|
1309 | |
---|
1310 | static void |
---|
1311 | sc_search_activated (GtkWidget *widget, gpointer user_data) |
---|
1312 | { |
---|
1313 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1314 | StoreData *store = sc->priv->current_store; |
---|
1315 | char *search; |
---|
1316 | |
---|
1317 | if (!store) |
---|
1318 | return; |
---|
1319 | |
---|
1320 | search = e_utf8_gtk_entry_get_text (GTK_ENTRY (widget)); |
---|
1321 | folder_etree_set_search (store->ftree, search); |
---|
1322 | } |
---|
1323 | |
---|
1324 | static void |
---|
1325 | sc_subscribe_pressed (GtkWidget *widget, gpointer user_data) |
---|
1326 | { |
---|
1327 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1328 | StoreData *store = sc->priv->current_store; |
---|
1329 | |
---|
1330 | if (!store) |
---|
1331 | return; |
---|
1332 | |
---|
1333 | store_data_selection_set_subscription (store, TRUE); |
---|
1334 | } |
---|
1335 | |
---|
1336 | static void |
---|
1337 | sc_unsubscribe_pressed (GtkWidget *widget, gpointer user_data) |
---|
1338 | { |
---|
1339 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1340 | StoreData *store = sc->priv->current_store; |
---|
1341 | |
---|
1342 | if (!store) |
---|
1343 | return; |
---|
1344 | |
---|
1345 | store_data_selection_set_subscription (store, FALSE); |
---|
1346 | } |
---|
1347 | |
---|
1348 | static void |
---|
1349 | sc_all_toggled (GtkWidget *widget, gpointer user_data) |
---|
1350 | { |
---|
1351 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1352 | StoreData *store = sc->priv->current_store; |
---|
1353 | |
---|
1354 | if (!store) |
---|
1355 | return; |
---|
1356 | |
---|
1357 | if (GTK_TOGGLE_BUTTON (widget)->active) { |
---|
1358 | gtk_widget_set_sensitive (sc->priv->search_entry, FALSE); |
---|
1359 | folder_etree_set_search (store->ftree, ""); |
---|
1360 | } |
---|
1361 | } |
---|
1362 | |
---|
1363 | static void |
---|
1364 | sc_filter_toggled (GtkWidget *widget, gpointer user_data) |
---|
1365 | { |
---|
1366 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1367 | StoreData *store = sc->priv->current_store; |
---|
1368 | |
---|
1369 | if (!store) |
---|
1370 | return; |
---|
1371 | |
---|
1372 | if (GTK_TOGGLE_BUTTON (widget)->active) { |
---|
1373 | gtk_widget_set_sensitive (sc->priv->search_entry, TRUE); |
---|
1374 | sc_search_activated (sc->priv->search_entry, sc); |
---|
1375 | } |
---|
1376 | } |
---|
1377 | |
---|
1378 | static void |
---|
1379 | populate_store_foreach (MailConfigService *service, SubscribeDialog *sc) |
---|
1380 | { |
---|
1381 | StoreData *sd; |
---|
1382 | |
---|
1383 | if (service->url == NULL || service->enabled == FALSE) |
---|
1384 | return; |
---|
1385 | |
---|
1386 | sd = store_data_new (service->url); |
---|
1387 | sc->priv->store_list = g_list_prepend (sc->priv->store_list, sd); |
---|
1388 | } |
---|
1389 | |
---|
1390 | static void |
---|
1391 | kill_default_view (SubscribeDialog *sc) |
---|
1392 | { |
---|
1393 | gtk_widget_hide (sc->priv->none_item); |
---|
1394 | |
---|
1395 | /* the entry will be set sensitive when one of the |
---|
1396 | * radio buttons is activated, if necessary. */ |
---|
1397 | |
---|
1398 | gtk_widget_set_sensitive (sc->priv->all_radio, TRUE); |
---|
1399 | gtk_widget_set_sensitive (sc->priv->filter_radio, TRUE); |
---|
1400 | gtk_widget_set_sensitive (sc->priv->sub_button, TRUE); |
---|
1401 | gtk_widget_set_sensitive (sc->priv->unsub_button, TRUE); |
---|
1402 | gtk_widget_set_sensitive (sc->priv->refresh_button, TRUE); |
---|
1403 | } |
---|
1404 | |
---|
1405 | static void |
---|
1406 | sc_selection_changed (GtkObject *obj, gpointer user_data) |
---|
1407 | { |
---|
1408 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1409 | gboolean sensitive; |
---|
1410 | |
---|
1411 | if (e_selection_model_selected_count (E_SELECTION_MODEL (obj))) |
---|
1412 | sensitive = TRUE; |
---|
1413 | else |
---|
1414 | sensitive = FALSE; |
---|
1415 | |
---|
1416 | gtk_widget_set_sensitive (sc->priv->sub_button, sensitive); |
---|
1417 | gtk_widget_set_sensitive (sc->priv->unsub_button, sensitive); |
---|
1418 | } |
---|
1419 | |
---|
1420 | static void |
---|
1421 | menu_item_selected (GtkMenuItem *item, gpointer user_data) |
---|
1422 | { |
---|
1423 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1424 | StoreData *sd = gtk_object_get_data (GTK_OBJECT (item), STORE_DATA_KEY); |
---|
1425 | |
---|
1426 | g_return_if_fail (sd); |
---|
1427 | |
---|
1428 | if (sd->widget == NULL) { |
---|
1429 | GtkWidget *widget; |
---|
1430 | ESelectionModel *esm; |
---|
1431 | ETree *tree; |
---|
1432 | |
---|
1433 | widget = store_data_get_widget (sd); |
---|
1434 | gtk_box_pack_start (GTK_BOX (sc->priv->hbox), widget, TRUE, TRUE, 0); |
---|
1435 | |
---|
1436 | tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (widget)); |
---|
1437 | esm = e_tree_get_selection_model (tree); |
---|
1438 | gtk_signal_connect (GTK_OBJECT (esm), "selection_changed", sc_selection_changed, sc); |
---|
1439 | sc_selection_changed ((GtkObject *)esm, sc); |
---|
1440 | } |
---|
1441 | |
---|
1442 | if (sc->priv->current_widget == sc->priv->default_widget) |
---|
1443 | kill_default_view (sc); |
---|
1444 | |
---|
1445 | gtk_widget_hide (sc->priv->current_widget); |
---|
1446 | gtk_widget_show (sd->widget); |
---|
1447 | sc->priv->current_widget = sd->widget; |
---|
1448 | sc->priv->current_store = sd; |
---|
1449 | |
---|
1450 | if (*sd->ftree->search) { |
---|
1451 | e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), sd->ftree->search); |
---|
1452 | gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc->priv->filter_radio), TRUE); |
---|
1453 | } else { |
---|
1454 | e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), ""); |
---|
1455 | gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sc->priv->all_radio), TRUE); |
---|
1456 | } |
---|
1457 | } |
---|
1458 | |
---|
1459 | static void |
---|
1460 | dummy_item_selected (GtkMenuItem *item, gpointer user_data) |
---|
1461 | { |
---|
1462 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); |
---|
1463 | |
---|
1464 | gtk_widget_hide (sc->priv->current_widget); |
---|
1465 | gtk_widget_show (sc->priv->default_widget); |
---|
1466 | sc->priv->current_widget = sc->priv->default_widget; |
---|
1467 | sc->priv->current_store = NULL; |
---|
1468 | |
---|
1469 | e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), ""); |
---|
1470 | } |
---|
1471 | |
---|
1472 | /* wonderful */ |
---|
1473 | |
---|
1474 | static void |
---|
1475 | got_sd_store (StoreData *sd, CamelStore *store, gpointer data) |
---|
1476 | { |
---|
1477 | if (store && camel_store_supports_subscriptions (store)) |
---|
1478 | gtk_widget_show (GTK_WIDGET (data)); |
---|
1479 | } |
---|
1480 | |
---|
1481 | /* FIXME: if there aren't any stores that are subscribable, the option |
---|
1482 | * menu will only have the "No server selected" item and the user will |
---|
1483 | * be confused. */ |
---|
1484 | |
---|
1485 | static void |
---|
1486 | populate_store_list (SubscribeDialog *sc) |
---|
1487 | { |
---|
1488 | const GSList *news; |
---|
1489 | GSList *sources; |
---|
1490 | GList *iter; |
---|
1491 | GtkWidget *menu; |
---|
1492 | GtkWidget *omenu; |
---|
1493 | |
---|
1494 | sources = mail_config_get_sources (); |
---|
1495 | g_slist_foreach (sources, (GFunc) populate_store_foreach, sc); |
---|
1496 | g_slist_free (sources); |
---|
1497 | |
---|
1498 | news = mail_config_get_news (); |
---|
1499 | g_slist_foreach ((GSList *) news, (GFunc) populate_store_foreach, sc); |
---|
1500 | |
---|
1501 | menu = gtk_menu_new (); |
---|
1502 | |
---|
1503 | for (iter = sc->priv->store_list; iter; iter = iter->next) { |
---|
1504 | GtkWidget *item; |
---|
1505 | CamelURL *url; |
---|
1506 | char *string; |
---|
1507 | |
---|
1508 | url = camel_url_new (((StoreData *) iter->data)->uri, NULL); |
---|
1509 | string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL); |
---|
1510 | camel_url_free (url); |
---|
1511 | item = gtk_menu_item_new_with_label (string); |
---|
1512 | store_data_async_get_store (iter->data, got_sd_store, item); |
---|
1513 | gtk_object_set_data (GTK_OBJECT (item), STORE_DATA_KEY, iter->data); |
---|
1514 | gtk_signal_connect (GTK_OBJECT (item), "activate", menu_item_selected, sc); |
---|
1515 | g_free (string); |
---|
1516 | |
---|
1517 | gtk_menu_prepend (GTK_MENU (menu), item); |
---|
1518 | } |
---|
1519 | |
---|
1520 | sc->priv->none_item = gtk_menu_item_new_with_label (_("No server has been selected")); |
---|
1521 | gtk_signal_connect (GTK_OBJECT (sc->priv->none_item), "activate", dummy_item_selected, sc); |
---|
1522 | gtk_widget_show (sc->priv->none_item); |
---|
1523 | gtk_menu_prepend (GTK_MENU (menu), sc->priv->none_item); |
---|
1524 | |
---|
1525 | gtk_widget_show (menu); |
---|
1526 | |
---|
1527 | omenu = glade_xml_get_widget (sc->priv->xml, "store_menu"); |
---|
1528 | gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu); |
---|
1529 | } |
---|
1530 | |
---|
1531 | static void |
---|
1532 | subscribe_dialog_destroy (GtkObject *object) |
---|
1533 | { |
---|
1534 | SubscribeDialog *sc; |
---|
1535 | GList *iter; |
---|
1536 | |
---|
1537 | sc = SUBSCRIBE_DIALOG (object); |
---|
1538 | |
---|
1539 | for (iter = sc->priv->store_list; iter; iter = iter->next) { |
---|
1540 | StoreData *data = iter->data; |
---|
1541 | |
---|
1542 | if (store_data_mid_request (data)) |
---|
1543 | store_data_cancel_get_store (data); |
---|
1544 | |
---|
1545 | data->store_func = NULL; |
---|
1546 | |
---|
1547 | store_data_unref (data); |
---|
1548 | } |
---|
1549 | |
---|
1550 | g_list_free (sc->priv->store_list); |
---|
1551 | |
---|
1552 | gtk_object_unref (GTK_OBJECT (sc->priv->xml)); |
---|
1553 | |
---|
1554 | g_free (sc->priv); |
---|
1555 | |
---|
1556 | subscribe_dialog_parent_class->destroy (object); |
---|
1557 | } |
---|
1558 | |
---|
1559 | static void |
---|
1560 | subscribe_dialog_class_init (GtkObjectClass *object_class) |
---|
1561 | { |
---|
1562 | object_class->destroy = subscribe_dialog_destroy; |
---|
1563 | |
---|
1564 | subscribe_dialog_parent_class = gtk_type_class (PARENT_TYPE); |
---|
1565 | } |
---|
1566 | |
---|
1567 | static void |
---|
1568 | subscribe_dialog_init (GtkObject *object) |
---|
1569 | { |
---|
1570 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (object); |
---|
1571 | |
---|
1572 | sc->priv = g_new0 (SubscribeDialogPrivate, 1); |
---|
1573 | } |
---|
1574 | |
---|
1575 | static GtkWidget * |
---|
1576 | sc_create_default_widget (void) |
---|
1577 | { |
---|
1578 | GtkWidget *label; |
---|
1579 | GtkWidget *viewport; |
---|
1580 | |
---|
1581 | label = gtk_label_new (_("Please select a server.")); |
---|
1582 | gtk_widget_show (label); |
---|
1583 | |
---|
1584 | viewport = gtk_viewport_new (NULL, NULL); |
---|
1585 | gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_IN); |
---|
1586 | gtk_container_add (GTK_CONTAINER (viewport), label); |
---|
1587 | |
---|
1588 | return viewport; |
---|
1589 | } |
---|
1590 | |
---|
1591 | static void |
---|
1592 | subscribe_dialog_construct (GtkObject *object) |
---|
1593 | { |
---|
1594 | SubscribeDialog *sc = SUBSCRIBE_DIALOG (object); |
---|
1595 | |
---|
1596 | /* Load the XML */ |
---|
1597 | sc->priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/subscribe-dialog.glade", NULL); |
---|
1598 | |
---|
1599 | sc->app = glade_xml_get_widget (sc->priv->xml, "Manage Subscriptions"); |
---|
1600 | sc->priv->hbox = glade_xml_get_widget (sc->priv->xml, "tree_box"); |
---|
1601 | sc->priv->search_entry = glade_xml_get_widget (sc->priv->xml, "search_entry"); |
---|
1602 | sc->priv->filter_radio = glade_xml_get_widget (sc->priv->xml, "filter_radio"); |
---|
1603 | sc->priv->all_radio = glade_xml_get_widget (sc->priv->xml, "all_radio"); |
---|
1604 | sc->priv->sub_button = glade_xml_get_widget (sc->priv->xml, "subscribe_button"); |
---|
1605 | sc->priv->unsub_button = glade_xml_get_widget (sc->priv->xml, "unsubscribe_button"); |
---|
1606 | sc->priv->refresh_button = glade_xml_get_widget (sc->priv->xml, "refresh_button"); |
---|
1607 | |
---|
1608 | /* create default view */ |
---|
1609 | sc->priv->default_widget = sc_create_default_widget(); |
---|
1610 | sc->priv->current_widget = sc->priv->default_widget; |
---|
1611 | gtk_box_pack_start (GTK_BOX (sc->priv->hbox), sc->priv->default_widget, TRUE, TRUE, 0); |
---|
1612 | gtk_widget_show (sc->priv->default_widget); |
---|
1613 | |
---|
1614 | gtk_widget_set_sensitive (sc->priv->all_radio, FALSE); |
---|
1615 | gtk_widget_set_sensitive (sc->priv->filter_radio, FALSE); |
---|
1616 | gtk_widget_set_sensitive (sc->priv->search_entry, FALSE); |
---|
1617 | gtk_widget_set_sensitive (sc->priv->sub_button, FALSE); |
---|
1618 | gtk_widget_set_sensitive (sc->priv->unsub_button, FALSE); |
---|
1619 | gtk_widget_set_sensitive (sc->priv->refresh_button, FALSE); |
---|
1620 | |
---|
1621 | /* hook up some signals */ |
---|
1622 | gtk_signal_connect (GTK_OBJECT (sc->priv->search_entry), "activate", sc_search_activated, sc); |
---|
1623 | gtk_signal_connect (GTK_OBJECT (sc->priv->sub_button), "clicked", sc_subscribe_pressed, sc); |
---|
1624 | gtk_signal_connect (GTK_OBJECT (sc->priv->unsub_button), "clicked", sc_unsubscribe_pressed, sc); |
---|
1625 | gtk_signal_connect (GTK_OBJECT (sc->priv->refresh_button), "clicked", sc_refresh_pressed, sc); |
---|
1626 | gtk_signal_connect (GTK_OBJECT (sc->priv->all_radio), "toggled", sc_all_toggled, sc); |
---|
1627 | gtk_signal_connect (GTK_OBJECT (sc->priv->filter_radio), "toggled", sc_filter_toggled, sc); |
---|
1628 | |
---|
1629 | /* Get the list of stores */ |
---|
1630 | populate_store_list (sc); |
---|
1631 | } |
---|
1632 | |
---|
1633 | GtkObject * |
---|
1634 | subscribe_dialog_new (void) |
---|
1635 | { |
---|
1636 | SubscribeDialog *subscribe_dialog; |
---|
1637 | |
---|
1638 | subscribe_dialog = gtk_type_new (SUBSCRIBE_DIALOG_TYPE); |
---|
1639 | subscribe_dialog_construct (GTK_OBJECT (subscribe_dialog)); |
---|
1640 | |
---|
1641 | return GTK_OBJECT (subscribe_dialog); |
---|
1642 | } |
---|
1643 | |
---|
1644 | E_MAKE_TYPE (subscribe_dialog, "SubscribeDialog", SubscribeDialog, subscribe_dialog_class_init, subscribe_dialog_init, PARENT_TYPE); |
---|