Mercurial > pidgin
annotate src/gtkblist.c @ 7786:203a18e56dc6
[gaim-migrate @ 8431]
* Documentation is good. Not that I have any idea if any of my
documentation works, because I haven't checked... I totally made up
@note, but it sure seems reasonable to me.
* A couple of stringref utility functions which seem useful in any
case, like len and cmp.
* I'm going ahead and pushing a stringref creation function which
creates a zero-refcount stringref into CVS... Nothing uses it yet,
but I think that it is useful even in the absence of major stringref
conversion ... because it's garbage collected! If no one refs it
before the next iteration of the mainloop, it will be freed ... if
it is ref'd, of course, it will have to be freed normally by an
unref at some later point.
committer: Tailor Script <tailor@pidgin.im>
author | Ethan Blanton <elb@pidgin.im> |
---|---|
date | Sun, 07 Dec 2003 16:28:34 +0000 |
parents | f1fda2e85015 |
children | 2e6e269504a4 |
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 |
7753 | 1051 gtk_tree_path_free(path); |
7620 | 1052 return FALSE; |
5228 | 1053 } |
1054 | |
1055 static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) | |
1056 { | |
7620 | 1057 gaim_prefs_set_bool("/gaim/gtk/blist/show_empty_groups", |
1058 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))); | |
5228 | 1059 } |
1060 | |
1061 static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, | |
1062 GtkWidget *checkitem) { | |
1063 if(gtkblist->window->window) { | |
1064 GdkCursor *cursor = gdk_cursor_new(GDK_WATCH); | |
1065 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
1066 while (gtk_events_pending()) | |
1067 gtk_main_iteration(); | |
1068 gdk_cursor_unref(cursor); | |
1069 } | |
1070 | |
7620 | 1071 gaim_prefs_set_bool("/gaim/gtk/blist/show_offline_buddies", |
1072 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))); | |
5228 | 1073 |
1074 if(gtkblist->window->window) { | |
1075 GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); | |
1076 gdk_window_set_cursor(gtkblist->window->window, cursor); | |
1077 gdk_cursor_unref(cursor); | |
1078 } | |
1079 } | |
1080 | |
1081 static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, | |
1082 GdkDragContext *dc, | |
1083 GtkSelectionData *data, | |
1084 guint info, | |
1085 guint time, | |
1086 gpointer *null) | |
1087 { | |
1088 if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { | |
1089 GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); | |
1090 GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); | |
1091 GtkTreeIter iter; | |
1092 GaimBlistNode *node = NULL; | |
1093 GValue val = {0}; | |
5273 | 1094 if(!sourcerow) |
1095 return; | |
5228 | 1096 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); |
1097 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
1098 node = g_value_get_pointer(&val); | |
1099 gtk_selection_data_set (data, | |
1100 gdk_atom_intern ("GAIM_BLIST_NODE", FALSE), | |
1101 8, /* bits */ | |
1102 (void*)&node, | |
1103 sizeof (node)); | |
5273 | 1104 |
5228 | 1105 gtk_tree_path_free(sourcerow); |
1106 } | |
7706 | 1107 else if (data->target == gdk_atom_intern("application/x-im-contact", |
1108 FALSE)) { | |
1109 | |
1110 GtkTreeRowReference *ref; | |
1111 GtkTreePath *sourcerow; | |
1112 GtkTreeIter iter; | |
1113 GaimBlistNode *node = NULL; | |
1114 GaimBuddy *buddy; | |
1115 GaimConnection *gc; | |
1116 GValue val = {0}; | |
1117 const char *prpl_id; | |
1118 GString *str; | |
1119 const char *protocol; | |
1120 char *mime_str; | |
1121 | |
1122 ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); | |
1123 sourcerow = gtk_tree_row_reference_get_path(ref); | |
1124 | |
1125 if (!sourcerow) | |
1126 return; | |
1127 | |
1128 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
1129 sourcerow); | |
1130 gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
1131 NODE_COLUMN, &val); | |
1132 | |
1133 node = g_value_get_pointer(&val); | |
1134 | |
1135 if (GAIM_BLIST_NODE_IS_CONTACT(node)) | |
1136 { | |
1137 buddy = gaim_contact_get_priority_buddy((GaimContact *)node); | |
1138 } | |
1139 else if (!GAIM_BLIST_NODE_IS_BUDDY(node)) | |
1140 { | |
1141 gtk_tree_path_free(sourcerow); | |
1142 return; | |
1143 } | |
1144 else | |
1145 { | |
1146 buddy = (GaimBuddy *)node; | |
1147 } | |
1148 | |
1149 prpl_id = gaim_account_get_protocol_id(buddy->account); | |
1150 | |
1151 gc = gaim_account_get_connection(buddy->account); | |
1152 | |
1153 if (gc == NULL) | |
1154 { | |
1155 gtk_tree_path_free(sourcerow); | |
1156 return; | |
1157 } | |
1158 | |
1159 protocol = | |
1160 GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->list_icon(buddy->account, | |
1161 buddy); | |
1162 | |
1163 str = g_string_new(NULL); | |
1164 g_string_printf(str, | |
1165 "MIME-Version: 1.0\r\n" | |
1166 "Content-Type: application/x-im-contact\r\n" | |
1167 "X-IM-Protocol: %s\r\n" | |
1168 "X-IM-Username: %s\r\n", | |
1169 protocol, | |
1170 buddy->name); | |
1171 | |
1172 if (buddy->alias != NULL) | |
1173 { | |
1174 g_string_append_printf(str, | |
1175 "X-IM-Alias: %s\r\n", | |
1176 buddy->alias); | |
1177 } | |
1178 | |
1179 str = g_string_append(str, "\r\n"); | |
1180 | |
1181 mime_str = g_string_free(str, FALSE); | |
1182 | |
1183 gtk_selection_data_set(data, | |
1184 gdk_atom_intern("application/x-im-contact", FALSE), | |
1185 8, /* bits */ | |
1186 mime_str, | |
1187 strlen(mime_str) + 1); | |
1188 | |
1189 g_free(mime_str); | |
1190 gtk_tree_path_free(sourcerow); | |
1191 } | |
5228 | 1192 } |
1193 | |
1194 static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, | |
1195 GtkSelectionData *sd, guint info, guint t) | |
7620 | 1196 { |
5228 | 1197 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { |
1198 GaimBlistNode *n = NULL; | |
1199 GtkTreePath *path = NULL; | |
1200 GtkTreeViewDropPosition position; | |
1201 memcpy(&n, sd->data, sizeof(n)); | |
1202 if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { | |
7636 | 1203 /* if we're here, I think it means the drop is ok */ |
7642 | 1204 GtkTreeIter iter; |
5228 | 1205 GaimBlistNode *node; |
1206 GValue val = {0}; | |
7620 | 1207 struct _gaim_gtk_blist_node *gtknode; |
1208 | |
1209 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), | |
1210 &iter, path); | |
1211 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), | |
1212 &iter, NODE_COLUMN, &val); | |
5228 | 1213 node = g_value_get_pointer(&val); |
7620 | 1214 gtknode = node->ui_data; |
1215 | |
1216 if (GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
1217 GaimContact *c = (GaimContact*)n; | |
1218 if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded) { | |
1219 gaim_blist_merge_contact(c, node); | |
1220 } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1221 GAIM_BLIST_NODE_IS_CHAT(node)) { |
5228 | 1222 switch(position) { |
1223 case GTK_TREE_VIEW_DROP_AFTER: | |
1224 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
7620 | 1225 gaim_blist_add_contact(c, (GaimGroup*)node->parent, |
1226 node); | |
1227 break; | |
1228 case GTK_TREE_VIEW_DROP_BEFORE: | |
1229 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1230 gaim_blist_add_contact(c, (GaimGroup*)node->parent, | |
1231 node->prev); | |
1232 break; | |
1233 } | |
1234 } else if(GAIM_BLIST_NODE_IS_GROUP(node)) { | |
1235 gaim_blist_add_contact(c, (GaimGroup*)node, NULL); | |
1236 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1237 gaim_blist_merge_contact(c, node); | |
1238 } | |
1239 } else if (GAIM_BLIST_NODE_IS_BUDDY(n)) { | |
1240 GaimBuddy *b = (GaimBuddy*)n; | |
1241 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1242 switch(position) { | |
1243 case GTK_TREE_VIEW_DROP_AFTER: | |
1244 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1245 gaim_blist_add_buddy(b, (GaimContact*)node->parent, | |
1246 (GaimGroup*)node->parent->parent, node); | |
5228 | 1247 break; |
1248 case GTK_TREE_VIEW_DROP_BEFORE: | |
1249 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
7620 | 1250 gaim_blist_add_buddy(b, (GaimContact*)node->parent, |
1251 (GaimGroup*)node->parent->parent, | |
1252 node->prev); | |
5228 | 1253 break; |
1254 } | |
7620 | 1255 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { |
1256 gaim_blist_add_buddy(b, NULL, (GaimGroup*)node->parent, | |
1257 NULL); | |
5228 | 1258 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
7620 | 1259 gaim_blist_add_buddy(b, NULL, (GaimGroup*)node, NULL); |
1260 } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
1261 if(gtknode->contact_expanded) { | |
1262 switch(position) { | |
1263 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1264 case GTK_TREE_VIEW_DROP_AFTER: | |
1265 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1266 gaim_blist_add_buddy(b, (GaimContact*)node, | |
1267 (GaimGroup*)node->parent, NULL); | |
1268 break; | |
1269 case GTK_TREE_VIEW_DROP_BEFORE: | |
1270 gaim_blist_add_buddy(b, NULL, | |
1271 (GaimGroup*)node->parent, node->prev); | |
1272 break; | |
1273 } | |
1274 } else { | |
1275 switch(position) { | |
1276 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1277 case GTK_TREE_VIEW_DROP_AFTER: | |
1278 gaim_blist_add_buddy(b, NULL, | |
1279 (GaimGroup*)node->parent, NULL); | |
1280 break; | |
1281 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1282 case GTK_TREE_VIEW_DROP_BEFORE: | |
1283 gaim_blist_add_buddy(b, NULL, | |
1284 (GaimGroup*)node->parent, node->prev); | |
1285 break; | |
1286 } | |
1287 } | |
5228 | 1288 } |
5234 | 1289 } else if (GAIM_BLIST_NODE_IS_CHAT(n)) { |
7620 | 1290 GaimChat *chat = (GaimChat *)n; |
1291 if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1292 switch(position) { | |
1293 case GTK_TREE_VIEW_DROP_AFTER: | |
1294 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1295 gaim_blist_add_chat(chat, | |
1296 (GaimGroup*)node->parent->parent, node); | |
1297 break; | |
1298 case GTK_TREE_VIEW_DROP_BEFORE: | |
1299 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1300 gaim_blist_add_chat(chat, | |
1301 (GaimGroup*)node->parent->parent, | |
1302 node->prev); | |
1303 break; | |
1304 } | |
1305 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1306 GAIM_BLIST_NODE_IS_CHAT(node)) { |
1307 switch(position) { | |
1308 case GTK_TREE_VIEW_DROP_AFTER: | |
1309 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
7620 | 1310 gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node); |
5234 | 1311 break; |
1312 case GTK_TREE_VIEW_DROP_BEFORE: | |
1313 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
7620 | 1314 gaim_blist_add_chat(chat, (GaimGroup*)node->parent, node->prev); |
5234 | 1315 break; |
1316 } | |
1317 } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { | |
7620 | 1318 gaim_blist_add_chat(chat, (GaimGroup*)node, NULL); |
5234 | 1319 } |
5228 | 1320 } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { |
7620 | 1321 GaimGroup *g = (GaimGroup*)n; |
5228 | 1322 if (GAIM_BLIST_NODE_IS_GROUP(node)) { |
1323 switch (position) { | |
1324 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: | |
1325 case GTK_TREE_VIEW_DROP_AFTER: | |
1326 gaim_blist_add_group(g, node); | |
1327 break; | |
1328 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: | |
1329 case GTK_TREE_VIEW_DROP_BEFORE: | |
1330 gaim_blist_add_group(g, node->prev); | |
1331 break; | |
1332 } | |
7620 | 1333 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { |
1334 gaim_blist_add_group(g, node->parent->parent); | |
1335 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || | |
5234 | 1336 GAIM_BLIST_NODE_IS_CHAT(node)) { |
5228 | 1337 gaim_blist_add_group(g, node->parent); |
1338 } | |
1339 } | |
1340 | |
1341 gtk_tree_path_free(path); | |
7620 | 1342 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
1343 | |
5228 | 1344 gaim_blist_save(); |
1345 } | |
1346 } | |
7706 | 1347 else if (sd->target == gdk_atom_intern("application/x-im-contact", |
1348 FALSE) && sd->data) | |
1349 { | |
1350 GaimGroup *group = NULL; | |
1351 GtkTreePath *path = NULL; | |
1352 GtkTreeViewDropPosition position; | |
7712
2823111061ba
[gaim-migrate @ 8357]
Christian Hammond <chipx86@chipx86.com>
parents:
7706
diff
changeset
|
1353 GaimAccount *account; |
7706 | 1354 char *protocol = NULL; |
1355 char *username = NULL; | |
1356 char *alias = NULL; | |
1357 | |
1358 if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), | |
1359 x, y, &path, &position)) | |
1360 { | |
1361 GtkTreeIter iter; | |
1362 GaimBlistNode *node; | |
1363 GValue val = {0}; | |
1364 | |
1365 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), | |
1366 &iter, path); | |
1367 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), | |
1368 &iter, NODE_COLUMN, &val); | |
1369 node = g_value_get_pointer(&val); | |
1370 | |
1371 if (GAIM_BLIST_NODE_IS_BUDDY(node)) | |
1372 { | |
1373 group = (GaimGroup *)node->parent->parent; | |
1374 } | |
1375 else if (GAIM_BLIST_NODE_IS_CHAT(node) || | |
1376 GAIM_BLIST_NODE_IS_CONTACT(node)) | |
1377 { | |
1378 group = (GaimGroup *)node->parent; | |
1379 } | |
1380 else if (GAIM_BLIST_NODE_IS_GROUP(node)) | |
1381 { | |
1382 group = (GaimGroup *)node; | |
1383 } | |
1384 } | |
1385 | |
7712
2823111061ba
[gaim-migrate @ 8357]
Christian Hammond <chipx86@chipx86.com>
parents:
7706
diff
changeset
|
1386 if (gaim_gtk_parse_x_im_contact(sd->data, FALSE, &account, |
2823111061ba
[gaim-migrate @ 8357]
Christian Hammond <chipx86@chipx86.com>
parents:
7706
diff
changeset
|
1387 &protocol, &username, &alias)) |
7706 | 1388 { |
1389 if (account == NULL) | |
1390 { | |
1391 gaim_notify_error(NULL, NULL, | |
1392 _("You are not currently signed on with an account that " | |
1393 "can add that buddy."), NULL); | |
1394 } | |
1395 else | |
1396 { | |
1397 gaim_blist_request_add_buddy(account, username, | |
1398 (group ? group->name : NULL), | |
1399 alias); | |
1400 } | |
1401 } | |
1402 | |
1403 if (username != NULL) g_free(username); | |
1404 if (protocol != NULL) g_free(protocol); | |
1405 if (alias != NULL) g_free(alias); | |
1406 | |
1407 if (path != NULL) | |
1408 gtk_tree_path_free(path); | |
1409 | |
1410 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); | |
1411 } | |
5228 | 1412 } |
1413 | |
5234 | 1414 static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, GaimBlistNode *node) |
5228 | 1415 { |
1416 GtkStyle *style; | |
5234 | 1417 GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(node, GAIM_STATUS_ICON_LARGE); |
5228 | 1418 PangoLayout *layout; |
5234 | 1419 char *tooltiptext = gaim_get_tooltip_text(node); |
5228 | 1420 |
7620 | 1421 if(!tooltiptext) |
1422 return; | |
1423 | |
5228 | 1424 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); |
1425 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
1426 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
1427 pango_layout_set_width(layout, 300000); | |
1428 style = gtkblist->tipwindow->style; | |
1429 | |
1430 gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, | |
1431 NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); | |
1432 | |
1433 #if GTK_CHECK_VERSION(2,2,0) | |
1434 gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, | |
1435 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); | |
1436 #else | |
1437 gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); | |
1438 #endif | |
1439 | |
1440 gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, | |
1441 NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); | |
1442 | |
1443 g_object_unref (pixbuf); | |
1444 g_object_unref (layout); | |
1445 g_free(tooltiptext); | |
7620 | 1446 |
7662 | 1447 #ifdef WANT_DROP_SHADOW |
7620 | 1448 shadow_paint(gtkblist, NULL, EAST_SIDE); |
1449 shadow_paint(gtkblist, NULL, SOUTH_SIDE); | |
1450 #endif | |
1451 | |
5228 | 1452 return; |
1453 } | |
1454 | |
1455 static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) | |
1456 { | |
1457 GtkTreePath *path; | |
1458 GtkTreeIter iter; | |
1459 GaimBlistNode *node; | |
1460 GValue val = {0}; | |
5234 | 1461 int scr_w,scr_h, w, h, x, y; |
1462 PangoLayout *layout; | |
7636 | 1463 gboolean tooltip_top = FALSE; |
5234 | 1464 char *tooltiptext = NULL; |
7636 | 1465 struct _gaim_gtk_blist_node *gtknode; |
7662 | 1466 #ifdef WANT_DROP_SHADOW |
7620 | 1467 GdkWindowAttr attr; |
1468 #endif | |
5228 | 1469 |
7636 | 1470 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 | 1471 return FALSE; |
1472 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); | |
1473 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); | |
1474 node = g_value_get_pointer(&val); | |
7620 | 1475 |
1476 if(!GAIM_BLIST_NODE_IS_CONTACT(node) && !GAIM_BLIST_NODE_IS_BUDDY(node) | |
1477 && !GAIM_BLIST_NODE_IS_CHAT(node)) | |
5234 | 1478 return FALSE; |
5228 | 1479 |
7636 | 1480 gtknode = node->ui_data; |
1481 | |
7731 | 1482 if (node->child && GAIM_BLIST_NODE_IS_CONTACT(node) && |
1483 ((GaimContact*)node)->online > 1 && !gtknode->contact_expanded && | |
1484 gaim_prefs_get_bool("/gaim/gtk/blist/auto_expand_contacts")) { | |
7719 | 1485 GtkTreeIter i; |
7636 | 1486 gaim_gtk_blist_expand_contact_cb(NULL, node); |
1487 tooltip_top = TRUE; /* When the person expands, the new screennames will be below. We'll draw the tip above | |
1488 the cursor so that the user can see the included buddies */ | |
1489 | |
1490 while (gtk_events_pending()) | |
1491 gtk_main_iteration(); | |
1492 | |
1493 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->contact_rect); | |
7720 | 1494 gdk_drawable_get_size(GDK_DRAWABLE(tv->window), &(gtkblist->contact_rect.width), NULL); |
7636 | 1495 gtkblist->mouseover_contact = node; |
1496 gtk_tree_path_down (path); | |
7719 | 1497 while (gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &i, path)) { |
7636 | 1498 GdkRectangle rect; |
1499 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect); | |
1500 gtkblist->contact_rect.height += rect.height; | |
1501 gtk_tree_path_next(path); | |
1502 } | |
1503 } | |
1504 | |
1505 gtk_tree_path_free(path); | |
1506 | |
5234 | 1507 tooltiptext = gaim_get_tooltip_text(node); |
7620 | 1508 |
1509 if(!tooltiptext) | |
1510 return FALSE; | |
1511 | |
5234 | 1512 gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); |
1513 gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); | |
1514 gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); | |
1515 gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); | |
1516 g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", | |
1517 G_CALLBACK(gaim_gtk_blist_paint_tip), node); | |
1518 gtk_widget_ensure_style (gtkblist->tipwindow); | |
7620 | 1519 |
7662 | 1520 #ifdef WANT_DROP_SHADOW |
7620 | 1521 attr.window_type = GDK_WINDOW_TEMP; |
1522 attr.override_redirect = TRUE; | |
1523 attr.x = gtkblist->tipwindow->allocation.x; | |
1524 attr.y = gtkblist->tipwindow->allocation.y; | |
1525 attr.width = gtkblist->tipwindow->allocation.width; | |
1526 attr.height = gtkblist->tipwindow->allocation.height; | |
1527 attr.wclass = GDK_INPUT_OUTPUT; | |
1528 attr.visual = gtk_widget_get_visual (gtkblist->window); | |
1529 attr.colormap = gtk_widget_get_colormap (gtkblist->window); | |
1530 | |
1531 attr.event_mask = gtk_widget_get_events (gtkblist->tipwindow); | |
1532 | |
1533 attr.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | | |
1534 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); | |
1535 gtkblist->east_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, | |
1536 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); | |
1537 gdk_window_set_user_data (gtkblist->east_shadow, gtkblist->tipwindow); | |
1538 gdk_window_set_back_pixmap (gtkblist->east_shadow, NULL, FALSE); | |
1539 | |
1540 gtkblist->south_shadow = gdk_window_new(gtk_widget_get_root_window(gtkblist->tipwindow), &attr, | |
1541 GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP); | |
1542 gdk_window_set_user_data (gtkblist->south_shadow, gtkblist->tipwindow); | |
1543 gdk_window_set_back_pixmap (gtkblist->south_shadow, NULL, FALSE); | |
1544 #endif | |
1545 | |
5234 | 1546 layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); |
1547 pango_layout_set_wrap(layout, PANGO_WRAP_WORD); | |
1548 pango_layout_set_width(layout, 300000); | |
1549 pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); | |
1550 scr_w = gdk_screen_width(); | |
1551 scr_h = gdk_screen_height(); | |
1552 pango_layout_get_size (layout, &w, &h); | |
1553 w = PANGO_PIXELS(w) + 8; | |
1554 h = PANGO_PIXELS(h) + 8; | |
5228 | 1555 |
5234 | 1556 /* 38 is the size of a large status icon plus 4 pixels padding on each side. |
1557 * I should #define this or something */ | |
1558 w = w + 38; | |
1559 h = MAX(h, 38); | |
1560 | |
1561 gdk_window_get_pointer(NULL, &x, &y, NULL); | |
1562 if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) | |
1563 y+=gtkblist->window->allocation.y; | |
1564 | |
1565 x -= ((w >> 1) + 4); | |
5228 | 1566 |
7636 | 1567 if ((y + h + 4) > scr_h || tooltip_top) |
7620 | 1568 y = y - h - 5; |
5234 | 1569 else |
1570 y = y + 6; | |
7620 | 1571 |
7719 | 1572 if (y < 0) |
1573 y = 0; | |
1574 | |
1575 if (y != 0) { | |
1576 if ((x + w) > scr_w) | |
1577 x -= (x + w + 5) - scr_w; | |
1578 else if (x < 0) | |
1579 x = 0; | |
1580 } else { | |
1581 x -= (w / 2 + 10); | |
1582 if (x < 0) | |
1583 x += w + 15; | |
1584 } | |
1585 | |
5234 | 1586 g_object_unref (layout); |
1587 g_free(tooltiptext); | |
1588 gtk_widget_set_size_request(gtkblist->tipwindow, w, h); | |
1589 gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); | |
1590 gtk_widget_show(gtkblist->tipwindow); | |
5228 | 1591 |
7662 | 1592 #ifdef WANT_DROP_SHADOW |
7620 | 1593 map_shadow_windows(gtkblist); |
1594 #endif | |
1595 | |
5228 | 1596 return FALSE; |
1597 } | |
1598 | |
1599 static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) | |
1600 { | |
1601 GtkTreePath *path; | |
1602 if (gtkblist->timeout) { | |
7636 | 1603 if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y)) |
5228 | 1604 return FALSE; |
1605 /* We've left the cell. Remove the timeout and create a new one below */ | |
1606 if (gtkblist->tipwindow) { | |
1607 gtk_widget_destroy(gtkblist->tipwindow); | |
7662 | 1608 #ifdef WANT_DROP_SHADOW |
7620 | 1609 gdk_window_set_user_data (gtkblist->east_shadow, NULL); |
1610 gdk_window_destroy (gtkblist->east_shadow); | |
1611 gtkblist->east_shadow = NULL; | |
1612 | |
1613 gdk_window_set_user_data (gtkblist->south_shadow, NULL); | |
1614 gdk_window_destroy (gtkblist->south_shadow); | |
1615 gtkblist->south_shadow = NULL; | |
1616 #endif | |
5228 | 1617 gtkblist->tipwindow = NULL; |
1618 } | |
7620 | 1619 |
5228 | 1620 g_source_remove(gtkblist->timeout); |
1621 } | |
1622 | |
1623 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); | |
7636 | 1624 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->tip_rect); |
5228 | 1625 if (path) |
1626 gtk_tree_path_free(path); | |
1627 gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); | |
7636 | 1628 |
1629 if (gtkblist->mouseover_contact) { | |
1630 if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) { | |
1631 gaim_gtk_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); | |
1632 gtkblist->mouseover_contact = NULL; | |
1633 } | |
1634 } | |
1635 | |
5228 | 1636 return FALSE; |
1637 } | |
1638 | |
1639 static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) | |
1640 { | |
1641 if (gtkblist->timeout) { | |
1642 g_source_remove(gtkblist->timeout); | |
1643 gtkblist->timeout = 0; | |
1644 } | |
1645 if (gtkblist->tipwindow) { | |
1646 gtk_widget_destroy(gtkblist->tipwindow); | |
7662 | 1647 #ifdef WANT_DROP_SHADOW |
7620 | 1648 gdk_window_set_user_data (gtkblist->east_shadow, NULL); |
1649 gdk_window_destroy (gtkblist->east_shadow); | |
1650 gtkblist->east_shadow = NULL; | |
1651 | |
1652 gdk_window_set_user_data (gtkblist->south_shadow, NULL); | |
1653 gdk_window_destroy (gtkblist->south_shadow); | |
1654 gtkblist->south_shadow = NULL; | |
1655 #endif | |
5228 | 1656 gtkblist->tipwindow = NULL; |
1657 } | |
7720 | 1658 |
1659 if (gtkblist->mouseover_contact && | |
1660 !((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) && | |
1661 (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) { | |
1662 gaim_gtk_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact); | |
7636 | 1663 gtkblist->mouseover_contact = NULL; |
1664 } | |
5228 | 1665 } |
1666 | |
1667 static void | |
1668 toggle_debug(void) | |
1669 { | |
7620 | 1670 gaim_prefs_set_bool("/gaim/gtk/debug/enabled", |
1671 !gaim_prefs_get_bool("/gaim/gtk/debug/enabled")); | |
5228 | 1672 } |
1673 | |
1674 | |
1675 /*************************************************** | |
1676 * Crap * | |
1677 ***************************************************/ | |
1678 static GtkItemFactoryEntry blist_menu[] = | |
1679 { | |
1680 /* Buddies menu */ | |
1681 { N_("/_Buddies"), NULL, NULL, 0, "<Branch>" }, | |
1682 { N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0, "<StockItem>", GAIM_STOCK_IM }, | |
1683 { N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0, "<StockItem>", GAIM_STOCK_CHAT }, | |
1684 { N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0, "<StockItem>", GAIM_STOCK_INFO }, | |
1685 { "/Buddies/sep1", NULL, NULL, 0, "<Separator>" }, | |
5398 | 1686 { N_("/Buddies/Show _Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, "<CheckItem>"}, |
5228 | 1687 { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, "<CheckItem>"}, |
5307 | 1688 { N_("/Buddies/_Add a Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0, "<StockItem>", GTK_STOCK_ADD }, |
1689 { N_("/Buddies/Add a C_hat..."), NULL, gaim_gtk_blist_add_chat_cb, 0, "<StockItem>", GTK_STOCK_ADD }, | |
7620 | 1690 { N_("/Buddies/Add a _Group..."), NULL, gaim_blist_request_add_group, 0, NULL}, |
5228 | 1691 { "/Buddies/sep2", NULL, NULL, 0, "<Separator>" }, |
7620 | 1692 { N_("/Buddies/_Signoff"), "<CTL>D", gaim_connections_disconnect_all, 0, "<StockItem>", GAIM_STOCK_SIGN_OFF }, |
1693 { N_("/Buddies/_Quit"), "<CTL>Q", gaim_core_quit, 0, "<StockItem>", GTK_STOCK_QUIT }, | |
5228 | 1694 |
1695 /* Tools */ | |
1696 { N_("/_Tools"), NULL, NULL, 0, "<Branch>" }, | |
1697 { N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" }, | |
1698 { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" }, | |
1699 { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" }, | |
1700 { "/Tools/sep1", NULL, NULL, 0, "<Separator>" }, | |
7620 | 1701 { N_("/Tools/A_ccounts"), "<CTL>A", gaim_gtk_accounts_window_show, 0, "<StockItem>", GAIM_STOCK_ACCOUNTS }, |
7737 | 1702 { N_("/Tools/_File Transfers"), NULL, gaim_show_xfer_dialog, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, |
7620 | 1703 { N_("/Tools/Preferences"), "<CTL>P", gaim_gtk_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, |
1704 { N_("/Tools/Pr_ivacy"), NULL, gaim_gtk_privacy_dialog_show, 0, "<StockItem>", GAIM_STOCK_PRIVACY }, | |
5228 | 1705 { "/Tools/sep2", NULL, NULL, 0, "<Separator>" }, |
7620 | 1706 { N_("/Tools/View System _Log"), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, |
5228 | 1707 |
1708 /* Help */ | |
1709 { N_("/_Help"), NULL, NULL, 0, "<Branch>" }, | |
1710 { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP }, | |
7620 | 1711 { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, NULL }, |
1712 { N_("/Help/_About"), NULL, show_about, 0, "<StockItem>", GAIM_STOCK_ABOUT }, | |
5228 | 1713 }; |
1714 | |
1715 /********************************************************* | |
1716 * Private Utility functions * | |
1717 *********************************************************/ | |
7620 | 1718 static void |
1719 rename_group_cb(GaimGroup *g, const char *new_name) | |
1720 { | |
1721 gaim_blist_rename_group(g, new_name); | |
1722 gaim_blist_save(); | |
1723 } | |
1724 | |
1725 static void | |
1726 show_rename_group(GtkWidget *unused, GaimGroup *g) | |
1727 { | |
1728 gaim_request_input(NULL, _("Rename Group"), _("New group name"), | |
1729 _("Please enter a new name for the selected group."), | |
1730 g->name, FALSE, FALSE, | |
1731 _("OK"), G_CALLBACK(rename_group_cb), | |
1732 _("Cancel"), NULL, g); | |
1733 } | |
5228 | 1734 |
5234 | 1735 static char *gaim_get_tooltip_text(GaimBlistNode *node) |
5228 | 1736 { |
5237 | 1737 GaimPlugin *prpl; |
1738 GaimPluginProtocolInfo *prpl_info = NULL; | |
5228 | 1739 char *text = NULL; |
7636 | 1740 |
5234 | 1741 if(GAIM_BLIST_NODE_IS_CHAT(node)) { |
7620 | 1742 GaimChat *chat = (GaimChat *)node; |
5237 | 1743 char *name = NULL; |
5274 | 1744 struct proto_chat_entry *pce; |
1745 GList *parts, *tmp; | |
1746 GString *parts_text = g_string_new(""); | |
1747 | |
7620 | 1748 prpl = gaim_find_prpl(gaim_account_get_protocol(chat->account)); |
5274 | 1749 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); |
1750 | |
1751 parts = prpl_info->chat_info(chat->account->gc); | |
5237 | 1752 |
7620 | 1753 name = g_markup_escape_text(gaim_chat_get_name(chat), -1); |
1754 | |
1755 if(g_list_length(gaim_connections_get_all()) > 1) { | |
5274 | 1756 char *account = g_markup_escape_text(chat->account->username, -1); |
1757 g_string_append_printf(parts_text, _("\n<b>Account:</b> %s"), | |
1758 account); | |
1759 g_free(account); | |
5237 | 1760 } |
5274 | 1761 for(tmp = parts; tmp; tmp = tmp->next) { |
1762 char *label, *value; | |
1763 pce = tmp->data; | |
5237 | 1764 |
7620 | 1765 if(pce->secret) |
1766 continue; | |
1767 | |
5274 | 1768 label = g_markup_escape_text(pce->label, -1); |
1769 | |
1770 value = g_markup_escape_text(g_hash_table_lookup(chat->components, | |
1771 pce->identifier), -1); | |
1772 | |
1773 g_string_append_printf(parts_text, "\n<b>%s</b> %s", label, value); | |
1774 g_free(label); | |
1775 g_free(value); | |
1776 g_free(pce); | |
1777 } | |
1778 g_list_free(parts); | |
1779 | |
1780 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>%s", | |
1781 name, parts_text->str); | |
1782 g_string_free(parts_text, TRUE); | |
5237 | 1783 g_free(name); |
7620 | 1784 } else if(GAIM_BLIST_NODE_IS_CONTACT(node) || |
1785 GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1786 GaimBuddy *b; | |
5234 | 1787 char *statustext = NULL; |
7620 | 1788 char *contactaliastext = NULL; |
5234 | 1789 char *aliastext = NULL, *nicktext = NULL; |
1790 char *warning = NULL, *idletime = NULL; | |
5274 | 1791 char *accounttext = NULL; |
5228 | 1792 |
7620 | 1793 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { |
1794 GaimContact *contact = (GaimContact*)node; | |
1795 b = gaim_contact_get_priority_buddy(contact); | |
1796 if(contact->alias) | |
1797 contactaliastext = g_markup_escape_text(contact->alias, -1); | |
1798 } else { | |
1799 b = (GaimBuddy *)node; | |
1800 } | |
1801 | |
1802 prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); | |
5234 | 1803 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); |
1804 | |
7620 | 1805 if (prpl_info && prpl_info->tooltip_text) { |
5234 | 1806 const char *end; |
1807 statustext = prpl_info->tooltip_text(b); | |
5228 | 1808 |
5234 | 1809 if(statustext && !g_utf8_validate(statustext, -1, &end)) { |
1810 char *new = g_strndup(statustext, | |
1811 g_utf8_pointer_to_offset(statustext, end)); | |
1812 g_free(statustext); | |
1813 statustext = new; | |
1814 } | |
1815 } | |
1816 | |
1817 if (!statustext && !GAIM_BUDDY_IS_ONLINE(b)) | |
1818 statustext = g_strdup(_("<b>Status:</b> Offline")); | |
5228 | 1819 |
5341 | 1820 if (b->idle > 0) |
7620 | 1821 idletime = gaim_str_seconds_to_string(time(NULL) - b->idle); |
5228 | 1822 |
5234 | 1823 if(b->alias && b->alias[0]) |
1824 aliastext = g_markup_escape_text(b->alias, -1); | |
5228 | 1825 |
5234 | 1826 if(b->server_alias) |
1827 nicktext = g_markup_escape_text(b->server_alias, -1); | |
5228 | 1828 |
5234 | 1829 if (b->evil > 0) |
1830 warning = g_strdup_printf(_("%d%%"), b->evil); | |
5228 | 1831 |
7620 | 1832 if(g_list_length(gaim_connections_get_all()) > 1) |
5274 | 1833 accounttext = g_markup_escape_text(b->account->username, -1); |
1834 | |
5234 | 1835 text = g_strdup_printf("<span size='larger' weight='bold'>%s</span>" |
5438 | 1836 "%s %s" /* Account */ |
7620 | 1837 "%s %s" /* Contact Alias */ |
5438 | 1838 "%s %s" /* Alias */ |
1839 "%s %s" /* Nickname */ | |
1840 "%s %s" /* Idle */ | |
1841 "%s %s" /* Warning */ | |
1842 "%s%s" /* Status */ | |
1843 "%s", | |
1844 b->name, | |
1845 accounttext ? _("\n<b>Account:</b>") : "", accounttext ? accounttext : "", | |
7620 | 1846 contactaliastext ? _("\n<b>Contact Alias:</b>") : "", contactaliastext ? contactaliastext : "", |
5438 | 1847 aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "", |
1848 nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "", | |
1849 idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "", | |
1850 b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "", | |
1851 statustext ? "\n" : "", statustext ? statustext : "", | |
1852 !g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") : | |
7620 | 1853 !g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") : |
1854 !g_ascii_strcasecmp(b->name, "chipx86") ? _("\n<b>Status</b>: Rockin'") : ""); | |
5234 | 1855 |
1856 if(warning) | |
1857 g_free(warning); | |
1858 if(idletime) | |
1859 g_free(idletime); | |
1860 if(statustext) | |
1861 g_free(statustext); | |
1862 if(nicktext) | |
1863 g_free(nicktext); | |
1864 if(aliastext) | |
1865 g_free(aliastext); | |
5274 | 1866 if(accounttext) |
1867 g_free(accounttext); | |
5234 | 1868 } |
5228 | 1869 |
1870 return text; | |
1871 } | |
1872 | |
7620 | 1873 struct _emblem_data { |
1874 char *filename; | |
1875 int x; | |
1876 int y; | |
1877 }; | |
1878 | |
5234 | 1879 GdkPixbuf *gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size) |
5228 | 1880 { |
7620 | 1881 GdkPixbuf *scale, *status = NULL; |
1882 int i, scalesize = 30; | |
1883 char *filename; | |
5228 | 1884 const char *protoname = NULL; |
7620 | 1885 struct _gaim_gtk_blist_node *gtknode = node->ui_data; |
1886 struct _emblem_data emblems[4] = {{NULL, 15, 15}, {NULL, 0, 15}, | |
1887 {NULL, 0, 0}, {NULL, 15, 0}}; | |
1888 | |
1889 GaimBuddy *buddy = NULL; | |
1890 GaimChat *chat = NULL; | |
1891 | |
1892 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
1893 if(!gtknode->contact_expanded) | |
1894 buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
1895 } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1896 buddy = (GaimBuddy*)node; | |
1897 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
1898 chat = (GaimChat*)node; | |
1899 } else { | |
5228 | 1900 return NULL; |
5234 | 1901 } |
1902 | |
7620 | 1903 if(buddy || chat) { |
1904 GaimAccount *account; | |
1905 GaimPlugin *prpl; | |
1906 GaimPluginProtocolInfo *prpl_info; | |
1907 | |
1908 if(buddy) | |
1909 account = buddy->account; | |
1910 else | |
1911 account = chat->account; | |
1912 | |
1913 prpl = gaim_find_prpl(gaim_account_get_protocol(account)); | |
1914 if(!prpl) | |
1915 return NULL; | |
1916 | |
1917 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
1918 | |
1919 if(prpl_info && prpl_info->list_icon) { | |
1920 protoname = prpl_info->list_icon(account, buddy); | |
1921 } | |
1922 if(prpl_info && prpl_info->list_emblems && buddy) { | |
1923 if(buddy->present != GAIM_BUDDY_SIGNING_OFF) | |
1924 prpl_info->list_emblems(buddy, &emblems[0].filename, | |
1925 &emblems[1].filename, &emblems[2].filename, | |
1926 &emblems[3].filename); | |
1927 } | |
5234 | 1928 } |
5228 | 1929 |
7620 | 1930 if(size == GAIM_STATUS_ICON_SMALL) { |
5228 | 1931 scalesize = 15; |
7620 | 1932 /* So that only the se icon will composite */ |
1933 emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL; | |
5228 | 1934 } |
1935 | |
7620 | 1936 if(buddy && buddy->present == GAIM_BUDDY_SIGNING_ON) { |
1937 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); | |
1938 } else if(buddy && buddy->present == GAIM_BUDDY_SIGNING_OFF) { | |
1939 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); | |
1940 } else if(buddy || chat) { | |
5228 | 1941 char *image = g_strdup_printf("%s.png", protoname); |
1942 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1943 g_free(image); | |
7620 | 1944 } else { |
1945 /* gaim dude */ | |
1946 filename = g_build_filename(DATADIR, "pixmaps", "gaim.png", NULL); | |
5228 | 1947 } |
7620 | 1948 |
1949 status = gdk_pixbuf_new_from_file(filename, NULL); | |
1950 g_free(filename); | |
1951 | |
1952 if(!status) | |
1953 return NULL; | |
1954 | |
1955 scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, | |
1956 GDK_INTERP_BILINEAR); | |
1957 g_object_unref(status); | |
1958 | |
1959 for(i=0; i<4; i++) { | |
1960 if(emblems[i].filename) { | |
1961 GdkPixbuf *emblem; | |
1962 char *image = g_strdup_printf("%s.png", emblems[i].filename); | |
1963 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); | |
1964 g_free(image); | |
1965 emblem = gdk_pixbuf_new_from_file(filename, NULL); | |
1966 g_free(filename); | |
1967 if(emblem) { | |
1968 if(i == 0 && size == GAIM_STATUS_ICON_SMALL) { | |
1969 gdk_pixbuf_composite(emblem, | |
1970 scale, 5, 5, | |
1971 10, 10, | |
1972 5, 5, | |
1973 .6, .6, | |
1974 GDK_INTERP_BILINEAR, | |
1975 255); | |
1976 } else { | |
1977 gdk_pixbuf_composite(emblem, | |
1978 scale, emblems[i].x, emblems[i].y, | |
1979 15, 15, | |
1980 emblems[i].x, emblems[i].y, | |
1981 1, 1, | |
1982 GDK_INTERP_BILINEAR, | |
1983 255); | |
1984 } | |
1985 g_object_unref(emblem); | |
1986 } | |
5228 | 1987 } |
1988 } | |
7620 | 1989 |
1990 if(buddy) { | |
1991 if(buddy->present == GAIM_BUDDY_OFFLINE) | |
1992 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); | |
1993 else if(buddy->idle && | |
1994 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) | |
1995 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); | |
5228 | 1996 } |
7620 | 1997 |
5228 | 1998 return scale; |
1999 } | |
2000 | |
7620 | 2001 static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(GaimBuddy *b) |
5228 | 2002 { |
7620 | 2003 const char *file; |
5228 | 2004 GdkPixbuf *buf, *ret; |
2005 | |
7620 | 2006 if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) |
5228 | 2007 return NULL; |
2008 | |
7693 | 2009 if ((file = gaim_blist_node_get_string((GaimBlistNode*)b, "buddy_icon")) == NULL) |
5228 | 2010 return NULL; |
2011 | |
2012 buf = gdk_pixbuf_new_from_file(file, NULL); | |
2013 | |
2014 | |
2015 if (buf) { | |
2016 if (!GAIM_BUDDY_IS_ONLINE(b)) | |
2017 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); | |
7620 | 2018 if (b->idle && gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) |
5228 | 2019 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); |
2020 | |
2021 ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); | |
2022 g_object_unref(G_OBJECT(buf)); | |
2023 return ret; | |
2024 } | |
2025 return NULL; | |
2026 } | |
2027 | |
7620 | 2028 static gchar *gaim_gtk_blist_get_name_markup(GaimBuddy *b, gboolean selected) |
5228 | 2029 { |
7620 | 2030 const char *name; |
2031 char *esc, *text = NULL; | |
5228 | 2032 GaimPlugin *prpl; |
2033 GaimPluginProtocolInfo *prpl_info = NULL; | |
7620 | 2034 GaimContact *contact; |
2035 struct _gaim_gtk_blist_node *gtkcontactnode = NULL; | |
5228 | 2036 int ihrs, imin; |
2037 char *idletime = NULL, *warning = NULL, *statustext = NULL; | |
2038 time_t t; | |
7620 | 2039 /* XXX Clean up this crap */ |
2040 | |
2041 contact = (GaimContact*)((GaimBlistNode*)b)->parent; | |
2042 if(contact) | |
2043 gtkcontactnode = ((GaimBlistNode*)contact)->ui_data; | |
2044 | |
2045 if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) | |
2046 name = contact->alias; | |
2047 else | |
2048 name = gaim_get_buddy_alias(b); | |
2049 esc = g_markup_escape_text(name, strlen(name)); | |
2050 | |
2051 prpl = gaim_find_prpl(gaim_account_get_protocol(b->account)); | |
5228 | 2052 |
2053 if (prpl != NULL) | |
2054 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
2055 | |
7620 | 2056 if (!gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { |
2057 if ((b->idle && !selected && | |
2058 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) || | |
2059 !GAIM_BUDDY_IS_ONLINE(b)) { | |
2060 if (selected) | |
2061 text = g_strdup(esc); | |
2062 else | |
2063 text = g_strdup_printf("<span color='dim grey'>%s</span>", | |
2064 esc); | |
5228 | 2065 g_free(esc); |
2066 return text; | |
7620 | 2067 } |
2068 else { | |
5228 | 2069 return esc; |
2070 } | |
2071 } | |
2072 | |
2073 time(&t); | |
2074 ihrs = (t - b->idle) / 3600; | |
2075 imin = ((t - b->idle) / 60) % 60; | |
2076 | |
7620 | 2077 if (prpl && prpl_info->status_text && b->account->gc) { |
5228 | 2078 char *tmp = prpl_info->status_text(b); |
2079 const char *end; | |
2080 | |
2081 if(tmp && !g_utf8_validate(tmp, -1, &end)) { | |
2082 char *new = g_strndup(tmp, | |
2083 g_utf8_pointer_to_offset(tmp, end)); | |
2084 g_free(tmp); | |
2085 tmp = new; | |
2086 } | |
2087 | |
2088 if(tmp) { | |
2089 char buf[32]; | |
2090 char *c = tmp; | |
2091 int length = 0, vis=0; | |
2092 gboolean inside = FALSE; | |
2093 g_strdelimit(tmp, "\n", ' '); | |
7620 | 2094 gaim_str_strip_cr(tmp); |
5228 | 2095 |
2096 while(*c && vis < 20) { | |
2097 if(*c == '&') | |
2098 inside = TRUE; | |
2099 else if(*c == ';') | |
2100 inside = FALSE; | |
2101 if(!inside) | |
2102 vis++; | |
7620 | 2103 c = g_utf8_next_char(c); /* this is fun */ |
5228 | 2104 } |
2105 | |
7620 | 2106 length = c - tmp; |
2107 | |
5228 | 2108 if(vis == 20) |
2109 g_snprintf(buf, sizeof(buf), "%%.%ds...", length); | |
2110 else | |
2111 g_snprintf(buf, sizeof(buf), "%%s "); | |
2112 | |
2113 statustext = g_strdup_printf(buf, tmp); | |
2114 | |
2115 g_free(tmp); | |
2116 } | |
2117 } | |
2118 | |
7620 | 2119 if (b->idle > 0 && |
2120 gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")) { | |
5228 | 2121 if (ihrs) |
2122 idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); | |
2123 else | |
2124 idletime = g_strdup_printf(_("Idle (%dm) "), imin); | |
2125 } | |
2126 | |
7620 | 2127 if (b->evil > 0 && |
2128 gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")) | |
5228 | 2129 warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); |
2130 | |
2131 if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) | |
7620 | 2132 statustext = g_strdup(_("Offline ")); |
2133 | |
2134 if (b->idle && !selected && | |
2135 gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies")) { | |
2136 | |
5228 | 2137 text = g_strdup_printf("<span color='dim grey'>%s</span>\n" |
2138 "<span color='dim grey' size='smaller'>%s%s%s</span>", | |
2139 esc, | |
2140 statustext != NULL ? statustext : "", | |
2141 idletime != NULL ? idletime : "", | |
2142 warning != NULL ? warning : ""); | |
7620 | 2143 } else if (statustext == NULL && idletime == NULL && warning == NULL && |
2144 GAIM_BUDDY_IS_ONLINE(b)) { | |
5228 | 2145 text = g_strdup(esc); |
2146 } else { | |
2147 text = g_strdup_printf("%s\n" | |
2148 "<span %s size='smaller'>%s%s%s</span>", esc, | |
2149 selected ? "" : "color='dim grey'", | |
2150 statustext != NULL ? statustext : "", | |
7620 | 2151 idletime != NULL ? idletime : "", |
5228 | 2152 warning != NULL ? warning : ""); |
2153 } | |
2154 if (idletime) | |
2155 g_free(idletime); | |
2156 if (warning) | |
2157 g_free(warning); | |
2158 if (statustext) | |
2159 g_free(statustext); | |
2160 if (esc) | |
2161 g_free(esc); | |
2162 | |
2163 return text; | |
2164 } | |
2165 | |
2166 static void gaim_gtk_blist_restore_position() | |
2167 { | |
7620 | 2168 int blist_x, blist_y, blist_width, blist_height; |
2169 | |
2170 blist_width = gaim_prefs_get_int("/gaim/gtk/blist/width"); | |
2171 | |
2172 /* if the window exists, is hidden, we're saving positions, and the | |
2173 * position is sane... */ | |
2174 if (gtkblist && gtkblist->window && | |
2175 !GTK_WIDGET_VISIBLE(gtkblist->window) && blist_width != 0) { | |
2176 | |
2177 blist_x = gaim_prefs_get_int("/gaim/gtk/blist/x"); | |
2178 blist_y = gaim_prefs_get_int("/gaim/gtk/blist/y"); | |
2179 blist_height = gaim_prefs_get_int("/gaim/gtk/blist/height"); | |
2180 | |
5228 | 2181 /* ...check position is on screen... */ |
7620 | 2182 if (blist_x >= gdk_screen_width()) |
2183 blist_x = gdk_screen_width() - 100; | |
2184 else if (blist_x + blist_width < 0) | |
2185 blist_x = 100; | |
2186 | |
2187 if (blist_y >= gdk_screen_height()) | |
2188 blist_y = gdk_screen_height() - 100; | |
2189 else if (blist_y + blist_height < 0) | |
2190 blist_y = 100; | |
2191 | |
5228 | 2192 /* ...and move it back. */ |
7620 | 2193 gtk_window_move(GTK_WINDOW(gtkblist->window), blist_x, blist_y); |
2194 gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_width, blist_height); | |
5228 | 2195 } |
2196 } | |
2197 | |
7620 | 2198 static gboolean gaim_gtk_blist_refresh_timer(GaimBuddyList *list) |
5228 | 2199 { |
7620 | 2200 GaimBlistNode *gnode, *cnode; |
2201 | |
2202 for(gnode = list->root; gnode; gnode = gnode->next) { | |
2203 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
5234 | 2204 continue; |
7620 | 2205 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
2206 if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2207 GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)cnode); | |
2208 if(buddy && buddy->idle) | |
2209 gaim_gtk_blist_update(list, cnode); | |
2210 } | |
5228 | 2211 } |
2212 } | |
2213 | |
2214 /* keep on going */ | |
2215 return TRUE; | |
2216 } | |
2217 | |
7620 | 2218 static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node) |
5260 | 2219 { |
2220 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2221 GtkTreeIter iter; | |
2222 | |
2223 if (!gtknode || !gtknode->row || !gtkblist) | |
2224 return; | |
2225 | |
2226 if(gtkblist->selected_node == node) | |
2227 gtkblist->selected_node = NULL; | |
2228 | |
2229 if (get_iter_from_node(node, &iter)) { | |
2230 gtk_tree_store_remove(gtkblist->treemodel, &iter); | |
7620 | 2231 if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node) |
2232 || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
5260 | 2233 gaim_gtk_blist_update(list, node->parent); |
2234 } | |
2235 } | |
2236 gtk_tree_row_reference_free(gtknode->row); | |
2237 gtknode->row = NULL; | |
2238 } | |
2239 | |
7620 | 2240 static void |
2241 signed_on_off_cb(GaimConnection *gc, GaimBuddyList *blist) | |
2242 { | |
2243 gaim_gtk_blist_update_protocol_actions(); | |
2244 } | |
2245 | |
2246 /* this is called on all sorts of signals, and we have no reason to pass | |
2247 * it anything, so it remains without arguments. If you need anything | |
2248 * more specific, do as below, and create another callback that calls | |
2249 * this */ | |
2250 static void | |
2251 raise_on_events_cb() | |
2252 { | |
2253 if(gtkblist && gtkblist->window && | |
2254 gaim_prefs_get_bool("/gaim/gtk/blist/raise_on_events")) { | |
2255 gtk_widget_show(gtkblist->window); | |
2256 gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); | |
2257 gdk_window_raise(gtkblist->window->window); | |
2258 } | |
2259 } | |
2260 | |
5260 | 2261 |
5228 | 2262 /********************************************************************************** |
2263 * Public API Functions * | |
2264 **********************************************************************************/ | |
7620 | 2265 static void gaim_gtk_blist_new_list(GaimBuddyList *blist) |
5228 | 2266 { |
7620 | 2267 GaimGtkBuddyList *gtkblist; |
2268 | |
2269 gtkblist = g_new0(GaimGtkBuddyList, 1); | |
2270 blist->ui_data = gtkblist; | |
2271 | |
2272 /* Setup some gaim signal handlers. */ | |
2273 gaim_signal_connect(gaim_connections_get_handle(), "signing-on", | |
2274 gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); | |
2275 gaim_signal_connect(gaim_connections_get_handle(), "signing-off", | |
2276 gtkblist, GAIM_CALLBACK(signed_on_off_cb), blist); | |
2277 | |
2278 /* Register some of our own. */ | |
2279 gaim_signal_register(gtkblist, "drawing-menu", | |
2280 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
2281 gaim_value_new(GAIM_TYPE_BOXED, "GtkMenu"), | |
2282 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2283 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2284 | |
2285 /* All of these signal handlers are for the "Raise on Events" option */ | |
2286 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", | |
2287 gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); | |
2288 gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", | |
2289 gtkblist, GAIM_CALLBACK(raise_on_events_cb), NULL); | |
5228 | 2290 } |
2291 | |
5256 | 2292 static void gaim_gtk_blist_new_node(GaimBlistNode *node) |
2293 { | |
2294 node->ui_data = g_new0(struct _gaim_gtk_blist_node, 1); | |
2295 } | |
2296 | |
5228 | 2297 void gaim_gtk_blist_update_columns() |
2298 { | |
2299 if(!gtkblist) | |
2300 return; | |
2301 | |
7620 | 2302 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { |
5228 | 2303 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); |
2304 gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); | |
2305 gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); | |
2306 } else { | |
7620 | 2307 gtk_tree_view_column_set_visible(gtkblist->idle_column, |
2308 gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time")); | |
2309 gtk_tree_view_column_set_visible(gtkblist->warning_column, | |
2310 gaim_prefs_get_bool("/gaim/gtk/blist/show_warning_level")); | |
5228 | 2311 gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); |
2312 } | |
2313 } | |
2314 | |
7636 | 2315 enum {DRAG_BUDDY, DRAG_ROW}; |
5228 | 2316 |
2317 static char * | |
2318 item_factory_translate_func (const char *path, gpointer func_data) | |
2319 { | |
7620 | 2320 return _((char *)path); |
5228 | 2321 } |
2322 | |
5422 | 2323 void gaim_gtk_blist_setup_sort_methods() |
2324 { | |
7620 | 2325 gaim_gtk_blist_sort_method_reg("none", _("None"), sort_method_none); |
2326 #if GTK_CHECK_VERSION(2,2,1) | |
2327 gaim_gtk_blist_sort_method_reg("alphabetical", _("Alphabetical"), sort_method_alphabetical); | |
2328 gaim_gtk_blist_sort_method_reg("status", _("By status"), sort_method_status); | |
2329 gaim_gtk_blist_sort_method_reg("log_size", _("By log size"), sort_method_log); | |
2330 #endif | |
2331 gaim_gtk_blist_sort_method_set(gaim_prefs_get_string("/gaim/gtk/blist/sort_type")); | |
2332 } | |
2333 | |
2334 static void _prefs_change_redo_list() { | |
2335 redo_buddy_list(gaim_get_blist(), TRUE); | |
2336 } | |
2337 | |
2338 static void _prefs_change_sort_method(const char *pref_name, GaimPrefType type, | |
2339 gpointer val, gpointer data) { | |
2340 if(!strcmp(pref_name, "/gaim/gtk/blist/sort_type")) | |
2341 gaim_gtk_blist_sort_method_set(val); | |
2342 } | |
2343 | |
2344 static void gaim_gtk_blist_show(GaimBuddyList *list) | |
5228 | 2345 { |
2346 GtkCellRenderer *rend; | |
2347 GtkTreeViewColumn *column; | |
2348 GtkWidget *sw; | |
2349 GtkWidget *button; | |
2350 GtkSizeGroup *sg; | |
2351 GtkAccelGroup *accel_group; | |
2352 GtkTreeSelection *selection; | |
2353 GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, | |
7636 | 2354 {"application/x-im-contact", 0, DRAG_BUDDY}}; |
5228 | 2355 |
2356 if (gtkblist && gtkblist->window) { | |
2357 gtk_widget_show(gtkblist->window); | |
2358 return; | |
2359 } | |
2360 | |
2361 gtkblist = GAIM_GTK_BLIST(list); | |
2362 | |
2363 gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
2364 gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); | |
2365 gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); | |
2366 | |
2367 GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; | |
2368 | |
2369 gtkblist->vbox = gtk_vbox_new(FALSE, 0); | |
2370 gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox); | |
2371 | |
2372 g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); | |
2373 g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); | |
2374 g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); | |
2375 gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); | |
2376 | |
2377 /******************************* Menu bar *************************************/ | |
2378 accel_group = gtk_accel_group_new(); | |
2379 gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); | |
2380 g_object_unref(accel_group); | |
5427 | 2381 gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", accel_group); |
2382 gtk_item_factory_set_translate_func (gtkblist->ift, | |
5228 | 2383 item_factory_translate_func, |
2384 NULL, NULL); | |
5427 | 2385 gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), |
5228 | 2386 blist_menu, NULL); |
7620 | 2387 gaim_gtk_load_accels(); |
2388 g_signal_connect(G_OBJECT(accel_group), "accel-changed", | |
2389 G_CALLBACK(gaim_gtk_save_accels_cb), NULL); | |
5427 | 2390 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>"), FALSE, FALSE, 0); |
5228 | 2391 |
5427 | 2392 awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away")); |
5228 | 2393 do_away_menu(); |
2394 | |
5427 | 2395 gtkblist->bpmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounce")); |
5228 | 2396 gaim_gtkpounce_menu_build(gtkblist->bpmenu); |
2397 | |
5427 | 2398 protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Protocol Actions")); |
7620 | 2399 gaim_gtk_blist_update_protocol_actions(); |
5228 | 2400 /****************************** GtkTreeView **********************************/ |
2401 sw = gtk_scrolled_window_new(NULL,NULL); | |
2402 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); | |
2403 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2404 | |
7620 | 2405 gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, |
2406 GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, | |
2407 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); | |
5228 | 2408 |
2409 gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); | |
2410 gtk_widget_set_size_request(gtkblist->treeview, -1, 200); | |
2411 | |
2412 /* Set up selection stuff */ | |
2413 | |
2414 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); | |
2415 g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL); | |
2416 | |
2417 | |
2418 /* Set up dnd */ | |
7650 | 2419 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), |
2420 GDK_BUTTON1_MASK, gte, 2, | |
2421 GDK_ACTION_COPY); | |
2422 gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), | |
2423 gte, 2, | |
2424 GDK_ACTION_COPY | GDK_ACTION_MOVE); | |
7636 | 2425 |
2426 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); | |
5228 | 2427 g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); |
2428 | |
2429 /* Tooltips */ | |
2430 g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); | |
2431 g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); | |
2432 | |
2433 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); | |
2434 | |
2435 column = gtk_tree_view_column_new (); | |
2436 | |
2437 rend = gtk_cell_renderer_pixbuf_new(); | |
2438 gtk_tree_view_column_pack_start (column, rend, FALSE); | |
7620 | 2439 gtk_tree_view_column_set_attributes (column, rend, |
5228 | 2440 "pixbuf", STATUS_ICON_COLUMN, |
2441 "visible", STATUS_ICON_VISIBLE_COLUMN, | |
2442 NULL); | |
2443 g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL); | |
2444 | |
2445 rend = gtk_cell_renderer_text_new(); | |
2446 gtk_tree_view_column_pack_start (column, rend, TRUE); | |
7620 | 2447 gtk_tree_view_column_set_attributes (column, rend, |
5228 | 2448 "markup", NAME_COLUMN, |
2449 NULL); | |
2450 g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); | |
2451 | |
2452 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); | |
2453 | |
2454 rend = gtk_cell_renderer_text_new(); | |
2455 gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL); | |
2456 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column); | |
2457 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2458 | |
2459 rend = gtk_cell_renderer_text_new(); | |
2460 gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL); | |
2461 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column); | |
2462 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2463 | |
2464 rend = gtk_cell_renderer_pixbuf_new(); | |
2465 gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL); | |
2466 g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); | |
2467 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column); | |
2468 | |
2469 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); | |
2470 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); | |
2471 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); | |
2472 g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); | |
7620 | 2473 g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL); |
5228 | 2474 |
5419 | 2475 /* Enable CTRL+F searching */ |
2476 gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); | |
2477 | |
5228 | 2478 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); |
2479 gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); | |
2480 gaim_gtk_blist_update_columns(); | |
2481 | |
2482 /* set the Show Offline Buddies option. must be done | |
2483 * after the treeview or faceprint gets mad. -Robot101 | |
2484 */ | |
5427 | 2485 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Offline Buddies"))), |
7620 | 2486 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")); |
5427 | 2487 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (gtkblist->ift, N_("/Buddies/Show Empty Groups"))), |
7620 | 2488 gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups")); |
5228 | 2489 |
2490 /* OK... let's show this bad boy. */ | |
2491 gaim_gtk_blist_refresh(list); | |
2492 gaim_gtk_blist_restore_position(); | |
2493 gtk_widget_show_all(gtkblist->window); | |
2494 | |
2495 /**************************** Button Box **************************************/ | |
2496 /* add this afterwards so it doesn't force up the width of the window */ | |
2497 | |
2498 gtkblist->tooltips = gtk_tooltips_new(); | |
2499 | |
2500 sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); | |
2501 gtkblist->bbox = gtk_hbox_new(TRUE, 0); | |
2502 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0); | |
2503 gtk_widget_show(gtkblist->bbox); | |
2504 | |
2505 button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL); | |
2506 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2507 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2508 gtk_size_group_add_widget(sg, button); | |
2509 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), | |
2510 gtkblist->treeview); | |
2511 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL); | |
2512 gtk_widget_show(button); | |
2513 | |
2514 button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL); | |
2515 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2516 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2517 gtk_size_group_add_widget(sg, button); | |
2518 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), | |
2519 gtkblist->treeview); | |
2520 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL); | |
2521 gtk_widget_show(button); | |
2522 | |
2523 button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL); | |
2524 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2525 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2526 gtk_size_group_add_widget(sg, button); | |
5234 | 2527 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), gtkblist->treeview); |
5228 | 2528 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL); |
2529 gtk_widget_show(button); | |
2530 | |
2531 button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL); | |
2532 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); | |
2533 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); | |
2534 gtk_size_group_add_widget(sg, button); | |
2535 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL); | |
2536 gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL); | |
2537 gtk_widget_show(button); | |
2538 | |
2539 /* this will show the right image/label widgets for us */ | |
2540 gaim_gtk_blist_update_toolbar(); | |
2541 | |
2542 /* start the refresh timer */ | |
7620 | 2543 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || |
2544 gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { | |
2545 | |
2546 gtkblist->refresh_timer = g_timeout_add(30000, | |
2547 (GSourceFunc)gaim_gtk_blist_refresh_timer, list); | |
2548 } | |
2549 | |
2550 /* attach prefs callbacks */ | |
2551 /* for the toolbar buttons */ | |
2552 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2553 GINT_TO_POINTER( | |
2554 gaim_prefs_connect_callback("/gaim/gtk/blist/button_style", | |
2555 gaim_gtk_blist_update_toolbar, NULL))); | |
2556 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2557 GINT_TO_POINTER( | |
2558 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buttons", | |
2559 gaim_gtk_blist_update_toolbar, NULL))); | |
2560 | |
2561 /* things that affect how buddies are displayed */ | |
2562 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2563 GINT_TO_POINTER( | |
2564 gaim_prefs_connect_callback("/gaim/gtk/blist/grey_idle_buddies", | |
2565 _prefs_change_redo_list, NULL))); | |
2566 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2567 GINT_TO_POINTER( | |
2568 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", | |
2569 _prefs_change_redo_list, NULL))); | |
2570 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2571 GINT_TO_POINTER( | |
2572 gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", | |
2573 _prefs_change_redo_list, NULL))); | |
2574 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2575 GINT_TO_POINTER( | |
2576 gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", | |
2577 _prefs_change_redo_list, NULL))); | |
2578 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2579 GINT_TO_POINTER( | |
2580 gaim_prefs_connect_callback("/gaim/gtk/blist/show_empty_groups", | |
2581 _prefs_change_redo_list, NULL))); | |
2582 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2583 GINT_TO_POINTER( | |
2584 gaim_prefs_connect_callback("/gaim/gtk/blist/show_group_count", | |
2585 _prefs_change_redo_list, NULL))); | |
2586 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2587 GINT_TO_POINTER( | |
2588 gaim_prefs_connect_callback("/gaim/gtk/blist/show_offline_buddies", | |
2589 _prefs_change_redo_list, NULL))); | |
2590 | |
2591 /* sorting */ | |
2592 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2593 GINT_TO_POINTER( | |
2594 gaim_prefs_connect_callback("/gaim/gtk/blist/sort_type", | |
2595 _prefs_change_sort_method, NULL))); | |
2596 | |
2597 /* things that affect what columns are displayed */ | |
2598 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2599 GINT_TO_POINTER( | |
2600 gaim_prefs_connect_callback("/gaim/gtk/blist/show_buddy_icons", | |
2601 gaim_gtk_blist_update_columns, NULL))); | |
2602 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2603 GINT_TO_POINTER( | |
2604 gaim_prefs_connect_callback("/gaim/gtk/blist/show_idle_time", | |
2605 gaim_gtk_blist_update_columns, NULL))); | |
2606 blist_prefs_callbacks = g_slist_prepend(blist_prefs_callbacks, | |
2607 GINT_TO_POINTER( | |
2608 gaim_prefs_connect_callback("/gaim/gtk/blist/show_warning_level", | |
2609 gaim_gtk_blist_update_columns, NULL))); | |
5228 | 2610 } |
2611 | |
7620 | 2612 /* XXX: does this need fixing? */ |
2613 static void redo_buddy_list(GaimBuddyList *list, gboolean remove) | |
5228 | 2614 { |
7620 | 2615 GaimBlistNode *gnode, *cnode, *bnode; |
2616 | |
2617 for(gnode = list->root; gnode; gnode = gnode->next) { | |
2618 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
5234 | 2619 continue; |
7620 | 2620 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
2621 if(GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2622 if(remove) | |
2623 gaim_gtk_blist_hide_node(list, cnode); | |
2624 | |
2625 for(bnode = cnode->child; bnode; bnode = bnode->next) { | |
2626 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
2627 continue; | |
2628 if(remove) | |
2629 gaim_gtk_blist_hide_node(list, bnode); | |
2630 gaim_gtk_blist_update(list, bnode); | |
2631 } | |
2632 | |
2633 gaim_gtk_blist_update(list, cnode); | |
2634 } else if(GAIM_BLIST_NODE_IS_CHAT(cnode)) { | |
2635 if(remove) | |
2636 gaim_gtk_blist_hide_node(list, cnode); | |
2637 | |
2638 gaim_gtk_blist_update(list, cnode); | |
2639 } | |
5228 | 2640 } |
7620 | 2641 gaim_gtk_blist_update(list, gnode); |
5228 | 2642 } |
2643 } | |
2644 | |
7620 | 2645 void gaim_gtk_blist_refresh(GaimBuddyList *list) |
5422 | 2646 { |
2647 redo_buddy_list(list, FALSE); | |
2648 } | |
2649 | |
5297 | 2650 void |
2651 gaim_gtk_blist_update_refresh_timeout() | |
2652 { | |
7620 | 2653 GaimBuddyList *blist; |
2654 GaimGtkBuddyList *gtkblist; | |
5297 | 2655 |
2656 blist = gaim_get_blist(); | |
2657 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
2658 | |
7620 | 2659 if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") || |
2660 gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")) { | |
2661 | |
2662 gtkblist->refresh_timer = g_timeout_add(30000, | |
2663 (GSourceFunc)gaim_gtk_blist_refresh_timer, blist); | |
5297 | 2664 } else { |
2665 g_source_remove(gtkblist->refresh_timer); | |
2666 gtkblist->refresh_timer = 0; | |
2667 } | |
2668 } | |
2669 | |
5256 | 2670 static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) { |
2671 struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2672 GtkTreePath *path; | |
5228 | 2673 |
7620 | 2674 /* XXX: why do we assume we have a buddy here? */ |
5263 | 2675 if (!gtknode) { |
7620 | 2676 #if 0 |
2677 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "buddy %s has no ui_data\n", ((GaimBuddy *)node)->name); | |
2678 #endif | |
5263 | 2679 return FALSE; |
2680 } | |
2681 | |
2682 if (!gtkblist) { | |
2683 gaim_debug(GAIM_DEBUG_ERROR, "gtkblist", "get_iter_from_node was called, but we don't seem to have a blist\n"); | |
2684 return FALSE; | |
2685 } | |
2686 | |
2687 if (!gtknode->row) | |
5228 | 2688 return FALSE; |
2689 | |
5256 | 2690 if ((path = gtk_tree_row_reference_get_path(gtknode->row)) == NULL) |
5228 | 2691 return FALSE; |
5256 | 2692 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), iter, path)) { |
2693 gtk_tree_path_free(path); | |
2694 return FALSE; | |
2695 } | |
2696 gtk_tree_path_free(path); | |
2697 return TRUE; | |
5228 | 2698 } |
2699 | |
7620 | 2700 static void |
2701 gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) | |
2702 { | |
2703 GaimButtonStyle style = gaim_prefs_get_int("/gaim/gtk/blist/button_style"); | |
2704 | |
5228 | 2705 if (GTK_IS_IMAGE(widget)) { |
7620 | 2706 if (style == GAIM_BUTTON_IMAGE || style == GAIM_BUTTON_TEXT_IMAGE) |
5228 | 2707 gtk_widget_show(widget); |
2708 else | |
2709 gtk_widget_hide(widget); | |
7620 | 2710 } |
2711 else if (GTK_IS_LABEL(widget)) { | |
2712 if (style == GAIM_BUTTON_IMAGE) | |
5228 | 2713 gtk_widget_hide(widget); |
2714 else | |
2715 gtk_widget_show(widget); | |
7620 | 2716 } |
2717 else if (GTK_IS_CONTAINER(widget)) { | |
2718 gtk_container_foreach(GTK_CONTAINER(widget), | |
2719 gaim_gtk_blist_update_toolbar_icons, NULL); | |
5228 | 2720 } |
2721 } | |
2722 | |
2723 void gaim_gtk_blist_update_toolbar() { | |
2724 if (!gtkblist) | |
2725 return; | |
2726 | |
7620 | 2727 if (gaim_prefs_get_int("/gaim/gtk/blist/button_style") == GAIM_BUTTON_NONE) |
5228 | 2728 gtk_widget_hide(gtkblist->bbox); |
2729 else { | |
7620 | 2730 gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), |
2731 gaim_gtk_blist_update_toolbar_icons, NULL); | |
5228 | 2732 gtk_widget_show(gtkblist->bbox); |
2733 } | |
2734 } | |
2735 | |
7620 | 2736 static void gaim_gtk_blist_remove(GaimBuddyList *list, GaimBlistNode *node) |
5228 | 2737 { |
5260 | 2738 gaim_gtk_blist_hide_node(list, node); |
5228 | 2739 |
7620 | 2740 if(node->parent) |
2741 gaim_gtk_blist_update(list, node->parent); | |
2742 | |
5263 | 2743 /* There's something I don't understand here */ |
2744 /* g_free(node->ui_data); | |
2745 node->ui_data = NULL; */ | |
5228 | 2746 } |
2747 | |
2748 static gboolean do_selection_changed(GaimBlistNode *new_selection) | |
2749 { | |
5254 | 2750 GaimBlistNode *old_selection = NULL; |
5228 | 2751 |
5254 | 2752 /* test for gtkblist because crazy timeout means we can be called after the blist is gone */ |
2753 if (gtkblist && new_selection != gtkblist->selected_node) { | |
2754 old_selection = gtkblist->selected_node; | |
5228 | 2755 gtkblist->selected_node = new_selection; |
2756 if(new_selection) | |
2757 gaim_gtk_blist_update(NULL, new_selection); | |
2758 if(old_selection) | |
2759 gaim_gtk_blist_update(NULL, old_selection); | |
2760 } | |
2761 | |
2762 return FALSE; | |
2763 } | |
2764 | |
2765 static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data) | |
2766 { | |
2767 GaimBlistNode *new_selection = NULL; | |
2768 GtkTreeIter iter; | |
2769 | |
2770 if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ | |
2771 gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, | |
2772 NODE_COLUMN, &new_selection, -1); | |
2773 } | |
5254 | 2774 |
5228 | 2775 /* we set this up as a timeout, otherwise the blist flickers */ |
2776 g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); | |
2777 } | |
2778 | |
7620 | 2779 static void insert_node(GaimBuddyList *list, GaimBlistNode *node, GtkTreeIter *iter) |
2780 { | |
2781 GtkTreeIter parent_iter, cur, *curptr = NULL; | |
2782 struct _gaim_gtk_blist_node *gtknode = node->ui_data; | |
5256 | 2783 GtkTreePath *newpath; |
7620 | 2784 |
2785 if(!gtknode || !iter) | |
2786 return; | |
2787 | |
2788 if(node->parent && !get_iter_from_node(node->parent, &parent_iter)) | |
2789 return; | |
2790 | |
2791 if(get_iter_from_node(node, &cur)) | |
2792 curptr = &cur; | |
2793 | |
2794 if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_CHAT(node)) { | |
2795 *iter = current_sort_method->func(node, list, parent_iter, curptr); | |
2796 } else { | |
2797 *iter = sort_method_none(node, list, parent_iter, curptr); | |
5228 | 2798 } |
2799 | |
7620 | 2800 gtk_tree_row_reference_free(gtknode->row); |
2801 newpath = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), | |
2802 iter); | |
2803 gtknode->row = | |
2804 gtk_tree_row_reference_new(GTK_TREE_MODEL(gtkblist->treemodel), | |
2805 newpath); | |
5256 | 2806 gtk_tree_path_free(newpath); |
2807 | |
5228 | 2808 gtk_tree_store_set(gtkblist->treemodel, iter, |
2809 NODE_COLUMN, node, | |
2810 -1); | |
7620 | 2811 |
2812 if(node->parent) { | |
2813 GtkTreePath *expand = NULL; | |
2814 struct _gaim_gtk_blist_node *gtkparentnode = node->parent->ui_data; | |
2815 | |
2816 if(GAIM_BLIST_NODE_IS_GROUP(node->parent)) { | |
7693 | 2817 if(!gaim_blist_node_get_bool(node->parent, "collapsed")) |
7620 | 2818 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); |
2819 } else if(GAIM_BLIST_NODE_IS_CONTACT(node->parent) && | |
2820 gtkparentnode->contact_expanded) { | |
2821 expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &parent_iter); | |
2822 } | |
2823 if(expand) { | |
7693 | 2824 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, FALSE); |
7620 | 2825 gtk_tree_path_free(expand); |
2826 } | |
2827 } | |
2828 | |
5228 | 2829 } |
2830 | |
7620 | 2831 static void gaim_gtk_blist_update_group(GaimBuddyList *list, GaimBlistNode *node) |
2832 { | |
2833 GaimGroup *group; | |
2834 | |
2835 g_return_if_fail(GAIM_BLIST_NODE_IS_GROUP(node)); | |
2836 | |
2837 group = (GaimGroup*)node; | |
2838 | |
2839 if(gaim_prefs_get_bool("/gaim/gtk/blist/show_empty_groups") || | |
2840 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies") || | |
2841 gaim_blist_get_group_online_count(group) > 0) { | |
2842 char *mark, *esc; | |
2843 GtkTreeIter iter; | |
2844 | |
2845 insert_node(list, node, &iter); | |
2846 | |
2847 esc = g_markup_escape_text(group->name, -1); | |
2848 if(gaim_prefs_get_bool("/gaim/gtk/blist/show_group_count")) { | |
2849 mark = g_strdup_printf("<span weight='bold'>%s</span> (%d/%d)", | |
2850 esc, gaim_blist_get_group_online_count(group), | |
2851 gaim_blist_get_group_size(group, FALSE)); | |
2852 } else { | |
2853 mark = g_strdup_printf("<span weight='bold'>%s</span>", esc); | |
2854 } | |
2855 g_free(esc); | |
2856 | |
2857 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
2858 STATUS_ICON_COLUMN, NULL, | |
2859 STATUS_ICON_VISIBLE_COLUMN, FALSE, | |
2860 NAME_COLUMN, mark, | |
2861 NODE_COLUMN, node, | |
2862 -1); | |
2863 g_free(mark); | |
2864 } else { | |
2865 gaim_gtk_blist_hide_node(list, node); | |
2866 } | |
2867 } | |
2868 | |
2869 static void buddy_node(GaimBuddy *buddy, GtkTreeIter *iter, GaimBlistNode *node) | |
5228 | 2870 { |
7620 | 2871 GdkPixbuf *status, *avatar; |
2872 char *mark; | |
2873 char *warning = NULL, *idle = NULL; | |
2874 | |
2875 gboolean selected = (gtkblist->selected_node == node); | |
2876 | |
2877 status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy, | |
2878 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") | |
2879 ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
2880 | |
2881 avatar = gaim_gtk_blist_get_buddy_icon(buddy); | |
2882 mark = gaim_gtk_blist_get_name_markup(buddy, selected); | |
2883 | |
2884 if (buddy->idle > 0) { | |
2885 time_t t; | |
2886 int ihrs, imin; | |
2887 time(&t); | |
2888 ihrs = (t - buddy->idle) / 3600; | |
2889 imin = ((t - buddy->idle) / 60) % 60; | |
2890 if(ihrs > 0) | |
2891 idle = g_strdup_printf("(%d:%02d)", ihrs, imin); | |
2892 else | |
2893 idle = g_strdup_printf("(%d)", imin); | |
2894 } | |
2895 | |
2896 if (buddy->evil > 0) | |
2897 warning = g_strdup_printf("%d%%", buddy->evil); | |
2898 | |
2899 if (gaim_prefs_get_bool("/gaim/gtk/blist/grey_idle_buddies") && | |
2900 buddy->idle) { | |
2901 | |
2902 if(warning && !selected) { | |
2903 char *w2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
2904 warning); | |
2905 g_free(warning); | |
2906 warning = w2; | |
2907 } | |
2908 | |
2909 if(idle && !selected) { | |
2910 char *i2 = g_strdup_printf("<span color='dim grey'>%s</span>", | |
2911 idle); | |
2912 g_free(idle); | |
2913 idle = i2; | |
5228 | 2914 } |
7620 | 2915 } |
2916 | |
2917 gtk_tree_store_set(gtkblist->treemodel, iter, | |
2918 STATUS_ICON_COLUMN, status, | |
2919 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
2920 NAME_COLUMN, mark, | |
2921 WARNING_COLUMN, warning, | |
2922 IDLE_COLUMN, idle, | |
2923 BUDDY_ICON_COLUMN, avatar, | |
2924 -1); | |
2925 | |
2926 g_free(mark); | |
2927 if(idle) | |
2928 g_free(idle); | |
2929 if(warning) | |
2930 g_free(warning); | |
2931 if(status) | |
2932 g_object_unref(status); | |
2933 if(avatar) | |
2934 g_object_unref(avatar); | |
2935 } | |
2936 | |
2937 static void gaim_gtk_blist_update_contact(GaimBuddyList *list, GaimBlistNode *node) | |
2938 { | |
2939 GaimContact *contact; | |
2940 GaimBuddy *buddy; | |
2941 struct _gaim_gtk_blist_node *gtknode; | |
2942 | |
2943 g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT(node)); | |
2944 | |
2945 /* First things first, update the group */ | |
2946 gaim_gtk_blist_update_group(list, node->parent); | |
2947 | |
2948 gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; | |
2949 contact = (GaimContact*)node; | |
2950 buddy = gaim_contact_get_priority_buddy(contact); | |
2951 | |
2952 if(buddy && (buddy->present != GAIM_BUDDY_OFFLINE || | |
2953 (gaim_account_is_connected(buddy->account) && | |
2954 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { | |
2955 GtkTreeIter iter; | |
2956 | |
2957 insert_node(list, node, &iter); | |
2958 | |
2959 if(gtknode->contact_expanded) { | |
2960 GdkPixbuf *status; | |
5228 | 2961 char *mark; |
2962 | |
7620 | 2963 status = gaim_gtk_blist_get_status_icon(node, |
2964 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? | |
2965 GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
2966 | |
2967 mark = g_markup_escape_text(gaim_contact_get_alias(contact), -1); | |
2968 | |
5228 | 2969 gtk_tree_store_set(gtkblist->treemodel, &iter, |
7620 | 2970 STATUS_ICON_COLUMN, status, |
2971 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
5228 | 2972 NAME_COLUMN, mark, |
7620 | 2973 WARNING_COLUMN, NULL, |
2974 IDLE_COLUMN, NULL, | |
2975 BUDDY_ICON_COLUMN, NULL, | |
5228 | 2976 -1); |
2977 g_free(mark); | |
7620 | 2978 if(status) |
2979 g_object_unref(status); | |
2980 } else { | |
2981 buddy_node(buddy, &iter, node); | |
5228 | 2982 } |
7620 | 2983 } else { |
2984 gaim_gtk_blist_hide_node(list, node); | |
5228 | 2985 } |
7620 | 2986 } |
2987 | |
2988 static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node) | |
2989 { | |
2990 GaimContact *contact; | |
2991 GaimBuddy *buddy; | |
2992 struct _gaim_gtk_blist_node *gtkparentnode; | |
2993 | |
2994 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
2995 | |
2996 buddy = (GaimBuddy*)node; | |
2997 contact = (GaimContact*)node->parent; | |
2998 gtkparentnode = (struct _gaim_gtk_blist_node *)node->parent->ui_data; | |
2999 | |
3000 /* First things first, update the contact */ | |
3001 gaim_gtk_blist_update_contact(list, node->parent); | |
3002 | |
3003 if(gtkparentnode->contact_expanded && | |
3004 (buddy->present != GAIM_BUDDY_OFFLINE || | |
3005 (gaim_account_is_connected(buddy->account) && | |
3006 gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies")))) { | |
3007 GtkTreeIter iter; | |
3008 | |
3009 insert_node(list, node, &iter); | |
3010 buddy_node(buddy, &iter, node); | |
3011 | |
3012 } else { | |
3013 gaim_gtk_blist_hide_node(list, node); | |
3014 } | |
3015 | |
3016 } | |
3017 | |
3018 static void gaim_gtk_blist_update_chat(GaimBuddyList *list, GaimBlistNode *node) | |
3019 { | |
3020 GaimChat *chat; | |
3021 | |
3022 g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT(node)); | |
3023 | |
3024 /* First things first, update the group */ | |
3025 gaim_gtk_blist_update_group(list, node->parent); | |
3026 | |
3027 chat = (GaimChat*)node; | |
3028 | |
3029 if(gaim_account_is_connected(chat->account)) { | |
3030 GtkTreeIter iter; | |
5234 | 3031 GdkPixbuf *status; |
7620 | 3032 char *mark; |
3033 | |
3034 insert_node(list, node, &iter); | |
5234 | 3035 |
3036 status = gaim_gtk_blist_get_status_icon(node, | |
7620 | 3037 (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") ? |
3038 GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); | |
3039 | |
3040 mark = g_markup_escape_text(gaim_chat_get_name(chat), -1); | |
5234 | 3041 |
3042 gtk_tree_store_set(gtkblist->treemodel, &iter, | |
7620 | 3043 STATUS_ICON_COLUMN, status, |
3044 STATUS_ICON_VISIBLE_COLUMN, TRUE, | |
3045 NAME_COLUMN, mark, | |
3046 -1); | |
5228 | 3047 |
3048 g_free(mark); | |
7620 | 3049 if(status) |
5228 | 3050 g_object_unref(status); |
7620 | 3051 } else { |
5260 | 3052 gaim_gtk_blist_hide_node(list, node); |
5228 | 3053 } |
7620 | 3054 } |
3055 | |
3056 static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node) | |
3057 { | |
3058 if(!gtkblist) | |
3059 return; | |
3060 | |
3061 switch(node->type) { | |
3062 case GAIM_BLIST_GROUP_NODE: | |
3063 gaim_gtk_blist_update_group(list, node); | |
3064 break; | |
3065 case GAIM_BLIST_CONTACT_NODE: | |
3066 gaim_gtk_blist_update_contact(list, node); | |
3067 break; | |
3068 case GAIM_BLIST_BUDDY_NODE: | |
3069 gaim_gtk_blist_update_buddy(list, node); | |
3070 break; | |
3071 case GAIM_BLIST_CHAT_NODE: | |
3072 gaim_gtk_blist_update_chat(list, node); | |
3073 break; | |
3074 case GAIM_BLIST_OTHER_NODE: | |
3075 return; | |
3076 } | |
5234 | 3077 |
5228 | 3078 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); |
3079 } | |
3080 | |
7620 | 3081 |
3082 static void gaim_gtk_blist_destroy(GaimBuddyList *list) | |
5228 | 3083 { |
3084 if (!gtkblist) | |
3085 return; | |
3086 | |
3087 gtk_widget_destroy(gtkblist->window); | |
7620 | 3088 |
3089 if (gtkblist->tipwindow) | |
3090 gtk_widget_destroy(gtkblist->tipwindow); | |
3091 | |
5228 | 3092 gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); |
3093 | |
3094 if (gtkblist->refresh_timer) | |
3095 g_source_remove(gtkblist->refresh_timer); | |
3096 if (gtkblist->timeout) | |
3097 g_source_remove(gtkblist->timeout); | |
3098 | |
3099 gtkblist->refresh_timer = 0; | |
3100 gtkblist->timeout = 0; | |
3101 gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; | |
3102 gtkblist->treemodel = NULL; | |
3103 gtkblist->idle_column = NULL; | |
3104 gtkblist->warning_column = gtkblist->buddy_icon_column = NULL; | |
3105 gtkblist->bbox = gtkblist->tipwindow = NULL; | |
5427 | 3106 g_object_unref(G_OBJECT(gtkblist->ift)); |
5228 | 3107 protomenu = NULL; |
3108 awaymenu = NULL; | |
3109 gtkblist = NULL; | |
7620 | 3110 |
3111 while(blist_prefs_callbacks) { | |
3112 gaim_prefs_disconnect_callback(GPOINTER_TO_INT(blist_prefs_callbacks->data)); | |
3113 blist_prefs_callbacks = g_slist_remove(blist_prefs_callbacks, blist_prefs_callbacks->data); | |
3114 } | |
5228 | 3115 } |
3116 | |
7620 | 3117 static void gaim_gtk_blist_set_visible(GaimBuddyList *list, gboolean show) |
5228 | 3118 { |
3119 if (!(gtkblist && gtkblist->window)) | |
3120 return; | |
3121 | |
3122 if (show) { | |
3123 gaim_gtk_blist_restore_position(); | |
3124 gtk_window_present(GTK_WINDOW(gtkblist->window)); | |
3125 } else { | |
7620 | 3126 if (!gaim_connections_get_all() || docklet_count) { |
5228 | 3127 #ifdef _WIN32 |
3128 wgaim_systray_minimize(gtkblist->window); | |
3129 #endif | |
3130 gtk_widget_hide(gtkblist->window); | |
3131 } else { | |
3132 gtk_window_iconify(GTK_WINDOW(gtkblist->window)); | |
3133 } | |
3134 } | |
3135 } | |
3136 | |
7620 | 3137 static GList * |
3138 groups_tree(void) | |
3139 { | |
3140 GList *tmp = NULL; | |
3141 char *tmp2; | |
3142 GaimGroup *g; | |
3143 GaimBlistNode *gnode; | |
3144 | |
3145 if (gaim_get_blist()->root == NULL) | |
3146 { | |
3147 tmp2 = g_strdup(_("Buddies")); | |
3148 tmp = g_list_append(tmp, tmp2); | |
3149 } | |
3150 else | |
3151 { | |
3152 for (gnode = gaim_get_blist()->root; | |
3153 gnode != NULL; | |
3154 gnode = gnode->next) | |
3155 { | |
3156 if (GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
3157 { | |
3158 g = (GaimGroup *)gnode; | |
3159 tmp2 = g->name; | |
3160 tmp = g_list_append(tmp, tmp2); | |
3161 } | |
3162 } | |
3163 } | |
3164 | |
3165 return tmp; | |
3166 } | |
3167 | |
3168 static void | |
3169 add_buddy_select_account_cb(GObject *w, GaimAccount *account, | |
3170 GaimGtkAddBuddyData *data) | |
3171 { | |
3172 /* Save our account */ | |
3173 data->account = account; | |
3174 } | |
3175 | |
3176 static void | |
3177 destroy_add_buddy_dialog_cb(GtkWidget *win, GaimGtkAddBuddyData *data) | |
3178 { | |
3179 g_free(data); | |
3180 } | |
3181 | |
3182 static void | |
3183 add_buddy_cb(GtkWidget *w, int resp, GaimGtkAddBuddyData *data) | |
3184 { | |
3185 const char *grp, *who, *whoalias; | |
3186 GaimConversation *c; | |
3187 GaimBuddy *b; | |
3188 GaimGroup *g; | |
3189 | |
3190 if (resp == GTK_RESPONSE_OK) | |
3191 { | |
3192 who = gtk_entry_get_text(GTK_ENTRY(data->entry)); | |
3193 grp = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry)); | |
3194 whoalias = gtk_entry_get_text(GTK_ENTRY(data->entry_for_alias)); | |
3195 | |
3196 c = gaim_find_conversation_with_account(who, data->account); | |
3197 | |
3198 if ((g = gaim_find_group(grp)) == NULL) | |
3199 { | |
3200 g = gaim_group_new(grp); | |
3201 gaim_blist_add_group(g, NULL); | |
3202 } | |
3203 | |
3204 b = gaim_buddy_new(data->account, who, whoalias); | |
3205 gaim_blist_add_buddy(b, NULL, g, NULL); | |
3206 serv_add_buddy(gaim_account_get_connection(data->account), who, g); | |
3207 | |
3208 if (c != NULL) { | |
3209 gaim_buddy_icon_update(gaim_conv_im_get_icon(GAIM_CONV_IM(c))); | |
3210 gaim_conversation_update(c, GAIM_CONV_UPDATE_ADD); | |
3211 } | |
3212 | |
3213 gaim_blist_save(); | |
3214 } | |
3215 | |
3216 gtk_widget_destroy(data->window); | |
3217 } | |
3218 | |
3219 static void | |
3220 gaim_gtk_blist_request_add_buddy(GaimAccount *account, const char *username, | |
3221 const char *group, const char *alias) | |
3222 { | |
3223 GtkWidget *table; | |
3224 GtkWidget *label; | |
3225 GtkWidget *hbox; | |
3226 GtkWidget *vbox; | |
3227 GtkWidget *img; | |
3228 GaimGtkBuddyList *gtkblist; | |
3229 GaimGtkAddBuddyData *data = g_new0(GaimGtkAddBuddyData, 1); | |
3230 | |
3231 data->account = | |
3232 (account != NULL | |
3233 ? account | |
3234 : gaim_connection_get_account(gaim_connections_get_all()->data)); | |
3235 | |
3236 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, | |
3237 GTK_ICON_SIZE_DIALOG); | |
3238 | |
3239 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
3240 | |
3241 data->window = gtk_dialog_new_with_buttons(_("Add Buddy"), | |
3242 (gtkblist->window ? GTK_WINDOW(gtkblist->window) : NULL), 0, | |
3243 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
3244 GTK_STOCK_ADD, GTK_RESPONSE_OK, | |
3245 NULL); | |
3246 | |
3247 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); | |
3248 gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); | |
3249 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); | |
3250 gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); | |
3251 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); | |
3252 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); | |
3253 gtk_window_set_role(GTK_WINDOW(data->window), "add_buddy"); | |
3254 | |
3255 hbox = gtk_hbox_new(FALSE, 12); | |
3256 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); | |
3257 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); | |
3258 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); | |
3259 | |
3260 vbox = gtk_vbox_new(FALSE, 0); | |
3261 gtk_container_add(GTK_CONTAINER(hbox), vbox); | |
3262 | |
3263 label = gtk_label_new( | |
3264 _("Please enter the screen name of the person you would like " | |
3265 "to add to your buddy list. You may optionally enter an alias, " | |
3266 "or nickname, for the buddy. The alias will be displayed in " | |
3267 "place of the screen name whenever possible.\n")); | |
3268 | |
3269 gtk_widget_set_size_request(GTK_WIDGET(label), 400, -1); | |
3270 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); | |
3271 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
3272 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); | |
3273 | |
3274 hbox = gtk_hbox_new(FALSE, 6); | |
3275 gtk_container_add(GTK_CONTAINER(vbox), hbox); | |
3276 | |
3277 g_signal_connect(G_OBJECT(data->window), "destroy", | |
3278 G_CALLBACK(destroy_add_buddy_dialog_cb), data); | |
3279 | |
3280 table = gtk_table_new(4, 2, FALSE); | |
3281 gtk_table_set_row_spacings(GTK_TABLE(table), 5); | |
3282 gtk_table_set_col_spacings(GTK_TABLE(table), 5); | |
3283 gtk_container_set_border_width(GTK_CONTAINER(table), 0); | |
3284 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); | |
3285 | |
3286 label = gtk_label_new(_("Screen Name:")); | |
3287 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3288 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); | |
3289 | |
3290 data->entry = gtk_entry_new(); | |
3291 gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1); | |
3292 gtk_widget_grab_focus(data->entry); | |
3293 | |
3294 if (username != NULL) | |
3295 gtk_entry_set_text(GTK_ENTRY(data->entry), username); | |
3296 | |
3297 gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE); | |
3298 | |
3299 label = gtk_label_new(_("Alias:")); | |
3300 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3301 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); | |
3302 | |
3303 data->entry_for_alias = gtk_entry_new(); | |
3304 gtk_table_attach_defaults(GTK_TABLE(table), | |
3305 data->entry_for_alias, 1, 2, 1, 2); | |
3306 | |
3307 if (alias != NULL) | |
3308 gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias); | |
3309 | |
3310 gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE); | |
3311 | |
3312 label = gtk_label_new(_("Group:")); | |
3313 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3314 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); | |
3315 | |
3316 data->combo = gtk_combo_new(); | |
3317 gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree()); | |
3318 gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3); | |
3319 | |
3320 /* Set up stuff for the account box */ | |
3321 label = gtk_label_new(_("Account:")); | |
3322 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3323 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); | |
3324 | |
3325 data->account_box = gaim_gtk_account_option_menu_new(account, FALSE, | |
3326 G_CALLBACK(add_buddy_select_account_cb), NULL, data); | |
3327 | |
3328 gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4); | |
3329 | |
3330 /* End of account box */ | |
3331 | |
3332 g_signal_connect(G_OBJECT(data->window), "response", | |
3333 G_CALLBACK(add_buddy_cb), data); | |
3334 | |
3335 gtk_widget_show_all(data->window); | |
3336 | |
3337 if (group != NULL) | |
3338 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->combo)->entry), group); | |
3339 } | |
3340 | |
3341 static void | |
3342 add_chat_cb(GtkWidget *w, GaimGtkAddChatData *data) | |
3343 { | |
3344 GHashTable *components; | |
3345 GList *tmp; | |
3346 GaimChat *chat; | |
3347 GaimGroup *group; | |
3348 const char *group_name; | |
3349 | |
3350 components = g_hash_table_new_full(g_str_hash, g_str_equal, | |
3351 g_free, g_free); | |
3352 | |
3353 for (tmp = data->entries; tmp; tmp = tmp->next) | |
3354 { | |
3355 if (g_object_get_data(tmp->data, "is_spin")) | |
3356 { | |
3357 g_hash_table_replace(components, | |
3358 g_strdup(g_object_get_data(tmp->data, "identifier")), | |
3359 g_strdup_printf("%d", | |
3360 gtk_spin_button_get_value_as_int(tmp->data))); | |
3361 } | |
3362 else | |
3363 { | |
3364 g_hash_table_replace(components, | |
3365 g_strdup(g_object_get_data(tmp->data, "identifier")), | |
3366 g_strdup(gtk_entry_get_text(tmp->data))); | |
3367 } | |
3368 } | |
3369 | |
3370 chat = gaim_chat_new(data->account, | |
3371 gtk_entry_get_text(GTK_ENTRY(data->alias_entry)), | |
3372 components); | |
3373 | |
3374 group_name = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry)); | |
3375 | |
3376 if ((group = gaim_find_group(group_name)) == NULL) | |
3377 { | |
3378 group = gaim_group_new(group_name); | |
3379 gaim_blist_add_group(group, NULL); | |
3380 } | |
3381 | |
3382 if (chat != NULL) | |
3383 { | |
3384 gaim_blist_add_chat(chat, group, NULL); | |
3385 gaim_blist_save(); | |
3386 } | |
3387 | |
3388 gtk_widget_destroy(data->window); | |
3389 g_list_free(data->entries); | |
3390 | |
3391 g_free(data); | |
3392 } | |
3393 | |
3394 static void | |
3395 add_chat_resp_cb(GtkWidget *w, int resp, GaimGtkAddChatData *data) | |
3396 { | |
3397 if (resp == GTK_RESPONSE_OK) | |
3398 { | |
3399 add_chat_cb(NULL, data); | |
3400 } | |
3401 else | |
3402 { | |
3403 gtk_widget_destroy(data->window); | |
3404 g_list_free(data->entries); | |
3405 g_free(data); | |
3406 } | |
3407 } | |
3408 | |
3409 static void | |
3410 rebuild_addchat_entries(GaimGtkAddChatData *data) | |
3411 { | |
3412 GaimConnection *gc; | |
3413 GList *list, *tmp; | |
3414 struct proto_chat_entry *pce; | |
3415 gboolean focus = TRUE; | |
3416 | |
3417 gc = gaim_account_get_connection(data->account); | |
3418 | |
3419 while (GTK_BOX(data->entries_box)->children) | |
3420 { | |
3421 gtk_container_remove(GTK_CONTAINER(data->entries_box), | |
3422 ((GtkBoxChild *)GTK_BOX(data->entries_box)->children->data)->widget); | |
3423 } | |
3424 | |
3425 if (data->entries != NULL) | |
3426 g_list_free(data->entries); | |
3427 | |
3428 data->entries = NULL; | |
3429 | |
3430 list = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc); | |
3431 | |
3432 for (tmp = list; tmp; tmp = tmp->next) | |
3433 { | |
3434 GtkWidget *label; | |
3435 GtkWidget *rowbox; | |
3436 | |
3437 pce = tmp->data; | |
3438 | |
3439 rowbox = gtk_hbox_new(FALSE, 5); | |
3440 gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0); | |
3441 | |
3442 label = gtk_label_new(pce->label); | |
3443 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3444 gtk_size_group_add_widget(data->sg, label); | |
3445 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3446 | |
3447 if (pce->is_int) | |
3448 { | |
3449 GtkObject *adjust; | |
3450 GtkWidget *spin; | |
3451 adjust = gtk_adjustment_new(pce->min, pce->min, pce->max, | |
3452 1, 10, 10); | |
3453 spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); | |
3454 g_object_set_data(G_OBJECT(spin), "is_spin", GINT_TO_POINTER(TRUE)); | |
3455 g_object_set_data(G_OBJECT(spin), "identifier", pce->identifier); | |
3456 data->entries = g_list_append(data->entries, spin); | |
3457 gtk_widget_set_size_request(spin, 50, -1); | |
3458 gtk_box_pack_end(GTK_BOX(rowbox), spin, FALSE, FALSE, 0); | |
3459 } | |
3460 else | |
3461 { | |
3462 GtkWidget *entry = gtk_entry_new(); | |
3463 | |
3464 g_object_set_data(G_OBJECT(entry), "identifier", pce->identifier); | |
3465 data->entries = g_list_append(data->entries, entry); | |
3466 | |
3467 if (pce->def) | |
3468 gtk_entry_set_text(GTK_ENTRY(entry), pce->def); | |
3469 | |
3470 if (focus) | |
3471 { | |
3472 gtk_widget_grab_focus(entry); | |
3473 focus = FALSE; | |
3474 } | |
3475 | |
3476 if (pce->secret) | |
3477 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); | |
3478 | |
3479 gtk_box_pack_end(GTK_BOX(rowbox), entry, TRUE, TRUE, 0); | |
3480 | |
3481 g_signal_connect(G_OBJECT(entry), "activate", | |
3482 G_CALLBACK(add_chat_cb), data); | |
3483 } | |
3484 | |
3485 g_free(pce); | |
3486 } | |
3487 | |
3488 g_list_free(list); | |
3489 | |
3490 gtk_widget_show_all(data->entries_box); | |
3491 } | |
3492 | |
3493 static void | |
3494 add_chat_select_account_cb(GObject *w, GaimAccount *account, | |
3495 GaimGtkAddChatData *data) | |
3496 { | |
3497 if (gaim_account_get_protocol(data->account) == | |
3498 gaim_account_get_protocol(account)) | |
3499 { | |
3500 data->account = account; | |
3501 } | |
3502 else | |
3503 { | |
3504 data->account = account; | |
3505 rebuild_addchat_entries(data); | |
3506 } | |
3507 } | |
3508 | |
3509 static gboolean | |
3510 add_chat_check_account_func(GaimAccount *account) | |
3511 { | |
3512 GaimConnection *gc = gaim_account_get_connection(account); | |
3513 | |
3514 return (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL); | |
3515 } | |
3516 | |
3517 void | |
3518 gaim_gtk_blist_request_add_chat(GaimAccount *account, GaimGroup *group) | |
3519 { | |
3520 GaimGtkAddChatData *data; | |
3521 GaimGtkBuddyList *gtkblist; | |
3522 GList *l; | |
3523 GaimConnection *gc; | |
3524 GtkWidget *label; | |
3525 GtkWidget *rowbox; | |
3526 GtkWidget *hbox; | |
3527 GtkWidget *vbox; | |
3528 GtkWidget *img; | |
3529 | |
3530 data = g_new0(GaimGtkAddChatData, 1); | |
3531 | |
3532 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, | |
3533 GTK_ICON_SIZE_DIALOG); | |
3534 | |
3535 gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); | |
3536 | |
3537 if (account != NULL) | |
3538 { | |
3539 data->account = account; | |
3540 } | |
3541 else | |
3542 { | |
3543 /* Select an account with chat capabilities */ | |
3544 for (l = gaim_connections_get_all(); l != NULL; l = l->next) | |
3545 { | |
3546 gc = (GaimConnection *)l->data; | |
3547 | |
3548 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat != NULL) | |
3549 { | |
3550 data->account = gaim_connection_get_account(gc); | |
3551 break; | |
3552 } | |
3553 } | |
3554 } | |
3555 | |
3556 if (data->account == NULL) | |
3557 { | |
3558 gaim_notify_error(NULL, NULL, | |
3559 _("You are not currently signed on with any " | |
3560 "protocols that have the ability to chat."), NULL); | |
3561 return; | |
3562 } | |
3563 | |
3564 data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); | |
3565 | |
3566 data->window = gtk_dialog_new_with_buttons(_("Add Chat"), | |
3567 GTK_WINDOW(gtkblist->window), 0, | |
3568 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
3569 GTK_STOCK_ADD, GTK_RESPONSE_OK, | |
3570 NULL); | |
3571 | |
3572 gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); | |
3573 gtk_container_set_border_width(GTK_CONTAINER(data->window), 6); | |
3574 gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE); | |
3575 gtk_dialog_set_has_separator(GTK_DIALOG(data->window), FALSE); | |
3576 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), 12); | |
3577 gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), 6); | |
3578 gtk_window_set_role(GTK_WINDOW(data->window), "add_chat"); | |
3579 | |
3580 hbox = gtk_hbox_new(FALSE, 12); | |
3581 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox); | |
3582 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); | |
3583 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); | |
3584 | |
3585 vbox = gtk_vbox_new(FALSE, 5); | |
3586 gtk_container_add(GTK_CONTAINER(hbox), vbox); | |
3587 | |
3588 label = gtk_label_new( | |
3589 _("Please enter an alias, and the appropriate information " | |
3590 "about the chat you would like to add to your buddy list.\n")); | |
3591 gtk_widget_set_size_request(label, 400, -1); | |
3592 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); | |
3593 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
3594 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); | |
3595 | |
3596 rowbox = gtk_hbox_new(FALSE, 5); | |
3597 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3598 | |
3599 label = gtk_label_new(_("Account:")); | |
3600 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3601 gtk_size_group_add_widget(data->sg, label); | |
3602 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3603 | |
3604 data->account_menu = gaim_gtk_account_option_menu_new(account, FALSE, | |
3605 G_CALLBACK(add_chat_select_account_cb), | |
3606 add_chat_check_account_func, data); | |
3607 gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); | |
3608 | |
3609 data->entries_box = gtk_vbox_new(FALSE, 5); | |
3610 gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0); | |
3611 gtk_box_pack_start(GTK_BOX(vbox), data->entries_box, TRUE, TRUE, 0); | |
3612 | |
3613 rebuild_addchat_entries(data); | |
3614 | |
3615 rowbox = gtk_hbox_new(FALSE, 5); | |
3616 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3617 | |
3618 label = gtk_label_new(_("Alias:")); | |
3619 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3620 gtk_size_group_add_widget(data->sg, label); | |
3621 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3622 | |
3623 data->alias_entry = gtk_entry_new(); | |
3624 gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); | |
3625 | |
3626 rowbox = gtk_hbox_new(FALSE, 5); | |
3627 gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); | |
3628 | |
3629 label = gtk_label_new(_("Group:")); | |
3630 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
3631 gtk_size_group_add_widget(data->sg, label); | |
3632 gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); | |
3633 | |
3634 data->group_combo = gtk_combo_new(); | |
3635 gtk_combo_set_popdown_strings(GTK_COMBO(data->group_combo), groups_tree()); | |
3636 gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); | |
3637 | |
3638 if (group) | |
3639 { | |
3640 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry), | |
3641 group->name); | |
3642 } | |
3643 | |
3644 g_signal_connect(G_OBJECT(data->window), "response", | |
3645 G_CALLBACK(add_chat_resp_cb), data); | |
3646 | |
3647 gtk_widget_show_all(data->window); | |
3648 } | |
3649 | |
3650 static void | |
3651 add_group_cb(GaimConnection *gc, const char *group_name) | |
3652 { | |
3653 GaimGroup *g; | |
3654 | |
3655 g = gaim_group_new(group_name); | |
3656 gaim_blist_add_group(g, NULL); | |
3657 gaim_blist_save(); | |
3658 } | |
3659 | |
3660 void | |
3661 gaim_gtk_blist_request_add_group(void) | |
3662 { | |
3663 gaim_request_input(NULL, _("Add Group"), _("Add a new group"), | |
3664 _("Please enter the name of the group to be added."), | |
3665 NULL, FALSE, FALSE, | |
3666 _("Add"), G_CALLBACK(add_group_cb), | |
3667 _("Cancel"), NULL, NULL); | |
3668 } | |
3669 | |
5228 | 3670 void gaim_gtk_blist_docklet_toggle() { |
3671 /* Useful for the docklet plugin and also for the win32 tray icon*/ | |
3672 /* This is called when one of those is clicked--it will show/hide the | |
3673 buddy list/login window--depending on which is active */ | |
7620 | 3674 if (gaim_connections_get_all()) { |
5228 | 3675 if (gtkblist && gtkblist->window) { |
3676 if (GTK_WIDGET_VISIBLE(gtkblist->window)) { | |
3677 gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); | |
3678 } else { | |
3679 #if _WIN32 | |
3680 wgaim_systray_maximize(gtkblist->window); | |
3681 #endif | |
3682 gaim_blist_set_visible(TRUE); | |
3683 } | |
3684 } else { | |
3685 /* we're logging in or something... do nothing */ | |
3686 /* or should I make the blist? */ | |
3687 gaim_debug(GAIM_DEBUG_WARNING, "blist", | |
7620 | 3688 "docklet_toggle called with gaim_connections_get_all() " |
5228 | 3689 "but no blist!\n"); |
3690 } | |
3691 } else if (mainwindow) { | |
3692 if (GTK_WIDGET_VISIBLE(mainwindow)) { | |
3693 if (GAIM_WINDOW_ICONIFIED(mainwindow)) { | |
3694 gtk_window_present(GTK_WINDOW(mainwindow)); | |
3695 } else { | |
3696 #if _WIN32 | |
3697 wgaim_systray_minimize(mainwindow); | |
3698 #endif | |
3699 gtk_widget_hide(mainwindow); | |
3700 } | |
3701 } else { | |
3702 #if _WIN32 | |
3703 wgaim_systray_maximize(mainwindow); | |
3704 #endif | |
3705 show_login(); | |
3706 } | |
3707 } else { | |
3708 show_login(); | |
3709 } | |
3710 } | |
3711 | |
3712 void gaim_gtk_blist_docklet_add() | |
3713 { | |
3714 docklet_count++; | |
3715 } | |
3716 | |
3717 void gaim_gtk_blist_docklet_remove() | |
3718 { | |
3719 docklet_count--; | |
3720 if (!docklet_count) { | |
7620 | 3721 if (gaim_connections_get_all()) |
5228 | 3722 gaim_blist_set_visible(TRUE); |
3723 else if (mainwindow) | |
3724 gtk_window_present(GTK_WINDOW(mainwindow)); | |
3725 else | |
3726 show_login(); | |
3727 } | |
3728 } | |
3729 | |
7620 | 3730 static GaimBlistUiOps blist_ui_ops = |
5228 | 3731 { |
3732 gaim_gtk_blist_new_list, | |
5256 | 3733 gaim_gtk_blist_new_node, |
5228 | 3734 gaim_gtk_blist_show, |
3735 gaim_gtk_blist_update, | |
3736 gaim_gtk_blist_remove, | |
3737 gaim_gtk_blist_destroy, | |
7620 | 3738 gaim_gtk_blist_set_visible, |
3739 gaim_gtk_blist_request_add_buddy, | |
3740 gaim_gtk_blist_request_add_chat, | |
3741 gaim_gtk_blist_request_add_group | |
5228 | 3742 }; |
3743 | |
3744 | |
7620 | 3745 GaimBlistUiOps * |
3746 gaim_gtk_blist_get_ui_ops(void) | |
5228 | 3747 { |
3748 return &blist_ui_ops; | |
3749 } | |
3750 | |
7620 | 3751 static void account_signon_cb(GaimConnection *gc, gpointer z) |
3752 { | |
3753 GaimAccount *account = gaim_connection_get_account(gc); | |
3754 GaimBlistNode *gnode, *cnode; | |
3755 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) | |
3756 { | |
3757 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
3758 continue; | |
3759 for(cnode = gnode->child; cnode; cnode = cnode->next) | |
3760 { | |
3761 GaimChat *chat; | |
3762 | |
3763 if(!GAIM_BLIST_NODE_IS_CHAT(cnode)) | |
3764 continue; | |
3765 | |
3766 chat = (GaimChat *)cnode; | |
3767 | |
3768 if(chat->account != account) | |
3769 continue; | |
3770 | |
7693 | 3771 if(gaim_blist_node_get_bool((GaimBlistNode*)chat, "gtk-autojoin")) |
7620 | 3772 serv_join_chat(gc, chat->components); |
3773 } | |
3774 } | |
3775 } | |
3776 | |
3777 void gaim_gtk_blist_init(void) | |
3778 { | |
3779 /* XXX */ | |
3780 static int gtk_blist_handle; | |
3781 | |
3782 gaim_signal_connect(gaim_connections_get_handle(), "signed-on", | |
3783 >k_blist_handle, GAIM_CALLBACK(account_signon_cb), | |
3784 NULL); | |
7731 | 3785 |
3786 /* Initialize prefs */ | |
3787 gaim_prefs_add_none("/gaim/gtk/blist"); | |
3788 gaim_prefs_add_bool("/gaim/gtk/blist/auto_expand_contacts", TRUE); | |
3789 gaim_prefs_add_int("/gaim/gtk/blist/button_style", GAIM_BUTTON_TEXT_IMAGE); | |
3790 gaim_prefs_add_bool("/gaim/gtk/blist/grey_idle_buddies", TRUE); | |
3791 gaim_prefs_add_bool("/gaim/gtk/blist/raise_on_events", FALSE); | |
3792 gaim_prefs_add_bool("/gaim/gtk/blist/show_buddy_icons", TRUE); | |
3793 gaim_prefs_add_bool("/gaim/gtk/blist/show_empty_groups", FALSE); | |
3794 gaim_prefs_add_bool("/gaim/gtk/blist/show_group_count", TRUE); | |
3795 gaim_prefs_add_bool("/gaim/gtk/blist/show_idle_time", TRUE); | |
3796 gaim_prefs_add_bool("/gaim/gtk/blist/show_offline_buddies", FALSE); | |
3797 gaim_prefs_add_bool("/gaim/gtk/blist/show_warning_level", TRUE); | |
3798 gaim_prefs_add_string("/gaim/gtk/blist/sort_type", ""); | |
3799 gaim_prefs_add_int("/gaim/gtk/blist/x", 0); | |
3800 gaim_prefs_add_int("/gaim/gtk/blist/y", 0); | |
3801 gaim_prefs_add_int("/gaim/gtk/blist/width", 0); | |
3802 gaim_prefs_add_int("/gaim/gtk/blist/height", 0); | |
3803 | |
7620 | 3804 } |
3805 | |
5228 | 3806 |
3807 | |
3808 /********************************************************************* | |
3809 * Public utility functions * | |
3810 *********************************************************************/ | |
3811 | |
3812 GdkPixbuf * | |
7620 | 3813 create_prpl_icon(GaimAccount *account) |
5228 | 3814 { |
3815 GaimPlugin *prpl; | |
3816 GaimPluginProtocolInfo *prpl_info = NULL; | |
3817 GdkPixbuf *status = NULL; | |
3818 char *filename = NULL; | |
3819 const char *protoname = NULL; | |
3820 char buf[256]; | |
3821 | |
7620 | 3822 prpl = gaim_find_prpl(gaim_account_get_protocol(account)); |
5228 | 3823 |
3824 if (prpl != NULL) { | |
3825 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
3826 | |
3827 if (prpl_info->list_icon != NULL) | |
3828 protoname = prpl_info->list_icon(account, NULL); | |
3829 } | |
3830 | |
3831 if (protoname == NULL) | |
3832 return NULL; | |
3833 | |
3834 /* | |
3835 * Status icons will be themeable too, and then it will look up | |
3836 * protoname from the theme | |
3837 */ | |
3838 g_snprintf(buf, sizeof(buf), "%s.png", protoname); | |
3839 | |
3840 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", | |
3841 "default", buf, NULL); | |
3842 status = gdk_pixbuf_new_from_file(filename, NULL); | |
3843 g_free(filename); | |
3844 | |
3845 return status; | |
3846 } | |
3847 | |
5422 | 3848 |
3849 /********************************************************************* | |
3850 * Buddy List sorting functions * | |
3851 *********************************************************************/ | |
3852 | |
7620 | 3853 void gaim_gtk_blist_sort_method_reg(const char *id, const char *name, gaim_gtk_blist_sort_function func) |
5422 | 3854 { |
3855 struct gaim_gtk_blist_sort_method *method = g_new0(struct gaim_gtk_blist_sort_method, 1); | |
7620 | 3856 method->id = g_strdup(id); |
5422 | 3857 method->name = g_strdup(name); |
7620 | 3858 method->func = func;; |
5422 | 3859 gaim_gtk_blist_sort_methods = g_slist_append(gaim_gtk_blist_sort_methods, method); |
3860 } | |
3861 | |
7620 | 3862 void gaim_gtk_blist_sort_method_unreg(const char *id){ |
3863 GSList *l = gaim_gtk_blist_sort_methods; | |
3864 | |
3865 while(l) { | |
3866 struct gaim_gtk_blist_sort_method *method = l->data; | |
3867 if(!strcmp(method->id, id)) { | |
3868 gaim_gtk_blist_sort_methods = g_slist_remove(gaim_gtk_blist_sort_methods, method); | |
3869 g_free(method->id); | |
3870 g_free(method->name); | |
3871 g_free(method); | |
3872 break; | |
3873 } | |
3874 } | |
5422 | 3875 } |
3876 | |
7620 | 3877 void gaim_gtk_blist_sort_method_set(const char *id){ |
5422 | 3878 GSList *l = gaim_gtk_blist_sort_methods; |
7620 | 3879 |
3880 if(!id) | |
3881 id = "none"; | |
3882 | |
3883 while (l && strcmp(((struct gaim_gtk_blist_sort_method*)l->data)->id, id)) | |
5422 | 3884 l = l->next; |
7620 | 3885 |
5422 | 3886 if (l) { |
3887 current_sort_method = l->data; | |
3888 } else if (!current_sort_method) { | |
7620 | 3889 gaim_gtk_blist_sort_method_set("none"); |
5422 | 3890 return; |
3891 } | |
3892 redo_buddy_list(gaim_get_blist(), TRUE); | |
3893 | |
3894 } | |
3895 | |
3896 /****************************************** | |
3897 ** Sort Methods | |
3898 ******************************************/ | |
3899 | |
7620 | 3900 static GtkTreeIter sort_method_none(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter parent_iter, GtkTreeIter *cur) |
5422 | 3901 { |
7620 | 3902 GtkTreeIter iter; |
3903 GaimBlistNode *sibling = node->prev; | |
3904 GtkTreeIter sibling_iter; | |
3905 | |
3906 if(cur) | |
5422 | 3907 return *cur; |
7620 | 3908 |
3909 while (sibling && !get_iter_from_node(sibling, &sibling_iter)) { | |
3910 sibling = sibling->prev; | |
5422 | 3911 } |
7620 | 3912 |
3913 gtk_tree_store_insert_after(gtkblist->treemodel, &iter, | |
3914 node->parent ? &parent_iter : NULL, | |
3915 sibling ? &sibling_iter : NULL); | |
3916 | |
5422 | 3917 return iter; |
3918 } | |
3919 | |
7620 | 3920 #if GTK_CHECK_VERSION(2,2,1) |
3921 | |
3922 static GtkTreeIter sort_method_alphabetical(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
5422 | 3923 { |
3924 GtkTreeIter more_z, iter; | |
3925 GaimBlistNode *n; | |
3926 GValue val = {0,}; | |
7620 | 3927 |
3928 const char *my_name; | |
3929 | |
3930 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
3931 my_name = gaim_contact_get_alias((GaimContact*)node); | |
3932 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
3933 my_name = gaim_chat_get_name((GaimChat*)node); | |
3934 } else { | |
3935 return sort_method_none(node, blist, groupiter, cur); | |
3936 } | |
3937 | |
5422 | 3938 |
3939 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
3940 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
7620 | 3941 return iter; |
3942 } | |
3943 | |
3944 do { | |
3945 const char *this_name; | |
3946 int cmp; | |
3947 | |
3948 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); | |
3949 n = g_value_get_pointer(&val); | |
3950 | |
3951 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
3952 this_name = gaim_contact_get_alias((GaimContact*)n); | |
3953 } else if(GAIM_BLIST_NODE_IS_CHAT(n)) { | |
3954 this_name = gaim_chat_get_name((GaimChat*)n); | |
3955 } else { | |
3956 this_name = NULL; | |
3957 } | |
3958 | |
3959 cmp = gaim_utf8_strcasecmp(my_name, this_name); | |
3960 | |
3961 if(this_name && (cmp < 0 || (cmp == 0 && node < n))) { | |
3962 if(cur) { | |
3963 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
3964 return *cur; | |
3965 } else { | |
3966 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
3967 &groupiter, &more_z); | |
3968 return iter; | |
3969 } | |
3970 } | |
3971 g_value_unset(&val); | |
3972 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
3973 | |
3974 if(cur) { | |
3975 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
3976 return *cur; | |
3977 } else { | |
3978 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3979 return iter; | |
3980 } | |
3981 } | |
3982 | |
3983 static GtkTreeIter sort_method_status(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) | |
3984 { | |
3985 GtkTreeIter more_z, iter; | |
3986 GaimBlistNode *n; | |
3987 GValue val = {0,}; | |
3988 | |
3989 GaimBuddy *my_buddy, *this_buddy; | |
3990 | |
3991 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
3992 my_buddy = gaim_contact_get_priority_buddy((GaimContact*)node); | |
3993 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
3994 if(cur) | |
3995 return *cur; | |
3996 | |
3997 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
3998 return iter; | |
3999 } else { | |
4000 return sort_method_none(node, blist, groupiter, cur); | |
4001 } | |
4002 | |
4003 | |
4004 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { | |
4005 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
5422 | 4006 return iter; |
4007 } | |
4008 | |
4009 do { | |
7620 | 4010 int cmp; |
4011 | |
5422 | 4012 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
4013 n = g_value_get_pointer(&val); | |
7620 | 4014 |
4015 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
4016 this_buddy = gaim_contact_get_priority_buddy((GaimContact*)n); | |
4017 } else { | |
4018 this_buddy = NULL; | |
4019 } | |
4020 | |
4021 cmp = gaim_utf8_strcasecmp(my_buddy ? | |
4022 gaim_contact_get_alias(gaim_buddy_get_contact(my_buddy)) | |
4023 : NULL, this_buddy ? | |
4024 gaim_contact_get_alias(gaim_buddy_get_contact(this_buddy)) | |
4025 : NULL); | |
4026 | |
4027 /* Hideous */ | |
4028 if(!this_buddy || | |
4029 ((my_buddy->present > this_buddy->present) || | |
4030 (my_buddy->present == this_buddy->present && | |
4031 (((my_buddy->uc & UC_UNAVAILABLE) < (this_buddy->uc & UC_UNAVAILABLE)) || | |
4032 (((my_buddy->uc & UC_UNAVAILABLE) == (this_buddy->uc & UC_UNAVAILABLE)) && | |
4033 (((my_buddy->idle == 0) && (this_buddy->idle != 0)) || | |
4034 (this_buddy->idle && (my_buddy->idle > this_buddy->idle)) || | |
4035 ((my_buddy->idle == this_buddy->idle) && | |
4036 (cmp < 0 || (cmp == 0 && node < n))))))))) { | |
4037 if(cur) { | |
4038 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
4039 return *cur; | |
4040 } else { | |
4041 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
4042 &groupiter, &more_z); | |
4043 return iter; | |
4044 } | |
5422 | 4045 } |
4046 g_value_unset(&val); | |
4047 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
7620 | 4048 |
4049 if(cur) { | |
4050 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
4051 return *cur; | |
4052 } else { | |
4053 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
4054 return iter; | |
4055 } | |
5422 | 4056 } |
4057 | |
7620 | 4058 static GtkTreeIter sort_method_log(GaimBlistNode *node, GaimBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur) |
5422 | 4059 { |
4060 GtkTreeIter more_z, iter; | |
7620 | 4061 GaimBlistNode *n = NULL, *n2; |
5422 | 4062 GValue val = {0,}; |
7620 | 4063 |
4064 int log_size = 0, this_log_size = 0; | |
4065 const char *buddy_name, *this_buddy_name; | |
4066 | |
4067 if(cur && (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter) == 1)) | |
4068 return *cur; | |
4069 | |
4070 if(GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
4071 for (n = node->child; n; n = n->next) | |
4072 log_size += gaim_log_get_total_size(((GaimBuddy*)(n))->name, ((GaimBuddy*)(n))->account); | |
4073 buddy_name = gaim_contact_get_alias((GaimContact*)node); | |
4074 } else if(GAIM_BLIST_NODE_IS_CHAT(node)) { | |
4075 /* we don't have a reliable way of getting the log filename | |
4076 * from the chat info in the blist, yet */ | |
4077 if(cur) | |
4078 return *cur; | |
4079 | |
4080 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
4081 return iter; | |
4082 } else { | |
4083 return sort_method_none(node, blist, groupiter, cur); | |
4084 } | |
4085 | |
4086 | |
5422 | 4087 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &more_z, &groupiter)) { |
4088 gtk_tree_store_insert(gtkblist->treemodel, &iter, &groupiter, 0); | |
4089 return iter; | |
4090 } | |
4091 | |
4092 do { | |
7620 | 4093 int cmp; |
4094 | |
5422 | 4095 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val); |
4096 n = g_value_get_pointer(&val); | |
7620 | 4097 this_log_size = 0; |
4098 | |
4099 if(GAIM_BLIST_NODE_IS_CONTACT(n)) { | |
4100 for (n2 = n->child; n2; n2 = n2->next) | |
4101 this_log_size += gaim_log_get_total_size(((GaimBuddy*)(n2))->name, ((GaimBuddy*)(n2))->account); | |
4102 this_buddy_name = gaim_contact_get_alias((GaimContact*)n); | |
4103 } else { | |
4104 this_buddy_name = NULL; | |
5422 | 4105 } |
7620 | 4106 |
4107 cmp = gaim_utf8_strcasecmp(buddy_name, this_buddy_name); | |
4108 | |
4109 if (!GAIM_BLIST_NODE_IS_CONTACT(n) || log_size > this_log_size || | |
4110 ((log_size == this_log_size) && | |
4111 (cmp < 0 || (cmp == 0 && node < n)))) { | |
4112 if(cur) { | |
4113 gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z); | |
4114 return *cur; | |
4115 } else { | |
4116 gtk_tree_store_insert_before(gtkblist->treemodel, &iter, | |
4117 &groupiter, &more_z); | |
4118 return iter; | |
4119 } | |
5422 | 4120 } |
4121 g_value_unset(&val); | |
4122 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL(gtkblist->treemodel), &more_z)); | |
7620 | 4123 |
4124 if(cur) { | |
4125 gtk_tree_store_move_before(gtkblist->treemodel, cur, NULL); | |
4126 return *cur; | |
4127 } else { | |
4128 gtk_tree_store_append(gtkblist->treemodel, &iter, &groupiter); | |
4129 return iter; | |
4130 } | |
4131 } | |
4132 | |
4133 #endif | |
4134 | |
4135 static void | |
4136 proto_act(GtkObject *obj, struct proto_actions_menu *pam) | |
4137 { | |
4138 if (pam->callback && pam->gc) | |
4139 pam->callback(pam->gc); | |
5422 | 4140 } |
7620 | 4141 |
4142 void | |
4143 gaim_gtk_blist_update_protocol_actions(void) | |
4144 { | |
4145 GtkWidget *menuitem; | |
4146 GtkWidget *submenu; | |
4147 GaimPluginProtocolInfo *prpl_info = NULL; | |
4148 GList *l; | |
4149 GList *c; | |
4150 struct proto_actions_menu *pam; | |
4151 GaimConnection *gc = NULL; | |
4152 int count = 0; | |
4153 char buf[256]; | |
4154 | |
4155 if (!protomenu) | |
4156 return; | |
4157 | |
4158 for (l = gtk_container_get_children(GTK_CONTAINER(protomenu)); | |
4159 l != NULL; | |
4160 l = l->next) { | |
4161 | |
4162 menuitem = l->data; | |
4163 pam = g_object_get_data(G_OBJECT(menuitem), "proto_actions_menu"); | |
4164 | |
4165 if (pam) | |
4166 g_free(pam); | |
4167 | |
4168 gtk_container_remove(GTK_CONTAINER(protomenu), GTK_WIDGET(menuitem)); | |
4169 } | |
4170 | |
4171 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
4172 gc = c->data; | |
4173 | |
4174 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
4175 | |
4176 if (prpl_info->actions && gc->login_time) | |
4177 count++; | |
4178 } | |
4179 | |
4180 if (!count) { | |
4181 g_snprintf(buf, sizeof(buf), _("No actions available")); | |
4182 menuitem = gtk_menu_item_new_with_label(buf); | |
4183 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4184 gtk_widget_show(menuitem); | |
4185 return; | |
4186 } | |
4187 | |
4188 if (count == 1) { | |
4189 GList *act; | |
4190 | |
4191 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
4192 gc = c->data; | |
4193 | |
4194 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
4195 | |
4196 if (prpl_info->actions && gc->login_time) | |
4197 break; | |
4198 } | |
4199 | |
4200 for (act = prpl_info->actions(gc); act != NULL; act = act->next) { | |
4201 if (act->data) { | |
4202 struct proto_actions_menu *pam = act->data; | |
4203 menuitem = gtk_menu_item_new_with_label(pam->label); | |
4204 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4205 g_signal_connect(G_OBJECT(menuitem), "activate", | |
4206 G_CALLBACK(proto_act), pam); | |
4207 g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", pam); | |
4208 gtk_widget_show(menuitem); | |
4209 } | |
4210 else | |
4211 gaim_separator(protomenu); | |
4212 } | |
4213 } | |
4214 else { | |
4215 for (c = gaim_connections_get_all(); c != NULL; c = c->next) { | |
4216 GaimAccount *account; | |
4217 GList *act; | |
4218 GdkPixbuf *pixbuf, *scale; | |
4219 GtkWidget *image; | |
4220 | |
4221 gc = c->data; | |
4222 | |
4223 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
4224 | |
4225 if (!prpl_info->actions || !gc->login_time) | |
4226 continue; | |
4227 | |
4228 account = gaim_connection_get_account(gc); | |
4229 | |
4230 g_snprintf(buf, sizeof(buf), "%s (%s)", | |
4231 gaim_account_get_username(account), | |
4232 gc->prpl->info->name); | |
4233 | |
4234 menuitem = gtk_image_menu_item_new_with_label(buf); | |
4235 | |
4236 pixbuf = create_prpl_icon(gc->account); | |
4237 if(pixbuf) { | |
4238 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, | |
4239 GDK_INTERP_BILINEAR); | |
4240 image = gtk_image_new_from_pixbuf(scale); | |
4241 g_object_unref(G_OBJECT(pixbuf)); | |
4242 g_object_unref(G_OBJECT(scale)); | |
4243 gtk_widget_show(image); | |
4244 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), | |
4245 image); | |
4246 } | |
4247 | |
4248 gtk_menu_shell_append(GTK_MENU_SHELL(protomenu), menuitem); | |
4249 gtk_widget_show(menuitem); | |
4250 | |
4251 submenu = gtk_menu_new(); | |
4252 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); | |
4253 gtk_widget_show(submenu); | |
4254 | |
4255 for (act = prpl_info->actions(gc); act != NULL; act = act->next) { | |
4256 if (act->data) { | |
4257 struct proto_actions_menu *pam = act->data; | |
4258 menuitem = gtk_menu_item_new_with_label(pam->label); | |
4259 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); | |
4260 g_signal_connect(G_OBJECT(menuitem), "activate", | |
4261 G_CALLBACK(proto_act), pam); | |
4262 g_object_set_data(G_OBJECT(menuitem), "proto_actions_menu", | |
4263 pam); | |
4264 gtk_widget_show(menuitem); | |
4265 } | |
4266 else | |
4267 gaim_separator(submenu); | |
4268 } | |
4269 } | |
4270 } | |
4271 } |