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