| |
1 /* |
| |
2 * @file gtkblist.c GTK+ BuddyList API |
| |
3 * @ingroup gtkui |
| |
4 * |
| |
5 * pidgin |
| |
6 * |
| |
7 * Pidgin is the legal property of its developers, whose names are too numerous |
| |
8 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
9 * source distribution. |
| |
10 * |
| |
11 * This program is free software; you can redistribute it and/or modify |
| |
12 * it under the terms of the GNU General Public License as published by |
| |
13 * the Free Software Foundation; either version 2 of the License, or |
| |
14 * (at your option) any later version. |
| |
15 * |
| |
16 * This program is distributed in the hope that it will be useful, |
| |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
19 * GNU General Public License for more details. |
| |
20 * |
| |
21 * You should have received a copy of the GNU General Public License |
| |
22 * along with this program; if not, write to the Free Software |
| |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
24 * |
| |
25 */ |
| |
26 #include "internal.h" |
| |
27 #include "pidgin.h" |
| |
28 |
| |
29 #include "account.h" |
| |
30 #include "connection.h" |
| |
31 #include "core.h" |
| |
32 #include "debug.h" |
| |
33 #include "notify.h" |
| |
34 #include "prpl.h" |
| |
35 #include "prefs.h" |
| |
36 #include "plugin.h" |
| |
37 #include "request.h" |
| |
38 #include "signals.h" |
| |
39 #include "pidginstock.h" |
| |
40 #include "util.h" |
| |
41 |
| |
42 #include "gtkaccount.h" |
| |
43 #include "gtkblist.h" |
| |
44 #include "gtkcellrendererexpander.h" |
| |
45 #include "gtkconv.h" |
| |
46 #include "gtkdebug.h" |
| |
47 #include "gtkdialogs.h" |
| |
48 #include "gtkft.h" |
| |
49 #include "gtklog.h" |
| |
50 #include "gtkmenutray.h" |
| |
51 #include "gtkpounce.h" |
| |
52 #include "gtkplugin.h" |
| |
53 #include "gtkprefs.h" |
| |
54 #include "gtkprivacy.h" |
| |
55 #include "gtkroomlist.h" |
| |
56 #include "gtkstatusbox.h" |
| |
57 #include "gtkscrollbook.h" |
| |
58 #include "gtkutils.h" |
| |
59 |
| |
60 #include <gdk/gdkkeysyms.h> |
| |
61 #include <gtk/gtk.h> |
| |
62 #include <gdk/gdk.h> |
| |
63 |
| |
64 #define HEADLINE_CLOSE_SIZE 12 |
| |
65 |
| |
66 typedef struct |
| |
67 { |
| |
68 PurpleAccount *account; |
| |
69 |
| |
70 GtkWidget *window; |
| |
71 GtkWidget *combo; |
| |
72 GtkWidget *entry; |
| |
73 GtkWidget *entry_for_alias; |
| |
74 GtkWidget *account_box; |
| |
75 |
| |
76 } PidginAddBuddyData; |
| |
77 |
| |
78 typedef struct |
| |
79 { |
| |
80 PurpleAccount *account; |
| |
81 gchar *default_chat_name; |
| |
82 |
| |
83 GtkWidget *window; |
| |
84 GtkWidget *account_menu; |
| |
85 GtkWidget *alias_entry; |
| |
86 GtkWidget *group_combo; |
| |
87 GtkWidget *entries_box; |
| |
88 GtkSizeGroup *sg; |
| |
89 |
| |
90 GList *entries; |
| |
91 |
| |
92 } PidginAddChatData; |
| |
93 |
| |
94 typedef struct |
| |
95 { |
| |
96 PurpleAccount *account; |
| |
97 |
| |
98 GtkWidget *window; |
| |
99 GtkWidget *account_menu; |
| |
100 GtkWidget *entries_box; |
| |
101 GtkSizeGroup *sg; |
| |
102 |
| |
103 GList *entries; |
| |
104 } PidginJoinChatData; |
| |
105 |
| |
106 |
| |
107 static GtkWidget *accountmenu = NULL; |
| |
108 |
| |
109 static guint visibility_manager_count = 0; |
| |
110 static gboolean gtk_blist_obscured = FALSE; |
| |
111 |
| |
112 static GList *pidgin_blist_sort_methods = NULL; |
| |
113 static struct pidgin_blist_sort_method *current_sort_method = NULL; |
| |
114 static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); |
| |
115 |
| |
116 /* The functions we use for sorting aren't available in gtk 2.0.x, and |
| |
117 * segfault in 2.2.0. 2.2.1 is known to work, so I'll require that */ |
| |
118 #if GTK_CHECK_VERSION(2,2,1) |
| |
119 static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); |
| |
120 static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); |
| |
121 static void sort_method_log(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter); |
| |
122 #endif |
| |
123 static PidginBuddyList *gtkblist = NULL; |
| |
124 |
| |
125 static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list); |
| |
126 static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange); |
| |
127 static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data); |
| |
128 static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node); |
| |
129 static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node); |
| |
130 static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node); |
| |
131 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full); |
| |
132 static const char *item_factory_translate_func (const char *path, gpointer func_data); |
| |
133 static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter); |
| |
134 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); |
| |
135 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); |
| |
136 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); |
| |
137 |
| |
138 static void pidgin_blist_tooltip_destroy(void); |
| |
139 |
| |
140 struct _pidgin_blist_node { |
| |
141 GtkTreeRowReference *row; |
| |
142 gboolean contact_expanded; |
| |
143 gboolean recent_signonoff; |
| |
144 gint recent_signonoff_timer; |
| |
145 }; |
| |
146 |
| |
147 static char dim_grey_string[8] = ""; |
| |
148 static char *dim_grey() |
| |
149 { |
| |
150 if (!gtkblist) |
| |
151 return "dim grey"; |
| |
152 if (!dim_grey_string[0]) { |
| |
153 GtkStyle *style = gtk_widget_get_style(gtkblist->treeview); |
| |
154 snprintf(dim_grey_string, sizeof(dim_grey_string), "#%02x%02x%02x", |
| |
155 style->text_aa[GTK_STATE_NORMAL].red >> 8, |
| |
156 style->text_aa[GTK_STATE_NORMAL].green >> 8, |
| |
157 style->text_aa[GTK_STATE_NORMAL].blue >> 8); |
| |
158 } |
| |
159 return dim_grey_string; |
| |
160 } |
| |
161 |
| |
162 /*************************************************** |
| |
163 * Callbacks * |
| |
164 ***************************************************/ |
| |
165 static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data) |
| |
166 { |
| |
167 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) |
| |
168 gtk_blist_obscured = TRUE; |
| |
169 else if (gtk_blist_obscured) { |
| |
170 gtk_blist_obscured = FALSE; |
| |
171 pidgin_blist_refresh_timer(purple_get_blist()); |
| |
172 } |
| |
173 |
| |
174 /* continue to handle event normally */ |
| |
175 return FALSE; |
| |
176 } |
| |
177 |
| |
178 static gboolean gtk_blist_window_state_cb(GtkWidget *w, GdkEventWindowState *event, gpointer data) |
| |
179 { |
| |
180 if(event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN) { |
| |
181 if(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) |
| |
182 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE); |
| |
183 else { |
| |
184 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", TRUE); |
| |
185 pidgin_blist_refresh_timer(purple_get_blist()); |
| |
186 } |
| |
187 } |
| |
188 |
| |
189 if(event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { |
| |
190 if(event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) |
| |
191 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", TRUE); |
| |
192 else |
| |
193 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", FALSE); |
| |
194 } |
| |
195 |
| |
196 /* Refresh gtkblist if un-iconifying */ |
| |
197 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED){ |
| |
198 if (!(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) |
| |
199 pidgin_blist_refresh_timer(purple_get_blist()); |
| |
200 } |
| |
201 |
| |
202 return FALSE; |
| |
203 } |
| |
204 |
| |
205 static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) |
| |
206 { |
| |
207 if(visibility_manager_count) |
| |
208 purple_blist_set_visible(FALSE); |
| |
209 else |
| |
210 purple_core_quit(); |
| |
211 |
| |
212 /* we handle everything, event should not propogate further */ |
| |
213 return TRUE; |
| |
214 } |
| |
215 |
| |
216 static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) |
| |
217 { |
| |
218 /* unfortunately GdkEventConfigure ignores the window gravity, but * |
| |
219 * the only way we have of setting the position doesn't. we have to * |
| |
220 * call get_position because it does pay attention to the gravity. * |
| |
221 * this is inefficient and I agree it sucks, but it's more likely * |
| |
222 * to work correctly. - Robot101 */ |
| |
223 gint x, y; |
| |
224 |
| |
225 /* check for visibility because when we aren't visible, this will * |
| |
226 * give us bogus (0,0) coordinates. - xOr */ |
| |
227 if (GTK_WIDGET_VISIBLE(w)) |
| |
228 gtk_window_get_position(GTK_WINDOW(w), &x, &y); |
| |
229 else |
| |
230 return FALSE; /* carry on normally */ |
| |
231 |
| |
232 #ifdef _WIN32 |
| |
233 /* Workaround for GTK+ bug # 169811 - "configure_event" is fired |
| |
234 * when the window is being maximized */ |
| |
235 if (gdk_window_get_state(w->window) |
| |
236 & GDK_WINDOW_STATE_MAXIMIZED) { |
| |
237 return FALSE; |
| |
238 } |
| |
239 #endif |
| |
240 |
| |
241 /* don't save if nothing changed */ |
| |
242 if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/x") && |
| |
243 y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/y") && |
| |
244 event->width == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width") && |
| |
245 event->height == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/height")) { |
| |
246 |
| |
247 return FALSE; /* carry on normally */ |
| |
248 } |
| |
249 |
| |
250 /* don't save off-screen positioning */ |
| |
251 if (x + event->width < 0 || |
| |
252 y + event->height < 0 || |
| |
253 x > gdk_screen_width() || |
| |
254 y > gdk_screen_height()) { |
| |
255 |
| |
256 return FALSE; /* carry on normally */ |
| |
257 } |
| |
258 |
| |
259 /* ignore changes when maximized */ |
| |
260 if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized")) |
| |
261 return FALSE; |
| |
262 |
| |
263 /* store the position */ |
| |
264 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/x", x); |
| |
265 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/y", y); |
| |
266 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/width", event->width); |
| |
267 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/height", event->height); |
| |
268 |
| |
269 gtk_widget_set_size_request(gtkblist->headline_label, |
| |
270 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25,-1); |
| |
271 /* continue to handle event normally */ |
| |
272 return FALSE; |
| |
273 } |
| |
274 |
| |
275 static void gtk_blist_menu_info_cb(GtkWidget *w, PurpleBuddy *b) |
| |
276 { |
| |
277 serv_get_info(b->account->gc, b->name); |
| |
278 } |
| |
279 |
| |
280 static void gtk_blist_menu_im_cb(GtkWidget *w, PurpleBuddy *b) |
| |
281 { |
| |
282 pidgindialogs_im_with_user(b->account, b->name); |
| |
283 } |
| |
284 |
| |
285 static void gtk_blist_menu_send_file_cb(GtkWidget *w, PurpleBuddy *b) |
| |
286 { |
| |
287 serv_send_file(b->account->gc, b->name, NULL); |
| |
288 } |
| |
289 |
| |
290 static void gtk_blist_menu_autojoin_cb(GtkWidget *w, PurpleChat *chat) |
| |
291 { |
| |
292 purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", |
| |
293 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))); |
| |
294 } |
| |
295 |
| |
296 static void gtk_blist_join_chat(PurpleChat *chat) |
| |
297 { |
| |
298 PurpleConversation *conv; |
| |
299 |
| |
300 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, |
| |
301 purple_chat_get_name(chat), |
| |
302 chat->account); |
| |
303 |
| |
304 if (conv != NULL) |
| |
305 purple_conversation_present(conv); |
| |
306 |
| |
307 serv_join_chat(chat->account->gc, chat->components); |
| |
308 } |
| |
309 |
| |
310 static void gtk_blist_menu_join_cb(GtkWidget *w, PurpleChat *chat) |
| |
311 { |
| |
312 gtk_blist_join_chat(chat); |
| |
313 } |
| |
314 |
| |
315 static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1, |
| |
316 char *arg2, gpointer nada) |
| |
317 { |
| |
318 GtkTreeIter iter; |
| |
319 GtkTreePath *path; |
| |
320 GValue val; |
| |
321 PurpleBlistNode *node; |
| |
322 PurpleGroup *dest; |
| |
323 |
| |
324 path = gtk_tree_path_new_from_string (arg1); |
| |
325 gtk_tree_model_get_iter (GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); |
| |
326 gtk_tree_path_free (path); |
| |
327 val.g_type = 0; |
| |
328 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
329 node = g_value_get_pointer(&val); |
| |
330 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), TRUE); |
| |
331 g_object_set(G_OBJECT(gtkblist->text_rend), "editable", FALSE, NULL); |
| |
332 |
| |
333 switch (node->type) |
| |
334 { |
| |
335 case PURPLE_BLIST_CONTACT_NODE: |
| |
336 { |
| |
337 PurpleContact *contact = (PurpleContact *)node; |
| |
338 struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
339 |
| |
340 if (contact->alias || gtknode->contact_expanded) |
| |
341 purple_blist_alias_contact(contact, arg2); |
| |
342 else |
| |
343 { |
| |
344 PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact); |
| |
345 purple_blist_alias_buddy(buddy, arg2); |
| |
346 serv_alias_buddy(buddy); |
| |
347 } |
| |
348 } |
| |
349 break; |
| |
350 |
| |
351 case PURPLE_BLIST_BUDDY_NODE: |
| |
352 purple_blist_alias_buddy((PurpleBuddy*)node, arg2); |
| |
353 serv_alias_buddy((PurpleBuddy *)node); |
| |
354 break; |
| |
355 case PURPLE_BLIST_GROUP_NODE: |
| |
356 dest = purple_find_group(arg2); |
| |
357 if (dest != NULL && strcmp(arg2, ((PurpleGroup*) node)->name)) { |
| |
358 pidgindialogs_merge_groups((PurpleGroup*) node, arg2); |
| |
359 } else |
| |
360 purple_blist_rename_group((PurpleGroup*)node, arg2); |
| |
361 break; |
| |
362 case PURPLE_BLIST_CHAT_NODE: |
| |
363 purple_blist_alias_chat((PurpleChat*)node, arg2); |
| |
364 break; |
| |
365 default: |
| |
366 break; |
| |
367 } |
| |
368 } |
| |
369 |
| |
370 static void gtk_blist_menu_alias_cb(GtkWidget *w, PurpleBlistNode *node) |
| |
371 { |
| |
372 GtkTreeIter iter; |
| |
373 GtkTreePath *path; |
| |
374 const char *text = NULL; |
| |
375 char *esc; |
| |
376 |
| |
377 if (!(get_iter_from_node(node, &iter))) { |
| |
378 /* This is either a bug, or the buddy is in a collapsed contact */ |
| |
379 node = node->parent; |
| |
380 if (!get_iter_from_node(node, &iter)) |
| |
381 /* Now it's definitely a bug */ |
| |
382 return; |
| |
383 } |
| |
384 |
| |
385 switch (node->type) { |
| |
386 case PURPLE_BLIST_BUDDY_NODE: |
| |
387 text = purple_buddy_get_alias((PurpleBuddy *)node); |
| |
388 break; |
| |
389 case PURPLE_BLIST_CONTACT_NODE: |
| |
390 text = purple_contact_get_alias((PurpleContact *)node); |
| |
391 break; |
| |
392 case PURPLE_BLIST_GROUP_NODE: |
| |
393 text = ((PurpleGroup *)node)->name; |
| |
394 break; |
| |
395 case PURPLE_BLIST_CHAT_NODE: |
| |
396 text = purple_chat_get_name((PurpleChat *)node); |
| |
397 break; |
| |
398 default: |
| |
399 g_return_if_reached(); |
| |
400 } |
| |
401 |
| |
402 esc = g_markup_escape_text(text, -1); |
| |
403 gtk_tree_store_set(gtkblist->treemodel, &iter, NAME_COLUMN, esc, -1); |
| |
404 g_free(esc); |
| |
405 |
| |
406 path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); |
| |
407 g_object_set(G_OBJECT(gtkblist->text_rend), "editable", TRUE, NULL); |
| |
408 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), FALSE); |
| |
409 gtk_widget_grab_focus(gtkblist->treeview); |
| |
410 #if GTK_CHECK_VERSION(2,2,0) |
| |
411 gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(gtkblist->treeview), path, |
| |
412 gtkblist->text_column, gtkblist->text_rend, TRUE); |
| |
413 #else |
| |
414 gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkblist->treeview), path, gtkblist->text_column, TRUE); |
| |
415 #endif |
| |
416 gtk_tree_path_free(path); |
| |
417 } |
| |
418 |
| |
419 static void gtk_blist_menu_bp_cb(GtkWidget *w, PurpleBuddy *b) |
| |
420 { |
| |
421 pidgin_pounce_editor_show(b->account, b->name, NULL); |
| |
422 } |
| |
423 |
| |
424 static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node) |
| |
425 { |
| |
426 PurpleLogType type; |
| |
427 PurpleAccount *account; |
| |
428 char *name = NULL; |
| |
429 |
| |
430 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
431 |
| |
432 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
433 PurpleBuddy *b = (PurpleBuddy*) node; |
| |
434 type = PURPLE_LOG_IM; |
| |
435 name = g_strdup(b->name); |
| |
436 account = b->account; |
| |
437 } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
438 PurpleChat *c = (PurpleChat*) node; |
| |
439 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
440 type = PURPLE_LOG_CHAT; |
| |
441 account = c->account; |
| |
442 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); |
| |
443 if (prpl_info && prpl_info->get_chat_name) { |
| |
444 name = prpl_info->get_chat_name(c->components); |
| |
445 } |
| |
446 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
447 pidgin_log_show_contact((PurpleContact *)node); |
| |
448 pidgin_clear_cursor(gtkblist->window); |
| |
449 return; |
| |
450 } else { |
| |
451 pidgin_clear_cursor(gtkblist->window); |
| |
452 |
| |
453 /* This callback should not have been registered for a node |
| |
454 * that doesn't match the type of one of the blocks above. */ |
| |
455 g_return_if_reached(); |
| |
456 } |
| |
457 |
| |
458 if (name && account) { |
| |
459 pidgin_log_show(type, name, account); |
| |
460 g_free(name); |
| |
461 |
| |
462 pidgin_clear_cursor(gtkblist->window); |
| |
463 } |
| |
464 } |
| |
465 |
| |
466 static void gtk_blist_show_systemlog_cb() |
| |
467 { |
| |
468 pidgin_syslog_show(); |
| |
469 } |
| |
470 |
| |
471 static void gtk_blist_show_onlinehelp_cb() |
| |
472 { |
| |
473 purple_notify_uri(NULL, PURPLE_WEBSITE "documentation.php"); |
| |
474 } |
| |
475 |
| |
476 static void |
| |
477 do_join_chat(PidginJoinChatData *data) |
| |
478 { |
| |
479 if (data) |
| |
480 { |
| |
481 GHashTable *components = |
| |
482 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
| |
483 GList *tmp; |
| |
484 PurpleChat *chat; |
| |
485 |
| |
486 for (tmp = data->entries; tmp != NULL; tmp = tmp->next) |
| |
487 { |
| |
488 if (g_object_get_data(tmp->data, "is_spin")) |
| |
489 { |
| |
490 g_hash_table_replace(components, |
| |
491 g_strdup(g_object_get_data(tmp->data, "identifier")), |
| |
492 g_strdup_printf("%d", |
| |
493 gtk_spin_button_get_value_as_int(tmp->data))); |
| |
494 } |
| |
495 else |
| |
496 { |
| |
497 g_hash_table_replace(components, |
| |
498 g_strdup(g_object_get_data(tmp->data, "identifier")), |
| |
499 g_strdup(gtk_entry_get_text(tmp->data))); |
| |
500 } |
| |
501 } |
| |
502 |
| |
503 chat = purple_chat_new(data->account, NULL, components); |
| |
504 gtk_blist_join_chat(chat); |
| |
505 purple_blist_remove_chat(chat); |
| |
506 } |
| |
507 } |
| |
508 |
| |
509 static void |
| |
510 do_joinchat(GtkWidget *dialog, int id, PidginJoinChatData *info) |
| |
511 { |
| |
512 switch(id) |
| |
513 { |
| |
514 case GTK_RESPONSE_OK: |
| |
515 do_join_chat(info); |
| |
516 |
| |
517 break; |
| |
518 } |
| |
519 |
| |
520 gtk_widget_destroy(GTK_WIDGET(dialog)); |
| |
521 g_list_free(info->entries); |
| |
522 g_free(info); |
| |
523 } |
| |
524 |
| |
525 /* |
| |
526 * Check the values of all the text entry boxes. If any required input |
| |
527 * strings are empty then don't allow the user to click on "OK." |
| |
528 */ |
| |
529 static void |
| |
530 joinchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data) |
| |
531 { |
| |
532 PidginJoinChatData *data; |
| |
533 GList *tmp; |
| |
534 const char *text; |
| |
535 gboolean required; |
| |
536 gboolean sensitive = TRUE; |
| |
537 |
| |
538 data = user_data; |
| |
539 |
| |
540 for (tmp = data->entries; tmp != NULL; tmp = tmp->next) |
| |
541 { |
| |
542 if (!g_object_get_data(tmp->data, "is_spin")) |
| |
543 { |
| |
544 required = GPOINTER_TO_INT(g_object_get_data(tmp->data, "required")); |
| |
545 text = gtk_entry_get_text(tmp->data); |
| |
546 if (required && (*text == '\0')) |
| |
547 sensitive = FALSE; |
| |
548 } |
| |
549 } |
| |
550 |
| |
551 gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive); |
| |
552 } |
| |
553 |
| |
554 static void |
| |
555 pidgin_blist_update_privacy_cb(PurpleBuddy *buddy) |
| |
556 { |
| |
557 pidgin_blist_update_buddy(purple_get_blist(), (PurpleBlistNode*)(buddy), TRUE); |
| |
558 } |
| |
559 |
| |
560 static void |
| |
561 rebuild_joinchat_entries(PidginJoinChatData *data) |
| |
562 { |
| |
563 PurpleConnection *gc; |
| |
564 GList *list = NULL, *tmp; |
| |
565 GHashTable *defaults = NULL; |
| |
566 struct proto_chat_entry *pce; |
| |
567 gboolean focus = TRUE; |
| |
568 |
| |
569 g_return_if_fail(data->account != NULL); |
| |
570 |
| |
571 gc = purple_account_get_connection(data->account); |
| |
572 |
| |
573 while ((tmp = gtk_container_get_children(GTK_CONTAINER(data->entries_box)))) |
| |
574 gtk_widget_destroy(tmp->data); |
| |
575 |
| |
576 g_list_free(data->entries); |
| |
577 data->entries = NULL; |
| |
578 |
| |
579 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) |
| |
580 list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc); |
| |
581 |
| |
582 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) |
| |
583 defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, NULL); |
| |
584 |
| |
585 for (tmp = list; tmp; tmp = tmp->next) |
| |
586 { |
| |
587 GtkWidget *label; |
| |
588 GtkWidget *rowbox; |
| |
589 GtkWidget *input; |
| |
590 |
| |
591 pce = tmp->data; |
| |
592 |
| |
593 rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
594 gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); |
| |
595 |
| |
596 label = gtk_label_new_with_mnemonic(pce->label); |
| |
597 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
598 gtk_size_group_add_widget(data->sg, label); |
| |
599 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
600 |
| |
601 if (pce->is_int) |
| |
602 { |
| |
603 GtkObject *adjust; |
| |
604 adjust = gtk_adjustment_new(pce->min, pce->min, pce->max, |
| |
605 1, 10, 10); |
| |
606 input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); |
| |
607 gtk_widget_set_size_request(input, 50, -1); |
| |
608 gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0); |
| |
609 } |
| |
610 else |
| |
611 { |
| |
612 char *value; |
| |
613 input = gtk_entry_new(); |
| |
614 gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE); |
| |
615 value = g_hash_table_lookup(defaults, pce->identifier); |
| |
616 if (value != NULL) |
| |
617 gtk_entry_set_text(GTK_ENTRY(input), value); |
| |
618 if (pce->secret) |
| |
619 { |
| |
620 gtk_entry_set_visibility(GTK_ENTRY(input), FALSE); |
| |
621 if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') |
| |
622 gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); |
| |
623 } |
| |
624 gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0); |
| |
625 g_signal_connect(G_OBJECT(input), "changed", |
| |
626 G_CALLBACK(joinchat_set_sensitive_if_input_cb), data); |
| |
627 } |
| |
628 |
| |
629 /* Do the following for any type of input widget */ |
| |
630 if (focus) |
| |
631 { |
| |
632 gtk_widget_grab_focus(input); |
| |
633 focus = FALSE; |
| |
634 } |
| |
635 gtk_label_set_mnemonic_widget(GTK_LABEL(label), input); |
| |
636 pidgin_set_accessible_label(input, label); |
| |
637 g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier); |
| |
638 g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int)); |
| |
639 g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required)); |
| |
640 data->entries = g_list_append(data->entries, input); |
| |
641 |
| |
642 g_free(pce); |
| |
643 } |
| |
644 |
| |
645 g_list_free(list); |
| |
646 g_hash_table_destroy(defaults); |
| |
647 |
| |
648 /* Set whether the "OK" button should be clickable initially */ |
| |
649 joinchat_set_sensitive_if_input_cb(NULL, data); |
| |
650 |
| |
651 gtk_widget_show_all(data->entries_box); |
| |
652 } |
| |
653 |
| |
654 static void |
| |
655 joinchat_select_account_cb(GObject *w, PurpleAccount *account, |
| |
656 PidginJoinChatData *data) |
| |
657 { |
| |
658 data->account = account; |
| |
659 rebuild_joinchat_entries(data); |
| |
660 } |
| |
661 |
| |
662 static gboolean |
| |
663 chat_account_filter_func(PurpleAccount *account) |
| |
664 { |
| |
665 PurpleConnection *gc = purple_account_get_connection(account); |
| |
666 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
667 |
| |
668 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); |
| |
669 |
| |
670 return (prpl_info->chat_info != NULL); |
| |
671 } |
| |
672 |
| |
673 gboolean |
| |
674 pidgin_blist_joinchat_is_showable() |
| |
675 { |
| |
676 GList *c; |
| |
677 PurpleConnection *gc; |
| |
678 |
| |
679 for (c = purple_connections_get_all(); c != NULL; c = c->next) { |
| |
680 gc = c->data; |
| |
681 |
| |
682 if (chat_account_filter_func(purple_connection_get_account(gc))) |
| |
683 return TRUE; |
| |
684 } |
| |
685 |
| |
686 return FALSE; |
| |
687 } |
| |
688 |
| |
689 void |
| |
690 pidgin_blist_joinchat_show(void) |
| |
691 { |
| |
692 GtkWidget *hbox, *vbox; |
| |
693 GtkWidget *rowbox; |
| |
694 GtkWidget *label; |
| |
695 PidginBuddyList *gtkblist; |
| |
696 GtkWidget *img = NULL; |
| |
697 PidginJoinChatData *data = NULL; |
| |
698 |
| |
699 gtkblist = PIDGIN_BLIST(purple_get_blist()); |
| |
700 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION, |
| |
701 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE)); |
| |
702 data = g_new0(PidginJoinChatData, 1); |
| |
703 |
| |
704 data->window = gtk_dialog_new_with_buttons(_("Join a Chat"), |
| |
705 NULL, GTK_DIALOG_NO_SEPARATOR, |
| |
706 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
| |
707 PIDGIN_STOCK_CHAT, GTK_RESPONSE_OK, NULL); |
| |
708 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); |
| |
709 gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE); |
| |
710 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); |
| |
711 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER); |
| |
712 gtk_container_set_border_width( |
| |
713 GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE); |
| |
714 gtk_window_set_role(GTK_WINDOW(data->window), "join_chat"); |
| |
715 |
| |
716 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
717 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); |
| |
718 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
| |
719 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
| |
720 |
| |
721 vbox = gtk_vbox_new(FALSE, 5); |
| |
722 gtk_container_set_border_width(GTK_CONTAINER(vbox), 0); |
| |
723 gtk_container_add(GTK_CONTAINER(hbox), vbox); |
| |
724 |
| |
725 label = gtk_label_new(_("Please enter the appropriate information " |
| |
726 "about the chat you would like to join.\n")); |
| |
727 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
728 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
| |
729 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
| |
730 |
| |
731 rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
732 gtk_box_pack_start(GTK_BOX(vbox), rowbox, TRUE, TRUE, 0); |
| |
733 |
| |
734 data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
| |
735 |
| |
736 label = gtk_label_new_with_mnemonic(_("_Account:")); |
| |
737 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
738 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
739 gtk_size_group_add_widget(data->sg, label); |
| |
740 |
| |
741 data->account_menu = pidgin_account_option_menu_new(NULL, FALSE, |
| |
742 G_CALLBACK(joinchat_select_account_cb), |
| |
743 chat_account_filter_func, data); |
| |
744 gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); |
| |
745 gtk_label_set_mnemonic_widget(GTK_LABEL(label), |
| |
746 GTK_WIDGET(data->account_menu)); |
| |
747 pidgin_set_accessible_label (data->account_menu, label); |
| |
748 |
| |
749 data->entries_box = gtk_vbox_new(FALSE, 5); |
| |
750 gtk_container_add(GTK_CONTAINER(vbox), data->entries_box); |
| |
751 gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); |
| |
752 |
| |
753 data->account = pidgin_account_option_menu_get_selected(data->account_menu); |
| |
754 |
| |
755 rebuild_joinchat_entries(data); |
| |
756 |
| |
757 g_signal_connect(G_OBJECT(data->window), "response", |
| |
758 G_CALLBACK(do_joinchat), data); |
| |
759 |
| |
760 g_object_unref(data->sg); |
| |
761 |
| |
762 gtk_widget_show_all(data->window); |
| |
763 } |
| |
764 |
| |
765 static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { |
| |
766 PurpleBlistNode *node; |
| |
767 GValue val; |
| |
768 |
| |
769 val.g_type = 0; |
| |
770 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); |
| |
771 |
| |
772 node = g_value_get_pointer(&val); |
| |
773 |
| |
774 if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
775 char *title; |
| |
776 |
| |
777 title = pidgin_get_group_title(node, TRUE); |
| |
778 |
| |
779 gtk_tree_store_set(gtkblist->treemodel, iter, |
| |
780 NAME_COLUMN, title, |
| |
781 -1); |
| |
782 |
| |
783 g_free(title); |
| |
784 |
| |
785 purple_blist_node_set_bool(node, "collapsed", FALSE); |
| |
786 } |
| |
787 } |
| |
788 |
| |
789 static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { |
| |
790 PurpleBlistNode *node; |
| |
791 GValue val; |
| |
792 |
| |
793 val.g_type = 0; |
| |
794 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); |
| |
795 |
| |
796 node = g_value_get_pointer(&val); |
| |
797 |
| |
798 if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
799 char *title; |
| |
800 |
| |
801 title = pidgin_get_group_title(node, FALSE); |
| |
802 |
| |
803 gtk_tree_store_set(gtkblist->treemodel, iter, |
| |
804 NAME_COLUMN, title, |
| |
805 -1); |
| |
806 |
| |
807 g_free(title); |
| |
808 |
| |
809 purple_blist_node_set_bool(node, "collapsed", TRUE); |
| |
810 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
811 pidgin_blist_collapse_contact_cb(NULL, node); |
| |
812 } |
| |
813 } |
| |
814 |
| |
815 static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { |
| |
816 PurpleBlistNode *node; |
| |
817 GtkTreeIter iter; |
| |
818 GValue val; |
| |
819 |
| |
820 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); |
| |
821 |
| |
822 val.g_type = 0; |
| |
823 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
824 node = g_value_get_pointer(&val); |
| |
825 |
| |
826 if(PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
827 PurpleBuddy *buddy; |
| |
828 |
| |
829 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
830 buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
831 else |
| |
832 buddy = (PurpleBuddy*)node; |
| |
833 |
| |
834 pidgindialogs_im_with_user(buddy->account, buddy->name); |
| |
835 } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
836 gtk_blist_join_chat((PurpleChat *)node); |
| |
837 } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
838 /* if (gtk_tree_view_row_expanded(tv, path)) |
| |
839 gtk_tree_view_collapse_row(tv, path); |
| |
840 else |
| |
841 gtk_tree_view_expand_row(tv,path,FALSE);*/ |
| |
842 } |
| |
843 } |
| |
844 |
| |
845 static void pidgin_blist_add_chat_cb() |
| |
846 { |
| |
847 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
848 GtkTreeIter iter; |
| |
849 PurpleBlistNode *node; |
| |
850 |
| |
851 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ |
| |
852 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); |
| |
853 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
854 purple_blist_request_add_chat(NULL, (PurpleGroup*)node->parent->parent, NULL, NULL); |
| |
855 if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) |
| |
856 purple_blist_request_add_chat(NULL, (PurpleGroup*)node->parent, NULL, NULL); |
| |
857 else if (PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
858 purple_blist_request_add_chat(NULL, (PurpleGroup*)node, NULL, NULL); |
| |
859 } |
| |
860 else { |
| |
861 purple_blist_request_add_chat(NULL, NULL, NULL, NULL); |
| |
862 } |
| |
863 } |
| |
864 |
| |
865 static void pidgin_blist_add_buddy_cb() |
| |
866 { |
| |
867 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
868 GtkTreeIter iter; |
| |
869 PurpleBlistNode *node; |
| |
870 |
| |
871 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ |
| |
872 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); |
| |
873 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
874 purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node->parent->parent)->name, |
| |
875 NULL); |
| |
876 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) |
| |
877 || PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
878 purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node->parent)->name, NULL); |
| |
879 } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
880 purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node)->name, NULL); |
| |
881 } |
| |
882 } |
| |
883 else { |
| |
884 purple_blist_request_add_buddy(NULL, NULL, NULL, NULL); |
| |
885 } |
| |
886 } |
| |
887 |
| |
888 static void |
| |
889 pidgin_blist_remove_cb (GtkWidget *w, PurpleBlistNode *node) |
| |
890 { |
| |
891 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
892 pidgindialogs_remove_buddy((PurpleBuddy*)node); |
| |
893 } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
894 pidgindialogs_remove_chat((PurpleChat*)node); |
| |
895 } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
896 pidgindialogs_remove_group((PurpleGroup*)node); |
| |
897 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
898 pidgindialogs_remove_contact((PurpleContact*)node); |
| |
899 } |
| |
900 } |
| |
901 |
| |
902 struct _expand { |
| |
903 GtkTreeView *treeview; |
| |
904 GtkTreePath *path; |
| |
905 PurpleBlistNode *node; |
| |
906 }; |
| |
907 |
| |
908 static gboolean |
| |
909 scroll_to_expanded_cell(gpointer data) |
| |
910 { |
| |
911 struct _expand *ex = data; |
| |
912 gtk_tree_view_scroll_to_cell(ex->treeview, ex->path, NULL, FALSE, 0, 0); |
| |
913 pidgin_blist_update_contact(NULL, ex->node); |
| |
914 |
| |
915 gtk_tree_path_free(ex->path); |
| |
916 g_free(ex); |
| |
917 |
| |
918 return FALSE; |
| |
919 } |
| |
920 |
| |
921 static void |
| |
922 pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node) |
| |
923 { |
| |
924 struct _pidgin_blist_node *gtknode; |
| |
925 GtkTreeIter iter, parent; |
| |
926 PurpleBlistNode *bnode; |
| |
927 GtkTreePath *path; |
| |
928 |
| |
929 if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
930 return; |
| |
931 |
| |
932 gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
933 |
| |
934 gtknode->contact_expanded = TRUE; |
| |
935 |
| |
936 for(bnode = node->child; bnode; bnode = bnode->next) { |
| |
937 pidgin_blist_update(NULL, bnode); |
| |
938 } |
| |
939 |
| |
940 /* This ensures that the bottom buddy is visible, i.e. not scrolled off the alignment */ |
| |
941 if (get_iter_from_node(node, &parent)) { |
| |
942 struct _expand *ex = g_new0(struct _expand, 1); |
| |
943 |
| |
944 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(gtkblist->treemodel), &iter, &parent, |
| |
945 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &parent) -1); |
| |
946 path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); |
| |
947 |
| |
948 /* Let the treeview draw so it knows where to scroll */ |
| |
949 ex->treeview = GTK_TREE_VIEW(gtkblist->treeview); |
| |
950 ex->path = path; |
| |
951 ex->node = node->child; |
| |
952 g_idle_add(scroll_to_expanded_cell, ex); |
| |
953 } |
| |
954 } |
| |
955 |
| |
956 static void |
| |
957 pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node) |
| |
958 { |
| |
959 PurpleBlistNode *bnode; |
| |
960 struct _pidgin_blist_node *gtknode; |
| |
961 |
| |
962 if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
963 return; |
| |
964 |
| |
965 gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
966 |
| |
967 gtknode->contact_expanded = FALSE; |
| |
968 |
| |
969 for(bnode = node->child; bnode; bnode = bnode->next) { |
| |
970 pidgin_blist_update(NULL, bnode); |
| |
971 } |
| |
972 } |
| |
973 |
| |
974 static void |
| |
975 toggle_privacy(GtkWidget *widget, PurpleBlistNode *node) |
| |
976 { |
| |
977 PurpleBuddy *buddy; |
| |
978 PurpleAccount *account; |
| |
979 gboolean permitted; |
| |
980 const char *name; |
| |
981 |
| |
982 if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
983 return; |
| |
984 |
| |
985 buddy = (PurpleBuddy *)node; |
| |
986 account = purple_buddy_get_account(buddy); |
| |
987 name = purple_buddy_get_name(buddy); |
| |
988 |
| |
989 permitted = purple_privacy_check(account, name); |
| |
990 |
| |
991 /* XXX: Perhaps ask whether to restore the previous lists where appropirate? */ |
| |
992 |
| |
993 if (permitted) |
| |
994 purple_privacy_deny(account, name, FALSE, FALSE); |
| |
995 else |
| |
996 purple_privacy_allow(account, name, FALSE, FALSE); |
| |
997 |
| |
998 pidgin_blist_update(purple_get_blist(), node); |
| |
999 } |
| |
1000 |
| |
1001 void pidgin_append_blist_node_privacy_menu(GtkWidget *menu, PurpleBlistNode *node) |
| |
1002 { |
| |
1003 PurpleBuddy *buddy = (PurpleBuddy *)node; |
| |
1004 PurpleAccount *account; |
| |
1005 gboolean permitted; |
| |
1006 |
| |
1007 account = purple_buddy_get_account(buddy); |
| |
1008 permitted = purple_privacy_check(account, purple_buddy_get_name(buddy)); |
| |
1009 |
| |
1010 pidgin_new_item_from_stock(menu, permitted ? _("_Block") : _("Un_block"), |
| |
1011 permitted ? PIDGIN_STOCK_TOOLBAR_BLOCK : PIDGIN_STOCK_TOOLBAR_UNBLOCK, G_CALLBACK(toggle_privacy), |
| |
1012 node, 0 ,0, NULL); |
| |
1013 } |
| |
1014 |
| |
1015 void |
| |
1016 pidgin_append_blist_node_proto_menu(GtkWidget *menu, PurpleConnection *gc, |
| |
1017 PurpleBlistNode *node) |
| |
1018 { |
| |
1019 GList *l, *ll; |
| |
1020 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); |
| |
1021 |
| |
1022 if(!prpl_info || !prpl_info->blist_node_menu) |
| |
1023 return; |
| |
1024 |
| |
1025 for(l = ll = prpl_info->blist_node_menu(node); l; l = l->next) { |
| |
1026 PurpleMenuAction *act = (PurpleMenuAction *) l->data; |
| |
1027 pidgin_append_menu_action(menu, act, node); |
| |
1028 } |
| |
1029 g_list_free(ll); |
| |
1030 } |
| |
1031 |
| |
1032 void |
| |
1033 pidgin_append_blist_node_extended_menu(GtkWidget *menu, PurpleBlistNode *node) |
| |
1034 { |
| |
1035 GList *l, *ll; |
| |
1036 |
| |
1037 for(l = ll = purple_blist_node_get_extended_menu(node); l; l = l->next) { |
| |
1038 PurpleMenuAction *act = (PurpleMenuAction *) l->data; |
| |
1039 pidgin_append_menu_action(menu, act, node); |
| |
1040 } |
| |
1041 g_list_free(ll); |
| |
1042 } |
| |
1043 |
| |
1044 void |
| |
1045 pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) { |
| |
1046 PurplePluginProtocolInfo *prpl_info; |
| |
1047 PurpleContact *contact; |
| |
1048 gboolean contact_expanded = FALSE; |
| |
1049 |
| |
1050 g_return_if_fail(menu); |
| |
1051 g_return_if_fail(buddy); |
| |
1052 |
| |
1053 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl); |
| |
1054 |
| |
1055 contact = purple_buddy_get_contact(buddy); |
| |
1056 if (contact) { |
| |
1057 contact_expanded = ((struct _pidgin_blist_node *)(((PurpleBlistNode*)contact)->ui_data))->contact_expanded; |
| |
1058 } |
| |
1059 |
| |
1060 if (prpl_info && prpl_info->get_info) { |
| |
1061 pidgin_new_item_from_stock(menu, _("Get _Info"), PIDGIN_STOCK_TOOLBAR_USER_INFO, |
| |
1062 G_CALLBACK(gtk_blist_menu_info_cb), buddy, 0, 0, NULL); |
| |
1063 } |
| |
1064 pidgin_new_item_from_stock(menu, _("I_M"), PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, |
| |
1065 G_CALLBACK(gtk_blist_menu_im_cb), buddy, 0, 0, NULL); |
| |
1066 if (prpl_info && prpl_info->send_file) { |
| |
1067 if (!prpl_info->can_receive_file || |
| |
1068 prpl_info->can_receive_file(buddy->account->gc, buddy->name)) |
| |
1069 { |
| |
1070 pidgin_new_item_from_stock(menu, _("_Send File"), |
| |
1071 PIDGIN_STOCK_FILE_TRANSFER, |
| |
1072 G_CALLBACK(gtk_blist_menu_send_file_cb), |
| |
1073 buddy, 0, 0, NULL); |
| |
1074 } |
| |
1075 } |
| |
1076 |
| |
1077 pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, |
| |
1078 G_CALLBACK(gtk_blist_menu_bp_cb), buddy, 0, 0, NULL); |
| |
1079 |
| |
1080 if(((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) { |
| |
1081 pidgin_new_item_from_stock(menu, _("View _Log"), NULL, |
| |
1082 G_CALLBACK(gtk_blist_menu_showlog_cb), |
| |
1083 contact, 0, 0, NULL); |
| |
1084 } else if (!sub) { |
| |
1085 pidgin_new_item_from_stock(menu, _("View _Log"), NULL, |
| |
1086 G_CALLBACK(gtk_blist_menu_showlog_cb), buddy, 0, 0, NULL); |
| |
1087 } |
| |
1088 |
| |
1089 |
| |
1090 pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, |
| |
1091 (PurpleBlistNode *)buddy); |
| |
1092 pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy); |
| |
1093 |
| |
1094 if (((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) { |
| |
1095 pidgin_separator(menu); |
| |
1096 pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy); |
| |
1097 pidgin_new_item_from_stock(menu, _("Alias..."), PIDGIN_STOCK_ALIAS, |
| |
1098 G_CALLBACK(gtk_blist_menu_alias_cb), |
| |
1099 contact, 0, 0, NULL); |
| |
1100 pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE, |
| |
1101 G_CALLBACK(pidgin_blist_remove_cb), |
| |
1102 contact, 0, 0, NULL); |
| |
1103 } else if (!sub || contact_expanded) { |
| |
1104 pidgin_separator(menu); |
| |
1105 pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy); |
| |
1106 pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, |
| |
1107 G_CALLBACK(gtk_blist_menu_alias_cb), buddy, 0, 0, NULL); |
| |
1108 pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, |
| |
1109 G_CALLBACK(pidgin_blist_remove_cb), buddy, |
| |
1110 0, 0, NULL); |
| |
1111 } |
| |
1112 } |
| |
1113 |
| |
1114 static gboolean |
| |
1115 gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, gpointer data) { |
| |
1116 PurpleBlistNode *node; |
| |
1117 GValue val; |
| |
1118 GtkTreeIter iter; |
| |
1119 GtkTreeSelection *sel; |
| |
1120 |
| |
1121 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); |
| |
1122 if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) |
| |
1123 return FALSE; |
| |
1124 |
| |
1125 val.g_type = 0; |
| |
1126 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, |
| |
1127 NODE_COLUMN, &val); |
| |
1128 node = g_value_get_pointer(&val); |
| |
1129 |
| |
1130 if(event->state & GDK_CONTROL_MASK && |
| |
1131 (event->keyval == 'o' || event->keyval == 'O')) { |
| |
1132 PurpleBuddy *buddy; |
| |
1133 |
| |
1134 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
1135 buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
1136 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1137 buddy = (PurpleBuddy*)node; |
| |
1138 } else { |
| |
1139 return FALSE; |
| |
1140 } |
| |
1141 if(buddy) |
| |
1142 serv_get_info(buddy->account->gc, buddy->name); |
| |
1143 } |
| |
1144 |
| |
1145 return FALSE; |
| |
1146 } |
| |
1147 |
| |
1148 static GtkWidget * |
| |
1149 create_group_menu (PurpleBlistNode *node, PurpleGroup *g) |
| |
1150 { |
| |
1151 GtkWidget *menu; |
| |
1152 GtkWidget *item; |
| |
1153 |
| |
1154 menu = gtk_menu_new(); |
| |
1155 pidgin_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, |
| |
1156 G_CALLBACK(pidgin_blist_add_buddy_cb), node, 0, 0, NULL); |
| |
1157 item = pidgin_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, |
| |
1158 G_CALLBACK(pidgin_blist_add_chat_cb), node, 0, 0, NULL); |
| |
1159 gtk_widget_set_sensitive(item, pidgin_blist_joinchat_is_showable()); |
| |
1160 pidgin_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, |
| |
1161 G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL); |
| |
1162 pidgin_new_item_from_stock(menu, _("_Rename"), NULL, |
| |
1163 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); |
| |
1164 |
| |
1165 pidgin_append_blist_node_extended_menu(menu, node); |
| |
1166 |
| |
1167 return menu; |
| |
1168 } |
| |
1169 |
| |
1170 |
| |
1171 static GtkWidget * |
| |
1172 create_chat_menu(PurpleBlistNode *node, PurpleChat *c) { |
| |
1173 GtkWidget *menu; |
| |
1174 gboolean autojoin; |
| |
1175 |
| |
1176 menu = gtk_menu_new(); |
| |
1177 autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") || |
| |
1178 (purple_blist_node_get_string(node, "gtk-autojoin") != NULL)); |
| |
1179 |
| |
1180 pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT, |
| |
1181 G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); |
| |
1182 pidgin_new_check_item(menu, _("Auto-Join"), |
| |
1183 G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin); |
| |
1184 pidgin_new_item_from_stock(menu, _("View _Log"), NULL, |
| |
1185 G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); |
| |
1186 |
| |
1187 pidgin_append_blist_node_proto_menu(menu, c->account->gc, node); |
| |
1188 pidgin_append_blist_node_extended_menu(menu, node); |
| |
1189 |
| |
1190 pidgin_separator(menu); |
| |
1191 |
| |
1192 pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, |
| |
1193 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); |
| |
1194 pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, |
| |
1195 G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL); |
| |
1196 |
| |
1197 return menu; |
| |
1198 } |
| |
1199 |
| |
1200 static GtkWidget * |
| |
1201 create_contact_menu (PurpleBlistNode *node) |
| |
1202 { |
| |
1203 GtkWidget *menu; |
| |
1204 |
| |
1205 menu = gtk_menu_new(); |
| |
1206 |
| |
1207 pidgin_new_item_from_stock(menu, _("View _Log"), NULL, |
| |
1208 G_CALLBACK(gtk_blist_menu_showlog_cb), |
| |
1209 node, 0, 0, NULL); |
| |
1210 |
| |
1211 pidgin_separator(menu); |
| |
1212 |
| |
1213 pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, |
| |
1214 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); |
| |
1215 pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, |
| |
1216 G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL); |
| |
1217 |
| |
1218 pidgin_separator(menu); |
| |
1219 |
| |
1220 pidgin_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, |
| |
1221 G_CALLBACK(pidgin_blist_collapse_contact_cb), |
| |
1222 node, 0, 0, NULL); |
| |
1223 |
| |
1224 pidgin_append_blist_node_extended_menu(menu, node); |
| |
1225 |
| |
1226 return menu; |
| |
1227 } |
| |
1228 |
| |
1229 static GtkWidget * |
| |
1230 create_buddy_menu(PurpleBlistNode *node, PurpleBuddy *b) { |
| |
1231 struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
1232 GtkWidget *menu; |
| |
1233 GtkWidget *menuitem; |
| |
1234 gboolean show_offline = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies"); |
| |
1235 |
| |
1236 menu = gtk_menu_new(); |
| |
1237 pidgin_blist_make_buddy_menu(menu, b, FALSE); |
| |
1238 |
| |
1239 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
1240 pidgin_separator(menu); |
| |
1241 |
| |
1242 if(gtknode->contact_expanded) { |
| |
1243 pidgin_new_item_from_stock(menu, _("_Collapse"), |
| |
1244 GTK_STOCK_ZOOM_OUT, |
| |
1245 G_CALLBACK(pidgin_blist_collapse_contact_cb), |
| |
1246 node, 0, 0, NULL); |
| |
1247 } else { |
| |
1248 pidgin_new_item_from_stock(menu, _("_Expand"), |
| |
1249 GTK_STOCK_ZOOM_IN, |
| |
1250 G_CALLBACK(pidgin_blist_expand_contact_cb), node, |
| |
1251 0, 0, NULL); |
| |
1252 } |
| |
1253 if(node->child->next) { |
| |
1254 PurpleBlistNode *bnode; |
| |
1255 |
| |
1256 for(bnode = node->child; bnode; bnode = bnode->next) { |
| |
1257 PurpleBuddy *buddy = (PurpleBuddy*)bnode; |
| |
1258 GdkPixbuf *buf; |
| |
1259 GtkWidget *submenu; |
| |
1260 GtkWidget *image; |
| |
1261 |
| |
1262 if(buddy == b) |
| |
1263 continue; |
| |
1264 if(!buddy->account->gc) |
| |
1265 continue; |
| |
1266 if(!show_offline && !PURPLE_BUDDY_IS_ONLINE(buddy)) |
| |
1267 continue; |
| |
1268 |
| |
1269 menuitem = gtk_image_menu_item_new_with_label(buddy->name); |
| |
1270 buf = pidgin_create_prpl_icon(buddy->account,PIDGIN_PRPL_ICON_SMALL); |
| |
1271 image = gtk_image_new_from_pixbuf(buf); |
| |
1272 g_object_unref(G_OBJECT(buf)); |
| |
1273 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), |
| |
1274 image); |
| |
1275 gtk_widget_show(image); |
| |
1276 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); |
| |
1277 gtk_widget_show(menuitem); |
| |
1278 |
| |
1279 submenu = gtk_menu_new(); |
| |
1280 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
| |
1281 gtk_widget_show(submenu); |
| |
1282 |
| |
1283 pidgin_blist_make_buddy_menu(submenu, buddy, TRUE); |
| |
1284 } |
| |
1285 } |
| |
1286 } |
| |
1287 return menu; |
| |
1288 } |
| |
1289 |
| |
1290 static gboolean |
| |
1291 pidgin_blist_show_context_menu(PurpleBlistNode *node, |
| |
1292 GtkMenuPositionFunc func, |
| |
1293 GtkWidget *tv, |
| |
1294 guint button, |
| |
1295 guint32 time) |
| |
1296 { |
| |
1297 struct _pidgin_blist_node *gtknode; |
| |
1298 GtkWidget *menu = NULL; |
| |
1299 gboolean handled = FALSE; |
| |
1300 |
| |
1301 gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
1302 |
| |
1303 /* Create a menu based on the thing we right-clicked on */ |
| |
1304 if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
1305 PurpleGroup *g = (PurpleGroup *)node; |
| |
1306 |
| |
1307 menu = create_group_menu(node, g); |
| |
1308 } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
1309 PurpleChat *c = (PurpleChat *)node; |
| |
1310 |
| |
1311 menu = create_chat_menu(node, c); |
| |
1312 } else if ((PURPLE_BLIST_NODE_IS_CONTACT(node)) && (gtknode->contact_expanded)) { |
| |
1313 menu = create_contact_menu(node); |
| |
1314 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1315 PurpleBuddy *b; |
| |
1316 |
| |
1317 if (PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
1318 b = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
1319 else |
| |
1320 b = (PurpleBuddy *)node; |
| |
1321 |
| |
1322 menu = create_buddy_menu(node, b); |
| |
1323 } |
| |
1324 |
| |
1325 #ifdef _WIN32 |
| |
1326 /* Unhook the tooltip-timeout since we don't want a tooltip |
| |
1327 * to appear and obscure the context menu we are about to show |
| |
1328 This is a workaround for GTK+ bug 107320. */ |
| |
1329 if (gtkblist->timeout) { |
| |
1330 g_source_remove(gtkblist->timeout); |
| |
1331 gtkblist->timeout = 0; |
| |
1332 } |
| |
1333 #endif |
| |
1334 |
| |
1335 /* Now display the menu */ |
| |
1336 if (menu != NULL) { |
| |
1337 gtk_widget_show_all(menu); |
| |
1338 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, func, tv, button, time); |
| |
1339 handled = TRUE; |
| |
1340 } |
| |
1341 |
| |
1342 return handled; |
| |
1343 } |
| |
1344 |
| |
1345 static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) |
| |
1346 { |
| |
1347 GtkTreePath *path; |
| |
1348 PurpleBlistNode *node; |
| |
1349 GValue val; |
| |
1350 GtkTreeIter iter; |
| |
1351 GtkTreeSelection *sel; |
| |
1352 PurplePlugin *prpl = NULL; |
| |
1353 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
1354 struct _pidgin_blist_node *gtknode; |
| |
1355 gboolean handled = FALSE; |
| |
1356 |
| |
1357 /* Here we figure out which node was clicked */ |
| |
1358 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) |
| |
1359 return FALSE; |
| |
1360 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); |
| |
1361 val.g_type = 0; |
| |
1362 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
1363 node = g_value_get_pointer(&val); |
| |
1364 gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
1365 |
| |
1366 /* Right click draws a context menu */ |
| |
1367 if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) { |
| |
1368 handled = pidgin_blist_show_context_menu(node, NULL, tv, 3, event->time); |
| |
1369 |
| |
1370 /* CTRL+middle click expands or collapse a contact */ |
| |
1371 } else if ((event->button == 2) && (event->type == GDK_BUTTON_PRESS) && |
| |
1372 (event->state & GDK_CONTROL_MASK) && (PURPLE_BLIST_NODE_IS_CONTACT(node))) { |
| |
1373 if (gtknode->contact_expanded) |
| |
1374 pidgin_blist_collapse_contact_cb(NULL, node); |
| |
1375 else |
| |
1376 pidgin_blist_expand_contact_cb(NULL, node); |
| |
1377 handled = TRUE; |
| |
1378 |
| |
1379 /* Double middle click gets info */ |
| |
1380 } else if ((event->button == 2) && (event->type == GDK_2BUTTON_PRESS) && |
| |
1381 ((PURPLE_BLIST_NODE_IS_CONTACT(node)) || (PURPLE_BLIST_NODE_IS_BUDDY(node)))) { |
| |
1382 PurpleBuddy *b; |
| |
1383 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
1384 b = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
1385 else |
| |
1386 b = (PurpleBuddy *)node; |
| |
1387 |
| |
1388 prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); |
| |
1389 if (prpl != NULL) |
| |
1390 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
1391 |
| |
1392 if (prpl && prpl_info->get_info) |
| |
1393 serv_get_info(b->account->gc, b->name); |
| |
1394 handled = TRUE; |
| |
1395 } |
| |
1396 |
| |
1397 #if (1) |
| |
1398 /* |
| |
1399 * This code only exists because GTK+ doesn't work. If we return |
| |
1400 * FALSE here, as would be normal the event propoagates down and |
| |
1401 * somehow gets interpreted as the start of a drag event. |
| |
1402 * |
| |
1403 * Um, isn't it _normal_ to return TRUE here? Since the event |
| |
1404 * was handled? --Mark |
| |
1405 */ |
| |
1406 if(handled) { |
| |
1407 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); |
| |
1408 gtk_tree_selection_select_path(sel, path); |
| |
1409 gtk_tree_path_free(path); |
| |
1410 return TRUE; |
| |
1411 } |
| |
1412 #endif |
| |
1413 gtk_tree_path_free(path); |
| |
1414 |
| |
1415 return FALSE; |
| |
1416 } |
| |
1417 |
| |
1418 static gboolean |
| |
1419 pidgin_blist_popup_menu_cb(GtkWidget *tv, void *user_data) |
| |
1420 { |
| |
1421 PurpleBlistNode *node; |
| |
1422 GValue val; |
| |
1423 GtkTreeIter iter; |
| |
1424 GtkTreeSelection *sel; |
| |
1425 gboolean handled = FALSE; |
| |
1426 |
| |
1427 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); |
| |
1428 if (!gtk_tree_selection_get_selected(sel, NULL, &iter)) |
| |
1429 return FALSE; |
| |
1430 |
| |
1431 val.g_type = 0; |
| |
1432 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
1433 &iter, NODE_COLUMN, &val); |
| |
1434 node = g_value_get_pointer(&val); |
| |
1435 |
| |
1436 /* Shift+F10 draws a context menu */ |
| |
1437 handled = pidgin_blist_show_context_menu(node, pidgin_treeview_popup_menu_position_func, tv, 0, GDK_CURRENT_TIME); |
| |
1438 |
| |
1439 return handled; |
| |
1440 } |
| |
1441 |
| |
1442 static void pidgin_blist_buddy_details_cb(gpointer data, guint action, GtkWidget *item) |
| |
1443 { |
| |
1444 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
1445 |
| |
1446 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons", |
| |
1447 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); |
| |
1448 |
| |
1449 pidgin_clear_cursor(gtkblist->window); |
| |
1450 } |
| |
1451 |
| |
1452 static void pidgin_blist_show_idle_time_cb(gpointer data, guint action, GtkWidget *item) |
| |
1453 { |
| |
1454 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
1455 |
| |
1456 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time", |
| |
1457 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); |
| |
1458 |
| |
1459 pidgin_clear_cursor(gtkblist->window); |
| |
1460 } |
| |
1461 |
| |
1462 static void pidgin_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) |
| |
1463 { |
| |
1464 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
1465 |
| |
1466 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups", |
| |
1467 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); |
| |
1468 |
| |
1469 pidgin_clear_cursor(gtkblist->window); |
| |
1470 } |
| |
1471 |
| |
1472 static void pidgin_blist_edit_mode_cb(gpointer callback_data, guint callback_action, |
| |
1473 GtkWidget *checkitem) |
| |
1474 { |
| |
1475 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
1476 |
| |
1477 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies", |
| |
1478 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))); |
| |
1479 |
| |
1480 pidgin_clear_cursor(gtkblist->window); |
| |
1481 } |
| |
1482 |
| |
1483 static void pidgin_blist_mute_sounds_cb(gpointer data, guint action, GtkWidget *item) |
| |
1484 { |
| |
1485 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", GTK_CHECK_MENU_ITEM(item)->active); |
| |
1486 } |
| |
1487 |
| |
1488 static void |
| |
1489 pidgin_blist_mute_pref_cb(const char *name, PurplePrefType type, |
| |
1490 gconstpointer value, gpointer data) |
| |
1491 { |
| |
1492 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(gtkblist->ift, |
| |
1493 N_("/Tools/Mute Sounds"))), (gboolean)GPOINTER_TO_INT(value)); |
| |
1494 } |
| |
1495 |
| |
1496 static void |
| |
1497 pidgin_blist_sound_method_pref_cb(const char *name, PurplePrefType type, |
| |
1498 gconstpointer value, gpointer data) |
| |
1499 { |
| |
1500 gboolean sensitive = TRUE; |
| |
1501 |
| |
1502 if(!strcmp(value, "none")) |
| |
1503 sensitive = FALSE; |
| |
1504 |
| |
1505 gtk_widget_set_sensitive(gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Mute Sounds")), sensitive); |
| |
1506 } |
| |
1507 |
| |
1508 static void |
| |
1509 add_buddies_from_vcard(const char *prpl_id, PurpleGroup *group, GList *list, |
| |
1510 const char *alias) |
| |
1511 { |
| |
1512 GList *l; |
| |
1513 PurpleAccount *account = NULL; |
| |
1514 PurpleConnection *gc; |
| |
1515 |
| |
1516 if (list == NULL) |
| |
1517 return; |
| |
1518 |
| |
1519 for (l = purple_connections_get_all(); l != NULL; l = l->next) |
| |
1520 { |
| |
1521 gc = (PurpleConnection *)l->data; |
| |
1522 account = purple_connection_get_account(gc); |
| |
1523 |
| |
1524 if (!strcmp(purple_account_get_protocol_id(account), prpl_id)) |
| |
1525 break; |
| |
1526 |
| |
1527 account = NULL; |
| |
1528 } |
| |
1529 |
| |
1530 if (account != NULL) |
| |
1531 { |
| |
1532 for (l = list; l != NULL; l = l->next) |
| |
1533 { |
| |
1534 purple_blist_request_add_buddy(account, l->data, |
| |
1535 (group ? group->name : NULL), |
| |
1536 alias); |
| |
1537 } |
| |
1538 } |
| |
1539 |
| |
1540 g_list_foreach(list, (GFunc)g_free, NULL); |
| |
1541 g_list_free(list); |
| |
1542 } |
| |
1543 |
| |
1544 static gboolean |
| |
1545 parse_vcard(const char *vcard, PurpleGroup *group) |
| |
1546 { |
| |
1547 char *temp_vcard; |
| |
1548 char *s, *c; |
| |
1549 char *alias = NULL; |
| |
1550 GList *aims = NULL; |
| |
1551 GList *icqs = NULL; |
| |
1552 GList *yahoos = NULL; |
| |
1553 GList *msns = NULL; |
| |
1554 GList *jabbers = NULL; |
| |
1555 |
| |
1556 s = temp_vcard = g_strdup(vcard); |
| |
1557 |
| |
1558 while (*s != '\0' && strncmp(s, "END:vCard", strlen("END:vCard"))) |
| |
1559 { |
| |
1560 char *field, *value; |
| |
1561 |
| |
1562 field = s; |
| |
1563 |
| |
1564 /* Grab the field */ |
| |
1565 while (*s != '\r' && *s != '\n' && *s != '\0' && *s != ':') |
| |
1566 s++; |
| |
1567 |
| |
1568 if (*s == '\r') s++; |
| |
1569 if (*s == '\n') |
| |
1570 { |
| |
1571 s++; |
| |
1572 continue; |
| |
1573 } |
| |
1574 |
| |
1575 if (*s != '\0') *s++ = '\0'; |
| |
1576 |
| |
1577 if ((c = strchr(field, ';')) != NULL) |
| |
1578 *c = '\0'; |
| |
1579 |
| |
1580 /* Proceed to the end of the line */ |
| |
1581 value = s; |
| |
1582 |
| |
1583 while (*s != '\r' && *s != '\n' && *s != '\0') |
| |
1584 s++; |
| |
1585 |
| |
1586 if (*s == '\r') *s++ = '\0'; |
| |
1587 if (*s == '\n') *s++ = '\0'; |
| |
1588 |
| |
1589 /* We only want to worry about a few fields here. */ |
| |
1590 if (!strcmp(field, "FN")) |
| |
1591 alias = g_strdup(value); |
| |
1592 else if (!strcmp(field, "X-AIM") || !strcmp(field, "X-ICQ") || |
| |
1593 !strcmp(field, "X-YAHOO") || !strcmp(field, "X-MSN") || |
| |
1594 !strcmp(field, "X-JABBER")) |
| |
1595 { |
| |
1596 char **values = g_strsplit(value, ":", 0); |
| |
1597 char **im; |
| |
1598 |
| |
1599 for (im = values; *im != NULL; im++) |
| |
1600 { |
| |
1601 if (!strcmp(field, "X-AIM")) |
| |
1602 aims = g_list_append(aims, g_strdup(*im)); |
| |
1603 else if (!strcmp(field, "X-ICQ")) |
| |
1604 icqs = g_list_append(icqs, g_strdup(*im)); |
| |
1605 else if (!strcmp(field, "X-YAHOO")) |
| |
1606 yahoos = g_list_append(yahoos, g_strdup(*im)); |
| |
1607 else if (!strcmp(field, "X-MSN")) |
| |
1608 msns = g_list_append(msns, g_strdup(*im)); |
| |
1609 else if (!strcmp(field, "X-JABBER")) |
| |
1610 jabbers = g_list_append(jabbers, g_strdup(*im)); |
| |
1611 } |
| |
1612 |
| |
1613 g_strfreev(values); |
| |
1614 } |
| |
1615 } |
| |
1616 |
| |
1617 g_free(temp_vcard); |
| |
1618 |
| |
1619 if (aims == NULL && icqs == NULL && yahoos == NULL && |
| |
1620 msns == NULL && jabbers == NULL) |
| |
1621 { |
| |
1622 g_free(alias); |
| |
1623 |
| |
1624 return FALSE; |
| |
1625 } |
| |
1626 |
| |
1627 add_buddies_from_vcard("prpl-oscar", group, aims, alias); |
| |
1628 add_buddies_from_vcard("prpl-oscar", group, icqs, alias); |
| |
1629 add_buddies_from_vcard("prpl-yahoo", group, yahoos, alias); |
| |
1630 add_buddies_from_vcard("prpl-msn", group, msns, alias); |
| |
1631 add_buddies_from_vcard("prpl-jabber", group, jabbers, alias); |
| |
1632 |
| |
1633 g_free(alias); |
| |
1634 |
| |
1635 return TRUE; |
| |
1636 } |
| |
1637 |
| |
1638 #ifdef _WIN32 |
| |
1639 static void pidgin_blist_drag_begin(GtkWidget *widget, |
| |
1640 GdkDragContext *drag_context, gpointer user_data) |
| |
1641 { |
| |
1642 pidgin_blist_tooltip_destroy(); |
| |
1643 |
| |
1644 |
| |
1645 /* Unhook the tooltip-timeout since we don't want a tooltip |
| |
1646 * to appear and obscure the dragging operation. |
| |
1647 * This is a workaround for GTK+ bug 107320. */ |
| |
1648 if (gtkblist->timeout) { |
| |
1649 g_source_remove(gtkblist->timeout); |
| |
1650 gtkblist->timeout = 0; |
| |
1651 } |
| |
1652 } |
| |
1653 #endif |
| |
1654 |
| |
1655 static void pidgin_blist_drag_data_get_cb(GtkWidget *widget, |
| |
1656 GdkDragContext *dc, |
| |
1657 GtkSelectionData *data, |
| |
1658 guint info, |
| |
1659 guint time, |
| |
1660 gpointer null) |
| |
1661 { |
| |
1662 |
| |
1663 if (data->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE)) |
| |
1664 { |
| |
1665 GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); |
| |
1666 GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); |
| |
1667 GtkTreeIter iter; |
| |
1668 PurpleBlistNode *node = NULL; |
| |
1669 GValue val; |
| |
1670 if(!sourcerow) |
| |
1671 return; |
| |
1672 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); |
| |
1673 val.g_type = 0; |
| |
1674 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
1675 node = g_value_get_pointer(&val); |
| |
1676 gtk_selection_data_set (data, |
| |
1677 gdk_atom_intern ("PURPLE_BLIST_NODE", FALSE), |
| |
1678 8, /* bits */ |
| |
1679 (void*)&node, |
| |
1680 sizeof (node)); |
| |
1681 |
| |
1682 gtk_tree_path_free(sourcerow); |
| |
1683 } |
| |
1684 else if (data->target == gdk_atom_intern("application/x-im-contact", FALSE)) |
| |
1685 { |
| |
1686 GtkTreeRowReference *ref; |
| |
1687 GtkTreePath *sourcerow; |
| |
1688 GtkTreeIter iter; |
| |
1689 PurpleBlistNode *node = NULL; |
| |
1690 PurpleBuddy *buddy; |
| |
1691 PurpleConnection *gc; |
| |
1692 GValue val; |
| |
1693 GString *str; |
| |
1694 const char *protocol; |
| |
1695 |
| |
1696 ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); |
| |
1697 sourcerow = gtk_tree_row_reference_get_path(ref); |
| |
1698 |
| |
1699 if (!sourcerow) |
| |
1700 return; |
| |
1701 |
| |
1702 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, |
| |
1703 sourcerow); |
| |
1704 val.g_type = 0; |
| |
1705 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, |
| |
1706 NODE_COLUMN, &val); |
| |
1707 |
| |
1708 node = g_value_get_pointer(&val); |
| |
1709 |
| |
1710 if (PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
1711 { |
| |
1712 buddy = purple_contact_get_priority_buddy((PurpleContact *)node); |
| |
1713 } |
| |
1714 else if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
1715 { |
| |
1716 gtk_tree_path_free(sourcerow); |
| |
1717 return; |
| |
1718 } |
| |
1719 else |
| |
1720 { |
| |
1721 buddy = (PurpleBuddy *)node; |
| |
1722 } |
| |
1723 |
| |
1724 gc = purple_account_get_connection(buddy->account); |
| |
1725 |
| |
1726 if (gc == NULL) |
| |
1727 { |
| |
1728 gtk_tree_path_free(sourcerow); |
| |
1729 return; |
| |
1730 } |
| |
1731 |
| |
1732 protocol = |
| |
1733 PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->list_icon(buddy->account, |
| |
1734 buddy); |
| |
1735 |
| |
1736 str = g_string_new(NULL); |
| |
1737 g_string_printf(str, |
| |
1738 "MIME-Version: 1.0\r\n" |
| |
1739 "Content-Type: application/x-im-contact\r\n" |
| |
1740 "X-IM-Protocol: %s\r\n" |
| |
1741 "X-IM-Username: %s\r\n", |
| |
1742 protocol, |
| |
1743 buddy->name); |
| |
1744 |
| |
1745 if (buddy->alias != NULL) |
| |
1746 { |
| |
1747 g_string_append_printf(str, |
| |
1748 "X-IM-Alias: %s\r\n", |
| |
1749 buddy->alias); |
| |
1750 } |
| |
1751 |
| |
1752 g_string_append(str, "\r\n"); |
| |
1753 |
| |
1754 gtk_selection_data_set(data, |
| |
1755 gdk_atom_intern("application/x-im-contact", FALSE), |
| |
1756 8, /* bits */ |
| |
1757 (const guchar *)str->str, |
| |
1758 strlen(str->str) + 1); |
| |
1759 |
| |
1760 g_string_free(str, TRUE); |
| |
1761 gtk_tree_path_free(sourcerow); |
| |
1762 } |
| |
1763 } |
| |
1764 |
| |
1765 static void pidgin_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, |
| |
1766 GtkSelectionData *sd, guint info, guint t) |
| |
1767 { |
| |
1768 if (gtkblist->drag_timeout) { |
| |
1769 g_source_remove(gtkblist->drag_timeout); |
| |
1770 gtkblist->drag_timeout = 0; |
| |
1771 } |
| |
1772 |
| |
1773 if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE) && sd->data) { |
| |
1774 PurpleBlistNode *n = NULL; |
| |
1775 GtkTreePath *path = NULL; |
| |
1776 GtkTreeViewDropPosition position; |
| |
1777 memcpy(&n, sd->data, sizeof(n)); |
| |
1778 if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { |
| |
1779 /* if we're here, I think it means the drop is ok */ |
| |
1780 GtkTreeIter iter; |
| |
1781 PurpleBlistNode *node; |
| |
1782 GValue val; |
| |
1783 struct _pidgin_blist_node *gtknode; |
| |
1784 |
| |
1785 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
1786 &iter, path); |
| |
1787 val.g_type = 0; |
| |
1788 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), |
| |
1789 &iter, NODE_COLUMN, &val); |
| |
1790 node = g_value_get_pointer(&val); |
| |
1791 gtknode = node->ui_data; |
| |
1792 |
| |
1793 if (PURPLE_BLIST_NODE_IS_CONTACT(n)) { |
| |
1794 PurpleContact *c = (PurpleContact*)n; |
| |
1795 if (PURPLE_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded) { |
| |
1796 purple_blist_merge_contact(c, node); |
| |
1797 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || |
| |
1798 PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
1799 switch(position) { |
| |
1800 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1801 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1802 purple_blist_add_contact(c, (PurpleGroup*)node->parent, |
| |
1803 node); |
| |
1804 break; |
| |
1805 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1806 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1807 purple_blist_add_contact(c, (PurpleGroup*)node->parent, |
| |
1808 node->prev); |
| |
1809 break; |
| |
1810 } |
| |
1811 } else if(PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
1812 purple_blist_add_contact(c, (PurpleGroup*)node, NULL); |
| |
1813 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1814 purple_blist_merge_contact(c, node); |
| |
1815 } |
| |
1816 } else if (PURPLE_BLIST_NODE_IS_BUDDY(n)) { |
| |
1817 PurpleBuddy *b = (PurpleBuddy*)n; |
| |
1818 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1819 switch(position) { |
| |
1820 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1821 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1822 purple_blist_add_buddy(b, (PurpleContact*)node->parent, |
| |
1823 (PurpleGroup*)node->parent->parent, node); |
| |
1824 break; |
| |
1825 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1826 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1827 purple_blist_add_buddy(b, (PurpleContact*)node->parent, |
| |
1828 (PurpleGroup*)node->parent->parent, |
| |
1829 node->prev); |
| |
1830 break; |
| |
1831 } |
| |
1832 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
1833 purple_blist_add_buddy(b, NULL, (PurpleGroup*)node->parent, |
| |
1834 NULL); |
| |
1835 } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
1836 purple_blist_add_buddy(b, NULL, (PurpleGroup*)node, NULL); |
| |
1837 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
1838 if(gtknode->contact_expanded) { |
| |
1839 switch(position) { |
| |
1840 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1841 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1842 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1843 purple_blist_add_buddy(b, (PurpleContact*)node, |
| |
1844 (PurpleGroup*)node->parent, NULL); |
| |
1845 break; |
| |
1846 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1847 purple_blist_add_buddy(b, NULL, |
| |
1848 (PurpleGroup*)node->parent, node->prev); |
| |
1849 break; |
| |
1850 } |
| |
1851 } else { |
| |
1852 switch(position) { |
| |
1853 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1854 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1855 purple_blist_add_buddy(b, NULL, |
| |
1856 (PurpleGroup*)node->parent, NULL); |
| |
1857 break; |
| |
1858 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1859 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1860 purple_blist_add_buddy(b, NULL, |
| |
1861 (PurpleGroup*)node->parent, node->prev); |
| |
1862 break; |
| |
1863 } |
| |
1864 } |
| |
1865 } |
| |
1866 } else if (PURPLE_BLIST_NODE_IS_CHAT(n)) { |
| |
1867 PurpleChat *chat = (PurpleChat *)n; |
| |
1868 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1869 switch(position) { |
| |
1870 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1871 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1872 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1873 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1874 purple_blist_add_chat(chat, |
| |
1875 (PurpleGroup*)node->parent->parent, |
| |
1876 node->parent); |
| |
1877 break; |
| |
1878 } |
| |
1879 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node) || |
| |
1880 PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
1881 switch(position) { |
| |
1882 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1883 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1884 purple_blist_add_chat(chat, (PurpleGroup*)node->parent, node); |
| |
1885 break; |
| |
1886 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1887 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1888 purple_blist_add_chat(chat, (PurpleGroup*)node->parent, node->prev); |
| |
1889 break; |
| |
1890 } |
| |
1891 } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
1892 purple_blist_add_chat(chat, (PurpleGroup*)node, NULL); |
| |
1893 } |
| |
1894 } else if (PURPLE_BLIST_NODE_IS_GROUP(n)) { |
| |
1895 PurpleGroup *g = (PurpleGroup*)n; |
| |
1896 if (PURPLE_BLIST_NODE_IS_GROUP(node)) { |
| |
1897 switch (position) { |
| |
1898 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: |
| |
1899 case GTK_TREE_VIEW_DROP_AFTER: |
| |
1900 purple_blist_add_group(g, node); |
| |
1901 break; |
| |
1902 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: |
| |
1903 case GTK_TREE_VIEW_DROP_BEFORE: |
| |
1904 purple_blist_add_group(g, node->prev); |
| |
1905 break; |
| |
1906 } |
| |
1907 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
1908 purple_blist_add_group(g, node->parent->parent); |
| |
1909 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node) || |
| |
1910 PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
1911 purple_blist_add_group(g, node->parent); |
| |
1912 } |
| |
1913 } |
| |
1914 |
| |
1915 gtk_tree_path_free(path); |
| |
1916 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
| |
1917 } |
| |
1918 } |
| |
1919 else if (sd->target == gdk_atom_intern("application/x-im-contact", |
| |
1920 FALSE) && sd->data) |
| |
1921 { |
| |
1922 PurpleGroup *group = NULL; |
| |
1923 GtkTreePath *path = NULL; |
| |
1924 GtkTreeViewDropPosition position; |
| |
1925 PurpleAccount *account; |
| |
1926 char *protocol = NULL; |
| |
1927 char *username = NULL; |
| |
1928 char *alias = NULL; |
| |
1929 |
| |
1930 if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), |
| |
1931 x, y, &path, &position)) |
| |
1932 { |
| |
1933 GtkTreeIter iter; |
| |
1934 PurpleBlistNode *node; |
| |
1935 GValue val; |
| |
1936 |
| |
1937 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
1938 &iter, path); |
| |
1939 val.g_type = 0; |
| |
1940 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), |
| |
1941 &iter, NODE_COLUMN, &val); |
| |
1942 node = g_value_get_pointer(&val); |
| |
1943 |
| |
1944 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
1945 { |
| |
1946 group = (PurpleGroup *)node->parent->parent; |
| |
1947 } |
| |
1948 else if (PURPLE_BLIST_NODE_IS_CHAT(node) || |
| |
1949 PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
1950 { |
| |
1951 group = (PurpleGroup *)node->parent; |
| |
1952 } |
| |
1953 else if (PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
1954 { |
| |
1955 group = (PurpleGroup *)node; |
| |
1956 } |
| |
1957 } |
| |
1958 |
| |
1959 if (pidgin_parse_x_im_contact((const char *)sd->data, FALSE, &account, |
| |
1960 &protocol, &username, &alias)) |
| |
1961 { |
| |
1962 if (account == NULL) |
| |
1963 { |
| |
1964 purple_notify_error(NULL, NULL, |
| |
1965 _("You are not currently signed on with an account that " |
| |
1966 "can add that buddy."), NULL); |
| |
1967 } |
| |
1968 else |
| |
1969 { |
| |
1970 purple_blist_request_add_buddy(account, username, |
| |
1971 (group ? group->name : NULL), |
| |
1972 alias); |
| |
1973 } |
| |
1974 } |
| |
1975 |
| |
1976 g_free(username); |
| |
1977 g_free(protocol); |
| |
1978 g_free(alias); |
| |
1979 |
| |
1980 if (path != NULL) |
| |
1981 gtk_tree_path_free(path); |
| |
1982 |
| |
1983 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
| |
1984 } |
| |
1985 else if (sd->target == gdk_atom_intern("text/x-vcard", FALSE) && sd->data) |
| |
1986 { |
| |
1987 gboolean result; |
| |
1988 PurpleGroup *group = NULL; |
| |
1989 GtkTreePath *path = NULL; |
| |
1990 GtkTreeViewDropPosition position; |
| |
1991 |
| |
1992 if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), |
| |
1993 x, y, &path, &position)) |
| |
1994 { |
| |
1995 GtkTreeIter iter; |
| |
1996 PurpleBlistNode *node; |
| |
1997 GValue val; |
| |
1998 |
| |
1999 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
2000 &iter, path); |
| |
2001 val.g_type = 0; |
| |
2002 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), |
| |
2003 &iter, NODE_COLUMN, &val); |
| |
2004 node = g_value_get_pointer(&val); |
| |
2005 |
| |
2006 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
2007 { |
| |
2008 group = (PurpleGroup *)node->parent->parent; |
| |
2009 } |
| |
2010 else if (PURPLE_BLIST_NODE_IS_CHAT(node) || |
| |
2011 PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
2012 { |
| |
2013 group = (PurpleGroup *)node->parent; |
| |
2014 } |
| |
2015 else if (PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
2016 { |
| |
2017 group = (PurpleGroup *)node; |
| |
2018 } |
| |
2019 } |
| |
2020 |
| |
2021 result = parse_vcard((const gchar *)sd->data, group); |
| |
2022 |
| |
2023 gtk_drag_finish(dc, result, (dc->action == GDK_ACTION_MOVE), t); |
| |
2024 } else if (sd->target == gdk_atom_intern("text/uri-list", FALSE) && sd->data) { |
| |
2025 GtkTreePath *path = NULL; |
| |
2026 GtkTreeViewDropPosition position; |
| |
2027 |
| |
2028 if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), |
| |
2029 x, y, &path, &position)) |
| |
2030 { |
| |
2031 GtkTreeIter iter; |
| |
2032 PurpleBlistNode *node; |
| |
2033 GValue val; |
| |
2034 |
| |
2035 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
2036 &iter, path); |
| |
2037 val.g_type = 0; |
| |
2038 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), |
| |
2039 &iter, NODE_COLUMN, &val); |
| |
2040 node = g_value_get_pointer(&val); |
| |
2041 |
| |
2042 if (PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
2043 PurpleBuddy *b = PURPLE_BLIST_NODE_IS_BUDDY(node) ? (PurpleBuddy*)node : purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
2044 pidgin_dnd_file_manage(sd, b->account, b->name); |
| |
2045 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
| |
2046 } else { |
| |
2047 gtk_drag_finish(dc, FALSE, FALSE, t); |
| |
2048 } |
| |
2049 } |
| |
2050 } |
| |
2051 } |
| |
2052 |
| |
2053 static void |
| |
2054 roundify(GdkPixbuf *pixbuf) { |
| |
2055 int width, height, rowstride; |
| |
2056 guchar *pixels; |
| |
2057 |
| |
2058 if (!gdk_pixbuf_get_has_alpha(pixbuf)) |
| |
2059 return; |
| |
2060 |
| |
2061 width = gdk_pixbuf_get_width(pixbuf); |
| |
2062 height = gdk_pixbuf_get_height(pixbuf); |
| |
2063 rowstride = gdk_pixbuf_get_rowstride(pixbuf); |
| |
2064 pixels = gdk_pixbuf_get_pixels(pixbuf); |
| |
2065 |
| |
2066 if (width < 6 || height < 6) |
| |
2067 return; |
| |
2068 |
| |
2069 /* Top left */ |
| |
2070 pixels[3] = 0; |
| |
2071 pixels[7] = 0x80; |
| |
2072 pixels[11] = 0xC0; |
| |
2073 pixels[rowstride + 3] = 0x80; |
| |
2074 pixels[rowstride * 2 + 3] = 0xC0; |
| |
2075 |
| |
2076 /* Top right */ |
| |
2077 pixels[width * 4 - 1] = 0; |
| |
2078 pixels[width * 4 - 5] = 0x80; |
| |
2079 pixels[width * 4 - 9] = 0xC0; |
| |
2080 pixels[rowstride + (width * 4) - 1] = 0x80; |
| |
2081 pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0; |
| |
2082 |
| |
2083 /* Bottom left */ |
| |
2084 pixels[(height - 1) * rowstride + 3] = 0; |
| |
2085 pixels[(height - 1) * rowstride + 7] = 0x80; |
| |
2086 pixels[(height - 1) * rowstride + 11] = 0xC0; |
| |
2087 pixels[(height - 2) * rowstride + 3] = 0x80; |
| |
2088 pixels[(height - 3) * rowstride + 3] = 0xC0; |
| |
2089 |
| |
2090 /* Bottom right */ |
| |
2091 pixels[height * rowstride - 1] = 0; |
| |
2092 pixels[(height - 1) * rowstride - 1] = 0x80; |
| |
2093 pixels[(height - 2) * rowstride - 1] = 0xC0; |
| |
2094 pixels[height * rowstride - 5] = 0x80; |
| |
2095 pixels[height * rowstride - 9] = 0xC0; |
| |
2096 } |
| |
2097 |
| |
2098 /* Altered from do_colorshift in gnome-panel */ |
| |
2099 static void |
| |
2100 do_alphashift (GdkPixbuf *dest, GdkPixbuf *src, int shift) |
| |
2101 { |
| |
2102 gint i, j; |
| |
2103 gint width, height, has_alpha, srcrowstride, destrowstride; |
| |
2104 guchar *target_pixels; |
| |
2105 guchar *original_pixels; |
| |
2106 guchar *pixsrc; |
| |
2107 guchar *pixdest; |
| |
2108 int val; |
| |
2109 guchar a; |
| |
2110 |
| |
2111 has_alpha = gdk_pixbuf_get_has_alpha (src); |
| |
2112 if (!has_alpha) |
| |
2113 return; |
| |
2114 |
| |
2115 width = gdk_pixbuf_get_width (src); |
| |
2116 height = gdk_pixbuf_get_height (src); |
| |
2117 srcrowstride = gdk_pixbuf_get_rowstride (src); |
| |
2118 destrowstride = gdk_pixbuf_get_rowstride (dest); |
| |
2119 target_pixels = gdk_pixbuf_get_pixels (dest); |
| |
2120 original_pixels = gdk_pixbuf_get_pixels (src); |
| |
2121 |
| |
2122 for (i = 0; i < height; i++) { |
| |
2123 pixdest = target_pixels + i*destrowstride; |
| |
2124 pixsrc = original_pixels + i*srcrowstride; |
| |
2125 for (j = 0; j < width; j++) { |
| |
2126 *(pixdest++) = *(pixsrc++); |
| |
2127 *(pixdest++) = *(pixsrc++); |
| |
2128 *(pixdest++) = *(pixsrc++); |
| |
2129 a = *(pixsrc++); |
| |
2130 val = a - shift; |
| |
2131 *(pixdest++) = CLAMP(val, 0, 255); |
| |
2132 } |
| |
2133 } |
| |
2134 } |
| |
2135 |
| |
2136 |
| |
2137 static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node, |
| |
2138 gboolean scaled, gboolean greyed, gboolean custom) |
| |
2139 { |
| |
2140 GdkPixbuf *buf, *ret = NULL; |
| |
2141 GdkPixbufLoader *loader; |
| |
2142 PurpleBuddyIcon *icon; |
| |
2143 const guchar *data = NULL; |
| |
2144 gsize len; |
| |
2145 PurpleBuddy *buddy = NULL; |
| |
2146 PurpleChat *chat = NULL; |
| |
2147 PurpleAccount *account = NULL; |
| |
2148 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
2149 |
| |
2150 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
2151 buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
2152 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
2153 buddy = (PurpleBuddy*)node; |
| |
2154 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
2155 chat = (PurpleChat*)node; |
| |
2156 } else { |
| |
2157 return NULL; |
| |
2158 } |
| |
2159 |
| |
2160 if(buddy != NULL) |
| |
2161 account = purple_buddy_get_account(buddy); |
| |
2162 else if(chat != NULL) |
| |
2163 account = chat->account; |
| |
2164 |
| |
2165 if(account && account->gc) |
| |
2166 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl); |
| |
2167 |
| |
2168 #if 0 |
| |
2169 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) |
| |
2170 return NULL; |
| |
2171 #endif |
| |
2172 |
| |
2173 if (custom) { |
| |
2174 const char *file = purple_blist_node_get_string((PurpleBlistNode*)purple_buddy_get_contact(buddy), |
| |
2175 "custom_buddy_icon"); |
| |
2176 if (file && *file) { |
| |
2177 char *contents; |
| |
2178 GError *err = NULL; |
| |
2179 if (!g_file_get_contents(file, &contents, &len, &err)) { |
| |
2180 purple_debug_info("custom -icon", "Could not open custom-icon %s for %s\n", |
| |
2181 file, purple_buddy_get_name(buddy), err->message); |
| |
2182 g_error_free(err); |
| |
2183 } else |
| |
2184 data = (const guchar*)contents; |
| |
2185 } |
| |
2186 } |
| |
2187 |
| |
2188 if (data == NULL) { |
| |
2189 if(buddy != NULL) { |
| |
2190 if (!(icon = purple_buddy_get_icon(buddy))) |
| |
2191 if (!(icon = purple_buddy_icons_find(buddy->account, buddy->name))) /* Not sure I like this...*/ |
| |
2192 return NULL; |
| |
2193 data = purple_buddy_icon_get_data(icon, &len); |
| |
2194 } |
| |
2195 custom = FALSE; /* We are not using the custom icon */ |
| |
2196 } |
| |
2197 |
| |
2198 if(data == NULL) |
| |
2199 return NULL; |
| |
2200 |
| |
2201 loader = gdk_pixbuf_loader_new(); |
| |
2202 gdk_pixbuf_loader_write(loader, data, len, NULL); |
| |
2203 gdk_pixbuf_loader_close(loader, NULL); |
| |
2204 buf = gdk_pixbuf_loader_get_pixbuf(loader); |
| |
2205 if (buf) |
| |
2206 g_object_ref(G_OBJECT(buf)); |
| |
2207 g_object_unref(G_OBJECT(loader)); |
| |
2208 |
| |
2209 if (custom) |
| |
2210 g_free((void*)data); |
| |
2211 if (buf) { |
| |
2212 int orig_width, orig_height; |
| |
2213 int scale_width, scale_height; |
| |
2214 |
| |
2215 if (greyed) { |
| |
2216 PurplePresence *presence = purple_buddy_get_presence(buddy); |
| |
2217 if (!PURPLE_BUDDY_IS_ONLINE(buddy)) |
| |
2218 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); |
| |
2219 if (purple_presence_is_idle(presence)) |
| |
2220 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); |
| |
2221 } |
| |
2222 |
| |
2223 /* i'd use the pidgin_buddy_icon_get_scale_size() thing, |
| |
2224 * but it won't tell me the original size, which I need for scaling |
| |
2225 * purposes */ |
| |
2226 scale_width = orig_width = gdk_pixbuf_get_width(buf); |
| |
2227 scale_height = orig_height = gdk_pixbuf_get_height(buf); |
| |
2228 |
| |
2229 if (prpl_info && prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY) |
| |
2230 purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &scale_width, &scale_height); |
| |
2231 |
| |
2232 if (scaled) { |
| |
2233 if(scale_height > scale_width) { |
| |
2234 scale_width = 32.0 * (double)scale_width / (double)scale_height; |
| |
2235 scale_height = 32; |
| |
2236 } else { |
| |
2237 scale_height = 32.0 * (double)scale_height / (double)scale_width; |
| |
2238 scale_width = 32; |
| |
2239 } |
| |
2240 |
| |
2241 ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); |
| |
2242 gdk_pixbuf_fill(ret, 0x00000000); |
| |
2243 gdk_pixbuf_scale(buf, ret, (32-scale_width)/2, (32-scale_height)/2, scale_width, scale_height, (32-scale_width)/2, (32-scale_height)/2, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR); |
| |
2244 if (pidgin_gdk_pixbuf_is_opaque(ret)) |
| |
2245 roundify(ret); |
| |
2246 } else { |
| |
2247 ret = gdk_pixbuf_scale_simple(buf,scale_width,scale_height, GDK_INTERP_BILINEAR); |
| |
2248 } |
| |
2249 g_object_unref(G_OBJECT(buf)); |
| |
2250 } |
| |
2251 |
| |
2252 return ret; |
| |
2253 } |
| |
2254 /* # - Status Icon |
| |
2255 * P - Protocol Icon |
| |
2256 * A - Buddy Icon |
| |
2257 * [ - SMALL_SPACE |
| |
2258 * = - LARGE_SPACE |
| |
2259 * +--- STATUS_SIZE +--- td->avatar_width |
| |
2260 * | +-- td->name_width | |
| |
2261 * +----+ +-------+ +---------+ |
| |
2262 * | | | | | | |
| |
2263 * +-------------------------------------------+ |
| |
2264 * | [ = [ |--- TOOLTIP_BORDER |
| |
2265 *name_height --+-| ######[BuddyName = PP [ AAAAAAAAAAA |--+ |
| |
2266 * | | ######[ = PP [ AAAAAAAAAAA | | |
| |
2267 * STATUS SIZE -| | ######[[[[[[[[[[[[[[[[[[[[[ AAAAAAAAAAA | | |
| |
2268 * +--+-| ######[Account: So-and-so [ AAAAAAAAAAA | |-- td->avatar_height |
| |
2269 * | | [Idle: 4h 15m [ AAAAAAAAAAA | | |
| |
2270 * height --+ | [Foo: Bar, Baz [ AAAAAAAAAAA | | |
| |
2271 * | | [Status: Awesome [ AAAAAAAAAAA |--+ |
| |
2272 * +----| [Stop: Hammer Time [ | |
| |
2273 * | [ [ |--- TOOLTIP_BORDER |
| |
2274 * +-------------------------------------------+ |
| |
2275 * | | | | |
| |
2276 * | +----------------+ | |
| |
2277 * | | | |
| |
2278 * | +-- td->width | |
| |
2279 * | | |
| |
2280 * +---- TOOLTIP_BORDER +---- TOOLTIP_BORDER |
| |
2281 * |
| |
2282 * |
| |
2283 */ |
| |
2284 #define STATUS_SIZE 22 |
| |
2285 #define TOOLTIP_BORDER 12 |
| |
2286 #define SMALL_SPACE 6 |
| |
2287 #define LARGE_SPACE 12 |
| |
2288 #define PRPL_SIZE 16 |
| |
2289 struct tooltip_data { |
| |
2290 PangoLayout *layout; |
| |
2291 PangoLayout *name_layout; |
| |
2292 GdkPixbuf *prpl_icon; |
| |
2293 GdkPixbuf *status_icon; |
| |
2294 GdkPixbuf *avatar; |
| |
2295 gboolean avatar_is_prpl_icon; |
| |
2296 int avatar_width; |
| |
2297 int avatar_height; |
| |
2298 int name_height; |
| |
2299 int name_width; |
| |
2300 int width; |
| |
2301 int height; |
| |
2302 }; |
| |
2303 |
| |
2304 static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean full) |
| |
2305 { |
| |
2306 char *tooltip_text = NULL; |
| |
2307 struct tooltip_data *td = g_new0(struct tooltip_data, 1); |
| |
2308 PurpleAccount *account = NULL; |
| |
2309 char *tmp, *node_name; |
| |
2310 |
| |
2311 if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
2312 account = ((PurpleBuddy*)(node))->account; |
| |
2313 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
2314 account = ((PurpleChat*)(node))->account; |
| |
2315 } |
| |
2316 |
| |
2317 td->status_icon = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_LARGE); |
| |
2318 td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE, TRUE); |
| |
2319 td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); |
| |
2320 tooltip_text = pidgin_get_tooltip_text(node, full); |
| |
2321 td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); |
| |
2322 td->name_layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL); |
| |
2323 |
| |
2324 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
2325 tmp = g_markup_escape_text(purple_buddy_get_name((PurpleBuddy*)node), -1); |
| |
2326 else |
| |
2327 tmp = g_markup_escape_text(purple_chat_get_name((PurpleChat*)node), -1); |
| |
2328 node_name = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", tmp); |
| |
2329 g_free(tmp); |
| |
2330 |
| |
2331 pango_layout_set_markup(td->layout, tooltip_text, -1); |
| |
2332 pango_layout_set_wrap(td->layout, PANGO_WRAP_WORD); |
| |
2333 pango_layout_set_width(td->layout, 300000); |
| |
2334 |
| |
2335 pango_layout_get_size (td->layout, &td->width, &td->height); |
| |
2336 td->width = PANGO_PIXELS(td->width); |
| |
2337 td->height = PANGO_PIXELS(td->height); |
| |
2338 |
| |
2339 pango_layout_set_markup(td->name_layout, node_name, -1); |
| |
2340 pango_layout_set_wrap(td->name_layout, PANGO_WRAP_WORD); |
| |
2341 pango_layout_set_width(td->name_layout, 300000); |
| |
2342 |
| |
2343 pango_layout_get_size (td->name_layout, &td->name_width, &td->name_height); |
| |
2344 td->name_width = PANGO_PIXELS(td->name_width) + SMALL_SPACE + PRPL_SIZE; |
| |
2345 td->name_height = MAX(PANGO_PIXELS(td->name_height), PRPL_SIZE + SMALL_SPACE); |
| |
2346 #if 0 /* PRPL Icon as avatar */ |
| |
2347 if(!td->avatar && full) { |
| |
2348 td->avatar = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE); |
| |
2349 td->avatar_is_prpl_icon = TRUE; |
| |
2350 } |
| |
2351 #endif |
| |
2352 |
| |
2353 if (td->avatar) { |
| |
2354 td->avatar_width = gdk_pixbuf_get_width(td->avatar); |
| |
2355 td->avatar_height = gdk_pixbuf_get_height(td->avatar); |
| |
2356 } |
| |
2357 |
| |
2358 g_free(node_name); |
| |
2359 g_free(tooltip_text); |
| |
2360 return td; |
| |
2361 } |
| |
2362 |
| |
2363 static void pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, PurpleBlistNode *node) |
| |
2364 { |
| |
2365 GtkStyle *style; |
| |
2366 int current_height, max_width; |
| |
2367 int max_text_width; |
| |
2368 int max_avatar_width; |
| |
2369 GList *l; |
| |
2370 int prpl_col = 0; |
| |
2371 GtkTextDirection dir = gtk_widget_get_direction(widget); |
| |
2372 |
| |
2373 if(gtkblist->tooltipdata == NULL) |
| |
2374 return; |
| |
2375 |
| |
2376 style = gtkblist->tipwindow->style; |
| |
2377 gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, |
| |
2378 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); |
| |
2379 |
| |
2380 max_text_width = 0; |
| |
2381 max_avatar_width = 0; |
| |
2382 |
| |
2383 for(l = gtkblist->tooltipdata; l; l = l->next) |
| |
2384 { |
| |
2385 struct tooltip_data *td = l->data; |
| |
2386 |
| |
2387 max_text_width = MAX(max_text_width, |
| |
2388 MAX(td->width, td->name_width)); |
| |
2389 max_avatar_width = MAX(max_avatar_width, td->avatar_width); |
| |
2390 } |
| |
2391 |
| |
2392 max_width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; |
| |
2393 if (dir == GTK_TEXT_DIR_RTL) |
| |
2394 prpl_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE; |
| |
2395 else |
| |
2396 prpl_col = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width - PRPL_SIZE; |
| |
2397 |
| |
2398 current_height = 12; |
| |
2399 for(l = gtkblist->tooltipdata; l; l = l->next) |
| |
2400 { |
| |
2401 struct tooltip_data *td = l->data; |
| |
2402 |
| |
2403 if (td->avatar && pidgin_gdk_pixbuf_is_opaque(td->avatar)) |
| |
2404 { |
| |
2405 if (dir == GTK_TEXT_DIR_RTL) |
| |
2406 gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, |
| |
2407 NULL, gtkblist->tipwindow, "tooltip", |
| |
2408 TOOLTIP_BORDER -1, current_height -1, td->avatar_width +2, td->avatar_height + 2); |
| |
2409 else |
| |
2410 gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, |
| |
2411 NULL, gtkblist->tipwindow, "tooltip", |
| |
2412 max_width - (td->avatar_width+ TOOLTIP_BORDER)-1, |
| |
2413 current_height-1,td->avatar_width+2, td->avatar_height+2); |
| |
2414 } |
| |
2415 |
| |
2416 #if GTK_CHECK_VERSION(2,2,0) |
| |
2417 if (dir == GTK_TEXT_DIR_RTL) |
| |
2418 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, |
| |
2419 0, 0, max_width - TOOLTIP_BORDER - STATUS_SIZE, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2420 else |
| |
2421 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon, |
| |
2422 0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2423 if(td->avatar) |
| |
2424 { |
| |
2425 if (dir == GTK_TEXT_DIR_RTL) |
| |
2426 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, |
| |
2427 td->avatar, 0, 0, TOOLTIP_BORDER, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2428 else |
| |
2429 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, |
| |
2430 td->avatar, 0, 0, max_width - (td->avatar_width + TOOLTIP_BORDER), |
| |
2431 current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2432 } |
| |
2433 |
| |
2434 if (!td->avatar_is_prpl_icon) |
| |
2435 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->prpl_icon, |
| |
2436 0, 0, |
| |
2437 prpl_col, |
| |
2438 current_height + ((td->name_height / 2) - (PRPL_SIZE / 2)), |
| |
2439 -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2440 |
| |
2441 #else |
| |
2442 gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2443 if(td->avatar) |
| |
2444 gdk_pixbuf_render_to_drawable(td->avatar, |
| |
2445 GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, |
| |
2446 max_width - (td->avatar_width + TOOLTIP_BORDER), |
| |
2447 current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); |
| |
2448 #endif |
| |
2449 if (dir == GTK_TEXT_DIR_RTL) { |
| |
2450 gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, |
| |
2451 NULL, gtkblist->tipwindow, "tooltip", |
| |
2452 max_width -(TOOLTIP_BORDER + STATUS_SIZE +SMALL_SPACE) - PANGO_PIXELS(300000), |
| |
2453 current_height, td->name_layout); |
| |
2454 } else { |
| |
2455 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, |
| |
2456 NULL, gtkblist->tipwindow, "tooltip", |
| |
2457 TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height, td->name_layout); |
| |
2458 } |
| |
2459 if (dir != GTK_TEXT_DIR_RTL) { |
| |
2460 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, |
| |
2461 NULL, gtkblist->tipwindow, "tooltip", |
| |
2462 TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height + td->name_height, td->layout); |
| |
2463 } else { |
| |
2464 gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE, |
| |
2465 NULL, gtkblist->tipwindow, "tooltip", |
| |
2466 max_width - (TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE) - PANGO_PIXELS(300000), |
| |
2467 current_height + td->name_height, |
| |
2468 td->layout); |
| |
2469 } |
| |
2470 |
| |
2471 current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER; |
| |
2472 } |
| |
2473 } |
| |
2474 |
| |
2475 |
| |
2476 static void pidgin_blist_tooltip_destroy() |
| |
2477 { |
| |
2478 while(gtkblist->tooltipdata) { |
| |
2479 struct tooltip_data *td = gtkblist->tooltipdata->data; |
| |
2480 |
| |
2481 if(td->avatar) |
| |
2482 g_object_unref(td->avatar); |
| |
2483 if(td->status_icon) |
| |
2484 g_object_unref(td->status_icon); |
| |
2485 if(td->prpl_icon) |
| |
2486 g_object_unref(td->prpl_icon); |
| |
2487 g_object_unref(td->layout); |
| |
2488 g_object_unref(td->name_layout); |
| |
2489 g_free(td); |
| |
2490 gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata); |
| |
2491 } |
| |
2492 |
| |
2493 if (gtkblist->tipwindow == NULL) |
| |
2494 return; |
| |
2495 |
| |
2496 gtk_widget_destroy(gtkblist->tipwindow); |
| |
2497 gtkblist->tipwindow = NULL; |
| |
2498 } |
| |
2499 |
| |
2500 static gboolean pidgin_blist_expand_timeout(GtkWidget *tv) |
| |
2501 { |
| |
2502 GtkTreePath *path; |
| |
2503 GtkTreeIter iter; |
| |
2504 PurpleBlistNode *node; |
| |
2505 GValue val; |
| |
2506 struct _pidgin_blist_node *gtknode; |
| |
2507 |
| |
2508 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL)) |
| |
2509 return FALSE; |
| |
2510 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); |
| |
2511 val.g_type = 0; |
| |
2512 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
2513 node = g_value_get_pointer(&val); |
| |
2514 |
| |
2515 if(!PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
2516 gtk_tree_path_free(path); |
| |
2517 return FALSE; |
| |
2518 } |
| |
2519 |
| |
2520 gtknode = node->ui_data; |
| |
2521 |
| |
2522 if (!gtknode->contact_expanded) { |
| |
2523 GtkTreeIter i; |
| |
2524 |
| |
2525 pidgin_blist_expand_contact_cb(NULL, node); |
| |
2526 |
| |
2527 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->contact_rect); |
| |
2528 gdk_drawable_get_size(GDK_DRAWABLE(tv->window), &(gtkblist->contact_rect.width), NULL); |
| |
2529 gtkblist->mouseover_contact = node; |
| |
2530 gtk_tree_path_down (path); |
| |
2531 while (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &i, path)) { |
| |
2532 GdkRectangle rect; |
| |
2533 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect); |
| |
2534 gtkblist->contact_rect.height += rect.height; |
| |
2535 gtk_tree_path_next(path); |
| |
2536 } |
| |
2537 } |
| |
2538 gtk_tree_path_free(path); |
| |
2539 return FALSE; |
| |
2540 } |
| |
2541 |
| |
2542 static gboolean buddy_is_displayable(PurpleBuddy *buddy) |
| |
2543 { |
| |
2544 struct _pidgin_blist_node *gtknode; |
| |
2545 |
| |
2546 if(!buddy) |
| |
2547 return FALSE; |
| |
2548 |
| |
2549 gtknode = ((PurpleBlistNode*)buddy)->ui_data; |
| |
2550 |
| |
2551 return (purple_account_is_connected(buddy->account) && |
| |
2552 (purple_presence_is_online(buddy->presence) || |
| |
2553 (gtknode && gtknode->recent_signonoff) || |
| |
2554 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies") || |
| |
2555 purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline"))); |
| |
2556 } |
| |
2557 |
| |
2558 static gboolean pidgin_blist_tooltip_timeout(GtkWidget *tv) |
| |
2559 { |
| |
2560 GtkTreePath *path; |
| |
2561 GtkTreeIter iter; |
| |
2562 PurpleBlistNode *node; |
| |
2563 GValue val; |
| |
2564 int scr_w, scr_h, w, h, x, y; |
| |
2565 #if GTK_CHECK_VERSION(2,2,0) |
| |
2566 int mon_num; |
| |
2567 GdkScreen *screen = NULL; |
| |
2568 #endif |
| |
2569 gboolean tooltip_top = FALSE; |
| |
2570 struct _pidgin_blist_node *gtknode; |
| |
2571 GdkRectangle mon_size; |
| |
2572 |
| |
2573 /* |
| |
2574 * Attempt to free the previous tooltip. I have a feeling |
| |
2575 * this is never needed... but just in case. |
| |
2576 */ |
| |
2577 pidgin_blist_tooltip_destroy(); |
| |
2578 |
| |
2579 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL)) |
| |
2580 return FALSE; |
| |
2581 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); |
| |
2582 val.g_type = 0; |
| |
2583 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); |
| |
2584 node = g_value_get_pointer(&val); |
| |
2585 |
| |
2586 gtk_tree_path_free(path); |
| |
2587 |
| |
2588 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); |
| |
2589 |
| |
2590 if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
2591 struct tooltip_data *td = create_tip_for_node(node, TRUE); |
| |
2592 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); |
| |
2593 w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + |
| |
2594 MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER; |
| |
2595 h = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height)) |
| |
2596 + TOOLTIP_BORDER; |
| |
2597 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
2598 PurpleBlistNode *child; |
| |
2599 PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node); |
| |
2600 int max_text_width = 0; |
| |
2601 int max_avatar_width = 0; |
| |
2602 w = h = 0; |
| |
2603 |
| |
2604 for(child = node->child; child; child = child->next) |
| |
2605 { |
| |
2606 if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) { |
| |
2607 struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child)); |
| |
2608 if (b == (PurpleBuddy *)child) { |
| |
2609 gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td); |
| |
2610 } else { |
| |
2611 gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td); |
| |
2612 } |
| |
2613 max_text_width = MAX(max_text_width, MAX(td->width, td->name_width)); |
| |
2614 max_avatar_width = MAX(max_avatar_width, td->avatar_width); |
| |
2615 h += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height), |
| |
2616 TOOLTIP_BORDER + td->height + td->name_height); |
| |
2617 } |
| |
2618 } |
| |
2619 h += TOOLTIP_BORDER; |
| |
2620 w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER; |
| |
2621 } else { |
| |
2622 gtk_widget_destroy(gtkblist->tipwindow); |
| |
2623 gtkblist->tipwindow = NULL; |
| |
2624 return FALSE; |
| |
2625 } |
| |
2626 |
| |
2627 if (gtkblist->tooltipdata == NULL) { |
| |
2628 gtk_widget_destroy(gtkblist->tipwindow); |
| |
2629 gtkblist->tipwindow = NULL; |
| |
2630 return FALSE; |
| |
2631 } |
| |
2632 |
| |
2633 gtknode = node->ui_data; |
| |
2634 |
| |
2635 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); |
| |
2636 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); |
| |
2637 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); |
| |
2638 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", |
| |
2639 G_CALLBACK(pidgin_blist_paint_tip), NULL); |
| |
2640 gtk_widget_ensure_style (gtkblist->tipwindow); |
| |
2641 |
| |
2642 |
| |
2643 #if GTK_CHECK_VERSION(2,2,0) |
| |
2644 gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL); |
| |
2645 mon_num = gdk_screen_get_monitor_at_point(screen, x, y); |
| |
2646 gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size); |
| |
2647 |
| |
2648 scr_w = mon_size.width + mon_size.x; |
| |
2649 scr_h = mon_size.height + mon_size.y; |
| |
2650 #else |
| |
2651 scr_w = gdk_screen_width(); |
| |
2652 scr_h = gdk_screen_height(); |
| |
2653 gdk_window_get_pointer(NULL, &x, &y, NULL); |
| |
2654 mon_size.x = 0; |
| |
2655 mon_size.y = 0; |
| |
2656 #endif |
| |
2657 |
| |
2658 #if GTK_CHECK_VERSION(2,2,0) |
| |
2659 if (w > mon_size.width) |
| |
2660 w = mon_size.width - 10; |
| |
2661 |
| |
2662 if (h > mon_size.height) |
| |
2663 h = mon_size.height - 10; |
| |
2664 #endif |
| |
2665 |
| |
2666 if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) |
| |
2667 y+=gtkblist->window->allocation.y; |
| |
2668 |
| |
2669 x -= ((w >> 1) + 4); |
| |
2670 |
| |
2671 if ((y + h + 4) > scr_h || tooltip_top) |
| |
2672 y = y - h - 5; |
| |
2673 else |
| |
2674 y = y + 6; |
| |
2675 |
| |
2676 if (y < mon_size.y) |
| |
2677 y = mon_size.y; |
| |
2678 |
| |
2679 if (y != mon_size.y) { |
| |
2680 if ((x + w) > scr_w) |
| |
2681 x -= (x + w + 5) - scr_w; |
| |
2682 else if (x < mon_size.x) |
| |
2683 x = mon_size.x; |
| |
2684 } else { |
| |
2685 x -= (w / 2 + 10); |
| |
2686 if (x < mon_size.x) |
| |
2687 x = mon_size.x; |
| |
2688 } |
| |
2689 |
| |
2690 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); |
| |
2691 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); |
| |
2692 gtk_widget_show(gtkblist->tipwindow); |
| |
2693 |
| |
2694 return FALSE; |
| |
2695 } |
| |
2696 |
| |
2697 static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context, |
| |
2698 gint x, gint y, guint time, gpointer user_data) |
| |
2699 { |
| |
2700 GtkTreePath *path; |
| |
2701 int delay; |
| |
2702 |
| |
2703 /* |
| |
2704 * When dragging a buddy into a contact, this is the delay before |
| |
2705 * the contact auto-expands. |
| |
2706 */ |
| |
2707 delay = 900; |
| |
2708 |
| |
2709 if (gtkblist->drag_timeout) { |
| |
2710 if ((y > gtkblist->tip_rect.y) && ((y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) |
| |
2711 return FALSE; |
| |
2712 /* We've left the cell. Remove the timeout and create a new one below */ |
| |
2713 g_source_remove(gtkblist->drag_timeout); |
| |
2714 } |
| |
2715 |
| |
2716 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), x, y, &path, NULL, NULL, NULL); |
| |
2717 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); |
| |
2718 |
| |
2719 if (path) |
| |
2720 gtk_tree_path_free(path); |
| |
2721 gtkblist->drag_timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_expand_timeout, tv); |
| |
2722 |
| |
2723 if (gtkblist->mouseover_contact) { |
| |
2724 if ((y < gtkblist->contact_rect.y) || ((y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { |
| |
2725 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); |
| |
2726 gtkblist->mouseover_contact = NULL; |
| |
2727 } |
| |
2728 } |
| |
2729 |
| |
2730 return FALSE; |
| |
2731 } |
| |
2732 |
| |
2733 static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) |
| |
2734 { |
| |
2735 GtkTreePath *path; |
| |
2736 int delay; |
| |
2737 |
| |
2738 delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); |
| |
2739 |
| |
2740 if (delay == 0) |
| |
2741 return FALSE; |
| |
2742 |
| |
2743 if (gtkblist->timeout) { |
| |
2744 if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) |
| |
2745 return FALSE; |
| |
2746 /* We've left the cell. Remove the timeout and create a new one below */ |
| |
2747 pidgin_blist_tooltip_destroy(); |
| |
2748 g_source_remove(gtkblist->timeout); |
| |
2749 } |
| |
2750 |
| |
2751 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); |
| |
2752 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); |
| |
2753 |
| |
2754 if (path) |
| |
2755 gtk_tree_path_free(path); |
| |
2756 gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv); |
| |
2757 |
| |
2758 if (gtkblist->mouseover_contact) { |
| |
2759 if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { |
| |
2760 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); |
| |
2761 gtkblist->mouseover_contact = NULL; |
| |
2762 } |
| |
2763 } |
| |
2764 |
| |
2765 return FALSE; |
| |
2766 } |
| |
2767 |
| |
2768 static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) |
| |
2769 { |
| |
2770 |
| |
2771 if (gtkblist->timeout) { |
| |
2772 g_source_remove(gtkblist->timeout); |
| |
2773 gtkblist->timeout = 0; |
| |
2774 } |
| |
2775 |
| |
2776 if (gtkblist->drag_timeout) { |
| |
2777 g_source_remove(gtkblist->drag_timeout); |
| |
2778 gtkblist->drag_timeout = 0; |
| |
2779 } |
| |
2780 |
| |
2781 pidgin_blist_tooltip_destroy(); |
| |
2782 |
| |
2783 if (gtkblist->mouseover_contact && |
| |
2784 !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) && |
| |
2785 (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) { |
| |
2786 pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); |
| |
2787 gtkblist->mouseover_contact = NULL; |
| |
2788 } |
| |
2789 } |
| |
2790 |
| |
2791 static void |
| |
2792 toggle_debug(void) |
| |
2793 { |
| |
2794 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", |
| |
2795 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")); |
| |
2796 } |
| |
2797 |
| |
2798 |
| |
2799 /*************************************************** |
| |
2800 * Crap * |
| |
2801 ***************************************************/ |
| |
2802 static GtkItemFactoryEntry blist_menu[] = |
| |
2803 { |
| |
2804 /* Buddies menu */ |
| |
2805 { N_("/_Buddies"), NULL, NULL, 0, "<Branch>", NULL }, |
| |
2806 { N_("/Buddies/New Instant _Message..."), "<CTL>M", pidgindialogs_im, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW }, |
| |
2807 { N_("/Buddies/Join a _Chat..."), "<CTL>C", pidgin_blist_joinchat_show, 0, "<Item>", NULL }, |
| |
2808 { N_("/Buddies/Get User _Info..."), "<CTL>I", pidgindialogs_info, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_USER_INFO }, |
| |
2809 { N_("/Buddies/View User _Log..."), "<CTL>L", pidgindialogs_log, 0, "<Item>", NULL }, |
| |
2810 { "/Buddies/sep1", NULL, NULL, 0, "<Separator>", NULL }, |
| |
2811 { N_("/Buddies/Show _Offline Buddies"), NULL, pidgin_blist_edit_mode_cb, 1, "<CheckItem>", NULL }, |
| |
2812 { N_("/Buddies/Show _Empty Groups"), NULL, pidgin_blist_show_empty_groups_cb, 1, "<CheckItem>", NULL }, |
| |
2813 { N_("/Buddies/Show Buddy _Details"), NULL, pidgin_blist_buddy_details_cb, 1, "<CheckItem>", NULL }, |
| |
2814 { N_("/Buddies/Show Idle _Times"), NULL, pidgin_blist_show_idle_time_cb, 1, "<CheckItem>", NULL }, |
| |
2815 { N_("/Buddies/_Sort Buddies"), NULL, NULL, 0, "<Branch>", NULL }, |
| |
2816 { "/Buddies/sep2", NULL, NULL, 0, "<Separator>", NULL }, |
| |
2817 { N_("/Buddies/_Add Buddy..."), "<CTL>B", pidgin_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, |
| |
2818 { N_("/Buddies/Add C_hat..."), NULL, pidgin_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD }, |
| |
2819 { N_("/Buddies/Add _Group..."), NULL, purple_blist_request_add_group, 0, "<StockItem>", GTK_STOCK_ADD }, |
| |
2820 { "/Buddies/sep3", NULL, NULL, 0, "<Separator>", NULL }, |
| |
2821 { N_("/Buddies/_Quit"), "<CTL>Q", purple_core_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, |
| |
2822 |
| |
2823 /* Accounts menu */ |
| |
2824 { N_("/_Accounts"), NULL, NULL, 0, "<Branch>", NULL }, |
| |
2825 { N_("/Accounts/Add\\/Edit"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL }, |
| |
2826 |
| |
2827 /* Tools */ |
| |
2828 { N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL }, |
| |
2829 { N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 0, "<Item>", NULL }, |
| |
2830 { N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS }, |
| |
2831 { N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, |
| |
2832 { N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL }, |
| |
2833 { "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL }, |
| |
2834 { N_("/Tools/_File Transfers"), "<CTL>T", pidginxfer_dialog_show, 0, "<Item>", NULL }, |
| |
2835 { N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL }, |
| |
2836 { N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, "<Item>", NULL }, |
| |
2837 { "/Tools/sep3", NULL, NULL, 0, "<Separator>", NULL }, |
| |
2838 { N_("/Tools/Mute _Sounds"), "<CTL>S", pidgin_blist_mute_sounds_cb, 0, "<CheckItem>", NULL }, |
| |
2839 |
| |
2840 /* Help */ |
| |
2841 { N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL }, |
| |
2842 { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, |
| |
2843 { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL }, |
| |
2844 { N_("/Help/_About"), NULL, pidgindialogs_about, 0, "<StockItem>", PIDGIN_STOCK_ABOUT }, |
| |
2845 }; |
| |
2846 |
| |
2847 /********************************************************* |
| |
2848 * Private Utility functions * |
| |
2849 *********************************************************/ |
| |
2850 |
| |
2851 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full) |
| |
2852 { |
| |
2853 GString *str = g_string_new(""); |
| |
2854 PurplePlugin *prpl; |
| |
2855 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
2856 char *tmp; |
| |
2857 |
| |
2858 if (PURPLE_BLIST_NODE_IS_CHAT(node)) |
| |
2859 { |
| |
2860 PurpleChat *chat; |
| |
2861 GList *cur; |
| |
2862 struct proto_chat_entry *pce; |
| |
2863 char *name, *value; |
| |
2864 |
| |
2865 chat = (PurpleChat *)node; |
| |
2866 prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account)); |
| |
2867 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
2868 |
| |
2869 if (g_list_length(purple_connections_get_all()) > 1) |
| |
2870 { |
| |
2871 tmp = g_markup_escape_text(chat->account->username, -1); |
| |
2872 g_string_append_printf(str, _("\n<b>Account:</b> %s"), tmp); |
| |
2873 g_free(tmp); |
| |
2874 } |
| |
2875 |
| |
2876 if (prpl_info->chat_info != NULL) |
| |
2877 cur = prpl_info->chat_info(chat->account->gc); |
| |
2878 else |
| |
2879 cur = NULL; |
| |
2880 |
| |
2881 while (cur != NULL) |
| |
2882 { |
| |
2883 pce = cur->data; |
| |
2884 |
| |
2885 if (!pce->secret && (!pce->required && |
| |
2886 g_hash_table_lookup(chat->components, pce->identifier) == NULL)) |
| |
2887 { |
| |
2888 tmp = purple_text_strip_mnemonic(pce->label); |
| |
2889 name = g_markup_escape_text(tmp, -1); |
| |
2890 g_free(tmp); |
| |
2891 value = g_markup_escape_text(g_hash_table_lookup( |
| |
2892 chat->components, pce->identifier), -1); |
| |
2893 g_string_append_printf(str, "\n<b>%s</b> %s", |
| |
2894 name ? name : "", |
| |
2895 value ? value : ""); |
| |
2896 g_free(name); |
| |
2897 g_free(value); |
| |
2898 } |
| |
2899 |
| |
2900 g_free(pce); |
| |
2901 cur = g_list_remove(cur, pce); |
| |
2902 } |
| |
2903 } |
| |
2904 else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
2905 { |
| |
2906 /* NOTE: THIS FUNCTION IS NO LONGER CALLED FOR CONTACTS. |
| |
2907 * It is only called by create_tip_for_node(), and create_tip_for_node() is never called for a contact. |
| |
2908 */ |
| |
2909 PurpleContact *c; |
| |
2910 PurpleBuddy *b; |
| |
2911 PurplePresence *presence; |
| |
2912 PurpleNotifyUserInfo *user_info; |
| |
2913 char *tmp; |
| |
2914 time_t idle_secs, signon; |
| |
2915 |
| |
2916 if (PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
2917 { |
| |
2918 c = (PurpleContact *)node; |
| |
2919 b = purple_contact_get_priority_buddy(c); |
| |
2920 } |
| |
2921 else |
| |
2922 { |
| |
2923 b = (PurpleBuddy *)node; |
| |
2924 c = purple_buddy_get_contact(b); |
| |
2925 } |
| |
2926 |
| |
2927 prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); |
| |
2928 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
2929 |
| |
2930 presence = purple_buddy_get_presence(b); |
| |
2931 user_info = purple_notify_user_info_new(); |
| |
2932 |
| |
2933 /* Account */ |
| |
2934 if (full && g_list_length(purple_connections_get_all()) > 1) |
| |
2935 { |
| |
2936 tmp = g_markup_escape_text(purple_account_get_username( |
| |
2937 purple_buddy_get_account(b)), -1); |
| |
2938 purple_notify_user_info_add_pair(user_info, _("Account"), tmp); |
| |
2939 g_free(tmp); |
| |
2940 } |
| |
2941 |
| |
2942 /* Alias */ |
| |
2943 /* If there's not a contact alias, the node is being displayed with |
| |
2944 * this alias, so there's no point in showing it in the tooltip. */ |
| |
2945 if (full && b->alias != NULL && b->alias[0] != '\0' && |
| |
2946 (c->alias != NULL && c->alias[0] != '\0') && |
| |
2947 strcmp(c->alias, b->alias) != 0) |
| |
2948 { |
| |
2949 tmp = g_markup_escape_text(b->alias, -1); |
| |
2950 purple_notify_user_info_add_pair(user_info, _("Buddy Alias"), tmp); |
| |
2951 g_free(tmp); |
| |
2952 } |
| |
2953 |
| |
2954 /* Nickname/Server Alias */ |
| |
2955 /* I'd like to only show this if there's a contact or buddy |
| |
2956 * alias, but many people on MSN set long nicknames, which |
| |
2957 * get ellipsized, so the only way to see the whole thing is |
| |
2958 * to look at the tooltip. */ |
| |
2959 if (full && b->server_alias != NULL && b->server_alias[0] != '\0') |
| |
2960 { |
| |
2961 tmp = g_markup_escape_text(b->server_alias, -1); |
| |
2962 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp); |
| |
2963 g_free(tmp); |
| |
2964 } |
| |
2965 |
| |
2966 /* Logged In */ |
| |
2967 signon = purple_presence_get_login_time(presence); |
| |
2968 if (full && PURPLE_BUDDY_IS_ONLINE(b) && signon > 0) |
| |
2969 { |
| |
2970 tmp = purple_str_seconds_to_string(time(NULL) - signon); |
| |
2971 purple_notify_user_info_add_pair(user_info, _("Logged In"), tmp); |
| |
2972 g_free(tmp); |
| |
2973 } |
| |
2974 |
| |
2975 /* Idle */ |
| |
2976 if (purple_presence_is_idle(presence)) |
| |
2977 { |
| |
2978 idle_secs = purple_presence_get_idle_time(presence); |
| |
2979 if (idle_secs > 0) |
| |
2980 { |
| |
2981 tmp = purple_str_seconds_to_string(time(NULL) - idle_secs); |
| |
2982 purple_notify_user_info_add_pair(user_info, _("Idle"), tmp); |
| |
2983 g_free(tmp); |
| |
2984 } |
| |
2985 } |
| |
2986 |
| |
2987 /* Last Seen */ |
| |
2988 if (full && !PURPLE_BUDDY_IS_ONLINE(b)) |
| |
2989 { |
| |
2990 struct _pidgin_blist_node *gtknode = ((PurpleBlistNode *)c)->ui_data; |
| |
2991 PurpleBlistNode *bnode; |
| |
2992 int lastseen = 0; |
| |
2993 |
| |
2994 if (!gtknode->contact_expanded || PURPLE_BLIST_NODE_IS_CONTACT(node)) |
| |
2995 { |
| |
2996 /* We're either looking at a buddy for a collapsed contact or |
| |
2997 * an expanded contact itself so we show the most recent |
| |
2998 * (largest) last_seen time for any of the buddies under |
| |
2999 * the contact. */ |
| |
3000 for (bnode = ((PurpleBlistNode *)c)->child ; bnode != NULL ; bnode = bnode->next) |
| |
3001 { |
| |
3002 int value = purple_blist_node_get_int(bnode, "last_seen"); |
| |
3003 if (value > lastseen) |
| |
3004 lastseen = value; |
| |
3005 } |
| |
3006 } |
| |
3007 else |
| |
3008 { |
| |
3009 /* We're dealing with a buddy under an expanded contact, |
| |
3010 * so we show the last_seen time for the buddy. */ |
| |
3011 lastseen = purple_blist_node_get_int(&b->node, "last_seen"); |
| |
3012 } |
| |
3013 |
| |
3014 if (lastseen > 0) |
| |
3015 { |
| |
3016 tmp = purple_str_seconds_to_string(time(NULL) - lastseen); |
| |
3017 purple_notify_user_info_add_pair(user_info, _("Last Seen"), tmp); |
| |
3018 g_free(tmp); |
| |
3019 } |
| |
3020 } |
| |
3021 |
| |
3022 |
| |
3023 /* Offline? */ |
| |
3024 /* FIXME: Why is this status special-cased by the core? -- rlaager */ |
| |
3025 if (!PURPLE_BUDDY_IS_ONLINE(b)) { |
| |
3026 purple_notify_user_info_add_pair(user_info, _("Status"), _("Offline")); |
| |
3027 } |
| |
3028 |
| |
3029 if (prpl_info && prpl_info->tooltip_text) |
| |
3030 { |
| |
3031 /* Additional text from the PRPL */ |
| |
3032 prpl_info->tooltip_text(b, user_info, full); |
| |
3033 } |
| |
3034 |
| |
3035 /* These are Easter Eggs. Patches to remove them will be rejected. */ |
| |
3036 if (!g_ascii_strcasecmp(b->name, "robflynn")) |
| |
3037 purple_notify_user_info_add_pair(user_info, _("Description"), _("Spooky")); |
| |
3038 if (!g_ascii_strcasecmp(b->name, "seanegn")) |
| |
3039 purple_notify_user_info_add_pair(user_info, _("Status"), _("Awesome")); |
| |
3040 if (!g_ascii_strcasecmp(b->name, "chipx86")) |
| |
3041 purple_notify_user_info_add_pair(user_info, _("Status"), _("Rockin'")); |
| |
3042 |
| |
3043 tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n"); |
| |
3044 g_string_append(str, tmp); |
| |
3045 g_free(tmp); |
| |
3046 |
| |
3047 purple_notify_user_info_destroy(user_info); |
| |
3048 } |
| |
3049 |
| |
3050 purple_signal_emit(pidgin_blist_get_handle(), |
| |
3051 "drawing-tooltip", node, str, full); |
| |
3052 |
| |
3053 return g_string_free(str, FALSE); |
| |
3054 } |
| |
3055 |
| |
3056 GdkPixbuf * |
| |
3057 pidgin_blist_get_emblem(PurpleBlistNode *node) |
| |
3058 { |
| |
3059 PurpleBuddy *buddy = NULL; |
| |
3060 struct _pidgin_blist_node *gtknode = node->ui_data; |
| |
3061 struct _pidgin_blist_node *gtkbuddynode = NULL; |
| |
3062 PurplePlugin *prpl; |
| |
3063 PurplePluginProtocolInfo *prpl_info; |
| |
3064 const char *name = NULL; |
| |
3065 char *filename, *path; |
| |
3066 GdkPixbuf *ret; |
| |
3067 PurplePresence *p; |
| |
3068 |
| |
3069 |
| |
3070 |
| |
3071 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
3072 if(!gtknode->contact_expanded) { |
| |
3073 buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
3074 gtkbuddynode = ((PurpleBlistNode*)buddy)->ui_data; |
| |
3075 } |
| |
3076 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
3077 buddy = (PurpleBuddy*)node; |
| |
3078 gtkbuddynode = node->ui_data; |
| |
3079 if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) |
| |
3080 return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL); |
| |
3081 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
3082 return pidgin_create_prpl_icon(((PurpleChat*)node)->account, PIDGIN_PRPL_ICON_SMALL); |
| |
3083 } else { |
| |
3084 return NULL; |
| |
3085 } |
| |
3086 |
| |
3087 if (!purple_privacy_check(buddy->account, purple_buddy_get_name(buddy))) { |
| |
3088 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "blocked.png", NULL); |
| |
3089 ret = gdk_pixbuf_new_from_file(path, NULL); |
| |
3090 g_free(path); |
| |
3091 return ret; |
| |
3092 } |
| |
3093 |
| |
3094 p = purple_buddy_get_presence(buddy); |
| |
3095 if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { |
| |
3096 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL); |
| |
3097 ret = gdk_pixbuf_new_from_file(path, NULL); |
| |
3098 g_free(path); |
| |
3099 return ret; |
| |
3100 } |
| |
3101 |
| |
3102 prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); |
| |
3103 if (!prpl) |
| |
3104 return NULL; |
| |
3105 |
| |
3106 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
3107 if (prpl_info && prpl_info->list_emblem) |
| |
3108 name = prpl_info->list_emblem(buddy); |
| |
3109 |
| |
3110 if (name == NULL) |
| |
3111 return NULL; |
| |
3112 |
| |
3113 filename = g_strdup_printf("%s.png", name); |
| |
3114 |
| |
3115 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL); |
| |
3116 ret = gdk_pixbuf_new_from_file(path, NULL); |
| |
3117 |
| |
3118 g_free(filename); |
| |
3119 g_free(path); |
| |
3120 |
| |
3121 return ret; |
| |
3122 } |
| |
3123 |
| |
3124 |
| |
3125 GdkPixbuf * |
| |
3126 pidgin_blist_get_status_icon(PurpleBlistNode *node, PidginStatusIconSize size) |
| |
3127 { |
| |
3128 GdkPixbuf *ret; |
| |
3129 const char *protoname = NULL; |
| |
3130 struct _pidgin_blist_node *gtknode = node->ui_data; |
| |
3131 struct _pidgin_blist_node *gtkbuddynode = NULL; |
| |
3132 PurpleBuddy *buddy = NULL; |
| |
3133 PurpleChat *chat = NULL; |
| |
3134 GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_SMALL : |
| |
3135 PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); |
| |
3136 |
| |
3137 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
3138 if(!gtknode->contact_expanded) { |
| |
3139 buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
3140 gtkbuddynode = ((PurpleBlistNode*)buddy)->ui_data; |
| |
3141 } |
| |
3142 } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
| |
3143 buddy = (PurpleBuddy*)node; |
| |
3144 gtkbuddynode = node->ui_data; |
| |
3145 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
3146 chat = (PurpleChat*)node; |
| |
3147 } else { |
| |
3148 return NULL; |
| |
3149 } |
| |
3150 |
| |
3151 if(buddy || chat) { |
| |
3152 PurpleAccount *account; |
| |
3153 PurplePlugin *prpl; |
| |
3154 PurplePluginProtocolInfo *prpl_info; |
| |
3155 |
| |
3156 if(buddy) |
| |
3157 account = buddy->account; |
| |
3158 else |
| |
3159 account = chat->account; |
| |
3160 |
| |
3161 prpl = purple_find_prpl(purple_account_get_protocol_id(account)); |
| |
3162 if(!prpl) |
| |
3163 return NULL; |
| |
3164 |
| |
3165 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
3166 |
| |
3167 if(prpl_info && prpl_info->list_icon) { |
| |
3168 protoname = prpl_info->list_icon(account, buddy); |
| |
3169 } |
| |
3170 } |
| |
3171 |
| |
3172 if(buddy) { |
| |
3173 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, |
| |
3174 purple_buddy_get_name(buddy), |
| |
3175 purple_buddy_get_account(buddy)); |
| |
3176 PurplePresence *p; |
| |
3177 if(conv != NULL) { |
| |
3178 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); |
| |
3179 if(gtkconv != NULL && pidgin_conv_is_hidden(gtkconv) && size == PIDGIN_STATUS_ICON_SMALL) { |
| |
3180 return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE, |
| |
3181 icon_size, "GtkTreeView"); |
| |
3182 } |
| |
3183 } |
| |
3184 p = purple_buddy_get_presence(buddy); |
| |
3185 |
| |
3186 if (PURPLE_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) |
| |
3187 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_LOGIN, |
| |
3188 icon_size, "GtkTreeView"); |
| |
3189 else if (gtkbuddynode && gtkbuddynode->recent_signonoff) |
| |
3190 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_LOGOUT, |
| |
3191 icon_size, "GtkTreeView"); |
| |
3192 else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) |
| |
3193 if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL) |
| |
3194 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_BUSY_I, |
| |
3195 icon_size, "GtkTreeView"); |
| |
3196 else |
| |
3197 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_BUSY, |
| |
3198 icon_size, "GtkTreeView"); |
| |
3199 else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY)) |
| |
3200 if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL) |
| |
3201 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AWAY_I, |
| |
3202 icon_size, "GtkTreeView"); |
| |
3203 else |
| |
3204 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AWAY, |
| |
3205 icon_size, "GtkTreeView"); |
| |
3206 else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY)) |
| |
3207 if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL) |
| |
3208 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_XA_I, |
| |
3209 icon_size, "GtkTreeView"); |
| |
3210 else |
| |
3211 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_XA, |
| |
3212 icon_size, "GtkTreeView"); |
| |
3213 else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) |
| |
3214 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_OFFLINE, |
| |
3215 icon_size, "GtkTreeView"); |
| |
3216 else if (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL) |
| |
3217 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE_I, |
| |
3218 icon_size, "GtkTreeView"); |
| |
3219 else |
| |
3220 ret = gtk_widget_render_icon(GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_AVAILABLE, |
| |
3221 icon_size, "GtkTreeView"); |
| |
3222 } else if (chat) { |
| |
3223 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_CHAT, |
| |
3224 icon_size, "GtkTreeView"); |
| |
3225 } else { |
| |
3226 ret = gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_PERSON, |
| |
3227 icon_size, "GtkTreeView"); |
| |
3228 } |
| |
3229 |
| |
3230 return ret; |
| |
3231 } |
| |
3232 |
| |
3233 static gchar *pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected) |
| |
3234 { |
| |
3235 const char *name; |
| |
3236 char *esc, *text = NULL; |
| |
3237 PurplePlugin *prpl; |
| |
3238 PurplePluginProtocolInfo *prpl_info = NULL; |
| |
3239 PurpleContact *contact; |
| |
3240 PurplePresence *presence; |
| |
3241 struct _pidgin_blist_node *gtkcontactnode = NULL; |
| |
3242 char *idletime = NULL, *statustext = NULL; |
| |
3243 time_t t; |
| |
3244 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, |
| |
3245 purple_buddy_get_name(b), |
| |
3246 purple_buddy_get_account(b)); |
| |
3247 PidginConversation *gtkconv; |
| |
3248 gboolean hidden_conv = FALSE; |
| |
3249 |
| |
3250 if(conv != NULL) { |
| |
3251 gtkconv = PIDGIN_CONVERSATION(conv); |
| |
3252 if(gtkconv != NULL && pidgin_conv_is_hidden(gtkconv)) { |
| |
3253 hidden_conv = TRUE; |
| |
3254 } |
| |
3255 } |
| |
3256 |
| |
3257 /* XXX Good luck cleaning up this crap */ |
| |
3258 |
| |
3259 contact = (PurpleContact*)((PurpleBlistNode*)b)->parent; |
| |
3260 if(contact) |
| |
3261 gtkcontactnode = ((PurpleBlistNode*)contact)->ui_data; |
| |
3262 |
| |
3263 if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) |
| |
3264 name = contact->alias; |
| |
3265 else |
| |
3266 name = purple_buddy_get_alias(b); |
| |
3267 esc = g_markup_escape_text(name, strlen(name)); |
| |
3268 |
| |
3269 presence = purple_buddy_get_presence(b); |
| |
3270 |
| |
3271 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) |
| |
3272 { |
| |
3273 if (!selected && purple_presence_is_idle(presence)) |
| |
3274 { |
| |
3275 text = g_strdup_printf("<span color='%s'>%s</span>", |
| |
3276 dim_grey(), esc); |
| |
3277 g_free(esc); |
| |
3278 if (hidden_conv) { |
| |
3279 char *tmp = text; |
| |
3280 text = g_strdup_printf("<b>%s</b>", text); |
| |
3281 g_free(tmp); |
| |
3282 } |
| |
3283 return text; |
| |
3284 } |
| |
3285 else |
| |
3286 if (hidden_conv) { |
| |
3287 char *tmp = esc; |
| |
3288 esc = g_strdup_printf("<b>%s</b>", esc); |
| |
3289 g_free(tmp); |
| |
3290 } |
| |
3291 return esc; |
| |
3292 } |
| |
3293 |
| |
3294 prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); |
| |
3295 |
| |
3296 if (prpl != NULL) |
| |
3297 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); |
| |
3298 |
| |
3299 if (prpl_info && prpl_info->status_text && b->account->gc) { |
| |
3300 char *tmp = prpl_info->status_text(b); |
| |
3301 const char *end; |
| |
3302 |
| |
3303 if(tmp && !g_utf8_validate(tmp, -1, &end)) { |
| |
3304 char *new = g_strndup(tmp, |
| |
3305 g_utf8_pointer_to_offset(tmp, end)); |
| |
3306 g_free(tmp); |
| |
3307 tmp = new; |
| |
3308 } |
| |
3309 |
| |
3310 #if !GTK_CHECK_VERSION(2,6,0) |
| |
3311 if(tmp) { |
| |
3312 char buf[32]; |
| |
3313 char *c = tmp; |
| |
3314 int length = 0, vis=0; |
| |
3315 gboolean inside = FALSE; |
| |
3316 g_strdelimit(tmp, "\n", ' '); |
| |
3317 purple_str_strip_char(tmp, '\r'); |
| |
3318 |
| |
3319 while(*c && vis < 20) { |
| |
3320 if(*c == '&') |
| |
3321 inside = TRUE; |
| |
3322 else if(*c == ';') |
| |
3323 inside = FALSE; |
| |
3324 if(!inside) |
| |
3325 vis++; |
| |
3326 c = g_utf8_next_char(c); /* this is fun */ |
| |
3327 } |
| |
3328 |
| |
3329 length = c - tmp; |
| |
3330 |
| |
3331 if(vis == 20) |
| |
3332 g_snprintf(buf, sizeof(buf), "%%.%ds...", length); |
| |
3333 else |
| |
3334 g_snprintf(buf, sizeof(buf), "%%s "); |
| |
3335 |
| |
3336 statustext = g_strdup_printf(buf, tmp); |
| |
3337 |
| |
3338 g_free(tmp); |
| |
3339 } |
| |
3340 #else |
| |
3341 if(tmp) { |
| |
3342 g_strdelimit(tmp, "\n", ' '); |
| |
3343 purple_str_strip_char(tmp, '\r'); |
| |
3344 } |
| |
3345 statustext = tmp; |
| |
3346 #endif |
| |
3347 } |
| |
3348 |
| |
3349 if(!purple_presence_is_online(presence) && !statustext) |
| |
3350 statustext = g_strdup(_("Offline")); |
| |
3351 else if (!statustext) |
| |
3352 text = g_strdup(esc); |
| |
3353 |
| |
3354 if (purple_presence_is_idle(presence)) { |
| |
3355 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) { |
| |
3356 time_t idle_secs = purple_presence_get_idle_time(presence); |
| |
3357 |
| |
3358 if (idle_secs > 0) { |
| |
3359 int ihrs, imin; |
| |
3360 |
| |
3361 time(&t); |
| |
3362 ihrs = (t - idle_secs) / 3600; |
| |
3363 imin = ((t - idle_secs) / 60) % 60; |
| |
3364 |
| |
3365 if (ihrs) |
| |
3366 idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin); |
| |
3367 else |
| |
3368 idletime = g_strdup_printf(_("Idle %dm"), imin); |
| |
3369 } |
| |
3370 else |
| |
3371 idletime = g_strdup(_("Idle")); |
| |
3372 |
| |
3373 if (!selected) |
| |
3374 text = g_strdup_printf("<span color='%s'>%s</span>\n" |
| |
3375 "<span color='%s' size='smaller'>%s%s%s</span>", |
| |
3376 dim_grey(), esc, dim_grey(), |
| |
3377 idletime != NULL ? idletime : "", |
| |
3378 (idletime != NULL && statustext != NULL) ? " - " : "", |
| |
3379 statustext != NULL ? statustext : ""); |
| |
3380 } |
| |
3381 else if (!selected && !statustext) /* We handle selected text later */ |
| |
3382 text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc); |
| |
3383 else if (!selected && !text) |
| |
3384 text = g_strdup_printf("<span color='%s'>%s</span>\n" |
| |
3385 "<span color='%s' size='smaller'>%s</span>", |
| |
3386 dim_grey(), esc, dim_grey(), |
| |
3387 statustext != NULL ? statustext : ""); |
| |
3388 } |
| |
3389 |
| |
3390 /* Not idle and not selected */ |
| |
3391 else if (!selected && !text) |
| |
3392 { |
| |
3393 text = g_strdup_printf("%s\n" |
| |
3394 "<span color='%s' size='smaller'>%s</span>", |
| |
3395 esc, dim_grey(), |
| |
3396 statustext != NULL ? statustext : ""); |
| |
3397 } |
| |
3398 |
| |
3399 /* It is selected. */ |
| |
3400 if ((selected && !text) || (selected && idletime)) |
| |
3401 text = g_strdup_printf("%s\n" |
| |
3402 "<span size='smaller'>%s%s%s</span>", |
| |
3403 esc, |
| |
3404 idletime != NULL ? idletime : "", |
| |
3405 (idletime != NULL && statustext != NULL) ? " - " : "", |
| |
3406 statustext != NULL ? statustext : ""); |
| |
3407 |
| |
3408 g_free(idletime); |
| |
3409 g_free(statustext); |
| |
3410 g_free(esc); |
| |
3411 |
| |
3412 if (hidden_conv) { |
| |
3413 char *tmp = text; |
| |
3414 text = g_strdup_printf("<b>%s</b>", tmp); |
| |
3415 g_free(tmp); |
| |
3416 } |
| |
3417 |
| |
3418 return text; |
| |
3419 } |
| |
3420 |
| |
3421 static void pidgin_blist_restore_position() |
| |
3422 { |
| |
3423 int blist_x, blist_y, blist_width, blist_height; |
| |
3424 |
| |
3425 blist_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width"); |
| |
3426 |
| |
3427 /* if the window exists, is hidden, we're saving positions, and the |
| |
3428 * position is sane... */ |
| |
3429 if (gtkblist && gtkblist->window && |
| |
3430 !GTK_WIDGET_VISIBLE(gtkblist->window) && blist_width != 0) { |
| |
3431 |
| |
3432 blist_x = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/x"); |
| |
3433 blist_y = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/y"); |
| |
3434 blist_height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/height"); |
| |
3435 |
| |
3436 /* ...check position is on screen... */ |
| |
3437 if (blist_x >= gdk_screen_width()) |
| |
3438 blist_x = gdk_screen_width() - 100; |
| |
3439 else if (blist_x + blist_width < 0) |
| |
3440 blist_x = 100; |
| |
3441 |
| |
3442 if (blist_y >= gdk_screen_height()) |
| |
3443 blist_y = gdk_screen_height() - 100; |
| |
3444 else if (blist_y + blist_height < 0) |
| |
3445 blist_y = 100; |
| |
3446 |
| |
3447 /* ...and move it back. */ |
| |
3448 gtk_window_move(GTK_WINDOW(gtkblist->window), blist_x, blist_y); |
| |
3449 gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_width, blist_height); |
| |
3450 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized")) |
| |
3451 gtk_window_maximize(GTK_WINDOW(gtkblist->window)); |
| |
3452 } |
| |
3453 } |
| |
3454 |
| |
3455 static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list) |
| |
3456 { |
| |
3457 PurpleBlistNode *gnode, *cnode; |
| |
3458 |
| |
3459 if (gtk_blist_obscured || !GTK_WIDGET_VISIBLE(gtkblist->window)) |
| |
3460 return TRUE; |
| |
3461 |
| |
3462 for(gnode = list->root; gnode; gnode = gnode->next) { |
| |
3463 if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) |
| |
3464 continue; |
| |
3465 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
| |
3466 if(PURPLE_BLIST_NODE_IS_CONTACT(cnode)) { |
| |
3467 PurpleBuddy *buddy; |
| |
3468 |
| |
3469 buddy = purple_contact_get_priority_buddy((PurpleContact*)cnode); |
| |
3470 |
| |
3471 if (buddy && |
| |
3472 purple_presence_is_idle(purple_buddy_get_presence(buddy))) |
| |
3473 pidgin_blist_update_contact(list, (PurpleBlistNode*)buddy); |
| |
3474 } |
| |
3475 } |
| |
3476 } |
| |
3477 |
| |
3478 /* keep on going */ |
| |
3479 return TRUE; |
| |
3480 } |
| |
3481 |
| |
3482 static void pidgin_blist_hide_node(PurpleBuddyList *list, PurpleBlistNode *node, gboolean update) |
| |
3483 { |
| |
3484 struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
3485 GtkTreeIter iter; |
| |
3486 |
| |
3487 if (!gtknode || !gtknode->row || !gtkblist) |
| |
3488 return; |
| |
3489 |
| |
3490 if(gtkblist->selected_node == node) |
| |
3491 gtkblist->selected_node = NULL; |
| |
3492 if (get_iter_from_node(node, &iter)) { |
| |
3493 gtk_tree_store_remove(gtkblist->treemodel, &iter); |
| |
3494 if(update && (PURPLE_BLIST_NODE_IS_CONTACT(node) || |
| |
3495 PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CHAT(node))) { |
| |
3496 pidgin_blist_update(list, node->parent); |
| |
3497 } |
| |
3498 } |
| |
3499 gtk_tree_row_reference_free(gtknode->row); |
| |
3500 gtknode->row = NULL; |
| |
3501 } |
| |
3502 |
| |
3503 static const char *require_connection[] = |
| |
3504 { |
| |
3505 N_("/Buddies/New Instant Message..."), |
| |
3506 N_("/Buddies/Join a Chat..."), |
| |
3507 N_("/Buddies/Get User Info..."), |
| |
3508 N_("/Buddies/Add Buddy..."), |
| |
3509 N_("/Buddies/Add Chat..."), |
| |
3510 N_("/Buddies/Add Group..."), |
| |
3511 }; |
| |
3512 |
| |
3513 static const int require_connection_size = sizeof(require_connection) |
| |
3514 / sizeof(*require_connection); |
| |
3515 |
| |
3516 /** |
| |
3517 * Rebuild dynamic menus and make menu items sensitive/insensitive |
| |
3518 * where appropriate. |
| |
3519 */ |
| |
3520 static void |
| |
3521 update_menu_bar(PidginBuddyList *gtkblist) |
| |
3522 { |
| |
3523 GtkWidget *widget; |
| |
3524 gboolean sensitive; |
| |
3525 int i; |
| |
3526 |
| |
3527 g_return_if_fail(gtkblist != NULL); |
| |
3528 |
| |
3529 pidgin_blist_update_accounts_menu(); |
| |
3530 |
| |
3531 sensitive = (purple_connections_get_all() != NULL); |
| |
3532 |
| |
3533 for (i = 0; i < require_connection_size; i++) |
| |
3534 { |
| |
3535 widget = gtk_item_factory_get_widget(gtkblist->ift, require_connection[i]); |
| |
3536 gtk_widget_set_sensitive(widget, sensitive); |
| |
3537 } |
| |
3538 |
| |
3539 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Join a Chat...")); |
| |
3540 gtk_widget_set_sensitive(widget, pidgin_blist_joinchat_is_showable()); |
| |
3541 |
| |
3542 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Add Chat...")); |
| |
3543 gtk_widget_set_sensitive(widget, pidgin_blist_joinchat_is_showable()); |
| |
3544 |
| |
3545 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounces")); |
| |
3546 gtk_widget_set_sensitive(widget, (purple_accounts_get_all() != NULL)); |
| |
3547 |
| |
3548 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Privacy")); |
| |
3549 gtk_widget_set_sensitive(widget, (purple_connections_get_all() != NULL)); |
| |
3550 |
| |
3551 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List")); |
| |
3552 gtk_widget_set_sensitive(widget, pidgin_roomlist_is_showable()); |
| |
3553 } |
| |
3554 |
| |
3555 static void |
| |
3556 sign_on_off_cb(PurpleConnection *gc, PurpleBuddyList *blist) |
| |
3557 { |
| |
3558 PidginBuddyList *gtkblist = PIDGIN_BLIST(blist); |
| |
3559 |
| |
3560 update_menu_bar(gtkblist); |
| |
3561 } |
| |
3562 |
| |
3563 static void |
| |
3564 plugin_changed_cb(PurplePlugin *p, gpointer *data) |
| |
3565 { |
| |
3566 pidgin_blist_update_plugin_actions(); |
| |
3567 } |
| |
3568 |
| |
3569 static void |
| |
3570 unseen_conv_menu() |
| |
3571 { |
| |
3572 static GtkWidget *menu = NULL; |
| |
3573 GList *convs = NULL; |
| |
3574 |
| |
3575 if (menu) { |
| |
3576 gtk_widget_destroy(menu); |
| |
3577 menu = NULL; |
| |
3578 } |
| |
3579 |
| |
3580 convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); |
| |
3581 if (!convs) |
| |
3582 /* no conversations added, don't show the menu */ |
| |
3583 return; |
| |
3584 |
| |
3585 menu = gtk_menu_new(); |
| |
3586 |
| |
3587 pidgin_conversations_fill_menu(menu, convs); |
| |
3588 g_list_free(convs); |
| |
3589 gtk_widget_show_all(menu); |
| |
3590 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, |
| |
3591 gtk_get_current_event_time()); |
| |
3592 } |
| |
3593 |
| |
3594 static gboolean |
| |
3595 menutray_press_cb(GtkWidget *widget, GdkEventButton *event) |
| |
3596 { |
| |
3597 GList *convs; |
| |
3598 |
| |
3599 switch (event->button) { |
| |
3600 case 1: |
| |
3601 convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, |
| |
3602 PIDGIN_UNSEEN_TEXT, TRUE, 1); |
| |
3603 if (convs) { |
| |
3604 purple_conversation_present((PurpleConversation*)convs->data); |
| |
3605 g_list_free(convs); |
| |
3606 } |
| |
3607 break; |
| |
3608 case 3: |
| |
3609 unseen_conv_menu(); |
| |
3610 break; |
| |
3611 } |
| |
3612 return TRUE; |
| |
3613 } |
| |
3614 |
| |
3615 static void |
| |
3616 conversation_updated_cb(PurpleConversation *conv, PurpleConvUpdateType type, |
| |
3617 PidginBuddyList *gtkblist) |
| |
3618 { |
| |
3619 GList *convs = NULL; |
| |
3620 GList *l = NULL; |
| |
3621 |
| |
3622 if (type != PURPLE_CONV_UPDATE_UNSEEN) |
| |
3623 return; |
| |
3624 |
| |
3625 if(conv->account != NULL && conv->name != NULL) { |
| |
3626 PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name); |
| |
3627 if(buddy != NULL) |
| |
3628 pidgin_blist_update_buddy(NULL, (PurpleBlistNode *)buddy, TRUE); |
| |
3629 } |
| |
3630 |
| |
3631 if (gtkblist->menutrayicon) { |
| |
3632 gtk_widget_destroy(gtkblist->menutrayicon); |
| |
3633 gtkblist->menutrayicon = NULL; |
| |
3634 } |
| |
3635 |
| |
3636 convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); |
| |
3637 if (convs) { |
| |
3638 GtkWidget *img = NULL; |
| |
3639 GString *tooltip_text = NULL; |
| |
3640 |
| |
3641 tooltip_text = g_string_new(""); |
| |
3642 l = convs; |
| |
3643 while (l != NULL) { |
| |
3644 if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) { |
| |
3645 PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data); |
| |
3646 |
| |
3647 g_string_append_printf(tooltip_text, |
| |
3648 ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), |
| |
3649 gtkconv->unseen_count, |
| |
3650 gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); |
| |
3651 } |
| |
3652 l = l->next; |
| |
3653 } |
| |
3654 if(tooltip_text->len > 0) { |
| |
3655 /* get rid of the last newline */ |
| |
3656 g_string_truncate(tooltip_text, tooltip_text->len -1); |
| |
3657 img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_PENDING, |
| |
3658 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); |
| |
3659 |
| |
3660 gtkblist->menutrayicon = gtk_event_box_new(); |
| |
3661 gtk_container_add(GTK_CONTAINER(gtkblist->menutrayicon), img); |
| |
3662 gtk_widget_show(img); |
| |
3663 gtk_widget_show(gtkblist->menutrayicon); |
| |
3664 g_signal_connect(G_OBJECT(gtkblist->menutrayicon), "button-press-event", G_CALLBACK(menutray_press_cb), NULL); |
| |
3665 |
| |
3666 pidgin_menu_tray_append(PIDGIN_MENU_TRAY(gtkblist->menutray), gtkblist->menutrayicon, tooltip_text->str); |
| |
3667 } |
| |
3668 g_string_free(tooltip_text, TRUE); |
| |
3669 g_list_free(convs); |
| |
3670 } |
| |
3671 } |
| |
3672 |
| |
3673 static void |
| |
3674 conversation_deleting_cb(PurpleConversation *conv, PidginBuddyList *gtkblist) |
| |
3675 { |
| |
3676 conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist); |
| |
3677 } |
| |
3678 |
| |
3679 /********************************************************************************** |
| |
3680 * Public API Functions * |
| |
3681 **********************************************************************************/ |
| |
3682 |
| |
3683 static void pidgin_blist_new_list(PurpleBuddyList *blist) |
| |
3684 { |
| |
3685 PidginBuddyList *gtkblist; |
| |
3686 |
| |
3687 gtkblist = g_new0(PidginBuddyList, 1); |
| |
3688 gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash, |
| |
3689 g_direct_equal, NULL, g_free); |
| |
3690 blist->ui_data = gtkblist; |
| |
3691 } |
| |
3692 |
| |
3693 static void pidgin_blist_new_node(PurpleBlistNode *node) |
| |
3694 { |
| |
3695 node->ui_data = g_new0(struct _pidgin_blist_node, 1); |
| |
3696 } |
| |
3697 |
| |
3698 gboolean pidgin_blist_node_is_contact_expanded(PurpleBlistNode *node) |
| |
3699 { |
| |
3700 if PURPLE_BLIST_NODE_IS_BUDDY(node) |
| |
3701 node = node->parent; |
| |
3702 |
| |
3703 g_return_val_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node), FALSE); |
| |
3704 |
| |
3705 return ((struct _pidgin_blist_node *)node->ui_data)->contact_expanded; |
| |
3706 } |
| |
3707 |
| |
3708 enum { |
| |
3709 DRAG_BUDDY, |
| |
3710 DRAG_ROW, |
| |
3711 DRAG_VCARD, |
| |
3712 DRAG_TEXT, |
| |
3713 DRAG_URI, |
| |
3714 NUM_TARGETS |
| |
3715 }; |
| |
3716 |
| |
3717 static const char * |
| |
3718 item_factory_translate_func (const char *path, gpointer func_data) |
| |
3719 { |
| |
3720 return _((char *)path); |
| |
3721 } |
| |
3722 |
| |
3723 void pidgin_blist_setup_sort_methods() |
| |
3724 { |
| |
3725 pidgin_blist_sort_method_reg("none", _("Manually"), sort_method_none); |
| |
3726 #if GTK_CHECK_VERSION(2,2,1) |
| |
3727 pidgin_blist_sort_method_reg("alphabetical", _("Alphabetically"), sort_method_alphabetical); |
| |
3728 pidgin_blist_sort_method_reg("status", _("By status"), sort_method_status); |
| |
3729 pidgin_blist_sort_method_reg("log_size", _("By log size"), sort_method_log); |
| |
3730 #endif |
| |
3731 pidgin_blist_sort_method_set(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type")); |
| |
3732 } |
| |
3733 |
| |
3734 static void _prefs_change_redo_list() |
| |
3735 { |
| |
3736 GtkTreeSelection *sel; |
| |
3737 GtkTreeIter iter; |
| |
3738 PurpleBlistNode *node = NULL; |
| |
3739 |
| |
3740 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
3741 if (gtk_tree_selection_get_selected(sel, NULL, &iter)) |
| |
3742 { |
| |
3743 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); |
| |
3744 } |
| |
3745 |
| |
3746 redo_buddy_list(purple_get_blist(), FALSE, FALSE); |
| |
3747 #if GTK_CHECK_VERSION(2,6,0) |
| |
3748 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
3749 #endif |
| |
3750 |
| |
3751 if (node) |
| |
3752 { |
| |
3753 struct _pidgin_blist_node *gtknode; |
| |
3754 GtkTreePath *path; |
| |
3755 |
| |
3756 gtknode = node->ui_data; |
| |
3757 if (gtknode && gtknode->row) |
| |
3758 { |
| |
3759 path = gtk_tree_row_reference_get_path(gtknode->row); |
| |
3760 gtk_tree_selection_select_path(sel, path); |
| |
3761 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(gtkblist->treeview), path, NULL, FALSE, 0, 0); |
| |
3762 gtk_tree_path_free(path); |
| |
3763 } |
| |
3764 } |
| |
3765 } |
| |
3766 |
| |
3767 static void _prefs_change_sort_method(const char *pref_name, PurplePrefType type, |
| |
3768 gconstpointer val, gpointer data) |
| |
3769 { |
| |
3770 if(!strcmp(pref_name, PIDGIN_PREFS_ROOT "/blist/sort_type")) |
| |
3771 pidgin_blist_sort_method_set(val); |
| |
3772 } |
| |
3773 |
| |
3774 static void account_modified(PurpleAccount *account, PidginBuddyList *gtkblist) |
| |
3775 { |
| |
3776 GList *list; |
| |
3777 if (!gtkblist) |
| |
3778 return; |
| |
3779 |
| |
3780 if ((list = purple_accounts_get_all_active()) != NULL) { |
| |
3781 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1); |
| |
3782 g_list_free(list); |
| |
3783 } else |
| |
3784 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 0); |
| |
3785 |
| |
3786 update_menu_bar(gtkblist); |
| |
3787 } |
| |
3788 |
| |
3789 static void |
| |
3790 account_status_changed(PurpleAccount *account, PurpleStatus *old, |
| |
3791 PurpleStatus *new, PidginBuddyList *gtkblist) |
| |
3792 { |
| |
3793 if (!gtkblist) |
| |
3794 return; |
| |
3795 |
| |
3796 update_menu_bar(gtkblist); |
| |
3797 } |
| |
3798 |
| |
3799 static gboolean |
| |
3800 gtk_blist_window_key_press_cb(GtkWidget *w, GdkEventKey *event, PidginBuddyList *gtkblist) |
| |
3801 { |
| |
3802 GtkWidget *imhtml; |
| |
3803 |
| |
3804 if (!gtkblist) |
| |
3805 return FALSE; |
| |
3806 |
| |
3807 imhtml = gtk_window_get_focus(GTK_WINDOW(gtkblist->window)); |
| |
3808 |
| |
3809 if (GTK_IS_IMHTML(imhtml) && gtk_bindings_activate(GTK_OBJECT(imhtml), event->keyval, event->state)) |
| |
3810 return TRUE; |
| |
3811 return FALSE; |
| |
3812 } |
| |
3813 |
| |
3814 static gboolean |
| |
3815 headline_hover_close(int x, int y) |
| |
3816 { |
| |
3817 GtkWidget *w = gtkblist->headline_hbox; |
| |
3818 if (x <= w->allocation.width && x >= w->allocation.width - HEADLINE_CLOSE_SIZE && |
| |
3819 y >= 0 && y <= HEADLINE_CLOSE_SIZE) |
| |
3820 return TRUE; |
| |
3821 return FALSE; |
| |
3822 } |
| |
3823 |
| |
3824 static gboolean |
| |
3825 headline_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, PidginBuddyList *gtkblist) |
| |
3826 { |
| |
3827 gdk_window_set_cursor(widget->window, gtkblist->hand_cursor); |
| |
3828 |
| |
3829 if (gtkblist->headline_close) { |
| |
3830 #if GTK_CHECK_VERSION(2,2,0) |
| |
3831 gdk_draw_pixbuf(widget->window, NULL, gtkblist->headline_close, |
| |
3832 #else |
| |
3833 gdk_pixbuf_render_to_drawable(gtkblist->headline_close, |
| |
3834 GDK_DRAWABLE(widget->window), NULL, |
| |
3835 #endif |
| |
3836 0, 0, |
| |
3837 widget->allocation.width - 2 - HEADLINE_CLOSE_SIZE, 2, |
| |
3838 HEADLINE_CLOSE_SIZE, HEADLINE_CLOSE_SIZE, |
| |
3839 GDK_RGB_DITHER_NONE, 0, 0); |
| |
3840 gtk_paint_focus(widget->style, widget->window, GTK_STATE_PRELIGHT, |
| |
3841 NULL, widget, NULL, |
| |
3842 widget->allocation.width - HEADLINE_CLOSE_SIZE - 3, 1, |
| |
3843 HEADLINE_CLOSE_SIZE + 2, HEADLINE_CLOSE_SIZE + 2); |
| |
3844 } |
| |
3845 |
| |
3846 return FALSE; |
| |
3847 } |
| |
3848 |
| |
3849 #if 0 |
| |
3850 static gboolean |
| |
3851 headline_box_motion_cb(GtkWidget *widget, GdkEventMotion *event, PidginBuddyList *gtkblist) |
| |
3852 { |
| |
3853 purple_debug_fatal("motion", "%d %d\n", (int)event->x, (int)event->y); |
| |
3854 if (headline_hover_close((int)event->x, (int)event->y)) |
| |
3855 gtk_paint_focus(widget->style, widget->window, GTK_STATE_PRELIGHT, |
| |
3856 NULL, widget, NULL, |
| |
3857 widget->allocation.width - HEADLINE_CLOSE_SIZE - 3, 1, |
| |
3858 HEADLINE_CLOSE_SIZE + 2, HEADLINE_CLOSE_SIZE + 2); |
| |
3859 return FALSE; |
| |
3860 } |
| |
3861 #endif |
| |
3862 |
| |
3863 static gboolean |
| |
3864 headline_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, PidginBuddyList *gtkblist) |
| |
3865 { |
| |
3866 gdk_window_set_cursor(widget->window, gtkblist->arrow_cursor); |
| |
3867 if (gtkblist->headline_close) { |
| |
3868 GdkRectangle rect = {widget->allocation.width - 3 - HEADLINE_CLOSE_SIZE, 1, |
| |
3869 HEADLINE_CLOSE_SIZE + 2, HEADLINE_CLOSE_SIZE + 2}; |
| |
3870 gdk_window_invalidate_rect(widget->window, &rect, TRUE); |
| |
3871 } |
| |
3872 return FALSE; |
| |
3873 } |
| |
3874 |
| |
3875 static void |
| |
3876 reset_headline(PidginBuddyList *gtkblist) |
| |
3877 { |
| |
3878 gtkblist->headline_callback = NULL; |
| |
3879 gtkblist->headline_data = NULL; |
| |
3880 gtkblist->headline_destroy = NULL; |
| |
3881 pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); |
| |
3882 } |
| |
3883 |
| |
3884 static gboolean |
| |
3885 headline_click_callback(gpointer data) |
| |
3886 { |
| |
3887 ((GSourceFunc)gtkblist->headline_callback)(gtkblist->headline_data); |
| |
3888 reset_headline(gtkblist); |
| |
3889 return FALSE; |
| |
3890 } |
| |
3891 |
| |
3892 static gboolean |
| |
3893 headline_box_press_cb(GtkWidget *widget, GdkEventButton *event, PidginBuddyList *gtkblist) |
| |
3894 { |
| |
3895 gtk_widget_hide(gtkblist->headline_hbox); |
| |
3896 if (gtkblist->headline_callback && !headline_hover_close((int)event->x, (int)event->y)) |
| |
3897 g_idle_add((GSourceFunc)headline_click_callback, gtkblist->headline_data); |
| |
3898 else { |
| |
3899 if (gtkblist->headline_destroy) |
| |
3900 gtkblist->headline_destroy(gtkblist->headline_data); |
| |
3901 reset_headline(gtkblist); |
| |
3902 } |
| |
3903 return TRUE; |
| |
3904 } |
| |
3905 |
| |
3906 /***********************************/ |
| |
3907 /* Connection error handling stuff */ |
| |
3908 /***********************************/ |
| |
3909 |
| |
3910 static void |
| |
3911 ce_modify_account_cb(PurpleAccount *account) |
| |
3912 { |
| |
3913 pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account); |
| |
3914 } |
| |
3915 |
| |
3916 static void |
| |
3917 ce_enable_account_cb(PurpleAccount *account) |
| |
3918 { |
| |
3919 purple_account_set_enabled(account, purple_core_get_ui(), TRUE); |
| |
3920 } |
| |
3921 |
| |
3922 static void |
| |
3923 connection_error_button_clicked_cb(GtkButton *widget, gpointer user_data) |
| |
3924 { |
| |
3925 PurpleAccount *account; |
| |
3926 char *primary; |
| |
3927 const char *text; |
| |
3928 gboolean enabled; |
| |
3929 |
| |
3930 account = user_data; |
| |
3931 primary = g_strdup_printf(_("%s disconnected"), |
| |
3932 purple_account_get_username(account)); |
| |
3933 text = g_hash_table_lookup(gtkblist->connection_errors, account); |
| |
3934 |
| |
3935 enabled = purple_account_get_enabled(account, purple_core_get_ui()); |
| |
3936 purple_request_action(account, _("Connection Error"), primary, text, 2, |
| |
3937 account, 3, |
| |
3938 _("OK"), NULL, |
| |
3939 _("Modify Account"), PURPLE_CALLBACK(ce_modify_account_cb), |
| |
3940 enabled ? _("Connect") : _("Re-enable Account"), |
| |
3941 enabled ? PURPLE_CALLBACK(purple_account_connect) : |
| |
3942 PURPLE_CALLBACK(ce_enable_account_cb)); |
| |
3943 g_free(primary); |
| |
3944 gtk_widget_destroy(GTK_WIDGET(widget)); |
| |
3945 g_hash_table_remove(gtkblist->connection_errors, account); |
| |
3946 } |
| |
3947 |
| |
3948 /* Add some buttons that show connection errors */ |
| |
3949 static void |
| |
3950 create_connection_error_buttons(gpointer key, gpointer value, |
| |
3951 gpointer user_data) |
| |
3952 { |
| |
3953 PurpleAccount *account; |
| |
3954 PurpleStatusType *status_type; |
| |
3955 gchar *escaped, *text; |
| |
3956 GtkWidget *button, *label, *image, *hbox; |
| |
3957 GdkPixbuf *pixbuf; |
| |
3958 |
| |
3959 account = key; |
| |
3960 escaped = g_markup_escape_text((const gchar *)value, -1); |
| |
3961 text = g_strdup_printf(_("<span color=\"red\">%s disconnected: %s</span>"), |
| |
3962 purple_account_get_username(account), |
| |
3963 escaped); |
| |
3964 g_free(escaped); |
| |
3965 |
| |
3966 hbox = gtk_hbox_new(FALSE, 0); |
| |
3967 |
| |
3968 /* Create the icon */ |
| |
3969 if ((status_type = purple_account_get_status_type_with_primitive(account, |
| |
3970 PURPLE_STATUS_OFFLINE))) { |
| |
3971 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); |
| |
3972 if (pixbuf != NULL) { |
| |
3973 image = gtk_image_new_from_pixbuf(pixbuf); |
| |
3974 g_object_unref(pixbuf); |
| |
3975 |
| |
3976 gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, |
| |
3977 PIDGIN_HIG_BOX_SPACE); |
| |
3978 } |
| |
3979 } |
| |
3980 |
| |
3981 /* Create the text */ |
| |
3982 label = gtk_label_new(""); |
| |
3983 gtk_label_set_markup(GTK_LABEL(label), text); |
| |
3984 g_free(text); |
| |
3985 #if GTK_CHECK_VERSION(2,6,0) |
| |
3986 g_object_set(label, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
| |
3987 #endif |
| |
3988 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, |
| |
3989 PIDGIN_HIG_BOX_SPACE); |
| |
3990 |
| |
3991 /* Create the actual button and put the icon and text on it */ |
| |
3992 button = gtk_button_new(); |
| |
3993 gtk_container_add(GTK_CONTAINER(button), hbox); |
| |
3994 g_signal_connect(G_OBJECT(button), "clicked", |
| |
3995 G_CALLBACK(connection_error_button_clicked_cb), |
| |
3996 account); |
| |
3997 gtk_widget_show_all(button); |
| |
3998 gtk_box_pack_end(GTK_BOX(gtkblist->error_buttons), button, |
| |
3999 FALSE, FALSE, 0); |
| |
4000 } |
| |
4001 |
| |
4002 void |
| |
4003 pidgin_blist_update_account_error_state(PurpleAccount *account, const char *text) |
| |
4004 { |
| |
4005 GList *l; |
| |
4006 |
| |
4007 if (text == NULL) |
| |
4008 g_hash_table_remove(gtkblist->connection_errors, account); |
| |
4009 else |
| |
4010 g_hash_table_insert(gtkblist->connection_errors, account, g_strdup(text)); |
| |
4011 |
| |
4012 /* Remove the old error buttons */ |
| |
4013 for (l = gtk_container_get_children(GTK_CONTAINER(gtkblist->error_buttons)); |
| |
4014 l != NULL; |
| |
4015 l = l->next) |
| |
4016 { |
| |
4017 gtk_widget_destroy(GTK_WIDGET(l->data)); |
| |
4018 } |
| |
4019 |
| |
4020 /* Add new error buttons */ |
| |
4021 g_hash_table_foreach(gtkblist->connection_errors, |
| |
4022 create_connection_error_buttons, NULL); |
| |
4023 } |
| |
4024 |
| |
4025 static gboolean |
| |
4026 paint_headline_hbox (GtkWidget *widget, |
| |
4027 GdkEventExpose *event, |
| |
4028 gpointer user_data) |
| |
4029 { |
| |
4030 gtk_paint_flat_box (widget->style, |
| |
4031 widget->window, |
| |
4032 GTK_STATE_NORMAL, |
| |
4033 GTK_SHADOW_OUT, |
| |
4034 NULL, |
| |
4035 widget, |
| |
4036 "tooltip", |
| |
4037 widget->allocation.x + 1, |
| |
4038 widget->allocation.y + 1, |
| |
4039 widget->allocation.width - 2, |
| |
4040 widget->allocation.height - 2); |
| |
4041 return FALSE; |
| |
4042 } |
| |
4043 |
| |
4044 /* This assumes there are not things like groupless buddies or multi-leveled groups. |
| |
4045 * I'm sure other things in this code assumes that also. |
| |
4046 */ |
| |
4047 static void |
| |
4048 treeview_style_set (GtkWidget *widget, |
| |
4049 GtkStyle *prev_style, |
| |
4050 gpointer data) |
| |
4051 { |
| |
4052 PurpleBuddyList *list = data; |
| |
4053 PurpleBlistNode *node = list->root; |
| |
4054 while (node) { |
| |
4055 pidgin_blist_update_group(list, node); |
| |
4056 node = node->next; |
| |
4057 } |
| |
4058 } |
| |
4059 |
| |
4060 static void |
| |
4061 headline_style_set (GtkWidget *widget, |
| |
4062 GtkStyle *prev_style) |
| |
4063 { |
| |
4064 GtkTooltips *tooltips; |
| |
4065 GtkStyle *style; |
| |
4066 |
| |
4067 if (gtkblist->changing_style) |
| |
4068 return; |
| |
4069 |
| |
4070 tooltips = gtk_tooltips_new (); |
| |
4071 #if GLIB_CHECK_VERSION(2,10,0) |
| |
4072 g_object_ref_sink (tooltips); |
| |
4073 #else |
| |
4074 g_object_ref(tooltips); |
| |
4075 gtk_object_sink(GTK_OBJECT(tooltips)); |
| |
4076 #endif |
| |
4077 |
| |
4078 gtk_tooltips_force_window (tooltips); |
| |
4079 gtk_widget_ensure_style (tooltips->tip_window); |
| |
4080 style = gtk_widget_get_style (tooltips->tip_window); |
| |
4081 |
| |
4082 gtkblist->changing_style = TRUE; |
| |
4083 gtk_widget_set_style (gtkblist->headline_hbox, style); |
| |
4084 gtkblist->changing_style = FALSE; |
| |
4085 |
| |
4086 g_object_unref (tooltips); |
| |
4087 } |
| |
4088 |
| |
4089 /******************************************/ |
| |
4090 /* End of connection error handling stuff */ |
| |
4091 /******************************************/ |
| |
4092 |
| |
4093 static int |
| |
4094 blist_focus_cb(GtkWidget *widget, gpointer data, PidginBuddyList *gtkblist) |
| |
4095 { |
| |
4096 pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); |
| |
4097 return 0; |
| |
4098 } |
| |
4099 |
| |
4100 #if 0 |
| |
4101 static GtkWidget * |
| |
4102 kiosk_page() |
| |
4103 { |
| |
4104 GtkWidget *ret = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
4105 GtkWidget *label; |
| |
4106 GtkWidget *entry; |
| |
4107 GtkWidget *bbox; |
| |
4108 GtkWidget *button; |
| |
4109 |
| |
4110 label = gtk_label_new(NULL); |
| |
4111 gtk_box_pack_start(GTK_BOX(ret), label, TRUE, TRUE, 0); |
| |
4112 |
| |
4113 label = gtk_label_new(NULL); |
| |
4114 gtk_label_set_markup(GTK_LABEL(label), _("<b>Username:</b>")); |
| |
4115 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); |
| |
4116 gtk_box_pack_start(GTK_BOX(ret), label, FALSE, FALSE, 0); |
| |
4117 entry = gtk_entry_new(); |
| |
4118 gtk_box_pack_start(GTK_BOX(ret), entry, FALSE, FALSE, 0); |
| |
4119 |
| |
4120 label = gtk_label_new(NULL); |
| |
4121 gtk_label_set_markup(GTK_LABEL(label), _("<b>Password:</b>")); |
| |
4122 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); |
| |
4123 gtk_box_pack_start(GTK_BOX(ret), label, FALSE, FALSE, 0); |
| |
4124 entry = gtk_entry_new(); |
| |
4125 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); |
| |
4126 gtk_box_pack_start(GTK_BOX(ret), entry, FALSE, FALSE, 0); |
| |
4127 |
| |
4128 label = gtk_label_new(" "); |
| |
4129 gtk_box_pack_start(GTK_BOX(ret), label, FALSE, FALSE, 0); |
| |
4130 |
| |
4131 bbox = gtk_hbutton_box_new(); |
| |
4132 button = gtk_button_new_with_mnemonic(_("_Login")); |
| |
4133 gtk_box_pack_start(GTK_BOX(ret), bbox, FALSE, FALSE, 0); |
| |
4134 gtk_container_add(GTK_CONTAINER(bbox), button); |
| |
4135 |
| |
4136 |
| |
4137 label = gtk_label_new(NULL); |
| |
4138 gtk_box_pack_start(GTK_BOX(ret), label, TRUE, TRUE, 0); |
| |
4139 |
| |
4140 gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER); |
| |
4141 |
| |
4142 gtk_widget_show_all(ret); |
| |
4143 return ret; |
| |
4144 } |
| |
4145 #endif |
| |
4146 |
| |
4147 static void pidgin_blist_show(PurpleBuddyList *list) |
| |
4148 { |
| |
4149 void *handle; |
| |
4150 GtkCellRenderer *rend; |
| |
4151 GtkTreeViewColumn *column; |
| |
4152 GtkWidget *menu; |
| |
4153 GtkWidget *ebox; |
| |
4154 GtkWidget *sw; |
| |
4155 GtkWidget *sep; |
| |
4156 GtkWidget *label; |
| |
4157 GList *accounts; |
| |
4158 char *pretty, *tmp; |
| |
4159 GtkAccelGroup *accel_group; |
| |
4160 GtkTreeSelection *selection; |
| |
4161 GtkTargetEntry dte[] = {{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, |
| |
4162 {"application/x-im-contact", 0, DRAG_BUDDY}, |
| |
4163 {"text/x-vcard", 0, DRAG_VCARD }, |
| |
4164 {"text/uri-list", 0, DRAG_URI}, |
| |
4165 {"text/plain", 0, DRAG_TEXT}}; |
| |
4166 GtkTargetEntry ste[] = {{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, |
| |
4167 {"application/x-im-contact", 0, DRAG_BUDDY}, |
| |
4168 {"text/x-vcard", 0, DRAG_VCARD }}; |
| |
4169 if (gtkblist && gtkblist->window) { |
| |
4170 purple_blist_set_visible(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_visible")); |
| |
4171 return; |
| |
4172 } |
| |
4173 |
| |
4174 gtkblist = PIDGIN_BLIST(list); |
| |
4175 |
| |
4176 gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); |
| |
4177 gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000); |
| |
4178 |
| |
4179 gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| |
4180 gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); |
| |
4181 gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); |
| |
4182 gdk_window_set_decorations(gtkblist->window->window, |
| |
4183 GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE); |
| |
4184 g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event", |
| |
4185 G_CALLBACK(blist_focus_cb), gtkblist); |
| |
4186 GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; |
| |
4187 |
| |
4188 gtkblist->main_vbox = gtk_vbox_new(FALSE, 0); |
| |
4189 gtk_widget_show(gtkblist->main_vbox); |
| |
4190 gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->main_vbox); |
| |
4191 |
| |
4192 g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); |
| |
4193 g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); |
| |
4194 g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); |
| |
4195 g_signal_connect(G_OBJECT(gtkblist->window), "window_state_event", G_CALLBACK(gtk_blist_window_state_cb), NULL); |
| |
4196 g_signal_connect(G_OBJECT(gtkblist->window), "key_press_event", G_CALLBACK(gtk_blist_window_key_press_cb), gtkblist); |
| |
4197 gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); |
| |
4198 |
| |
4199 /******************************* Menu bar *************************************/ |
| |
4200 accel_group = gtk_accel_group_new(); |
| |
4201 gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); |
| |
4202 g_object_unref(accel_group); |
| |
4203 gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<PurpleMain>", accel_group); |
| |
4204 gtk_item_factory_set_translate_func(gtkblist->ift, |
| |
4205 (GtkTranslateFunc)item_factory_translate_func, |
| |
4206 NULL, NULL); |
| |
4207 gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), |
| |
4208 blist_menu, NULL); |
| |
4209 pidgin_load_accels(); |
| |
4210 g_signal_connect(G_OBJECT(accel_group), "accel-changed", |
| |
4211 G_CALLBACK(pidgin_save_accels_cb), NULL); |
| |
4212 menu = gtk_item_factory_get_widget(gtkblist->ift, "<PurpleMain>"); |
| |
4213 gtkblist->menutray = pidgin_menu_tray_new(); |
| |
4214 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray); |
| |
4215 gtk_widget_show(gtkblist->menutray); |
| |
4216 gtk_widget_show(menu); |
| |
4217 gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), menu, FALSE, FALSE, 0); |
| |
4218 |
| |
4219 accountmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts")); |
| |
4220 |
| |
4221 |
| |
4222 /****************************** Notebook *************************************/ |
| |
4223 gtkblist->notebook = gtk_notebook_new(); |
| |
4224 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gtkblist->notebook), FALSE); |
| |
4225 gtk_notebook_set_show_border(GTK_NOTEBOOK(gtkblist->notebook), FALSE); |
| |
4226 gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), gtkblist->notebook, TRUE, TRUE, 0); |
| |
4227 |
| |
4228 #if 0 |
| |
4229 gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook), kiosk_page(), NULL); |
| |
4230 #endif |
| |
4231 |
| |
4232 /* Translators: Please maintain the use of -> and <- to refer to menu heirarchy */ |
| |
4233 tmp = g_strdup_printf(_("<span weight='bold' size='larger'>Welcome to %s!</span>\n\n" |
| |
4234 |
| |
4235 "You have no accounts enabled. Enable your IM accounts from the " |
| |
4236 "<b>Accounts</b> window at <b>Accounts->Add/Edit</b>. Once you " |
| |
4237 "enable accounts, you'll be able to sign on, set your status, " |
| |
4238 "and talk to your friends."), PIDGIN_NAME); |
| |
4239 pretty = pidgin_make_pretty_arrows(tmp); |
| |
4240 g_free(tmp); |
| |
4241 label = gtk_label_new(NULL); |
| |
4242 gtk_widget_set_size_request(label, purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width") - 12, -1); |
| |
4243 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
4244 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.2); |
| |
4245 gtk_label_set_markup(GTK_LABEL(label), pretty); |
| |
4246 g_free(pretty); |
| |
4247 gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook),label, NULL); |
| |
4248 gtkblist->vbox = gtk_vbox_new(FALSE, 0); |
| |
4249 gtk_notebook_append_page(GTK_NOTEBOOK(gtkblist->notebook), gtkblist->vbox, NULL); |
| |
4250 gtk_widget_show_all(gtkblist->notebook); |
| |
4251 if ((accounts = purple_accounts_get_all_active())) { |
| |
4252 g_list_free(accounts); |
| |
4253 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1); |
| |
4254 } |
| |
4255 |
| |
4256 ebox = gtk_event_box_new(); |
| |
4257 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), ebox, FALSE, FALSE, 0); |
| |
4258 gtkblist->headline_hbox = gtk_hbox_new(FALSE, 3); |
| |
4259 gtk_container_set_border_width(GTK_CONTAINER(gtkblist->headline_hbox), 6); |
| |
4260 gtk_container_add(GTK_CONTAINER(ebox), gtkblist->headline_hbox); |
| |
4261 gtkblist->headline_image = gtk_image_new_from_pixbuf(NULL); |
| |
4262 gtk_misc_set_alignment(GTK_MISC(gtkblist->headline_image), 0.0, 0); |
| |
4263 gtkblist->headline_label = gtk_label_new(NULL); |
| |
4264 gtk_widget_set_size_request(gtkblist->headline_label, |
| |
4265 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25,-1); |
| |
4266 gtk_label_set_line_wrap(GTK_LABEL(gtkblist->headline_label), TRUE); |
| |
4267 gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_image, FALSE, FALSE, 0); |
| |
4268 gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_label, TRUE, TRUE, 0); |
| |
4269 g_signal_connect(gtkblist->headline_hbox, |
| |
4270 "style-set", |
| |
4271 G_CALLBACK(headline_style_set), |
| |
4272 NULL); |
| |
4273 g_signal_connect (gtkblist->headline_hbox, |
| |
4274 "expose_event", |
| |
4275 G_CALLBACK (paint_headline_hbox), |
| |
4276 NULL); |
| |
4277 gtk_widget_set_name(gtkblist->headline_hbox, "gtk-tooltips"); |
| |
4278 |
| |
4279 gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE, -1, NULL); |
| |
4280 if (gtkblist->headline_close) { |
| |
4281 GdkPixbuf *scale = gdk_pixbuf_scale_simple(gtkblist->headline_close, |
| |
4282 HEADLINE_CLOSE_SIZE, HEADLINE_CLOSE_SIZE, GDK_INTERP_BILINEAR); |
| |
4283 gdk_pixbuf_unref(gtkblist->headline_close); |
| |
4284 gtkblist->headline_close = scale; |
| |
4285 } |
| |
4286 |
| |
4287 gtkblist->hand_cursor = gdk_cursor_new (GDK_HAND2); |
| |
4288 gtkblist->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); |
| |
4289 |
| |
4290 g_signal_connect(G_OBJECT(ebox), "enter-notify-event", G_CALLBACK(headline_box_enter_cb), gtkblist); |
| |
4291 g_signal_connect(G_OBJECT(ebox), "leave-notify-event", G_CALLBACK(headline_box_leave_cb), gtkblist); |
| |
4292 g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(headline_box_press_cb), gtkblist); |
| |
4293 #if 0 |
| |
4294 /* I couldn't get this to work. The idea was to draw the focus-border only |
| |
4295 * when hovering over the close image. So for now, the focus-border is |
| |
4296 * always there. -- sad */ |
| |
4297 gtk_widget_set_events(ebox, gtk_widget_get_events(ebox) | GDK_POINTER_MOTION_HINT_MASK); |
| |
4298 g_signal_connect(G_OBJECT(ebox), "motion-notify-event", G_CALLBACK(headline_box_motion_cb), gtkblist); |
| |
4299 #endif |
| |
4300 |
| |
4301 /****************************** GtkTreeView **********************************/ |
| |
4302 sw = gtk_scrolled_window_new(NULL,NULL); |
| |
4303 gtk_widget_show(sw); |
| |
4304 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_NONE); |
| |
4305 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
| |
4306 |
| |
4307 gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, |
| |
4308 GDK_TYPE_PIXBUF, /* Status icon */ |
| |
4309 G_TYPE_BOOLEAN, /* Status icon visible */ |
| |
4310 G_TYPE_STRING, /* Name */ |
| |
4311 G_TYPE_STRING, /* Idle */ |
| |
4312 G_TYPE_BOOLEAN, /* Idle visible */ |
| |
4313 GDK_TYPE_PIXBUF, /* Buddy icon */ |
| |
4314 G_TYPE_BOOLEAN, /* Buddy icon visible */ |
| |
4315 G_TYPE_POINTER, /* Node */ |
| |
4316 GDK_TYPE_COLOR, /* bgcolor */ |
| |
4317 G_TYPE_BOOLEAN, /* Group expander */ |
| |
4318 G_TYPE_BOOLEAN, /* Group expander visible */ |
| |
4319 G_TYPE_BOOLEAN, /* Contact expander */ |
| |
4320 G_TYPE_BOOLEAN, /* Contact expander visible */ |
| |
4321 GDK_TYPE_PIXBUF, /* Emblem */ |
| |
4322 G_TYPE_BOOLEAN); /* Emblem visible */ |
| |
4323 |
| |
4324 gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); |
| |
4325 |
| |
4326 gtk_widget_show(gtkblist->treeview); |
| |
4327 gtk_widget_set_name(gtkblist->treeview, "pidginblist_treeview"); |
| |
4328 |
| |
4329 g_signal_connect(gtkblist->treeview, |
| |
4330 "style-set", |
| |
4331 G_CALLBACK(treeview_style_set), list); |
| |
4332 /* Set up selection stuff */ |
| |
4333 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
4334 g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(pidgin_blist_selection_changed), NULL); |
| |
4335 |
| |
4336 /* Set up dnd */ |
| |
4337 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), |
| |
4338 GDK_BUTTON1_MASK, ste, 3, |
| |
4339 GDK_ACTION_COPY); |
| |
4340 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), |
| |
4341 dte, 5, |
| |
4342 GDK_ACTION_COPY | GDK_ACTION_MOVE); |
| |
4343 |
| |
4344 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(pidgin_blist_drag_data_rcv_cb), NULL); |
| |
4345 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(pidgin_blist_drag_data_get_cb), NULL); |
| |
4346 #ifdef _WIN32 |
| |
4347 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL); |
| |
4348 #endif |
| |
4349 |
| |
4350 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL); |
| |
4351 |
| |
4352 /* Tooltips */ |
| |
4353 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL); |
| |
4354 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL); |
| |
4355 |
| |
4356 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); |
| |
4357 |
| |
4358 column = gtk_tree_view_column_new(); |
| |
4359 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); |
| |
4360 gtk_tree_view_column_set_visible(column, FALSE); |
| |
4361 gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gtkblist->treeview), column); |
| |
4362 |
| |
4363 gtkblist->text_column = column = gtk_tree_view_column_new (); |
| |
4364 rend = pidgin_cell_renderer_expander_new(); |
| |
4365 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4366 gtk_tree_view_column_set_attributes(column, rend, |
| |
4367 "visible", GROUP_EXPANDER_VISIBLE_COLUMN, |
| |
4368 "expander-visible", GROUP_EXPANDER_COLUMN, |
| |
4369 #if GTK_CHECK_VERSION(2,6,0) |
| |
4370 "sensitive", GROUP_EXPANDER_COLUMN, |
| |
4371 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4372 #endif |
| |
4373 NULL); |
| |
4374 |
| |
4375 rend = pidgin_cell_renderer_expander_new(); |
| |
4376 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4377 gtk_tree_view_column_set_attributes(column, rend, |
| |
4378 "expander-visible", CONTACT_EXPANDER_COLUMN, |
| |
4379 #if GTK_CHECK_VERSION(2,6,0) |
| |
4380 "sensitive", CONTACT_EXPANDER_COLUMN, |
| |
4381 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4382 #endif |
| |
4383 "visible", CONTACT_EXPANDER_VISIBLE_COLUMN, |
| |
4384 NULL); |
| |
4385 |
| |
4386 rend = gtk_cell_renderer_pixbuf_new(); |
| |
4387 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4388 gtk_tree_view_column_set_attributes(column, rend, |
| |
4389 "pixbuf", STATUS_ICON_COLUMN, |
| |
4390 "visible", STATUS_ICON_VISIBLE_COLUMN, |
| |
4391 #if GTK_CHECK_VERSION(2,6,0) |
| |
4392 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4393 #endif |
| |
4394 NULL); |
| |
4395 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); |
| |
4396 |
| |
4397 gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); |
| |
4398 gtk_tree_view_column_pack_start (column, rend, TRUE); |
| |
4399 gtk_tree_view_column_set_attributes(column, rend, |
| |
4400 #if GTK_CHECK_VERSION(2,6,0) |
| |
4401 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4402 #endif |
| |
4403 "markup", NAME_COLUMN, |
| |
4404 NULL); |
| |
4405 g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), NULL); |
| |
4406 g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); |
| |
4407 #if GTK_CHECK_VERSION(2,6,0) |
| |
4408 g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
| |
4409 #endif |
| |
4410 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); |
| |
4411 |
| |
4412 rend = gtk_cell_renderer_text_new(); |
| |
4413 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); |
| |
4414 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4415 gtk_tree_view_column_set_attributes(column, rend, |
| |
4416 "markup", IDLE_COLUMN, |
| |
4417 "visible", IDLE_VISIBLE_COLUMN, |
| |
4418 #if GTK_CHECK_VERSION(2,6,0) |
| |
4419 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4420 #endif |
| |
4421 NULL); |
| |
4422 |
| |
4423 rend = gtk_cell_renderer_pixbuf_new(); |
| |
4424 g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL); |
| |
4425 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4426 gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN, |
| |
4427 #if GTK_CHECK_VERSION(2,6,0) |
| |
4428 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4429 #endif |
| |
4430 "visible", EMBLEM_VISIBLE_COLUMN, NULL); |
| |
4431 |
| |
4432 rend = gtk_cell_renderer_pixbuf_new(); |
| |
4433 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); |
| |
4434 gtk_tree_view_column_pack_start(column, rend, FALSE); |
| |
4435 gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN, |
| |
4436 #if GTK_CHECK_VERSION(2,6,0) |
| |
4437 "cell-background-gdk", BGCOLOR_COLUMN, |
| |
4438 #endif |
| |
4439 "visible", BUDDY_ICON_VISIBLE_COLUMN, |
| |
4440 NULL); |
| |
4441 |
| |
4442 |
| |
4443 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); |
| |
4444 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); |
| |
4445 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); |
| |
4446 g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); |
| |
4447 g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL); |
| |
4448 g_signal_connect(G_OBJECT(gtkblist->treeview), "popup-menu", G_CALLBACK(pidgin_blist_popup_menu_cb), NULL); |
| |
4449 |
| |
4450 /* Enable CTRL+F searching */ |
| |
4451 gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); |
| |
4452 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(gtkblist->treeview), pidgin_tree_view_search_equal_func, NULL, NULL); |
| |
4453 |
| |
4454 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); |
| |
4455 gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); |
| |
4456 |
| |
4457 sep = gtk_hseparator_new(); |
| |
4458 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sep, FALSE, FALSE, 0); |
| |
4459 |
| |
4460 gtkblist->scrollbook = pidgin_scroll_book_new(); |
| |
4461 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->scrollbook, FALSE, FALSE, 0); |
| |
4462 |
| |
4463 /* Create an empty vbox used for showing connection errors */ |
| |
4464 gtkblist->error_buttons = gtk_vbox_new(FALSE, 0); |
| |
4465 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->error_buttons, FALSE, FALSE, 0); |
| |
4466 |
| |
4467 /* Add the statusbox */ |
| |
4468 gtkblist->statusbox = pidgin_status_box_new(); |
| |
4469 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0); |
| |
4470 gtk_widget_set_name(gtkblist->statusbox, "pidginblist_statusbox"); |
| |
4471 gtk_container_set_border_width(GTK_CONTAINER(gtkblist->statusbox), 3); |
| |
4472 gtk_widget_show(gtkblist->statusbox); |
| |
4473 |
| |
4474 /* set the Show Offline Buddies option. must be done |
| |
4475 * after the treeview or faceprint gets mad. -Robot101 |
| |
4476 */ |
| |
4477 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))), |
| |
4478 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies")); |
| |
4479 |
| |
4480 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))), |
| |
4481 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups")); |
| |
4482 |
| |
4483 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Tools/Mute Sounds"))), |
| |
4484 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute")); |
| |
4485 |
| |
4486 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Buddy Details"))), |
| |
4487 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")); |
| |
4488 |
| |
4489 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Idle Times"))), |
| |
4490 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")); |
| |
4491 |
| |
4492 if(!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none")) |
| |
4493 gtk_widget_set_sensitive(gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Mute Sounds")), FALSE); |
| |
4494 |
| |
4495 /* Update some dynamic things */ |
| |
4496 update_menu_bar(gtkblist); |
| |
4497 pidgin_blist_update_plugin_actions(); |
| |
4498 pidgin_blist_update_sort_methods(); |
| |
4499 |
| |
4500 /* OK... let's show this bad boy. */ |
| |
4501 pidgin_blist_refresh(list); |
| |
4502 pidgin_blist_restore_position(); |
| |
4503 gtk_widget_show_all(GTK_WIDGET(gtkblist->vbox)); |
| |
4504 gtk_widget_realize(GTK_WIDGET(gtkblist->window)); |
| |
4505 purple_blist_set_visible(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_visible")); |
| |
4506 |
| |
4507 /* start the refresh timer */ |
| |
4508 gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)pidgin_blist_refresh_timer, list); |
| |
4509 |
| |
4510 handle = pidgin_blist_get_handle(); |
| |
4511 |
| |
4512 /* things that affect how buddies are displayed */ |
| |
4513 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_buddy_icons", |
| |
4514 _prefs_change_redo_list, NULL); |
| |
4515 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_idle_time", |
| |
4516 _prefs_change_redo_list, NULL); |
| |
4517 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_empty_groups", |
| |
4518 _prefs_change_redo_list, NULL); |
| |
4519 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/show_offline_buddies", |
| |
4520 _prefs_change_redo_list, NULL); |
| |
4521 |
| |
4522 /* sorting */ |
| |
4523 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/sort_type", |
| |
4524 _prefs_change_sort_method, NULL); |
| |
4525 |
| |
4526 /* menus */ |
| |
4527 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/sound/mute", |
| |
4528 pidgin_blist_mute_pref_cb, NULL); |
| |
4529 purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/sound/method", |
| |
4530 pidgin_blist_sound_method_pref_cb, NULL); |
| |
4531 |
| |
4532 /* Setup some purple signal handlers. */ |
| |
4533 purple_signal_connect(purple_accounts_get_handle(), "account-enabled", |
| |
4534 gtkblist, PURPLE_CALLBACK(account_modified), gtkblist); |
| |
4535 purple_signal_connect(purple_accounts_get_handle(), "account-disabled", |
| |
4536 gtkblist, PURPLE_CALLBACK(account_modified), gtkblist); |
| |
4537 purple_signal_connect(purple_accounts_get_handle(), "account-removed", |
| |
4538 gtkblist, PURPLE_CALLBACK(account_modified), gtkblist); |
| |
4539 purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", |
| |
4540 gtkblist, PURPLE_CALLBACK(account_status_changed), gtkblist); |
| |
4541 |
| |
4542 purple_signal_connect(pidgin_account_get_handle(), "account-modified", |
| |
4543 gtkblist, PURPLE_CALLBACK(account_modified), gtkblist); |
| |
4544 |
| |
4545 purple_signal_connect(purple_connections_get_handle(), "signed-on", |
| |
4546 gtkblist, PURPLE_CALLBACK(sign_on_off_cb), list); |
| |
4547 purple_signal_connect(purple_connections_get_handle(), "signed-off", |
| |
4548 gtkblist, PURPLE_CALLBACK(sign_on_off_cb), list); |
| |
4549 |
| |
4550 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", |
| |
4551 gtkblist, PURPLE_CALLBACK(plugin_changed_cb), NULL); |
| |
4552 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", |
| |
4553 gtkblist, PURPLE_CALLBACK(plugin_changed_cb), NULL); |
| |
4554 |
| |
4555 purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", |
| |
4556 gtkblist, PURPLE_CALLBACK(conversation_updated_cb), |
| |
4557 gtkblist); |
| |
4558 purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", |
| |
4559 gtkblist, PURPLE_CALLBACK(conversation_deleting_cb), |
| |
4560 gtkblist); |
| |
4561 |
| |
4562 gtk_widget_hide(gtkblist->headline_hbox); |
| |
4563 |
| |
4564 /* emit our created signal */ |
| |
4565 purple_signal_emit(handle, "gtkblist-created", list); |
| |
4566 } |
| |
4567 |
| |
4568 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender) |
| |
4569 { |
| |
4570 PurpleBlistNode *node; |
| |
4571 |
| |
4572 gtkblist = PIDGIN_BLIST(list); |
| |
4573 if(!gtkblist || !gtkblist->treeview) |
| |
4574 return; |
| |
4575 |
| |
4576 node = list->root; |
| |
4577 |
| |
4578 while (node) |
| |
4579 { |
| |
4580 /* This is only needed when we're reverting to a non-GTK+ sorted |
| |
4581 * status. We shouldn't need to remove otherwise. |
| |
4582 */ |
| |
4583 if (remove && !PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
4584 pidgin_blist_hide_node(list, node, FALSE); |
| |
4585 |
| |
4586 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
4587 pidgin_blist_update_buddy(list, node, rerender); |
| |
4588 else if (PURPLE_BLIST_NODE_IS_CHAT(node)) |
| |
4589 pidgin_blist_update(list, node); |
| |
4590 else if (PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
4591 pidgin_blist_update(list, node); |
| |
4592 node = purple_blist_node_next(node, FALSE); |
| |
4593 } |
| |
4594 |
| |
4595 } |
| |
4596 |
| |
4597 void pidgin_blist_refresh(PurpleBuddyList *list) |
| |
4598 { |
| |
4599 redo_buddy_list(list, FALSE, TRUE); |
| |
4600 } |
| |
4601 |
| |
4602 void |
| |
4603 pidgin_blist_update_refresh_timeout() |
| |
4604 { |
| |
4605 PurpleBuddyList *blist; |
| |
4606 PidginBuddyList *gtkblist; |
| |
4607 |
| |
4608 blist = purple_get_blist(); |
| |
4609 gtkblist = PIDGIN_BLIST(purple_get_blist()); |
| |
4610 |
| |
4611 gtkblist->refresh_timer = g_timeout_add(30000,(GSourceFunc)pidgin_blist_refresh_timer, blist); |
| |
4612 } |
| |
4613 |
| |
4614 static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) { |
| |
4615 struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
4616 GtkTreePath *path; |
| |
4617 |
| |
4618 if (!gtknode) { |
| |
4619 return FALSE; |
| |
4620 } |
| |
4621 |
| |
4622 if (!gtkblist) { |
| |
4623 purple_debug_error("gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n"); |
| |
4624 return FALSE; |
| |
4625 } |
| |
4626 |
| |
4627 if (!gtknode->row) |
| |
4628 return FALSE; |
| |
4629 |
| |
4630 |
| |
4631 if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL) |
| |
4632 return FALSE; |
| |
4633 |
| |
4634 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) { |
| |
4635 gtk_tree_path_free(path); |
| |
4636 return FALSE; |
| |
4637 } |
| |
4638 gtk_tree_path_free(path); |
| |
4639 return TRUE; |
| |
4640 } |
| |
4641 |
| |
4642 static void pidgin_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node) |
| |
4643 { |
| |
4644 struct _pidgin_blist_node *gtknode = node->ui_data; |
| |
4645 |
| |
4646 purple_request_close_with_handle(node); |
| |
4647 |
| |
4648 pidgin_blist_hide_node(list, node, TRUE); |
| |
4649 |
| |
4650 if(node->parent) |
| |
4651 pidgin_blist_update(list, node->parent); |
| |
4652 |
| |
4653 /* There's something I don't understand here - Ethan */ |
| |
4654 /* Ethan said that back in 2003, but this g_free has been left commented |
| |
4655 * out ever since. I can't find any reason at all why this is bad and |
| |
4656 * valgrind found several reasons why it's good. If this causes problems |
| |
4657 * comment it out again. Stu */ |
| |
4658 /* Of course it still causes problems - this breaks dragging buddies into |
| |
4659 * contacts, the dragged buddy mysteriously 'disappears'. Stu. */ |
| |
4660 /* I think it's fixed now. Stu. */ |
| |
4661 |
| |
4662 if(gtknode) { |
| |
4663 if(gtknode->recent_signonoff_timer > 0) |
| |
4664 purple_timeout_remove(gtknode->recent_signonoff_timer); |
| |
4665 |
| |
4666 g_free(node->ui_data); |
| |
4667 node->ui_data = NULL; |
| |
4668 } |
| |
4669 } |
| |
4670 |
| |
4671 static gboolean do_selection_changed(PurpleBlistNode *new_selection) |
| |
4672 { |
| |
4673 PurpleBlistNode *old_selection = NULL; |
| |
4674 |
| |
4675 /* test for gtkblist because crazy timeout means we can be called after the blist is gone */ |
| |
4676 if (gtkblist && new_selection != gtkblist->selected_node) { |
| |
4677 old_selection = gtkblist->selected_node; |
| |
4678 gtkblist->selected_node = new_selection; |
| |
4679 if(new_selection) |
| |
4680 pidgin_blist_update(NULL, new_selection); |
| |
4681 if(old_selection) |
| |
4682 pidgin_blist_update(NULL, old_selection); |
| |
4683 } |
| |
4684 |
| |
4685 return FALSE; |
| |
4686 } |
| |
4687 |
| |
4688 static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data) |
| |
4689 { |
| |
4690 PurpleBlistNode *new_selection = NULL; |
| |
4691 GtkTreeIter iter; |
| |
4692 |
| |
4693 if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ |
| |
4694 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, |
| |
4695 NODE_COLUMN, &new_selection, -1); |
| |
4696 } |
| |
4697 |
| |
4698 /* we set this up as a timeout, otherwise the blist flickers */ |
| |
4699 g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); |
| |
4700 } |
| |
4701 |
| |
4702 static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter) |
| |
4703 { |
| |
4704 GtkTreeIter parent_iter, cur, *curptr = NULL; |
| |
4705 struct _pidgin_blist_node *gtknode = node->ui_data; |
| |
4706 GtkTreePath *newpath; |
| |
4707 |
| |
4708 if(!iter) |
| |
4709 return FALSE; |
| |
4710 |
| |
4711 if(node->parent && !get_iter_from_node(node->parent, &parent_iter)) |
| |
4712 return FALSE; |
| |
4713 |
| |
4714 if(get_iter_from_node(node, &cur)) |
| |
4715 curptr = &cur; |
| |
4716 |
| |
4717 if(PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
4718 current_sort_method->func(node, list, parent_iter, curptr, iter); |
| |
4719 } else { |
| |
4720 sort_method_none(node, list, parent_iter, curptr, iter); |
| |
4721 } |
| |
4722 |
| |
4723 if(gtknode != NULL) { |
| |
4724 gtk_tree_row_reference_free(gtknode->row); |
| |
4725 } else { |
| |
4726 pidgin_blist_new_node(node); |
| |
4727 gtknode = (struct _pidgin_blist_node *)node->ui_data; |
| |
4728 } |
| |
4729 |
| |
4730 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
4731 iter); |
| |
4732 gtknode->row = |
| |
4733 gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
4734 newpath); |
| |
4735 |
| |
4736 gtk_tree_path_free(newpath); |
| |
4737 |
| |
4738 gtk_tree_store_set(gtkblist->treemodel, iter, |
| |
4739 NODE_COLUMN, node, |
| |
4740 -1); |
| |
4741 |
| |
4742 if(node->parent) { |
| |
4743 GtkTreePath *expand = NULL; |
| |
4744 struct _pidgin_blist_node *gtkparentnode = node->parent->ui_data; |
| |
4745 |
| |
4746 if(PURPLE_BLIST_NODE_IS_GROUP(node->parent)) { |
| |
4747 if(!purple_blist_node_get_bool(node->parent, "collapsed")) |
| |
4748 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); |
| |
4749 } else if(PURPLE_BLIST_NODE_IS_CONTACT(node->parent) && |
| |
4750 gtkparentnode->contact_expanded) { |
| |
4751 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); |
| |
4752 } |
| |
4753 if(expand) { |
| |
4754 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, FALSE); |
| |
4755 gtk_tree_path_free(expand); |
| |
4756 } |
| |
4757 } |
| |
4758 |
| |
4759 return TRUE; |
| |
4760 } |
| |
4761 |
| |
4762 /*This version of pidgin_blist_update_group can take the original buddy |
| |
4763 or a group, but has much better algorithmic performance with a pre-known buddy*/ |
| |
4764 static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node) |
| |
4765 { |
| |
4766 PurpleGroup *group; |
| |
4767 int count; |
| |
4768 gboolean show = FALSE; |
| |
4769 PurpleBlistNode* gnode; |
| |
4770 |
| |
4771 g_return_if_fail(node != NULL); |
| |
4772 |
| |
4773 if (PURPLE_BLIST_NODE_IS_GROUP(node)) |
| |
4774 gnode = node; |
| |
4775 else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
4776 gnode = node->parent->parent; |
| |
4777 else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) |
| |
4778 gnode = node->parent; |
| |
4779 else |
| |
4780 return; |
| |
4781 |
| |
4782 group = (PurpleGroup*)gnode; |
| |
4783 |
| |
4784 if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies")) |
| |
4785 count = purple_blist_get_group_size(group, FALSE); |
| |
4786 else |
| |
4787 count = purple_blist_get_group_online_count(group); |
| |
4788 |
| |
4789 if (count > 0 || purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups")) |
| |
4790 show = TRUE; |
| |
4791 else if (PURPLE_BLIST_NODE_IS_BUDDY(node)){ /* Or chat? */ |
| |
4792 if (buddy_is_displayable((PurpleBuddy*)node)) |
| |
4793 show = TRUE;} |
| |
4794 |
| |
4795 if (show) { |
| |
4796 GtkTreeIter iter; |
| |
4797 GtkTreePath *path; |
| |
4798 gboolean expanded; |
| |
4799 GdkColor bgcolor; |
| |
4800 char *title; |
| |
4801 |
| |
4802 |
| |
4803 if(!insert_node(list, gnode, &iter)) |
| |
4804 return; |
| |
4805 |
| |
4806 bgcolor = gtkblist->treeview->style->bg[GTK_STATE_ACTIVE]; |
| |
4807 |
| |
4808 path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); |
| |
4809 expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path); |
| |
4810 gtk_tree_path_free(path); |
| |
4811 |
| |
4812 title = pidgin_get_group_title(gnode, expanded); |
| |
4813 |
| |
4814 gtk_tree_store_set(gtkblist->treemodel, &iter, |
| |
4815 STATUS_ICON_VISIBLE_COLUMN, FALSE, |
| |
4816 STATUS_ICON_COLUMN, NULL, |
| |
4817 NAME_COLUMN, title, |
| |
4818 NODE_COLUMN, gnode, |
| |
4819 BGCOLOR_COLUMN, &bgcolor, |
| |
4820 GROUP_EXPANDER_COLUMN, TRUE, |
| |
4821 GROUP_EXPANDER_VISIBLE_COLUMN, TRUE, |
| |
4822 CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE, |
| |
4823 BUDDY_ICON_VISIBLE_COLUMN, FALSE, |
| |
4824 IDLE_VISIBLE_COLUMN, FALSE, |
| |
4825 EMBLEM_VISIBLE_COLUMN, FALSE, |
| |
4826 -1); |
| |
4827 g_free(title); |
| |
4828 } else { |
| |
4829 pidgin_blist_hide_node(list, gnode, TRUE); |
| |
4830 } |
| |
4831 } |
| |
4832 |
| |
4833 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded) |
| |
4834 { |
| |
4835 PurpleGroup *group; |
| |
4836 GdkColor textcolor; |
| |
4837 gboolean selected; |
| |
4838 char group_count[12] = ""; |
| |
4839 char *mark, *esc; |
| |
4840 |
| |
4841 group = (PurpleGroup*)gnode; |
| |
4842 textcolor = gtkblist->treeview->style->fg[GTK_STATE_ACTIVE]; |
| |
4843 selected = gtkblist ? (gtkblist->selected_node == gnode) : FALSE; |
| |
4844 |
| |
4845 if (!expanded) { |
| |
4846 g_snprintf(group_count, sizeof(group_count), " (%d/%d)", |
| |
4847 purple_blist_get_group_online_count(group), |
| |
4848 purple_blist_get_group_size(group, FALSE)); |
| |
4849 } |
| |
4850 |
| |
4851 esc = g_markup_escape_text(group->name, -1); |
| |
4852 if (selected) |
| |
4853 mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc, group_count); |
| |
4854 else |
| |
4855 mark = g_strdup_printf("<span color='#%02x%02x%02x' weight='bold'>%s</span>%s", |
| |
4856 textcolor.red>>8, textcolor.green>>8, textcolor.blue>>8, |
| |
4857 esc, group_count); |
| |
4858 |
| |
4859 g_free(esc); |
| |
4860 return mark; |
| |
4861 } |
| |
4862 |
| |
4863 static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) |
| |
4864 { |
| |
4865 PurplePresence *presence; |
| |
4866 GdkPixbuf *status, *avatar, *emblem; |
| |
4867 char *mark; |
| |
4868 char *idle = NULL; |
| |
4869 gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded; |
| |
4870 gboolean selected = (gtkblist->selected_node == node); |
| |
4871 gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); |
| |
4872 presence = purple_buddy_get_presence(buddy); |
| |
4873 |
| |
4874 status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy, |
| |
4875 PIDGIN_STATUS_ICON_SMALL); |
| |
4876 |
| |
4877 avatar = pidgin_blist_get_buddy_icon((PurpleBlistNode *)buddy, TRUE, TRUE, TRUE); |
| |
4878 if (!avatar) { |
| |
4879 g_object_ref(G_OBJECT(gtkblist->empty_avatar)); |
| |
4880 avatar = gtkblist->empty_avatar; |
| |
4881 } else if ((!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence))) { |
| |
4882 do_alphashift(avatar, avatar, 77); |
| |
4883 } |
| |
4884 |
| |
4885 emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy); |
| |
4886 mark = pidgin_blist_get_name_markup(buddy, selected); |
| |
4887 |
| |
4888 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time") && |
| |
4889 purple_presence_is_idle(presence) && |
| |
4890 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) |
| |
4891 { |
| |
4892 time_t idle_secs = purple_presence_get_idle_time(presence); |
| |
4893 |
| |
4894 if (idle_secs > 0) |
| |
4895 { |
| |
4896 time_t t; |
| |
4897 int ihrs, imin; |
| |
4898 time(&t); |
| |
4899 ihrs = (t - idle_secs) / 3600; |
| |
4900 imin = ((t - idle_secs) / 60) % 60; |
| |
4901 idle = g_strdup_printf("%d:%02d", ihrs, imin); |
| |
4902 } |
| |
4903 } |
| |
4904 |
| |
4905 if (purple_presence_is_idle(presence)) |
| |
4906 { |
| |
4907 if (idle && !selected) { |
| |
4908 char *i2 = g_strdup_printf("<span color='%s'>%s</span>", |
| |
4909 dim_grey(), idle); |
| |
4910 g_free(idle); |
| |
4911 idle = i2; |
| |
4912 } |
| |
4913 } |
| |
4914 |
| |
4915 gtk_tree_store_set(gtkblist->treemodel, iter, |
| |
4916 STATUS_ICON_COLUMN, status, |
| |
4917 STATUS_ICON_VISIBLE_COLUMN, TRUE, |
| |
4918 NAME_COLUMN, mark, |
| |
4919 IDLE_COLUMN, idle, |
| |
4920 IDLE_VISIBLE_COLUMN, !biglist && idle, |
| |
4921 BUDDY_ICON_COLUMN, avatar, |
| |
4922 BUDDY_ICON_VISIBLE_COLUMN, biglist, |
| |
4923 EMBLEM_COLUMN, emblem, |
| |
4924 EMBLEM_VISIBLE_COLUMN, emblem, |
| |
4925 BGCOLOR_COLUMN, NULL, |
| |
4926 CONTACT_EXPANDER_COLUMN, NULL, |
| |
4927 CONTACT_EXPANDER_VISIBLE_COLUMN, expanded, |
| |
4928 GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, |
| |
4929 -1); |
| |
4930 |
| |
4931 g_free(mark); |
| |
4932 g_free(idle); |
| |
4933 if(status) |
| |
4934 g_object_unref(status); |
| |
4935 if(avatar) |
| |
4936 g_object_unref(avatar); |
| |
4937 } |
| |
4938 |
| |
4939 /* This is a variation on the original gtk_blist_update_contact. Here we |
| |
4940 can know in advance which buddy has changed so we can just update that */ |
| |
4941 static void pidgin_blist_update_contact(PurpleBuddyList *list, PurpleBlistNode *node) |
| |
4942 { |
| |
4943 PurpleBlistNode *cnode; |
| |
4944 PurpleContact *contact; |
| |
4945 PurpleBuddy *buddy; |
| |
4946 struct _pidgin_blist_node *gtknode; |
| |
4947 |
| |
4948 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
4949 cnode = node->parent; |
| |
4950 else |
| |
4951 cnode = node; |
| |
4952 |
| |
4953 g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(cnode)); |
| |
4954 |
| |
4955 /* First things first, update the group */ |
| |
4956 if (PURPLE_BLIST_NODE_IS_BUDDY(node)) |
| |
4957 pidgin_blist_update_group(list, node); |
| |
4958 else |
| |
4959 pidgin_blist_update_group(list, cnode->parent); |
| |
4960 |
| |
4961 contact = (PurpleContact*)cnode; |
| |
4962 buddy = purple_contact_get_priority_buddy(contact); |
| |
4963 |
| |
4964 if (buddy_is_displayable(buddy)) |
| |
4965 { |
| |
4966 GtkTreeIter iter; |
| |
4967 |
| |
4968 if(!insert_node(list, cnode, &iter)) |
| |
4969 return; |
| |
4970 |
| |
4971 gtknode = (struct _pidgin_blist_node *)cnode->ui_data; |
| |
4972 |
| |
4973 if(gtknode->contact_expanded) { |
| |
4974 GdkPixbuf *status; |
| |
4975 char *mark; |
| |
4976 |
| |
4977 status = pidgin_blist_get_status_icon(cnode, |
| |
4978 PIDGIN_STATUS_ICON_SMALL); |
| |
4979 |
| |
4980 mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); |
| |
4981 gtk_tree_store_set(gtkblist->treemodel, &iter, |
| |
4982 STATUS_ICON_COLUMN, status, |
| |
4983 STATUS_ICON_VISIBLE_COLUMN, TRUE, |
| |
4984 NAME_COLUMN, mark, |
| |
4985 IDLE_COLUMN, NULL, |
| |
4986 IDLE_VISIBLE_COLUMN, FALSE, |
| |
4987 BGCOLOR_COLUMN, NULL, |
| |
4988 BUDDY_ICON_COLUMN, NULL, |
| |
4989 CONTACT_EXPANDER_COLUMN, TRUE, |
| |
4990 CONTACT_EXPANDER_VISIBLE_COLUMN, TRUE, |
| |
4991 GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, |
| |
4992 -1); |
| |
4993 g_free(mark); |
| |
4994 if(status) |
| |
4995 g_object_unref(status); |
| |
4996 } else { |
| |
4997 buddy_node(buddy, &iter, cnode); |
| |
4998 } |
| |
4999 } else { |
| |
5000 pidgin_blist_hide_node(list, cnode, TRUE); |
| |
5001 } |
| |
5002 } |
| |
5003 |
| |
5004 |
| |
5005 |
| |
5006 static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange) |
| |
5007 { |
| |
5008 PurpleBuddy *buddy; |
| |
5009 struct _pidgin_blist_node *gtkparentnode; |
| |
5010 |
| |
5011 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
| |
5012 |
| |
5013 if (node->parent == NULL) |
| |
5014 return; |
| |
5015 |
| |
5016 buddy = (PurpleBuddy*)node; |
| |
5017 |
| |
5018 /* First things first, update the contact */ |
| |
5019 pidgin_blist_update_contact(list, node); |
| |
5020 |
| |
5021 gtkparentnode = (struct _pidgin_blist_node *)node->parent->ui_data; |
| |
5022 |
| |
5023 if (gtkparentnode->contact_expanded && buddy_is_displayable(buddy)) |
| |
5024 { |
| |
5025 GtkTreeIter iter; |
| |
5026 |
| |
5027 if (!insert_node(list, node, &iter)) |
| |
5028 return; |
| |
5029 |
| |
5030 buddy_node(buddy, &iter, node); |
| |
5031 |
| |
5032 } else { |
| |
5033 pidgin_blist_hide_node(list, node, TRUE); |
| |
5034 } |
| |
5035 |
| |
5036 } |
| |
5037 |
| |
5038 static void pidgin_blist_update_chat(PurpleBuddyList *list, PurpleBlistNode *node) |
| |
5039 { |
| |
5040 PurpleChat *chat; |
| |
5041 |
| |
5042 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); |
| |
5043 |
| |
5044 /* First things first, update the group */ |
| |
5045 pidgin_blist_update_group(list, node->parent); |
| |
5046 |
| |
5047 chat = (PurpleChat*)node; |
| |
5048 |
| |
5049 if(purple_account_is_connected(chat->account)) { |
| |
5050 GtkTreeIter iter; |
| |
5051 GdkPixbuf *status; |
| |
5052 GdkPixbuf *avatar; |
| |
5053 GdkPixbuf *emblem; |
| |
5054 char *mark; |
| |
5055 |
| |
5056 if(!insert_node(list, node, &iter)) |
| |
5057 return; |
| |
5058 |
| |
5059 status = pidgin_blist_get_status_icon(node, |
| |
5060 PIDGIN_STATUS_ICON_SMALL); |
| |
5061 emblem = pidgin_blist_get_emblem(node); |
| |
5062 avatar = pidgin_blist_get_buddy_icon(node, TRUE, FALSE, TRUE); |
| |
5063 |
| |
5064 mark = g_markup_escape_text(purple_chat_get_name(chat), -1); |
| |
5065 |
| |
5066 gtk_tree_store_set(gtkblist->treemodel, &iter, |
| |
5067 STATUS_ICON_COLUMN, status, |
| |
5068 STATUS_ICON_VISIBLE_COLUMN, TRUE, |
| |
5069 BUDDY_ICON_COLUMN, avatar ? avatar : gtkblist->empty_avatar, |
| |
5070 BUDDY_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"), |
| |
5071 EMBLEM_COLUMN, emblem, |
| |
5072 EMBLEM_VISIBLE_COLUMN, emblem != NULL, |
| |
5073 NAME_COLUMN, mark, |
| |
5074 GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, |
| |
5075 -1); |
| |
5076 |
| |
5077 g_free(mark); |
| |
5078 if(status) |
| |
5079 g_object_unref(status); |
| |
5080 if(avatar) |
| |
5081 g_object_unref(avatar); |
| |
5082 } else { |
| |
5083 pidgin_blist_hide_node(list, node, TRUE); |
| |
5084 } |
| |
5085 } |
| |
5086 |
| |
5087 static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node) |
| |
5088 { |
| |
5089 if (list) |
| |
5090 gtkblist = PIDGIN_BLIST(list); |
| |
5091 if(!gtkblist || !gtkblist->treeview || !node) |
| |
5092 return; |
| |
5093 |
| |
5094 if (node->ui_data == NULL) |
| |
5095 pidgin_blist_new_node(node); |
| |
5096 |
| |
5097 switch(node->type) { |
| |
5098 case PURPLE_BLIST_GROUP_NODE: |
| |
5099 pidgin_blist_update_group(list, node); |
| |
5100 break; |
| |
5101 case PURPLE_BLIST_CONTACT_NODE: |
| |
5102 pidgin_blist_update_contact(list, node); |
| |
5103 break; |
| |
5104 case PURPLE_BLIST_BUDDY_NODE: |
| |
5105 pidgin_blist_update_buddy(list, node, TRUE); |
| |
5106 break; |
| |
5107 case PURPLE_BLIST_CHAT_NODE: |
| |
5108 pidgin_blist_update_chat(list, node); |
| |
5109 break; |
| |
5110 case PURPLE_BLIST_OTHER_NODE: |
| |
5111 return; |
| |
5112 } |
| |
5113 |
| |
5114 #if !GTK_CHECK_VERSION(2,6,0) |
| |
5115 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); |
| |
5116 #endif |
| |
5117 } |
| |
5118 |
| |
5119 |
| |
5120 static void pidgin_blist_destroy(PurpleBuddyList *list) |
| |
5121 { |
| |
5122 if (!gtkblist) |
| |
5123 return; |
| |
5124 |
| |
5125 purple_signals_disconnect_by_handle(gtkblist); |
| |
5126 |
| |
5127 if (gtkblist->headline_close) |
| |
5128 gdk_pixbuf_unref(gtkblist->headline_close); |
| |
5129 |
| |
5130 gtk_widget_destroy(gtkblist->window); |
| |
5131 |
| |
5132 pidgin_blist_tooltip_destroy(); |
| |
5133 |
| |
5134 if (gtkblist->refresh_timer) |
| |
5135 g_source_remove(gtkblist->refresh_timer); |
| |
5136 if (gtkblist->timeout) |
| |
5137 g_source_remove(gtkblist->timeout); |
| |
5138 if (gtkblist->drag_timeout) |
| |
5139 g_source_remove(gtkblist->drag_timeout); |
| |
5140 |
| |
5141 g_hash_table_destroy(gtkblist->connection_errors); |
| |
5142 gtkblist->refresh_timer = 0; |
| |
5143 gtkblist->timeout = 0; |
| |
5144 gtkblist->drag_timeout = 0; |
| |
5145 gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; |
| |
5146 gtkblist->treemodel = NULL; |
| |
5147 g_object_unref(G_OBJECT(gtkblist->ift)); |
| |
5148 g_object_unref(G_OBJECT(gtkblist->empty_avatar)); |
| |
5149 |
| |
5150 gdk_cursor_unref(gtkblist->hand_cursor); |
| |
5151 gdk_cursor_unref(gtkblist->arrow_cursor); |
| |
5152 gtkblist->hand_cursor = NULL; |
| |
5153 gtkblist->arrow_cursor = NULL; |
| |
5154 |
| |
5155 g_free(gtkblist); |
| |
5156 accountmenu = NULL; |
| |
5157 gtkblist = NULL; |
| |
5158 purple_prefs_disconnect_by_handle(pidgin_blist_get_handle()); |
| |
5159 } |
| |
5160 |
| |
5161 static void pidgin_blist_set_visible(PurpleBuddyList *list, gboolean show) |
| |
5162 { |
| |
5163 if (!(gtkblist && gtkblist->window)) |
| |
5164 return; |
| |
5165 |
| |
5166 if (show) { |
| |
5167 if(!PIDGIN_WINDOW_ICONIFIED(gtkblist->window) && !GTK_WIDGET_VISIBLE(gtkblist->window)) |
| |
5168 purple_signal_emit(pidgin_blist_get_handle(), "gtkblist-unhiding", gtkblist); |
| |
5169 pidgin_blist_restore_position(); |
| |
5170 gtk_window_present(GTK_WINDOW(gtkblist->window)); |
| |
5171 } else { |
| |
5172 if(visibility_manager_count) { |
| |
5173 purple_signal_emit(pidgin_blist_get_handle(), "gtkblist-hiding", gtkblist); |
| |
5174 gtk_widget_hide(gtkblist->window); |
| |
5175 } else { |
| |
5176 if (!GTK_WIDGET_VISIBLE(gtkblist->window)) |
| |
5177 gtk_widget_show(gtkblist->window); |
| |
5178 gtk_window_iconify(GTK_WINDOW(gtkblist->window)); |
| |
5179 } |
| |
5180 } |
| |
5181 } |
| |
5182 |
| |
5183 static GList * |
| |
5184 groups_tree(void) |
| |
5185 { |
| |
5186 GList *tmp = NULL; |
| |
5187 char *tmp2; |
| |
5188 PurpleGroup *g; |
| |
5189 PurpleBlistNode *gnode; |
| |
5190 |
| |
5191 if (purple_get_blist()->root == NULL) |
| |
5192 { |
| |
5193 tmp2 = g_strdup(_("Buddies")); |
| |
5194 tmp = g_list_append(tmp, tmp2); |
| |
5195 } |
| |
5196 else |
| |
5197 { |
| |
5198 for (gnode = purple_get_blist()->root; |
| |
5199 gnode != NULL; |
| |
5200 gnode = gnode->next) |
| |
5201 { |
| |
5202 if (PURPLE_BLIST_NODE_IS_GROUP(gnode)) |
| |
5203 { |
| |
5204 g = (PurpleGroup *)gnode; |
| |
5205 tmp2 = g->name; |
| |
5206 tmp = g_list_append(tmp, tmp2); |
| |
5207 } |
| |
5208 } |
| |
5209 } |
| |
5210 |
| |
5211 return tmp; |
| |
5212 } |
| |
5213 |
| |
5214 static void |
| |
5215 add_buddy_select_account_cb(GObject *w, PurpleAccount *account, |
| |
5216 PidginAddBuddyData *data) |
| |
5217 { |
| |
5218 /* Save our account */ |
| |
5219 data->account = account; |
| |
5220 } |
| |
5221 |
| |
5222 static void |
| |
5223 destroy_add_buddy_dialog_cb(GtkWidget *win, PidginAddBuddyData *data) |
| |
5224 { |
| |
5225 g_free(data); |
| |
5226 } |
| |
5227 |
| |
5228 static void |
| |
5229 add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data) |
| |
5230 { |
| |
5231 const char *grp, *who, *whoalias; |
| |
5232 PurpleGroup *g; |
| |
5233 PurpleBuddy *b; |
| |
5234 PurpleConversation *c; |
| |
5235 PurpleBuddyIcon *icon; |
| |
5236 |
| |
5237 if (resp == GTK_RESPONSE_OK) |
| |
5238 { |
| |
5239 who = gtk_entry_get_text(GTK_ENTRY(data->entry)); |
| |
5240 grp = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry)); |
| |
5241 whoalias = gtk_entry_get_text(GTK_ENTRY(data->entry_for_alias)); |
| |
5242 if (*whoalias == '\0') |
| |
5243 whoalias = NULL; |
| |
5244 |
| |
5245 if ((g = purple_find_group(grp)) == NULL) |
| |
5246 { |
| |
5247 g = purple_group_new(grp); |
| |
5248 purple_blist_add_group(g, NULL); |
| |
5249 } |
| |
5250 |
| |
5251 b = purple_buddy_new(data->account, who, whoalias); |
| |
5252 purple_blist_add_buddy(b, NULL, g, NULL); |
| |
5253 purple_account_add_buddy(data->account, b); |
| |
5254 |
| |
5255 /* |
| |
5256 * XXX |
| |
5257 * It really seems like it would be better if the call to |
| |
5258 * purple_account_add_buddy() and purple_conversation_update() were done in |
| |
5259 * blist.c, possibly in the purple_blist_add_buddy() function. Maybe |
| |
5260 * purple_account_add_buddy() should be renamed to |
| |
5261 * purple_blist_add_new_buddy() or something, and have it call |
| |
5262 * purple_blist_add_buddy() after it creates it. --Mark |
| |
5263 * |
| |
5264 * No that's not good. blist.c should only deal with adding nodes to the |
| |
5265 * local list. We need a new, non-gtk file that calls both |
| |
5266 * purple_account_add_buddy and purple_blist_add_buddy(). |
| |
5267 * Or something. --Mark |
| |
5268 */ |
| |
5269 |
| |
5270 c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, data->account); |
| |
5271 if (c != NULL) { |
| |
5272 icon = purple_conv_im_get_icon(PURPLE_CONV_IM(c)); |
| |
5273 if (icon != NULL) |
| |
5274 purple_buddy_icon_update(icon); |
| |
5275 } |
| |
5276 } |
| |
5277 |
| |
5278 gtk_widget_destroy(data->window); |
| |
5279 } |
| |
5280 |
| |
5281 static void |
| |
5282 pidgin_blist_request_add_buddy(PurpleAccount *account, const char *username, |
| |
5283 const char *group, const char *alias) |
| |
5284 { |
| |
5285 GtkWidget *table; |
| |
5286 GtkWidget *label; |
| |
5287 GtkWidget *hbox; |
| |
5288 GtkWidget *vbox; |
| |
5289 GtkWidget *img; |
| |
5290 PidginBuddyList *gtkblist; |
| |
5291 PidginAddBuddyData *data = g_new0(PidginAddBuddyData, 1); |
| |
5292 |
| |
5293 data->account = |
| |
5294 (account != NULL |
| |
5295 ? account |
| |
5296 : purple_connection_get_account(purple_connections_get_all()->data)); |
| |
5297 |
| |
5298 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION, |
| |
5299 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE)); |
| |
5300 |
| |
5301 gtkblist = PIDGIN_BLIST(purple_get_blist()); |
| |
5302 |
| |
5303 data->window = gtk_dialog_new_with_buttons(_("Add Buddy"), |
| |
5304 NULL, GTK_DIALOG_NO_SEPARATOR, |
| |
5305 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
| |
5306 GTK_STOCK_ADD, GTK_RESPONSE_OK, |
| |
5307 NULL); |
| |
5308 |
| |
5309 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); |
| |
5310 gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE); |
| |
5311 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); |
| |
5312 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER); |
| |
5313 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE); |
| |
5314 gtk_window_set_role(GTK_WINDOW(data->window), "add_buddy"); |
| |
5315 gtk_window_set_type_hint(GTK_WINDOW(data->window), |
| |
5316 GDK_WINDOW_TYPE_HINT_DIALOG); |
| |
5317 |
| |
5318 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
5319 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); |
| |
5320 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
| |
5321 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
| |
5322 |
| |
5323 vbox = gtk_vbox_new(FALSE, 0); |
| |
5324 gtk_container_add(GTK_CONTAINER(hbox), vbox); |
| |
5325 |
| |
5326 label = gtk_label_new( |
| |
5327 _("Please enter the screen name of the person you would like " |
| |
5328 "to add to your buddy list. You may optionally enter an alias, " |
| |
5329 "or nickname, for the buddy. The alias will be displayed in " |
| |
5330 "place of the screen name whenever possible.\n")); |
| |
5331 |
| |
5332 gtk_widget_set_size_request(GTK_WIDGET(label), 400, -1); |
| |
5333 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
5334 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
| |
5335 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
| |
5336 |
| |
5337 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); |
| |
5338 gtk_container_add(GTK_CONTAINER(vbox), hbox); |
| |
5339 |
| |
5340 g_signal_connect(G_OBJECT(data->window), "destroy", |
| |
5341 G_CALLBACK(destroy_add_buddy_dialog_cb), data); |
| |
5342 |
| |
5343 table = gtk_table_new(4, 2, FALSE); |
| |
5344 gtk_table_set_row_spacings(GTK_TABLE(table), 5); |
| |
5345 gtk_table_set_col_spacings(GTK_TABLE(table), 5); |
| |
5346 gtk_container_set_border_width(GTK_CONTAINER(table), 0); |
| |
5347 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); |
| |
5348 |
| |
5349 label = gtk_label_new(_("Screen name:")); |
| |
5350 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5351 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); |
| |
5352 |
| |
5353 data->entry = gtk_entry_new(); |
| |
5354 gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1); |
| |
5355 gtk_widget_grab_focus(data->entry); |
| |
5356 |
| |
5357 if (username != NULL) |
| |
5358 gtk_entry_set_text(GTK_ENTRY(data->entry), username); |
| |
5359 else |
| |
5360 gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), |
| |
5361 GTK_RESPONSE_OK, FALSE); |
| |
5362 |
| |
5363 gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE); |
| |
5364 pidgin_set_accessible_label (data->entry, label); |
| |
5365 |
| |
5366 g_signal_connect(G_OBJECT(data->entry), "changed", |
| |
5367 G_CALLBACK(pidgin_set_sensitive_if_input), |
| |
5368 data->window); |
| |
5369 |
| |
5370 label = gtk_label_new(_("Alias:")); |
| |
5371 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5372 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); |
| |
5373 |
| |
5374 data->entry_for_alias = gtk_entry_new(); |
| |
5375 gtk_table_attach_defaults(GTK_TABLE(table), |
| |
5376 data->entry_for_alias, 1, 2, 1, 2); |
| |
5377 |
| |
5378 if (alias != NULL) |
| |
5379 gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias); |
| |
5380 |
| |
5381 if (username != NULL) |
| |
5382 gtk_widget_grab_focus(GTK_WIDGET(data->entry_for_alias)); |
| |
5383 |
| |
5384 gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE); |
| |
5385 pidgin_set_accessible_label (data->entry_for_alias, label); |
| |
5386 |
| |
5387 label = gtk_label_new(_("Group:")); |
| |
5388 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5389 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); |
| |
5390 |
| |
5391 data->combo = gtk_combo_new(); |
| |
5392 gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree()); |
| |
5393 gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3); |
| |
5394 pidgin_set_accessible_label (data->combo, label); |
| |
5395 |
| |
5396 /* Set up stuff for the account box */ |
| |
5397 label = gtk_label_new(_("Account:")); |
| |
5398 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5399 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); |
| |
5400 |
| |
5401 data->account_box = pidgin_account_option_menu_new(account, FALSE, |
| |
5402 G_CALLBACK(add_buddy_select_account_cb), NULL, data); |
| |
5403 |
| |
5404 gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4); |
| |
5405 pidgin_set_accessible_label (data->account_box, label); |
| |
5406 /* End of account box */ |
| |
5407 |
| |
5408 g_signal_connect(G_OBJECT(data->window), "response", |
| |
5409 G_CALLBACK(add_buddy_cb), data); |
| |
5410 |
| |
5411 gtk_widget_show_all(data->window); |
| |
5412 |
| |
5413 if (group != NULL) |
| |
5414 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry), group); |
| |
5415 } |
| |
5416 |
| |
5417 static void |
| |
5418 add_chat_cb(GtkWidget *w, PidginAddChatData *data) |
| |
5419 { |
| |
5420 GHashTable *components; |
| |
5421 GList *tmp; |
| |
5422 PurpleChat *chat; |
| |
5423 PurpleGroup *group; |
| |
5424 const char *group_name; |
| |
5425 const char *value; |
| |
5426 |
| |
5427 components = g_hash_table_new_full(g_str_hash, g_str_equal, |
| |
5428 g_free, g_free); |
| |
5429 |
| |
5430 for (tmp = data->entries; tmp; tmp = tmp->next) |
| |
5431 { |
| |
5432 if (g_object_get_data(tmp->data, "is_spin")) |
| |
5433 { |
| |
5434 g_hash_table_replace(components, |
| |
5435 g_strdup(g_object_get_data(tmp->data, "identifier")), |
| |
5436 g_strdup_printf("%d", |
| |
5437 gtk_spin_button_get_value_as_int(tmp->data))); |
| |
5438 } |
| |
5439 else |
| |
5440 { |
| |
5441 value = gtk_entry_get_text(tmp->data); |
| |
5442 if (*value != '\0') |
| |
5443 g_hash_table_replace(components, |
| |
5444 g_strdup(g_object_get_data(tmp->data, "identifier")), |
| |
5445 g_strdup(value)); |
| |
5446 } |
| |
5447 } |
| |
5448 |
| |
5449 chat = purple_chat_new(data->account, |
| |
5450 gtk_entry_get_text(GTK_ENTRY(data->alias_entry)), |
| |
5451 components); |
| |
5452 |
| |
5453 group_name = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry)); |
| |
5454 |
| |
5455 if ((group = purple_find_group(group_name)) == NULL) |
| |
5456 { |
| |
5457 group = purple_group_new(group_name); |
| |
5458 purple_blist_add_group(group, NULL); |
| |
5459 } |
| |
5460 |
| |
5461 if (chat != NULL) |
| |
5462 { |
| |
5463 purple_blist_add_chat(chat, group, NULL); |
| |
5464 } |
| |
5465 |
| |
5466 gtk_widget_destroy(data->window); |
| |
5467 g_free(data->default_chat_name); |
| |
5468 g_list_free(data->entries); |
| |
5469 g_free(data); |
| |
5470 } |
| |
5471 |
| |
5472 static void |
| |
5473 add_chat_resp_cb(GtkWidget *w, int resp, PidginAddChatData *data) |
| |
5474 { |
| |
5475 if (resp == GTK_RESPONSE_OK) |
| |
5476 { |
| |
5477 add_chat_cb(NULL, data); |
| |
5478 } |
| |
5479 else |
| |
5480 { |
| |
5481 gtk_widget_destroy(data->window); |
| |
5482 g_free(data->default_chat_name); |
| |
5483 g_list_free(data->entries); |
| |
5484 g_free(data); |
| |
5485 } |
| |
5486 } |
| |
5487 |
| |
5488 /* |
| |
5489 * Check the values of all the text entry boxes. If any required input |
| |
5490 * strings are empty then don't allow the user to click on "OK." |
| |
5491 */ |
| |
5492 static void |
| |
5493 addchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data) |
| |
5494 { |
| |
5495 PidginAddChatData *data; |
| |
5496 GList *tmp; |
| |
5497 const char *text; |
| |
5498 gboolean required; |
| |
5499 gboolean sensitive = TRUE; |
| |
5500 |
| |
5501 data = user_data; |
| |
5502 |
| |
5503 for (tmp = data->entries; tmp != NULL; tmp = tmp->next) |
| |
5504 { |
| |
5505 if (!g_object_get_data(tmp->data, "is_spin")) |
| |
5506 { |
| |
5507 required = GPOINTER_TO_INT(g_object_get_data(tmp->data, "required")); |
| |
5508 text = gtk_entry_get_text(tmp->data); |
| |
5509 if (required && (*text == '\0')) |
| |
5510 sensitive = FALSE; |
| |
5511 } |
| |
5512 } |
| |
5513 |
| |
5514 gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive); |
| |
5515 } |
| |
5516 |
| |
5517 static void |
| |
5518 rebuild_addchat_entries(PidginAddChatData *data) |
| |
5519 { |
| |
5520 PurpleConnection *gc; |
| |
5521 GList *list = NULL, *tmp; |
| |
5522 GHashTable *defaults = NULL; |
| |
5523 struct proto_chat_entry *pce; |
| |
5524 gboolean focus = TRUE; |
| |
5525 |
| |
5526 g_return_if_fail(data->account != NULL); |
| |
5527 |
| |
5528 gc = purple_account_get_connection(data->account); |
| |
5529 |
| |
5530 while ((tmp = gtk_container_get_children(GTK_CONTAINER(data->entries_box)))) |
| |
5531 gtk_widget_destroy(tmp->data); |
| |
5532 |
| |
5533 g_list_free(data->entries); |
| |
5534 |
| |
5535 data->entries = NULL; |
| |
5536 |
| |
5537 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) |
| |
5538 list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc); |
| |
5539 |
| |
5540 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) |
| |
5541 defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, data->default_chat_name); |
| |
5542 |
| |
5543 for (tmp = list; tmp; tmp = tmp->next) |
| |
5544 { |
| |
5545 GtkWidget *label; |
| |
5546 GtkWidget *rowbox; |
| |
5547 GtkWidget *input; |
| |
5548 |
| |
5549 pce = tmp->data; |
| |
5550 |
| |
5551 rowbox = gtk_hbox_new(FALSE, 5); |
| |
5552 gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); |
| |
5553 |
| |
5554 label = gtk_label_new_with_mnemonic(pce->label); |
| |
5555 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5556 gtk_size_group_add_widget(data->sg, label); |
| |
5557 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
5558 |
| |
5559 if (pce->is_int) |
| |
5560 { |
| |
5561 GtkObject *adjust; |
| |
5562 adjust = gtk_adjustment_new(pce->min, pce->min, pce->max, |
| |
5563 1, 10, 10); |
| |
5564 input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); |
| |
5565 gtk_widget_set_size_request(input, 50, -1); |
| |
5566 gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0); |
| |
5567 } |
| |
5568 else |
| |
5569 { |
| |
5570 char *value; |
| |
5571 input = gtk_entry_new(); |
| |
5572 gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE); |
| |
5573 value = g_hash_table_lookup(defaults, pce->identifier); |
| |
5574 if (value != NULL) |
| |
5575 gtk_entry_set_text(GTK_ENTRY(input), value); |
| |
5576 if (pce->secret) |
| |
5577 { |
| |
5578 gtk_entry_set_visibility(GTK_ENTRY(input), FALSE); |
| |
5579 if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') |
| |
5580 gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); |
| |
5581 } |
| |
5582 gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0); |
| |
5583 g_signal_connect(G_OBJECT(input), "changed", |
| |
5584 G_CALLBACK(addchat_set_sensitive_if_input_cb), data); |
| |
5585 } |
| |
5586 |
| |
5587 /* Do the following for any type of input widget */ |
| |
5588 if (focus) |
| |
5589 { |
| |
5590 gtk_widget_grab_focus(input); |
| |
5591 focus = FALSE; |
| |
5592 } |
| |
5593 gtk_label_set_mnemonic_widget(GTK_LABEL(label), input); |
| |
5594 pidgin_set_accessible_label(input, label); |
| |
5595 g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier); |
| |
5596 g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int)); |
| |
5597 g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required)); |
| |
5598 data->entries = g_list_append(data->entries, input); |
| |
5599 |
| |
5600 g_free(pce); |
| |
5601 } |
| |
5602 |
| |
5603 g_list_free(list); |
| |
5604 g_hash_table_destroy(defaults); |
| |
5605 |
| |
5606 /* Set whether the "OK" button should be clickable initially */ |
| |
5607 addchat_set_sensitive_if_input_cb(NULL, data); |
| |
5608 |
| |
5609 gtk_widget_show_all(data->entries_box); |
| |
5610 } |
| |
5611 |
| |
5612 static void |
| |
5613 addchat_select_account_cb(GObject *w, PurpleAccount *account, |
| |
5614 PidginAddChatData *data) |
| |
5615 { |
| |
5616 if (strcmp(purple_account_get_protocol_id(data->account), |
| |
5617 purple_account_get_protocol_id(account)) == 0) |
| |
5618 { |
| |
5619 data->account = account; |
| |
5620 } |
| |
5621 else |
| |
5622 { |
| |
5623 data->account = account; |
| |
5624 rebuild_addchat_entries(data); |
| |
5625 } |
| |
5626 } |
| |
5627 |
| |
5628 static void |
| |
5629 pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group, |
| |
5630 const char *alias, const char *name) |
| |
5631 { |
| |
5632 PidginAddChatData *data; |
| |
5633 PidginBuddyList *gtkblist; |
| |
5634 GList *l; |
| |
5635 PurpleConnection *gc; |
| |
5636 GtkWidget *label; |
| |
5637 GtkWidget *rowbox; |
| |
5638 GtkWidget *hbox; |
| |
5639 GtkWidget *vbox; |
| |
5640 GtkWidget *img; |
| |
5641 |
| |
5642 if (account != NULL) { |
| |
5643 gc = purple_account_get_connection(account); |
| |
5644 |
| |
5645 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat == NULL) { |
| |
5646 purple_notify_error(gc, NULL, _("This protocol does not support chat rooms."), NULL); |
| |
5647 return; |
| |
5648 } |
| |
5649 } else { |
| |
5650 /* Find an account with chat capabilities */ |
| |
5651 for (l = purple_connections_get_all(); l != NULL; l = l->next) { |
| |
5652 gc = (PurpleConnection *)l->data; |
| |
5653 |
| |
5654 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat != NULL) { |
| |
5655 account = purple_connection_get_account(gc); |
| |
5656 break; |
| |
5657 } |
| |
5658 } |
| |
5659 |
| |
5660 if (account == NULL) { |
| |
5661 purple_notify_error(NULL, NULL, |
| |
5662 _("You are not currently signed on with any " |
| |
5663 "protocols that have the ability to chat."), NULL); |
| |
5664 return; |
| |
5665 } |
| |
5666 } |
| |
5667 |
| |
5668 data = g_new0(PidginAddChatData, 1); |
| |
5669 data->account = account; |
| |
5670 data->default_chat_name = g_strdup(name); |
| |
5671 |
| |
5672 img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION, |
| |
5673 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE)); |
| |
5674 |
| |
5675 gtkblist = PIDGIN_BLIST(purple_get_blist()); |
| |
5676 |
| |
5677 data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
| |
5678 |
| |
5679 data->window = gtk_dialog_new_with_buttons(_("Add Chat"), |
| |
5680 NULL, GTK_DIALOG_NO_SEPARATOR, |
| |
5681 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
| |
5682 GTK_STOCK_ADD, GTK_RESPONSE_OK, |
| |
5683 NULL); |
| |
5684 |
| |
5685 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); |
| |
5686 gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE); |
| |
5687 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); |
| |
5688 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER); |
| |
5689 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE); |
| |
5690 gtk_window_set_role(GTK_WINDOW(data->window), "add_chat"); |
| |
5691 gtk_window_set_type_hint(GTK_WINDOW(data->window), |
| |
5692 GDK_WINDOW_TYPE_HINT_DIALOG); |
| |
5693 |
| |
5694 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); |
| |
5695 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); |
| |
5696 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
| |
5697 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
| |
5698 |
| |
5699 vbox = gtk_vbox_new(FALSE, 5); |
| |
5700 gtk_container_add(GTK_CONTAINER(hbox), vbox); |
| |
5701 |
| |
5702 label = gtk_label_new( |
| |
5703 _("Please enter an alias, and the appropriate information " |
| |
5704 "about the chat you would like to add to your buddy list.\n")); |
| |
5705 gtk_widget_set_size_request(label, 400, -1); |
| |
5706 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
5707 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
| |
5708 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
| |
5709 |
| |
5710 rowbox = gtk_hbox_new(FALSE, 5); |
| |
5711 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); |
| |
5712 |
| |
5713 label = gtk_label_new(_("Account:")); |
| |
5714 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5715 gtk_size_group_add_widget(data->sg, label); |
| |
5716 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
5717 |
| |
5718 data->account_menu = pidgin_account_option_menu_new(account, FALSE, |
| |
5719 G_CALLBACK(addchat_select_account_cb), |
| |
5720 chat_account_filter_func, data); |
| |
5721 gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); |
| |
5722 pidgin_set_accessible_label (data->account_menu, label); |
| |
5723 |
| |
5724 data->entries_box = gtk_vbox_new(FALSE, 5); |
| |
5725 gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); |
| |
5726 gtk_box_pack_start(GTK_BOX(vbox), data->entries_box, TRUE, TRUE, 0); |
| |
5727 |
| |
5728 rebuild_addchat_entries(data); |
| |
5729 |
| |
5730 rowbox = gtk_hbox_new(FALSE, 5); |
| |
5731 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); |
| |
5732 |
| |
5733 label = gtk_label_new(_("Alias:")); |
| |
5734 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5735 gtk_size_group_add_widget(data->sg, label); |
| |
5736 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
5737 |
| |
5738 data->alias_entry = gtk_entry_new(); |
| |
5739 if (alias != NULL) |
| |
5740 gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias); |
| |
5741 gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); |
| |
5742 gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE); |
| |
5743 pidgin_set_accessible_label (data->alias_entry, label); |
| |
5744 |
| |
5745 rowbox = gtk_hbox_new(FALSE, 5); |
| |
5746 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); |
| |
5747 |
| |
5748 label = gtk_label_new(_("Group:")); |
| |
5749 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); |
| |
5750 gtk_size_group_add_widget(data->sg, label); |
| |
5751 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); |
| |
5752 |
| |
5753 data->group_combo = gtk_combo_new(); |
| |
5754 gtk_combo_set_popdown_strings(GTK_COMBO(data->group_combo), groups_tree()); |
| |
5755 gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); |
| |
5756 |
| |
5757 if (group) |
| |
5758 { |
| |
5759 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry), |
| |
5760 group->name); |
| |
5761 } |
| |
5762 pidgin_set_accessible_label (data->group_combo, label); |
| |
5763 |
| |
5764 g_signal_connect(G_OBJECT(data->window), "response", |
| |
5765 G_CALLBACK(add_chat_resp_cb), data); |
| |
5766 |
| |
5767 gtk_widget_show_all(data->window); |
| |
5768 } |
| |
5769 |
| |
5770 static void |
| |
5771 add_group_cb(PurpleConnection *gc, const char *group_name) |
| |
5772 { |
| |
5773 PurpleGroup *group; |
| |
5774 |
| |
5775 if ((group_name == NULL) || (*group_name == '\0')) |
| |
5776 return; |
| |
5777 |
| |
5778 group = purple_group_new(group_name); |
| |
5779 purple_blist_add_group(group, NULL); |
| |
5780 } |
| |
5781 |
| |
5782 static void |
| |
5783 pidgin_blist_request_add_group(void) |
| |
5784 { |
| |
5785 purple_request_input(NULL, _("Add Group"), NULL, |
| |
5786 _("Please enter the name of the group to be added."), |
| |
5787 NULL, FALSE, FALSE, NULL, |
| |
5788 _("Add"), G_CALLBACK(add_group_cb), |
| |
5789 _("Cancel"), NULL, NULL); |
| |
5790 } |
| |
5791 |
| |
5792 void |
| |
5793 pidgin_blist_toggle_visibility() |
| |
5794 { |
| |
5795 if (gtkblist && gtkblist->window) { |
| |
5796 if (GTK_WIDGET_VISIBLE(gtkblist->window)) { |
| |
5797 purple_blist_set_visible(PIDGIN_WINDOW_ICONIFIED(gtkblist->window) || gtk_blist_obscured); |
| |
5798 } else { |
| |
5799 purple_blist_set_visible(TRUE); |
| |
5800 } |
| |
5801 } |
| |
5802 } |
| |
5803 |
| |
5804 void |
| |
5805 pidgin_blist_visibility_manager_add() |
| |
5806 { |
| |
5807 visibility_manager_count++; |
| |
5808 purple_debug_info("gtkblist", "added visibility manager: %d\n", visibility_manager_count); |
| |
5809 } |
| |
5810 |
| |
5811 void |
| |
5812 pidgin_blist_visibility_manager_remove() |
| |
5813 { |
| |
5814 if (visibility_manager_count) |
| |
5815 visibility_manager_count--; |
| |
5816 if (!visibility_manager_count) |
| |
5817 purple_blist_set_visible(TRUE); |
| |
5818 purple_debug_info("gtkblist", "removed visibility manager: %d\n", visibility_manager_count); |
| |
5819 } |
| |
5820 |
| |
5821 void pidgin_blist_add_alert(GtkWidget *widget) |
| |
5822 { |
| |
5823 gtk_container_add(GTK_CONTAINER(gtkblist->scrollbook), widget); |
| |
5824 if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window)) |
| |
5825 pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE); |
| |
5826 } |
| |
5827 |
| |
5828 void |
| |
5829 pidgin_blist_set_headline(const char *text, GdkPixbuf *pixbuf, GCallback callback, |
| |
5830 gpointer user_data, GDestroyNotify destroy) |
| |
5831 { |
| |
5832 /* Destroy any existing headline first */ |
| |
5833 if (gtkblist->headline_destroy) |
| |
5834 gtkblist->headline_destroy(gtkblist->headline_data); |
| |
5835 |
| |
5836 gtk_label_set_markup(GTK_LABEL(gtkblist->headline_label), text); |
| |
5837 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkblist->headline_image), pixbuf); |
| |
5838 |
| |
5839 gtkblist->headline_callback = callback; |
| |
5840 gtkblist->headline_data = user_data; |
| |
5841 gtkblist->headline_destroy = destroy; |
| |
5842 if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window)) |
| |
5843 pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE); |
| |
5844 gtk_widget_show_all(gtkblist->headline_hbox); |
| |
5845 } |
| |
5846 |
| |
5847 static PurpleBlistUiOps blist_ui_ops = |
| |
5848 { |
| |
5849 pidgin_blist_new_list, |
| |
5850 pidgin_blist_new_node, |
| |
5851 pidgin_blist_show, |
| |
5852 pidgin_blist_update, |
| |
5853 pidgin_blist_remove, |
| |
5854 pidgin_blist_destroy, |
| |
5855 pidgin_blist_set_visible, |
| |
5856 pidgin_blist_request_add_buddy, |
| |
5857 pidgin_blist_request_add_chat, |
| |
5858 pidgin_blist_request_add_group |
| |
5859 }; |
| |
5860 |
| |
5861 |
| |
5862 PurpleBlistUiOps * |
| |
5863 pidgin_blist_get_ui_ops(void) |
| |
5864 { |
| |
5865 return &blist_ui_ops; |
| |
5866 } |
| |
5867 |
| |
5868 PidginBuddyList *pidgin_blist_get_default_gtk_blist() |
| |
5869 { |
| |
5870 return gtkblist; |
| |
5871 } |
| |
5872 |
| |
5873 static void account_signon_cb(PurpleConnection *gc, gpointer z) |
| |
5874 { |
| |
5875 PurpleAccount *account = purple_connection_get_account(gc); |
| |
5876 PurpleBlistNode *gnode, *cnode; |
| |
5877 for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) |
| |
5878 { |
| |
5879 if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) |
| |
5880 continue; |
| |
5881 for(cnode = gnode->child; cnode; cnode = cnode->next) |
| |
5882 { |
| |
5883 PurpleChat *chat; |
| |
5884 |
| |
5885 if(!PURPLE_BLIST_NODE_IS_CHAT(cnode)) |
| |
5886 continue; |
| |
5887 |
| |
5888 chat = (PurpleChat *)cnode; |
| |
5889 |
| |
5890 if(chat->account != account) |
| |
5891 continue; |
| |
5892 |
| |
5893 if(purple_blist_node_get_bool((PurpleBlistNode*)chat, "gtk-autojoin") || |
| |
5894 (purple_blist_node_get_string((PurpleBlistNode*)chat, |
| |
5895 "gtk-autojoin") != NULL)) |
| |
5896 serv_join_chat(gc, chat->components); |
| |
5897 } |
| |
5898 } |
| |
5899 } |
| |
5900 |
| |
5901 void * |
| |
5902 pidgin_blist_get_handle() { |
| |
5903 static int handle; |
| |
5904 |
| |
5905 return &handle; |
| |
5906 } |
| |
5907 |
| |
5908 static gboolean buddy_signonoff_timeout_cb(PurpleBuddy *buddy) |
| |
5909 { |
| |
5910 struct _pidgin_blist_node *gtknode = ((PurpleBlistNode*)buddy)->ui_data; |
| |
5911 |
| |
5912 gtknode->recent_signonoff = FALSE; |
| |
5913 gtknode->recent_signonoff_timer = 0; |
| |
5914 |
| |
5915 pidgin_blist_update(NULL, (PurpleBlistNode*)buddy); |
| |
5916 |
| |
5917 return FALSE; |
| |
5918 } |
| |
5919 |
| |
5920 static void buddy_signonoff_cb(PurpleBuddy *buddy) |
| |
5921 { |
| |
5922 struct _pidgin_blist_node *gtknode; |
| |
5923 |
| |
5924 if(!((PurpleBlistNode*)buddy)->ui_data) { |
| |
5925 pidgin_blist_new_node((PurpleBlistNode*)buddy); |
| |
5926 } |
| |
5927 |
| |
5928 gtknode = ((PurpleBlistNode*)buddy)->ui_data; |
| |
5929 |
| |
5930 gtknode->recent_signonoff = TRUE; |
| |
5931 |
| |
5932 if(gtknode->recent_signonoff_timer > 0) |
| |
5933 purple_timeout_remove(gtknode->recent_signonoff_timer); |
| |
5934 gtknode->recent_signonoff_timer = purple_timeout_add(10000, |
| |
5935 (GSourceFunc)buddy_signonoff_timeout_cb, buddy); |
| |
5936 } |
| |
5937 |
| |
5938 void pidgin_blist_init(void) |
| |
5939 { |
| |
5940 void *gtk_blist_handle = pidgin_blist_get_handle(); |
| |
5941 |
| |
5942 purple_signal_connect(purple_connections_get_handle(), "signed-on", |
| |
5943 gtk_blist_handle, PURPLE_CALLBACK(account_signon_cb), |
| |
5944 NULL); |
| |
5945 |
| |
5946 /* Initialize prefs */ |
| |
5947 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/blist"); |
| |
5948 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons", TRUE); |
| |
5949 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups", FALSE); |
| |
5950 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time", TRUE); |
| |
5951 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies", FALSE); |
| |
5952 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE); |
| |
5953 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", FALSE); |
| |
5954 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/sort_type", "alphabetical"); |
| |
5955 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/x", 0); |
| |
5956 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/y", 0); |
| |
5957 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/width", 250); /* Golden ratio, baby */ |
| |
5958 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/height", 405); /* Golden ratio, baby */ |
| |
5959 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500); |
| |
5960 |
| |
5961 /* Register our signals */ |
| |
5962 purple_signal_register(gtk_blist_handle, "gtkblist-hiding", |
| |
5963 purple_marshal_VOID__POINTER, NULL, 1, |
| |
5964 purple_value_new(PURPLE_TYPE_SUBTYPE, |
| |
5965 PURPLE_SUBTYPE_BLIST)); |
| |
5966 |
| |
5967 purple_signal_register(gtk_blist_handle, "gtkblist-unhiding", |
| |
5968 purple_marshal_VOID__POINTER, NULL, 1, |
| |
5969 purple_value_new(PURPLE_TYPE_SUBTYPE, |
| |
5970 PURPLE_SUBTYPE_BLIST)); |
| |
5971 |
| |
5972 purple_signal_register(gtk_blist_handle, "gtkblist-created", |
| |
5973 purple_marshal_VOID__POINTER, NULL, 1, |
| |
5974 purple_value_new(PURPLE_TYPE_SUBTYPE, |
| |
5975 PURPLE_SUBTYPE_BLIST)); |
| |
5976 |
| |
5977 purple_signal_register(gtk_blist_handle, "drawing-tooltip", |
| |
5978 purple_marshal_VOID__POINTER_POINTER_UINT, NULL, 3, |
| |
5979 purple_value_new(PURPLE_TYPE_SUBTYPE, |
| |
5980 PURPLE_SUBTYPE_BLIST_NODE), |
| |
5981 purple_value_new_outgoing(PURPLE_TYPE_BOXED, "GString *"), |
| |
5982 purple_value_new(PURPLE_TYPE_BOOLEAN)); |
| |
5983 |
| |
5984 |
| |
5985 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", gtk_blist_handle, PURPLE_CALLBACK(buddy_signonoff_cb), NULL); |
| |
5986 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", gtk_blist_handle, PURPLE_CALLBACK(buddy_signonoff_cb), NULL); |
| |
5987 purple_signal_connect(purple_blist_get_handle(), "buddy-privacy-changed", gtk_blist_handle, PURPLE_CALLBACK(pidgin_blist_update_privacy_cb), NULL); |
| |
5988 } |
| |
5989 |
| |
5990 void |
| |
5991 pidgin_blist_uninit(void) { |
| |
5992 purple_signals_unregister_by_instance(pidgin_blist_get_handle()); |
| |
5993 purple_signals_disconnect_by_handle(pidgin_blist_get_handle()); |
| |
5994 } |
| |
5995 |
| |
5996 /********************************************************************* |
| |
5997 * Buddy List sorting functions * |
| |
5998 *********************************************************************/ |
| |
5999 |
| |
6000 GList *pidgin_blist_get_sort_methods() |
| |
6001 { |
| |
6002 return pidgin_blist_sort_methods; |
| |
6003 } |
| |
6004 |
| |
6005 void pidgin_blist_sort_method_reg(const char *id, const char *name, pidgin_blist_sort_function func) |
| |
6006 { |
| |
6007 struct pidgin_blist_sort_method *method = g_new0(struct pidgin_blist_sort_method, 1); |
| |
6008 method->id = g_strdup(id); |
| |
6009 method->name = g_strdup(name); |
| |
6010 method->func = func; |
| |
6011 pidgin_blist_sort_methods = g_list_append(pidgin_blist_sort_methods, method); |
| |
6012 pidgin_blist_update_sort_methods(); |
| |
6013 } |
| |
6014 |
| |
6015 void pidgin_blist_sort_method_unreg(const char *id){ |
| |
6016 GList *l = pidgin_blist_sort_methods; |
| |
6017 |
| |
6018 while(l) { |
| |
6019 struct pidgin_blist_sort_method *method = l->data; |
| |
6020 if(!strcmp(method->id, id)) { |
| |
6021 pidgin_blist_sort_methods = g_list_delete_link(pidgin_blist_sort_methods, l); |
| |
6022 g_free(method->id); |
| |
6023 g_free(method->name); |
| |
6024 g_free(method); |
| |
6025 break; |
| |
6026 } |
| |
6027 } |
| |
6028 pidgin_blist_update_sort_methods(); |
| |
6029 } |
| |
6030 |
| |
6031 void pidgin_blist_sort_method_set(const char *id){ |
| |
6032 GList *l = pidgin_blist_sort_methods; |
| |
6033 |
| |
6034 if(!id) |
| |
6035 id = "none"; |
| |
6036 |
| |
6037 while (l && strcmp(((struct pidgin_blist_sort_method*)l->data)->id, id)) |
| |
6038 l = l->next; |
| |
6039 |
| |
6040 if (l) { |
| |
6041 current_sort_method = l->data; |
| |
6042 } else if (!current_sort_method) { |
| |
6043 pidgin_blist_sort_method_set("none"); |
| |
6044 return; |
| |
6045 } |
| |
6046 if (!strcmp(id, "none")) { |
| |
6047 redo_buddy_list(purple_get_blist(), TRUE, FALSE); |
| |
6048 } else { |
| |
6049 redo_buddy_list(purple_get_blist(), FALSE, FALSE); |
| |
6050 } |
| |
6051 } |
| |
6052 |
| |
6053 /****************************************** |
| |
6054 ** Sort Methods |
| |
6055 ******************************************/ |
| |
6056 |
| |
6057 static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter parent_iter, GtkTreeIter *cur, GtkTreeIter *iter) |
| |
6058 { |
| |
6059 PurpleBlistNode *sibling = node->prev; |
| |
6060 GtkTreeIter sibling_iter; |
| |
6061 |
| |
6062 if (cur != NULL) { |
| |
6063 *iter = *cur; |
| |
6064 return; |
| |
6065 } |
| |
6066 |
| |
6067 while (sibling && !get_iter_from_node(sibling, &sibling_iter)) { |
| |
6068 sibling = sibling->prev; |
| |
6069 } |
| |
6070 |
| |
6071 gtk_tree_store_insert_after(gtkblist->treemodel, iter, |
| |
6072 node->parent ? &parent_iter : NULL, |
| |
6073 sibling ? &sibling_iter : NULL); |
| |
6074 } |
| |
6075 |
| |
6076 #if GTK_CHECK_VERSION(2,2,1) |
| |
6077 |
| |
6078 static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter) |
| |
6079 { |
| |
6080 GtkTreeIter more_z; |
| |
6081 |
| |
6082 const char *my_name; |
| |
6083 |
| |
6084 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
6085 my_name = purple_contact_get_alias((PurpleContact*)node); |
| |
6086 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
6087 my_name = purple_chat_get_name((PurpleChat*)node); |
| |
6088 } else { |
| |
6089 sort_method_none(node, blist, groupiter, cur, iter); |
| |
6090 return; |
| |
6091 } |
| |
6092 |
| |
6093 |
| |
6094 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { |
| |
6095 gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0); |
| |
6096 return; |
| |
6097 } |
| |
6098 |
| |
6099 do { |
| |
6100 GValue val; |
| |
6101 PurpleBlistNode *n; |
| |
6102 const char *this_name; |
| |
6103 int cmp; |
| |
6104 |
| |
6105 val.g_type = 0; |
| |
6106 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
| |
6107 n = g_value_get_pointer(&val); |
| |
6108 |
| |
6109 if(PURPLE_BLIST_NODE_IS_CONTACT(n)) { |
| |
6110 this_name = purple_contact_get_alias((PurpleContact*)n); |
| |
6111 } else if(PURPLE_BLIST_NODE_IS_CHAT(n)) { |
| |
6112 this_name = purple_chat_get_name((PurpleChat*)n); |
| |
6113 } else { |
| |
6114 this_name = NULL; |
| |
6115 } |
| |
6116 |
| |
6117 cmp = purple_utf8_strcasecmp(my_name, this_name); |
| |
6118 |
| |
6119 if(this_name && (cmp < 0 || (cmp == 0 && node < n))) { |
| |
6120 if(cur) { |
| |
6121 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); |
| |
6122 *iter = *cur; |
| |
6123 return; |
| |
6124 } else { |
| |
6125 gtk_tree_store_insert_before(gtkblist->treemodel, iter, |
| |
6126 &groupiter, &more_z); |
| |
6127 return; |
| |
6128 } |
| |
6129 } |
| |
6130 g_value_unset(&val); |
| |
6131 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); |
| |
6132 |
| |
6133 if(cur) { |
| |
6134 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); |
| |
6135 *iter = *cur; |
| |
6136 return; |
| |
6137 } else { |
| |
6138 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| |
6139 return; |
| |
6140 } |
| |
6141 } |
| |
6142 |
| |
6143 static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter) |
| |
6144 { |
| |
6145 GtkTreeIter more_z; |
| |
6146 |
| |
6147 PurpleBuddy *my_buddy, *this_buddy; |
| |
6148 |
| |
6149 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
6150 my_buddy = purple_contact_get_priority_buddy((PurpleContact*)node); |
| |
6151 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
6152 if (cur != NULL) { |
| |
6153 *iter = *cur; |
| |
6154 return; |
| |
6155 } |
| |
6156 |
| |
6157 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| |
6158 return; |
| |
6159 } else { |
| |
6160 sort_method_none(node, blist, groupiter, cur, iter); |
| |
6161 return; |
| |
6162 } |
| |
6163 |
| |
6164 |
| |
6165 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { |
| |
6166 gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0); |
| |
6167 return; |
| |
6168 } |
| |
6169 |
| |
6170 do { |
| |
6171 GValue val; |
| |
6172 PurpleBlistNode *n; |
| |
6173 gint name_cmp; |
| |
6174 gint presence_cmp; |
| |
6175 |
| |
6176 val.g_type = 0; |
| |
6177 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
| |
6178 n = g_value_get_pointer(&val); |
| |
6179 |
| |
6180 if(PURPLE_BLIST_NODE_IS_CONTACT(n)) { |
| |
6181 this_buddy = purple_contact_get_priority_buddy((PurpleContact*)n); |
| |
6182 } else { |
| |
6183 this_buddy = NULL; |
| |
6184 } |
| |
6185 |
| |
6186 name_cmp = purple_utf8_strcasecmp( |
| |
6187 purple_contact_get_alias(purple_buddy_get_contact(my_buddy)), |
| |
6188 (this_buddy |
| |
6189 ? purple_contact_get_alias(purple_buddy_get_contact(this_buddy)) |
| |
6190 : NULL)); |
| |
6191 |
| |
6192 presence_cmp = purple_presence_compare( |
| |
6193 purple_buddy_get_presence(my_buddy), |
| |
6194 this_buddy ? purple_buddy_get_presence(this_buddy) : NULL); |
| |
6195 |
| |
6196 if (this_buddy == NULL || |
| |
6197 (presence_cmp < 0 || |
| |
6198 (presence_cmp == 0 && |
| |
6199 (name_cmp < 0 || (name_cmp == 0 && node < n))))) |
| |
6200 { |
| |
6201 if (cur != NULL) |
| |
6202 { |
| |
6203 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); |
| |
6204 *iter = *cur; |
| |
6205 return; |
| |
6206 } |
| |
6207 else |
| |
6208 { |
| |
6209 gtk_tree_store_insert_before(gtkblist->treemodel, iter, |
| |
6210 &groupiter, &more_z); |
| |
6211 return; |
| |
6212 } |
| |
6213 } |
| |
6214 |
| |
6215 g_value_unset(&val); |
| |
6216 } |
| |
6217 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), |
| |
6218 &more_z)); |
| |
6219 |
| |
6220 if (cur) { |
| |
6221 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); |
| |
6222 *iter = *cur; |
| |
6223 return; |
| |
6224 } else { |
| |
6225 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| |
6226 return; |
| |
6227 } |
| |
6228 } |
| |
6229 |
| |
6230 static void sort_method_log(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter) |
| |
6231 { |
| |
6232 GtkTreeIter more_z; |
| |
6233 |
| |
6234 int log_size = 0, this_log_size = 0; |
| |
6235 const char *buddy_name, *this_buddy_name; |
| |
6236 |
| |
6237 if(cur && (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter) == 1)) { |
| |
6238 *iter = *cur; |
| |
6239 return; |
| |
6240 } |
| |
6241 |
| |
6242 if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { |
| |
6243 PurpleBlistNode *n; |
| |
6244 for (n = node->child; n; n = n->next) |
| |
6245 log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy*)(n))->name, ((PurpleBuddy*)(n))->account); |
| |
6246 buddy_name = purple_contact_get_alias((PurpleContact*)node); |
| |
6247 } else if(PURPLE_BLIST_NODE_IS_CHAT(node)) { |
| |
6248 /* we don't have a reliable way of getting the log filename |
| |
6249 * from the chat info in the blist, yet */ |
| |
6250 if (cur != NULL) { |
| |
6251 *iter = *cur; |
| |
6252 return; |
| |
6253 } |
| |
6254 |
| |
6255 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| |
6256 return; |
| |
6257 } else { |
| |
6258 sort_method_none(node, blist, groupiter, cur, iter); |
| |
6259 return; |
| |
6260 } |
| |
6261 |
| |
6262 |
| |
6263 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { |
| |
6264 gtk_tree_store_insert(gtkblist->treemodel, iter, &groupiter, 0); |
| |
6265 return; |
| |
6266 } |
| |
6267 |
| |
6268 do { |
| |
6269 GValue val; |
| |
6270 PurpleBlistNode *n; |
| |
6271 PurpleBlistNode *n2; |
| |
6272 int cmp; |
| |
6273 |
| |
6274 val.g_type = 0; |
| |
6275 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
| |
6276 n = g_value_get_pointer(&val); |
| |
6277 this_log_size = 0; |
| |
6278 |
| |
6279 if(PURPLE_BLIST_NODE_IS_CONTACT(n)) { |
| |
6280 for (n2 = n->child; n2; n2 = n2->next) |
| |
6281 this_log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy*)(n2))->name, ((PurpleBuddy*)(n2))->account); |
| |
6282 this_buddy_name = purple_contact_get_alias((PurpleContact*)n); |
| |
6283 } else { |
| |
6284 this_buddy_name = NULL; |
| |
6285 } |
| |
6286 |
| |
6287 cmp = purple_utf8_strcasecmp(buddy_name, this_buddy_name); |
| |
6288 |
| |
6289 if (!PURPLE_BLIST_NODE_IS_CONTACT(n) || log_size > this_log_size || |
| |
6290 ((log_size == this_log_size) && |
| |
6291 (cmp < 0 || (cmp == 0 && node < n)))) { |
| |
6292 if (cur != NULL) { |
| |
6293 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); |
| |
6294 *iter = *cur; |
| |
6295 return; |
| |
6296 } else { |
| |
6297 gtk_tree_store_insert_before(gtkblist->treemodel, iter, |
| |
6298 &groupiter, &more_z); |
| |
6299 return; |
| |
6300 } |
| |
6301 } |
| |
6302 g_value_unset(&val); |
| |
6303 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); |
| |
6304 |
| |
6305 if (cur != NULL) { |
| |
6306 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); |
| |
6307 *iter = *cur; |
| |
6308 return; |
| |
6309 } else { |
| |
6310 gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter); |
| |
6311 return; |
| |
6312 } |
| |
6313 } |
| |
6314 |
| |
6315 #endif |
| |
6316 |
| |
6317 static void |
| |
6318 plugin_act(GtkObject *obj, PurplePluginAction *pam) |
| |
6319 { |
| |
6320 if (pam && pam->callback) |
| |
6321 pam->callback(pam); |
| |
6322 } |
| |
6323 |
| |
6324 static void |
| |
6325 build_plugin_actions(GtkWidget *menu, PurplePlugin *plugin, |
| |
6326 gpointer context) |
| |
6327 { |
| |
6328 GtkWidget *menuitem; |
| |
6329 PurplePluginAction *action = NULL; |
| |
6330 GList *actions, *l; |
| |
6331 |
| |
6332 actions = PURPLE_PLUGIN_ACTIONS(plugin, context); |
| |
6333 |
| |
6334 for (l = actions; l != NULL; l = l->next) |
| |
6335 { |
| |
6336 if (l->data) |
| |
6337 { |
| |
6338 action = (PurplePluginAction *) l->data; |
| |
6339 action->plugin = plugin; |
| |
6340 action->context = context; |
| |
6341 |
| |
6342 menuitem = gtk_menu_item_new_with_label(action->label); |
| |
6343 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); |
| |
6344 |
| |
6345 g_signal_connect(G_OBJECT(menuitem), "activate", |
| |
6346 G_CALLBACK(plugin_act), action); |
| |
6347 g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", |
| |
6348 action, |
| |
6349 (GDestroyNotify)purple_plugin_action_free); |
| |
6350 gtk_widget_show(menuitem); |
| |
6351 } |
| |
6352 else |
| |
6353 pidgin_separator(menu); |
| |
6354 } |
| |
6355 |
| |
6356 g_list_free(actions); |
| |
6357 } |
| |
6358 |
| |
6359 static void |
| |
6360 modify_account_cb(GtkWidget *widget, gpointer data) |
| |
6361 { |
| |
6362 pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, data); |
| |
6363 } |
| |
6364 |
| |
6365 static void |
| |
6366 enable_account_cb(GtkCheckMenuItem *widget, gpointer data) |
| |
6367 { |
| |
6368 PurpleAccount *account = data; |
| |
6369 const PurpleSavedStatus *saved_status; |
| |
6370 |
| |
6371 saved_status = purple_savedstatus_get_current(); |
| |
6372 purple_savedstatus_activate_for_account(saved_status, account); |
| |
6373 |
| |
6374 purple_account_set_enabled(account, PIDGIN_UI, TRUE); |
| |
6375 } |
| |
6376 |
| |
6377 static void |
| |
6378 disable_account_cb(GtkCheckMenuItem *widget, gpointer data) |
| |
6379 { |
| |
6380 PurpleAccount *account = data; |
| |
6381 |
| |
6382 purple_account_set_enabled(account, PIDGIN_UI, FALSE); |
| |
6383 } |
| |
6384 |
| |
6385 void |
| |
6386 pidgin_blist_update_accounts_menu(void) |
| |
6387 { |
| |
6388 GtkWidget *menuitem = NULL, *submenu = NULL; |
| |
6389 GtkAccelGroup *accel_group = NULL; |
| |
6390 GList *l = NULL, *accounts = NULL; |
| |
6391 gboolean disabled_accounts = FALSE; |
| |
6392 |
| |
6393 if (accountmenu == NULL) |
| |
6394 return; |
| |
6395 |
| |
6396 /* Clear the old Accounts menu */ |
| |
6397 for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = l->next) { |
| |
6398 menuitem = l->data; |
| |
6399 |
| |
6400 if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Add\\/Edit"))) |
| |
6401 gtk_widget_destroy(menuitem); |
| |
6402 } |
| |
6403 |
| |
6404 for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { |
| |
6405 char *buf = NULL; |
| |
6406 char *accel_path_buf = NULL; |
| |
6407 GtkWidget *image = NULL; |
| |
6408 PurpleConnection *gc = NULL; |
| |
6409 PurpleAccount *account = NULL; |
| |
6410 GdkPixbuf *pixbuf = NULL; |
| |
6411 PurplePlugin *plugin = NULL; |
| |
6412 |
| |
6413 account = accounts->data; |
| |
6414 accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu)); |
| |
6415 |
| |
6416 if(purple_account_get_enabled(account, PIDGIN_UI)) { |
| |
6417 buf = g_strconcat(purple_account_get_username(account), " (", |
| |
6418 purple_account_get_protocol_name(account), ")", NULL); |
| |
6419 menuitem = gtk_image_menu_item_new_with_label(buf); |
| |
6420 accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL); |
| |
6421 g_free(buf); |
| |
6422 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); |
| |
6423 if (pixbuf != NULL) |
| |
6424 { |
| |
6425 if (!purple_account_is_connected(account)) |
| |
6426 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, |
| |
6427 0.0, FALSE); |
| |
6428 image = gtk_image_new_from_pixbuf(pixbuf); |
| |
6429 g_object_unref(G_OBJECT(pixbuf)); |
| |
6430 gtk_widget_show(image); |
| |
6431 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); |
| |
6432 } |
| |
6433 gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); |
| |
6434 gtk_widget_show(menuitem); |
| |
6435 |
| |
6436 submenu = gtk_menu_new(); |
| |
6437 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); |
| |
6438 gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf); |
| |
6439 g_free(accel_path_buf); |
| |
6440 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
| |
6441 gtk_widget_show(submenu); |
| |
6442 |
| |
6443 |
| |
6444 menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account")); |
| |
6445 g_signal_connect(G_OBJECT(menuitem), "activate", |
| |
6446 G_CALLBACK(modify_account_cb), account); |
| |
6447 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
| |
6448 gtk_widget_show(menuitem); |
| |
6449 |
| |
6450 pidgin_separator(submenu); |
| |
6451 |
| |
6452 gc = purple_account_get_connection(account); |
| |
6453 plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL; |
| |
6454 if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) { |
| |
6455 build_plugin_actions(submenu, plugin, gc); |
| |
6456 } else { |
| |
6457 menuitem = gtk_menu_item_new_with_label(_("No actions available")); |
| |
6458 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
| |
6459 gtk_widget_set_sensitive(menuitem, FALSE); |
| |
6460 gtk_widget_show(menuitem); |
| |
6461 } |
| |
6462 |
| |
6463 pidgin_separator(submenu); |
| |
6464 |
| |
6465 menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable")); |
| |
6466 g_signal_connect(G_OBJECT(menuitem), "activate", |
| |
6467 G_CALLBACK(disable_account_cb), account); |
| |
6468 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
| |
6469 gtk_widget_show(menuitem); |
| |
6470 } else { |
| |
6471 disabled_accounts = TRUE; |
| |
6472 } |
| |
6473 } |
| |
6474 |
| |
6475 if(disabled_accounts) { |
| |
6476 pidgin_separator(accountmenu); |
| |
6477 menuitem = gtk_menu_item_new_with_label(_("Enable Account")); |
| |
6478 gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); |
| |
6479 gtk_widget_show(menuitem); |
| |
6480 |
| |
6481 submenu = gtk_menu_new(); |
| |
6482 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); |
| |
6483 gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account")); |
| |
6484 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
| |
6485 gtk_widget_show(submenu); |
| |
6486 |
| |
6487 for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { |
| |
6488 char *buf = NULL; |
| |
6489 GtkWidget *image = NULL; |
| |
6490 PurpleAccount *account = NULL; |
| |
6491 GdkPixbuf *pixbuf = NULL; |
| |
6492 |
| |
6493 account = accounts->data; |
| |
6494 |
| |
6495 if(!purple_account_get_enabled(account, PIDGIN_UI)) { |
| |
6496 |
| |
6497 disabled_accounts = TRUE; |
| |
6498 |
| |
6499 buf = g_strconcat(purple_account_get_username(account), " (", |
| |
6500 purple_account_get_protocol_name(account), ")", NULL); |
| |
6501 menuitem = gtk_image_menu_item_new_with_label(buf); |
| |
6502 g_free(buf); |
| |
6503 pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); |
| |
6504 if (pixbuf != NULL) |
| |
6505 { |
| |
6506 if (!purple_account_is_connected(account)) |
| |
6507 gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); |
| |
6508 image = gtk_image_new_from_pixbuf(pixbuf); |
| |
6509 g_object_unref(G_OBJECT(pixbuf)); |
| |
6510 gtk_widget_show(image); |
| |
6511 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); |
| |
6512 } |
| |
6513 g_signal_connect(G_OBJECT(menuitem), "activate", |
| |
6514 G_CALLBACK(enable_account_cb), account); |
| |
6515 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); |
| |
6516 gtk_widget_show(menuitem); |
| |
6517 } |
| |
6518 } |
| |
6519 } |
| |
6520 } |
| |
6521 |
| |
6522 static GList *plugin_submenus = NULL; |
| |
6523 |
| |
6524 void |
| |
6525 pidgin_blist_update_plugin_actions(void) |
| |
6526 { |
| |
6527 GtkWidget *menuitem, *submenu; |
| |
6528 PurplePlugin *plugin = NULL; |
| |
6529 GList *l; |
| |
6530 GtkAccelGroup *accel_group; |
| |
6531 |
| |
6532 GtkWidget *pluginmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools")); |
| |
6533 |
| |
6534 g_return_if_fail(pluginmenu != NULL); |
| |
6535 |
| |
6536 /* Remove old plugin action submenus from the Tools menu */ |
| |
6537 for (l = plugin_submenus; l; l = l->next) |
| |
6538 gtk_widget_destroy(GTK_WIDGET(l->data)); |
| |
6539 g_list_free(plugin_submenus); |
| |
6540 plugin_submenus = NULL; |
| |
6541 |
| |
6542 accel_group = gtk_menu_get_accel_group(GTK_MENU(pluginmenu)); |
| |
6543 |
| |
6544 /* Add a submenu for each plugin with custom actions */ |
| |
6545 for (l = purple_plugins_get_loaded(); l; l = l->next) { |
| |
6546 char *path; |
| |
6547 |
| |
6548 plugin = (PurplePlugin *) l->data; |
| |
6549 |
| |
6550 if (PURPLE_IS_PROTOCOL_PLUGIN(plugin)) |
| |
6551 continue; |
| |
6552 |
| |
6553 if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin)) |
| |
6554 continue; |
| |
6555 |
| |
6556 menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name)); |
| |
6557 gtk_menu_shell_append(GTK_MENU_SHELL(pluginmenu), menuitem); |
| |
6558 gtk_widget_show(menuitem); |
| |
6559 |
| |
6560 plugin_submenus = g_list_append(plugin_submenus, menuitem); |
| |
6561 |
| |
6562 submenu = gtk_menu_new(); |
| |
6563 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
| |
6564 gtk_widget_show(submenu); |
| |
6565 |
| |
6566 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); |
| |
6567 path = g_strdup_printf("%s/Tools/%s", gtkblist->ift->path, plugin->info->name); |
| |
6568 gtk_menu_set_accel_path(GTK_MENU(submenu), path); |
| |
6569 g_free(path); |
| |
6570 |
| |
6571 build_plugin_actions(submenu, plugin, NULL); |
| |
6572 } |
| |
6573 } |
| |
6574 |
| |
6575 static void |
| |
6576 sortmethod_act(GtkCheckMenuItem *checkmenuitem, char *id) |
| |
6577 { |
| |
6578 if (gtk_check_menu_item_get_active(checkmenuitem)) |
| |
6579 { |
| |
6580 pidgin_set_cursor(gtkblist->window, GDK_WATCH); |
| |
6581 /* This is redundant. I think. */ |
| |
6582 /* pidgin_blist_sort_method_set(id); */ |
| |
6583 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/sort_type", id); |
| |
6584 |
| |
6585 pidgin_clear_cursor(gtkblist->window); |
| |
6586 } |
| |
6587 } |
| |
6588 |
| |
6589 void |
| |
6590 pidgin_blist_update_sort_methods(void) |
| |
6591 { |
| |
6592 GtkWidget *menuitem = NULL, *activeitem = NULL; |
| |
6593 PidginBlistSortMethod *method = NULL; |
| |
6594 GList *l; |
| |
6595 GSList *sl = NULL; |
| |
6596 GtkWidget *sortmenu; |
| |
6597 const char *m = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type"); |
| |
6598 |
| |
6599 if ((gtkblist == NULL) || (gtkblist->ift == NULL)) |
| |
6600 return; |
| |
6601 |
| |
6602 sortmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Buddies/Sort Buddies")); |
| |
6603 |
| |
6604 if (sortmenu == NULL) |
| |
6605 return; |
| |
6606 |
| |
6607 /* Clear the old menu */ |
| |
6608 for (l = gtk_container_get_children(GTK_CONTAINER(sortmenu)); l; l = l->next) { |
| |
6609 menuitem = l->data; |
| |
6610 gtk_widget_destroy(GTK_WIDGET(menuitem)); |
| |
6611 } |
| |
6612 |
| |
6613 for (l = pidgin_blist_sort_methods; l; l = l->next) { |
| |
6614 method = (PidginBlistSortMethod *) l->data; |
| |
6615 menuitem = gtk_radio_menu_item_new_with_label(sl, _(method->name)); |
| |
6616 if (!strcmp(m, method->id)) |
| |
6617 activeitem = menuitem; |
| |
6618 sl = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem)); |
| |
6619 gtk_menu_shell_append(GTK_MENU_SHELL(sortmenu), menuitem); |
| |
6620 g_signal_connect(G_OBJECT(menuitem), "toggled", |
| |
6621 G_CALLBACK(sortmethod_act), method->id); |
| |
6622 gtk_widget_show(menuitem); |
| |
6623 } |
| |
6624 if (activeitem) |
| |
6625 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(activeitem), TRUE); |
| |
6626 } |