| |
1 /* |
| |
2 * Evolution integration plugin for Gaim |
| |
3 * |
| |
4 * Copyright (C) 2003 Christian Hammond. |
| |
5 * |
| |
6 * This program is free software; you can redistribute it and/or |
| |
7 * modify it under the terms of the GNU General Public License as |
| |
8 * published by the Free Software Foundation; either version 2 of the |
| |
9 * License, or (at your option) any later version. |
| |
10 * |
| |
11 * This program is distributed in the hope that it will be useful, but |
| |
12 * WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| |
14 * General Public License for more details. |
| |
15 * |
| |
16 * You should have received a copy of the GNU General Public License |
| |
17 * along with this program; if not, write to the Free Software |
| |
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| |
19 * 02111-1307, USA. |
| |
20 */ |
| |
21 #include "internal.h" |
| |
22 #include "gtkblist.h" |
| |
23 #include "gtkexpander.h" |
| |
24 #include "gtkgaim.h" |
| |
25 #include "gtkutils.h" |
| |
26 #include "gtkimhtml.h" |
| |
27 |
| |
28 #include "debug.h" |
| |
29 |
| |
30 #include "gevolution.h" |
| |
31 |
| |
32 #include <stdlib.h> |
| |
33 #include <gtk/gtk.h> |
| |
34 |
| |
35 enum |
| |
36 { |
| |
37 COLUMN_NAME, |
| |
38 COLUMN_DATA, |
| |
39 NUM_COLUMNS |
| |
40 }; |
| |
41 |
| |
42 static gint |
| |
43 delete_win_cb(GtkWidget *w, GdkEvent *event, GevoAssociateBuddyDialog *dialog) |
| |
44 { |
| |
45 gtk_widget_destroy(dialog->win); |
| |
46 |
| |
47 if (dialog->contacts != NULL) |
| |
48 { |
| |
49 g_list_foreach(dialog->contacts, (GFunc)g_object_unref, NULL); |
| |
50 g_list_free(dialog->contacts); |
| |
51 } |
| |
52 |
| |
53 g_object_unref(dialog->book); |
| |
54 gevo_addrbooks_model_unref(dialog->addrbooks); |
| |
55 |
| |
56 g_free(dialog); |
| |
57 |
| |
58 return 0; |
| |
59 } |
| |
60 |
| |
61 static void |
| |
62 search_changed_cb(GtkEntry *entry, GevoAssociateBuddyDialog *dialog) |
| |
63 { |
| |
64 const char *text = gtk_entry_get_text(entry); |
| |
65 GList *l; |
| |
66 |
| |
67 gtk_list_store_clear(dialog->model); |
| |
68 |
| |
69 for (l = dialog->contacts; l != NULL; l = l->next) |
| |
70 { |
| |
71 EContact *contact = E_CONTACT(l->data); |
| |
72 const char *name; |
| |
73 GtkTreeIter iter; |
| |
74 |
| |
75 name = e_contact_get_const(contact, E_CONTACT_FULL_NAME); |
| |
76 |
| |
77 if (text != NULL && *text != '\0' && name != NULL && |
| |
78 g_ascii_strncasecmp(name, text, strlen(text))) |
| |
79 { |
| |
80 continue; |
| |
81 } |
| |
82 |
| |
83 gtk_list_store_append(dialog->model, &iter); |
| |
84 |
| |
85 gtk_list_store_set(dialog->model, &iter, |
| |
86 COLUMN_NAME, name, |
| |
87 COLUMN_DATA, contact, |
| |
88 -1); |
| |
89 } |
| |
90 } |
| |
91 |
| |
92 static void |
| |
93 clear_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog) |
| |
94 { |
| |
95 static gboolean lock = FALSE; |
| |
96 |
| |
97 if (lock) |
| |
98 return; |
| |
99 |
| |
100 lock = TRUE; |
| |
101 gtk_entry_set_text(GTK_ENTRY(dialog->search_field), ""); |
| |
102 lock = FALSE; |
| |
103 } |
| |
104 |
| |
105 static void |
| |
106 selected_cb(GtkTreeSelection *sel, GevoAssociateBuddyDialog *dialog) |
| |
107 { |
| |
108 GtkTreeSelection *selection; |
| |
109 |
| |
110 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
111 gtk_widget_set_sensitive(dialog->assoc_button, |
| |
112 gtk_tree_selection_get_selected(selection, NULL, NULL)); |
| |
113 } |
| |
114 |
| |
115 static void |
| |
116 add_columns(GevoAssociateBuddyDialog *dialog) |
| |
117 { |
| |
118 GtkCellRenderer *renderer; |
| |
119 GtkTreeViewColumn *column; |
| |
120 |
| |
121 /* Name column */ |
| |
122 column = gtk_tree_view_column_new(); |
| |
123 gtk_tree_view_column_set_title(column, _("Name")); |
| |
124 gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1); |
| |
125 gtk_tree_view_column_set_sort_column_id(column, COLUMN_NAME); |
| |
126 |
| |
127 renderer = gtk_cell_renderer_text_new(); |
| |
128 gtk_tree_view_column_pack_start(column, renderer, TRUE); |
| |
129 gtk_tree_view_column_add_attribute(column, renderer, |
| |
130 "text", COLUMN_NAME); |
| |
131 } |
| |
132 |
| |
133 static void |
| |
134 populate_treeview(GevoAssociateBuddyDialog *dialog, const gchar *uri) |
| |
135 { |
| |
136 EBook *book; |
| |
137 EBookQuery *query; |
| |
138 const char *prpl_id; |
| |
139 gboolean status; |
| |
140 GList *cards, *c; |
| |
141 |
| |
142 if (dialog->book != NULL) |
| |
143 { |
| |
144 g_object_unref(dialog->book); |
| |
145 dialog->book = NULL; |
| |
146 } |
| |
147 |
| |
148 if (dialog->contacts != NULL) |
| |
149 { |
| |
150 g_list_foreach(dialog->contacts, (GFunc) g_object_unref, NULL); |
| |
151 g_list_free(dialog->contacts); |
| |
152 dialog->contacts = NULL; |
| |
153 } |
| |
154 |
| |
155 gtk_list_store_clear(dialog->model); |
| |
156 |
| |
157 if (!gevo_load_addressbook(uri, &book, NULL)) |
| |
158 { |
| |
159 gaim_debug_error("evolution", |
| |
160 "Error retrieving addressbook\n"); |
| |
161 |
| |
162 return; |
| |
163 } |
| |
164 |
| |
165 query = e_book_query_field_exists(E_CONTACT_FULL_NAME); |
| |
166 |
| |
167 if (query == NULL) |
| |
168 { |
| |
169 gaim_debug_error("evolution", "Error in creating query\n"); |
| |
170 |
| |
171 g_object_unref(book); |
| |
172 |
| |
173 return; |
| |
174 } |
| |
175 |
| |
176 status = e_book_get_contacts(book, query, &cards, NULL); |
| |
177 |
| |
178 e_book_query_unref(query); |
| |
179 |
| |
180 if (!status) |
| |
181 { |
| |
182 gaim_debug_error("evolution", "Error %d in getting card list\n", |
| |
183 status); |
| |
184 |
| |
185 g_object_unref(book); |
| |
186 |
| |
187 return; |
| |
188 } |
| |
189 |
| |
190 prpl_id = gaim_account_get_protocol_id(dialog->buddy->account); |
| |
191 |
| |
192 for (c = cards; c != NULL; c = c->next) |
| |
193 { |
| |
194 EContact *contact = E_CONTACT(c->data); |
| |
195 const char *name; |
| |
196 GtkTreeIter iter; |
| |
197 EContactField protocol_field = 0; |
| |
198 |
| |
199 name = e_contact_get_const(contact, E_CONTACT_FULL_NAME); |
| |
200 |
| |
201 gtk_list_store_append(dialog->model, &iter); |
| |
202 |
| |
203 gtk_list_store_set(dialog->model, &iter, |
| |
204 COLUMN_NAME, name, |
| |
205 COLUMN_DATA, contact, |
| |
206 -1); |
| |
207 |
| |
208 /* See if this user has the buddy in its list. */ |
| |
209 protocol_field = gevo_prpl_get_field(dialog->buddy->account, |
| |
210 dialog->buddy); |
| |
211 |
| |
212 if (protocol_field > 0) |
| |
213 { |
| |
214 GList *ims, *l; |
| |
215 |
| |
216 ims = e_contact_get(contact, protocol_field); |
| |
217 |
| |
218 for (l = ims; l != NULL; l = l->next) |
| |
219 { |
| |
220 if (!strcmp(l->data, dialog->buddy->name)) |
| |
221 { |
| |
222 GtkTreeSelection *selection; |
| |
223 |
| |
224 /* This is it. Select it. */ |
| |
225 selection = gtk_tree_view_get_selection( |
| |
226 GTK_TREE_VIEW(dialog->treeview)); |
| |
227 |
| |
228 gtk_tree_selection_select_iter(selection, &iter); |
| |
229 break; |
| |
230 } |
| |
231 } |
| |
232 } |
| |
233 } |
| |
234 |
| |
235 dialog->contacts = cards; |
| |
236 dialog->book = book; |
| |
237 } |
| |
238 |
| |
239 static void |
| |
240 addrbook_change_cb(GtkComboBox *combo, GevoAssociateBuddyDialog *dialog) |
| |
241 { |
| |
242 GtkTreeIter iter; |
| |
243 const char *esource_uri; |
| |
244 |
| |
245 if (!gtk_combo_box_get_active_iter(combo, &iter)) |
| |
246 return; |
| |
247 |
| |
248 gtk_tree_model_get(GTK_TREE_MODEL(dialog->addrbooks), &iter, |
| |
249 ADDRBOOK_COLUMN_URI, &esource_uri, |
| |
250 -1); |
| |
251 |
| |
252 populate_treeview(dialog, esource_uri); |
| |
253 } |
| |
254 |
| |
255 static void |
| |
256 new_person_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog) |
| |
257 { |
| |
258 gevo_new_person_dialog_show(dialog->book, NULL, dialog->buddy->account, |
| |
259 dialog->buddy->name, NULL, dialog->buddy, |
| |
260 TRUE); |
| |
261 |
| |
262 delete_win_cb(NULL, NULL, dialog); |
| |
263 } |
| |
264 |
| |
265 static void |
| |
266 cancel_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog) |
| |
267 { |
| |
268 delete_win_cb(NULL, NULL, dialog); |
| |
269 } |
| |
270 |
| |
271 static void |
| |
272 assoc_buddy_cb(GtkWidget *w, GevoAssociateBuddyDialog *dialog) |
| |
273 { |
| |
274 GtkTreeSelection *selection; |
| |
275 GtkTreeIter iter; |
| |
276 GList *list; |
| |
277 const char *fullname; |
| |
278 EContactField protocol_field; |
| |
279 EContact *contact; |
| |
280 |
| |
281 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
282 |
| |
283 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) |
| |
284 return; |
| |
285 |
| |
286 gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, |
| |
287 COLUMN_NAME, &fullname, |
| |
288 COLUMN_DATA, &contact, |
| |
289 -1); |
| |
290 |
| |
291 protocol_field = gevo_prpl_get_field(dialog->buddy->account, dialog->buddy); |
| |
292 |
| |
293 if (protocol_field == 0) |
| |
294 return; /* XXX */ |
| |
295 |
| |
296 list = e_contact_get(contact, protocol_field); |
| |
297 list = g_list_append(list, g_strdup(dialog->buddy->name)); |
| |
298 |
| |
299 e_contact_set(contact, protocol_field, list); |
| |
300 |
| |
301 if (!e_book_commit_contact(dialog->book, contact, NULL)) |
| |
302 gaim_debug_error("evolution", "Error adding contact to book\n"); |
| |
303 |
| |
304 /* Free the list. */ |
| |
305 g_list_foreach(list, (GFunc)g_free, NULL); |
| |
306 g_list_free(list); |
| |
307 |
| |
308 delete_win_cb(NULL, NULL, dialog); |
| |
309 } |
| |
310 |
| |
311 GevoAssociateBuddyDialog * |
| |
312 gevo_associate_buddy_dialog_new(GaimBuddy *buddy) |
| |
313 { |
| |
314 GevoAssociateBuddyDialog *dialog; |
| |
315 GtkWidget *button; |
| |
316 GtkWidget *sw; |
| |
317 GtkWidget *label; |
| |
318 GtkWidget *vbox; |
| |
319 GtkWidget *hbox; |
| |
320 GtkWidget *bbox; |
| |
321 GtkWidget *sep; |
| |
322 GtkWidget *expander; |
| |
323 GtkTreeSelection *selection; |
| |
324 GtkCellRenderer *cell; |
| |
325 |
| |
326 g_return_val_if_fail(buddy != NULL, NULL); |
| |
327 |
| |
328 dialog = g_new0(GevoAssociateBuddyDialog, 1); |
| |
329 |
| |
330 dialog->buddy = buddy; |
| |
331 |
| |
332 dialog->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| |
333 gtk_window_set_role(GTK_WINDOW(dialog->win), "assoc_buddy"); |
| |
334 gtk_container_set_border_width(GTK_CONTAINER(dialog->win), 12); |
| |
335 |
| |
336 g_signal_connect(G_OBJECT(dialog->win), "delete_event", |
| |
337 G_CALLBACK(delete_win_cb), dialog); |
| |
338 |
| |
339 /* Setup the vbox */ |
| |
340 vbox = gtk_vbox_new(FALSE, 12); |
| |
341 gtk_container_add(GTK_CONTAINER(dialog->win), vbox); |
| |
342 gtk_widget_show(vbox); |
| |
343 |
| |
344 /* Add the label. */ |
| |
345 label = gtk_label_new(_("Select a person from your address book to " |
| |
346 "add this buddy to, or create a new person.")); |
| |
347 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
348 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
| |
349 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0); |
| |
350 gtk_widget_show(label); |
| |
351 |
| |
352 /* Add the search hbox */ |
| |
353 hbox = gtk_hbox_new(FALSE, 6); |
| |
354 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); |
| |
355 gtk_widget_show(hbox); |
| |
356 |
| |
357 /* "Search" */ |
| |
358 label = gtk_label_new(_("Search")); |
| |
359 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
360 gtk_widget_show(label); |
| |
361 |
| |
362 /* Addressbooks */ |
| |
363 dialog->addrbooks = gevo_addrbooks_model_new(); |
| |
364 |
| |
365 dialog->addrbooks_combo = gtk_combo_box_new_with_model(dialog->addrbooks); |
| |
366 cell = gtk_cell_renderer_text_new(); |
| |
367 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dialog->addrbooks_combo), |
| |
368 cell, TRUE); |
| |
369 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dialog->addrbooks_combo), |
| |
370 cell, |
| |
371 "text", ADDRBOOK_COLUMN_NAME, |
| |
372 NULL); |
| |
373 gtk_box_pack_start(GTK_BOX(hbox), dialog->addrbooks_combo, FALSE, FALSE, 0); |
| |
374 gtk_widget_show(dialog->addrbooks_combo); |
| |
375 |
| |
376 |
| |
377 /* Search field */ |
| |
378 dialog->search_field = gtk_entry_new(); |
| |
379 gtk_box_pack_start(GTK_BOX(hbox), dialog->search_field, TRUE, TRUE, 0); |
| |
380 gtk_widget_show(dialog->search_field); |
| |
381 |
| |
382 g_signal_connect(G_OBJECT(dialog->search_field), "changed", |
| |
383 G_CALLBACK(search_changed_cb), dialog); |
| |
384 |
| |
385 /* Clear button */ |
| |
386 button = gtk_button_new_from_stock(GTK_STOCK_CLEAR); |
| |
387 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); |
| |
388 gtk_widget_show(button); |
| |
389 |
| |
390 g_signal_connect(G_OBJECT(button), "clicked", |
| |
391 G_CALLBACK(clear_cb), dialog); |
| |
392 |
| |
393 /* Scrolled Window */ |
| |
394 sw = gtk_scrolled_window_new(0, 0); |
| |
395 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), |
| |
396 GTK_POLICY_AUTOMATIC, |
| |
397 GTK_POLICY_ALWAYS); |
| |
398 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), |
| |
399 GTK_SHADOW_IN); |
| |
400 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); |
| |
401 gtk_widget_show(sw); |
| |
402 |
| |
403 /* Create the list model for the treeview. */ |
| |
404 dialog->model = gtk_list_store_new(NUM_COLUMNS, |
| |
405 G_TYPE_STRING, G_TYPE_POINTER); |
| |
406 |
| |
407 /* Now for the treeview */ |
| |
408 dialog->treeview = gtk_tree_view_new_with_model( |
| |
409 GTK_TREE_MODEL(dialog->model)); |
| |
410 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->treeview), TRUE); |
| |
411 gtk_container_add(GTK_CONTAINER(sw), dialog->treeview); |
| |
412 gtk_widget_show(dialog->treeview); |
| |
413 |
| |
414 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); |
| |
415 |
| |
416 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); |
| |
417 |
| |
418 g_signal_connect(G_OBJECT(selection), "changed", |
| |
419 G_CALLBACK(selected_cb), dialog); |
| |
420 |
| |
421 add_columns(dialog); |
| |
422 |
| |
423 /* |
| |
424 * Catch addressbook selection and populate treeview with the first |
| |
425 * addressbook |
| |
426 */ |
| |
427 gevo_addrbooks_model_populate( dialog->addrbooks ); |
| |
428 g_signal_connect(G_OBJECT(dialog->addrbooks_combo), "changed", |
| |
429 G_CALLBACK(addrbook_change_cb), dialog); |
| |
430 gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->addrbooks_combo), 0); |
| |
431 |
| |
432 /* Add the expander */ |
| |
433 expander = gtk_expander_new_with_mnemonic(_("User _details")); |
| |
434 gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0); |
| |
435 gtk_widget_show(expander); |
| |
436 |
| |
437 /* |
| |
438 * User details |
| |
439 */ |
| |
440 |
| |
441 /* Scrolled Window */ |
| |
442 sw = gtk_scrolled_window_new(0, 0); |
| |
443 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), |
| |
444 GTK_POLICY_NEVER, |
| |
445 GTK_POLICY_ALWAYS); |
| |
446 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), |
| |
447 GTK_SHADOW_IN); |
| |
448 gtk_container_add(GTK_CONTAINER(expander), sw); |
| |
449 gtk_widget_show(sw); |
| |
450 |
| |
451 /* Textview */ |
| |
452 dialog->imhtml = gtk_imhtml_new(NULL, NULL); |
| |
453 gtk_container_add(GTK_CONTAINER(sw), dialog->imhtml); |
| |
454 gtk_widget_show(dialog->imhtml); |
| |
455 |
| |
456 /* Separator. */ |
| |
457 sep = gtk_hseparator_new(); |
| |
458 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); |
| |
459 gtk_widget_show(sep); |
| |
460 |
| |
461 /* Button box */ |
| |
462 bbox = gtk_hbutton_box_new(); |
| |
463 gtk_box_set_spacing(GTK_BOX(bbox), 6); |
| |
464 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); |
| |
465 gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0); |
| |
466 gtk_widget_show(bbox); |
| |
467 |
| |
468 /* "New Person" button */ |
| |
469 button = gaim_pixbuf_button_from_stock(_("New Person"), GTK_STOCK_NEW, |
| |
470 GAIM_BUTTON_HORIZONTAL); |
| |
471 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
472 gtk_widget_show(button); |
| |
473 |
| |
474 g_signal_connect(G_OBJECT(button), "clicked", |
| |
475 G_CALLBACK(new_person_cb), dialog); |
| |
476 |
| |
477 /* "Cancel" button */ |
| |
478 button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); |
| |
479 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
480 gtk_widget_show(button); |
| |
481 |
| |
482 g_signal_connect(G_OBJECT(button), "clicked", |
| |
483 G_CALLBACK(cancel_cb), dialog); |
| |
484 |
| |
485 /* "Associate Buddy" button */ |
| |
486 button = gaim_pixbuf_button_from_stock(_("_Associate Buddy"), |
| |
487 GTK_STOCK_APPLY, |
| |
488 GAIM_BUTTON_HORIZONTAL); |
| |
489 dialog->assoc_button = button; |
| |
490 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
| |
491 gtk_widget_set_sensitive(button, FALSE); |
| |
492 gtk_widget_show(button); |
| |
493 |
| |
494 g_signal_connect(G_OBJECT(button), "clicked", |
| |
495 G_CALLBACK(assoc_buddy_cb), dialog); |
| |
496 |
| |
497 /* Show it. */ |
| |
498 gtk_widget_show(dialog->win); |
| |
499 |
| |
500 return dialog; |
| |
501 } |