Mercurial > pidgin.yaz
annotate src/gtkblist.c @ 7636:01c1b3ea70c6
[gaim-migrate @ 8278]
This auto-expands Persons on mouse-over. I think it works rather well, and
performs such niceties as drawing the tooltip as not to cover up the child
buddies and such. The only thing I'm not sure about yet is that, although
it won't expand contacts only containing a single buddy, it will expand
persons (note how I go back and forth in terminology) that have only one
*online* buddy. That's probably the correct thing; it shows the user that
only one buddy is online. But I can see how it might cause confusion.
Comments to gaim-devel@lists.sourceforge.net are welcome.
committer: Tailor Script <tailor@pidgin.im>
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Wed, 26 Nov 2003 08:04:47 +0000 |
parents | e293d0c42ccb |
children | 9008b5be4275 |
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 */ | |
7620 | 21 #include "gtkinternal.h" |
22 | |
23 #include "account.h" | |
24 #include "core.h" | |
25 #include "debug.h" | |
26 #include "multi.h" | |
27 #include "notify.h" | |
28 #include "prpl.h" | |
29 #include "prefs.h" | |
30 #include "request.h" | |
31 #include "signals.h" | |
32 #include "sound.h" | |
33 #include "stock.h" | |
34 #include "util.h" | |
35 | |
36 #include "gtkaccount.h" | |
37 #include "gtkblist.h" | |
38 #include "gtkconv.h" | |
39 #include "gtkdebug.h" | |
40 #include "gtkft.h" | |
41 #include "gtklog.h" | |
42 #include "gtkpounce.h" | |
43 #include "gtkprefs.h" | |
44 #include "gtkprivacy.h" | |
45 #include "gtkutils.h" | |
46 | |
47 #include "ui.h" | |
48 | |
49 #include "gaim.h" | |
5228 | 50 |
51 #include <gdk/gdkkeysyms.h> | |
52 #include <gtk/gtk.h> | |
7620 | 53 #include <gdk/gdk.h> |
54 | |
55 typedef struct | |
56 { | |
57 GaimAccount *account; | |
58 | |
59 GtkWidget *window; | |
60 GtkWidget *combo; | |
61 GtkWidget *entry; | |
62 GtkWidget *entry_for_alias; | |
63 GtkWidget *account_box; | |
64 | |
65 } GaimGtkAddBuddyData; | |
66 | |
67 typedef struct | |
68 { | |
69 GaimAccount *account; | |
70 | |
71 GtkWidget *window; | |
72 GtkWidget *account_menu; | |
73 GtkWidget *alias_entry; | |
74 GtkWidget *group_combo; | |
75 GtkWidget *entries_box; | |
76 GtkSizeGroup *sg; | |
77 | |
78 GList *entries; | |
79 | |
80 } GaimGtkAddChatData; | |
81 | |
82 | |
83 static GtkWidget *protomenu = NULL; | |
5228 | 84 |
5422 | 85 GSList *gaim_gtk_blist_sort_methods = NULL; |
86 static struct gaim_gtk_blist_sort_method *current_sort_method = NULL; | |
7620 | 87 static GtkTreeIter sort_method_none(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); |
88 | |
89 /* The functions we use for sorting aren't available in gtk 2.0.x, and | |
90 * segfault in 2.2.0. 2.2.1 is known to work, so I'll require that */ | |
91 #if GTK_CHECK_VERSION(2,2,1) | |
92 static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
93 static GtkTreeIter sort_method_status(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
94 static GtkTreeIter sort_method_log(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur); | |
95 #endif | |
96 static GaimGtkBuddyList *gtkblist = NULL; | |
5228 | 97 |
98 /* part of the best damn Docklet code this side of Tahiti */ | |
99 static gboolean gaim_gtk_blist_obscured = FALSE; | |
100 | |
101 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); | |
7620 | 102 static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node); |
5234 | 103 static char *gaim_get_tooltip_text(GaimBlistNode *node); |
5228 | 104 static char *item_factory_translate_func (const char *path, gpointer func_data); |
5273 | 105 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter); |
7620 | 106 static void redo_buddy_list(GaimBuddyList *list, gboolean remove); |
107 static void gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node); | |
108 | |
109 static void show_rename_group(GtkWidget *unused, GaimGroup *g); | |
5422 | 110 |
5256 | 111 struct _gaim_gtk_blist_node { |
112 GtkTreeRowReference *row; | |
7620 | 113 gboolean contact_expanded; |
5256 | 114 }; |
115 | |
7620 | 116 #if GTK_CHECK_VERSION(2,2,0) |
117 /**************************** Weird drop shadow stuff *******************/ | |
118 /* This is based on a patch for drop shadows in GTK menus available at http://www.xfce.org/gtkmenu-shadow/ */ | |
119 | |
120 enum side { | |
121 EAST_SIDE, | |
122 SOUTH_SIDE | |
123 }; | |
124 | |
125 const double shadow_strip_l[5] = { | |
126 .937, .831, .670, .478, .180 | |
127 }; | |
128 | |
129 const double bottom_left_corner[25] = { | |
130 1.00, .682, .423, .333, .258, | |
131 1.00, .898, .800, .682, .584, | |
132 1.00, .937, .874, .800, .737, | |
133 1.00, .968, .937, .898, .866, | |
134 1.00, .988, .976, .960, .945 | |
135 }; | |
136 | |
137 const double bottom_right_corner[25] = { | |
138 .258, .584, .737, .866, .945, | |
139 .584, .682, .800, .898, .960, | |
140 .737, .800, .874, .937, .976, | |
141 .866, .898, .937, .968, .988, | |
142 .945, .960, .976, .988, .996 | |
143 }; | |
144 | |
145 const double top_right_corner[25] = { | |
146 1.00, 1.00, 1.00, 1.00, 1.00, | |
147 .686, .898, .937, .968, .988, | |
148 .423, .803, .874, .937, .976, | |
149 .333, .686, .800, .898, .960, | |
150 .258, .584, .737, .866, .945 | |
151 }; | |
152 | |
153 const double top_left_corner[25] = { | |
154 .988, .968, .937, .898, .498, | |
155 .976, .937, .874, .803, .423, | |
156 .960, .898, .800, .686, .333, | |
157 .945, .866, .737, .584, .258, | |
158 .941, .847, .698, .521, .215 | |
159 }; | |
160 | |
161 | |
162 static GdkPixbuf * | |
163 get_pixbuf (GtkWidget *menu, | |
164 int x, | |
165 int y, | |
166 int width, | |
167 int height) | |
168 { | |
169 GdkPixbuf *dest, *src; | |
170 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(menu)); | |
171 GdkWindow *root = gdk_screen_get_root_window (screen); | |
172 gint screen_height = gdk_screen_get_height (screen); | |
173 gint screen_width = gdk_screen_get_width (screen); | |
174 gint original_width = width; | |
175 gint original_height = height; | |
176 | |
177 #ifdef _WIN32 | |
178 /* In Win32, GDK gets the workarea that isn't occupied by toolbars | |
179 (including the taskbar) and uses that region as the screen size. | |
180 GTK returns positions based on a screen size that ignores these | |
181 toolbars. Since we want a pixmap with real X,Y coordinates, we | |
182 need to find out the offset from GTK's screen to GDK's screen, | |
183 and adjust the pixmaps we grab accordingly. GDK will not deal | |
184 with toolbar position updates, so we're stuck restarting Gaim | |
185 if that happens. */ | |
186 RECT *workarea = g_malloc(sizeof(RECT)); | |
187 SystemParametersInfo(SPI_GETWORKAREA, 0, (void *)workarea, 0); | |
188 x += (workarea->left); | |
189 y += (workarea->top); | |
190 g_free(workarea); | |
191 #endif | |
192 | |
193 if (x < 0) | |
194 { | |
195 width += x; | |
196 x = 0; | |
197 } | |
198 | |
199 if (y < 0) | |
200 { | |
201 height += y; | |
202 y = 0; | |
203 } | |
204 | |
205 if (x + width > screen_width) | |
206 { | |
207 width = screen_width - x; | |
208 } | |
209 | |
210 if (y + height > screen_height) | |
211 { | |
212 height = screen_height - y; | |
213 } | |
214 | |
215 if (width <= 0 || height <= 0) | |
216 return NULL; | |
217 | |
218 dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, | |
219 original_width, original_height); | |
220 src = gdk_pixbuf_get_from_drawable (NULL, root, NULL, x, y, 0, 0, | |
221 width, height); | |
222 gdk_pixbuf_copy_area (src, 0, 0, width, height, dest, 0, 0); | |
223 | |
224 g_object_unref (G_OBJECT (src)); | |
225 | |
226 return dest; | |
227 } | |
228 | |
229 static void | |
230 shadow_paint(GaimGtkBuddyList *blist, GdkRectangle *area, enum side shadow) | |
231 { | |
232 gint width, height; | |
233 GdkGC *gc = gtkblist->tipwindow->style->black_gc; | |
234 | |
235 switch (shadow) | |
236 { | |
237 case EAST_SIDE: | |
238 if (gtkblist->east != NULL) | |
239 { | |
240 if (area) | |
241 gdk_gc_set_clip_rectangle (gc, area); | |
242 | |
243 width = gdk_pixbuf_get_width (gtkblist->east); | |
244 height = gdk_pixbuf_get_height (gtkblist->east); | |
245 | |
246 #if GTK_CHECK_VERSION(2,2,0) | |
247 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->east_shadow), gc, | |
248 gtkblist->east, 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, | |
249 0, 0); | |
250 #else | |
251 gdk_pixbuf_render_to_drawable(gtkblist->east, | |
252 GDK_DRAWABLE(gtkblist->east_shadow), gc, 0, 0, 0, 0, | |
253 width, height, GDK_RGB_DITHER_NONE, 0, 0); | |
254 #endif | |
255 | |
256 if (area) | |
257 gdk_gc_set_clip_rectangle (gc, NULL); | |
258 } | |
259 break; | |
260 case SOUTH_SIDE: | |
261 if (blist->south != NULL) | |
262 { | |
263 if (area) | |
264 gdk_gc_set_clip_rectangle (gc, area); | |
265 | |
266 width = gdk_pixbuf_get_width (gtkblist->south); | |
267 height = gdk_pixbuf_get_height (gtkblist->south); | |
268 #if GTK_CHECK_VERSION(2,2,0) | |
269 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->south_shadow), gc, gtkblist->south, | |
270 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0); | |
271 #else | |
272 gdk_pixbuf_render_to_drawable(gtkblist->south, GDK_DRAWABLE(gtkblist->south_shadow), gc, | |
273 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0); | |
274 #endif | |
275 if (area) | |
276 gdk_gc_set_clip_rectangle (gc, NULL); | |
277 } | |
278 break; | |
279 default: | |
280 break; | |
281 } | |
282 } | |
283 | |
284 static void | |
285 pixbuf_add_shadow (GdkPixbuf *pb, | |
286 enum side shadow) | |
287 { | |
288 gint width, rowstride, height; | |
289 gint i; | |
290 guchar *pixels, *p; | |
291 | |
292 width = gdk_pixbuf_get_width (pb); | |
293 height = gdk_pixbuf_get_height (pb); | |
294 rowstride = gdk_pixbuf_get_rowstride (pb); | |
295 pixels = gdk_pixbuf_get_pixels (pb); | |
296 | |
297 switch (shadow) | |
298 { | |
299 case EAST_SIDE: | |
300 if (height > 5) | |
301 { | |
302 for (i = 0; i < width; i++) | |
303 { | |
304 gint j, k; | |
305 | |
306 p = pixels + (i * rowstride); | |
307 for (j = 0, k = 0; j < 3 * width; j += 3, k++) | |
308 { | |
309 p[j] = (guchar) (p[j] * top_right_corner [i * width + k]); | |
310 p[j + 1] = (guchar) (p[j + 1] * top_right_corner [i * width + k]); | |
311 p[j + 2] = (guchar) (p[j + 2] * top_right_corner [i * width + k]); | |
312 } | |
313 } | |
314 | |
315 i = 5; | |
316 } | |
317 else | |
318 { | |
319 i = 0; | |
320 } | |
321 | |
322 for (;i < height; i++) | |
323 { | |
324 gint j, k; | |
325 | |
326 p = pixels + (i * rowstride); | |
327 for (j = 0, k = 0; j < 3 * width; j += 3, k++) | |
328 { | |
329 p[j] = (guchar) (p[j] * shadow_strip_l[width - 1 - k]); | |
330 p[j + 1] = (guchar) (p[j + 1] * shadow_strip_l[width - 1 - k]); | |
331 p[j + 2] = (guchar) (p[j + 2] * shadow_strip_l[width - 1 - k]); | |
332 } | |
333 } | |
334 break; | |
335 | |
336 case SOUTH_SIDE: | |
337 for (i = 0; i < height; i++) | |
338 { | |
339 gint j, k; | |
340 | |
341 p = pixels + (i * rowstride); | |
342 for (j = 0, k = 0; j < 3 * height; j += 3, k++) | |
343 { | |
344 p[j] = (guchar) (p[j] * bottom_left_corner[i * height + k]); | |
345 p[j + 1] = (guchar) (p[j + 1] * bottom_left_corner[i * height + k]); | |
346 p[j + 2] = (guchar) (p[j + 2] * bottom_left_corner[i * height + k]); | |
347 } | |
348 | |
349 p = pixels + (i * rowstride) + 3 * height; | |
350 for (j = 0, k = 0; j < (width * 3) - (6 * height); j += 3, k++) | |
351 { | |
352 p[j] = (guchar) (p[j] * bottom_right_corner [i * height]); | |
353 p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner [i * height]); | |
354 p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner [i * height]); | |
355 } | |
356 | |
357 p = pixels + (i * rowstride) + ((width * 3) - (3 * height)); | |
358 for (j = 0, k = 0; j < 3 * height; j += 3, k++) | |
359 { | |
360 p[j] = (guchar) (p[j] * bottom_right_corner[i * height + k]); | |
361 p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner[i * height + k]); | |
362 p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner[i * height + k]); | |
363 } | |
364 } | |
365 break; | |
366 | |
367 default: | |
368 break; | |
369 } | |
370 } | |
371 | |
372 static gboolean | |
373 map_shadow_windows (gpointer data) | |
374 { | |
375 GaimGtkBuddyList *blist = (GaimGtkBuddyList*)data; | |
376 GtkWidget *widget = blist->tipwindow; | |
377 GdkPixbuf *pixbuf; | |
378 int x, y; | |
379 | |
380 gtk_window_get_position(GTK_WINDOW(widget), &x, &y); | |
381 pixbuf = get_pixbuf (widget, | |
382 x + widget->allocation.width, y, | |
383 5, widget->allocation.height + 5); | |
384 if (pixbuf != NULL) | |
385 { | |
386 pixbuf_add_shadow (pixbuf, EAST_SIDE); | |
387 if (blist->east != NULL) | |
388 { | |
389 g_object_unref (G_OBJECT (blist->east)); | |
390 } | |
391 blist->east = pixbuf; | |
392 } | |
393 | |
394 pixbuf = get_pixbuf (widget, | |
395 x, y + widget->allocation.height, | |
396 widget->allocation.width + 5, 5); | |
397 if (pixbuf != NULL) | |
398 { | |
399 pixbuf_add_shadow (pixbuf, SOUTH_SIDE); | |
400 if (blist->south != NULL) | |
401 { | |
402 g_object_unref (G_OBJECT (blist->south)); | |
403 } | |
404 blist->south = pixbuf; | |
405 } | |
406 | |
407 gdk_window_move_resize (blist->east_shadow, | |
408 x + widget->allocation.width, y, | |
409 5, widget->allocation.height); | |
410 | |
411 gdk_window_move_resize (blist->south_shadow, | |
412 x, y + widget->allocation.height, | |
413 widget->allocation.width + 5, 5); | |
414 gdk_window_show (blist->east_shadow); | |
415 gdk_window_show (blist->south_shadow); | |
416 shadow_paint(blist, NULL, EAST_SIDE); | |
417 shadow_paint(blist, NULL, SOUTH_SIDE); | |
418 | |
419 return FALSE; | |
420 } | |
421 | |
422 /**************** END WEIRD DROP SHADOW STUFF ***********************************/ | |
423 #endif | |
424 static GSList *blist_prefs_callbacks = NULL; | |
425 | |
5228 | 426 /*************************************************** |
427 * Callbacks * | |
428 ***************************************************/ | |
429 | |
430 static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) | |
431 { | |
432 if (docklet_count) | |
433 gaim_blist_set_visible(FALSE); | |
434 else | |
7620 | 435 gaim_core_quit(); |
5228 | 436 |
437 /* we handle everything, event should not propogate further */ | |
438 return TRUE; | |
439 } | |
440 | |
441 static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) | |
442 { | |
443 /* unfortunately GdkEventConfigure ignores the window gravity, but * | |
444 * the only way we have of setting the position doesn't. we have to * | |
7620 | 445 * call get_position because it does pay attention to the gravity. * |
446 * this is inefficient and I agree it sucks, but it's more likely * | |
447 * to work correctly. - Robot101 */ | |
5228 | 448 gint x, y; |
449 | |
450 /* check for visibility because when we aren't visible, this will * | |
451 * give us bogus (0,0) coordinates. - xOr */ | |
7620 | 452 if (GTK_WIDGET_VISIBLE(w)) |
5228 | 453 gtk_window_get_position(GTK_WINDOW(w), &x, &y); |
7620 | 454 else |
455 return FALSE; /* carry on normally */ | |
456 | |
457 /* don't save if nothing changed */ | |
458 if (x == gaim_prefs_get_int("/gaim/gtk/blist/x") && | |
459 y == gaim_prefs_get_int("/gaim/gtk/blist/y") && | |
460 event->width == gaim_prefs_get_int("/gaim/gtk/blist/width") && | |
461 event->height == gaim_prefs_get_int("/gaim/gtk/blist/height")) { | |
462 | |
463 return FALSE; /* carry on normally */ | |
5228 | 464 } |
465 | |
7620 | 466 /* don't save off-screen positioning */ |
467 if (x + event->width < 0 || | |
468 y + event->height < 0 || | |
469 x > gdk_screen_width() || | |
470 y > gdk_screen_height()) { | |
471 | |
472 return FALSE; /* carry on normally */ | |
473 } | |
474 | |
475 /* store the position */ | |
476 gaim_prefs_set_int("/gaim/gtk/blist/x", x); | |
477 gaim_prefs_set_int("/gaim/gtk/blist/y", y); | |
478 gaim_prefs_set_int("/gaim/gtk/blist/width", event->width); | |
479 gaim_prefs_set_int("/gaim/gtk/blist/height", event->height); | |
480 | |
5228 | 481 /* continue to handle event normally */ |
482 return FALSE; | |
483 } | |
484 | |
485 static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data) | |
486 { | |
487 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) | |
488 gaim_gtk_blist_obscured = TRUE; | |
489 else | |
490 gaim_gtk_blist_obscured = FALSE; | |
491 | |
492 /* continue to handle event normally */ | |
493 return FALSE; | |
494 } | |
495 | |
7620 | 496 static void gtk_blist_menu_info_cb(GtkWidget *w, GaimBuddy *b) |
5228 | 497 { |
498 serv_get_info(b->account->gc, b->name); | |
499 } | |
500 | |
7620 | 501 static void gtk_blist_menu_im_cb(GtkWidget *w, GaimBuddy *b) |
5228 | 502 { |
7620 | 503 GaimConversation *conv = gaim_conversation_new(GAIM_CONV_IM, b->account, |
504 b->name); | |
505 | |
506 if(conv) { | |
507 GaimConvWindow *win = gaim_conversation_get_window(conv); | |
508 | |
509 gaim_conv_window_raise(win); | |
510 gaim_conv_window_switch_conversation( | |
511 gaim_conversation_get_window(conv), | |
512 gaim_conversation_get_index(conv)); | |
513 | |
514 if (GAIM_IS_GTK_WINDOW(win)) | |
515 gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window)); | |
516 } | |
5228 | 517 } |
518 | |
7620 | 519 static void gtk_blist_menu_autojoin_cb(GtkWidget *w, GaimChat *chat) |
520 { | |
521 if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) | |
522 gaim_chat_set_setting(chat, "gtk-autojoin", "true"); | |
523 else | |
524 gaim_chat_set_setting(chat, "gtk-autojoin", NULL); | |
525 | |
526 gaim_blist_save(); | |
527 } | |
528 | |
529 static void gtk_blist_menu_join_cb(GtkWidget *w, GaimChat *chat) | |
5228 | 530 { |
5234 | 531 serv_join_chat(chat->account->gc, chat->components); |
532 } | |
533 | |
534 static void gtk_blist_menu_alias_cb(GtkWidget *w, GaimBlistNode *node) | |
535 { | |
536 if(GAIM_BLIST_NODE_IS_BUDDY(node)) | |
7620 | 537 alias_dialog_bud((GaimBuddy*)node); |
538 else if(GAIM_BLIST_NODE_IS_CONTACT(node)) | |
539 alias_dialog_contact((GaimContact*)node); | |
5234 | 540 else if(GAIM_BLIST_NODE_IS_CHAT(node)) |
7620 | 541 alias_dialog_blist_chat((GaimChat*)node); |
5228 | 542 } |
543 | |
7620 | 544 static void gtk_blist_menu_bp_cb(GtkWidget *w, GaimBuddy *b) |
5228 | 545 { |
7620 | 546 gaim_gtkpounce_dialog_show(b->account, b->name, NULL); |
5228 | 547 } |
548 | |
7620 | 549 static void gtk_blist_menu_showlog_cb(GtkWidget *w, GaimBuddy *b) |
5228 | 550 { |
7620 | 551 gaim_gtk_log_show(b->name, b->account); |
552 } | |
553 | |
5228 | 554 static void gtk_blist_show_systemlog_cb() |
555 { | |
7620 | 556 /* LOG show_log(NULL); */ |
5228 | 557 } |
558 | |
559 static void gtk_blist_show_onlinehelp_cb() | |
560 { | |
7620 | 561 gaim_notify_uri(NULL, GAIM_WEBSITE "documentation.php"); |
5228 | 562 } |
563 | |
564 static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv) | |
565 { | |
566 GtkTreeIter iter; | |
567 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
568 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
569 | |
570 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
571 GaimBlistNode *node; | |
572 | |
573 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
574 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
7620 | 575 gaim_conversation_new(GAIM_CONV_IM, ((GaimBuddy*)node)->account, ((GaimBuddy*)node)->name); |
576 return; | |
577 } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
578 GaimBuddy *buddy = | |
579 gaim_contact_get_priority_buddy((GaimContact*)node); | |
580 gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); | |
5228 | 581 return; |
582 } | |
583 } | |
584 show_im_dialog(); | |
585 } | |
586 | |
587 static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv) | |
588 { | |
589 GtkTreeIter iter; | |
590 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
591 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
592 | |
593 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
594 GaimBlistNode *node; | |
595 | |
596 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
597 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
7620 | 598 serv_get_info(((GaimBuddy*)node)->account->gc, ((GaimBuddy*)node)->name); |
599 return; | |
600 } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
601 GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
602 serv_get_info(buddy->account->gc, buddy->name); | |
5228 | 603 return; |
604 } | |
605 } | |
606 show_info_dialog(); | |
607 } | |
608 | |
5234 | 609 static void gtk_blist_button_chat_cb(GtkWidget *w, GtkTreeView *tv) |
5228 | 610 { |
5234 | 611 GtkTreeIter iter; |
612 GtkTreeModel *model = gtk_tree_view_get_model(tv); | |
613 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); | |
614 | |
615 if(gtk_tree_selection_get_selected(sel, &model, &iter)){ | |
616 GaimBlistNode *node; | |
617 | |
618 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
619 if (GAIM_BLIST_NODE_IS_CHAT(node)) { | |
7620 | 620 serv_join_chat(((GaimChat *)node)->account->gc, ((GaimChat *)node)->components); |
5234 | 621 return; |
622 } | |
623 } | |
5228 | 624 join_chat(); |
625 } | |
626 | |
627 static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data) | |
628 { | |
629 gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME); | |
630 } | |
631 | |
632 static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { | |
633 GaimBlistNode *node; | |
634 GValue val = {0,}; | |
635 | |
636 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); | |
637 | |
638 node = g_value_get_pointer(&val); | |
639 | |
640 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
7620 | 641 gaim_group_set_setting((GaimGroup *)node, "collapsed", NULL); |
5228 | 642 gaim_blist_save(); |
643 } | |
644 } | |
645 | |
646 static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { | |
647 GaimBlistNode *node; | |
648 GValue val = {0,}; | |
649 | |
650 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); | |
651 | |
652 node = g_value_get_pointer(&val); | |
653 | |
654 if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
7620 | 655 gaim_group_set_setting((GaimGroup *)node, "collapsed", "true"); |
5228 | 656 gaim_blist_save(); |
7620 | 657 } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) { |
658 gaim_gtk_blist_collapse_contact_cb(NULL, node); | |
5228 | 659 } |
660 } | |
661 | |
662 static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { | |
663 GaimBlistNode *node; | |
664 GtkTreeIter iter; | |
665 GValue val = { 0, }; | |
666 | |
667 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
668 | |
669 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
670 node = g_value_get_pointer(&val); | |
671 | |
7620 | 672 if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node)) { |
673 GaimBuddy *buddy; | |
674 GaimConversation *conv; | |
675 | |
676 if(GAIM_BLIST_NODE_IS_CONTACT(node)) | |
677 buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
678 else | |
679 buddy = (GaimBuddy*)node; | |
680 | |
681 conv = gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); | |
5489
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
682 |
5228 | 683 if(conv) { |
7620 | 684 GaimConvWindow *win = gaim_conversation_get_window(conv); |
685 | |
686 gaim_conv_window_raise(win); | |
687 gaim_conv_window_switch_conversation( | |
5228 | 688 gaim_conversation_get_window(conv), |
689 gaim_conversation_get_index(conv)); | |
5489
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
690 |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
691 if (GAIM_IS_GTK_WINDOW(win)) |
5b5aa701d46b
[gaim-migrate @ 5885]
Christian Hammond <chipx86@chipx86.com>
parents:
5451
diff
changeset
|
692 gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window)); |
5228 | 693 } |
5234 | 694 } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { |
7620 | 695 serv_join_chat(((GaimChat *)node)->account->gc, ((GaimChat *)node)->components); |
5228 | 696 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
697 if (gtk_tree_view_row_expanded(tv, path)) | |
698 gtk_tree_view_collapse_row(tv, path); | |
699 else | |
700 gtk_tree_view_expand_row(tv,path,FALSE); | |
701 } | |
702 } | |
703 | |
5234 | 704 static void gaim_gtk_blist_add_chat_cb() |
705 { | |
706 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
707 GtkTreeIter iter; | |
708 GaimBlistNode *node; | |
709 | |
710 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ | |
711 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
7620 | 712 if (GAIM_BLIST_NODE_IS_BUDDY(node)) |
713 gaim_blist_request_add_chat(NULL, (GaimGroup*)node->parent->parent); | |
714 if (GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) | |
715 gaim_blist_request_add_chat(NULL, (GaimGroup*)node->parent); | |
5234 | 716 else if (GAIM_BLIST_NODE_IS_GROUP(node)) |
7620 | 717 gaim_blist_request_add_chat(NULL, (GaimGroup*)node); |
5234 | 718 } |
719 else { | |
7620 | 720 gaim_blist_request_add_chat(NULL, NULL); |
5234 | 721 } |
722 } | |
723 | |
5228 | 724 static void gaim_gtk_blist_add_buddy_cb() |
725 { | |
726 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
727 GtkTreeIter iter; | |
728 GaimBlistNode *node; | |
729 | |
730 if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ | |
731 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); | |
7620 | 732 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { |
733 gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node->parent->parent)->name, | |
734 NULL); | |
735 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) | |
736 || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
737 gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node->parent)->name, NULL); | |
738 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
739 gaim_blist_request_add_buddy(NULL, NULL, ((GaimGroup*)node)->name, NULL); | |
740 } | |
5228 | 741 } |
742 else { | |
7620 | 743 gaim_blist_request_add_buddy(NULL, NULL, NULL, NULL); |
744 } | |
745 } | |
746 | |
747 static void | |
748 gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) | |
749 { | |
750 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
751 show_confirm_del((GaimBuddy*)node); | |
752 } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { | |
753 show_confirm_del_blist_chat((GaimChat*)node); | |
754 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
755 show_confirm_del_group((GaimGroup*)node); | |
756 } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
757 show_confirm_del_contact((GaimContact*)node); | |
5228 | 758 } |
759 } | |
760 | |
761 static void | |
7620 | 762 gaim_gtk_blist_expand_contact_cb(GtkWidget *w, GaimBlistNode *node) |
5228 | 763 { |
7620 | 764 struct _gaim_gtk_blist_node *gtknode; |
765 GaimBlistNode *bnode; | |
766 | |
767 if(!GAIM_BLIST_NODE_IS_CONTACT(node)) | |
768 return; | |
769 | |
770 gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
771 | |
772 gtknode->contact_expanded = TRUE; | |
773 | |
774 for(bnode = node->child; bnode; bnode = bnode->next) { | |
775 gaim_gtk_blist_update(NULL, bnode); | |
776 } | |
777 gaim_gtk_blist_update(NULL, node); | |
778 } | |
779 | |
780 static void | |
781 gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node) | |
782 { | |
783 GaimBlistNode *bnode; | |
784 struct _gaim_gtk_blist_node *gtknode; | |
785 | |
786 if(!GAIM_BLIST_NODE_IS_CONTACT(node)) | |
787 return; | |
788 | |
789 gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
790 | |
791 gtknode->contact_expanded = FALSE; | |
792 | |
793 for(bnode = node->child; bnode; bnode = bnode->next) { | |
794 gaim_gtk_blist_update(NULL, bnode); | |
5228 | 795 } |
796 } | |
797 | |
7620 | 798 static void gaim_proto_menu_cb(GtkMenuItem *item, GaimBuddy *b) |
5228 | 799 { |
800 struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback"); | |
801 if (pbm->callback) | |
802 pbm->callback(pbm->gc, b->name); | |
803 } | |
804 | |
7620 | 805 static void make_buddy_menu(GtkWidget *menu, GaimPluginProtocolInfo *prpl_info, GaimBuddy *b) |
806 { | |
807 GList *list; | |
808 GtkWidget *menuitem; | |
809 | |
810 if (prpl_info && prpl_info->get_info) { | |
811 gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, | |
812 G_CALLBACK(gtk_blist_menu_info_cb), b, 0, 0, NULL); | |
813 } | |
814 gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, | |
815 G_CALLBACK(gtk_blist_menu_im_cb), b, 0, 0, NULL); | |
816 gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, | |
817 G_CALLBACK(gtk_blist_menu_bp_cb), b, 0, 0, NULL); | |
818 gaim_new_item_from_stock(menu, _("View _Log"), NULL, | |
819 G_CALLBACK(gtk_blist_menu_showlog_cb), b, 0, 0, NULL); | |
820 | |
821 if (prpl_info && prpl_info->buddy_menu) { | |
822 list = prpl_info->buddy_menu(b->account->gc, b->name); | |
823 while (list) { | |
824 struct proto_buddy_menu *pbm = list->data; | |
825 menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); | |
826 g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); | |
827 g_signal_connect(G_OBJECT(menuitem), "activate", | |
828 G_CALLBACK(gaim_proto_menu_cb), b); | |
829 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); | |
830 list = list->next; | |
831 } | |
832 } | |
833 | |
834 gaim_signal_emit(GAIM_GTK_BLIST(gaim_get_blist()), | |
835 "drawing-menu", menu, b); | |
836 | |
837 gaim_separator(menu); | |
838 gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, | |
839 G_CALLBACK(gtk_blist_menu_alias_cb), b, 0, 0, NULL); | |
840 gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, | |
841 G_CALLBACK(gaim_gtk_blist_remove_cb), b, | |
842 0, 0, NULL); | |
843 } | |
844 | |
845 static gboolean gtk_blist_key_press_cb(GtkWidget *tv, GdkEventKey *event, | |
846 gpointer null) | |
847 { | |
848 GaimBlistNode *node; | |
849 GValue val = { 0, }; | |
850 GtkTreeIter iter; | |
851 GtkTreeSelection *sel; | |
852 | |
853 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); | |
854 if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) | |
855 return FALSE; | |
856 | |
857 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
858 NODE_COLUMN, &val); | |
859 node = g_value_get_pointer(&val); | |
860 | |
861 if(event->state & GDK_CONTROL_MASK && | |
862 (event->keyval == 'o' || event->keyval == 'O')) { | |
863 GaimBuddy *buddy; | |
864 | |
865 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
866 buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
867 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
868 buddy = (GaimBuddy*)node; | |
869 } else { | |
870 return FALSE; | |
871 } | |
872 if(buddy) | |
873 serv_get_info(buddy->account->gc, buddy->name); | |
874 } | |
875 | |
876 return FALSE; | |
877 } | |
878 | |
5228 | 879 static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) |
880 { | |
881 GtkTreePath *path; | |
882 GaimBlistNode *node; | |
883 GValue val = { 0, }; | |
884 GtkTreeIter iter; | |
885 GtkWidget *menu, *menuitem; | |
886 GtkTreeSelection *sel; | |
887 GaimPlugin *prpl = NULL; | |
888 GaimPluginProtocolInfo *prpl_info = NULL; | |
7620 | 889 struct _gaim_gtk_blist_node *gtknode; |
890 gboolean handled = FALSE; | |
5228 | 891 |
892 /* Here we figure out which node was clicked */ | |
893 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) | |
894 return FALSE; | |
895 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
896 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
897 node = g_value_get_pointer(&val); | |
7620 | 898 gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; |
899 | |
900 if (GAIM_BLIST_NODE_IS_GROUP(node) && | |
901 event->button == 3 && event->type == GDK_BUTTON_PRESS) { | |
902 menu = gtk_menu_new(); | |
903 gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, | |
904 G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); | |
905 gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, | |
906 G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); | |
907 gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, | |
908 G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); | |
909 gaim_new_item_from_stock(menu, _("_Rename"), NULL, | |
910 G_CALLBACK(show_rename_group), node, 0, 0, NULL); | |
911 gtk_widget_show_all(menu); | |
912 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); | |
913 | |
914 handled = TRUE; | |
915 } else if (GAIM_BLIST_NODE_IS_CHAT(node) && | |
916 event->button == 3 && event->type == GDK_BUTTON_PRESS) { | |
917 GaimChat *chat = (GaimChat *)node; | |
918 const char *autojoin = gaim_chat_get_setting(chat, "gtk-autojoin"); | |
919 | |
920 menu = gtk_menu_new(); | |
921 gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, | |
922 G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); | |
923 gaim_new_check_item(menu, _("Auto-Join"), | |
924 G_CALLBACK(gtk_blist_menu_autojoin_cb), node, | |
925 (autojoin && !strcmp(autojoin, "true"))); | |
926 gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, | |
927 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); | |
928 gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, | |
929 G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); | |
930 gtk_widget_show_all(menu); | |
931 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); | |
932 | |
933 handled = TRUE; | |
934 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && | |
935 event->state & GDK_CONTROL_MASK && event->button == 2 && | |
936 event->type == GDK_BUTTON_PRESS) { | |
937 if(gtknode->contact_expanded) | |
938 gaim_gtk_blist_collapse_contact_cb(NULL, node); | |
939 else | |
940 gaim_gtk_blist_expand_contact_cb(NULL, node); | |
941 handled = TRUE; | |
942 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded | |
943 && event->button == 3 && event->type == GDK_BUTTON_PRESS) { | |
944 menu = gtk_menu_new(); | |
945 gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, | |
946 G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); | |
947 gaim_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, | |
948 G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), | |
949 node, 0, 0, NULL); | |
950 gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, | |
951 G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); | |
952 gtk_widget_show_all(menu); | |
953 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); | |
954 handled = TRUE; | |
955 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || | |
956 GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
957 GaimBuddy *b; | |
958 if(GAIM_BLIST_NODE_IS_CONTACT(node)) | |
959 b = gaim_contact_get_priority_buddy((GaimContact*)node); | |
960 else | |
961 b = (GaimBuddy *)node; | |
962 | |
5228 | 963 /* Protocol specific options */ |
7620 | 964 prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); |
5228 | 965 |
966 if (prpl != NULL) | |
967 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
968 | |
7620 | 969 if(event->button == 2 && event->type == GDK_2BUTTON_PRESS) { |
970 if (prpl && prpl_info->get_info) | |
971 serv_get_info(b->account->gc, b->name); | |
972 handled = TRUE; | |
973 } else if(event->button == 3 && event->type == GDK_BUTTON_PRESS) { | |
974 gboolean show_offline = gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies"); | |
975 menu = gtk_menu_new(); | |
976 make_buddy_menu(menu, prpl_info, b); | |
977 | |
978 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
979 gaim_separator(menu); | |
980 | |
981 if(gtknode->contact_expanded) { | |
982 gaim_new_item_from_stock(menu, _("_Collapse"), | |
983 GTK_STOCK_ZOOM_OUT, | |
984 G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), | |
985 node, 0, 0, NULL); | |
986 } else { | |
987 gaim_new_item_from_stock(menu, _("_Expand"), | |
988 GTK_STOCK_ZOOM_IN, | |
989 G_CALLBACK(gaim_gtk_blist_expand_contact_cb), node, | |
990 0, 0, NULL); | |
991 } | |
992 if(node->child->next) { | |
993 GaimBlistNode *bnode; | |
994 | |
995 for(bnode = node->child; bnode; bnode = bnode->next) { | |
996 GaimBuddy *buddy = (GaimBuddy*)bnode; | |
997 GtkWidget *submenu; | |
998 GtkWidget *image; | |
999 | |
1000 if(buddy == b) | |
1001 continue; | |
1002 if(!buddy->account->gc) | |
1003 continue; | |
1004 if(!show_offline && !GAIM_BUDDY_IS_ONLINE(buddy)) | |
1005 continue; | |
1006 | |
1007 | |
1008 menuitem = gtk_image_menu_item_new_with_label(buddy->name); | |
1009 image = gtk_image_new_from_pixbuf( | |
1010 gaim_gtk_blist_get_status_icon(bnode, | |
1011 GAIM_STATUS_ICON_SMALL)); | |
1012 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), | |
1013 image); | |
1014 gtk_widget_show(image); | |
1015 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); | |
1016 gtk_widget_show(menuitem); | |
1017 | |
1018 submenu = gtk_menu_new(); | |
1019 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); | |
1020 gtk_widget_show(submenu); | |
1021 | |
1022 prpl = gaim_find_prpl(gaim_account_get_protocol(buddy->account)); | |
1023 prpl_info = prpl ? GAIM_PLUGIN_PROTOCOL_INFO(prpl) : NULL; | |
1024 | |
1025 make_buddy_menu(submenu, prpl_info, buddy); | |
1026 } | |
1027 } | |
5228 | 1028 } |
7620 | 1029 |
1030 gtk_widget_show_all(menu); | |
1031 | |
1032 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, | |
1033 event->time); | |
1034 | |
1035 handled = TRUE; | |
5228 | 1036 } |
1037 } | |
1038 | |
1039 #if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal | |
1040 * the event propoagates down and somehow gets interpreted as the start of a drag event. */ | |
7620 | 1041 if(handled) { |
1042 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); | |
1043 gtk_tree_selection_select_path(sel, path); | |
1044 gtk_tree_path_free(path); | |
1045 return TRUE; | |
1046 } | |
5228 | 1047 #endif |
7620 | 1048 return FALSE; |
5228 | 1049 } |
1050 | |
1051 static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) | |
1052 { | |
7620 | 1053 gaim_prefs_set_bool("/gaim/gtk/blist/show_empty_groups", |
1054 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); | |
5228 | 1055 } |
1056 | |
1057 static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, | |
1058 GtkWidget *checkitem) { | |
1059 if(gtkblist->window->window) { | |
1060 GdkCursor *cursor = gdk_cursor_new(GDK_WATCH); | |
1061 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
1062 while (gtk_events_pending()) | |
1063 gtk_main_iteration(); | |
1064 gdk_cursor_unref(cursor); | |
1065 } | |
1066 | |
7620 | 1067 gaim_prefs_set_bool("/gaim/gtk/blist/show_offline_buddies", |
1068 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))); | |
5228 | 1069 |
1070 if(gtkblist->window->window) { | |
1071 GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); | |
1072 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
1073 gdk_cursor_unref(cursor); | |
1074 } | |
1075 } | |
1076 | |
1077 static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, | |
1078 GdkDragContext *dc, | |
1079 GtkSelectionData *data, | |
1080 guint info, | |
1081 guint time, | |
1082 gpointer *null) | |
1083 { | |
1084 if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { | |
1085 GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); | |
1086 GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); | |
1087 GtkTreeIter iter; | |
1088 GaimBlistNode *node = NULL; | |
1089 GValue val = {0}; | |
5273 | 1090 if(!sourcerow) |
1091 return; | |
5228 | 1092 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); |
1093 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
1094 node = g_value_get_pointer(&val); | |
1095 gtk_selection_data_set (data, | |
1096 gdk_atom_intern ("GAIM_BLIST_NODE", FALSE), | |
1097 8, /* bits */ | |
1098 (void*)&node, | |
1099 sizeof (node)); | |
5273 | 1100 |
5228 | 1101 gtk_tree_path_free(sourcerow); |
1102 } | |
5273 | 1103 |
5228 | 1104 } |
1105 | |
1106 static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, | |
1107 GtkSelectionData *sd, guint info, guint t) | |
7620 | 1108 { |
5228 | 1109 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { |
1110 GaimBlistNode *n = NULL; | |
1111 GtkTreePath *path = NULL; | |
1112 GtkTreeViewDropPosition position; | |
1113 memcpy(&n, sd->data, sizeof(n)); | |
1114 if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { | |
7636 | 1115 /* if we're here, I think it means the drop is ok */ |
1116 GtkTreeIter iter; | |
5228 | 1117 GaimBlistNode *node; |
1118 GValue val = {0}; | |
7620 | 1119 struct _gaim_gtk_blist_node *gtknode; |
1120 | |
1121 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), | |
1122 &iter, path); | |
1123 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), | |
1124 &iter, NODE_COLUMN, &val); | |
5228 | 1125 node = g_value_get_pointer(&val); |
7620 | 1126 gtknode = node->ui_data; |
1127 | |
1128 if (GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
1129 GaimContact *c = (GaimContact*)n; | |
1130 if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded) { | |
1131 gaim_blist_merge_contact(c, node); | |
1132 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1133 GAIM_BLIST_NODE_IS_CHAT(node)) { |
5228 | 1134 switch(position) { |
1135 case GTK_TREE_VIEW_DROP_AFTER: | |
1136 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
7620 | 1137 gaim_blist_add_contact(c, (GaimGroup*)node->parent, |
1138 node); | |
1139 break; | |
1140 case GTK_TREE_VIEW_DROP_BEFORE: | |
1141 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1142 gaim_blist_add_contact(c, (GaimGroup*)node->parent, | |
1143 node->prev); | |
1144 break; | |
1145 } | |
1146 } else if(GAIM_BLIST_NODE_IS_GROUP(node)) { | |
1147 gaim_blist_add_contact(c, (GaimGroup*)node, NULL); | |
1148 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1149 gaim_blist_merge_contact(c, node); | |
1150 } | |
1151 } else if (GAIM_BLIST_NODE_IS_BUDDY(n)) { | |
1152 GaimBuddy *b = (GaimBuddy*)n; | |
1153 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1154 switch(position) { | |
1155 case GTK_TREE_VIEW_DROP_AFTER: | |
1156 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1157 gaim_blist_add_buddy(b, (GaimContact*)node->parent, | |
1158 (GaimGroup*)node->parent->parent, node); | |
5228 | 1159 break; |
1160 case GTK_TREE_VIEW_DROP_BEFORE: | |
1161 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
7620 | 1162 gaim_blist_add_buddy(b, (GaimContact*)node->parent, |
1163 (GaimGroup*)node->parent->parent, | |
1164 node->prev); | |
5228 | 1165 break; |
1166 } | |
7620 | 1167 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { |
1168 gaim_blist_add_buddy(b, NULL, (GaimGroup*)node->parent, | |
1169 NULL); | |
5228 | 1170 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
7620 | 1171 gaim_blist_add_buddy(b, NULL, (GaimGroup*)node, NULL); |
1172 } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
1173 if(gtknode->contact_expanded) { | |
1174 switch(position) { | |
1175 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1176 case GTK_TREE_VIEW_DROP_AFTER: | |
1177 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1178 gaim_blist_add_buddy(b, (GaimContact*)node, | |
1179 (GaimGroup*)node->parent, NULL); | |
1180 break; | |
1181 case GTK_TREE_VIEW_DROP_BEFORE: | |
1182 gaim_blist_add_buddy(b, NULL, | |
1183 (GaimGroup*)node->parent, node->prev); | |
1184 break; | |
1185 } | |
1186 } else { | |
1187 switch(position) { | |
1188 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1189 case GTK_TREE_VIEW_DROP_AFTER: | |
1190 gaim_blist_add_buddy(b, NULL, | |
1191 (GaimGroup*)node->parent, NULL); | |
1192 break; | |
1193 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1194 case GTK_TREE_VIEW_DROP_BEFORE: | |
1195 gaim_blist_add_buddy(b, NULL, | |
1196 (GaimGroup*)node->parent, node->prev); | |
1197 break; | |
1198 } | |
1199 } | |
5228 | 1200 } |
5234 | 1201 } else if (GAIM_BLIST_NODE_IS_CHAT(n)) { |
7620 | 1202 GaimChat *chat = (GaimChat *)n; |
1203 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1204 switch(position) { | |
1205 case GTK_TREE_VIEW_DROP_AFTER: | |
1206 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1207 gaim_blist_add_chat(chat, | |
1208 (GaimGroup*)node->parent->parent, node); | |
1209 break; | |
1210 case GTK_TREE_VIEW_DROP_BEFORE: | |
1211 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1212 gaim_blist_add_chat(chat, | |
1213 (GaimGroup*)node->parent->parent, | |
1214 node->prev); | |
1215 break; | |
1216 } | |
1217 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1218 GAIM_BLIST_NODE_IS_CHAT(node)) { |
1219 switch(position) { | |
1220 case GTK_TREE_VIEW_DROP_AFTER: | |
1221 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
7620 | 1222 gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node); |
5234 | 1223 break; |
1224 case GTK_TREE_VIEW_DROP_BEFORE: | |
1225 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
7620 | 1226 gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node->prev); |
5234 | 1227 break; |
1228 } | |
1229 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
7620 | 1230 gaim_blist_add_chat(chat, (GaimGroup*)node, NULL); |
5234 | 1231 } |
5228 | 1232 } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { |
7620 | 1233 GaimGroup *g = (GaimGroup*)n; |
5228 | 1234 if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
1235 switch (position) { | |
1236 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1237 case GTK_TREE_VIEW_DROP_AFTER: | |
1238 gaim_blist_add_group(g, node); | |
1239 break; | |
1240 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1241 case GTK_TREE_VIEW_DROP_BEFORE: | |
1242 gaim_blist_add_group(g, node->prev); | |
1243 break; | |
1244 } | |
7620 | 1245 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { |
1246 gaim_blist_add_group(g, node->parent->parent); | |
1247 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1248 GAIM_BLIST_NODE_IS_CHAT(node)) { |
5228 | 1249 gaim_blist_add_group(g, node->parent); |
1250 } | |
1251 } | |
1252 | |
1253 gtk_tree_path_free(path); | |
7620 | 1254 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
1255 | |
5228 | 1256 gaim_blist_save(); |
1257 } | |
1258 } | |
1259 } | |
1260 | |
5234 | 1261 static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, GaimBlistNode *node) |
5228 | 1262 { |
1263 GtkStyle *style; | |
5234 | 1264 GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(node, GAIM_STATUS_ICON_LARGE); |
5228 | 1265 PangoLayout *layout; |
5234 | 1266 char *tooltiptext = gaim_get_tooltip_text(node); |
5228 | 1267 |
7620 | 1268 if(!tooltiptext) |
1269 return; | |
1270 | |
5228 | 1271 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); |
1272 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
1273 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
1274 pango_layout_set_width(layout, 300000); | |
1275 style = gtkblist->tipwindow->style; | |
1276 | |
1277 gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, | |
1278 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); | |
1279 | |
1280 #if GTK_CHECK_VERSION(2,2,0) | |
1281 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, | |
1282 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); | |
1283 #else | |
1284 gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); | |
1285 #endif | |
1286 | |
1287 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, | |
1288 NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); | |
1289 | |
1290 g_object_unref (pixbuf); | |
1291 g_object_unref (layout); | |
1292 g_free(tooltiptext); | |
7620 | 1293 |
1294 #if GTK_CHECK_VERSION(2,2,0) | |
1295 shadow_paint(gtkblist, NULL, EAST_SIDE); | |
1296 shadow_paint(gtkblist, NULL, SOUTH_SIDE); | |
1297 #endif | |
1298 | |
5228 | 1299 return; |
1300 } | |
1301 | |
1302 static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) | |
1303 { | |
1304 GtkTreePath *path; | |
1305 GtkTreeIter iter; | |
1306 GaimBlistNode *node; | |
1307 GValue val = {0}; | |
5234 | 1308 int scr_w,scr_h, w, h, x, y; |
1309 PangoLayout *layout; | |
7636 | 1310 gboolean tooltip_top = FALSE; |
5234 | 1311 char *tooltiptext = NULL; |
7636 | 1312 struct _gaim_gtk_blist_node *gtknode; |
7620 | 1313 #if GTK_CHECK_VERSION(2,2,0) |
1314 GdkWindowAttr attr; | |
1315 #endif | |
5228 | 1316 |
7636 | 1317 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y, &path, NULL, NULL, NULL)) |
5228 | 1318 return FALSE; |
1319 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
1320 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
1321 node = g_value_get_pointer(&val); | |
7620 | 1322 |
1323 if(!GAIM_BLIST_NODE_IS_CONTACT(node) && !GAIM_BLIST_NODE_IS_BUDDY(node) | |
1324 && !GAIM_BLIST_NODE_IS_CHAT(node)) | |
5234 | 1325 return FALSE; |
5228 | 1326 |
7636 | 1327 gtknode = node->ui_data; |
1328 | |
1329 if (node->child && node->child->next && GAIM_BLIST_NODE_IS_CONTACT(node) && !gtknode->contact_expanded) { | |
1330 gaim_gtk_blist_expand_contact_cb(NULL, node); | |
1331 tooltip_top = TRUE; /* When the person expands, the new screennames will be below. We'll draw the tip above | |
1332 the cursor so that the user can see the included buddies */ | |
1333 | |
1334 while (gtk_events_pending()) | |
1335 gtk_main_iteration(); | |
1336 | |
1337 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->contact_rect); | |
1338 gtkblist->mouseover_contact = node; | |
1339 gtk_tree_path_down (path); | |
1340 while (1) { | |
1341 GtkTreePath *path2; | |
1342 GdkRectangle rect; | |
1343 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect); | |
1344 gtkblist->contact_rect.height += rect.height; | |
1345 path2 = path; | |
1346 gtk_tree_path_next(path); | |
1347 if (path2 == path) { | |
1348 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path2, NULL, &rect); | |
1349 gtkblist->contact_rect.height += rect.height; | |
1350 break; | |
1351 } | |
1352 } | |
1353 } | |
1354 | |
1355 gtk_tree_path_free(path); | |
1356 | |
5234 | 1357 tooltiptext = gaim_get_tooltip_text(node); |
7620 | 1358 |
1359 if(!tooltiptext) | |
1360 return FALSE; | |
1361 | |
5234 | 1362 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); |
1363 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); | |
1364 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); | |
1365 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); | |
1366 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", | |
1367 G_CALLBACK(gaim_gtk_blist_paint_tip), node); | |
1368 gtk_widget_ensure_style (gtkblist->tipwindow); | |
7620 | 1369 |
1370 #if GTK_CHECK_VERSION(2,2,0) | |
1371 attr.window_type = GDK_WINDOW_TEMP; | |
1372 attr.override_redirect = TRUE; | |
1373 attr.x = gtkblist->tipwindow->allocation.x; | |
1374 attr.y = gtkblist->tipwindow->allocation.y; | |
1375 attr.width = gtkblist->tipwindow->allocation.width; | |
1376 attr.height = gtkblist->tipwindow->allocation.height; | |
1377 attr.wclass = GDK_INPUT_OUTPUT; | |
1378 attr.visual = gtk_widget_get_visual (gtkblist->window); | |
1379 attr.colormap = gtk_widget_get_colormap (gtkblist->window); | |
1380 | |
1381 attr.event_mask = gtk_widget_get_events (gtkblist->tipwindow); | |
1382 | |
1383 attr.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | | |
1384 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); | |
1385 gtkblist->east_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, | |
1386 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); | |
1387 gdk_window_set_user_data (gtkblist->east_shadow, gtkblist->tipwindow); | |
1388 gdk_window_set_back_pixmap (gtkblist->east_shadow, NULL, FALSE); | |
1389 | |
1390 gtkblist->south_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, | |
1391 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); | |
1392 gdk_window_set_user_data (gtkblist->south_shadow, gtkblist->tipwindow); | |
1393 gdk_window_set_back_pixmap (gtkblist->south_shadow, NULL, FALSE); | |
1394 #endif | |
1395 | |
5234 | 1396 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); |
1397 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
1398 pango_layout_set_width(layout, 300000); | |
1399 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
1400 scr_w = gdk_screen_width(); | |
1401 scr_h = gdk_screen_height(); | |
1402 pango_layout_get_size (layout, &w, &h); | |
1403 w = PANGO_PIXELS(w) + 8; | |
1404 h = PANGO_PIXELS(h) + 8; | |
5228 | 1405 |
5234 | 1406 /* 38 is the size of a large status icon plus 4 pixels padding on each side. |
1407 * I should #define this or something */ | |
1408 w = w + 38; | |
1409 h = MAX(h, 38); | |
1410 | |
1411 gdk_window_get_pointer(NULL, &x, &y, NULL); | |
1412 if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) | |
1413 y+=gtkblist->window->allocation.y; | |
1414 | |
1415 x -= ((w >> 1) + 4); | |
5228 | 1416 |
5234 | 1417 if ((x + w) > scr_w) |
7620 | 1418 x -= (x + w + 5) - scr_w; |
5234 | 1419 else if (x < 0) |
1420 x = 0; | |
5228 | 1421 |
7636 | 1422 if ((y + h + 4) > scr_h || tooltip_top) |
7620 | 1423 y = y - h - 5; |
5234 | 1424 else |
1425 y = y + 6; | |
7620 | 1426 |
5234 | 1427 g_object_unref (layout); |
1428 g_free(tooltiptext); | |
1429 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); | |
1430 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); | |
1431 gtk_widget_show(gtkblist->tipwindow); | |
5228 | 1432 |
7620 | 1433 #if GTK_CHECK_VERSION(2,2,0) |
1434 map_shadow_windows(gtkblist); | |
1435 #endif | |
1436 | |
5228 | 1437 return FALSE; |
1438 } | |
1439 | |
1440 static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) | |
1441 { | |
1442 GtkTreePath *path; | |
1443 if (gtkblist->timeout) { | |
7636 | 1444 if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) |
5228 | 1445 return FALSE; |
1446 /* We've left the cell. Remove the timeout and create a new one below */ | |
1447 if (gtkblist->tipwindow) { | |
1448 gtk_widget_destroy(gtkblist->tipwindow); | |
7620 | 1449 #if GTK_CHECK_VERSION(2,2,0) |
1450 gdk_window_set_user_data (gtkblist->east_shadow, NULL); | |
1451 gdk_window_destroy (gtkblist->east_shadow); | |
1452 gtkblist->east_shadow = NULL; | |
1453 | |
1454 gdk_window_set_user_data (gtkblist->south_shadow, NULL); | |
1455 gdk_window_destroy (gtkblist->south_shadow); | |
1456 gtkblist->south_shadow = NULL; | |
1457 #endif | |
5228 | 1458 gtkblist->tipwindow = NULL; |
1459 } | |
7620 | 1460 |
5228 | 1461 g_source_remove(gtkblist->timeout); |
1462 } | |
1463 | |
1464 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); | |
7636 | 1465 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); |
5228 | 1466 if (path) |
1467 gtk_tree_path_free(path); | |
1468 gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); | |
7636 | 1469 |
1470 if (gtkblist->mouseover_contact) { | |
1471 if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { | |
1472 gaim_gtk_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); | |
1473 gtkblist->mouseover_contact = NULL; | |
1474 } | |
1475 } | |
1476 | |
5228 | 1477 return FALSE; |
1478 } | |
1479 | |
1480 static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) | |
1481 { | |
1482 if (gtkblist->timeout) { | |
1483 g_source_remove(gtkblist->timeout); | |
1484 gtkblist->timeout = 0; | |
1485 } | |
1486 if (gtkblist->tipwindow) { | |
1487 gtk_widget_destroy(gtkblist->tipwindow); | |
7620 | 1488 #if GTK_CHECK_VERSION(2,2,0) |
1489 gdk_window_set_user_data (gtkblist->east_shadow, NULL); | |
1490 gdk_window_destroy (gtkblist->east_shadow); | |
1491 gtkblist->east_shadow = NULL; | |
1492 | |
1493 gdk_window_set_user_data (gtkblist->south_shadow, NULL); | |
1494 gdk_window_destroy (gtkblist->south_shadow); | |
1495 gtkblist->south_shadow = NULL; | |
1496 #endif | |
5228 | 1497 gtkblist->tipwindow = NULL; |
1498 } | |
7636 | 1499 |
1500 if (gtkblist->mouseover_contact) { | |
1501 gaim_gtk_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); | |
1502 gtkblist->mouseover_contact = NULL; | |
1503 } | |
5228 | 1504 } |
1505 | |
1506 static void | |
1507 toggle_debug(void) | |
1508 { | |
7620 | 1509 gaim_prefs_set_bool("/gaim/gtk/debug/enabled", |
1510 !gaim_prefs_get_bool("/gaim/gtk/debug/enabled")); | |
5228 | 1511 } |
1512 | |
1513 | |
1514 /*************************************************** | |
1515 * Crap * | |
1516 ***************************************************/ | |
1517 static GtkItemFactoryEntry blist_menu[] = | |
1518 { | |
1519 /* Buddies menu */ | |
1520 { N_("/_Buddies"), NULL, NULL, 0, "<Branch>" }, | |
1521 { N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0, "<StockItem>", GAIM_STOCK_IM }, | |
1522 { N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, "<StockItem>", GAIM_STOCK_CHAT }, | |
1523 { N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0, "<StockItem>", GAIM_STOCK_INFO }, | |
1524 { "/Buddies/sep1", NULL, NULL, 0, "<Separator>" }, | |
5398 | 1525 { N_("/Buddies/Show _Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, "<CheckItem>"}, |
5228 | 1526 { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"}, |
5307 | 1527 { N_("/Buddies/_Add a Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, |
1528 { N_("/Buddies/Add a C_hat..."), NULL, gaim_gtk_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD }, | |
7620 | 1529 { N_("/Buddies/Add a _Group..."), NULL, gaim_blist_request_add_group, 0, NULL}, |
5228 | 1530 { "/Buddies/sep2", NULL, NULL, 0, "<Separator>" }, |
7620 | 1531 { N_("/Buddies/_Signoff"), "<CTL>D", gaim_connections_disconnect_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF }, |
1532 { N_("/Buddies/_Quit"), "<CTL>Q", gaim_core_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, | |
5228 | 1533 |
1534 /* Tools */ | |
1535 { N_("/_Tools"), NULL, NULL, 0, "<Branch>" }, | |
1536 { N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" }, | |
1537 { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" }, | |
1538 { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" }, | |
1539 { "/Tools/sep1", NULL, NULL, 0, "<Separator>" }, | |
7620 | 1540 { N_("/Tools/A_ccounts"), "<CTL>A", gaim_gtk_accounts_window_show, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, |
5228 | 1541 { N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, |
7620 | 1542 { N_("/Tools/Preferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, |
1543 { N_("/Tools/Pr_ivacy"), NULL, gaim_gtk_privacy_dialog_show, 0, "<StockItem>", GAIM_STOCK_PRIVACY }, | |
5228 | 1544 { "/Tools/sep2", NULL, NULL, 0, "<Separator>" }, |
7620 | 1545 { N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, |
5228 | 1546 |
1547 /* Help */ | |
1548 { N_("/_Help"), NULL, NULL, 0, "<Branch>" }, | |
1549 { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, | |
7620 | 1550 { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, NULL }, |
1551 { N_("/Help/_About"), NULL, show_about, 0, "<StockItem>", GAIM_STOCK_ABOUT }, | |
5228 | 1552 }; |
1553 | |
1554 /********************************************************* | |
1555 * Private Utility functions * | |
1556 *********************************************************/ | |
7620 | 1557 static void |
1558 rename_group_cb(GaimGroup *g, const char *new_name) | |
1559 { | |
1560 gaim_blist_rename_group(g, new_name); | |
1561 gaim_blist_save(); | |
1562 } | |
1563 | |
1564 static void | |
1565 show_rename_group(GtkWidget *unused, GaimGroup *g) | |
1566 { | |
1567 gaim_request_input(NULL, _("Rename Group"), _("New group name"), | |
1568 _("Please enter a new name for the selected group."), | |
1569 g->name, FALSE, FALSE, | |
1570 _("OK"), G_CALLBACK(rename_group_cb), | |
1571 _("Cancel"), NULL, g); | |
1572 } | |
5228 | 1573 |
5234 | 1574 static char *gaim_get_tooltip_text(GaimBlistNode *node) |
5228 | 1575 { |
5237 | 1576 GaimPlugin *prpl; |
1577 GaimPluginProtocolInfo *prpl_info = NULL; | |
5228 | 1578 char *text = NULL; |
7636 | 1579 |
5234 | 1580 if(GAIM_BLIST_NODE_IS_CHAT(node)) { |
7620 | 1581 GaimChat *chat = (GaimChat *)node; |
5237 | 1582 char *name = NULL; |
5274 | 1583 struct proto_chat_entry *pce; |
1584 GList *parts, *tmp; | |
1585 GString *parts_text = g_string_new(""); | |
1586 | |
7620 | 1587 prpl = gaim_find_prpl(gaim_account_get_protocol(chat->account)); |
5274 | 1588 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); |
1589 | |
1590 parts = prpl_info->chat_info(chat->account->gc); | |
5237 | 1591 |
7620 | 1592 name = g_markup_escape_text(gaim_chat_get_name(chat), -1); |
1593 | |
1594 if(g_list_length(gaim_connections_get_all()) > 1) { | |
5274 | 1595 char *account = g_markup_escape_text(chat->account->username, -1); |
1596 g_string_append_printf(parts_text, _("\n<b>Account:</b> %s"), | |
1597 account); | |
1598 g_free(account); | |
5237 | 1599 } |
5274 | 1600 for(tmp = parts; tmp; tmp = tmp->next) { |
1601 char *label, *value; | |
1602 pce = tmp->data; | |
5237 | 1603 |
7620 | 1604 if(pce->secret) |
1605 continue; | |
1606 | |
5274 | 1607 label = g_markup_escape_text(pce->label, -1); |
1608 | |
1609 value = g_markup_escape_text(g_hash_table_lookup(chat->components, | |
1610 pce->identifier), -1); | |
1611 | |
1612 g_string_append_printf(parts_text, "\n<b>%s</b> %s", label, value); | |
1613 g_free(label); | |
1614 g_free(value); | |
1615 g_free(pce); | |
1616 } | |
1617 g_list_free(parts); | |
1618 | |
1619 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>%s", | |
1620 name, parts_text->str); | |
1621 g_string_free(parts_text, TRUE); | |
5237 | 1622 g_free(name); |
7620 | 1623 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || |
1624 GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1625 GaimBuddy *b; | |
5234 | 1626 char *statustext = NULL; |
7620 | 1627 char *contactaliastext = NULL; |
5234 | 1628 char *aliastext = NULL, *nicktext = NULL; |
1629 char *warning = NULL, *idletime = NULL; | |
5274 | 1630 char *accounttext = NULL; |
5228 | 1631 |
7620 | 1632 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { |
1633 GaimContact *contact = (GaimContact*)node; | |
1634 b = gaim_contact_get_priority_buddy(contact); | |
1635 if(contact->alias) | |
1636 contactaliastext = g_markup_escape_text(contact->alias, -1); | |
1637 } else { | |
1638 b = (GaimBuddy *)node; | |
1639 } | |
1640 | |
1641 prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); | |
5234 | 1642 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); |
1643 | |
7620 | 1644 if (prpl_info && prpl_info->tooltip_text) { |
5234 | 1645 const char *end; |
1646 statustext = prpl_info->tooltip_text(b); | |
5228 | 1647 |
5234 | 1648 if(statustext && !g_utf8_validate(statustext, -1, &end)) { |
1649 char *new = g_strndup(statustext, | |
1650 g_utf8_pointer_to_offset(statustext, end)); | |
1651 g_free(statustext); | |
1652 statustext = new; | |
1653 } | |
1654 } | |
1655 | |
1656 if (!statustext && !GAIM_BUDDY_IS_ONLINE(b)) | |
1657 statustext = g_strdup(_("<b>Status:</b> Offline")); | |
5228 | 1658 |
5341 | 1659 if (b->idle > 0) |
7620 | 1660 idletime = gaim_str_seconds_to_string(time(NULL) - b->idle); |
5228 | 1661 |
5234 | 1662 if(b->alias && b->alias[0]) |
1663 aliastext = g_markup_escape_text(b->alias, -1); | |
5228 | 1664 |
5234 | 1665 if(b->server_alias) |
1666 nicktext = g_markup_escape_text(b->server_alias, -1); | |
5228 | 1667 |
5234 | 1668 if (b->evil > 0) |
1669 warning = g_strdup_printf(_("%d%%"), b->evil); | |
5228 | 1670 |
7620 | 1671 if(g_list_length(gaim_connections_get_all()) > 1) |
5274 | 1672 accounttext = g_markup_escape_text(b->account->username, -1); |
1673 | |
5234 | 1674 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>" |
5438 | 1675 "%s %s" /* Account */ |
7620 | 1676 "%s %s" /* Contact Alias */ |
5438 | 1677 "%s %s" /* Alias */ |
1678 "%s %s" /* Nickname */ | |
1679 "%s %s" /* Idle */ | |
1680 "%s %s" /* Warning */ | |
1681 "%s%s" /* Status */ | |
1682 "%s", | |
1683 b->name, | |
1684 accounttext ? _("\n<b>Account:</b>") : "", accounttext ? accounttext : "", | |
7620 | 1685 contactaliastext ? _("\n<b>Contact Alias:</b>") : "", contactaliastext ? contactaliastext : "", |
5438 | 1686 aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "", |
1687 nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "", | |
1688 idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "", | |
1689 b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "", | |
1690 statustext ? "\n" : "", statustext ? statustext : "", | |
1691 !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : | |
7620 | 1692 !g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") : |
1693 !g_ascii_strcasecmp(b->name, "chipx86") ? _("\n<b>Status</b>: Rockin'") : ""); | |
5234 | 1694 |
1695 if(warning) | |
1696 g_free(warning); | |
1697 if(idletime) | |
1698 g_free(idletime); | |
1699 if(statustext) | |
1700 g_free(statustext); | |
1701 if(nicktext) | |
1702 g_free(nicktext); | |
1703 if(aliastext) | |
1704 g_free(aliastext); | |
5274 | 1705 if(accounttext) |
1706 g_free(accounttext); | |
5234 | 1707 } |
5228 | 1708 |
1709 return text; | |
1710 } | |
1711 | |
7620 | 1712 struct _emblem_data { |
1713 char *filename; | |
1714 int x; | |
1715 int y; | |
1716 }; | |
1717 | |
5234 | 1718 GdkPixbuf *gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size) |
5228 | 1719 { |
7620 | 1720 GdkPixbuf *scale, *status = NULL; |
1721 int i, scalesize = 30; | |
1722 char *filename; | |
5228 | 1723 const char *protoname = NULL; |
7620 | 1724 struct _gaim_gtk_blist_node *gtknode = node->ui_data; |
1725 struct _emblem_data emblems[4] = {{NULL, 15, 15}, {NULL, 0, 15}, | |
1726 {NULL, 0, 0}, {NULL, 15, 0}}; | |
1727 | |
1728 GaimBuddy *buddy = NULL; | |
1729 GaimChat *chat = NULL; | |
1730 | |
1731 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
1732 if(!gtknode->contact_expanded) | |
1733 buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
1734 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1735 buddy = (GaimBuddy*)node; | |
1736 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
1737 chat = (GaimChat*)node; | |
1738 } else { | |
5228 | 1739 return NULL; |
5234 | 1740 } |
1741 | |
7620 | 1742 if(buddy || chat) { |
1743 GaimAccount *account; | |
1744 GaimPlugin *prpl; | |
1745 GaimPluginProtocolInfo *prpl_info; | |
1746 | |
1747 if(buddy) | |
1748 account = buddy->account; | |
1749 else | |
1750 account = chat->account; | |
1751 | |
1752 prpl = gaim_find_prpl(gaim_account_get_protocol(account)); | |
1753 if(!prpl) | |
1754 return NULL; | |
1755 | |
1756 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
1757 | |
1758 if(prpl_info && prpl_info->list_icon) { | |
1759 protoname = prpl_info->list_icon(account, buddy); | |
1760 } | |
1761 if(prpl_info && prpl_info->list_emblems && buddy) { | |
1762 if(buddy->present != GAIM_BUDDY_SIGNING_OFF) | |
1763 prpl_info->list_emblems(buddy, &emblems[0].filename, | |
1764 &emblems[1].filename, &emblems[2].filename, | |
1765 &emblems[3].filename); | |
1766 } | |
5234 | 1767 } |
5228 | 1768 |
7620 | 1769 if(size == GAIM_STATUS_ICON_SMALL) { |
5228 | 1770 scalesize = 15; |
7620 | 1771 /* So that only the se icon will composite */ |
1772 emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL; | |
5228 | 1773 } |
1774 | |
7620 | 1775 if(buddy && buddy->present == GAIM_BUDDY_SIGNING_ON) { |
1776 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); | |
1777 } else if(buddy && buddy->present == GAIM_BUDDY_SIGNING_OFF) { | |
1778 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); | |
1779 } else if(buddy || chat) { | |
5228 | 1780 char *image = g_strdup_printf("%s.png", protoname); |
1781 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1782 g_free(image); | |
7620 | 1783 } else { |
1784 /* gaim dude */ | |
1785 filename = g_build_filename(DATADIR, "pixmaps", "gaim.png", NULL); | |
5228 | 1786 } |
7620 | 1787 |
1788 status = gdk_pixbuf_new_from_file(filename, NULL); | |
1789 g_free(filename); | |
1790 | |
1791 if(!status) | |
1792 return NULL; | |
1793 | |
1794 scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, | |
1795 GDK_INTERP_BILINEAR); | |
1796 g_object_unref(status); | |
1797 | |
1798 for(i=0; i<4; i++) { | |
1799 if(emblems[i].filename) { | |
1800 GdkPixbuf *emblem; | |
1801 char *image = g_strdup_printf("%s.png", emblems[i].filename); | |
1802 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1803 g_free(image); | |
1804 emblem = gdk_pixbuf_new_from_file(filename, NULL); | |
1805 g_free(filename); | |
1806 if(emblem) { | |
1807 if(i == 0 && size == GAIM_STATUS_ICON_SMALL) { | |
1808 gdk_pixbuf_composite(emblem, | |
1809 scale, 5, 5, | |
1810 10, 10, | |
1811 5, 5, | |
1812 .6, .6, | |
1813 GDK_INTERP_BILINEAR, | |
1814 255); | |
1815 } else { | |
1816 gdk_pixbuf_composite(emblem, | |
1817 scale, emblems[i].x, emblems[i].y, | |
1818 15, 15, | |
1819 emblems[i].x, emblems[i].y, | |
1820 1, 1, | |
1821 GDK_INTERP_BILINEAR, | |
1822 255); | |
1823 } | |
1824 g_object_unref(emblem); | |
1825 } | |
5228 | 1826 } |
1827 } | |
7620 | 1828 |
1829 if(buddy) { | |
1830 if(buddy->present == GAIM_BUDDY_OFFLINE) | |
1831 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); | |
1832 else if(buddy->idle && | |
1833 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) | |
1834 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); | |
5228 | 1835 } |
7620 | 1836 |
5228 | 1837 return scale; |
1838 } | |
1839 | |
7620 | 1840 static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(GaimBuddy *b) |
5228 | 1841 { |
7620 | 1842 const char *file; |
5228 | 1843 GdkPixbuf *buf, *ret; |
1844 | |
7620 | 1845 if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) |
5228 | 1846 return NULL; |
1847 | |
1848 if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL) | |
1849 return NULL; | |
1850 | |
1851 buf = gdk_pixbuf_new_from_file(file, NULL); | |
1852 | |
1853 | |
1854 if (buf) { | |
1855 if (!GAIM_BUDDY_IS_ONLINE(b)) | |
1856 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); | |
7620 | 1857 if (b->idle && gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) |
5228 | 1858 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); |
1859 | |
1860 ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); | |
1861 g_object_unref(G_OBJECT(buf)); | |
1862 return ret; | |
1863 } | |
1864 return NULL; | |
1865 } | |
1866 | |
7620 | 1867 static gchar *gaim_gtk_blist_get_name_markup(GaimBuddy *b, gboolean selected) |
5228 | 1868 { |
7620 | 1869 const char *name; |
1870 char *esc, *text = NULL; | |
5228 | 1871 GaimPlugin *prpl; |
1872 GaimPluginProtocolInfo *prpl_info = NULL; | |
7620 | 1873 GaimContact *contact; |
1874 struct _gaim_gtk_blist_node *gtkcontactnode = NULL; | |
5228 | 1875 int ihrs, imin; |
1876 char *idletime = NULL, *warning = NULL, *statustext = NULL; | |
1877 time_t t; | |
7620 | 1878 /* XXX Clean up this crap */ |
1879 | |
1880 contact = (GaimContact*)((GaimBlistNode*)b)->parent; | |
1881 if(contact) | |
1882 gtkcontactnode = ((GaimBlistNode*)contact)->ui_data; | |
1883 | |
1884 if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) | |
1885 name = contact->alias; | |
1886 else | |
1887 name = gaim_get_buddy_alias(b); | |
1888 esc = g_markup_escape_text(name, strlen(name)); | |
1889 | |
1890 prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); | |
5228 | 1891 |
1892 if (prpl != NULL) | |
1893 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
1894 | |
7620 | 1895 if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { |
1896 if ((b->idle && !selected && | |
1897 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) || | |
1898 !GAIM_BUDDY_IS_ONLINE(b)) { | |
1899 if (selected) | |
1900 text = g_strdup(esc); | |
1901 else | |
1902 text = g_strdup_printf("<span color='dim grey'>%s</span>", | |
1903 esc); | |
5228 | 1904 g_free(esc); |
1905 return text; | |
7620 | 1906 } |
1907 else { | |
5228 | 1908 return esc; |
1909 } | |
1910 } | |
1911 | |
1912 time(&t); | |
1913 ihrs = (t - b->idle) / 3600; | |
1914 imin = ((t - b->idle) / 60) % 60; | |
1915 | |
7620 | 1916 if (prpl && prpl_info->status_text && b->account->gc) { |
5228 | 1917 char *tmp = prpl_info->status_text(b); |
1918 const char *end; | |
1919 | |
1920 if(tmp && !g_utf8_validate(tmp, -1, &end)) { | |
1921 char *new = g_strndup(tmp, | |
1922 g_utf8_pointer_to_offset(tmp, end)); | |
1923 g_free(tmp); | |
1924 tmp = new; | |
1925 } | |
1926 | |
1927 if(tmp) { | |
1928 char buf[32]; | |
1929 char *c = tmp; | |
1930 int length = 0, vis=0; | |
1931 gboolean inside = FALSE; | |
1932 g_strdelimit(tmp, "\n", ' '); | |
7620 | 1933 gaim_str_strip_cr(tmp); |
5228 | 1934 |
1935 while(*c && vis < 20) { | |
1936 if(*c == '&') | |
1937 inside = TRUE; | |
1938 else if(*c == ';') | |
1939 inside = FALSE; | |
1940 if(!inside) | |
1941 vis++; | |
7620 | 1942 c = g_utf8_next_char(c); /* this is fun */ |
5228 | 1943 } |
1944 | |
7620 | 1945 length = c - tmp; |
1946 | |
5228 | 1947 if(vis == 20) |
1948 g_snprintf(buf, sizeof(buf), "%%.%ds...", length); | |
1949 else | |
1950 g_snprintf(buf, sizeof(buf), "%%s "); | |
1951 | |
1952 statustext = g_strdup_printf(buf, tmp); | |
1953 | |
1954 g_free(tmp); | |
1955 } | |
1956 } | |
1957 | |
7620 | 1958 if (b->idle > 0 && |
1959 gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")) { | |
5228 | 1960 if (ihrs) |
1961 idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); | |
1962 else | |
1963 idletime = g_strdup_printf(_("Idle (%dm) "), imin); | |
1964 } | |
1965 | |
7620 | 1966 if (b->evil > 0 && |
1967 gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")) | |
5228 | 1968 warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); |
1969 | |
1970 if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) | |
7620 | 1971 statustext = g_strdup(_("Offline ")); |
1972 | |
1973 if (b->idle && !selected && | |
1974 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) { | |
1975 | |
5228 | 1976 text = g_strdup_printf("<span color='dim grey'>%s</span>\n" |
1977 "<span color='dim grey' size='smaller'>%s%s%s</span>", | |
1978 esc, | |
1979 statustext != NULL ? statustext : "", | |
1980 idletime != NULL ? idletime : "", | |
1981 warning != NULL ? warning : ""); | |
7620 | 1982 } else if (statustext == NULL && idletime == NULL && warning == NULL && |
1983 GAIM_BUDDY_IS_ONLINE(b)) { | |
5228 | 1984 text = g_strdup(esc); |
1985 } else { | |
1986 text = g_strdup_printf("%s\n" | |
1987 "<span %s size='smaller'>%s%s%s</span>", esc, | |
1988 selected ? "" : "color='dim grey'", | |
1989 statustext != NULL ? statustext : "", | |
7620 | 1990 idletime != NULL ? idletime : "", |
5228 | 1991 warning != NULL ? warning : ""); |
1992 } | |
1993 if (idletime) | |
1994 g_free(idletime); | |
1995 if (warning) | |
1996 g_free(warning); | |
1997 if (statustext) | |
1998 g_free(statustext); | |
1999 if (esc) | |
2000 g_free(esc); | |
2001 | |
2002 return text; | |
2003 } | |
2004 | |
2005 static void gaim_gtk_blist_restore_position() | |
2006 { | |
7620 | 2007 int blist_x, blist_y, blist_width, blist_height; |
2008 | |
2009 blist_width = gaim_prefs_get_int("/gaim/gtk/blist/width"); | |
2010 | |
2011 /* if the window exists, is hidden, we're saving positions, and the | |
2012 * position is sane... */ | |
2013 if (gtkblist && gtkblist->window && | |
2014 !GTK_WIDGET_VISIBLE(gtkblist->window) && blist_width != 0) { | |
2015 | |
2016 blist_x = gaim_prefs_get_int("/gaim/gtk/blist/x"); | |
2017 blist_y = gaim_prefs_get_int("/gaim/gtk/blist/y"); | |
2018 blist_height = gaim_prefs_get_int("/gaim/gtk/blist/height"); | |
2019 | |
5228 | 2020 /* ...check position is on screen... */ |
7620 | 2021 if (blist_x >= gdk_screen_width()) |
2022 blist_x = gdk_screen_width() - 100; | |
2023 else if (blist_x + blist_width < 0) | |
2024 blist_x = 100; | |
2025 | |
2026 if (blist_y >= gdk_screen_height()) | |
2027 blist_y = gdk_screen_height() - 100; | |
2028 else if (blist_y + blist_height < 0) | |
2029 blist_y = 100; | |
2030 | |
5228 | 2031 /* ...and move it back. */ |
7620 | 2032 gtk_window_move(GTK_WINDOW(gtkblist->window), blist_x, blist_y); |
2033 gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_width, blist_height); | |
5228 | 2034 } |
2035 } | |
2036 | |
7620 | 2037 static gboolean gaim_gtk_blist_refresh_timer(GaimBuddyList *list) |
5228 | 2038 { |
7620 | 2039 GaimBlistNode *gnode, *cnode; |
2040 | |
2041 for(gnode = list->root; gnode; gnode = gnode->next) { | |
2042 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
5234 | 2043 continue; |
7620 | 2044 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
2045 if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2046 GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)cnode); | |
2047 if(buddy && buddy->idle) | |
2048 gaim_gtk_blist_update(list, cnode); | |
2049 } | |
5228 | 2050 } |
2051 } | |
2052 | |
2053 /* keep on going */ | |
2054 return TRUE; | |
2055 } | |
2056 | |
7620 | 2057 static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node) |
5260 | 2058 { |
2059 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2060 GtkTreeIter iter; | |
2061 | |
2062 if (!gtknode || !gtknode->row || !gtkblist) | |
2063 return; | |
2064 | |
2065 if(gtkblist->selected_node == node) | |
2066 gtkblist->selected_node = NULL; | |
2067 | |
2068 if (get_iter_from_node(node, &iter)) { | |
2069 gtk_tree_store_remove(gtkblist->treemodel, &iter); | |
7620 | 2070 if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node) |
2071 || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
5260 | 2072 gaim_gtk_blist_update(list, node->parent); |
2073 } | |
2074 } | |
2075 gtk_tree_row_reference_free(gtknode->row); | |
2076 gtknode->row = NULL; | |
2077 } | |
2078 | |
7620 | 2079 static void |
2080 signed_on_off_cb(GaimConnection *gc, GaimBuddyList *blist) | |
2081 { | |
2082 gaim_gtk_blist_update_protocol_actions(); | |
2083 } | |
2084 | |
2085 /* this is called on all sorts of signals, and we have no reason to pass | |
2086 * it anything, so it remains without arguments. If you need anything | |
2087 * more specific, do as below, and create another callback that calls | |
2088 * this */ | |
2089 static void | |
2090 raise_on_events_cb() | |
2091 { | |
2092 if(gtkblist && gtkblist->window && | |
2093 gaim_prefs_get_bool("/gaim/gtk/blist/raise_on_events")) { | |
2094 gtk_widget_show(gtkblist->window); | |
2095 gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); | |
2096 gdk_window_raise(gtkblist->window->window); | |
2097 } | |
2098 } | |
2099 | |
5260 | 2100 |
5228 | 2101 /********************************************************************************** |
2102 * Public API Functions * | |
2103 **********************************************************************************/ | |
7620 | 2104 static void gaim_gtk_blist_new_list(GaimBuddyList *blist) |
5228 | 2105 { |
7620 | 2106 GaimGtkBuddyList *gtkblist; |
2107 | |
2108 gtkblist = g_new0(GaimGtkBuddyList, 1); | |
2109 blist->ui_data = gtkblist; | |
2110 | |
2111 /* Setup some gaim signal handlers. */ | |
2112 gaim_signal_connect(gaim_connections_get_handle(), "signing-on", | |
2113 gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); | |
2114 gaim_signal_connect(gaim_connections_get_handle(), "signing-off", | |
2115 gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); | |
2116 | |
2117 /* Register some of our own. */ | |
2118 gaim_signal_register(gtkblist, "drawing-menu", | |
2119 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
2120 gaim_value_new(GAIM_TYPE_BOXED, "GtkMenu"), | |
2121 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2122 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2123 | |
2124 /* All of these signal handlers are for the "Raise on Events" option */ | |
2125 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", | |
2126 gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); | |
2127 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", | |
2128 gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); | |
5228 | 2129 } |
2130 | |
5256 | 2131 static void gaim_gtk_blist_new_node(GaimBlistNode *node) |
2132 { | |
2133 node->ui_data = g_new0(struct _gaim_gtk_blist_node, 1); | |
2134 } | |
2135 | |
5228 | 2136 void gaim_gtk_blist_update_columns() |
2137 { | |
2138 if(!gtkblist) | |
2139 return; | |
2140 | |
7620 | 2141 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { |
5228 | 2142 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); |
2143 gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); | |
2144 gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); | |
2145 } else { | |
7620 | 2146 gtk_tree_view_column_set_visible(gtkblist->idle_column, |
2147 gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")); | |
2148 gtk_tree_view_column_set_visible(gtkblist->warning_column, | |
2149 gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")); | |
5228 | 2150 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); |
2151 } | |
2152 } | |
2153 | |
7636 | 2154 enum {DRAG_BUDDY, DRAG_ROW}; |
5228 | 2155 |
2156 static char * | |
2157 item_factory_translate_func (const char *path, gpointer func_data) | |
2158 { | |
7620 | 2159 return _((char *)path); |
5228 | 2160 } |
2161 | |
5422 | 2162 void gaim_gtk_blist_setup_sort_methods() |
2163 { | |
7620 | 2164 gaim_gtk_blist_sort_method_reg("none", _("None"), sort_method_none); |
2165 #if GTK_CHECK_VERSION(2,2,1) | |
2166 gaim_gtk_blist_sort_method_reg("alphabetical", _("Alphabetical"), sort_method_alphabetical); | |
2167 gaim_gtk_blist_sort_method_reg("status", _("By status"), sort_method_status); | |
2168 gaim_gtk_blist_sort_method_reg("log_size", _("By log size"), sort_method_log); | |
2169 #endif | |
2170 gaim_gtk_blist_sort_method_set(gaim_prefs_get_string("/gaim/gtk/blist/sort_type")); | |
2171 } | |
2172 | |
2173 static void _prefs_change_redo_list() { | |
2174 redo_buddy_list(gaim_get_blist(), TRUE); | |
2175 } | |
2176 | |
2177 static void _prefs_change_sort_method(const char *pref_name, GaimPrefType type, | |
2178 gpointer val, gpointer data) { | |
2179 if(!strcmp(pref_name, "/gaim/gtk/blist/sort_type")) | |
2180 gaim_gtk_blist_sort_method_set(val); | |
2181 } | |
2182 | |
2183 static void gaim_gtk_blist_show(GaimBuddyList *list) | |
5228 | 2184 { |
2185 GtkCellRenderer *rend; | |
2186 GtkTreeViewColumn *column; | |
2187 GtkWidget *sw; | |
2188 GtkWidget *button; | |
2189 GtkSizeGroup *sg; | |
2190 GtkAccelGroup *accel_group; | |
2191 GtkTreeSelection *selection; | |
2192 GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, | |
7636 | 2193 {"application/x-im-contact", 0, DRAG_BUDDY}}; |
5228 | 2194 |
2195 if (gtkblist && gtkblist->window) { | |
2196 gtk_widget_show(gtkblist->window); | |
2197 return; | |
2198 } | |
2199 | |
2200 gtkblist = GAIM_GTK_BLIST(list); | |
2201 | |
2202 gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
2203 gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); | |
2204 gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); | |
2205 | |
2206 GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; | |
2207 | |
2208 gtkblist->vbox = gtk_vbox_new(FALSE, 0); | |
2209 gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox); | |
2210 | |
2211 g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); | |
2212 g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); | |
2213 g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); | |
2214 gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); | |
2215 | |
2216 /******************************* Menu bar *************************************/ | |
2217 accel_group = gtk_accel_group_new(); | |
2218 gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); | |
2219 g_object_unref(accel_group); | |
5427 | 2220 gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", accel_group); |
2221 gtk_item_factory_set_translate_func (gtkblist->ift, | |
5228 | 2222 item_factory_translate_func, |
2223 NULL, NULL); | |
5427 | 2224 gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), |
5228 | 2225 blist_menu, NULL); |
7620 | 2226 gaim_gtk_load_accels(); |
2227 g_signal_connect(G_OBJECT(accel_group), "accel-changed", | |
2228 G_CALLBACK(gaim_gtk_save_accels_cb), NULL); | |
5427 | 2229 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>"), FALSE, FALSE, 0); |
5228 | 2230 |
5427 | 2231 awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away")); |
5228 | 2232 do_away_menu(); |
2233 | |
5427 | 2234 gtkblist->bpmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounce")); |
5228 | 2235 gaim_gtkpounce_menu_build(gtkblist->bpmenu); |
2236 | |
5427 | 2237 protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Protocol Actions")); |
7620 | 2238 gaim_gtk_blist_update_protocol_actions(); |
5228 | 2239 /****************************** GtkTreeView **********************************/ |
2240 sw = gtk_scrolled_window_new(NULL,NULL); | |
2241 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); | |
2242 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2243 | |
7620 | 2244 gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, |
2245 GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, | |
2246 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); | |
5228 | 2247 |
2248 gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); | |
2249 gtk_widget_set_size_request(gtkblist->treeview, -1, 200); | |
2250 | |
2251 /* Set up selection stuff */ | |
2252 | |
2253 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
2254 g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL); | |
2255 | |
2256 | |
2257 /* Set up dnd */ | |
2258 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte, | |
2259 2, GDK_ACTION_COPY); | |
7620 | 2260 |
7636 | 2261 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, |
2262 GDK_ACTION_COPY | GDK_ACTION_MOVE); | |
2263 | |
2264 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); | |
5228 | 2265 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); |
2266 | |
2267 /* Tooltips */ | |
2268 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); | |
2269 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); | |
2270 | |
2271 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); | |
2272 | |
2273 column = gtk_tree_view_column_new (); | |
2274 | |
2275 rend = gtk_cell_renderer_pixbuf_new(); | |
2276 gtk_tree_view_column_pack_start (column, rend, FALSE); | |
7620 | 2277 gtk_tree_view_column_set_attributes (column, rend, |
5228 | 2278 "pixbuf", STATUS_ICON_COLUMN, |
2279 "visible", STATUS_ICON_VISIBLE_COLUMN, | |
2280 NULL); | |
2281 g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL); | |
2282 | |
2283 rend = gtk_cell_renderer_text_new(); | |
2284 gtk_tree_view_column_pack_start (column, rend, TRUE); | |
7620 | 2285 gtk_tree_view_column_set_attributes (column, rend, |
5228 | 2286 "markup", NAME_COLUMN, |
2287 NULL); | |
2288 g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); | |
2289 | |
2290 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); | |
2291 | |
2292 rend = gtk_cell_renderer_text_new(); | |
2293 gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL); | |
2294 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column); | |
2295 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2296 | |
2297 rend = gtk_cell_renderer_text_new(); | |
2298 gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL); | |
2299 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column); | |
2300 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2301 | |
2302 rend = gtk_cell_renderer_pixbuf_new(); | |
2303 gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL); | |
2304 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2305 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column); | |
2306 | |
2307 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); | |
2308 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); | |
2309 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); | |
2310 g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); | |
7620 | 2311 g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL); |
5228 | 2312 |
5419 | 2313 /* Enable CTRL+F searching */ |
2314 gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); | |
2315 | |
5228 | 2316 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); |
2317 gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); | |
2318 gaim_gtk_blist_update_columns(); | |
2319 | |
2320 /* set the Show Offline Buddies option. must be done | |
2321 * after the treeview or faceprint gets mad. -Robot101 | |
2322 */ | |
5427 | 2323 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))), |
7620 | 2324 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")); |
5427 | 2325 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))), |
7620 | 2326 gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups")); |
5228 | 2327 |
2328 /* OK... let's show this bad boy. */ | |
2329 gaim_gtk_blist_refresh(list); | |
2330 gaim_gtk_blist_restore_position(); | |
2331 gtk_widget_show_all(gtkblist->window); | |
2332 | |
2333 /**************************** Button Box **************************************/ | |
2334 /* add this afterwards so it doesn't force up the width of the window */ | |
2335 | |
2336 gtkblist->tooltips = gtk_tooltips_new(); | |
2337 | |
2338 sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); | |
2339 gtkblist->bbox = gtk_hbox_new(TRUE, 0); | |
2340 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0); | |
2341 gtk_widget_show(gtkblist->bbox); | |
2342 | |
2343 button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL); | |
2344 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2345 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2346 gtk_size_group_add_widget(sg, button); | |
2347 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), | |
2348 gtkblist->treeview); | |
2349 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL); | |
2350 gtk_widget_show(button); | |
2351 | |
2352 button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL); | |
2353 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2354 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2355 gtk_size_group_add_widget(sg, button); | |
2356 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), | |
2357 gtkblist->treeview); | |
2358 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL); | |
2359 gtk_widget_show(button); | |
2360 | |
2361 button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL); | |
2362 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2363 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2364 gtk_size_group_add_widget(sg, button); | |
5234 | 2365 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), gtkblist->treeview); |
5228 | 2366 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL); |
2367 gtk_widget_show(button); | |
2368 | |
2369 button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL); | |
2370 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2371 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2372 gtk_size_group_add_widget(sg, button); | |
2373 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL); | |
2374 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL); | |
2375 gtk_widget_show(button); | |
2376 | |
2377 /* this will show the right image/label widgets for us */ | |
2378 gaim_gtk_blist_update_toolbar(); | |
2379 | |
2380 /* start the refresh timer */ | |
7620 | 2381 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || |
2382 gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { | |
2383 | |
2384 gtkblist->refresh_timer = g_timeout_add(30000, | |
2385 (GSourceFunc)gaim_gtk_blist_refresh_timer, list); | |
2386 } | |
2387 | |
2388 /* attach prefs callbacks */ | |
2389 /* for the toolbar buttons */ | |
2390 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2391 GINT_TO_POINTER( | |
2392 gaim_prefs_connect_callback("/gaim/gtk/blist/button_style", | |
2393 gaim_gtk_blist_update_toolbar, NULL))); | |
2394 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2395 GINT_TO_POINTER( | |
2396 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buttons", | |
2397 gaim_gtk_blist_update_toolbar, NULL))); | |
2398 | |
2399 /* things that affect how buddies are displayed */ | |
2400 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2401 GINT_TO_POINTER( | |
2402 gaim_prefs_connect_callback("/gaim/gtk/blist/grey_idle_buddies", | |
2403 _prefs_change_redo_list, NULL))); | |
2404 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2405 GINT_TO_POINTER( | |
2406 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", | |
2407 _prefs_change_redo_list, NULL))); | |
2408 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2409 GINT_TO_POINTER( | |
2410 gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", | |
2411 _prefs_change_redo_list, NULL))); | |
2412 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2413 GINT_TO_POINTER( | |
2414 gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", | |
2415 _prefs_change_redo_list, NULL))); | |
2416 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2417 GINT_TO_POINTER( | |
2418 gaim_prefs_connect_callback("/gaim/gtk/blist/show_empty_groups", | |
2419 _prefs_change_redo_list, NULL))); | |
2420 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2421 GINT_TO_POINTER( | |
2422 gaim_prefs_connect_callback("/gaim/gtk/blist/show_group_count", | |
2423 _prefs_change_redo_list, NULL))); | |
2424 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2425 GINT_TO_POINTER( | |
2426 gaim_prefs_connect_callback("/gaim/gtk/blist/show_offline_buddies", | |
2427 _prefs_change_redo_list, NULL))); | |
2428 | |
2429 /* sorting */ | |
2430 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2431 GINT_TO_POINTER( | |
2432 gaim_prefs_connect_callback("/gaim/gtk/blist/sort_type", | |
2433 _prefs_change_sort_method, NULL))); | |
2434 | |
2435 /* things that affect what columns are displayed */ | |
2436 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2437 GINT_TO_POINTER( | |
2438 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", | |
2439 gaim_gtk_blist_update_columns, NULL))); | |
2440 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2441 GINT_TO_POINTER( | |
2442 gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", | |
2443 gaim_gtk_blist_update_columns, NULL))); | |
2444 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2445 GINT_TO_POINTER( | |
2446 gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", | |
2447 gaim_gtk_blist_update_columns, NULL))); | |
5228 | 2448 } |
2449 | |
7620 | 2450 /* XXX: does this need fixing? */ |
2451 static void redo_buddy_list(GaimBuddyList *list, gboolean remove) | |
5228 | 2452 { |
7620 | 2453 GaimBlistNode *gnode, *cnode, *bnode; |
2454 | |
2455 for(gnode = list->root; gnode; gnode = gnode->next) { | |
2456 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
5234 | 2457 continue; |
7620 | 2458 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
2459 if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2460 if(remove) | |
2461 gaim_gtk_blist_hide_node(list, cnode); | |
2462 | |
2463 for(bnode = cnode->child; bnode; bnode = bnode->next) { | |
2464 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
2465 continue; | |
2466 if(remove) | |
2467 gaim_gtk_blist_hide_node(list, bnode); | |
2468 gaim_gtk_blist_update(list, bnode); | |
2469 } | |
2470 | |
2471 gaim_gtk_blist_update(list, cnode); | |
2472 } else if(GAIM_BLIST_NODE_IS_CHAT(cnode)) { | |
2473 if(remove) | |
2474 gaim_gtk_blist_hide_node(list, cnode); | |
2475 | |
2476 gaim_gtk_blist_update(list, cnode); | |
2477 } | |
5228 | 2478 } |
7620 | 2479 gaim_gtk_blist_update(list, gnode); |
5228 | 2480 } |
2481 } | |
2482 | |
7620 | 2483 void gaim_gtk_blist_refresh(GaimBuddyList *list) |
5422 | 2484 { |
2485 redo_buddy_list(list, FALSE); | |
2486 } | |
2487 | |
5297 | 2488 void |
2489 gaim_gtk_blist_update_refresh_timeout() | |
2490 { | |
7620 | 2491 GaimBuddyList *blist; |
2492 GaimGtkBuddyList *gtkblist; | |
5297 | 2493 |
2494 blist = gaim_get_blist(); | |
2495 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
2496 | |
7620 | 2497 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || |
2498 gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { | |
2499 | |
2500 gtkblist->refresh_timer = g_timeout_add(30000, | |
2501 (GSourceFunc)gaim_gtk_blist_refresh_timer, blist); | |
5297 | 2502 } else { |
2503 g_source_remove(gtkblist->refresh_timer); | |
2504 gtkblist->refresh_timer = 0; | |
2505 } | |
2506 } | |
2507 | |
5256 | 2508 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) { |
2509 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2510 GtkTreePath *path; | |
5228 | 2511 |
7620 | 2512 /* XXX: why do we assume we have a buddy here? */ |
5263 | 2513 if (!gtknode) { |
7620 | 2514 #if 0 |
2515 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "buddy %s has no ui_data\n", ((GaimBuddy *)node)->name); | |
2516 #endif | |
5263 | 2517 return FALSE; |
2518 } | |
2519 | |
2520 if (!gtkblist) { | |
2521 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n"); | |
2522 return FALSE; | |
2523 } | |
2524 | |
2525 if (!gtknode->row) | |
5228 | 2526 return FALSE; |
2527 | |
5256 | 2528 if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL) |
5228 | 2529 return FALSE; |
5256 | 2530 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) { |
2531 gtk_tree_path_free(path); | |
2532 return FALSE; | |
2533 } | |
2534 gtk_tree_path_free(path); | |
2535 return TRUE; | |
5228 | 2536 } |
2537 | |
7620 | 2538 static void |
2539 gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) | |
2540 { | |
2541 GaimButtonStyle style = gaim_prefs_get_int("/gaim/gtk/blist/button_style"); | |
2542 | |
5228 | 2543 if (GTK_IS_IMAGE(widget)) { |
7620 | 2544 if (style == GAIM_BUTTON_IMAGE || style == GAIM_BUTTON_TEXT_IMAGE) |
5228 | 2545 gtk_widget_show(widget); |
2546 else | |
2547 gtk_widget_hide(widget); | |
7620 | 2548 } |
2549 else if (GTK_IS_LABEL(widget)) { | |
2550 if (style == GAIM_BUTTON_IMAGE) | |
5228 | 2551 gtk_widget_hide(widget); |
2552 else | |
2553 gtk_widget_show(widget); | |
7620 | 2554 } |
2555 else if (GTK_IS_CONTAINER(widget)) { | |
2556 gtk_container_foreach(GTK_CONTAINER(widget), | |
2557 gaim_gtk_blist_update_toolbar_icons, NULL); | |
5228 | 2558 } |
2559 } | |
2560 | |
2561 void gaim_gtk_blist_update_toolbar() { | |
2562 if (!gtkblist) | |
2563 return; | |
2564 | |
7620 | 2565 if (gaim_prefs_get_int("/gaim/gtk/blist/button_style") == GAIM_BUTTON_NONE) |
5228 | 2566 gtk_widget_hide(gtkblist->bbox); |
2567 else { | |
7620 | 2568 gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), |
2569 gaim_gtk_blist_update_toolbar_icons, NULL); | |
5228 | 2570 gtk_widget_show(gtkblist->bbox); |
2571 } | |
2572 } | |
2573 | |
7620 | 2574 static void gaim_gtk_blist_remove(GaimBuddyList *list, GaimBlistNode *node) |
5228 | 2575 { |
5260 | 2576 gaim_gtk_blist_hide_node(list, node); |
5228 | 2577 |
7620 | 2578 if(node->parent) |
2579 gaim_gtk_blist_update(list, node->parent); | |
2580 | |
5263 | 2581 /* There's something I don't understand here */ |
2582 /* g_free(node->ui_data); | |
2583 node->ui_data = NULL; */ | |
5228 | 2584 } |
2585 | |
2586 static gboolean do_selection_changed(GaimBlistNode *new_selection) | |
2587 { | |
5254 | 2588 GaimBlistNode *old_selection = NULL; |
5228 | 2589 |
5254 | 2590 /* test for gtkblist because crazy timeout means we can be called after the blist is gone */ |
2591 if (gtkblist && new_selection != gtkblist->selected_node) { | |
2592 old_selection = gtkblist->selected_node; | |
5228 | 2593 gtkblist->selected_node = new_selection; |
2594 if(new_selection) | |
2595 gaim_gtk_blist_update(NULL, new_selection); | |
2596 if(old_selection) | |
2597 gaim_gtk_blist_update(NULL, old_selection); | |
2598 } | |
2599 | |
2600 return FALSE; | |
2601 } | |
2602 | |
2603 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data) | |
2604 { | |
2605 GaimBlistNode *new_selection = NULL; | |
2606 GtkTreeIter iter; | |
2607 | |
2608 if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ | |
2609 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
2610 NODE_COLUMN, &new_selection, -1); | |
2611 } | |
5254 | 2612 |
5228 | 2613 /* we set this up as a timeout, otherwise the blist flickers */ |
2614 g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); | |
2615 } | |
2616 | |
7620 | 2617 static void insert_node(GaimBuddyList *list, GaimBlistNode *node, GtkTreeIter *iter) |
2618 { | |
2619 GtkTreeIter parent_iter, cur, *curptr = NULL; | |
2620 struct _gaim_gtk_blist_node *gtknode = node->ui_data; | |
5256 | 2621 GtkTreePath *newpath; |
7620 | 2622 |
2623 if(!gtknode || !iter) | |
2624 return; | |
2625 | |
2626 if(node->parent && !get_iter_from_node(node->parent, &parent_iter)) | |
2627 return; | |
2628 | |
2629 if(get_iter_from_node(node, &cur)) | |
2630 curptr = &cur; | |
2631 | |
2632 if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
2633 *iter = current_sort_method->func(node, list, parent_iter, curptr); | |
2634 } else { | |
2635 *iter = sort_method_none(node, list, parent_iter, curptr); | |
5228 | 2636 } |
2637 | |
7620 | 2638 gtk_tree_row_reference_free(gtknode->row); |
2639 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), | |
2640 iter); | |
2641 gtknode->row = | |
2642 gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), | |
2643 newpath); | |
5256 | 2644 gtk_tree_path_free(newpath); |
2645 | |
5228 | 2646 gtk_tree_store_set(gtkblist->treemodel, iter, |
2647 NODE_COLUMN, node, | |
2648 -1); | |
7620 | 2649 |
2650 if(node->parent) { | |
2651 GtkTreePath *expand = NULL; | |
2652 struct _gaim_gtk_blist_node *gtkparentnode = node->parent->ui_data; | |
2653 | |
2654 if(GAIM_BLIST_NODE_IS_GROUP(node->parent)) { | |
2655 if(!gaim_group_get_setting((GaimGroup*)node->parent, "collapsed")) | |
2656 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); | |
2657 } else if(GAIM_BLIST_NODE_IS_CONTACT(node->parent) && | |
2658 gtkparentnode->contact_expanded) { | |
2659 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); | |
2660 } | |
2661 if(expand) { | |
2662 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, | |
2663 FALSE); | |
2664 gtk_tree_path_free(expand); | |
2665 } | |
2666 } | |
2667 | |
5228 | 2668 } |
2669 | |
7620 | 2670 static void gaim_gtk_blist_update_group(GaimBuddyList *list, GaimBlistNode *node) |
2671 { | |
2672 GaimGroup *group; | |
2673 | |
2674 g_return_if_fail(GAIM_BLIST_NODE_IS_GROUP(node)); | |
2675 | |
2676 group = (GaimGroup*)node; | |
2677 | |
2678 if(gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups") || | |
2679 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies") || | |
2680 gaim_blist_get_group_online_count(group) > 0) { | |
2681 char *mark, *esc; | |
2682 GtkTreeIter iter; | |
2683 | |
2684 insert_node(list, node, &iter); | |
2685 | |
2686 esc = g_markup_escape_text(group->name, -1); | |
2687 if(gaim_prefs_get_bool("/gaim/gtk/blist/show_group_count")) { | |
2688 mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", | |
2689 esc, gaim_blist_get_group_online_count(group), | |
2690 gaim_blist_get_group_size(group, FALSE)); | |
2691 } else { | |
2692 mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); | |
2693 } | |
2694 g_free(esc); | |
2695 | |
2696 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
2697 STATUS_ICON_COLUMN, NULL, | |
2698 STATUS_ICON_VISIBLE_COLUMN, FALSE, | |
2699 NAME_COLUMN, mark, | |
2700 NODE_COLUMN, node, | |
2701 -1); | |
2702 g_free(mark); | |
2703 } else { | |
2704 gaim_gtk_blist_hide_node(list, node); | |
2705 } | |
2706 } | |
2707 | |
2708 static void buddy_node(GaimBuddy *buddy, GtkTreeIter *iter, GaimBlistNode *node) | |
5228 | 2709 { |
7620 | 2710 GdkPixbuf *status, *avatar; |
2711 char *mark; | |
2712 char *warning = NULL, *idle = NULL; | |
2713 | |
2714 gboolean selected = (gtkblist->selected_node == node); | |
2715 | |
2716 status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy, | |
2717 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") | |
2718 ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
2719 | |
2720 avatar = gaim_gtk_blist_get_buddy_icon(buddy); | |
2721 mark = gaim_gtk_blist_get_name_markup(buddy, selected); | |
2722 | |
2723 if (buddy->idle > 0) { | |
2724 time_t t; | |
2725 int ihrs, imin; | |
2726 time(&t); | |
2727 ihrs = (t - buddy->idle) / 3600; | |
2728 imin = ((t - buddy->idle) / 60) % 60; | |
2729 if(ihrs > 0) | |
2730 idle = g_strdup_printf("(%d:%02d)", ihrs, imin); | |
2731 else | |
2732 idle = g_strdup_printf("(%d)", imin); | |
2733 } | |
2734 | |
2735 if (buddy->evil > 0) | |
2736 warning = g_strdup_printf("%d%%", buddy->evil); | |
2737 | |
2738 if (gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies") && | |
2739 buddy->idle) { | |
2740 | |
2741 if(warning && !selected) { | |
2742 char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
2743 warning); | |
2744 g_free(warning); | |
2745 warning = w2; | |
2746 } | |
2747 | |
2748 if(idle && !selected) { | |
2749 char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
2750 idle); | |
2751 g_free(idle); | |
2752 idle = i2; | |
5228 | 2753 } |
7620 | 2754 } |
2755 | |
2756 gtk_tree_store_set(gtkblist->treemodel, iter, | |
2757 STATUS_ICON_COLUMN, status, | |
2758 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
2759 NAME_COLUMN, mark, | |
2760 WARNING_COLUMN, warning, | |
2761 IDLE_COLUMN, idle, | |
2762 BUDDY_ICON_COLUMN, avatar, | |
2763 -1); | |
2764 | |
2765 g_free(mark); | |
2766 if(idle) | |
2767 g_free(idle); | |
2768 if(warning) | |
2769 g_free(warning); | |
2770 if(status) | |
2771 g_object_unref(status); | |
2772 if(avatar) | |
2773 g_object_unref(avatar); | |
2774 } | |
2775 | |
2776 static void gaim_gtk_blist_update_contact(GaimBuddyList *list, GaimBlistNode *node) | |
2777 { | |
2778 GaimContact *contact; | |
2779 GaimBuddy *buddy; | |
2780 struct _gaim_gtk_blist_node *gtknode; | |
2781 | |
2782 g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); | |
2783 | |
2784 /* First things first, update the group */ | |
2785 gaim_gtk_blist_update_group(list, node->parent); | |
2786 | |
2787 gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2788 contact = (GaimContact*)node; | |
2789 buddy = gaim_contact_get_priority_buddy(contact); | |
2790 | |
2791 if(buddy && (buddy->present != GAIM_BUDDY_OFFLINE || | |
2792 (gaim_account_is_connected(buddy->account) && | |
2793 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { | |
2794 GtkTreeIter iter; | |
2795 | |
2796 insert_node(list, node, &iter); | |
2797 | |
2798 if(gtknode->contact_expanded) { | |
2799 GdkPixbuf *status; | |
5228 | 2800 char *mark; |
2801 | |
7620 | 2802 status = gaim_gtk_blist_get_status_icon(node, |
2803 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? | |
2804 GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
2805 | |
2806 mark = g_markup_escape_text(gaim_contact_get_alias(contact), -1); | |
2807 | |
5228 | 2808 gtk_tree_store_set(gtkblist->treemodel, &iter, |
7620 | 2809 STATUS_ICON_COLUMN, status, |
2810 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
5228 | 2811 NAME_COLUMN, mark, |
7620 | 2812 WARNING_COLUMN, NULL, |
2813 IDLE_COLUMN, NULL, | |
2814 BUDDY_ICON_COLUMN, NULL, | |
5228 | 2815 -1); |
2816 g_free(mark); | |
7620 | 2817 if(status) |
2818 g_object_unref(status); | |
2819 } else { | |
2820 buddy_node(buddy, &iter, node); | |
5228 | 2821 } |
7620 | 2822 } else { |
2823 gaim_gtk_blist_hide_node(list, node); | |
5228 | 2824 } |
7620 | 2825 } |
2826 | |
2827 static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node) | |
2828 { | |
2829 GaimContact *contact; | |
2830 GaimBuddy *buddy; | |
2831 struct _gaim_gtk_blist_node *gtkparentnode; | |
2832 | |
2833 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
2834 | |
2835 buddy = (GaimBuddy*)node; | |
2836 contact = (GaimContact*)node->parent; | |
2837 gtkparentnode = (struct _gaim_gtk_blist_node *)node->parent->ui_data; | |
2838 | |
2839 /* First things first, update the contact */ | |
2840 gaim_gtk_blist_update_contact(list, node->parent); | |
2841 | |
2842 if(gtkparentnode->contact_expanded && | |
2843 (buddy->present != GAIM_BUDDY_OFFLINE || | |
2844 (gaim_account_is_connected(buddy->account) && | |
2845 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { | |
2846 GtkTreeIter iter; | |
2847 | |
2848 insert_node(list, node, &iter); | |
2849 buddy_node(buddy, &iter, node); | |
2850 | |
2851 } else { | |
2852 gaim_gtk_blist_hide_node(list, node); | |
2853 } | |
2854 | |
2855 } | |
2856 | |
2857 static void gaim_gtk_blist_update_chat(GaimBuddyList *list, GaimBlistNode *node) | |
2858 { | |
2859 GaimChat *chat; | |
2860 | |
2861 g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node)); | |
2862 | |
2863 /* First things first, update the group */ | |
2864 gaim_gtk_blist_update_group(list, node->parent); | |
2865 | |
2866 chat = (GaimChat*)node; | |
2867 | |
2868 if(gaim_account_is_connected(chat->account)) { | |
2869 GtkTreeIter iter; | |
5234 | 2870 GdkPixbuf *status; |
7620 | 2871 char *mark; |
2872 | |
2873 insert_node(list, node, &iter); | |
5234 | 2874 |
2875 status = gaim_gtk_blist_get_status_icon(node, | |
7620 | 2876 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? |
2877 GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
2878 | |
2879 mark = g_markup_escape_text(gaim_chat_get_name(chat), -1); | |
5234 | 2880 |
2881 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
7620 | 2882 STATUS_ICON_COLUMN, status, |
2883 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
2884 NAME_COLUMN, mark, | |
2885 -1); | |
5228 | 2886 |
2887 g_free(mark); | |
7620 | 2888 if(status) |
5228 | 2889 g_object_unref(status); |
7620 | 2890 } else { |
5260 | 2891 gaim_gtk_blist_hide_node(list, node); |
5228 | 2892 } |
7620 | 2893 } |
2894 | |
2895 static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node) | |
2896 { | |
2897 if(!gtkblist) | |
2898 return; | |
2899 | |
2900 switch(node->type) { | |
2901 case GAIM_BLIST_GROUP_NODE: | |
2902 gaim_gtk_blist_update_group(list, node); | |
2903 break; | |
2904 case GAIM_BLIST_CONTACT_NODE: | |
2905 gaim_gtk_blist_update_contact(list, node); | |
2906 break; | |
2907 case GAIM_BLIST_BUDDY_NODE: | |
2908 gaim_gtk_blist_update_buddy(list, node); | |
2909 break; | |
2910 case GAIM_BLIST_CHAT_NODE: | |
2911 gaim_gtk_blist_update_chat(list, node); | |
2912 break; | |
2913 case GAIM_BLIST_OTHER_NODE: | |
2914 return; | |
2915 } | |
5234 | 2916 |
5228 | 2917 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); |
2918 } | |
2919 | |
7620 | 2920 |
2921 static void gaim_gtk_blist_destroy(GaimBuddyList *list) | |
5228 | 2922 { |
2923 if (!gtkblist) | |
2924 return; | |
2925 | |
2926 gtk_widget_destroy(gtkblist->window); | |
7620 | 2927 |
2928 if (gtkblist->tipwindow) | |
2929 gtk_widget_destroy(gtkblist->tipwindow); | |
2930 | |
5228 | 2931 gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); |
2932 | |
2933 if (gtkblist->refresh_timer) | |
2934 g_source_remove(gtkblist->refresh_timer); | |
2935 if (gtkblist->timeout) | |
2936 g_source_remove(gtkblist->timeout); | |
2937 | |
2938 gtkblist->refresh_timer = 0; | |
2939 gtkblist->timeout = 0; | |
2940 gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; | |
2941 gtkblist->treemodel = NULL; | |
2942 gtkblist->idle_column = NULL; | |
2943 gtkblist->warning_column = gtkblist->buddy_icon_column = NULL; | |
2944 gtkblist->bbox = gtkblist->tipwindow = NULL; | |
5427 | 2945 g_object_unref(G_OBJECT(gtkblist->ift)); |
5228 | 2946 protomenu = NULL; |
2947 awaymenu = NULL; | |
2948 gtkblist = NULL; | |
7620 | 2949 |
2950 while(blist_prefs_callbacks) { | |
2951 gaim_prefs_disconnect_callback(GPOINTER_TO_INT(blist_prefs_callbacks->data)); | |
2952 blist_prefs_callbacks = g_slist_remove(blist_prefs_callbacks, blist_prefs_callbacks->data); | |
2953 } | |
5228 | 2954 } |
2955 | |
7620 | 2956 static void gaim_gtk_blist_set_visible(GaimBuddyList *list, gboolean show) |
5228 | 2957 { |
2958 if (!(gtkblist && gtkblist->window)) | |
2959 return; | |
2960 | |
2961 if (show) { | |
2962 gaim_gtk_blist_restore_position(); | |
2963 gtk_window_present(GTK_WINDOW(gtkblist->window)); | |
2964 } else { | |
7620 | 2965 if (!gaim_connections_get_all() || docklet_count) { |
5228 | 2966 #ifdef _WIN32 |
2967 wgaim_systray_minimize(gtkblist->window); | |
2968 #endif | |
2969 gtk_widget_hide(gtkblist->window); | |
2970 } else { | |
2971 gtk_window_iconify(GTK_WINDOW(gtkblist->window)); | |
2972 } | |
2973 } | |
2974 } | |
2975 | |
7620 | 2976 static GList * |
2977 groups_tree(void) | |
2978 { | |
2979 GList *tmp = NULL; | |
2980 char *tmp2; | |
2981 GaimGroup *g; | |
2982 GaimBlistNode *gnode; | |
2983 | |
2984 if (gaim_get_blist()->root == NULL) | |
2985 { | |
2986 tmp2 = g_strdup(_("Buddies")); | |
2987 tmp = g_list_append(tmp, tmp2); | |
2988 } | |
2989 else | |
2990 { | |
2991 for (gnode = gaim_get_blist()->root; | |
2992 gnode != NULL; | |
2993 gnode = gnode->next) | |
2994 { | |
2995 if (GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
2996 { | |
2997 g = (GaimGroup *)gnode; | |
2998 tmp2 = g->name; | |
2999 tmp = g_list_append(tmp, tmp2); | |
3000 } | |
3001 } | |
3002 } | |
3003 | |
3004 return tmp; | |
3005 } | |
3006 | |
3007 static void | |
3008 add_buddy_select_account_cb(GObject *w, GaimAccount *account, | |
3009 GaimGtkAddBuddyData *data) | |
3010 { | |
3011 /* Save our account */ | |
3012 data->account = account; | |
3013 } | |
3014 | |
3015 static void | |
3016 destroy_add_buddy_dialog_cb(GtkWidget *win, GaimGtkAddBuddyData *data) | |
3017 { | |
3018 g_free(data); | |
3019 } | |
3020 | |
3021 static void | |
3022 add_buddy_cb(GtkWidget *w, int resp, GaimGtkAddBuddyData *data) | |
3023 { | |
3024 const char *grp, *who, *whoalias; | |
3025 GaimConversation *c; | |
3026 GaimBuddy *b; | |
3027 GaimGroup *g; | |
3028 | |
3029 if (resp == GTK_RESPONSE_OK) | |
3030 { | |
3031 who = gtk_entry_get_text(GTK_ENTRY(data->entry)); | |
3032 grp = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry)); | |
3033 whoalias = gtk_entry_get_text(GTK_ENTRY(data->entry_for_alias)); | |
3034 | |
3035 c = gaim_find_conversation_with_account(who, data->account); | |
3036 | |
3037 if ((g = gaim_find_group(grp)) == NULL) | |
3038 { | |
3039 g = gaim_group_new(grp); | |
3040 gaim_blist_add_group(g, NULL); | |
3041 } | |
3042 | |
3043 b = gaim_buddy_new(data->account, who, whoalias); | |
3044 gaim_blist_add_buddy(b, NULL, g, NULL); | |
3045 serv_add_buddy(gaim_account_get_connection(data->account), who, g); | |
3046 | |
3047 if (c != NULL) { | |
3048 gaim_buddy_icon_update(gaim_conv_im_get_icon(GAIM_CONV_IM(c))); | |
3049 gaim_conversation_update(c, GAIM_CONV_UPDATE_ADD); | |
3050 } | |
3051 | |
3052 gaim_blist_save(); | |
3053 } | |
3054 | |
3055 gtk_widget_destroy(data->window); | |
3056 } | |
3057 | |
3058 static void | |
3059 gaim_gtk_blist_request_add_buddy(GaimAccount *account, const char *username, | |
3060 const char *group, const char *alias) | |
3061 { | |
3062 GtkWidget *table; | |
3063 GtkWidget *label; | |
3064 GtkWidget *hbox; | |
3065 GtkWidget *vbox; | |
3066 GtkWidget *img; | |
3067 GaimGtkBuddyList *gtkblist; | |
3068 GaimGtkAddBuddyData *data = g_new0(GaimGtkAddBuddyData, 1); | |
3069 | |
3070 data->account = | |
3071 (account != NULL | |
3072 ? account | |
3073 : gaim_connection_get_account(gaim_connections_get_all()->data)); | |
3074 | |
3075 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, | |
3076 GTK_ICON_SIZE_DIALOG); | |
3077 | |
3078 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
3079 | |
3080 data->window = gtk_dialog_new_with_buttons(_("Add Buddy"), | |
3081 (gtkblist->window ? GTK_WINDOW(gtkblist->window) : NULL), 0, | |
3082 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
3083 GTK_STOCK_ADD, GTK_RESPONSE_OK, | |
3084 NULL); | |
3085 | |
3086 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); | |
3087 gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); | |
3088 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); | |
3089 gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); | |
3090 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); | |
3091 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); | |
3092 gtk_window_set_role(GTK_WINDOW(data->window), "add_buddy"); | |
3093 | |
3094 hbox = gtk_hbox_new(FALSE, 12); | |
3095 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); | |
3096 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); | |
3097 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); | |
3098 | |
3099 vbox = gtk_vbox_new(FALSE, 0); | |
3100 gtk_container_add(GTK_CONTAINER(hbox), vbox); | |
3101 | |
3102 label = gtk_label_new( | |
3103 _("Please enter the screen name of the person you would like " | |
3104 "to add to your buddy list. You may optionally enter an alias, " | |
3105 "or nickname, for the buddy. The alias will be displayed in " | |
3106 "place of the screen name whenever possible.\n")); | |
3107 | |
3108 gtk_widget_set_size_request(GTK_WIDGET(label), 400, -1); | |
3109 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); | |
3110 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
3111 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); | |
3112 | |
3113 hbox = gtk_hbox_new(FALSE, 6); | |
3114 gtk_container_add(GTK_CONTAINER(vbox), hbox); | |
3115 | |
3116 g_signal_connect(G_OBJECT(data->window), "destroy", | |
3117 G_CALLBACK(destroy_add_buddy_dialog_cb), data); | |
3118 | |
3119 table = gtk_table_new(4, 2, FALSE); | |
3120 gtk_table_set_row_spacings(GTK_TABLE(table), 5); | |
3121 gtk_table_set_col_spacings(GTK_TABLE(table), 5); | |
3122 gtk_container_set_border_width(GTK_CONTAINER(table), 0); | |
3123 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); | |
3124 | |
3125 label = gtk_label_new(_("Screen Name:")); | |
3126 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3127 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); | |
3128 | |
3129 data->entry = gtk_entry_new(); | |
3130 gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1); | |
3131 gtk_widget_grab_focus(data->entry); | |
3132 | |
3133 if (username != NULL) | |
3134 gtk_entry_set_text(GTK_ENTRY(data->entry), username); | |
3135 | |
3136 gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE); | |
3137 | |
3138 label = gtk_label_new(_("Alias:")); | |
3139 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3140 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); | |
3141 | |
3142 data->entry_for_alias = gtk_entry_new(); | |
3143 gtk_table_attach_defaults(GTK_TABLE(table), | |
3144 data->entry_for_alias, 1, 2, 1, 2); | |
3145 | |
3146 if (alias != NULL) | |
3147 gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias); | |
3148 | |
3149 gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE); | |
3150 | |
3151 label = gtk_label_new(_("Group:")); | |
3152 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3153 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); | |
3154 | |
3155 data->combo = gtk_combo_new(); | |
3156 gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree()); | |
3157 gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3); | |
3158 | |
3159 /* Set up stuff for the account box */ | |
3160 label = gtk_label_new(_("Account:")); | |
3161 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3162 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); | |
3163 | |
3164 data->account_box = gaim_gtk_account_option_menu_new(account, FALSE, | |
3165 G_CALLBACK(add_buddy_select_account_cb), NULL, data); | |
3166 | |
3167 gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4); | |
3168 | |
3169 /* End of account box */ | |
3170 | |
3171 g_signal_connect(G_OBJECT(data->window), "response", | |
3172 G_CALLBACK(add_buddy_cb), data); | |
3173 | |
3174 gtk_widget_show_all(data->window); | |
3175 | |
3176 if (group != NULL) | |
3177 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry), group); | |
3178 } | |
3179 | |
3180 static void | |
3181 add_chat_cb(GtkWidget *w, GaimGtkAddChatData *data) | |
3182 { | |
3183 GHashTable *components; | |
3184 GList *tmp; | |
3185 GaimChat *chat; | |
3186 GaimGroup *group; | |
3187 const char *group_name; | |
3188 | |
3189 components = g_hash_table_new_full(g_str_hash, g_str_equal, | |
3190 g_free, g_free); | |
3191 | |
3192 for (tmp = data->entries; tmp; tmp = tmp->next) | |
3193 { | |
3194 if (g_object_get_data(tmp->data, "is_spin")) | |
3195 { | |
3196 g_hash_table_replace(components, | |
3197 g_strdup(g_object_get_data(tmp->data, "identifier")), | |
3198 g_strdup_printf("%d", | |
3199 gtk_spin_button_get_value_as_int(tmp->data))); | |
3200 } | |
3201 else | |
3202 { | |
3203 g_hash_table_replace(components, | |
3204 g_strdup(g_object_get_data(tmp->data, "identifier")), | |
3205 g_strdup(gtk_entry_get_text(tmp->data))); | |
3206 } | |
3207 } | |
3208 | |
3209 chat = gaim_chat_new(data->account, | |
3210 gtk_entry_get_text(GTK_ENTRY(data->alias_entry)), | |
3211 components); | |
3212 | |
3213 group_name = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry)); | |
3214 | |
3215 if ((group = gaim_find_group(group_name)) == NULL) | |
3216 { | |
3217 group = gaim_group_new(group_name); | |
3218 gaim_blist_add_group(group, NULL); | |
3219 } | |
3220 | |
3221 if (chat != NULL) | |
3222 { | |
3223 gaim_blist_add_chat(chat, group, NULL); | |
3224 gaim_blist_save(); | |
3225 } | |
3226 | |
3227 gtk_widget_destroy(data->window); | |
3228 g_list_free(data->entries); | |
3229 | |
3230 g_free(data); | |
3231 } | |
3232 | |
3233 static void | |
3234 add_chat_resp_cb(GtkWidget *w, int resp, GaimGtkAddChatData *data) | |
3235 { | |
3236 if (resp == GTK_RESPONSE_OK) | |
3237 { | |
3238 add_chat_cb(NULL, data); | |
3239 } | |
3240 else | |
3241 { | |
3242 gtk_widget_destroy(data->window); | |
3243 g_list_free(data->entries); | |
3244 g_free(data); | |
3245 } | |
3246 } | |
3247 | |
3248 static void | |
3249 rebuild_addchat_entries(GaimGtkAddChatData *data) | |
3250 { | |
3251 GaimConnection *gc; | |
3252 GList *list, *tmp; | |
3253 struct proto_chat_entry *pce; | |
3254 gboolean focus = TRUE; | |
3255 | |
3256 gc = gaim_account_get_connection(data->account); | |
3257 | |
3258 while (GTK_BOX(data->entries_box)->children) | |
3259 { | |
3260 gtk_container_remove(GTK_CONTAINER(data->entries_box), | |
3261 ((GtkBoxChild *)GTK_BOX(data->entries_box)->children->data)->widget); | |
3262 } | |
3263 | |
3264 if (data->entries != NULL) | |
3265 g_list_free(data->entries); | |
3266 | |
3267 data->entries = NULL; | |
3268 | |
3269 list = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc); | |
3270 | |
3271 for (tmp = list; tmp; tmp = tmp->next) | |
3272 { | |
3273 GtkWidget *label; | |
3274 GtkWidget *rowbox; | |
3275 | |
3276 pce = tmp->data; | |
3277 | |
3278 rowbox = gtk_hbox_new(FALSE, 5); | |
3279 gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); | |
3280 | |
3281 label = gtk_label_new(pce->label); | |
3282 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3283 gtk_size_group_add_widget(data->sg, label); | |
3284 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3285 | |
3286 if (pce->is_int) | |
3287 { | |
3288 GtkObject *adjust; | |
3289 GtkWidget *spin; | |
3290 adjust = gtk_adjustment_new(pce->min, pce->min, pce->max, | |
3291 1, 10, 10); | |
3292 spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); | |
3293 g_object_set_data(G_OBJECT(spin), "is_spin", GINT_TO_POINTER(TRUE)); | |
3294 g_object_set_data(G_OBJECT(spin), "identifier", pce->identifier); | |
3295 data->entries = g_list_append(data->entries, spin); | |
3296 gtk_widget_set_size_request(spin, 50, -1); | |
3297 gtk_box_pack_end(GTK_BOX(rowbox), spin, FALSE, FALSE, 0); | |
3298 } | |
3299 else | |
3300 { | |
3301 GtkWidget *entry = gtk_entry_new(); | |
3302 | |
3303 g_object_set_data(G_OBJECT(entry), "identifier", pce->identifier); | |
3304 data->entries = g_list_append(data->entries, entry); | |
3305 | |
3306 if (pce->def) | |
3307 gtk_entry_set_text(GTK_ENTRY(entry), pce->def); | |
3308 | |
3309 if (focus) | |
3310 { | |
3311 gtk_widget_grab_focus(entry); | |
3312 focus = FALSE; | |
3313 } | |
3314 | |
3315 if (pce->secret) | |
3316 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); | |
3317 | |
3318 gtk_box_pack_end(GTK_BOX(rowbox), entry, TRUE, TRUE, 0); | |
3319 | |
3320 g_signal_connect(G_OBJECT(entry), "activate", | |
3321 G_CALLBACK(add_chat_cb), data); | |
3322 } | |
3323 | |
3324 g_free(pce); | |
3325 } | |
3326 | |
3327 g_list_free(list); | |
3328 | |
3329 gtk_widget_show_all(data->entries_box); | |
3330 } | |
3331 | |
3332 static void | |
3333 add_chat_select_account_cb(GObject *w, GaimAccount *account, | |
3334 GaimGtkAddChatData *data) | |
3335 { | |
3336 if (gaim_account_get_protocol(data->account) == | |
3337 gaim_account_get_protocol(account)) | |
3338 { | |
3339 data->account = account; | |
3340 } | |
3341 else | |
3342 { | |
3343 data->account = account; | |
3344 rebuild_addchat_entries(data); | |
3345 } | |
3346 } | |
3347 | |
3348 static gboolean | |
3349 add_chat_check_account_func(GaimAccount *account) | |
3350 { | |
3351 GaimConnection *gc = gaim_account_get_connection(account); | |
3352 | |
3353 return (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL); | |
3354 } | |
3355 | |
3356 void | |
3357 gaim_gtk_blist_request_add_chat(GaimAccount *account, GaimGroup *group) | |
3358 { | |
3359 GaimGtkAddChatData *data; | |
3360 GaimGtkBuddyList *gtkblist; | |
3361 GList *l; | |
3362 GaimConnection *gc; | |
3363 GtkWidget *label; | |
3364 GtkWidget *rowbox; | |
3365 GtkWidget *hbox; | |
3366 GtkWidget *vbox; | |
3367 GtkWidget *img; | |
3368 | |
3369 data = g_new0(GaimGtkAddChatData, 1); | |
3370 | |
3371 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, | |
3372 GTK_ICON_SIZE_DIALOG); | |
3373 | |
3374 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
3375 | |
3376 if (account != NULL) | |
3377 { | |
3378 data->account = account; | |
3379 } | |
3380 else | |
3381 { | |
3382 /* Select an account with chat capabilities */ | |
3383 for (l = gaim_connections_get_all(); l != NULL; l = l->next) | |
3384 { | |
3385 gc = (GaimConnection *)l->data; | |
3386 | |
3387 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat != NULL) | |
3388 { | |
3389 data->account = gaim_connection_get_account(gc); | |
3390 break; | |
3391 } | |
3392 } | |
3393 } | |
3394 | |
3395 if (data->account == NULL) | |
3396 { | |
3397 gaim_notify_error(NULL, NULL, | |
3398 _("You are not currently signed on with any " | |
3399 "protocols that have the ability to chat."), NULL); | |
3400 return; | |
3401 } | |
3402 | |
3403 data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); | |
3404 | |
3405 data->window = gtk_dialog_new_with_buttons(_("Add Chat"), | |
3406 GTK_WINDOW(gtkblist->window), 0, | |
3407 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
3408 GTK_STOCK_ADD, GTK_RESPONSE_OK, | |
3409 NULL); | |
3410 | |
3411 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); | |
3412 gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); | |
3413 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); | |
3414 gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); | |
3415 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); | |
3416 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); | |
3417 gtk_window_set_role(GTK_WINDOW(data->window), "add_chat"); | |
3418 | |
3419 hbox = gtk_hbox_new(FALSE, 12); | |
3420 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); | |
3421 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); | |
3422 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); | |
3423 | |
3424 vbox = gtk_vbox_new(FALSE, 5); | |
3425 gtk_container_add(GTK_CONTAINER(hbox), vbox); | |
3426 | |
3427 label = gtk_label_new( | |
3428 _("Please enter an alias, and the appropriate information " | |
3429 "about the chat you would like to add to your buddy list.\n")); | |
3430 gtk_widget_set_size_request(label, 400, -1); | |
3431 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); | |
3432 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
3433 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); | |
3434 | |
3435 rowbox = gtk_hbox_new(FALSE, 5); | |
3436 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3437 | |
3438 label = gtk_label_new(_("Account:")); | |
3439 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3440 gtk_size_group_add_widget(data->sg, label); | |
3441 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3442 | |
3443 data->account_menu = gaim_gtk_account_option_menu_new(account, FALSE, | |
3444 G_CALLBACK(add_chat_select_account_cb), | |
3445 add_chat_check_account_func, data); | |
3446 gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); | |
3447 | |
3448 data->entries_box = gtk_vbox_new(FALSE, 5); | |
3449 gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); | |
3450 gtk_box_pack_start(GTK_BOX(vbox), data->entries_box, TRUE, TRUE, 0); | |
3451 | |
3452 rebuild_addchat_entries(data); | |
3453 | |
3454 rowbox = gtk_hbox_new(FALSE, 5); | |
3455 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3456 | |
3457 label = gtk_label_new(_("Alias:")); | |
3458 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3459 gtk_size_group_add_widget(data->sg, label); | |
3460 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3461 | |
3462 data->alias_entry = gtk_entry_new(); | |
3463 gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); | |
3464 | |
3465 rowbox = gtk_hbox_new(FALSE, 5); | |
3466 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3467 | |
3468 label = gtk_label_new(_("Group:")); | |
3469 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3470 gtk_size_group_add_widget(data->sg, label); | |
3471 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3472 | |
3473 data->group_combo = gtk_combo_new(); | |
3474 gtk_combo_set_popdown_strings(GTK_COMBO(data->group_combo), groups_tree()); | |
3475 gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); | |
3476 | |
3477 if (group) | |
3478 { | |
3479 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry), | |
3480 group->name); | |
3481 } | |
3482 | |
3483 g_signal_connect(G_OBJECT(data->window), "response", | |
3484 G_CALLBACK(add_chat_resp_cb), data); | |
3485 | |
3486 gtk_widget_show_all(data->window); | |
3487 } | |
3488 | |
3489 static void | |
3490 add_group_cb(GaimConnection *gc, const char *group_name) | |
3491 { | |
3492 GaimGroup *g; | |
3493 | |
3494 g = gaim_group_new(group_name); | |
3495 gaim_blist_add_group(g, NULL); | |
3496 gaim_blist_save(); | |
3497 } | |
3498 | |
3499 void | |
3500 gaim_gtk_blist_request_add_group(void) | |
3501 { | |
3502 gaim_request_input(NULL, _("Add Group"), _("Add a new group"), | |
3503 _("Please enter the name of the group to be added."), | |
3504 NULL, FALSE, FALSE, | |
3505 _("Add"), G_CALLBACK(add_group_cb), | |
3506 _("Cancel"), NULL, NULL); | |
3507 } | |
3508 | |
5228 | 3509 void gaim_gtk_blist_docklet_toggle() { |
3510 /* Useful for the docklet plugin and also for the win32 tray icon*/ | |
3511 /* This is called when one of those is clicked--it will show/hide the | |
3512 buddy list/login window--depending on which is active */ | |
7620 | 3513 if (gaim_connections_get_all()) { |
5228 | 3514 if (gtkblist && gtkblist->window) { |
3515 if (GTK_WIDGET_VISIBLE(gtkblist->window)) { | |
3516 gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); | |
3517 } else { | |
3518 #if _WIN32 | |
3519 wgaim_systray_maximize(gtkblist->window); | |
3520 #endif | |
3521 gaim_blist_set_visible(TRUE); | |
3522 } | |
3523 } else { | |
3524 /* we're logging in or something... do nothing */ | |
3525 /* or should I make the blist? */ | |
3526 gaim_debug(GAIM_DEBUG_WARNING, "blist", | |
7620 | 3527 "docklet_toggle called with gaim_connections_get_all() " |
5228 | 3528 "but no blist!\n"); |
3529 } | |
3530 } else if (mainwindow) { | |
3531 if (GTK_WIDGET_VISIBLE(mainwindow)) { | |
3532 if (GAIM_WINDOW_ICONIFIED(mainwindow)) { | |
3533 gtk_window_present(GTK_WINDOW(mainwindow)); | |
3534 } else { | |
3535 #if _WIN32 | |
3536 wgaim_systray_minimize(mainwindow); | |
3537 #endif | |
3538 gtk_widget_hide(mainwindow); | |
3539 } | |
3540 } else { | |
3541 #if _WIN32 | |
3542 wgaim_systray_maximize(mainwindow); | |
3543 #endif | |
3544 show_login(); | |
3545 } | |
3546 } else { | |
3547 show_login(); | |
3548 } | |
3549 } | |
3550 | |
3551 void gaim_gtk_blist_docklet_add() | |
3552 { | |
3553 docklet_count++; | |
3554 } | |
3555 | |
3556 void gaim_gtk_blist_docklet_remove() | |
3557 { | |
3558 docklet_count--; | |
3559 if (!docklet_count) { | |
7620 | 3560 if (gaim_connections_get_all()) |
5228 | 3561 gaim_blist_set_visible(TRUE); |
3562 else if (mainwindow) | |
3563 gtk_window_present(GTK_WINDOW(mainwindow)); | |
3564 else | |
3565 show_login(); | |
3566 } | |
3567 } | |
3568 | |
7620 | 3569 static GaimBlistUiOps blist_ui_ops = |
5228 | 3570 { |
3571 gaim_gtk_blist_new_list, | |
5256 | 3572 gaim_gtk_blist_new_node, |
5228 | 3573 gaim_gtk_blist_show, |
3574 gaim_gtk_blist_update, | |
3575 gaim_gtk_blist_remove, | |
3576 gaim_gtk_blist_destroy, | |
7620 | 3577 gaim_gtk_blist_set_visible, |
3578 gaim_gtk_blist_request_add_buddy, | |
3579 gaim_gtk_blist_request_add_chat, | |
3580 gaim_gtk_blist_request_add_group | |
5228 | 3581 }; |
3582 | |
3583 | |
7620 | 3584 GaimBlistUiOps * |
3585 gaim_gtk_blist_get_ui_ops(void) | |
5228 | 3586 { |
3587 return &blist_ui_ops; | |
3588 } | |
3589 | |
7620 | 3590 static void account_signon_cb(GaimConnection *gc, gpointer z) |
3591 { | |
3592 GaimAccount *account = gaim_connection_get_account(gc); | |
3593 GaimBlistNode *gnode, *cnode; | |
3594 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) | |
3595 { | |
3596 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
3597 continue; | |
3598 for(cnode = gnode->child; cnode; cnode = cnode->next) | |
3599 { | |
3600 GaimChat *chat; | |
3601 const char *autojoin; | |
3602 | |
3603 if(!GAIM_BLIST_NODE_IS_CHAT(cnode)) | |
3604 continue; | |
3605 | |
3606 chat = (GaimChat *)cnode; | |
3607 | |
3608 if(chat->account != account) | |
3609 continue; | |
3610 | |
3611 autojoin = gaim_chat_get_setting(chat, "gtk-autojoin"); | |
3612 | |
3613 if(autojoin && !strcmp(autojoin, "true")) | |
3614 serv_join_chat(gc, chat->components); | |
3615 } | |
3616 } | |
3617 } | |
3618 | |
3619 void gaim_gtk_blist_init(void) | |
3620 { | |
3621 /* XXX */ | |
3622 static int gtk_blist_handle; | |
3623 | |
3624 gaim_signal_connect(gaim_connections_get_handle(), "signed-on", | |
3625 >k_blist_handle, GAIM_CALLBACK(account_signon_cb), | |
3626 NULL); | |
3627 } | |
3628 | |
5228 | 3629 |
3630 | |
3631 /********************************************************************* | |
3632 * Public utility functions * | |
3633 *********************************************************************/ | |
3634 | |
3635 GdkPixbuf * | |
7620 | 3636 create_prpl_icon(GaimAccount *account) |
5228 | 3637 { |
3638 GaimPlugin *prpl; | |
3639 GaimPluginProtocolInfo *prpl_info = NULL; | |
3640 GdkPixbuf *status = NULL; | |
3641 char *filename = NULL; | |
3642 const char *protoname = NULL; | |
3643 char buf[256]; | |
3644 | |
7620 | 3645 prpl = gaim_find_prpl(gaim_account_get_protocol(account)); |
5228 | 3646 |
3647 if (prpl != NULL) { | |
3648 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
3649 | |
3650 if (prpl_info->list_icon != NULL) | |
3651 protoname = prpl_info->list_icon(account, NULL); | |
3652 } | |
3653 | |
3654 if (protoname == NULL) | |
3655 return NULL; | |
3656 | |
3657 /* | |
3658 * Status icons will be themeable too, and then it will look up | |
3659 * protoname from the theme | |
3660 */ | |
3661 g_snprintf(buf, sizeof(buf), "%s.png", protoname); | |
3662 | |
3663 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", | |
3664 "default", buf, NULL); | |
3665 status = gdk_pixbuf_new_from_file(filename, NULL); | |
3666 g_free(filename); | |
3667 | |
3668 return status; | |
3669 } | |
3670 | |
5422 | 3671 |
3672 /********************************************************************* | |
3673 * Buddy List sorting functions * | |
3674 *********************************************************************/ | |
3675 | |
7620 | 3676 void gaim_gtk_blist_sort_method_reg(const char *id, const char *name, gaim_gtk_blist_sort_function func) |
5422 | 3677 { |
3678 struct gaim_gtk_blist_sort_method *method = g_new0(struct gaim_gtk_blist_sort_method, 1); | |
7620 | 3679 method->id = g_strdup(id); |
5422 | 3680 method->name = g_strdup(name); |
7620 | 3681 method->func = func;; |
5422 | 3682 gaim_gtk_blist_sort_methods = g_slist_append(gaim_gtk_blist_sort_methods, method); |
3683 } | |
3684 | |
7620 | 3685 void gaim_gtk_blist_sort_method_unreg(const char *id){ |
3686 GSList *l = gaim_gtk_blist_sort_methods; | |
3687 | |
3688 while(l) { | |
3689 struct gaim_gtk_blist_sort_method *method = l->data; | |
3690 if(!strcmp(method->id, id)) { | |
3691 gaim_gtk_blist_sort_methods = g_slist_remove(gaim_gtk_blist_sort_methods, method); | |
3692 g_free(method->id); | |
3693 g_free(method->name); | |
3694 g_free(method); | |
3695 break; | |
3696 } | |
3697 } | |
5422 | 3698 } |
3699 | |
7620 | 3700 void gaim_gtk_blist_sort_method_set(const char *id){ |
5422 | 3701 GSList *l = gaim_gtk_blist_sort_methods; |
7620 | 3702 |
3703 if(!id) | |
3704 id = "none"; | |
3705 | |
3706 while (l && strcmp(((struct gaim_gtk_blist_sort_method*)l->data)->id, id)) | |
5422 | 3707 l = l->next; |
7620 | 3708 |
5422 | 3709 if (l) { |
3710 current_sort_method = l->data; | |
3711 } else if (!current_sort_method) { | |
7620 | 3712 gaim_gtk_blist_sort_method_set("none"); |
5422 | 3713 return; |
3714 } | |
3715 redo_buddy_list(gaim_get_blist(), TRUE); | |
3716 | |
3717 } | |
3718 | |
3719 /****************************************** | |
3720 ** Sort Methods | |
3721 ******************************************/ | |
3722 | |
7620 | 3723 static GtkTreeIter sort_method_none(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter parent_iter, GtkTreeIter *cur) |
5422 | 3724 { |
7620 | 3725 GtkTreeIter iter; |
3726 GaimBlistNode *sibling = node->prev; | |
3727 GtkTreeIter sibling_iter; | |
3728 | |
3729 if(cur) | |
5422 | 3730 return *cur; |
7620 | 3731 |
3732 while (sibling && !get_iter_from_node(sibling, &sibling_iter)) { | |
3733 sibling = sibling->prev; | |
5422 | 3734 } |
7620 | 3735 |
3736 gtk_tree_store_insert_after(gtkblist->treemodel, &iter, | |
3737 node->parent ? &parent_iter : NULL, | |
3738 sibling ? &sibling_iter : NULL); | |
3739 | |
5422 | 3740 return iter; |
3741 } | |
3742 | |
7620 | 3743 #if GTK_CHECK_VERSION(2,2,1) |
3744 | |
3745 static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
5422 | 3746 { |
3747 GtkTreeIter more_z, iter; | |
3748 GaimBlistNode *n; | |
3749 GValue val = {0,}; | |
7620 | 3750 |
3751 const char *my_name; | |
3752 | |
3753 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
3754 my_name = gaim_contact_get_alias((GaimContact*)node); | |
3755 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
3756 my_name = gaim_chat_get_name((GaimChat*)node); | |
3757 } else { | |
3758 return sort_method_none(node, blist, groupiter, cur); | |
3759 } | |
3760 | |
5422 | 3761 |
3762 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
3763 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
7620 | 3764 return iter; |
3765 } | |
3766 | |
3767 do { | |
3768 const char *this_name; | |
3769 int cmp; | |
3770 | |
3771 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); | |
3772 n = g_value_get_pointer(&val); | |
3773 | |
3774 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
3775 this_name = gaim_contact_get_alias((GaimContact*)n); | |
3776 } else if(GAIM_BLIST_NODE_IS_CHAT(n)) { | |
3777 this_name = gaim_chat_get_name((GaimChat*)n); | |
3778 } else { | |
3779 this_name = NULL; | |
3780 } | |
3781 | |
3782 cmp = gaim_utf8_strcasecmp(my_name, this_name); | |
3783 | |
3784 if(this_name && (cmp < 0 || (cmp == 0 && node < n))) { | |
3785 if(cur) { | |
3786 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
3787 return *cur; | |
3788 } else { | |
3789 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
3790 &groupiter, &more_z); | |
3791 return iter; | |
3792 } | |
3793 } | |
3794 g_value_unset(&val); | |
3795 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
3796 | |
3797 if(cur) { | |
3798 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
3799 return *cur; | |
3800 } else { | |
3801 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3802 return iter; | |
3803 } | |
3804 } | |
3805 | |
3806 static GtkTreeIter sort_method_status(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
3807 { | |
3808 GtkTreeIter more_z, iter; | |
3809 GaimBlistNode *n; | |
3810 GValue val = {0,}; | |
3811 | |
3812 GaimBuddy *my_buddy, *this_buddy; | |
3813 | |
3814 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
3815 my_buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
3816 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
3817 if(cur) | |
3818 return *cur; | |
3819 | |
3820 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3821 return iter; | |
3822 } else { | |
3823 return sort_method_none(node, blist, groupiter, cur); | |
3824 } | |
3825 | |
3826 | |
3827 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
3828 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
5422 | 3829 return iter; |
3830 } | |
3831 | |
3832 do { | |
7620 | 3833 int cmp; |
3834 | |
5422 | 3835 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
3836 n = g_value_get_pointer(&val); | |
7620 | 3837 |
3838 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
3839 this_buddy = gaim_contact_get_priority_buddy((GaimContact*)n); | |
3840 } else { | |
3841 this_buddy = NULL; | |
3842 } | |
3843 | |
3844 cmp = gaim_utf8_strcasecmp(my_buddy ? | |
3845 gaim_contact_get_alias(gaim_buddy_get_contact(my_buddy)) | |
3846 : NULL, this_buddy ? | |
3847 gaim_contact_get_alias(gaim_buddy_get_contact(this_buddy)) | |
3848 : NULL); | |
3849 | |
3850 /* Hideous */ | |
3851 if(!this_buddy || | |
3852 ((my_buddy->present > this_buddy->present) || | |
3853 (my_buddy->present == this_buddy->present && | |
3854 (((my_buddy->uc & UC_UNAVAILABLE) < (this_buddy->uc & UC_UNAVAILABLE)) || | |
3855 (((my_buddy->uc & UC_UNAVAILABLE) == (this_buddy->uc & UC_UNAVAILABLE)) && | |
3856 (((my_buddy->idle == 0) && (this_buddy->idle != 0)) || | |
3857 (this_buddy->idle && (my_buddy->idle > this_buddy->idle)) || | |
3858 ((my_buddy->idle == this_buddy->idle) && | |
3859 (cmp < 0 || (cmp == 0 && node < n))))))))) { | |
3860 if(cur) { | |
3861 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
3862 return *cur; | |
3863 } else { | |
3864 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
3865 &groupiter, &more_z); | |
3866 return iter; | |
3867 } | |
5422 | 3868 } |
3869 g_value_unset(&val); | |
3870 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
7620 | 3871 |
3872 if(cur) { | |
3873 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
3874 return *cur; | |
3875 } else { | |
3876 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3877 return iter; | |
3878 } | |
5422 | 3879 } |
3880 | |
7620 | 3881 static GtkTreeIter sort_method_log(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) |
5422 | 3882 { |
3883 GtkTreeIter more_z, iter; | |
7620 | 3884 GaimBlistNode *n = NULL, *n2; |
5422 | 3885 GValue val = {0,}; |
7620 | 3886 |
3887 int log_size = 0, this_log_size = 0; | |
3888 const char *buddy_name, *this_buddy_name; | |
3889 | |
3890 if(cur && (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter) == 1)) | |
3891 return *cur; | |
3892 | |
3893 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
3894 for (n = node->child; n; n = n->next) | |
3895 log_size += gaim_log_get_total_size(((GaimBuddy*)(n))->name, ((GaimBuddy*)(n))->account); | |
3896 buddy_name = gaim_contact_get_alias((GaimContact*)node); | |
3897 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
3898 /* we don't have a reliable way of getting the log filename | |
3899 * from the chat info in the blist, yet */ | |
3900 if(cur) | |
3901 return *cur; | |
3902 | |
3903 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3904 return iter; | |
3905 } else { | |
3906 return sort_method_none(node, blist, groupiter, cur); | |
3907 } | |
3908 | |
3909 | |
5422 | 3910 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { |
3911 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
3912 return iter; | |
3913 } | |
3914 | |
3915 do { | |
7620 | 3916 int cmp; |
3917 | |
5422 | 3918 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
3919 n = g_value_get_pointer(&val); | |
7620 | 3920 this_log_size = 0; |
3921 | |
3922 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
3923 for (n2 = n->child; n2; n2 = n2->next) | |
3924 this_log_size += gaim_log_get_total_size(((GaimBuddy*)(n2))->name, ((GaimBuddy*)(n2))->account); | |
3925 this_buddy_name = gaim_contact_get_alias((GaimContact*)n); | |
3926 } else { | |
3927 this_buddy_name = NULL; | |
5422 | 3928 } |
7620 | 3929 |
3930 cmp = gaim_utf8_strcasecmp(buddy_name, this_buddy_name); | |
3931 | |
3932 if (!GAIM_BLIST_NODE_IS_CONTACT(n) || log_size > this_log_size || | |
3933 ((log_size == this_log_size) && | |
3934 (cmp < 0 || (cmp == 0 && node < n)))) { | |
3935 if(cur) { | |
3936 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
3937 return *cur; | |
3938 } else { | |
3939 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
3940 &groupiter, &more_z); | |
3941 return iter; | |
3942 } | |
5422 | 3943 } |
3944 g_value_unset(&val); | |
3945 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
7620 | 3946 |
3947 if(cur) { | |
3948 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
3949 return *cur; | |
3950 } else { | |
3951 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3952 return iter; | |
3953 } | |
3954 } | |
3955 | |
3956 #endif | |
3957 | |
3958 static void | |
3959 proto_act(GtkObject *obj, struct proto_actions_menu *pam) | |
3960 { | |
3961 if (pam->callback && pam->gc) | |
3962 pam->callback(pam->gc); | |
5422 | 3963 } |
7620 | 3964 |
3965 void | |
3966 gaim_gtk_blist_update_protocol_actions(void) | |
3967 { | |
3968 GtkWidget *menuitem; | |
3969 GtkWidget *submenu; | |
3970 GaimPluginProtocolInfo *prpl_info = NULL; | |
3971 GList *l; | |
3972 GList *c; | |
3973 struct proto_actions_menu *pam; | |
3974 GaimConnection *gc = NULL; | |
3975 int count = 0; | |
3976 char buf[256]; | |
3977 | |
3978 if (!protomenu) | |
3979 return; | |
3980 | |
3981 for (l = gtk_container_get_children(GTK_CONTAINER(protomenu)); | |
3982 l != NULL; | |
3983 l = l->next) { | |
3984 | |
3985 menuitem = l->data; | |
3986 pam = g_object_get_data(G_OBJECT(menuitem), "proto_actions_menu"); | |
3987 | |
3988 if (pam) | |
3989 g_free(pam); | |
3990 | |
3991 gtk_container_remove(GTK_CONTAINER(protomenu), GTK_WIDGET(menuitem)); | |
3992 } | |
3993 | |
3994 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
3995 gc = c->data; | |
3996 | |
3997 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
3998 | |
3999 if (prpl_info->actions && gc->login_time) | |
4000 count++; | |
4001 } | |
4002 | |
4003 if (!count) { | |
4004 g_snprintf(buf, sizeof(buf), _("No actions available")); | |
4005 menuitem = gtk_menu_item_new_with_label(buf); | |
4006 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4007 gtk_widget_show(menuitem); | |
4008 return; | |
4009 } | |
4010 | |
4011 if (count == 1) { | |
4012 GList *act; | |
4013 | |
4014 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
4015 gc = c->data; | |
4016 | |
4017 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
4018 | |
4019 if (prpl_info->actions && gc->login_time) | |
4020 break; | |
4021 } | |
4022 | |
4023 for (act = prpl_info->actions(gc); act != NULL; act = act->next) { | |
4024 if (act->data) { | |
4025 struct proto_actions_menu *pam = act->data; | |
4026 menuitem = gtk_menu_item_new_with_label(pam->label); | |
4027 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4028 g_signal_connect(G_OBJECT(menuitem), "activate", | |
4029 G_CALLBACK(proto_act), pam); | |
4030 g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", pam); | |
4031 gtk_widget_show(menuitem); | |
4032 } | |
4033 else | |
4034 gaim_separator(protomenu); | |
4035 } | |
4036 } | |
4037 else { | |
4038 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
4039 GaimAccount *account; | |
4040 GList *act; | |
4041 GdkPixbuf *pixbuf, *scale; | |
4042 GtkWidget *image; | |
4043 | |
4044 gc = c->data; | |
4045 | |
4046 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
4047 | |
4048 if (!prpl_info->actions || !gc->login_time) | |
4049 continue; | |
4050 | |
4051 account = gaim_connection_get_account(gc); | |
4052 | |
4053 g_snprintf(buf, sizeof(buf), "%s (%s)", | |
4054 gaim_account_get_username(account), | |
4055 gc->prpl->info->name); | |
4056 | |
4057 menuitem = gtk_image_menu_item_new_with_label(buf); | |
4058 | |
4059 pixbuf = create_prpl_icon(gc->account); | |
4060 if(pixbuf) { | |
4061 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, | |
4062 GDK_INTERP_BILINEAR); | |
4063 image = gtk_image_new_from_pixbuf(scale); | |
4064 g_object_unref(G_OBJECT(pixbuf)); | |
4065 g_object_unref(G_OBJECT(scale)); | |
4066 gtk_widget_show(image); | |
4067 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), | |
4068 image); | |
4069 } | |
4070 | |
4071 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4072 gtk_widget_show(menuitem); | |
4073 | |
4074 submenu = gtk_menu_new(); | |
4075 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); | |
4076 gtk_widget_show(submenu); | |
4077 | |
4078 for (act = prpl_info->actions(gc); act != NULL; act = act->next) { | |
4079 if (act->data) { | |
4080 struct proto_actions_menu *pam = act->data; | |
4081 menuitem = gtk_menu_item_new_with_label(pam->label); | |
4082 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); | |
4083 g_signal_connect(G_OBJECT(menuitem), "activate", | |
4084 G_CALLBACK(proto_act), pam); | |
4085 g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", | |
4086 pam); | |
4087 gtk_widget_show(menuitem); | |
4088 } | |
4089 else | |
4090 gaim_separator(submenu); | |
4091 } | |
4092 } | |
4093 } | |
4094 } |