Mercurial > pidgin
annotate src/gtkblist.c @ 6056:395116fdd270
[gaim-migrate @ 6506]
I'm reverting the patch to make f2 retroactively toggle timestamps because
it causes highlighting in the gtkimhtml window to act strangely when
timestamps are disabled. Cheung, if you think you can fix this please do,
but it seems like it's due to the incomplete nature of invisibility in
GtkTextViews.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 08 Jul 2003 01:28:42 +0000 |
parents | dcfe83cdfd42 |
children | 4f41c4aa9913 |
rev | line source |
---|---|
5228 | 1 /* |
2 * gaim | |
3 * | |
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 */ | |
21 | |
22 #ifdef HAVE_CONFIG_H | |
23 #include <config.h> | |
24 #endif | |
25 #ifdef GAIM_PLUGINS | |
26 #ifndef _WIN32 | |
27 #include <dlfcn.h> | |
28 #endif | |
29 #endif /* GAIM_PLUGINS */ | |
30 #include <string.h> | |
31 #include <stdio.h> | |
32 #include <stdlib.h> | |
33 #include <ctype.h> | |
5422 | 34 #include <sys/types.h> |
35 #include <sys/stat.h> | |
36 #include <unistd.h> | |
5228 | 37 #include <math.h> |
38 #include <time.h> | |
39 | |
40 #ifdef _WIN32 | |
41 #include <gdk/gdkwin32.h> | |
42 #else | |
43 #include <unistd.h> | |
44 #include <gdk/gdkx.h> | |
45 #endif | |
46 | |
47 #include <gdk/gdkkeysyms.h> | |
48 #include <gtk/gtk.h> | |
49 #include "prpl.h" | |
50 #include "sound.h" | |
51 #include "gaim.h" | |
52 #include "gtkblist.h" | |
53 #include "gtkpounce.h" | |
54 #include "gtkft.h" | |
5234 | 55 #include "gtkdebug.h" |
5228 | 56 |
57 #ifdef _WIN32 | |
58 #include "win32dep.h" | |
59 #endif | |
60 | |
5422 | 61 GSList *gaim_gtk_blist_sort_methods = NULL; |
62 static struct gaim_gtk_blist_sort_method *current_sort_method = NULL; | |
63 static GtkTreeIter sort_method_none(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
64 static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
65 static GtkTreeIter sort_method_status(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
66 static GtkTreeIter sort_method_log(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
5228 | 67 static struct gaim_gtk_buddy_list *gtkblist = NULL; |
68 | |
69 /* part of the best damn Docklet code this side of Tahiti */ | |
70 static gboolean gaim_gtk_blist_obscured = FALSE; | |
71 | |
72 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); | |
73 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); | |
5234 | 74 static char *gaim_get_tooltip_text(GaimBlistNode *node); |
5228 | 75 static char *item_factory_translate_func (const char *path, gpointer func_data); |
5273 | 76 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter); |
5228 | 77 |
5422 | 78 char sort_method[64]; |
79 | |
5256 | 80 struct _gaim_gtk_blist_node { |
81 GtkTreeRowReference *row; | |
82 }; | |
83 | |
5228 | 84 /*************************************************** |
85 * Callbacks * | |
86 ***************************************************/ | |
87 | |
88 static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) | |
89 { | |
90 if (docklet_count) | |
91 gaim_blist_set_visible(FALSE); | |
92 else | |
93 do_quit(); | |
94 | |
95 /* we handle everything, event should not propogate further */ | |
96 return TRUE; | |
97 } | |
98 | |
99 static gboolean gtk_blist_save_prefs_cb(gpointer data) | |
100 { | |
101 save_prefs(); | |
102 | |
103 /* only run once */ | |
104 return FALSE; | |
105 } | |
106 | |
107 static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) | |
108 { | |
109 /* unfortunately GdkEventConfigure ignores the window gravity, but * | |
110 * the only way we have of setting the position doesn't. we have to * | |
111 * call get_position and get_size because they do pay attention to * | |
112 * the gravity. this is inefficient and I agree it sucks, but it's * | |
113 * more likely to work correctly. - Robot101 */ | |
114 gint x, y; | |
115 | |
116 /* check for visibility because when we aren't visible, this will * | |
117 * give us bogus (0,0) coordinates. - xOr */ | |
118 if (GTK_WIDGET_VISIBLE(w)) { | |
119 gtk_window_get_position(GTK_WINDOW(w), &x, &y); | |
120 | |
121 if (x != blist_pos.x || | |
122 y != blist_pos.y || | |
123 event->width != blist_pos.width || | |
124 event->height != blist_pos.height) { | |
125 blist_pos.x = x; | |
126 blist_pos.y = y; | |
127 blist_pos.width = event->width; | |
128 blist_pos.height = event->height; | |
129 | |
130 if (!g_main_context_find_source_by_user_data(NULL, >k_blist_save_prefs_cb)) { | |
131 g_timeout_add(5000, gtk_blist_save_prefs_cb, >k_blist_save_prefs_cb); | |
132 } | |
133 } | |
134 } | |
135 | |
136 /* continue to handle event normally */ | |
137 return FALSE; | |
138 } | |
139 | |
140 static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data) | |
141 { | |
142 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) | |
143 gaim_gtk_blist_obscured = TRUE; | |
144 else | |
145 gaim_gtk_blist_obscured = FALSE; | |
146 | |
147 /* continue to handle event normally */ | |
148 return FALSE; | |
149 } | |
150 | |
151 static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b) | |
152 { | |
153 serv_get_info(b->account->gc, b->name); | |
154 } | |
155 | |
156 static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b) | |
157 { | |
5234 | 158 gaim_conversation_new(GAIM_CONV_IM, b->account, b->name); |
5228 | 159 } |
160 | |
5234 | 161 static void gtk_blist_menu_join_cb(GtkWidget *w, struct chat *chat) |
5228 | 162 { |
5234 | 163 serv_join_chat(chat->account->gc, chat->components); |
164 } | |
165 | |
166 static void gtk_blist_menu_alias_cb(GtkWidget *w, GaimBlistNode *node) | |
167 { | |
168 if(GAIM_BLIST_NODE_IS_BUDDY(node)) | |
169 alias_dialog_bud((struct buddy*)node); | |
170 else if(GAIM_BLIST_NODE_IS_CHAT(node)) | |
171 alias_dialog_chat((struct chat*)node); | |
5228 | 172 } |
173 | |
174 static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b) | |
175 { | |
176 gaim_gtkpounce_dialog_show(b, NULL); | |
177 } | |
178 | |
179 static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b) | |
180 { | |
5234 | 181 show_log(b->name); |
5228 | 182 } |
183 | |
184 static void gtk_blist_show_systemlog_cb() | |
185 { | |
5234 | 186 show_log(NULL); |
5228 | 187 } |
188 | |
189 static void gtk_blist_show_onlinehelp_cb() | |
190 { | |
5234 | 191 open_url(NULL, WEBSITE "documentation.php"); |
5228 | 192 } |
193 | |
194 static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv) | |
195 { | |
196 GtkTreeIter iter; | |
197 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
198 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
199 | |
200 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
201 GaimBlistNode *node; | |
202 | |
203 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
204 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
205 gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); | |
206 return; | |
207 } | |
208 } | |
209 show_im_dialog(); | |
210 } | |
211 | |
212 static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv) | |
213 { | |
214 GtkTreeIter iter; | |
215 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
216 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
217 | |
218 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
219 GaimBlistNode *node; | |
220 | |
221 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
222 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
223 serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); | |
224 return; | |
225 } | |
226 } | |
227 show_info_dialog(); | |
228 } | |
229 | |
5234 | 230 static void gtk_blist_button_chat_cb(GtkWidget *w, GtkTreeView *tv) |
5228 | 231 { |
5234 | 232 GtkTreeIter iter; |
233 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
234 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
235 | |
236 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
237 GaimBlistNode *node; | |
238 | |
239 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
240 if (GAIM_BLIST_NODE_IS_CHAT(node)) { | |
241 serv_join_chat(((struct chat *)node)->account->gc, ((struct chat*)node)->components); | |
242 return; | |
243 } | |
244 } | |
5228 | 245 join_chat(); |
246 } | |
247 | |
248 static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data) | |
249 { | |
250 gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME); | |
251 } | |
252 | |
253 static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { | |
254 GaimBlistNode *node; | |
255 GValue val = {0,}; | |
256 | |
257 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); | |
258 | |
259 node = g_value_get_pointer(&val); | |
260 | |
261 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
262 gaim_group_set_setting((struct group *)node, "collapsed", NULL); | |
263 gaim_blist_save(); | |
264 } | |
265 } | |
266 | |
267 static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { | |
268 GaimBlistNode *node; | |
269 GValue val = {0,}; | |
270 | |
271 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); | |
272 | |
273 node = g_value_get_pointer(&val); | |
274 | |
275 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
276 gaim_group_set_setting((struct group *)node, "collapsed", "true"); | |
277 gaim_blist_save(); | |
278 } | |
279 } | |
280 | |
281 static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { | |
282 GaimBlistNode *node; | |
283 GtkTreeIter iter; | |
284 GValue val = { 0, }; | |
285 | |
286 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
287 | |
288 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
289 node = g_value_get_pointer(&val); | |
290 | |
291 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
292 struct gaim_conversation *conv = | |
293 gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); | |
5489
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
294 |
5228 | 295 if(conv) { |
5489
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
296 struct gaim_window *win = gaim_conversation_get_window(conv); |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
297 |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
298 gaim_window_raise(win); |
5228 | 299 gaim_window_switch_conversation( |
300 gaim_conversation_get_window(conv), | |
301 gaim_conversation_get_index(conv)); | |
5489
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
302 |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
303 if (GAIM_IS_GTK_WINDOW(win)) |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
304 gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window)); |
5228 | 305 } |
5234 | 306 } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { |
307 serv_join_chat(((struct chat *)node)->account->gc, ((struct chat*)node)->components); | |
5228 | 308 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
309 if (gtk_tree_view_row_expanded(tv, path)) | |
310 gtk_tree_view_collapse_row(tv, path); | |
311 else | |
312 gtk_tree_view_expand_row(tv,path,FALSE); | |
313 } | |
314 } | |
315 | |
5234 | 316 static void gaim_gtk_blist_add_chat_cb() |
317 { | |
318 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
319 GtkTreeIter iter; | |
320 GaimBlistNode *node; | |
321 | |
322 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ | |
323 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
324 if (GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) | |
325 show_add_chat(NULL, (struct group*)node->parent); | |
326 else if (GAIM_BLIST_NODE_IS_GROUP(node)) | |
327 show_add_chat(NULL, (struct group*)node); | |
328 } | |
329 else { | |
330 show_add_chat(NULL, NULL); | |
331 } | |
332 } | |
333 | |
5228 | 334 static void gaim_gtk_blist_add_buddy_cb() |
335 { | |
336 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
337 GtkTreeIter iter; | |
338 GaimBlistNode *node; | |
339 | |
340 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ | |
341 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
5234 | 342 if (GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) |
5228 | 343 show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL); |
344 else if (GAIM_BLIST_NODE_IS_GROUP(node)) | |
345 show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL); | |
346 } | |
347 else { | |
348 show_add_buddy(NULL, NULL, NULL, NULL); | |
349 } | |
350 } | |
351 | |
352 static void | |
353 gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) | |
354 { | |
355 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
356 struct buddy *b = (struct buddy*)node; | |
357 show_confirm_del(b->account->gc, b->name); | |
5234 | 358 } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { |
359 struct chat *chat = (struct chat*)node; | |
360 show_confirm_del_chat(chat); | |
5228 | 361 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
362 struct group *g = (struct group*)node; | |
363 show_confirm_del_group(g); | |
364 } | |
365 } | |
366 | |
367 static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b) | |
368 { | |
369 struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback"); | |
370 if (pbm->callback) | |
371 pbm->callback(pbm->gc, b->name); | |
372 } | |
373 | |
374 static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) | |
375 { | |
376 GtkTreePath *path; | |
377 GaimBlistNode *node; | |
378 GValue val = { 0, }; | |
379 GtkTreeIter iter; | |
380 GtkWidget *menu, *menuitem; | |
381 GtkTreeSelection *sel; | |
382 GList *list; | |
383 GaimPlugin *prpl = NULL; | |
384 GaimPluginProtocolInfo *prpl_info = NULL; | |
385 | |
386 if (event->button != 3) | |
387 return FALSE; | |
388 | |
389 /* Here we figure out which node was clicked */ | |
390 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) | |
391 return FALSE; | |
392 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
393 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
394 node = g_value_get_pointer(&val); | |
395 menu = gtk_menu_new(); | |
396 | |
397 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
5366 | 398 gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); |
5307 | 399 gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); |
5228 | 400 gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); |
401 gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL); | |
5234 | 402 } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { |
403 gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); | |
404 gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); | |
405 gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); | |
5228 | 406 } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { |
407 /* Protocol specific options */ | |
408 prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol); | |
409 | |
410 if (prpl != NULL) | |
411 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
412 | |
413 if (prpl && prpl_info->get_info) | |
414 gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL); | |
415 | |
416 gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL); | |
417 gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL); | |
418 gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); | |
419 | |
5290 | 420 if (prpl && prpl_info->buddy_menu) { |
5228 | 421 list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); |
422 while (list) { | |
423 struct proto_buddy_menu *pbm = list->data; | |
424 menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); | |
425 g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); | |
426 g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node); | |
427 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); | |
428 list = list->next; | |
429 } | |
430 } | |
431 | |
432 gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name); | |
433 | |
434 gaim_separator(menu); | |
435 gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); | |
436 gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); | |
437 } | |
438 | |
439 gtk_widget_show_all(menu); | |
440 | |
441 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); | |
442 | |
443 #if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal | |
444 * the event propoagates down and somehow gets interpreted as the start of a drag event. */ | |
445 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); | |
446 gtk_tree_selection_select_path(sel, path); | |
447 gtk_tree_path_free(path); | |
448 return TRUE; | |
449 #endif | |
450 } | |
451 | |
452 static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) | |
453 { | |
454 if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) | |
455 blist_options &= ~OPT_BLIST_NO_MT_GRP; | |
456 else | |
457 blist_options |= OPT_BLIST_NO_MT_GRP; | |
458 save_prefs(); | |
459 gaim_gtk_blist_refresh(gaim_get_blist()); | |
460 } | |
461 | |
462 static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, | |
463 GtkWidget *checkitem) { | |
464 if(gtkblist->window->window) { | |
465 GdkCursor *cursor = gdk_cursor_new(GDK_WATCH); | |
466 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
467 while (gtk_events_pending()) | |
468 gtk_main_iteration(); | |
469 gdk_cursor_unref(cursor); | |
470 } | |
471 | |
472 if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))) | |
473 blist_options |= OPT_BLIST_SHOW_OFFLINE; | |
474 else | |
475 blist_options &= ~OPT_BLIST_SHOW_OFFLINE; | |
476 save_prefs(); | |
477 | |
478 if(gtkblist->window->window) { | |
479 GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); | |
480 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
481 gdk_cursor_unref(cursor); | |
482 } | |
483 | |
484 gaim_gtk_blist_refresh(gaim_get_blist()); | |
485 } | |
486 | |
487 static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, | |
488 GdkDragContext *dc, | |
489 GtkSelectionData *data, | |
490 guint info, | |
491 guint time, | |
492 gpointer *null) | |
493 { | |
494 if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { | |
495 GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); | |
496 GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); | |
497 GtkTreeIter iter; | |
498 GaimBlistNode *node = NULL; | |
499 GValue val = {0}; | |
5273 | 500 if(!sourcerow) |
501 return; | |
5228 | 502 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); |
503 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
504 node = g_value_get_pointer(&val); | |
505 gtk_selection_data_set (data, | |
506 gdk_atom_intern ("GAIM_BLIST_NODE", FALSE), | |
507 8, /* bits */ | |
508 (void*)&node, | |
509 sizeof (node)); | |
5273 | 510 |
5228 | 511 gtk_tree_path_free(sourcerow); |
512 } | |
5273 | 513 |
5228 | 514 } |
515 | |
516 static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, | |
517 GtkSelectionData *sd, guint info, guint t) | |
518 { | |
519 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { | |
520 GaimBlistNode *n = NULL; | |
521 GtkTreePath *path = NULL; | |
522 GtkTreeViewDropPosition position; | |
523 memcpy(&n, sd->data, sizeof(n)); | |
524 if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { | |
525 /* if we're here, I think it means the drop is ok */ | |
526 GtkTreeIter iter; | |
527 GaimBlistNode *node; | |
528 GValue val = {0}; | |
529 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
530 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
531 node = g_value_get_pointer(&val); | |
532 | |
533 if (GAIM_BLIST_NODE_IS_BUDDY(n)) { | |
534 struct buddy *b = (struct buddy*)n; | |
5234 | 535 if (GAIM_BLIST_NODE_IS_BUDDY(node) || |
536 GAIM_BLIST_NODE_IS_CHAT(node)) { | |
5228 | 537 switch(position) { |
538 case GTK_TREE_VIEW_DROP_AFTER: | |
539 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
540 gaim_blist_add_buddy(b, (struct group*)node->parent, node); | |
541 break; | |
542 case GTK_TREE_VIEW_DROP_BEFORE: | |
543 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
544 gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev); | |
545 break; | |
546 } | |
547 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
548 gaim_blist_add_buddy(b, (struct group*)node, NULL); | |
549 } | |
5234 | 550 } else if (GAIM_BLIST_NODE_IS_CHAT(n)) { |
551 struct chat *chat = (struct chat*)n; | |
552 if (GAIM_BLIST_NODE_IS_BUDDY(node) || | |
553 GAIM_BLIST_NODE_IS_CHAT(node)) { | |
554 switch(position) { | |
555 case GTK_TREE_VIEW_DROP_AFTER: | |
556 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
557 gaim_blist_add_chat(chat, (struct group*)node->parent, node); | |
558 break; | |
559 case GTK_TREE_VIEW_DROP_BEFORE: | |
560 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
561 gaim_blist_add_chat(chat, (struct group*)node->parent, node->prev); | |
562 break; | |
563 } | |
564 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
565 gaim_blist_add_chat(chat, (struct group*)node, NULL); | |
566 } | |
5228 | 567 } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { |
568 struct group *g = (struct group*)n; | |
569 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
570 switch (position) { | |
571 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
572 case GTK_TREE_VIEW_DROP_AFTER: | |
573 gaim_blist_add_group(g, node); | |
574 break; | |
575 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
576 case GTK_TREE_VIEW_DROP_BEFORE: | |
577 gaim_blist_add_group(g, node->prev); | |
578 break; | |
579 } | |
580 | |
5234 | 581 } else if(GAIM_BLIST_NODE_IS_BUDDY(node) || |
582 GAIM_BLIST_NODE_IS_CHAT(node)) { | |
5228 | 583 gaim_blist_add_group(g, node->parent); |
584 } | |
585 | |
586 } | |
587 | |
588 gtk_tree_path_free(path); | |
589 gaim_blist_save(); | |
590 } | |
591 } | |
592 } | |
593 | |
5234 | 594 static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, GaimBlistNode *node) |
5228 | 595 { |
596 GtkStyle *style; | |
5234 | 597 GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(node, GAIM_STATUS_ICON_LARGE); |
5228 | 598 PangoLayout *layout; |
5234 | 599 char *tooltiptext = gaim_get_tooltip_text(node); |
5228 | 600 |
601 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); | |
602 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
603 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
604 pango_layout_set_width(layout, 300000); | |
605 style = gtkblist->tipwindow->style; | |
606 | |
607 gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, | |
608 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); | |
609 | |
610 #if GTK_CHECK_VERSION(2,2,0) | |
611 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, | |
612 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); | |
613 #else | |
614 gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); | |
615 #endif | |
616 | |
617 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, | |
618 NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); | |
619 | |
620 g_object_unref (pixbuf); | |
621 g_object_unref (layout); | |
622 g_free(tooltiptext); | |
623 return; | |
624 } | |
625 | |
626 static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) | |
627 { | |
628 GtkTreePath *path; | |
629 GtkTreeIter iter; | |
630 GaimBlistNode *node; | |
631 GValue val = {0}; | |
5234 | 632 int scr_w,scr_h, w, h, x, y; |
633 PangoLayout *layout; | |
634 char *tooltiptext = NULL; | |
5228 | 635 |
636 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL)) | |
637 return FALSE; | |
638 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
639 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
640 node = g_value_get_pointer(&val); | |
5234 | 641 if(!GAIM_BLIST_NODE_IS_BUDDY(node) && !GAIM_BLIST_NODE_IS_CHAT(node)) |
642 return FALSE; | |
5228 | 643 |
5234 | 644 tooltiptext = gaim_get_tooltip_text(node); |
645 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); | |
646 gtkblist->tipwindow->parent = tv; | |
647 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); | |
648 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); | |
649 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); | |
650 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", | |
651 G_CALLBACK(gaim_gtk_blist_paint_tip), node); | |
652 gtk_widget_ensure_style (gtkblist->tipwindow); | |
653 | |
654 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); | |
655 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
656 pango_layout_set_width(layout, 300000); | |
657 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
658 scr_w = gdk_screen_width(); | |
659 scr_h = gdk_screen_height(); | |
660 pango_layout_get_size (layout, &w, &h); | |
661 w = PANGO_PIXELS(w) + 8; | |
662 h = PANGO_PIXELS(h) + 8; | |
5228 | 663 |
5234 | 664 /* 38 is the size of a large status icon plus 4 pixels padding on each side. |
665 * I should #define this or something */ | |
666 w = w + 38; | |
667 h = MAX(h, 38); | |
668 | |
669 gdk_window_get_pointer(NULL, &x, &y, NULL); | |
670 if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) | |
671 y+=gtkblist->window->allocation.y; | |
672 | |
673 x -= ((w >> 1) + 4); | |
5228 | 674 |
5234 | 675 if ((x + w) > scr_w) |
676 x -= (x + w) - scr_w; | |
677 else if (x < 0) | |
678 x = 0; | |
5228 | 679 |
5234 | 680 if ((y + h + 4) > scr_h) |
681 y = y - h; | |
682 else | |
683 y = y + 6; | |
684 g_object_unref (layout); | |
685 g_free(tooltiptext); | |
686 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); | |
687 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); | |
688 gtk_widget_show(gtkblist->tipwindow); | |
5228 | 689 |
690 gtk_tree_path_free(path); | |
691 return FALSE; | |
692 } | |
693 | |
694 static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) | |
695 { | |
696 GtkTreePath *path; | |
697 if (gtkblist->timeout) { | |
698 if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y)) | |
699 return FALSE; | |
700 /* We've left the cell. Remove the timeout and create a new one below */ | |
701 if (gtkblist->tipwindow) { | |
702 gtk_widget_destroy(gtkblist->tipwindow); | |
703 gtkblist->tipwindow = NULL; | |
704 } | |
705 | |
706 g_source_remove(gtkblist->timeout); | |
707 } | |
708 | |
709 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); | |
710 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->rect); | |
711 if (path) | |
712 gtk_tree_path_free(path); | |
713 gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); | |
714 return FALSE; | |
715 } | |
716 | |
717 static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) | |
718 { | |
719 if (gtkblist->timeout) { | |
720 g_source_remove(gtkblist->timeout); | |
721 gtkblist->timeout = 0; | |
722 } | |
723 if (gtkblist->tipwindow) { | |
724 gtk_widget_destroy(gtkblist->tipwindow); | |
725 gtkblist->tipwindow = NULL; | |
726 } | |
727 } | |
728 | |
729 static void | |
730 toggle_debug(void) | |
731 { | |
732 misc_options ^= OPT_MISC_DEBUG; | |
733 | |
734 if ((misc_options & OPT_MISC_DEBUG)) | |
735 gaim_gtk_debug_window_show(); | |
736 else | |
737 gaim_gtk_debug_window_hide(); | |
738 | |
739 save_prefs(); | |
740 } | |
741 | |
742 | |
743 /*************************************************** | |
744 * Crap * | |
745 ***************************************************/ | |
746 static GtkItemFactoryEntry blist_menu[] = | |
747 { | |
748 /* Buddies menu */ | |
749 { N_("/_Buddies"), NULL, NULL, 0, "<Branch>" }, | |
750 { N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0, "<StockItem>", GAIM_STOCK_IM }, | |
751 { N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, "<StockItem>", GAIM_STOCK_CHAT }, | |
752 { N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0, "<StockItem>", GAIM_STOCK_INFO }, | |
753 { "/Buddies/sep1", NULL, NULL, 0, "<Separator>" }, | |
5398 | 754 { N_("/Buddies/Show _Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, "<CheckItem>"}, |
5228 | 755 { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"}, |
5307 | 756 { N_("/Buddies/_Add a Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, |
757 { N_("/Buddies/Add a C_hat..."), NULL, gaim_gtk_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD }, | |
5228 | 758 { N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL}, |
759 { "/Buddies/sep2", NULL, NULL, 0, "<Separator>" }, | |
760 { N_("/Buddies/_Signoff"), "<CTL>D", signoff_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF }, | |
761 { N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, | |
762 | |
763 /* Tools */ | |
764 { N_("/_Tools"), NULL, NULL, 0, "<Branch>" }, | |
765 { N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" }, | |
766 { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" }, | |
767 { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" }, | |
768 { "/Tools/sep1", NULL, NULL, 0, "<Separator>" }, | |
769 { N_("/Tools/A_ccounts..."), "<CTL>A", account_editor, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, | |
770 { N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, | |
771 { N_("/Tools/Preferences..."), "<CTL>P", show_prefs, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, | |
772 { N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "<StockItem>", GAIM_STOCK_PRIVACY }, | |
773 { "/Tools/sep2", NULL, NULL, 0, "<Separator>" }, | |
774 { N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, | |
775 | |
776 /* Help */ | |
777 { N_("/_Help"), NULL, NULL, 0, "<Branch>" }, | |
778 { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, | |
779 { N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL }, | |
5307 | 780 { N_("/Help/_About..."), "<CTL>F1", show_about, 0, "<StockItem>", GAIM_STOCK_ABOUT }, |
5228 | 781 }; |
782 | |
783 /********************************************************* | |
784 * Private Utility functions * | |
785 *********************************************************/ | |
786 | |
5234 | 787 static char *gaim_get_tooltip_text(GaimBlistNode *node) |
5228 | 788 { |
5237 | 789 GaimPlugin *prpl; |
790 GaimPluginProtocolInfo *prpl_info = NULL; | |
5228 | 791 char *text = NULL; |
792 | |
5234 | 793 if(GAIM_BLIST_NODE_IS_CHAT(node)) { |
794 struct chat *chat = (struct chat *)node; | |
5237 | 795 char *name = NULL; |
5274 | 796 struct proto_chat_entry *pce; |
797 GList *parts, *tmp; | |
798 GString *parts_text = g_string_new(""); | |
799 | |
800 prpl = gaim_find_prpl(chat->account->protocol); | |
801 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
802 | |
803 parts = prpl_info->chat_info(chat->account->gc); | |
5237 | 804 |
805 if(chat->alias) { | |
806 name = g_markup_escape_text(chat->alias, -1); | |
807 } else { | |
808 pce = parts->data; | |
809 name = g_markup_escape_text(g_hash_table_lookup(chat->components, | |
810 pce->identifier), -1); | |
5274 | 811 } |
812 if(g_slist_length(connections) > 1) { | |
813 char *account = g_markup_escape_text(chat->account->username, -1); | |
814 g_string_append_printf(parts_text, _("\n<b>Account:</b> %s"), | |
815 account); | |
816 g_free(account); | |
5237 | 817 } |
5274 | 818 for(tmp = parts; tmp; tmp = tmp->next) { |
819 char *label, *value; | |
820 pce = tmp->data; | |
5237 | 821 |
5274 | 822 label = g_markup_escape_text(pce->label, -1); |
823 | |
824 value = g_markup_escape_text(g_hash_table_lookup(chat->components, | |
825 pce->identifier), -1); | |
826 | |
827 g_string_append_printf(parts_text, "\n<b>%s</b> %s", label, value); | |
828 g_free(label); | |
829 g_free(value); | |
830 g_free(pce); | |
831 } | |
832 g_list_free(parts); | |
833 | |
834 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>%s", | |
835 name, parts_text->str); | |
836 g_string_free(parts_text, TRUE); | |
5237 | 837 g_free(name); |
5234 | 838 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { |
839 struct buddy *b = (struct buddy *)node; | |
840 char *statustext = NULL; | |
841 char *aliastext = NULL, *nicktext = NULL; | |
842 char *warning = NULL, *idletime = NULL; | |
5274 | 843 char *accounttext = NULL; |
5228 | 844 |
5234 | 845 prpl = gaim_find_prpl(b->account->protocol); |
846 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
847 | |
848 if (prpl_info->tooltip_text) { | |
849 const char *end; | |
850 statustext = prpl_info->tooltip_text(b); | |
5228 | 851 |
5234 | 852 if(statustext && !g_utf8_validate(statustext, -1, &end)) { |
853 char *new = g_strndup(statustext, | |
854 g_utf8_pointer_to_offset(statustext, end)); | |
855 g_free(statustext); | |
856 statustext = new; | |
857 } | |
858 } | |
859 | |
860 if (!statustext && !GAIM_BUDDY_IS_ONLINE(b)) | |
861 statustext = g_strdup(_("<b>Status:</b> Offline")); | |
5228 | 862 |
5341 | 863 if (b->idle > 0) |
864 idletime = sec_to_text(time(NULL) - b->idle); | |
5228 | 865 |
5234 | 866 if(b->alias && b->alias[0]) |
867 aliastext = g_markup_escape_text(b->alias, -1); | |
5228 | 868 |
5234 | 869 if(b->server_alias) |
870 nicktext = g_markup_escape_text(b->server_alias, -1); | |
5228 | 871 |
5234 | 872 if (b->evil > 0) |
873 warning = g_strdup_printf(_("%d%%"), b->evil); | |
5228 | 874 |
5274 | 875 if(g_slist_length(connections) > 1) |
876 accounttext = g_markup_escape_text(b->account->username, -1); | |
877 | |
5234 | 878 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>" |
5438 | 879 "%s %s" /* Account */ |
880 "%s %s" /* Alias */ | |
881 "%s %s" /* Nickname */ | |
882 "%s %s" /* Idle */ | |
883 "%s %s" /* Warning */ | |
884 "%s%s" /* Status */ | |
885 "%s", | |
886 b->name, | |
887 accounttext ? _("\n<b>Account:</b>") : "", accounttext ? accounttext : "", | |
888 aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "", | |
889 nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "", | |
890 idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "", | |
891 b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "", | |
892 statustext ? "\n" : "", statustext ? statustext : "", | |
893 !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : | |
894 !g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") : ""); | |
5234 | 895 |
896 if(warning) | |
897 g_free(warning); | |
898 if(idletime) | |
899 g_free(idletime); | |
900 if(statustext) | |
901 g_free(statustext); | |
902 if(nicktext) | |
903 g_free(nicktext); | |
904 if(aliastext) | |
905 g_free(aliastext); | |
5274 | 906 if(accounttext) |
907 g_free(accounttext); | |
5234 | 908 } |
5228 | 909 |
910 return text; | |
911 } | |
912 | |
5234 | 913 GdkPixbuf *gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size) |
5228 | 914 { |
915 GdkPixbuf *status = NULL; | |
916 GdkPixbuf *scale = NULL; | |
917 GdkPixbuf *emblem = NULL; | |
918 gchar *filename = NULL; | |
919 const char *protoname = NULL; | |
920 | |
921 char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL; | |
922 | |
923 int scalesize = 30; | |
924 | |
5234 | 925 GaimPlugin *prpl = NULL; |
5228 | 926 GaimPluginProtocolInfo *prpl_info = NULL; |
927 | |
5234 | 928 if(GAIM_BLIST_NODE_IS_BUDDY(node)) |
929 prpl = gaim_find_prpl(((struct buddy *)node)->account->protocol); | |
930 else if(GAIM_BLIST_NODE_IS_CHAT(node)) | |
931 prpl = gaim_find_prpl(((struct chat *)node)->account->protocol); | |
5228 | 932 |
5234 | 933 if (!prpl) |
5228 | 934 return NULL; |
935 | |
936 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
937 | |
5234 | 938 if (prpl_info->list_icon) { |
939 if(GAIM_BLIST_NODE_IS_BUDDY(node)) | |
940 protoname = prpl_info->list_icon(((struct buddy*)node)->account, | |
941 (struct buddy *)node); | |
942 else if(GAIM_BLIST_NODE_IS_CHAT(node)) | |
943 protoname = prpl_info->list_icon(((struct chat*)node)->account, NULL); | |
944 } | |
945 | |
946 if (GAIM_BLIST_NODE_IS_BUDDY(node) && | |
947 ((struct buddy *)node)->present != GAIM_BUDDY_SIGNING_OFF && | |
948 prpl_info->list_emblems) { | |
949 prpl_info->list_emblems((struct buddy*)node, &se, &sw, &nw, &ne); | |
950 } | |
5228 | 951 |
952 if (size == GAIM_STATUS_ICON_SMALL) { | |
953 scalesize = 15; | |
954 sw = nw = ne = NULL; /* So that only the se icon will composite */ | |
955 } | |
956 | |
957 | |
5234 | 958 if (GAIM_BLIST_NODE_IS_BUDDY(node) && |
959 ((struct buddy*)node)->present == GAIM_BUDDY_SIGNING_ON) { | |
5228 | 960 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); |
961 status = gdk_pixbuf_new_from_file(filename,NULL); | |
962 g_free(filename); | |
5234 | 963 } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && |
964 ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { | |
5228 | 965 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); |
966 status = gdk_pixbuf_new_from_file(filename,NULL); | |
967 g_free(filename); | |
968 | |
969 /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and | |
970 then it will look up protoname from the theme */ | |
971 } else { | |
972 char *image = g_strdup_printf("%s.png", protoname); | |
973 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
974 status = gdk_pixbuf_new_from_file(filename,NULL); | |
975 g_free(image); | |
976 g_free(filename); | |
977 | |
978 } | |
979 | |
980 if (!status) | |
981 return NULL; | |
982 | |
983 scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR); | |
984 | |
985 g_object_unref(G_OBJECT(status)); | |
986 | |
987 /* Emblems */ | |
988 | |
989 /* Each protocol can specify up to four "emblems" to composite over the base icon. "away", "busy", "mobile user" | |
990 * are all examples of states represented by emblems. I'm not even really sure I like this yet. */ | |
991 | |
992 /* XXX Clean this crap up, yo. */ | |
993 if (se) { | |
994 char *image = g_strdup_printf("%s.png", se); | |
995 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
996 g_free(image); | |
997 emblem = gdk_pixbuf_new_from_file(filename,NULL); | |
998 g_free(filename); | |
999 if (emblem) { | |
1000 if (size == GAIM_STATUS_ICON_LARGE) | |
1001 gdk_pixbuf_composite (emblem, | |
1002 scale, 15, 15, | |
1003 15, 15, | |
1004 15, 15, | |
1005 1, 1, | |
1006 GDK_INTERP_BILINEAR, | |
1007 255); | |
1008 else | |
1009 gdk_pixbuf_composite (emblem, | |
1010 scale, 5, 5, | |
1011 10, 10, | |
1012 5, 5, | |
1013 .6, .6, | |
1014 GDK_INTERP_BILINEAR, | |
1015 255); | |
1016 g_object_unref(G_OBJECT(emblem)); | |
1017 } | |
1018 } | |
1019 if (sw) { | |
1020 char *image = g_strdup_printf("%s.png", sw); | |
1021 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1022 g_free(image); | |
1023 emblem = gdk_pixbuf_new_from_file(filename,NULL); | |
1024 g_free(filename); | |
1025 if (emblem) { | |
1026 gdk_pixbuf_composite (emblem, | |
1027 scale, 0, 15, | |
1028 15, 15, | |
1029 0, 15, | |
1030 1, 1, | |
1031 GDK_INTERP_BILINEAR, | |
1032 255); | |
1033 g_object_unref(G_OBJECT(emblem)); | |
1034 } | |
1035 } | |
1036 if (nw) { | |
1037 char *image = g_strdup_printf("%s.png", nw); | |
1038 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1039 g_free(image); | |
1040 emblem = gdk_pixbuf_new_from_file(filename,NULL); | |
1041 g_free(filename); | |
1042 if (emblem) { | |
1043 gdk_pixbuf_composite (emblem, | |
1044 scale, 0, 0, | |
1045 15, 15, | |
1046 0, 0, | |
1047 1, 1, | |
1048 GDK_INTERP_BILINEAR, | |
1049 255); | |
1050 g_object_unref(G_OBJECT(emblem)); | |
1051 } | |
1052 } | |
1053 if (ne) { | |
1054 char *image = g_strdup_printf("%s.png", ne); | |
1055 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1056 g_free(image); | |
1057 emblem = gdk_pixbuf_new_from_file(filename,NULL); | |
1058 g_free(filename); | |
1059 if (emblem) { | |
1060 gdk_pixbuf_composite (emblem, | |
1061 scale, 15, 0, | |
1062 15, 15, | |
1063 15, 0, | |
1064 1, 1, | |
1065 GDK_INTERP_BILINEAR, | |
1066 255); | |
1067 g_object_unref(G_OBJECT(emblem)); | |
1068 } | |
1069 } | |
1070 | |
1071 | |
1072 /* Idle grey buddies affects the whole row. This converts the status icon to greyscale. */ | |
5234 | 1073 if (GAIM_BLIST_NODE_IS_BUDDY(node) && |
1074 ((struct buddy *)node)->present == GAIM_BUDDY_OFFLINE) | |
5228 | 1075 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); |
5234 | 1076 else if (GAIM_BLIST_NODE_IS_BUDDY(node) && |
1077 ((struct buddy *)node)->idle && | |
1078 blist_options & OPT_BLIST_GREY_IDLERS) | |
5228 | 1079 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); |
1080 return scale; | |
1081 } | |
1082 | |
1083 static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b) | |
1084 { | |
1085 /* This just opens a file from ~/.gaim/icons/screenname. This needs to change to be more gooder. */ | |
1086 char *file; | |
1087 GdkPixbuf *buf, *ret; | |
1088 | |
1089 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) | |
1090 return NULL; | |
1091 | |
1092 if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL) | |
1093 return NULL; | |
1094 | |
1095 buf = gdk_pixbuf_new_from_file(file, NULL); | |
1096 g_free(file); | |
1097 | |
1098 | |
1099 if (buf) { | |
1100 if (!GAIM_BUDDY_IS_ONLINE(b)) | |
1101 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); | |
1102 if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) | |
1103 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); | |
1104 | |
1105 ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); | |
1106 g_object_unref(G_OBJECT(buf)); | |
1107 return ret; | |
1108 } | |
1109 return NULL; | |
1110 } | |
1111 | |
1112 static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected) | |
1113 { | |
1114 char *name = gaim_get_buddy_alias(b); | |
1115 char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL; | |
1116 GaimPlugin *prpl; | |
1117 GaimPluginProtocolInfo *prpl_info = NULL; | |
1118 /* XXX Clean up this crap */ | |
1119 | |
1120 int ihrs, imin; | |
1121 char *idletime = NULL, *warning = NULL, *statustext = NULL; | |
1122 time_t t; | |
1123 | |
1124 prpl = gaim_find_prpl(b->account->protocol); | |
1125 | |
1126 if (prpl != NULL) | |
1127 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
1128 | |
1129 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { | |
1130 if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) { | |
1131 text = g_strdup_printf("<span color='dim grey'>%s</span>", | |
1132 esc); | |
1133 g_free(esc); | |
1134 return text; | |
1135 } else { | |
1136 return esc; | |
1137 } | |
1138 } | |
1139 | |
1140 time(&t); | |
1141 ihrs = (t - b->idle) / 3600; | |
1142 imin = ((t - b->idle) / 60) % 60; | |
1143 | |
1144 if (prpl && prpl_info->status_text) { | |
1145 char *tmp = prpl_info->status_text(b); | |
1146 const char *end; | |
1147 | |
1148 if(tmp && !g_utf8_validate(tmp, -1, &end)) { | |
1149 char *new = g_strndup(tmp, | |
1150 g_utf8_pointer_to_offset(tmp, end)); | |
1151 g_free(tmp); | |
1152 tmp = new; | |
1153 } | |
1154 | |
1155 if(tmp) { | |
1156 char buf[32]; | |
1157 char *c = tmp; | |
1158 int length = 0, vis=0; | |
1159 gboolean inside = FALSE; | |
1160 g_strdelimit(tmp, "\n", ' '); | |
1161 | |
1162 while(*c && vis < 20) { | |
1163 if(*c == '&') | |
1164 inside = TRUE; | |
1165 else if(*c == ';') | |
1166 inside = FALSE; | |
1167 if(!inside) | |
1168 vis++; | |
1169 length++; | |
1170 c++; /* this is fun */ | |
1171 } | |
1172 | |
1173 if(vis == 20) | |
1174 g_snprintf(buf, sizeof(buf), "%%.%ds...", length); | |
1175 else | |
1176 g_snprintf(buf, sizeof(buf), "%%s "); | |
1177 | |
1178 statustext = g_strdup_printf(buf, tmp); | |
1179 | |
1180 g_free(tmp); | |
1181 } | |
1182 } | |
1183 | |
1184 if (b->idle > 0) { | |
1185 if (ihrs) | |
1186 idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); | |
1187 else | |
1188 idletime = g_strdup_printf(_("Idle (%dm) "), imin); | |
1189 } | |
1190 | |
1191 if (b->evil > 0) | |
1192 warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); | |
1193 | |
1194 if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) | |
1195 statustext = g_strdup("Offline "); | |
1196 | |
1197 if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) { | |
1198 text = g_strdup_printf("<span color='dim grey'>%s</span>\n" | |
1199 "<span color='dim grey' size='smaller'>%s%s%s</span>", | |
1200 esc, | |
1201 statustext != NULL ? statustext : "", | |
1202 idletime != NULL ? idletime : "", | |
1203 warning != NULL ? warning : ""); | |
1204 } else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) { | |
1205 text = g_strdup(esc); | |
1206 } else { | |
1207 text = g_strdup_printf("%s\n" | |
1208 "<span %s size='smaller'>%s%s%s</span>", esc, | |
1209 selected ? "" : "color='dim grey'", | |
1210 statustext != NULL ? statustext : "", | |
1211 idletime != NULL ? idletime : "", | |
1212 warning != NULL ? warning : ""); | |
1213 } | |
1214 if (idletime) | |
1215 g_free(idletime); | |
1216 if (warning) | |
1217 g_free(warning); | |
1218 if (statustext) | |
1219 g_free(statustext); | |
1220 if (esc) | |
1221 g_free(esc); | |
1222 | |
1223 return text; | |
1224 } | |
1225 | |
1226 static void gaim_gtk_blist_restore_position() | |
1227 { | |
1228 /* if the window exists, is hidden, we're saving positions, and the position is sane... */ | |
1229 if(gtkblist && gtkblist->window && | |
1230 !GTK_WIDGET_VISIBLE(gtkblist->window) && | |
1231 blist_pos.width != 0) { | |
1232 /* ...check position is on screen... */ | |
1233 if (blist_pos.x >= gdk_screen_width()) | |
1234 blist_pos.x = gdk_screen_width() - 100; | |
1235 else if (blist_pos.x < 0) | |
1236 blist_pos.x = 100; | |
1237 | |
1238 if (blist_pos.y >= gdk_screen_height()) | |
1239 blist_pos.y = gdk_screen_height() - 100; | |
1240 else if (blist_pos.y < 0) | |
1241 blist_pos.y = 100; | |
1242 | |
1243 /* ...and move it back. */ | |
1244 gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y); | |
1245 gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height); | |
1246 } | |
1247 } | |
1248 | |
1249 static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list) | |
1250 { | |
5234 | 1251 GaimBlistNode *group, *buddy; |
5228 | 1252 |
5234 | 1253 for(group = list->root; group; group = group->next) { |
1254 if(!GAIM_BLIST_NODE_IS_GROUP(group)) | |
1255 continue; | |
1256 for(buddy = group->child; buddy; buddy = buddy->next) { | |
1257 if(!GAIM_BLIST_NODE_IS_BUDDY(buddy)) | |
1258 continue; | |
5228 | 1259 if (((struct buddy *)buddy)->idle) |
1260 gaim_gtk_blist_update(list, buddy); | |
1261 } | |
1262 } | |
1263 | |
1264 /* keep on going */ | |
1265 return TRUE; | |
1266 } | |
1267 | |
5260 | 1268 static void gaim_gtk_blist_hide_node(struct gaim_buddy_list *list, GaimBlistNode *node) |
1269 { | |
1270 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
1271 GtkTreeIter iter; | |
1272 | |
1273 if (!gtknode || !gtknode->row || !gtkblist) | |
1274 return; | |
1275 | |
1276 if(gtkblist->selected_node == node) | |
1277 gtkblist->selected_node = NULL; | |
1278 | |
1279 if (get_iter_from_node(node, &iter)) { | |
1280 gtk_tree_store_remove(gtkblist->treemodel, &iter); | |
1281 if(GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
1282 gaim_gtk_blist_update(list, node->parent); | |
1283 } | |
1284 } | |
1285 gtk_tree_row_reference_free(gtknode->row); | |
1286 gtknode->row = NULL; | |
1287 } | |
1288 | |
1289 | |
5228 | 1290 /********************************************************************************** |
1291 * Public API Functions * | |
1292 **********************************************************************************/ | |
1293 static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist) | |
1294 { | |
1295 blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1); | |
1296 } | |
1297 | |
5256 | 1298 static void gaim_gtk_blist_new_node(GaimBlistNode *node) |
1299 { | |
1300 node->ui_data = g_new0(struct _gaim_gtk_blist_node, 1); | |
1301 } | |
1302 | |
5228 | 1303 void gaim_gtk_blist_update_columns() |
1304 { | |
1305 if(!gtkblist) | |
1306 return; | |
1307 | |
1308 if (blist_options & OPT_BLIST_SHOW_ICONS) { | |
1309 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); | |
1310 gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); | |
1311 gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); | |
1312 } else { | |
1313 gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME); | |
1314 gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN); | |
1315 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); | |
1316 } | |
1317 } | |
1318 | |
1319 enum {DRAG_BUDDY, DRAG_ROW}; | |
1320 | |
1321 static char * | |
1322 item_factory_translate_func (const char *path, gpointer func_data) | |
1323 { | |
1324 return _(path); | |
1325 } | |
1326 | |
5422 | 1327 void gaim_gtk_blist_setup_sort_methods() |
1328 { | |
5438 | 1329 gaim_gtk_blist_sort_method_reg(_("None"), sort_method_none); |
1330 gaim_gtk_blist_sort_method_reg(_("Alphabetical"), sort_method_alphabetical); | |
1331 gaim_gtk_blist_sort_method_reg(_("By status"), sort_method_status); | |
1332 gaim_gtk_blist_sort_method_reg(_("By log size"), sort_method_log); | |
1333 gaim_gtk_blist_sort_method_set(sort_method[0] ? sort_method : _("None")); | |
5422 | 1334 } |
1335 | |
1336 | |
5228 | 1337 static void gaim_gtk_blist_show(struct gaim_buddy_list *list) |
1338 { | |
1339 GtkCellRenderer *rend; | |
1340 GtkTreeViewColumn *column; | |
1341 GtkWidget *sw; | |
1342 GtkWidget *button; | |
1343 GtkSizeGroup *sg; | |
1344 GtkAccelGroup *accel_group; | |
1345 GtkTreeSelection *selection; | |
1346 GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, | |
1347 {"application/x-im-contact", 0, DRAG_BUDDY}}; | |
1348 | |
1349 if (gtkblist && gtkblist->window) { | |
1350 gtk_widget_show(gtkblist->window); | |
1351 return; | |
1352 } | |
1353 | |
1354 gtkblist = GAIM_GTK_BLIST(list); | |
1355 | |
1356 gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
1357 gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); | |
1358 gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); | |
1359 | |
1360 GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; | |
1361 | |
1362 gtkblist->vbox = gtk_vbox_new(FALSE, 0); | |
1363 gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox); | |
1364 | |
1365 g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); | |
1366 g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); | |
1367 g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); | |
1368 gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); | |
1369 | |
1370 /******************************* Menu bar *************************************/ | |
1371 accel_group = gtk_accel_group_new(); | |
1372 gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); | |
1373 g_object_unref(accel_group); | |
5427 | 1374 gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", accel_group); |
1375 gtk_item_factory_set_translate_func (gtkblist->ift, | |
5228 | 1376 item_factory_translate_func, |
1377 NULL, NULL); | |
5427 | 1378 gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), |
5228 | 1379 blist_menu, NULL); |
5427 | 1380 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>"), FALSE, FALSE, 0); |
5228 | 1381 |
5427 | 1382 awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away")); |
5228 | 1383 do_away_menu(); |
1384 | |
5427 | 1385 gtkblist->bpmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounce")); |
5228 | 1386 gaim_gtkpounce_menu_build(gtkblist->bpmenu); |
1387 | |
5427 | 1388 protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Protocol Actions")); |
5228 | 1389 do_proto_menu(); |
1390 | |
1391 /****************************** GtkTreeView **********************************/ | |
1392 sw = gtk_scrolled_window_new(NULL,NULL); | |
1393 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); | |
1394 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
1395 | |
1396 gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, | |
1397 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); | |
1398 | |
1399 gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); | |
1400 gtk_widget_set_size_request(gtkblist->treeview, -1, 200); | |
1401 | |
1402 /* Set up selection stuff */ | |
1403 | |
1404 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
1405 g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL); | |
1406 | |
1407 | |
1408 /* Set up dnd */ | |
1409 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte, | |
1410 2, GDK_ACTION_COPY); | |
1411 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, | |
1412 GDK_ACTION_COPY | GDK_ACTION_MOVE); | |
1413 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); | |
1414 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); | |
1415 | |
1416 /* Tooltips */ | |
1417 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); | |
1418 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); | |
1419 | |
1420 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); | |
1421 | |
1422 column = gtk_tree_view_column_new (); | |
1423 | |
1424 rend = gtk_cell_renderer_pixbuf_new(); | |
1425 gtk_tree_view_column_pack_start (column, rend, FALSE); | |
1426 gtk_tree_view_column_set_attributes (column, rend, | |
1427 "pixbuf", STATUS_ICON_COLUMN, | |
1428 "visible", STATUS_ICON_VISIBLE_COLUMN, | |
1429 NULL); | |
1430 g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL); | |
1431 | |
1432 rend = gtk_cell_renderer_text_new(); | |
1433 gtk_tree_view_column_pack_start (column, rend, TRUE); | |
1434 gtk_tree_view_column_set_attributes (column, rend, | |
1435 "markup", NAME_COLUMN, | |
1436 NULL); | |
1437 g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); | |
1438 | |
1439 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); | |
1440 | |
1441 rend = gtk_cell_renderer_text_new(); | |
1442 gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL); | |
1443 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column); | |
1444 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
1445 | |
1446 rend = gtk_cell_renderer_text_new(); | |
1447 gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL); | |
1448 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column); | |
1449 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
1450 | |
1451 rend = gtk_cell_renderer_pixbuf_new(); | |
1452 gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL); | |
1453 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
1454 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column); | |
1455 | |
1456 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); | |
1457 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); | |
1458 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); | |
1459 g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); | |
1460 | |
5419 | 1461 /* Enable CTRL+F searching */ |
1462 gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); | |
1463 | |
5228 | 1464 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); |
1465 gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); | |
1466 gaim_gtk_blist_update_columns(); | |
1467 | |
1468 /* set the Show Offline Buddies option. must be done | |
1469 * after the treeview or faceprint gets mad. -Robot101 | |
1470 */ | |
5427 | 1471 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))), |
5228 | 1472 blist_options & OPT_BLIST_SHOW_OFFLINE); |
5427 | 1473 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))), |
5228 | 1474 !(blist_options & OPT_BLIST_NO_MT_GRP)); |
1475 | |
1476 /* OK... let's show this bad boy. */ | |
1477 gaim_gtk_blist_refresh(list); | |
1478 gaim_gtk_blist_restore_position(); | |
1479 gtk_widget_show_all(gtkblist->window); | |
1480 | |
1481 /**************************** Button Box **************************************/ | |
1482 /* add this afterwards so it doesn't force up the width of the window */ | |
1483 | |
1484 gtkblist->tooltips = gtk_tooltips_new(); | |
1485 | |
1486 sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); | |
1487 gtkblist->bbox = gtk_hbox_new(TRUE, 0); | |
1488 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0); | |
1489 gtk_widget_show(gtkblist->bbox); | |
1490 | |
1491 button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL); | |
1492 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
1493 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
1494 gtk_size_group_add_widget(sg, button); | |
1495 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), | |
1496 gtkblist->treeview); | |
1497 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL); | |
1498 gtk_widget_show(button); | |
1499 | |
1500 button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL); | |
1501 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
1502 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
1503 gtk_size_group_add_widget(sg, button); | |
1504 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), | |
1505 gtkblist->treeview); | |
1506 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL); | |
1507 gtk_widget_show(button); | |
1508 | |
1509 button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL); | |
1510 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
1511 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
1512 gtk_size_group_add_widget(sg, button); | |
5234 | 1513 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), gtkblist->treeview); |
5228 | 1514 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL); |
1515 gtk_widget_show(button); | |
1516 | |
1517 button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL); | |
1518 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
1519 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
1520 gtk_size_group_add_widget(sg, button); | |
1521 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL); | |
1522 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL); | |
1523 gtk_widget_show(button); | |
1524 | |
1525 /* this will show the right image/label widgets for us */ | |
1526 gaim_gtk_blist_update_toolbar(); | |
1527 | |
1528 /* start the refresh timer */ | |
5297 | 1529 if (blist_options & (OPT_BLIST_SHOW_IDLETIME | OPT_BLIST_SHOW_ICONS)) |
1530 gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list); | |
5228 | 1531 } |
1532 | |
5422 | 1533 static void redo_buddy_list(struct gaim_buddy_list *list, gboolean remove) |
5228 | 1534 { |
5234 | 1535 GaimBlistNode *group, *buddy; |
5422 | 1536 |
5234 | 1537 for(group = list->root; group; group = group->next) { |
1538 if(!GAIM_BLIST_NODE_IS_GROUP(group)) | |
1539 continue; | |
5228 | 1540 gaim_gtk_blist_update(list, group); |
5234 | 1541 for(buddy = group->child; buddy; buddy = buddy->next) { |
5422 | 1542 if (remove) |
1543 gaim_gtk_blist_hide_node(list, buddy); | |
5228 | 1544 gaim_gtk_blist_update(list, buddy); |
1545 } | |
1546 } | |
1547 } | |
1548 | |
5422 | 1549 void gaim_gtk_blist_refresh(struct gaim_buddy_list *list) |
1550 { | |
1551 redo_buddy_list(list, FALSE); | |
1552 } | |
1553 | |
5297 | 1554 void |
1555 gaim_gtk_blist_update_refresh_timeout() | |
1556 { | |
1557 struct gaim_buddy_list *blist; | |
1558 struct gaim_gtk_buddy_list *gtkblist; | |
1559 | |
1560 blist = gaim_get_blist(); | |
1561 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
1562 | |
1563 if (blist_options & (OPT_BLIST_SHOW_IDLETIME | OPT_BLIST_SHOW_ICONS)) { | |
1564 gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, blist); | |
1565 } else { | |
1566 g_source_remove(gtkblist->refresh_timer); | |
1567 gtkblist->refresh_timer = 0; | |
1568 } | |
1569 } | |
1570 | |
5256 | 1571 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) { |
1572 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
1573 GtkTreePath *path; | |
5228 | 1574 |
5263 | 1575 if (!gtknode) { |
1576 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "buddy %s has no ui_data\n", ((struct buddy *)node)->name); | |
1577 return FALSE; | |
1578 } | |
1579 | |
1580 if (!gtkblist) { | |
1581 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n"); | |
1582 return FALSE; | |
1583 } | |
1584 | |
1585 if (!gtknode->row) | |
5228 | 1586 return FALSE; |
1587 | |
5256 | 1588 if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL) |
5228 | 1589 return FALSE; |
5256 | 1590 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) { |
1591 gtk_tree_path_free(path); | |
1592 return FALSE; | |
1593 } | |
1594 gtk_tree_path_free(path); | |
1595 return TRUE; | |
5228 | 1596 } |
1597 | |
1598 /* | |
1599 * These state assignments suck. I'm sorry. They're for historical reasons. | |
1600 * Roll on new prefs. -Robot101 | |
1601 * | |
1602 * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image | |
1603 * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text | |
1604 * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images | |
1605 * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none | |
1606 */ | |
1607 | |
1608 static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) { | |
1609 if (GTK_IS_IMAGE(widget)) { | |
1610 if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM) | |
1611 gtk_widget_show(widget); | |
1612 else | |
1613 gtk_widget_hide(widget); | |
1614 } else if (GTK_IS_LABEL(widget)) { | |
1615 if (blist_options & OPT_BLIST_NO_BUTTON_TEXT) | |
1616 gtk_widget_hide(widget); | |
1617 else | |
1618 gtk_widget_show(widget); | |
1619 } else if (GTK_IS_CONTAINER(widget)) { | |
1620 gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL); | |
1621 } | |
1622 } | |
1623 | |
1624 void gaim_gtk_blist_update_toolbar() { | |
1625 if (!gtkblist) | |
1626 return; | |
1627 | |
1628 if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM)) | |
1629 gtk_widget_hide(gtkblist->bbox); | |
1630 else { | |
1631 gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL); | |
1632 gtk_widget_show(gtkblist->bbox); | |
1633 } | |
1634 } | |
1635 | |
1636 static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node) | |
1637 { | |
5260 | 1638 gaim_gtk_blist_hide_node(list, node); |
5228 | 1639 |
5263 | 1640 /* There's something I don't understand here */ |
1641 /* g_free(node->ui_data); | |
1642 node->ui_data = NULL; */ | |
5228 | 1643 } |
1644 | |
1645 static gboolean do_selection_changed(GaimBlistNode *new_selection) | |
1646 { | |
5254 | 1647 GaimBlistNode *old_selection = NULL; |
5228 | 1648 |
5254 | 1649 /* test for gtkblist because crazy timeout means we can be called after the blist is gone */ |
1650 if (gtkblist && new_selection != gtkblist->selected_node) { | |
1651 old_selection = gtkblist->selected_node; | |
5228 | 1652 gtkblist->selected_node = new_selection; |
1653 if(new_selection) | |
1654 gaim_gtk_blist_update(NULL, new_selection); | |
1655 if(old_selection) | |
1656 gaim_gtk_blist_update(NULL, old_selection); | |
1657 } | |
1658 | |
1659 return FALSE; | |
1660 } | |
1661 | |
1662 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data) | |
1663 { | |
1664 GaimBlistNode *new_selection = NULL; | |
1665 GtkTreeIter iter; | |
1666 | |
1667 if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ | |
1668 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
1669 NODE_COLUMN, &new_selection, -1); | |
1670 } | |
5254 | 1671 |
5228 | 1672 /* we set this up as a timeout, otherwise the blist flickers */ |
1673 g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); | |
1674 } | |
1675 | |
1676 static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) { | |
1677 GaimBlistNode *sibling; | |
1678 GtkTreeIter siblingiter; | |
5256 | 1679 GtkTreePath *newpath; |
5228 | 1680 struct group *group = (struct group *)node; |
5256 | 1681 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; |
5228 | 1682 char *esc = g_markup_escape_text(group->name, -1); |
1683 char *mark; | |
1684 | |
1685 if(blist_options & OPT_BLIST_SHOW_GRPNUM) | |
1686 mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); | |
1687 else | |
1688 mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); | |
1689 | |
1690 g_free(esc); | |
1691 | |
1692 sibling = node->prev; | |
1693 while (sibling && !get_iter_from_node(sibling, &siblingiter)) { | |
1694 sibling = sibling->prev; | |
1695 } | |
1696 | |
1697 gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL, | |
1698 sibling ? &siblingiter : NULL); | |
5256 | 1699 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), iter); |
1700 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
1701 gtk_tree_path_free(newpath); | |
1702 | |
5228 | 1703 gtk_tree_store_set(gtkblist->treemodel, iter, |
1704 STATUS_ICON_COLUMN, NULL, | |
1705 STATUS_ICON_VISIBLE_COLUMN, FALSE, | |
1706 NAME_COLUMN, mark, | |
1707 NODE_COLUMN, node, | |
1708 -1); | |
1709 g_free(mark); | |
1710 } | |
1711 | |
1712 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node) | |
1713 { | |
1714 GtkTreeIter iter; | |
5256 | 1715 GtkTreePath *expand = NULL, *newpath = NULL; |
1716 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
5228 | 1717 gboolean new_entry = FALSE; |
1718 | |
5256 | 1719 if (!gtkblist || !gtknode) |
5228 | 1720 return; |
1721 | |
5256 | 1722 if (!get_iter_from_node(node, &iter)) { |
5228 | 1723 new_entry = TRUE; |
1724 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1725 if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) { | |
1726 GtkTreeIter groupiter; | |
1727 char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); | |
1728 | |
1729 if(node->parent && | |
1730 !get_iter_from_node(node->parent, &groupiter)) { | |
1731 /* This buddy's group has not yet been added. | |
1732 * We do that here */ | |
1733 make_a_group(node->parent, &groupiter); | |
1734 } | |
1735 if(!collapsed) | |
1736 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); | |
1737 else | |
1738 g_free(collapsed); | |
5422 | 1739 |
1740 iter = current_sort_method->func(node, list, groupiter, NULL); | |
1741 | |
5228 | 1742 if (blist_options & OPT_BLIST_POPUP) { |
1743 gtk_widget_show(gtkblist->window); | |
1744 gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); | |
1745 gdk_window_raise(gtkblist->window->window); | |
1746 } | |
1747 | |
1748 } | |
5234 | 1749 } else if (GAIM_BLIST_NODE_IS_CHAT(node) && |
1750 ((struct chat *)node)->account->gc) { | |
1751 GtkTreeIter groupiter; | |
1752 GaimBlistNode *oldersibling; | |
1753 GtkTreeIter oldersiblingiter; | |
1754 char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); | |
1755 | |
1756 if(node->parent && | |
1757 !get_iter_from_node(node->parent, &groupiter)) { | |
1758 /* This buddy's group has not yet been added. | |
1759 * We do that here */ | |
1760 make_a_group(node->parent, &groupiter); | |
1761 } | |
1762 if(!collapsed) | |
1763 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); | |
1764 else | |
1765 g_free(collapsed); | |
1766 | |
1767 oldersibling = node->prev; | |
1768 while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { | |
1769 oldersibling = oldersibling->prev; | |
1770 } | |
1771 | |
1772 gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); | |
5256 | 1773 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); |
1774 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
1775 gtk_tree_path_free(newpath); | |
5234 | 1776 |
1777 } else if (GAIM_BLIST_NODE_IS_GROUP(node) && | |
5228 | 1778 ((blist_options & OPT_BLIST_SHOW_OFFLINE) || |
1779 !(blist_options & OPT_BLIST_NO_MT_GRP))) { | |
1780 make_a_group(node, &iter); | |
1781 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
1782 } | |
1783 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
1784 if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) { | |
1785 gtk_tree_store_remove(gtkblist->treemodel, &iter); | |
1786 } else { | |
1787 struct group *group = (struct group *)node; | |
1788 char *esc = g_markup_escape_text(group->name, -1); | |
1789 char *mark; | |
1790 | |
1791 if(blist_options & OPT_BLIST_SHOW_GRPNUM) | |
1792 mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); | |
1793 else | |
1794 mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); | |
1795 | |
1796 g_free(esc); | |
1797 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
1798 NAME_COLUMN, mark, | |
1799 -1); | |
1800 g_free(mark); | |
1801 } | |
1802 } | |
1803 | |
5234 | 1804 if (GAIM_BLIST_NODE_IS_CHAT(node) && ((struct chat*)node)->account->gc) { |
1805 GdkPixbuf *status; | |
5237 | 1806 struct chat *chat = (struct chat *)node; |
1807 char *name; | |
5234 | 1808 |
1809 status = gaim_gtk_blist_get_status_icon(node, | |
1810 blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); | |
5237 | 1811 if(chat->alias) { |
1812 name = g_markup_escape_text(chat->alias, -1); | |
1813 } else { | |
1814 struct proto_chat_entry *pce; | |
5241 | 1815 GList *parts, *tmp; |
5237 | 1816 |
1817 parts = GAIM_PLUGIN_PROTOCOL_INFO(chat->account->gc->prpl)->chat_info(chat->account->gc); | |
1818 pce = parts->data; | |
1819 name = g_markup_escape_text(g_hash_table_lookup(chat->components, | |
1820 pce->identifier), -1); | |
5241 | 1821 for(tmp = parts; tmp; tmp = tmp->next) |
1822 g_free(tmp->data); | |
5237 | 1823 g_list_free(parts); |
1824 } | |
1825 | |
5234 | 1826 |
1827 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
1828 STATUS_ICON_COLUMN, status, | |
1829 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
5237 | 1830 NAME_COLUMN, name, |
5234 | 1831 NODE_COLUMN, node, |
1832 -1); | |
1833 | |
5237 | 1834 g_free(name); |
5234 | 1835 if (status != NULL) |
1836 g_object_unref(status); | |
1837 } else if(GAIM_BLIST_NODE_IS_CHAT(node) && !((struct chat *)node)->account->gc) { | |
5260 | 1838 gaim_gtk_blist_hide_node(list, node); |
5234 | 1839 } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) { |
5228 | 1840 GdkPixbuf *status, *avatar; |
5422 | 1841 GtkTreeIter groupiter; |
5228 | 1842 char *mark; |
1843 char *warning = NULL, *idle = NULL; | |
1844 | |
1845 gboolean selected = (gtkblist->selected_node == node); | |
1846 | |
5234 | 1847 status = gaim_gtk_blist_get_status_icon(node, |
5228 | 1848 blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); |
1849 avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); | |
1850 mark = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected); | |
1851 | |
1852 if (((struct buddy*)node)->idle > 0) { | |
1853 time_t t; | |
1854 int ihrs, imin; | |
1855 time(&t); | |
1856 ihrs = (t - ((struct buddy *)node)->idle) / 3600; | |
1857 imin = ((t - ((struct buddy*)node)->idle) / 60) % 60; | |
1858 if(ihrs > 0) | |
1859 idle = g_strdup_printf("(%d:%02d)", ihrs, imin); | |
1860 else | |
1861 idle = g_strdup_printf("(%d)", imin); | |
1862 } | |
1863 | |
1864 if (((struct buddy*)node)->evil > 0) | |
1865 warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil); | |
1866 | |
1867 | |
1868 if((blist_options & OPT_BLIST_GREY_IDLERS) | |
1869 && ((struct buddy *)node)->idle) { | |
1870 if(warning && !selected) { | |
1871 char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
1872 warning); | |
1873 g_free(warning); | |
1874 warning = w2; | |
1875 } | |
1876 | |
1877 if(idle && !selected) { | |
1878 char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
1879 idle); | |
1880 g_free(idle); | |
1881 idle = i2; | |
1882 } | |
1883 } | |
5422 | 1884 if (!selected) { |
1885 get_iter_from_node(node->parent, &groupiter); | |
1886 iter = current_sort_method->func(node, list, groupiter, &iter); | |
1887 } | |
5228 | 1888 |
1889 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
1890 STATUS_ICON_COLUMN, status, | |
1891 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
1892 NAME_COLUMN, mark, | |
1893 WARNING_COLUMN, warning, | |
1894 IDLE_COLUMN, idle, | |
1895 BUDDY_ICON_COLUMN, avatar, | |
1896 NODE_COLUMN, node, | |
1897 -1); | |
1898 | |
1899 if (blist_options & OPT_BLIST_POPUP && | |
1900 ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { | |
1901 gtk_widget_show(gtkblist->window); | |
1902 gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); | |
1903 gdk_window_raise(gtkblist->window->window); | |
1904 } | |
1905 | |
1906 g_free(mark); | |
1907 if (idle) | |
1908 g_free(idle); | |
1909 if (warning) | |
1910 g_free(warning); | |
1911 | |
1912 if (status != NULL) | |
1913 g_object_unref(status); | |
1914 | |
1915 if (avatar != NULL) | |
1916 g_object_unref(avatar); | |
1917 | |
1918 } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) { | |
5260 | 1919 gaim_gtk_blist_hide_node(list, node); |
5228 | 1920 } |
5234 | 1921 |
5228 | 1922 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); |
1923 | |
1924 | |
1925 if(expand) { | |
1926 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); | |
1927 gtk_tree_path_free(expand); | |
1928 } | |
1929 | |
5234 | 1930 if(GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node)) |
5228 | 1931 gaim_gtk_blist_update(list, node->parent); |
1932 } | |
1933 | |
1934 static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list) | |
1935 { | |
1936 if (!gtkblist) | |
1937 return; | |
1938 | |
1939 gtk_widget_destroy(gtkblist->window); | |
1940 gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); | |
1941 | |
1942 if (gtkblist->refresh_timer) | |
1943 g_source_remove(gtkblist->refresh_timer); | |
1944 if (gtkblist->timeout) | |
1945 g_source_remove(gtkblist->timeout); | |
1946 | |
1947 gtkblist->refresh_timer = 0; | |
1948 gtkblist->timeout = 0; | |
1949 gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; | |
1950 gtkblist->treemodel = NULL; | |
1951 gtkblist->idle_column = NULL; | |
1952 gtkblist->warning_column = gtkblist->buddy_icon_column = NULL; | |
1953 gtkblist->bbox = gtkblist->tipwindow = NULL; | |
5427 | 1954 g_object_unref(G_OBJECT(gtkblist->ift)); |
5228 | 1955 protomenu = NULL; |
1956 awaymenu = NULL; | |
1957 gtkblist = NULL; | |
1958 } | |
1959 | |
1960 static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show) | |
1961 { | |
1962 if (!(gtkblist && gtkblist->window)) | |
1963 return; | |
1964 | |
1965 if (show) { | |
1966 gaim_gtk_blist_restore_position(); | |
1967 gtk_window_present(GTK_WINDOW(gtkblist->window)); | |
1968 } else { | |
1969 if (!connections || docklet_count) { | |
1970 #ifdef _WIN32 | |
1971 wgaim_systray_minimize(gtkblist->window); | |
1972 #endif | |
1973 gtk_widget_hide(gtkblist->window); | |
1974 } else { | |
1975 gtk_window_iconify(GTK_WINDOW(gtkblist->window)); | |
1976 } | |
1977 } | |
1978 } | |
1979 | |
1980 void gaim_gtk_blist_docklet_toggle() { | |
1981 /* Useful for the docklet plugin and also for the win32 tray icon*/ | |
1982 /* This is called when one of those is clicked--it will show/hide the | |
1983 buddy list/login window--depending on which is active */ | |
1984 if (connections) { | |
1985 if (gtkblist && gtkblist->window) { | |
1986 if (GTK_WIDGET_VISIBLE(gtkblist->window)) { | |
1987 gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); | |
1988 } else { | |
1989 #if _WIN32 | |
1990 wgaim_systray_maximize(gtkblist->window); | |
1991 #endif | |
1992 gaim_blist_set_visible(TRUE); | |
1993 } | |
1994 } else { | |
1995 /* we're logging in or something... do nothing */ | |
1996 /* or should I make the blist? */ | |
1997 gaim_debug(GAIM_DEBUG_WARNING, "blist", | |
1998 "docklet_toggle called with connections " | |
1999 "but no blist!\n"); | |
2000 } | |
2001 } else if (mainwindow) { | |
2002 if (GTK_WIDGET_VISIBLE(mainwindow)) { | |
2003 if (GAIM_WINDOW_ICONIFIED(mainwindow)) { | |
2004 gtk_window_present(GTK_WINDOW(mainwindow)); | |
2005 } else { | |
2006 #if _WIN32 | |
2007 wgaim_systray_minimize(mainwindow); | |
2008 #endif | |
2009 gtk_widget_hide(mainwindow); | |
2010 } | |
2011 } else { | |
2012 #if _WIN32 | |
2013 wgaim_systray_maximize(mainwindow); | |
2014 #endif | |
2015 show_login(); | |
2016 } | |
2017 } else { | |
2018 show_login(); | |
2019 } | |
2020 } | |
2021 | |
2022 void gaim_gtk_blist_docklet_add() | |
2023 { | |
2024 docklet_count++; | |
2025 } | |
2026 | |
2027 void gaim_gtk_blist_docklet_remove() | |
2028 { | |
2029 docklet_count--; | |
2030 if (!docklet_count) { | |
2031 if (connections) | |
2032 gaim_blist_set_visible(TRUE); | |
2033 else if (mainwindow) | |
2034 gtk_window_present(GTK_WINDOW(mainwindow)); | |
2035 else | |
2036 show_login(); | |
2037 } | |
2038 } | |
2039 | |
2040 static struct gaim_blist_ui_ops blist_ui_ops = | |
2041 { | |
2042 gaim_gtk_blist_new_list, | |
5256 | 2043 gaim_gtk_blist_new_node, |
5228 | 2044 gaim_gtk_blist_show, |
2045 gaim_gtk_blist_update, | |
2046 gaim_gtk_blist_remove, | |
2047 gaim_gtk_blist_destroy, | |
2048 gaim_gtk_blist_set_visible | |
2049 }; | |
2050 | |
2051 | |
2052 struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops() | |
2053 { | |
2054 return &blist_ui_ops; | |
2055 } | |
2056 | |
2057 | |
2058 | |
2059 /********************************************************************* | |
2060 * Public utility functions * | |
2061 *********************************************************************/ | |
2062 | |
2063 GdkPixbuf * | |
2064 create_prpl_icon(struct gaim_account *account) | |
2065 { | |
2066 GaimPlugin *prpl; | |
2067 GaimPluginProtocolInfo *prpl_info = NULL; | |
2068 GdkPixbuf *status = NULL; | |
2069 char *filename = NULL; | |
2070 const char *protoname = NULL; | |
2071 char buf[256]; | |
2072 | |
2073 prpl = gaim_find_prpl(account->protocol); | |
2074 | |
2075 if (prpl != NULL) { | |
2076 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
2077 | |
2078 if (prpl_info->list_icon != NULL) | |
2079 protoname = prpl_info->list_icon(account, NULL); | |
2080 } | |
2081 | |
2082 if (protoname == NULL) | |
2083 return NULL; | |
2084 | |
2085 /* | |
2086 * Status icons will be themeable too, and then it will look up | |
2087 * protoname from the theme | |
2088 */ | |
2089 g_snprintf(buf, sizeof(buf), "%s.png", protoname); | |
2090 | |
2091 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", | |
2092 "default", buf, NULL); | |
2093 status = gdk_pixbuf_new_from_file(filename, NULL); | |
2094 g_free(filename); | |
2095 | |
2096 return status; | |
2097 } | |
2098 | |
5422 | 2099 |
2100 /********************************************************************* | |
2101 * Buddy List sorting functions * | |
2102 *********************************************************************/ | |
2103 | |
2104 void gaim_gtk_blist_sort_method_reg(const char *name, gaim_gtk_blist_sort_function func) | |
2105 { | |
2106 struct gaim_gtk_blist_sort_method *method = g_new0(struct gaim_gtk_blist_sort_method, 1); | |
2107 method->name = g_strdup(name); | |
2108 method->func = func; | |
2109 gaim_gtk_blist_sort_methods = g_slist_append(gaim_gtk_blist_sort_methods, method); | |
2110 } | |
2111 | |
2112 void gaim_gtk_blist_sort_method_unreg(const char *name){ | |
2113 | |
2114 } | |
2115 | |
2116 void gaim_gtk_blist_sort_method_set(const char *name){ | |
2117 GSList *l = gaim_gtk_blist_sort_methods; | |
2118 while (l && gaim_utf8_strcasecmp(((struct gaim_gtk_blist_sort_method*)l->data)->name, name)) | |
2119 l = l->next; | |
2120 | |
2121 if (l) { | |
2122 current_sort_method = l->data; | |
2123 strcpy(sort_method, ((struct gaim_gtk_blist_sort_method*)l->data)->name); | |
2124 } else if (!current_sort_method) { | |
5516 | 2125 gaim_gtk_blist_sort_method_set(_("None")); |
5422 | 2126 return; |
2127 } | |
2128 save_prefs(); | |
2129 redo_buddy_list(gaim_get_blist(), TRUE); | |
2130 | |
2131 } | |
2132 | |
2133 /****************************************** | |
2134 ** Sort Methods | |
2135 ******************************************/ | |
2136 | |
2137 /* A sort method takes a core buddy list node, the buddy list it belongs in, the GtkTreeIter of its group and | |
2138 * the nodes own iter if it has one. It returns the iter the buddy list should use to represent this buddy, be | |
2139 * it a new iter, or an existing one. If it is a new iter, and cur is defined, the buddy list will probably want | |
2140 * to remove cur from the buddy list. */ | |
2141 static GtkTreeIter sort_method_none(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
2142 { | |
2143 GtkTreePath *newpath; | |
2144 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2145 GaimBlistNode *oldersibling = node->prev; | |
2146 GtkTreeIter iter, oldersiblingiter; | |
2147 | |
2148 if (cur) | |
2149 return *cur; | |
2150 | |
2151 while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { | |
2152 oldersibling = oldersibling->prev; | |
2153 } | |
2154 | |
2155 gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); | |
2156 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2157 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2158 gtk_tree_path_free(newpath); | |
2159 return iter; | |
2160 } | |
2161 | |
2162 static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
2163 { | |
2164 GtkTreeIter more_z, iter; | |
2165 GaimBlistNode *n; | |
2166 GtkTreePath *newpath; | |
2167 GValue val = {0,}; | |
2168 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2169 | |
2170 if (cur) | |
2171 return *cur; | |
2172 | |
2173 | |
2174 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
2175 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
2176 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2177 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2178 gtk_tree_path_free(newpath); | |
2179 return iter; | |
2180 } | |
2181 | |
2182 do { | |
2183 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); | |
2184 n = g_value_get_pointer(&val); | |
2185 | |
2186 if (GAIM_BLIST_NODE_IS_BUDDY(n) && gaim_utf8_strcasecmp(gaim_get_buddy_alias((struct buddy*)node), | |
2187 gaim_get_buddy_alias((struct buddy*)n)) < 0) { | |
2188 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); | |
2189 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2190 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2191 gtk_tree_path_free(newpath); | |
2192 return iter; | |
2193 } | |
2194 g_value_unset(&val); | |
2195 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
2196 | |
2197 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
2198 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2199 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2200 gtk_tree_path_free(newpath); | |
2201 return iter; | |
2202 } | |
2203 | |
2204 static GtkTreeIter sort_method_status(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
2205 { | |
2206 GtkTreeIter more_z, iter; | |
2207 GaimBlistNode *n; | |
5451 | 2208 GtkTreePath *newpath, *expand = NULL; |
5422 | 2209 GValue val = {0,}; |
2210 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2211 char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); | |
2212 if(!collapsed) | |
2213 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); | |
2214 else | |
2215 g_free(collapsed); | |
2216 | |
2217 | |
2218 if (cur) | |
2219 gaim_gtk_blist_hide_node(blist, node); | |
2220 | |
2221 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
2222 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
2223 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2224 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2225 gtk_tree_path_free(newpath); | |
2226 if(expand) { | |
2227 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); | |
2228 gtk_tree_path_free(expand); | |
2229 } | |
2230 return iter; | |
2231 } | |
2232 | |
2233 do { | |
2234 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); | |
2235 n = g_value_get_pointer(&val); | |
2236 | |
2237 if (n && GAIM_BLIST_NODE_IS_BUDDY(n)) { | |
2238 struct buddy *new = (struct buddy*)node, *it = (struct buddy*)n; | |
5438 | 2239 /* This is the worst if statement ever. */ |
2240 if ((it->present < new->present) || | |
2241 ((it->present == new->present) && (it->uc & UC_UNAVAILABLE) > (new->uc & UC_UNAVAILABLE)) || | |
2242 ((it->present == new->present) && ((it->uc & UC_UNAVAILABLE) == (new->uc & UC_UNAVAILABLE)) && | |
2243 ((it->idle && !new->idle) || (it->idle && (it->idle < new->idle)))) || | |
2244 ((it->present == new->present) && (it->uc & UC_UNAVAILABLE) == (new->uc & UC_UNAVAILABLE) && (it->idle == new->idle) && | |
2245 (gaim_utf8_strcasecmp(gaim_get_buddy_alias((struct buddy*)node), gaim_get_buddy_alias((struct buddy*)n)) < 0))) | |
5422 | 2246 { |
2247 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); | |
2248 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2249 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2250 gtk_tree_path_free(newpath); | |
2251 return iter; | |
2252 } | |
2253 g_value_unset(&val); | |
2254 } | |
2255 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
2256 | |
2257 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
2258 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2259 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2260 gtk_tree_path_free(newpath); | |
2261 return iter; | |
2262 } | |
2263 | |
2264 static GtkTreeIter sort_method_log(GaimBlistNode *node, struct gaim_buddy_list *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
2265 { | |
2266 GtkTreeIter more_z, iter; | |
2267 GaimBlistNode *n; | |
2268 GtkTreePath *newpath; | |
2269 GValue val = {0,}; | |
2270 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2271 char *logname = g_strdup_printf("%s.log", normalize(((struct buddy*)node)->name)); | |
2272 char *filename = g_build_filename(gaim_user_dir(), "logs", logname, NULL); | |
2273 struct stat st, st2; | |
2274 | |
2275 if (cur) | |
2276 return *cur; | |
2277 | |
2278 if (stat(filename, &st)) | |
2279 st.st_size = 0; | |
2280 g_free(filename); | |
2281 g_free(logname); | |
2282 | |
2283 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
2284 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
2285 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2286 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2287 gtk_tree_path_free(newpath); | |
2288 return iter; | |
2289 } | |
2290 | |
2291 do { | |
2292 | |
2293 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); | |
2294 n = g_value_get_pointer(&val); | |
2295 | |
2296 logname = g_strdup_printf("%s.log", normalize(((struct buddy*)n)->name)); | |
2297 filename = g_build_filename(gaim_user_dir(), "logs", logname, NULL); | |
2298 if (stat(filename, &st2)) | |
2299 st2.st_size = 0; | |
2300 g_free(filename); | |
2301 g_free(logname); | |
2302 if (GAIM_BLIST_NODE_IS_BUDDY(n) && st.st_size > st2.st_size) { | |
2303 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, &groupiter, &more_z); | |
2304 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2305 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2306 gtk_tree_path_free(newpath); | |
2307 return iter; | |
2308 } | |
2309 g_value_unset(&val); | |
2310 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
2311 | |
2312 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
2313 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); | |
2314 gtknode->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), newpath); | |
2315 gtk_tree_path_free(newpath); | |
2316 return iter; | |
2317 } |