comparison src/buddy.c @ 4687:283fb289c510

[gaim-migrate @ 4998] This is a new buddy list. Lots of things about it just Don't Work. I probably already know about those things, and you'd just be wasting my time in submitting a bug report about it. I decided that instead of getting it to all work perfectly before committing, that I'd get it in cvs, and slowly fix it with regular commits. That way, it's easier to keep track of things, and other developers can help. Plus, I'm getting pissed off at the buddy list and want it to die. It's kinda boring, and doing nothing but the buddy list for such a long time has just gotten me very bitter. After 0.60 is released later this week, Gaim will resume being fun. This week is going to be very stressful, though, I'm sure. Things you ought to know about this buddy list: - It crashes - It leaks - There's no way to edit the buddy list, or access offline buddies - Most of the menus and buttons and whatnot just plain ol' don't work. - Status icons are only implemented for AIM. That's mostly just because I'm lazy. As such, you may want to be wary of updating this. If you do decide to update this, you may want to learn "cvs update -D yesterday" as well :) All the art there is just placeholder art. You probably won't really have as many problems as it sounds like you will from reading this. This message is extra-negative to stress that I don't want to be bothered with complaints about something not working about it :). I'll repeat: If something doesn't work, I probably already know about it. If you want to actually help with something, I'd be delighted to have it. IM me. -s. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Mon, 10 Mar 2003 05:30:31 +0000
parents 42d53c416bb9
children e19f91053ad0
comparison
equal deleted inserted replaced
4686:a1de8a9c99ba 4687:283fb289c510
17 * along with this program; if not, write to the Free Software 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 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * 19 *
20 */ 20 */
21 21
22 #ifdef GTK_DISABLE_DEPRECATED
23 #undef GTK_DISABLE_DEPRECATED
24 #endif
25
26 #define GTK_ENABLE_BROKEN
27
28 #ifdef HAVE_CONFIG_H 22 #ifdef HAVE_CONFIG_H
29 #include <config.h> 23 #include <config.h>
30 #endif 24 #endif
31 #ifdef GAIM_PLUGINS 25 #ifdef GAIM_PLUGINS
32 #ifndef _WIN32 26 #ifndef _WIN32
39 #include <ctype.h> 33 #include <ctype.h>
40 #include <math.h> 34 #include <math.h>
41 #include <time.h> 35 #include <time.h>
42 #include <ctype.h> 36 #include <ctype.h>
43 37
38 #include "pixmaps/no_icon.xpm"
39
44 #ifdef _WIN32 40 #ifdef _WIN32
45 #include <gdk/gdkwin32.h> 41 #include <gdk/gdkwin32.h>
46 #else 42 #else
47 #include <unistd.h> 43 #include <unistd.h>
48 #include <gdk/gdkx.h> 44 #include <gdk/gdkx.h>
51 #include <gdk/gdkkeysyms.h> 47 #include <gdk/gdkkeysyms.h>
52 #include <gtk/gtk.h> 48 #include <gtk/gtk.h>
53 #include "prpl.h" 49 #include "prpl.h"
54 #include "sound.h" 50 #include "sound.h"
55 #include "gaim.h" 51 #include "gaim.h"
52 #include "gtklist.h"
53 #include "gtkft.h"
56 54
57 #ifdef _WIN32 55 #ifdef _WIN32
58 #include "win32dep.h" 56 #include "win32dep.h"
59 #endif 57 #endif
60 58
61 #include "pixmaps/login_icon.xpm" 59 static gboolean obscured = FALSE;
62 #include "pixmaps/logout_icon.xpm" 60
63 #include "pixmaps/no_icon.xpm" 61 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node);
64 62
65 #include "pixmaps/away_big.xpm" 63 /***************************************************
66 64 * Callbacks *
67 #include "pixmaps/group.xpm" 65 ***************************************************/
68 66
69 #include "pixmaps/arrow_down.xpm" 67 static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) {
70 #include "pixmaps/arrow_right.xpm" 68 GaimBlistNode *node;
71 69 GtkTreeIter iter;
72 static GtkTooltips *tips; 70 GValue val = { 0, };
73 static GtkAccelGroup *accel; 71
74 static GtkWidget *editpane; 72 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
75 static GtkWidget *buddypane; 73
76 static GtkWidget *imchatbox; 74 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
77 static GtkWidget *edittree; 75 node = g_value_get_pointer(&val);
78 static GtkWidget *imbutton, *infobutton, *chatbutton, *awaybutton; 76
79 static GtkWidget *addbutton, *groupbutton, *rembutton; 77 if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
80 78 gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name);
81 GtkWidget *blist = NULL; 79 }
82 GtkWidget *bpmenu; 80 }
83 GtkWidget *buddies; 81
84 82 static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b)
85 typedef struct _GtkTreePixmaps GtkTreePixmaps; 83 {
86 84 struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
87 struct buddy_show { 85 if (pbm->callback)
88 GtkWidget *item; 86 pbm->callback(pbm->gc, b->name);
89 GtkWidget *pix; 87 }
90 GtkWidget *label; 88
91 GtkWidget *warn; 89 static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null)
92 GtkWidget *idle; 90 {
93 char *name; 91 GtkTreePath *path;
94 GSList *connlist; 92 GaimBlistNode *node;
95 guint log_timer; 93 GValue val = { 0, };
96 gint sound; 94 GtkTreeIter iter;
95 GtkWidget *menu, *menuitem;
96 GtkWidget *image;
97 GList *list;
98 struct prpl *prpl;
99
100 if (event->button != 3)
101 return FALSE;
102
103 /* Here we figure out which node was clicked */
104 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL))
105 return FALSE;
106 gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
107 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
108 node = g_value_get_pointer(&val);
109
110 if (!GAIM_BLIST_NODE_IS_BUDDY(node))
111 return FALSE;
112
113 menu = gtk_menu_new();
114
115 /* Protocol specific options */
116 prpl = find_prpl(((struct buddy*)node)->account->protocol);
117 if (prpl) {
118 list = prpl->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
119 while (list) {
120 struct proto_buddy_menu *pbm = list->data;
121 menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
122 g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm);
123 g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node);
124 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
125 list = list->next;
126 }
127 }
128
129 menuitem = gtk_image_menu_item_new_with_mnemonic("_IM");
130 image = gtk_image_new_from_stock(GAIM_STOCK_IM, GTK_ICON_SIZE_MENU);
131 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
132 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
133
134 menuitem = gtk_image_menu_item_new_with_mnemonic("_Alias");
135 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
136
137 menuitem = gtk_image_menu_item_new_with_mnemonic("Add Buddy _Pounce");
138 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
139
140 menuitem = gtk_image_menu_item_new_with_mnemonic("View _Log");
141 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
142
143 gtk_widget_show_all(menu);
144
145 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
146
147 return FALSE;
148 }
149
150 static void gaim_gtk_blist_reordered_cb(GtkTreeModel *model,
151 GtkTreePath *path,
152 GtkTreeIter *iter,
153 gint *neworder,
154 gpointer null)
155 {
156 debug_printf("This doesn't work because GTK is broken\n");
157
158 }
159
160 /* This is called 10 seconds after the buddy logs in. It removes the "logged in" icon and replaces it with
161 * the normal status icon */
162
163 static gboolean gaim_reset_present_icon(GaimBlistNode *b)
164 {
165 ((struct buddy*)b)->present = 1;
166 gaim_gtk_blist_update(NULL, b);
167 return FALSE;
168 }
169
170 static void gaim_gtk_blist_add_buddy_cb()
171 {
172 GtkTreeSelection *sel = gtk_tree_view_get_selection(gtkblist->treeview);
173 GtkTreeIter iter;
174 GaimBlistNode *node;
175 GValue val;
176
177 gtk_tree_selection_get_selected(sel, NULL, &iter);
178 gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
179 node = g_value_get_pointer(&val);
180
181 if (GAIM_BLIST_NODE_IS_BUDDY(node))
182 show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL);
183 else if (GAIM_BLIST_NODE_IS_GROUP(node))
184 show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL);
185
186 }
187
188
189 /***************************************************
190 * Crap *
191 ***************************************************/
192 static GtkItemFactoryEntry blist_menu[] =
193 {
194 /* Buddies menu */
195 { N_("/_Buddies"), NULL, NULL, 0, "<Branch>" },
196 { N_("/Buddies/_Add A Buddy..."), "<CTL>B", gaim_gtk_blist_add_buddy_cb, 0,
197 "<StockItem>", GTK_STOCK_ADD },
198 { N_("/Buddies/New _Instant Message..."), "<CTL>I", show_im_dialog, 0,
199 "<StockItem>", GAIM_STOCK_IM },
200 { N_("/Buddies/Join a _Chat..."), "<CTL>C", join_chat, 0,
201 "<StockItem>", GAIM_STOCK_CHAT },
202 { N_("/Buddies/sep1"), NULL, NULL, 0, "<Separator>" },
203 { N_("/Buddies/Get _User Info..."), "<CTL>J", show_info_dialog, 0,
204 "<StockItem>", GAIM_STOCK_INFO },
205 { N_("/Buddies/sep2"), NULL, NULL, 0, "<Separator>" },
206 { N_("/Buddies/_Signoff"), "<CTL>D", signoff_all, 0, NULL },
207 { N_("/Buddies/_Quit"), "<CTL>Q", do_quit, 0,
208 "<StockItem>", GTK_STOCK_QUIT },
209
210 /* Tools */
211 { N_("/_Tools"), NULL, NULL, 0, "<Branch>" },
212 { N_("/Tools/_Away"), NULL, NULL, 0, "<Branch>" },
213 { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "<Branch>" },
214 { N_("/Tools/sep1"), NULL, NULL, 0, "<Separator>" },
215 { N_("/Tools/A_ccounts"), "<CTL>A", account_editor, 0, NULL },
216 { N_("/Tools/Preferences"), "<CTL>P", show_prefs, 0,
217 "<StockItem>", GTK_STOCK_PREFERENCES },
218 { N_("/Tools/_File Transfers"), NULL, NULL, 0,
219 "<StockItem>", GTK_STOCK_REVERT_TO_SAVED },
220 { N_("/Tools/sep2"), NULL, NULL, 0, "<Separator>" },
221 { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "<Branch>" },
222 { N_("/Tools/Pr_ivacy"), NULL, show_privacy_options, 0, NULL },
223 { N_("/Tools/View System _Log"), NULL, NULL, 0, NULL },
224
225 /* Help */
226 { N_("/_Help"), NULL, NULL, 0, "<Branch>" },
227 { N_("/Help/Online _Help"), "F1", NULL, 0,
228 "<StockItem>", GTK_STOCK_HELP },
229 { N_("/Help/_Debug Window"), NULL, NULL, 0, NULL },
230 { N_("/Help/_About"), NULL, show_about, 0, NULL },
231
97 }; 232 };
98 233
99 /* stuff for actual display of buddy list */ 234 /*********************************************************
100 struct group_show { 235 * Private Utility functions *
101 GtkWidget *item; 236 *********************************************************/
102 GtkWidget *label; 237
103 GtkWidget *tree; 238 static GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b)
104 GSList *members; 239 {
105 char *name; 240 GdkPixbuf *status = NULL;
106 }; 241 GdkPixbuf *scale = NULL;
107 static GSList *shows = NULL; 242 GdkPixbuf *emblem = NULL;
108 243 gchar *filename = NULL;
109 int docklet_count = 0; 244 const char *protoname = NULL;
110 static gboolean obscured = FALSE; 245
111 246 char *se,*sw,*nw,*ne;
112 /* Predefine some functions */ 247
113 static void new_bp_callback(GtkWidget *w, struct buddy *bs); 248 int scalesize = 30;
114 static struct group_show *find_group_show(char *group); 249
115 static struct buddy_show *find_buddy_show(struct group_show *gs, char *name); 250 struct prpl* prpl = find_prpl(b->account->protocol);
116 static int group_number(char *group); 251 if (prpl->list_icon)
117 static int buddy_number(char *group, char *buddy); 252 protoname = prpl->list_icon(b->account, b);
118 static struct group_show *new_group_show(char *group); 253 if (prpl->list_emblems)
119 static struct buddy_show *new_buddy_show(struct group_show *gs, struct buddy *buddy, char **xpm); 254 prpl->list_emblems(b, &se, &sw, &nw, &ne);
120 static void remove_buddy_show(struct group_show *gs, struct buddy_show *bs); 255
121 static struct group_show *find_gs_by_bs(struct buddy_show *b); 256 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
122 static void update_num_group(struct group_show *gs); 257 scalesize = 15;
123 static void update_idle_time(struct buddy_show *bs); 258 sw = nw = ne = NULL; /* So that only the se icon will composite */
124 259 }
125 void handle_group_rename(struct group *g, char *prevname) 260
126 { 261
127 struct group_show *gs, *new_gs; 262 if (b->present == 2) {
128 GtkCTreeNode *c; 263 /* If b->present is 2, that means this buddy has just signed on. We use the "login" icon for the
129 264 * status, and we set a timeout to change it to a normal icon after 10 seconds. */
130 c = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), NULL, g); 265 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL);
131 gtk_ctree_node_set_text(GTK_CTREE(edittree), c, 0, g->name); 266 status = gdk_pixbuf_new_from_file(filename,NULL);
132 267 g_free(filename);
133 gs = find_group_show(prevname); 268 g_timeout_add(10000, (GSourceFunc)gaim_reset_present_icon, b);
134 if (!gs) { 269
135 return; 270 /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and
136 } 271 then it will look up protoname from the theme */
137 new_gs = find_group_show(g->name);
138 if (new_gs) {
139 /* transfer everything that was in gs and is in the same gaim_conn as g
140 * over to new_gs. */
141 while (gs->members) {
142 new_gs->members = g_slist_append(new_gs->members, gs->members->data);
143 gs->members = g_slist_remove(gs->members, gs->members->data);
144
145 }
146 shows = g_slist_remove(shows, gs);
147 gtk_tree_remove_item(GTK_TREE(buddies), gs->item);
148 g_free(gs->name);
149 g_free(gs);
150 update_num_group(new_gs);
151 } else { 272 } else {
152 g_free(gs->name); 273 char *image = g_strdup_printf("%s.png", protoname);
153 gs->name = g_strdup(g->name); 274 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
154 update_num_group(gs); 275 status = gdk_pixbuf_new_from_file(filename,NULL);
155 } 276 g_free(image);
156 } 277 g_free(filename);
157 278
158 void handle_buddy_rename(struct buddy *b, char *prevname) 279 }
159 { 280
160 struct gaim_conversation *cnv; 281 if (!status)
161 struct buddy_show *bs; 282 return NULL;
162 struct group_show *gs; 283
163 struct group *g; 284 scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR);
164 GtkCTreeNode *c; 285
165 char buf[256]; 286 /* Emblems */
166 287
167 c = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), NULL, b); 288 /* Each protocol can specify up to four "emblems" to composite over the base icon. "away", "busy", "mobile user"
168 if (get_buddy_alias_only(b)) 289 * are all examples of states represented by emblems. I'm not even really sure I like this yet. */
169 g_snprintf(buf, sizeof(buf), "%s (%s)", b->name, get_buddy_alias(b)); 290
170 else 291 /* XXX Clean this crap up, yo. */
171 g_snprintf(buf, sizeof(buf), "%s", b->name); 292 if (se) {
172 gtk_ctree_node_set_text(GTK_CTREE(edittree), c, 0, buf); 293 char *image = g_strdup_printf("%s.png", se);
173 294 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
174 if ((cnv = gaim_find_conversation(b->name)) != NULL) 295 g_free(image);
175 gaim_conversation_autoset_title(cnv); 296 emblem = gdk_pixbuf_new_from_file(filename,NULL);
176 297 g_free(filename);
177 g = find_group_by_buddy(b); 298 if (emblem) {
178 if (!g) { 299 if (blist_options & OPT_BLIST_SHOW_ICONS)
179 /* shouldn't happen */ 300 gdk_pixbuf_composite (emblem,
180 return; 301 scale, 15, 15,
181 } 302 15, 15,
182 gs = find_group_show(g->name); 303 15, 15,
183 if (!gs) { 304 1, 1,
184 return; 305 GDK_INTERP_BILINEAR,
185 } 306 255);
186 bs = find_buddy_show(gs, prevname);
187 if (!bs) {
188 /* buddy's offline */
189 return;
190 }
191
192 if (g_strcasecmp(b->name, prevname)) {
193 bs->connlist = g_slist_remove(bs->connlist, b->account->gc);
194 if (!bs->connlist) {
195 gs->members = g_slist_remove(gs->members, bs);
196 if (bs->log_timer > 0)
197 g_source_remove(bs->log_timer);
198 bs->log_timer = 0;
199 remove_buddy_show(gs, bs);
200 g_free(bs->name);
201 g_free(bs);
202 }
203 update_num_group(gs);
204 } else {
205 gtk_label_set_text(GTK_LABEL(bs->label), get_buddy_alias(b));
206 update_idle_time(bs);
207 }
208 }
209
210 void destroy_buddy()
211 {
212 GSList *s = shows;
213 struct group_show *g;
214 GSList *m;
215 struct buddy_show *b;
216 while (s) {
217 g = (struct group_show *)s->data;
218 debug_printf("group_show still exists: %s\n", g->name);
219 m = g->members;
220 while (m) {
221 b = (struct buddy_show *)m->data;
222 debug_printf("buddy_show still exists: %s\n", b->name);
223 m = g_slist_remove(m, b);
224 if (b->log_timer > 0)
225 g_source_remove(b->log_timer);
226 b->log_timer = 0;
227 gtk_tree_remove_item(GTK_TREE(g->tree), b->item);
228 g_free(b->name);
229 g_free(b);
230 }
231 gtk_tree_remove_item(GTK_TREE(buddies), g->item);
232 s = g_slist_remove(s, g);
233 g_free(g->name);
234 g_free(g);
235 }
236 shows = NULL;
237
238 if (blist)
239 gtk_widget_destroy(blist);
240 blist = NULL;
241 imchatbox = NULL;
242 awaymenu = NULL;
243 protomenu = NULL;
244 }
245
246 static void adjust_pic(GtkWidget *button, const char *c, gchar **xpm)
247 {
248 GdkPixmap *pm;
249 GdkBitmap *bm;
250 GtkWidget *pic;
251 GtkWidget *label;
252
253 /*if the user had opted to put pictures on the buttons */
254 if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM && xpm) {
255 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm, NULL, xpm);
256 pic = gtk_pixmap_new(pm, bm);
257 gtk_widget_show(pic);
258 gdk_pixmap_unref(pm);
259 gdk_bitmap_unref(bm);
260 label = GTK_BIN(button)->child;
261 gtk_container_remove(GTK_CONTAINER(button), label);
262 gtk_container_add(GTK_CONTAINER(button), pic);
263 } else {
264 label = gtk_label_new(c);
265 gtk_widget_show(label);
266 pic = GTK_BIN(button)->child;
267 gtk_container_remove(GTK_CONTAINER(button), pic);
268 gtk_container_add(GTK_CONTAINER(button), label);
269 }
270
271 }
272
273 /* This will remain here until we phase out the others */
274 static void adjust_pic2(GtkWidget *button, const char *c, gchar *icon)
275 {
276 GtkWidget *pic;
277 GtkWidget *label;
278
279 /*if the user had opted to put pictures on the buttons */
280 if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM && icon) {
281 label = GTK_BIN(button)->child;
282 gtk_container_remove(GTK_CONTAINER(button), label);
283 gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON));
284 gtk_widget_show_all(button);
285 } else {
286 label = gtk_label_new(c);
287 gtk_widget_show(label);
288 pic = GTK_BIN(button)->child;
289 gtk_container_remove(GTK_CONTAINER(button), pic);
290 gtk_container_add(GTK_CONTAINER(button), label);
291 }
292
293 }
294
295
296 void toggle_show_empty_groups()
297 {
298 if (blist_options & OPT_BLIST_NO_MT_GRP) {
299 /* remove any group_shows with empty members */
300 GSList *s = shows;
301 struct group_show *g;
302
303 while (s) {
304 g = (struct group_show *)s->data;
305 if (!g_slist_length(g->members)) {
306 shows = g_slist_remove(shows, g);
307 s = shows;
308 gtk_tree_remove_item(GTK_TREE(buddies), g->item);
309 g_free(g->name);
310 g_free(g);
311 } else
312 s = g_slist_next(s);
313 }
314
315 } else {
316 /* put back all groups */
317 GSList *m = groups;
318
319 while (m) {
320 struct group *g = (struct group *)m->data;
321 if (!find_group_show(g->name) && gaim_group_on_account(g, NULL))
322 new_group_show(g->name);
323 m = g_slist_next(m);
324 }
325 }
326 }
327
328 void toggle_buddy_pixmaps()
329 {
330 GSList *s = shows;
331 struct group_show *g;
332 GSList *m;
333 struct buddy_show *b;
334
335 while (s) {
336 g = s->data;
337 m = g->members;
338 while (m) {
339 b = m->data;
340 if (blist_options & OPT_BLIST_SHOW_PIXMAPS)
341 gtk_widget_show(b->pix);
342 else 307 else
343 gtk_widget_hide(b->pix); 308 gdk_pixbuf_composite (emblem,
344 m = m->next; 309 scale, 0, 0,
345 } 310 15, 15,
346 s = s->next; 311 0, 0,
347 } 312 1, 1,
348 } 313 GDK_INTERP_BILINEAR,
349 314 255);
350 static void update_num_group(struct group_show *gs) 315 }
351 { 316 }
352 GSList *c = connections; 317 if (sw) {
353 struct group *g = find_group(gs->name); 318 char *image = g_strdup_printf("%s.png", sw);
354 struct buddy *b; 319 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
355 int total = 0, on = 0; 320 g_free(image);
356 char buf[256]; 321 emblem = gdk_pixbuf_new_from_file(filename,NULL);
357 322 g_free(filename);
358 if (!g_slist_find(shows, gs)) { 323 if (emblem) {
359 debug_printf("update_num_group called for unfound group_show %s\n", gs->name); 324 gdk_pixbuf_composite (emblem,
360 return; 325 scale, 0, 15,
361 } 326 15, 15,
362 if (g) { 327 0, 15,
363 for (c = g->members; c; c = c->next) { 328 1, 1,
364 b = c->data; 329 GDK_INTERP_BILINEAR,
365 if(b->account->gc) { 330 255);
366 if(b->present) 331 }
367 on++; 332 }
368 total++; 333 if (nw) {
369 } 334 char *image = g_strdup_printf("%s.png", nw);
370 } 335 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
371 } 336 g_free(image);
372 337 emblem = gdk_pixbuf_new_from_file(filename,NULL);
373 if (blist_options & OPT_BLIST_SHOW_GRPNUM) 338 g_free(filename);
374 g_snprintf(buf, sizeof buf, "%s (%d/%d)", gs->name, on, total); 339 if (emblem) {
375 else 340 gdk_pixbuf_composite (emblem,
376 g_snprintf(buf, sizeof buf, "%s", gs->name); 341 scale, 0, 0,
377 342 15, 15,
378 gtk_label_set_text(GTK_LABEL(gs->label), buf); 343 0, 0,
379 } 344 1, 1,
380 345 GDK_INTERP_BILINEAR,
381 void update_num_groups(void) 346 255);
382 { 347 }
383 GSList *s = shows; 348 }
384 struct group_show *g; 349 if (ne) {
385 350 char *image = g_strdup_printf("%s.png", ne);
386 while (s) { 351 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL);
387 g = (struct group_show *)s->data; 352 g_free(image);
388 update_num_group(g); 353 emblem = gdk_pixbuf_new_from_file(filename,NULL);
389 s = g_slist_next(s); 354 g_free(filename);
390 } 355 if (emblem) {
391 } 356 gdk_pixbuf_composite (emblem,
392 357 scale, 15, 0,
393 void update_button_pix() 358 15, 15,
394 { 359 15, 0,
395 360 1, 1,
396 adjust_pic2(addbutton, _("Add"), GTK_STOCK_ADD); 361 GDK_INTERP_BILINEAR,
397 adjust_pic(groupbutton, _("Group"), (gchar **)group_xpm); 362 255);
398 adjust_pic2(rembutton, _("Remove"), GTK_STOCK_REMOVE); 363 }
399 364 }
400 if (!(blist_options & OPT_BLIST_NO_BUTTONS)) { 365
401 adjust_pic(awaybutton, _("Away"), (gchar **)away_big_xpm); 366
402 adjust_pic2(chatbutton, _("Chat"), GTK_STOCK_JUMP_TO); 367 /* Idle gray buddies affects the whole row. This converts the status icon to greyscale. */
403 adjust_pic2(imbutton, _("IM"), GTK_STOCK_CONVERT); 368 if (b->idle)
404 adjust_pic2(infobutton, _("Info"), GTK_STOCK_FIND); 369 gdk_pixbuf_saturate_and_pixelate(scale, scale, 0, FALSE);
405 } 370 return scale;
406 gtk_widget_hide(addbutton->parent); 371 }
407 gtk_widget_show(addbutton->parent); 372
408 if (!(blist_options & OPT_BLIST_NO_BUTTONS)) { 373 static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b)
409 gtk_widget_hide(chatbutton->parent); 374 {
410 gtk_widget_show(chatbutton->parent); 375 /* This just opens a file from ~/.gaim/icons/screenname. This needs to change to be more gooder. */
411 } 376 char *file = g_build_filename(gaim_user_dir(), "icons", normalize(b->name), NULL);
412 } 377 GdkPixbuf *buf = gdk_pixbuf_new_from_file(file, NULL);
413 378
414 void set_blist_tab() 379 if (!(blist_options & OPT_BLIST_SHOW_ICONS))
415 { 380 return NULL;
416 GtkWidget *blist_notebook; 381
417 if (!buddypane) 382 if (buf) {
418 return; 383 if (b->idle) {
419 384 gdk_pixbuf_saturate_and_pixelate(buf, buf, 0, FALSE);
420 blist_notebook = buddypane->parent; /* The "Online" Page */ 385 }
421 386 return gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR);
422 debug_printf("blist_options = %d\n", blist_options); 387 }
423 if((blist_options & OPT_BLIST_BOTTOM_TAB)) 388 return NULL;
424 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(blist_notebook), GTK_POS_BOTTOM); 389 }
425 else 390
426 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(blist_notebook), GTK_POS_TOP); 391 static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b)
427 392 {
428 } 393 char *name = gaim_get_buddy_alias(b);
429 394 char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL;
430 395 /* XXX Clean up this crap */
431 static int handle_click_group(GtkWidget *widget, GdkEventButton *event, struct group *g) 396
432 { 397 int ihrs, imin;
433 if (event->type == GDK_2BUTTON_PRESS) { 398 char *idletime = "";
434 if (GTK_TREE_ITEM(widget)->expanded) 399 char *warning = idletime;
435 gtk_tree_item_collapse(GTK_TREE_ITEM(widget)); 400 time_t t;
436 else 401
437 gtk_tree_item_expand(GTK_TREE_ITEM(widget)); 402 if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
438 return TRUE; 403 if (b->idle > 0) {
439 } 404 text = g_strdup_printf("<span color='gray'>%s</span>",
440 405 esc);
441 return FALSE; 406 g_free(esc);
442 } 407 return text;
443
444 void pressed_im_bud(GtkWidget *widget, struct buddy *b)
445 {
446 struct gaim_conversation *c;
447
448 c = gaim_find_conversation(b->name);
449
450 if (c != NULL)
451 gaim_window_show(gaim_conversation_get_window(c));
452 else
453 c = gaim_conversation_new(GAIM_CONV_IM, b->account, b->name);
454 }
455
456 void pressed_im(GtkWidget *widget, struct buddy_show *b)
457 {
458 struct gaim_conversation *c;
459
460 c = gaim_find_conversation(b->name);
461
462 if (c != NULL) {
463 gaim_window_show(gaim_conversation_get_window(c));
464 } else {
465 struct gaim_account *account;
466
467 account = ((struct gaim_connection *)b->connlist->data)->account;
468 c = gaim_conversation_new(GAIM_CONV_IM, account, b->name);
469 }
470 }
471
472 void pressed_log(GtkWidget *widget, char *name)
473 {
474 show_log(name);
475 }
476
477 void show_syslog()
478 {
479 show_log(NULL);
480 }
481
482 void pressed_alias_bs(GtkWidget *widget, struct buddy_show *bs)
483 {
484 struct gaim_connection *gc = bs->connlist->data;
485 alias_dialog_bud(find_buddy(gc->account, bs->name));
486 }
487
488 void pressed_alias_bud(GtkWidget *widget, struct buddy *b)
489 {
490 alias_dialog_bud(b);
491 }
492
493 static void menu_click(GtkObject *obj, char *who)
494 {
495 GList *list = gtk_object_get_user_data(obj);
496 struct proto_buddy_menu *pbm = list->data;
497 if (pbm->callback)
498 pbm->callback(pbm->gc, who);
499 }
500
501 static int handle_click_buddy(GtkWidget *widget, GdkEventButton *event, struct buddy_show *b)
502 {
503 if (!b->connlist)
504 return FALSE;
505
506 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
507 struct gaim_conversation *c;
508 struct gaim_account *account;
509
510 account = ((struct gaim_connection *)b->connlist->data)->account;
511
512 c = gaim_find_conversation(b->name);
513
514 if (c != NULL) {
515 struct gaim_window *win = gaim_conversation_get_window(c);
516 size_t index = gaim_conversation_get_index(c);
517
518 gaim_window_switch_conversation(win, index);
519 gaim_window_show(win);
520
521 gaim_conversation_set_account(c, account);
522 }
523 else
524 c = gaim_conversation_new(GAIM_CONV_IM, account, b->name);
525
526 gaim_window_switch_conversation(gaim_conversation_get_window(c),
527 gaim_conversation_get_index(c));
528
529 gaim_window_raise(gaim_conversation_get_window(c));
530
531 /* XXX-GTK gtk_widget_grab_focus(c->entry); */
532 } else if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
533 static GtkWidget *menu = NULL;
534 static GList *mo_top = NULL;
535 GtkWidget *button;
536 GtkWidget *menuitem;
537 GtkWidget *conmenu;
538 GSList *cn = b->connlist;
539 struct gaim_connection *g;
540 /* We're gonna make us a menu right here */
541
542 /*
543 * If a menu already exists, destroy it before creating a new one,
544 * thus freeing-up the memory it occupied. Same for its associated
545 * (prpl menu items) GList.
546 */
547 if(menu) {
548 gtk_widget_destroy(menu);
549 if(mo_top) {
550 g_list_foreach(mo_top, (GFunc)g_free, NULL);
551 g_list_free(mo_top);
552 mo_top = NULL;
553 }
554 }
555
556 menu = gtk_menu_new();
557
558 button = gtk_menu_item_new_with_label(_("IM"));
559 g_signal_connect(GTK_OBJECT(button), "activate", G_CALLBACK(pressed_im), b);
560 gtk_menu_append(GTK_MENU(menu), button);
561 gtk_widget_show(button);
562
563 button = gtk_menu_item_new_with_label(_("Alias"));
564 g_signal_connect(GTK_OBJECT(button), "activate", G_CALLBACK(pressed_alias_bs), b);
565 gtk_menu_append(GTK_MENU(menu), button);
566 gtk_widget_show(button);
567
568 button = gtk_menu_item_new_with_label(_("Add Buddy Pounce"));
569 g_signal_connect(GTK_OBJECT(button), "activate",
570 G_CALLBACK(new_bp_callback),
571 cn ? find_buddy(((struct gaim_connection *)cn->data)->account, b->name) : NULL);
572 gtk_menu_append(GTK_MENU(menu), button);
573 gtk_widget_show(button);
574
575 button = gtk_menu_item_new_with_label(_("View Log"));
576 g_signal_connect(GTK_OBJECT(button), "activate",
577 G_CALLBACK(pressed_log), b->name);
578 gtk_menu_append(GTK_MENU(menu), button);
579 gtk_widget_show(button);
580
581 if (g_slist_length(cn) > 1) {
582 while (cn) {
583 g = (struct gaim_connection *)cn->data;
584 if (g->prpl->buddy_menu) {
585 GList *mo = mo_top = g->prpl->buddy_menu(g, b->name);
586
587 menuitem = gtk_menu_item_new_with_label(g->username);
588 gtk_menu_append(GTK_MENU(menu), menuitem);
589 gtk_widget_show(menuitem);
590
591 conmenu = gtk_menu_new();
592 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), conmenu);
593 gtk_widget_show(conmenu);
594
595 while (mo) {
596 struct proto_buddy_menu *pbm = mo->data;
597 GtkWidget *button;
598
599 button = gtk_menu_item_new_with_label(pbm->label);
600 g_signal_connect(GTK_OBJECT(button), "activate",
601 G_CALLBACK(menu_click), b->name);
602 gtk_object_set_user_data(GTK_OBJECT(button), mo);
603 gtk_menu_append(GTK_MENU(conmenu), button);
604 gtk_widget_show(button);
605
606 mo = mo->next;
607 }
608 }
609 cn = g_slist_next(cn);
610 }
611 } else { 408 } else {
612 g = (struct gaim_connection *)cn->data; 409 return esc;
613 if (g->prpl->buddy_menu) { 410 }
614 GList *mo = mo_top = g->prpl->buddy_menu(g, b->name); 411 }
615
616 while (mo) {
617 struct proto_buddy_menu *pbm = mo->data;
618 GtkWidget *button;
619
620 button = gtk_menu_item_new_with_label(pbm->label);
621 g_signal_connect(GTK_OBJECT(button), "activate",
622 G_CALLBACK(menu_click), b->name);
623 gtk_object_set_user_data(GTK_OBJECT(button), mo);
624 gtk_menu_append(GTK_MENU(menu), button);
625 gtk_widget_show(button);
626
627 mo = mo->next;
628 }
629 }
630 }
631
632 /* we send the menu widget so we can add menuitems within a plugin */
633 plugin_event(event_draw_menu, menu, b->name);
634
635 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
636
637 } else if (event->type == GDK_3BUTTON_PRESS && event->button == 2) {
638 if (!g_strcasecmp("zilding", normalize (b->name)))
639 show_ee_dialog(0);
640 else if (!g_strcasecmp("robflynn", normalize (b->name)))
641 show_ee_dialog(1);
642 else if (!g_strcasecmp("flynorange", normalize (b->name)))
643 show_ee_dialog(2);
644 else if (!g_strcasecmp("ewarmenhoven", normalize (b->name)))
645 show_ee_dialog(3);
646 else if (!g_strcasecmp("markster97", normalize (b->name)))
647 show_ee_dialog(4);
648 else if (!g_strcasecmp("seanegn", normalize (b->name)))
649 show_ee_dialog(5);
650 else if (!g_strcasecmp("chipx86", normalize (b->name)))
651 show_ee_dialog(6);
652 else if (!g_strcasecmp("kingant", normalize (b->name)))
653 show_ee_dialog(7);
654 else if (!g_strcasecmp("lschiere", normalize (b->name)))
655 show_ee_dialog(8);
656
657 } else {
658
659 /* Anything for other buttons? :) */
660 }
661
662 return FALSE;
663 }
664
665 static void un_alias(GtkWidget *a, struct buddy *b)
666 {
667 b->alias[0] = '\0';
668 handle_buddy_rename(b, b->name); /* make me a sammich! */
669 serv_alias_buddy(b);
670 gaim_blist_save();
671 }
672
673 static gboolean click_edit_tree(GtkWidget *widget, GdkEventButton *event, gpointer data)
674 {
675 GtkCTreeNode *node;
676 int *type;
677 int row, column;
678 static GtkWidget *menu = NULL;
679 GtkWidget *button;
680 static GList *mo_top = NULL;
681
682 if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
683 return FALSE;
684
685 if (!gtk_clist_get_selection_info(GTK_CLIST(edittree), event->x, event->y, &row, &column))
686 return FALSE;
687
688 node = gtk_ctree_node_nth(GTK_CTREE(edittree), row);
689 type = gtk_ctree_node_get_row_data(GTK_CTREE(edittree), node);
690
691 /*
692 * If a menu already exists, destroy it before creating a new one,
693 * thus freeing-up the memory it occupied.
694 */
695 if(menu) {
696 gtk_widget_destroy(menu);
697 menu = NULL; /* safety measure */
698 if(mo_top) {
699 g_list_foreach(mo_top, (GFunc)g_free, NULL);
700 g_list_free(mo_top);
701 mo_top = NULL;
702 }
703 }
704
705 if (*type == EDIT_GROUP) {
706 struct group *group = (struct group *)type;
707 menu = gtk_menu_new();
708
709 button = gtk_menu_item_new_with_label(_("Rename"));
710 g_signal_connect(GTK_OBJECT(button), "activate",
711 G_CALLBACK(show_rename_group), group);
712 gtk_menu_append(GTK_MENU(menu), button);
713 gtk_widget_show(button);
714
715 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
716
717 return TRUE;
718 } else if (*type == EDIT_BUDDY) {
719 struct buddy *b = (struct buddy *)type;
720 menu = gtk_menu_new();
721
722 button = gtk_menu_item_new_with_label(_("IM"));
723 g_signal_connect(GTK_OBJECT(button), "activate", G_CALLBACK(pressed_im_bud), b);
724 gtk_menu_append(GTK_MENU(menu), button);
725 gtk_widget_show(button);
726
727 button = gtk_menu_item_new_with_label(_("Alias"));
728 g_signal_connect(GTK_OBJECT(button), "activate",
729 G_CALLBACK(pressed_alias_bud), b);
730 gtk_menu_append(GTK_MENU(menu), button);
731 gtk_widget_show(button);
732
733 if (b->alias[0]) {
734 button = gtk_menu_item_new_with_label(_("Un-Alias"));
735 g_signal_connect(GTK_OBJECT(button), "activate", G_CALLBACK(un_alias), b);
736 gtk_menu_append(GTK_MENU(menu), button);
737 gtk_widget_show(button);
738 }
739
740 button = gtk_menu_item_new_with_label(_("Rename"));
741 g_signal_connect(GTK_OBJECT(button), "activate",
742 G_CALLBACK(show_rename_buddy), b);
743 gtk_menu_append(GTK_MENU(menu), button);
744 gtk_widget_show(button);
745
746 button = gtk_menu_item_new_with_label(_("Add Buddy Pounce"));
747 g_signal_connect(GTK_OBJECT(button), "activate",
748 G_CALLBACK(new_bp_callback), b);
749 gtk_menu_append(GTK_MENU(menu), button);
750 gtk_widget_show(button);
751
752 button = gtk_menu_item_new_with_label(_("View Log"));
753 g_signal_connect(GTK_OBJECT(button), "activate",
754 G_CALLBACK(pressed_log), b->name);
755 gtk_menu_append(GTK_MENU(menu), button);
756 gtk_widget_show(button);
757
758 /*
759 * Add protocol-specific edit buddy menu items if they exist
760 */
761 if (b->account->gc && b->account->gc->prpl->edit_buddy_menu) {
762 GList *mo = mo_top = b->account->gc->prpl->edit_buddy_menu(b->account->gc, b->name);
763
764 while (mo) {
765 struct proto_buddy_menu *pbm = mo->data;
766 GtkWidget *button;
767
768 button = gtk_menu_item_new_with_label(pbm->label);
769 g_signal_connect(GTK_OBJECT(button), "activate",
770 G_CALLBACK(menu_click), b->name);
771 gtk_object_set_user_data(GTK_OBJECT(button), mo);
772 gtk_menu_append(GTK_MENU(menu), button);
773 gtk_widget_show(button);
774
775 mo = mo->next;
776 }
777 }
778
779 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
780
781 return TRUE;
782 }
783
784 return FALSE;
785 }
786
787
788 /*
789 * Find and remove CTree node associated with buddylist entry
790 */
791 static void ui_remove_buddy_node(struct group *rem_g, struct buddy *rem_b)
792 {
793 GtkCTreeNode *gnode = NULL, *bnode;
794
795 if((gnode = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), NULL, rem_g)) != NULL &&
796 (bnode = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), gnode, rem_b)) != NULL)
797 {
798 gtk_ctree_remove_node(GTK_CTREE(edittree), bnode);
799 }
800 }
801
802 void ui_remove_buddy(struct buddy *rem_b)
803 {
804 struct gaim_conversation *c;
805 struct group *rem_g = find_group_by_buddy(rem_b);
806 struct group_show *gs;
807 struct buddy_show *bs;
808
809 gs = find_group_show(rem_g->name);
810 if (gs) {
811 bs = find_buddy_show(gs, rem_b->name);
812 if (bs) {
813 if (g_slist_find(bs->connlist, rem_b->account->gc)) {
814 bs->connlist = g_slist_remove(bs->connlist, rem_b->account->gc);
815 if (!g_slist_length(bs->connlist)) {
816 gs->members = g_slist_remove(gs->members, bs);
817 if (bs->log_timer > 0)
818 g_source_remove(bs->log_timer);
819 bs->log_timer = 0;
820 remove_buddy_show(gs, bs);
821 g_free(bs->name);
822 g_free(bs);
823 if (!g_slist_length(gs->members) &&
824 (blist_options & OPT_BLIST_NO_MT_GRP)) {
825 shows = g_slist_remove(shows, gs);
826 gtk_tree_remove_item(GTK_TREE(buddies), gs->item);
827 g_free(gs->name);
828 g_free(gs);
829 gs = NULL;
830 }
831 }
832 }
833 }
834 if (gs)
835 update_num_group(gs);
836 }
837
838 c = gaim_find_conversation(rem_b->name);
839
840 if (c)
841 gaim_conversation_update(c, GAIM_CONV_UPDATE_REMOVE);
842
843 /* CONVXXX update_buttons_by_protocol(c); */
844
845 /* Remove CTree node for buddy */
846 ui_remove_buddy_node(rem_g, rem_b);
847
848 }
849
850 void ui_remove_group(struct group *rem_g)
851 {
852 struct group_show *gs;
853 GtkCTreeNode *gnode = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), NULL, rem_g);
854 gtk_ctree_remove_node(GTK_CTREE(edittree), gnode);
855
856
857 if ((gs = find_group_show(rem_g->name)) != NULL) {
858 shows = g_slist_remove(shows, gs);
859 gtk_tree_remove_item(GTK_TREE(buddies), gs->item);
860 g_free(gs->name);
861 g_free(gs);
862 }
863 }
864
865 gboolean edit_drag_compare_func(GtkCTree *ctree, GtkCTreeNode *source_node,
866 GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling)
867 {
868 int *type;
869
870 type = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(ctree), source_node);
871
872 if (*type == EDIT_GROUP) {
873 if (!new_parent)
874 return TRUE;
875 } else if (*type == EDIT_BUDDY) {
876 if (new_parent) {
877 type = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(ctree), new_parent);
878 if (*type == EDIT_GROUP)
879 return TRUE;
880 }
881 }
882
883 return FALSE;
884 }
885
886
887 /* you really shouldn't call this function */
888 void redo_buddy_list()
889 {
890 /* so here we can safely assume that we don't have to add or delete anything, we
891 * just have to go through and reorder everything. remember, nothing is going to
892 * change connections, so we can assume that we don't have to change any user
893 * data or anything. this is just a simple reordering. so calm down. */
894 /* note: we only have to do this if we want to strongly enforce order; however,
895 * order doesn't particularly matter to the stability of the program. but, it's
896 * kind of nice to have */
897 /* the easy way to implement this is just to go through shows and destroy all the
898 * group_shows, then go through the connections and put everything back. though,
899 * there are slight complications with that; most of them deal with timeouts and
900 * people not seeing the login icon for the full 10 seconds. butt fuck them. */
901 GSList *s = shows;
902 struct group_show *gs;
903 GSList *m;
904 struct buddy_show *bs;
905 GSList *gr;
906 struct group *g;
907 struct buddy *b;
908
909 if (!blist)
910 return;
911
912 while (s) {
913 gs = (struct group_show *)s->data;
914 s = g_slist_remove(s, gs);
915 m = gs->members;
916 gtk_tree_remove_item(GTK_TREE(buddies), gs->item);
917 while (m) {
918 bs = (struct buddy_show *)m->data;
919 m = g_slist_remove(m, bs);
920 if (bs->log_timer > 0)
921 g_source_remove(bs->log_timer);
922 g_free(bs->name);
923 g_free(bs);
924 }
925 g_free(gs->name);
926 g_free(gs);
927 }
928 shows = NULL;
929 gr = groups;
930 while (gr) {
931 g = (struct group *)gr->data;
932 gr = gr->next;
933 gs = find_group_show(g->name);
934 if (!gs && !(blist_options & OPT_BLIST_NO_MT_GRP)
935 && gaim_group_on_account(g, NULL))
936 gs = new_group_show(g->name);
937 m = g->members;
938 while (m) {
939 b = (struct buddy *)m->data;
940 m = m->next;
941 if (b->present) {
942 if (!gs)
943 gs = new_group_show(g->name);
944 bs = find_buddy_show(gs, b->name);
945 if (!bs) {
946 if (b->account->gc->prpl->list_icon)
947 bs = new_buddy_show(gs, b,
948 b->account->gc->prpl->list_icon(b->
949 uc));
950 else
951 bs = new_buddy_show(gs, b, (char **)no_icon_xpm);
952 }
953 bs->connlist = g_slist_append(bs->connlist, b->account->gc);
954 update_num_group(gs);
955 }
956 }
957 }
958 update_idle_times();
959 }
960
961 static void edit_tree_move(GtkCTree *ctree, GtkCTreeNode *child,
962 GtkCTreeNode *parent, GtkCTreeNode *sibling,
963 gpointer data)
964 {
965 int *ctype, *ptype = NULL, *stype = NULL;
966
967 ctype = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(ctree), child);
968
969 if (parent)
970 ptype = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(ctree), parent);
971
972 if (sibling)
973 stype = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(ctree), sibling);
974
975 if (*ctype == EDIT_BUDDY) {
976 /* we moved a buddy. hopefully we just changed groups or positions or something.
977 * if we changed connections, we copy the buddy to the new connection. if the new
978 * connection already had the buddy in its buddy list but in a different group,
979 * we change the group that the buddy is in */
980 struct group *old_g, *new_g = (struct group *)ptype;
981 struct buddy *s = NULL, *buddy = (struct buddy *)ctype;
982 int pos;
983
984 old_g = find_group_by_buddy(buddy);
985
986 old_g->members = g_slist_remove(old_g->members, buddy);
987
988 if (sibling) {
989 s = (struct buddy *)stype;
990 pos = g_slist_index(new_g->members, s);
991 if (pos)
992 new_g->members = g_slist_insert(new_g->members, buddy, pos);
993 else
994 new_g->members = g_slist_prepend(new_g->members, buddy);
995 } else
996 new_g->members = g_slist_append(new_g->members, buddy);
997
998 serv_move_buddy(buddy, old_g, new_g);
999
1000 gaim_blist_save();
1001 } else { /* group */
1002
1003 /* move the group. if moving connections, copy the group, and each buddy in the
1004 * group. if the buddy exists in the new connection, leave it where it is. */
1005
1006 struct group *g, *g2, *group;
1007 int pos;
1008
1009 group = (struct group *)ctype;
1010
1011 g = group;
1012
1013 groups = g_slist_remove(groups, g);
1014
1015 if (sibling) {
1016 g2 = (struct group *)stype;
1017 pos = g_slist_index(groups, g2);
1018 if (pos)
1019 groups = g_slist_insert(groups, g, pos);
1020 else
1021 groups = g_slist_prepend(groups, g);
1022 } else
1023 groups = g_slist_append(groups, g);
1024
1025 gaim_blist_save();
1026 }
1027
1028 redo_buddy_list();
1029 update_num_groups();
1030 }
1031
1032 void
1033 create_prpl_icon(GtkWidget *widget, struct gaim_connection *gc,
1034 GdkPixmap **pixmap, GdkBitmap **mask)
1035 {
1036 /* This whole thing is a hack--but it looks nice.
1037 * Probably should have a prpl->icon(struct gaim_connection *) to
1038 * do this. */
1039 GtkStyle *style;
1040 char **xpm = NULL;
1041
1042 if (widget == NULL || gc == NULL || pixmap == NULL || mask == NULL)
1043 return;
1044
1045 style = gtk_widget_get_style( widget );
1046
1047 if (gc->prpl->list_icon) {
1048 if (gc->prpl->protocol == PROTO_OSCAR) {
1049 if (isdigit(*gc->username)) {
1050 xpm = gc->prpl->list_icon(0);
1051 } else {
1052 xpm = gc->prpl->list_icon(0x10);
1053 }
1054 } else {
1055 xpm = gc->prpl->list_icon (0);
1056 }
1057 }
1058 if (xpm == NULL)
1059 xpm = (char **)no_icon_xpm;
1060
1061 *pixmap = gdk_pixmap_create_from_xpm_d(widget->window, mask, &style->bg[GTK_STATE_NORMAL], xpm);
1062 }
1063
1064 void build_edit_tree()
1065 {
1066 GtkCTreeNode *p = NULL, *n;
1067 GSList *grp;
1068 GSList *mem;
1069 struct group *g;
1070 struct buddy *b;
1071 char *text[1];
1072
1073 if (!blist)
1074 return;
1075
1076 gtk_clist_freeze(GTK_CLIST(edittree));
1077 gtk_clist_clear(GTK_CLIST(edittree));
1078
1079
1080 grp = groups;
1081
1082 while (grp) {
1083
1084 g = (struct group *)grp->data;
1085
1086 text[0] = g->name;
1087
1088 p = gtk_ctree_insert_node(GTK_CTREE(edittree), NULL,
1089 NULL, text, 5, NULL, NULL, NULL, NULL, 0, 1);
1090
1091 gtk_ctree_node_set_row_data(GTK_CTREE(edittree), p, g);
1092
1093 n = NULL;
1094
1095 mem = g->members;
1096
1097 while (mem) {
1098 char buf[256];
1099 b = (struct buddy *)mem->data;
1100 if(b->account->gc) {
1101 if (get_buddy_alias_only(b)) {
1102 g_snprintf(buf, sizeof(buf), "%s (%s)", b->name, get_buddy_alias(b));
1103 text[0] = buf;
1104 } else
1105 text[0] = b->name;
1106
1107 n = gtk_ctree_insert_node(GTK_CTREE(edittree),
1108 p, NULL, text, 5,
1109 NULL, NULL, NULL, NULL, 1, 1);
1110
1111 gtk_ctree_node_set_row_data(GTK_CTREE(edittree), n, b);
1112 }
1113
1114 mem = mem->next;
1115 }
1116 grp = g_slist_next(grp);
1117 }
1118
1119 gtk_clist_thaw(GTK_CLIST(edittree));
1120
1121 }
1122
1123 void ui_add_buddy(struct gaim_connection *gc, struct group *g, struct buddy *b)
1124 {
1125 GtkCTreeNode *p = NULL, *n;
1126 char *text[1];
1127 char buf[256];
1128 struct group_show *gs = find_group_show(g->name);
1129
1130 b->edittype = EDIT_BUDDY;
1131
1132 if (gs)
1133 update_num_group(gs);
1134
1135 if (!blist)
1136 return;
1137
1138 if(!b->account->gc)
1139 return;
1140
1141 p = gtk_ctree_find_by_row_data(GTK_CTREE(edittree), NULL, g);
1142 if (get_buddy_alias_only(b)) {
1143 g_snprintf(buf, sizeof(buf), "%s (%s)", b->name, get_buddy_alias(b));
1144 text[0] = buf;
1145 } else
1146 text[0] = b->name;
1147
1148 n = gtk_ctree_insert_node(GTK_CTREE(edittree), p, NULL, text, 5, NULL, NULL, NULL, NULL, 1, 1);
1149 gtk_ctree_node_set_row_data(GTK_CTREE(edittree), n, b);
1150 }
1151
1152 void ui_add_group(struct group *g)
1153 {
1154 GtkCTreeNode *p;
1155 char *text[1];
1156
1157 g->edittype = EDIT_GROUP;
1158
1159 if (!blist)
1160 return;
1161
1162 text[0] = g->name;
1163 p = gtk_ctree_insert_node(GTK_CTREE(edittree), NULL, NULL, text, 5, NULL, NULL, NULL, NULL, 0, 1);
1164 gtk_ctree_node_set_row_data(GTK_CTREE(edittree), p, g);
1165
1166 if (!(blist_options & OPT_BLIST_NO_MT_GRP) && !find_group_show(g->name)
1167 && gaim_group_on_account(g, NULL))
1168 new_group_show(g->name);
1169 }
1170
1171
1172 static void do_del_buddy(GtkWidget *w, GtkCTree *ctree)
1173 {
1174 GtkCTreeNode *node;
1175 struct buddy *b;
1176 struct group *g;
1177 int *type;
1178 GList *i;
1179
1180 i = GTK_CLIST(edittree)->selection;
1181 if (i) {
1182 node = i->data;
1183 type = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(edittree), node);
1184
1185 if (*type == EDIT_BUDDY) {
1186 b = (struct buddy *)type;
1187 g = find_group_by_buddy(b);
1188 serv_remove_buddy(b->account->gc, b->name, g->name);
1189 remove_buddy(b);
1190 gaim_blist_save();
1191 } else if (*type == EDIT_GROUP) {
1192 remove_group((struct group *)type);
1193 gaim_blist_save();
1194 }
1195
1196 } else {
1197 /* Nothing selected. */
1198 }
1199 }
1200
1201
1202 void import_callback(GtkWidget *widget, void *null)
1203 {
1204 show_import_dialog();
1205 }
1206
1207 void add_buddy_callback(GtkWidget *widget, void *dummy)
1208 {
1209 char *grp = NULL;
1210 GtkCTreeNode *node;
1211 GList *i;
1212 struct gaim_connection *gc = NULL;
1213 int *type;
1214
1215 i = GTK_CLIST(edittree)->selection;
1216 if (i) {
1217 node = i->data;
1218 type = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(edittree), node);
1219
1220 if (*type == EDIT_BUDDY) {
1221 struct buddy *b = (struct buddy *)type;
1222 struct group *g = find_group_by_buddy(b);
1223 grp = g->name;
1224 gc = b->account->gc;
1225 } else if (*type == EDIT_GROUP) {
1226 struct group *g = (struct group *)type;
1227 grp = g->name;
1228 if(g->members)
1229 gc = ((struct buddy *)g->members->data)->account->gc;
1230 else
1231 gc = connections->data;
1232 } else {
1233 gc = (struct gaim_connection *)type;
1234 }
1235 }
1236 show_add_buddy(gc, NULL, grp, NULL);
1237
1238 }
1239
1240 void add_group_callback(GtkWidget *widget, void *dummy)
1241 {
1242 GtkCTreeNode *node;
1243 GList *i;
1244 struct gaim_connection *gc = NULL;
1245 int *type;
1246
1247 i = GTK_CLIST(edittree)->selection;
1248 if (i) {
1249 node = i->data;
1250 type = (int *)gtk_ctree_node_get_row_data(GTK_CTREE(edittree), node);
1251 if (*type == EDIT_BUDDY)
1252 gc = ((struct buddy *)type)->account->gc;
1253 else if (*type == EDIT_GROUP)
1254 gc = connections->data;
1255 else
1256 gc = (struct gaim_connection *)type;
1257 }
1258 show_add_group(gc);
1259 }
1260
1261 static void im_callback(GtkWidget *widget, GtkTree *tree)
1262 {
1263 GList *i;
1264 struct buddy_show *b = NULL;
1265 struct gaim_conversation *c;
1266 struct gaim_account *account;
1267
1268 i = GTK_TREE_SELECTION_OLD(tree);
1269 if (i) {
1270 b = gtk_object_get_user_data(GTK_OBJECT(i->data));
1271 }
1272 if (!i || !b) {
1273 show_im_dialog();
1274 return;
1275 }
1276 if (!b->name)
1277 return;
1278
1279 account = ((struct gaim_connection *)b->connlist->data)->account;
1280
1281 c = gaim_find_conversation(b->name);
1282
1283 if (c == NULL)
1284 c = gaim_conversation_new(GAIM_CONV_IM, account, b->name);
1285 else {
1286 gaim_conversation_set_account(c, account);
1287 gaim_window_raise(gaim_conversation_get_window(c));
1288 }
1289 }
1290
1291 static void info_callback(GtkWidget *widget, GtkTree *tree)
1292 {
1293 GList *i;
1294 struct buddy_show *b = NULL;
1295 i = GTK_TREE_SELECTION_OLD(tree);
1296 if (i) {
1297 b = gtk_object_get_user_data(GTK_OBJECT(i->data));
1298 }
1299 if (!i || !b) {
1300 show_info_dialog();
1301 return;
1302 }
1303 if (!b->name)
1304 return;
1305 if (b->connlist)
1306 serv_get_info(b->connlist->data, b->name);
1307 }
1308
1309
1310 void chat_callback(GtkWidget *widget, GtkTree *tree)
1311 {
1312 join_chat();
1313 }
1314
1315 static void away_callback(GtkWidget *widget, GtkTree *tree)
1316 {
1317 GSList *awy = away_messages;
1318 static GtkWidget *menu = NULL;
1319 GtkWidget *menuitem;
1320
1321 if (!awy)
1322 return;
1323
1324 /*
1325 * If a menu already exists, destroy it before creating a new one,
1326 * thus freeing-up the memory it occupied.
1327 */
1328 if(menu)
1329 gtk_widget_destroy(menu);
1330
1331 menu = gtk_menu_new();
1332
1333 while (awy) {
1334 struct away_message *a = awy->data;
1335
1336 menuitem = gtk_menu_item_new_with_label(a->name);
1337 gtk_menu_append(GTK_MENU(menu), menuitem);
1338 g_signal_connect(GTK_OBJECT(menuitem), "activate",
1339 G_CALLBACK(do_away_message), a);
1340 gtk_widget_show(menuitem);
1341 awy = awy->next;
1342 }
1343
1344 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME );
1345 }
1346
1347 void rem_bp(GtkWidget *w, struct buddy_pounce *b)
1348 {
1349 buddy_pounces = g_list_remove(buddy_pounces, b);
1350 do_bp_menu();
1351 save_prefs();
1352 }
1353
1354 void do_pounce(struct gaim_connection *gc, char *name, int when)
1355 {
1356 char *who;
1357
1358 struct buddy_pounce *b;
1359 struct gaim_conversation *c;
1360 struct gaim_account *account;
1361
1362 GList *bp = buddy_pounces;
1363
1364 who = g_strdup(normalize (name));
1365
1366 while (bp) {
1367 b = (struct buddy_pounce *)bp->data;
1368 bp = bp->next; /* increment the list here because rem_bp can make our handle bad */
1369
1370 if (!(b->options & when))
1371 continue;
1372
1373 account = gaim_account_find(b->pouncer, b->protocol); /* find our user */
1374 if (account == NULL)
1375 continue;
1376
1377 /* check and see if we're signed on as the pouncer */
1378 if (account->gc != gc)
1379 continue;
1380
1381 if (!g_strcasecmp(who, normalize (b->name))) { /* find someone to pounce */
1382 if (b->options & OPT_POUNCE_POPUP) {
1383 c = gaim_find_conversation(name);
1384
1385 if (c == NULL)
1386 c = gaim_conversation_new(GAIM_CONV_IM, account, name);
1387 else
1388 gaim_conversation_set_account(c, account);
1389 }
1390 if (b->options & OPT_POUNCE_NOTIFY) {
1391 char tmp[1024];
1392
1393 /* I know the line below is really ugly. I only did it this way
1394 * because I thought it'd be funny :-) */
1395
1396 g_snprintf(tmp, sizeof(tmp),
1397 (when & OPT_POUNCE_TYPING) ? _("%s has started typing to you") :
1398 (when & OPT_POUNCE_SIGNON) ? _("%s has signed on") :
1399 (when & OPT_POUNCE_UNIDLE) ? _("%s has returned from being idle") :
1400 _("%s has returned from being away"), name);
1401
1402 do_error_dialog(tmp, NULL, GAIM_INFO);
1403 }
1404 if (b->options & OPT_POUNCE_SEND_IM) {
1405 if (strlen(b->message) > 0) {
1406 c = gaim_find_conversation(name);
1407
1408 if (c == NULL)
1409 c = gaim_conversation_new(GAIM_CONV_IM, account, name);
1410 else
1411 gaim_conversation_set_account(c, account);
1412
1413 gaim_conversation_write(c, NULL, b->message, -1,
1414 WFLAG_SEND, time(NULL));
1415
1416 serv_send_im(account->gc, name, b->message, -1, 0);
1417 }
1418 }
1419 if (b->options & OPT_POUNCE_COMMAND) {
1420 #ifndef _WIN32
1421 int pid = fork();
1422
1423 if (pid == 0) {
1424 char *args[4];
1425 args[0] = "sh";
1426 args[1] = "-c";
1427 args[2] = b->command;
1428 args[3] = NULL;
1429 execvp(args[0], args);
1430 _exit(0);
1431 }
1432 #else
1433 STARTUPINFO si;
1434 PROCESS_INFORMATION pi;
1435
1436 ZeroMemory( &si, sizeof(si) );
1437 si.cb = sizeof(si);
1438 ZeroMemory( &pi, sizeof(pi) );
1439
1440 CreateProcess( NULL, b->command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
1441 CloseHandle( pi.hProcess );
1442 CloseHandle( pi.hThread );
1443 #endif /*_WIN32*/
1444 }
1445 if (b->options & OPT_POUNCE_SOUND) {
1446 if (strlen(b->sound))
1447 gaim_sound_play_file(b->sound);
1448 else
1449 gaim_sound_play_event(GAIM_SOUND_POUNCE_DEFAULT);
1450 }
1451
1452 if (!(b->options & OPT_POUNCE_SAVE))
1453 rem_bp(NULL, b);
1454
1455 }
1456 }
1457 g_free(who);
1458 }
1459
1460 static void new_bp_callback(GtkWidget *w, struct buddy *b)
1461 {
1462 if (b)
1463 show_new_bp(b->name, b->account->gc, b->idle, b->uc & UC_UNAVAILABLE, NULL);
1464 else
1465 show_new_bp(NULL, NULL, 0, 0, NULL);
1466 }
1467
1468 static void edit_bp_callback(GtkWidget *w, struct buddy_pounce *b)
1469 {
1470 show_new_bp(NULL, NULL, 0, 0, b);
1471 }
1472
1473 static GtkTooltips *bp_tooltip = NULL;
1474 void do_bp_menu()
1475 {
1476 GtkWidget *menuitem, *mess, *messmenu;
1477 static GtkWidget *remmenu;
1478 GtkWidget *remitem;
1479 GtkWidget *sep;
1480 GList *l;
1481 struct buddy_pounce *b;
1482 GList *bp = buddy_pounces;
1483
1484 /* Tooltip for editing bp's */
1485 if(!bp_tooltip)
1486 bp_tooltip = gtk_tooltips_new();
1487
1488 l = gtk_container_children(GTK_CONTAINER(bpmenu));
1489
1490 while (l) {
1491 gtk_widget_destroy(GTK_WIDGET(l->data));
1492 l = l->next;
1493 }
1494
1495 remmenu = gtk_menu_new();
1496
1497 menuitem = gtk_menu_item_new_with_label(_("New Buddy Pounce"));
1498 gtk_menu_append(GTK_MENU(bpmenu), menuitem);
1499 gtk_widget_show(menuitem);
1500 g_signal_connect(GTK_OBJECT(menuitem), "activate", G_CALLBACK(new_bp_callback), NULL);
1501
1502
1503 while (bp) {
1504
1505 b = (struct buddy_pounce *)bp->data;
1506 remitem = gtk_menu_item_new_with_label(b->name);
1507 gtk_menu_append(GTK_MENU(remmenu), remitem);
1508 gtk_widget_show(remitem);
1509 g_signal_connect(GTK_OBJECT(remitem), "activate", G_CALLBACK(rem_bp), b);
1510
1511 bp = bp->next;
1512
1513 }
1514
1515 menuitem = gtk_menu_item_new_with_label(_("Remove Buddy Pounce"));
1516 gtk_menu_append(GTK_MENU(bpmenu), menuitem);
1517 gtk_widget_show(menuitem);
1518 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), remmenu);
1519 gtk_widget_show(remmenu);
1520
1521 sep = gtk_hseparator_new();
1522 menuitem = gtk_menu_item_new();
1523 gtk_menu_append(GTK_MENU(bpmenu), menuitem);
1524 gtk_container_add(GTK_CONTAINER(menuitem), sep);
1525 gtk_widget_set_sensitive(menuitem, FALSE);
1526 gtk_widget_show(menuitem);
1527 gtk_widget_show(sep);
1528
1529 bp = buddy_pounces;
1530
1531 while (bp) {
1532
1533 b = (struct buddy_pounce *)bp->data;
1534
1535 menuitem = gtk_menu_item_new_with_label(b->name);
1536 gtk_menu_append(GTK_MENU(bpmenu), menuitem);
1537 messmenu = gtk_menu_new();
1538 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), messmenu);
1539 gtk_widget_show(menuitem);
1540
1541 if (strlen(b->message))
1542 mess = gtk_menu_item_new_with_label(b->message);
1543 else
1544 mess = gtk_menu_item_new_with_label(_("[no message]"));
1545 gtk_menu_append(GTK_MENU(messmenu), mess);
1546 gtk_tooltips_set_tip(bp_tooltip, GTK_WIDGET(mess), _("[Click to edit]"), NULL);
1547 gtk_widget_show(mess);
1548 g_signal_connect(GTK_OBJECT(mess), "activate", G_CALLBACK(edit_bp_callback), b);
1549 bp = bp->next;
1550
1551 }
1552
1553 }
1554
1555
1556 static struct group_show *find_group_show(char *group)
1557 {
1558 GSList *m = shows;
1559 struct group_show *g = NULL;
1560 char *who = g_strdup(normalize (group));
1561
1562 while (m) {
1563 g = (struct group_show *)m->data;
1564 if (!g_strcasecmp(normalize (g->name), who))
1565 break;
1566 g = NULL;
1567 m = m->next;
1568 }
1569 g_free(who);
1570
1571 return g;
1572 }
1573
1574 static struct buddy_show *find_buddy_show(struct group_show *gs, char *name)
1575 {
1576 GSList *m = gs->members;
1577 struct buddy_show *b = NULL;
1578 char *who = g_strdup(normalize (name));
1579
1580 while (m) {
1581 b = (struct buddy_show *)m->data;
1582 if (!g_strcasecmp(normalize (b->name), who))
1583 break;
1584 b = NULL;
1585 m = m->next;
1586 }
1587 g_free(who);
1588
1589 return b;
1590 }
1591
1592 static int group_number(char *group)
1593 {
1594 GSList *m;
1595 struct group *p;
1596 int pos = 0;
1597
1598 m = groups;
1599 while (m) {
1600 p = (struct group *)m->data;
1601 if (!strcmp(p->name, group))
1602 return pos;
1603 if (find_group_show(p->name))
1604 pos++;
1605 m = m->next;
1606 }
1607 /* um..... we'll never get here */
1608 return -1;
1609 }
1610
1611 static int buddy_number(char *group, char *buddy)
1612 {
1613 struct group *p;
1614 GSList *z;
1615 struct buddy *b;
1616 int pos = 0;
1617 char *tmp1 = g_strdup(normalize (buddy));
1618 struct group_show *gs = find_group_show(group);
1619 struct buddy_show *bs;
1620 GSList *seen = NULL;
1621
1622 p = find_group(group);
1623 if(p) {
1624 z = p->members;
1625 while (z) {
1626 b = (struct buddy *)z->data;
1627 if (!strcmp(tmp1, normalize (b->name))) {
1628 g_free(tmp1);
1629 g_slist_free(seen);
1630 return pos;
1631 }
1632 if ((bs = find_buddy_show(gs, b->name))) {
1633 if(!g_slist_find(seen, bs)) {
1634 seen = g_slist_append(seen, bs);
1635 pos++;
1636 }
1637 }
1638 z = z->next;
1639 }
1640 }
1641 /* we shouldn't ever get here */
1642 debug_printf("got to bad place in buddy_number\n");
1643 g_free(tmp1);
1644 g_slist_free(seen);
1645 return -1;
1646 }
1647
1648
1649
1650 static struct group_show *new_group_show(char *group)
1651 {
1652 struct group_show *g = g_new0(struct group_show, 1);
1653 int pos = group_number(group);
1654 GdkPixmap *pm;
1655 GdkBitmap *bm;
1656 GtkStyle *style;
1657 GtkStyle *style2;
1658
1659 g->name = g_strdup(group);
1660
1661 g->item = gtk_tree_item_new();
1662
1663 g_signal_connect(GTK_OBJECT(g->item), "button_press_event",
1664 G_CALLBACK(handle_click_group), g);
1665
1666 gtk_tree_insert(GTK_TREE(buddies), g->item, pos);
1667
1668 gtk_widget_show(g->item);
1669
1670 g->label = gtk_label_new(group);
1671 gtk_misc_set_alignment(GTK_MISC(g->label), 0.0, 0.5);
1672 gtk_widget_show(g->label);
1673
1674 gtk_container_add(GTK_CONTAINER(g->item), g->label);
1675
1676 shows = g_slist_insert(shows, g, pos);
1677
1678 /* Rob does drugs - this is still evil, damn you becausse I SAID SO! */
1679
1680 pm = gdk_pixmap_create_from_xpm_d(g->item->window,
1681 &bm, NULL, arrow_down_xpm);
1682
1683 gtk_pixmap_set(GTK_PIXMAP(GTK_TREE_ITEM(g->item)->minus_pix_widget),
1684 pm, bm);
1685
1686 gdk_pixmap_unref(pm);
1687 gdk_bitmap_unref(bm);
1688
1689 pm = gdk_pixmap_create_from_xpm_d(buddies->window,
1690 &bm, NULL, arrow_right_xpm);
1691
1692 gtk_pixmap_set(GTK_PIXMAP(GTK_TREE_ITEM(g->item)->plus_pix_widget),
1693 pm, bm);
1694
1695 gdk_pixmap_unref(pm);
1696 gdk_bitmap_unref(bm);
1697
1698 // style = gtk_widget_get_style(GTK_TREE_ITEM(g->item)->pixmaps_box);
1699 style2 = gtk_style_copy(gtk_widget_get_style(g->item));
1700 style = gtk_style_copy(gtk_widget_get_style(GTK_WIDGET(g->label)));
1701
1702 style->bg[0] = style2->base[0];
1703 gtk_widget_set_style(GTK_TREE_ITEM(g->item)->pixmaps_box, style);
1704
1705 gtk_style_unref(style);
1706 gtk_style_unref(style2);
1707
1708 /* bad drugs */
1709
1710 update_num_group(g);
1711
1712 return g;
1713 }
1714
1715 static struct buddy_show *new_buddy_show(struct group_show *gs, struct buddy *buddy, char **xpm)
1716 {
1717 struct buddy_show *b = g_new0(struct buddy_show, 1);
1718 GtkWidget *box;
1719 GdkPixmap *pm;
1720 GdkBitmap *bm;
1721 int pos = buddy_number(gs->name, buddy->name);
1722 b->sound = 0;
1723
1724 if (gs->members == NULL) {
1725 gs->tree = gtk_tree_new();
1726 gtk_tree_item_set_subtree(GTK_TREE_ITEM(gs->item), gs->tree);
1727 gtk_tree_item_expand(GTK_TREE_ITEM(gs->item));
1728 gtk_widget_show(gs->tree);
1729 }
1730
1731 b->name = g_strdup(buddy->name);
1732
1733 b->item = gtk_tree_item_new();
1734 gtk_tree_insert(GTK_TREE(gs->tree), b->item, pos);
1735 gtk_object_set_user_data(GTK_OBJECT(b->item), b);
1736 g_signal_connect(GTK_OBJECT(b->item), "button_press_event",
1737 G_CALLBACK(handle_click_buddy), b);
1738 gtk_widget_show(b->item);
1739
1740 box = gtk_hbox_new(FALSE, 1);
1741 gtk_container_add(GTK_CONTAINER(b->item), box);
1742 gtk_widget_show(box);
1743
1744 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm, NULL, xpm ? xpm : no_icon_xpm);
1745 b->pix = gtk_pixmap_new(pm, bm);
1746 gtk_box_pack_start(GTK_BOX(box), b->pix, FALSE, FALSE, 1);
1747 gtk_widget_show(b->pix);
1748 if (!(blist_options & OPT_BLIST_SHOW_PIXMAPS))
1749 gtk_widget_hide(b->pix);
1750 gdk_pixmap_unref(pm);
1751 gdk_bitmap_unref(bm);
1752
1753 b->label = gtk_label_new(get_buddy_alias(buddy));
1754 gtk_misc_set_alignment(GTK_MISC(b->label), 0.0, 0.5);
1755 gtk_box_pack_start(GTK_BOX(box), b->label, FALSE, FALSE, 1);
1756 gtk_widget_show(b->label);
1757
1758 b->warn = gtk_label_new("");
1759 gtk_box_pack_start(GTK_BOX(box), b->warn, FALSE, FALSE, 1);
1760 gtk_widget_show(b->warn);
1761
1762 b->idle = gtk_label_new("");
1763 gtk_box_pack_end(GTK_BOX(box), b->idle, FALSE, FALSE, 1);
1764 gtk_widget_show(b->idle);
1765
1766 gs->members = g_slist_insert(gs->members, b, pos);
1767 update_num_group(gs);
1768 return b;
1769 }
1770
1771 static void remove_buddy_show(struct group_show *gs, struct buddy_show *bs)
1772 {
1773 /* the name of this function may be misleading, but don't let it fool you. the point
1774 * of this is to remove bs->item from gs->tree, and make sure gs->tree still exists
1775 * and is a valid tree afterwards. Otherwise, Bad Things will happen. */
1776 gtk_tree_remove_item(GTK_TREE(gs->tree), bs->item);
1777 bs->item = NULL;
1778 }
1779
1780 static struct group_show *find_gs_by_bs(struct buddy_show *b)
1781 {
1782 GSList *m, *n;
1783 struct group_show *g = NULL;
1784 struct buddy_show *h;
1785
1786 m = shows;
1787 while (m) {
1788 g = (struct group_show *)m->data;
1789 n = g->members;
1790 while (n) {
1791 h = (struct buddy_show *)n->data;
1792 if (h == b)
1793 return g;
1794 n = n->next;
1795 }
1796 g = NULL;
1797 m = m->next;
1798 }
1799
1800 return g;
1801 }
1802
1803 /* used by this file, and by iconaway.so */
1804 void hide_buddy_list() {
1805 if (blist) {
1806 if (!connections || docklet_count) {
1807 #ifdef _WIN32
1808 /* minimize to systray with effects */
1809 wgaim_systray_minimize(blist);
1810 #endif
1811 gtk_widget_hide(blist);
1812 } else {
1813 gtk_window_iconify(GTK_WINDOW(blist));
1814 }
1815 }
1816 }
1817
1818 /* mostly used by code in this file */
1819 void unhide_buddy_list() {
1820 if (blist) {
1821 if (!GTK_WIDGET_VISIBLE(blist) && blist_options & OPT_BLIST_SAVED_WINDOWS &&
1822 blist_pos.width != 0) {
1823 /* don't move it off screen */
1824 if (blist_pos.x >= gdk_screen_width()) {
1825 blist_pos.x = gdk_screen_width() - 100;
1826 } else if (blist_pos.x < 0) {
1827 blist_pos.x = 100;
1828 }
1829
1830 if (blist_pos.y >= gdk_screen_height()) {
1831 blist_pos.y = gdk_screen_height() - 100;
1832 } else if (blist_pos.y < 0) {
1833 blist_pos.y = 100;
1834 }
1835
1836 gtk_window_move(GTK_WINDOW(blist), blist_pos.x, blist_pos.y);
1837 gtk_window_resize(GTK_WINDOW(blist), blist_pos.width, blist_pos.height);
1838 }
1839
1840 gtk_window_present(GTK_WINDOW(blist));
1841 }
1842 }
1843
1844 /* for the delete_event handler */
1845 static void close_buddy_list() {
1846 if (docklet_count) {
1847 hide_buddy_list();
1848 } else {
1849 do_quit();
1850 }
1851 }
1852
1853 void docklet_add() {
1854 docklet_count++;
1855 debug_printf("docklet_count: %d\n",docklet_count);
1856 }
1857
1858 void docklet_remove() {
1859 docklet_count--;
1860 debug_printf("docklet_count: %d\n",docklet_count);
1861 if (!docklet_count) {
1862 if (connections) {
1863 unhide_buddy_list();
1864 } else {
1865 gtk_window_present(GTK_WINDOW(mainwindow));
1866 }
1867 }
1868 }
1869
1870 void docklet_toggle() {
1871 /* Useful for the docklet plugin and also for the win32 tray icon*/
1872 /* This is called when one of those is clicked--it will show/hide the
1873 buddy list/login window--depending on which is active */
1874 if (connections && blist) {
1875 if (GTK_WIDGET_VISIBLE(blist)) {
1876 if (GAIM_WINDOW_ICONIFIED(blist) || obscured) {
1877 unhide_buddy_list();
1878 } else {
1879 hide_buddy_list();
1880 }
1881 } else {
1882 #if _WIN32
1883 wgaim_systray_maximize(blist);
1884 #endif
1885 unhide_buddy_list();
1886 }
1887 } else if (connections) {
1888 /* we're logging in or something... do nothing */
1889 debug_printf("docklet_toggle called with connections but no blist!\n");
1890 } else {
1891 if (GTK_WIDGET_VISIBLE(mainwindow)) {
1892 if (GAIM_WINDOW_ICONIFIED(mainwindow)) {
1893 gtk_window_present(GTK_WINDOW(mainwindow));
1894 } else {
1895 #if _WIN32
1896 wgaim_systray_minimize(mainwindow);
1897 #endif
1898 gtk_widget_hide(mainwindow);
1899 }
1900 } else {
1901 #if _WIN32
1902 wgaim_systray_maximize(mainwindow);
1903 #endif
1904 gtk_window_present(GTK_WINDOW(mainwindow));
1905 }
1906 }
1907 }
1908
1909 static gboolean log_timeout(gpointer data)
1910 {
1911 struct buddy_show *b = data;
1912 /* this part is really just a bad hack because of a bug I can't find */
1913 GSList *s = shows;
1914 while (s) {
1915 struct group_show *gs = s->data;
1916 GSList *m = gs->members;
1917 while (m) {
1918 if (b == m->data)
1919 break;
1920 m = m->next;
1921 }
1922 if (m != NULL)
1923 break;
1924 s = s->next;
1925 }
1926 if (!s)
1927 return FALSE;
1928
1929 /* this is the real part. */
1930 if (!b->connlist) {
1931 struct group_show *g = find_gs_by_bs(b);
1932 g->members = g_slist_remove(g->members, b);
1933 if (blist)
1934 remove_buddy_show(g, b);
1935 else
1936 debug_printf("log_timeout but buddy list not available\n");
1937 if ((g->members == NULL) && (blist_options & OPT_BLIST_NO_MT_GRP)) {
1938 shows = g_slist_remove(shows, g);
1939 if (blist)
1940 gtk_tree_remove_item(GTK_TREE(buddies), g->item);
1941 g_free(g->name);
1942 g_free(g);
1943 }
1944 g_source_remove(b->log_timer);
1945 b->log_timer = 0;
1946 g_free(b->name);
1947 g_free(b);
1948 } else {
1949 /* um.... what do we have to do here? just update the pixmap? */
1950 GdkPixmap *pm;
1951 GdkBitmap *bm;
1952 gchar **xpm = NULL;
1953 struct gaim_connection *gc = b->connlist->data;
1954 struct buddy *light = find_buddy(gc->account, b->name);
1955 if (gc->prpl->list_icon)
1956 xpm = gc->prpl->list_icon(light->uc);
1957 if (xpm == NULL)
1958 xpm = (char **)no_icon_xpm;
1959 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm, NULL, xpm);
1960 gtk_widget_hide(b->pix);
1961 gtk_pixmap_set(GTK_PIXMAP(b->pix), pm, bm);
1962 gtk_widget_show(b->pix);
1963 if (!(blist_options & OPT_BLIST_SHOW_PIXMAPS))
1964 gtk_widget_hide(b->pix);
1965 gdk_pixmap_unref(pm);
1966 gdk_bitmap_unref(bm);
1967 g_source_remove(b->log_timer);
1968 b->log_timer = 0;
1969 b->sound = 0;
1970 }
1971 return FALSE;
1972 }
1973
1974 static char *caps_string(guint caps)
1975 {
1976 static char buf[256], *tmp;
1977 int count = 0, i = 0;
1978 guint bit = 1;
1979 while (bit <= 0x10000) {
1980 if (bit & caps) {
1981 switch (bit) {
1982 case 0x1:
1983 tmp = _("Buddy Icon");
1984 break;
1985 case 0x2:
1986 tmp = _("Voice");
1987 break;
1988 case 0x4:
1989 tmp = _("IM Image");
1990 break;
1991 case 0x8:
1992 tmp = _("Chat");
1993 break;
1994 case 0x10:
1995 tmp = _("Get File");
1996 break;
1997 case 0x20:
1998 tmp = _("Send File");
1999 break;
2000 case 0x40:
2001 case 0x200:
2002 tmp = _("Games");
2003 break;
2004 case 0x80:
2005 tmp = _("Stocks");
2006 break;
2007 case 0x100:
2008 tmp = _("Send Buddy List");
2009 break;
2010 case 0x400:
2011 tmp = _("EveryBuddy Bug");
2012 break;
2013 case 0x800:
2014 tmp = _("AP User");
2015 break;
2016 case 0x1000:
2017 tmp = _("ICQ RTF");
2018 break;
2019 case 0x2000:
2020 tmp = _("Nihilist");
2021 break;
2022 case 0x4000:
2023 tmp = _("ICQ Server Relay");
2024 break;
2025 case 0x8000:
2026 tmp = _("ICQ Unknown");
2027 break;
2028 case 0x10000:
2029 tmp = _("Trillian Encryption");
2030 break;
2031 default:
2032 tmp = NULL;
2033 break;
2034 }
2035 if (tmp)
2036 i += g_snprintf(buf + i, sizeof(buf) - i, "%s%s", (count ? ", " : ""),
2037 tmp);
2038 count++;
2039 }
2040 bit <<= 1;
2041 }
2042 return buf;
2043 }
2044
2045 /* for this we're just going to assume the first connection that registered the buddy.
2046 * if it's not the one you were hoping for then you're shit out of luck */
2047 static void update_idle_time(struct buddy_show *bs)
2048 {
2049 /* this also updates the tooltip since that has idle time in it */
2050 char idlet[16], warnl[16];
2051 time_t t;
2052 int ihrs, imin;
2053 struct buddy *b;
2054 GtkStyle *style;
2055
2056 char infotip[2048];
2057 char warn[256];
2058 char caps[256];
2059 char alias[512];
2060 char serv_alias[512];
2061 char *sotime = NULL, *itime;
2062
2063 struct gaim_connection *gc;
2064
2065 int i;
2066 412
2067 time(&t); 413 time(&t);
2068 if (!bs->connlist)
2069 return;
2070 gc = bs->connlist->data;
2071 b = find_buddy(gc->account, bs->name);
2072 if (!b)
2073 return;
2074 ihrs = (t - b->idle) / 3600; 414 ihrs = (t - b->idle) / 3600;
2075 imin = ((t - b->idle) / 60) % 60; 415 imin = ((t - b->idle) / 60) % 60;
2076 416
2077 if (ihrs) 417 if (b->idle) {
2078 g_snprintf(idlet, sizeof idlet, "(%d:%02d)", ihrs, imin); 418 if (ihrs)
419 idletime = g_strdup_printf(_("Idle (%dh%02dm)"), ihrs, imin);
420 else
421 idletime = g_strdup_printf(_("Idle (%dm)"), imin);
422 }
423
424 if (b->evil > 0)
425 warning = g_strdup_printf(_("Warned (%d%%)"), b->evil);
426
427 if (b->idle)
428 text = g_strdup_printf("<span color='grey'>%s</span>\n<span color='gray' size='smaller'>%s %s</span>",
429 esc,
430 idletime, warning);
2079 else 431 else
2080 g_snprintf(idlet, sizeof idlet, "(%d)", imin); 432 text = g_strdup_printf("%s\n<span color='gray' size='smaller'>%s</span>", esc, warning);
2081 433
2082 gtk_widget_hide(bs->idle); 434 if (idletime[0])
2083 if (b->idle) 435 g_free(idletime);
2084 gtk_label_set(GTK_LABEL(bs->idle), idlet); 436 if (warning[0])
437 g_free(warning);
438
439 return text;
440 }
441
442 /**********************************************************************************
443 * Public API Functions *
444 **********************************************************************************/
445 static void gaim_gtk_blist_show(struct gaim_buddy_list *list)
446 {
447 GtkItemFactory *ift;
448 GtkCellRenderer *rend;
449 GtkTreeViewColumn *column;
450 GtkWidget *sw;
451 GtkWidget *button;
452 GValue *val;
453 GtkTreeIter iter;
454
455 if (gtkblist) {
456 gtk_widget_show(gtkblist->window);
457 return;
458 }
459
460 gtkblist = g_new0(struct gaim_gtk_buddy_list , 1);
461 gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
462 gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));
463
464 gtkblist->vbox = gtk_vbox_new(FALSE, 6);
465 gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox);
466
467 /******************************* Menu bar *************************************/
468 ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<GaimMain>", NULL);
469 gtk_item_factory_create_items(ift, sizeof(blist_menu) / sizeof(*blist_menu),
470 blist_menu, NULL);
471 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(ift, "<GaimMain>"), FALSE, FALSE, 0);
472
473 /****************************** GtkTreeView **********************************/
474 sw = gtk_scrolled_window_new(NULL,NULL);
475 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
476 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
477 gtk_widget_set_usize(sw, 200, 200);
478
479 gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
480 G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
481 /* This is broken because GTK is broken
482 g_signal_connect(G_OBJECT(gtkblist->treemodel), "row-reordered", gaim_gtk_blist_reordered_cb, NULL); */
483
484 gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
485
486 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
487
488 rend = gtk_cell_renderer_pixbuf_new();
489 column = gtk_tree_view_column_new_with_attributes("Status", rend, "pixbuf", STATUS_ICON_COLUMN, NULL);
490 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
491
492 rend = gtk_cell_renderer_text_new();
493 column = gtk_tree_view_column_new_with_attributes("Name", rend, "markup", NAME_COLUMN, NULL);
494 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
495
496 rend = gtk_cell_renderer_text_new();
497 column = gtk_tree_view_column_new_with_attributes("Warning", rend, "text", WARNING_COLUMN, NULL);
498 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
499
500 rend = gtk_cell_renderer_text_new();
501 column = gtk_tree_view_column_new_with_attributes("Idle", rend, "text", IDLE_COLUMN, NULL);
502 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
503
504 rend = gtk_cell_renderer_pixbuf_new();
505 column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL);
506 g_object_set(rend, "xalign", 1.0, NULL);
507 gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
508
509 g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
510 g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL);
511
512 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0);
513 gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
514
515 /**************************** Button Box **************************************/
516 gtkblist->bbox = gtk_hbox_new(TRUE, 0);
517 gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0);
518 button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL);
519 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
520 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
521 button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL);
522 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
523 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
524 button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL);
525 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
526 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
527 button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_AWAY, GAIM_BUTTON_VERTICAL);
528 gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
529 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
530
531 /* OK... let's show this bad boy. */
532 gaim_gtk_blist_refresh(list);
533 gtk_widget_show_all(gtkblist->window);
534 }
535
536 void gaim_gtk_blist_refresh(struct gaim_buddy_list *list)
537 {
538 GaimBlistNode *group = list->root;
539 GaimBlistNode *buddy;
540
541 while (group) {
542 gaim_gtk_blist_update(list, group);
543 buddy = group->child;
544 while (buddy) {
545 gaim_gtk_blist_update(list, buddy);
546 buddy = buddy->next;
547 }
548 group = group->next;
549 }
550 }
551
552 static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node)
553 {
554 GtkTreeIter *iter = node->ui_data;
555 GtkTreePath *path;
556 GdkPixbuf *buf = NULL;
557 gboolean expand = FALSE;
558
559 if (!gtkblist)
560 return;
561
562 if (!iter) { /* This is a newly added node */
563 if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
564 if (((struct buddy*)node)->present) {
565 if(node->parent && node->parent && !node->parent->ui_data) {
566
567 /* This buddy's group has not yet been added. We do that here */
568
569 char *mark = g_strdup_printf("<span weight='bold'>%s</span>", ((struct group*)node->parent)->name);
570 GtkTreeIter *iter2 = g_new0(GtkTreeIter, 1);
571 GaimBlistNode *insertat = node->parent->prev;
572 GtkTreeIter *insertatiter = NULL;
573
574 /* We traverse backwards through the buddy list to find the node in the tree to insert it after */
575 while (insertat && !insertat->ui_data)
576 insertat = insertat->prev;
577 if (insertat)
578 insertatiter = insertat->ui_data;
579
580 /* This is where we create the node and add it. */
581 gtk_tree_store_insert_after(gtkblist->treemodel, iter2,
582 node->parent->parent ? node->parent->parent->ui_data : NULL, insertatiter);
583 gtk_tree_store_set(gtkblist->treemodel, iter2,
584 STATUS_ICON_COLUMN, gtk_widget_render_icon
585 (gtkblist->treeview,GTK_STOCK_OPEN,GTK_ICON_SIZE_LARGE_TOOLBAR,NULL),
586 NAME_COLUMN, mark,
587 NODE_COLUMN, node->parent,
588 -1);
589 node->parent->ui_data = iter2;
590 expand = TRUE;
591 }
592 iter = g_new0(GtkTreeIter, 1);
593 node->ui_data = iter;
594
595 gtk_tree_store_insert_after (gtkblist->treemodel, iter, node->parent ? node->parent->ui_data : NULL,
596 node->prev ? node->prev->ui_data : NULL);
597
598
599 if (expand) { /* expand was set to true if this is the first element added to a group. In such case
600 * we expand the group node */
601 GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), node->parent->ui_data);
602 gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), path, TRUE);
603 }
604 node->ui_data = iter;
605 }
606 }
607 }
608
609 if (GAIM_BLIST_NODE_IS_BUDDY(node) && ((struct buddy*)node)->present) {
610 GdkPixbuf *status, *avatar;
611 char *mark;
612
613 status = gaim_gtk_blist_get_status_icon((struct buddy*)node);
614 avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node);
615 mark = gaim_gtk_blist_get_name_markup((struct buddy*)node);
616
617 gtk_tree_store_set(gtkblist->treemodel, iter,
618 STATUS_ICON_COLUMN, status,
619 NAME_COLUMN, mark,
620 WARNING_COLUMN, "",
621 IDLE_COLUMN, "",
622 BUDDY_ICON_COLUMN, avatar,
623 NODE_COLUMN, node,
624 -1);
625
626 g_free(mark);
627 g_object_unref(status);
628 if (avatar) {
629 g_object_unref(avatar);
630 }
631 } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && node->ui_data){
632 gtk_tree_store_remove(GTK_TREE_STORE(gtkblist->treemodel), (GtkTreeIter*)(node->ui_data));
633 g_free(node->ui_data);
634 node->ui_data = NULL;
635 }
636
637 }
638
639 static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node)
640 {
641 if (!node->ui_data)
642 return;
643 gtk_tree_store_remove(gtkblist->treemodel, (GtkTreeIter*)(node->ui_data));
644 }
645
646 static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list)
647 {
648 gtk_widget_destroy(gtkblist->window);
649 }
650
651 static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show)
652 {
653 if (show)
654 gtk_widget_show(gtkblist->window);
2085 else 655 else
2086 gtk_label_set(GTK_LABEL(bs->idle), ""); 656 gtk_widget_hide(gtkblist->window);
2087 if (blist_options & OPT_BLIST_SHOW_IDLETIME) 657 }
2088 gtk_widget_show(bs->idle); 658
2089 659 static struct gaim_blist_ui_ops blist_ui_ops =
2090 style = gtk_style_new(); 660 {
2091 gtk_style_set_font(style, gdk_font_ref(gtk_style_get_font(bs->label->style))); 661 gaim_gtk_blist_show,
2092 for (i = 0; i < 5; i++) 662 gaim_gtk_blist_update,
2093 style->fg[i] = bs->idle->style->fg[i]; 663 gaim_gtk_blist_remove,
2094 if ((blist_options & OPT_BLIST_GREY_IDLERS) && (b->idle)) { 664 gaim_gtk_blist_destroy,
2095 style->fg[GTK_STATE_NORMAL].red = 665 gaim_gtk_blist_set_visible
2096 (style->fg[GTK_STATE_NORMAL].red / 2) + (style->base[GTK_STATE_NORMAL].red / 2); 666 };
2097 style->fg[GTK_STATE_NORMAL].green = 667
2098 (style->fg[GTK_STATE_NORMAL].green / 2) + (style->base[GTK_STATE_NORMAL].green / 2); 668
2099 style->fg[GTK_STATE_NORMAL].blue = 669 struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops()
2100 (style->fg[GTK_STATE_NORMAL].blue / 2) + (style->base[GTK_STATE_NORMAL].blue / 2); 670 {
2101 } 671 return &blist_ui_ops;
2102 gtk_widget_set_style(bs->label, style); 672 }
2103 gtk_style_unref(style); 673
2104 674
2105 /* now we do the tooltip */ 675
2106 if (b->signon) { 676 /*********************************************************************
2107 char *stime = sec_to_text(t - b->signon + 677 * Public utility functions *
2108 ((struct gaim_connection *)bs->connlist->data)-> 678 *********************************************************************/
2109 correction_time); 679
2110 sotime = g_strdup_printf(_("Logged in: %s\n"), stime); 680 GdkPixbuf *
2111 g_free(stime); 681 create_prpl_icon(struct gaim_account *account)
2112 } 682 {
2113 683 struct prpl *prpl = find_prpl(account->protocol);
2114 if (b->idle) 684 GdkPixbuf *status = NULL;
2115 itime = sec_to_text(t - b->idle); 685 char *filename = NULL;
2116 else { 686 const char *protoname = prpl->list_icon(account, NULL);
2117 itime = g_malloc(1); 687 /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and
2118 itime[0] = 0; 688 then it will look up protoname from the theme */
2119 } 689 if (!strcmp(protoname, "aim")) {
2120 690 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "aim.png", NULL);
2121 if (b->evil) { 691 status = gdk_pixbuf_new_from_file(filename,NULL);
2122 g_snprintf(warn, sizeof warn, _("Warnings: %d%%\n"), b->evil); 692 g_free(filename);
2123 g_snprintf(warnl, sizeof warnl, "(%d%%)", b->evil); 693 } else if (!strcmp(protoname, "yahoo")) {
2124 } else { 694 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "yahoo.png", NULL);
2125 warn[0] = '\0'; 695 status = gdk_pixbuf_new_from_file(filename,NULL);
2126 warnl[0] = '\0'; 696 g_free(filename);
2127 } 697 } else if (!strcmp(protoname, "msn")) {
2128 gtk_widget_hide(bs->warn); 698 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "msn.png", NULL);
2129 gtk_label_set(GTK_LABEL(bs->warn), warnl); 699 status = gdk_pixbuf_new_from_file(filename,NULL);
2130 if (blist_options & OPT_BLIST_SHOW_WARN) 700 g_free(filename);
2131 gtk_widget_show(bs->warn); 701 } else if (!strcmp(protoname, "jabber")) {
2132 702 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "jabber.png", NULL);
2133 if (b->caps) 703 status = gdk_pixbuf_new_from_file(filename,NULL);
2134 g_snprintf(caps, sizeof caps, _("Capabilities: %s\n"), caps_string(b->caps)); 704 g_free(filename);
2135 else 705 } else if (!strcmp(protoname, "icq")) {
2136 caps[0] = '\0'; 706 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "icq.png", NULL);
2137 707 status = gdk_pixbuf_new_from_file(filename,NULL);
2138 if (b->alias[0]) 708 g_free(filename);
2139 g_snprintf(alias, sizeof alias, _("Alias: %s\n"), b->alias); 709 } else if (!strcmp(protoname, "gadu-gadu")) {
2140 else 710 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "gadugadu.png", NULL);
2141 alias[0] = '\0'; 711 status = gdk_pixbuf_new_from_file(filename,NULL);
2142 712 g_free(filename);
2143 if (b->server_alias[0]) 713 } else if (!strcmp(protoname, "napster")) {
2144 g_snprintf(serv_alias, sizeof serv_alias, _("Nickname: %s\n"), 714 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "napster.png", NULL);
2145 b->server_alias); 715 status = gdk_pixbuf_new_from_file(filename,NULL);
2146 else 716 g_free(filename);
2147 serv_alias[0] = '\0'; 717 } else if (!strcmp(protoname, "irc")) {
2148 718 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "irc.png", NULL);
2149 g_snprintf(infotip, sizeof infotip, _("%s%sScreen Name: %s\n%s%s%s%s%s%s"), 719 status = gdk_pixbuf_new_from_file(filename,NULL);
2150 alias, serv_alias, b->name, (b->signon ? sotime : ""), warn, 720 g_free(filename);
2151 (b->idle ? _("Idle: ") : ""), itime, (b->idle ? "\n" : ""), caps); 721 }
2152 722 return status;
2153 gtk_tooltips_set_tip(tips, GTK_WIDGET(bs->item), infotip, ""); 723 }
2154
2155 if (b->signon)
2156 g_free(sotime);
2157 g_free(itime);
2158 }
2159
2160 void update_idle_times()
2161 {
2162 GSList *grp = shows;
2163 GSList *mem;
2164 struct buddy_show *b;
2165 struct group_show *g;
2166
2167 while (grp) {
2168 g = (struct group_show *)grp->data;
2169 mem = g->members;
2170 while (mem) {
2171 b = (struct buddy_show *)mem->data;
2172 update_idle_time(b);
2173 mem = mem->next;
2174 }
2175 grp = grp->next;
2176 }
2177 }
2178
2179 void set_buddy(struct gaim_connection *gc, struct buddy *b)
2180 {
2181 struct group *g = find_group_by_buddy(b);
2182 struct group_show *gs;
2183 struct buddy_show *bs;
2184 GdkPixmap *pm;
2185 GdkBitmap *bm;
2186 char **xpm = NULL;
2187
2188 if (!blist)
2189 return;
2190
2191 if (b->present) {
2192 if ((gs = find_group_show(g->name)) == NULL)
2193 gs = new_group_show(g->name);
2194 if ((bs = find_buddy_show(gs, b->name)) == NULL)
2195 bs = new_buddy_show(gs, b, (char **)login_icon_xpm);
2196 if (!g_slist_find(bs->connlist, gc)) {
2197 bs->connlist = g_slist_append(bs->connlist, gc);
2198 update_num_group(gs);
2199 }
2200 if (b->present == 1) {
2201 if (bs->sound != 2)
2202 gaim_sound_play_event(GAIM_SOUND_BUDDY_ARRIVE);
2203 if (blist_options & OPT_BLIST_POPUP)
2204 gdk_window_show(blist->window);
2205 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm,
2206 NULL, (char **)login_icon_xpm);
2207 gtk_widget_hide(bs->pix);
2208 gtk_pixmap_set(GTK_PIXMAP(bs->pix), pm, bm);
2209 gtk_widget_show(bs->pix);
2210 gdk_pixmap_unref(pm);
2211 gdk_bitmap_unref(bm);
2212 b->present = 2;
2213 if (bs->log_timer > 0)
2214 g_source_remove(bs->log_timer);
2215 bs->log_timer = g_timeout_add(10000, log_timeout, bs);
2216 if ((bs->sound != 2) && (im_options & OPT_IM_LOGON)) {
2217 struct gaim_conversation *c = gaim_find_conversation(b->name);
2218 if (c) {
2219 char tmp[1024];
2220 g_snprintf(tmp, sizeof(tmp), _("%s logged in."),
2221 get_buddy_alias(b));
2222 gaim_conversation_write(c, NULL, tmp, -1,
2223 WFLAG_SYSTEM, time(NULL));
2224 } else if (awayqueue && find_queue_total_by_name(b->name)) {
2225 struct queued_message *qm = g_new0(struct queued_message, 1);
2226 g_snprintf(qm->name, sizeof(qm->name), "%s", b->name);
2227 qm->message = g_strdup_printf(_("%s logged in."),
2228 get_buddy_alias(b));
2229 qm->account = gc->account;
2230 qm->tm = time(NULL);
2231 qm->flags = WFLAG_SYSTEM;
2232 qm->len = -1;
2233 message_queue = g_slist_append(message_queue, qm);
2234 }
2235 }
2236 bs->sound = 2;
2237 } else if (bs->log_timer == 0) {
2238 if (gc->prpl->list_icon)
2239 xpm = gc->prpl->list_icon(b->uc);
2240 if (xpm == NULL)
2241 xpm = (char **)no_icon_xpm;
2242 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm, NULL, xpm);
2243 gtk_widget_hide(bs->pix);
2244 gtk_pixmap_set(GTK_PIXMAP(bs->pix), pm, bm);
2245 gtk_widget_show(bs->pix);
2246 if (!(blist_options & OPT_BLIST_SHOW_PIXMAPS))
2247 gtk_widget_hide(bs->pix);
2248 gdk_pixmap_unref(pm);
2249 gdk_bitmap_unref(bm);
2250 }
2251 update_idle_time(bs);
2252 } else {
2253 gs = find_group_show(g->name);
2254 if (!gs)
2255 return;
2256 bs = find_buddy_show(gs, b->name);
2257 if (!bs)
2258 return;
2259 if (!bs->connlist)
2260 return; /* we won't do signoff updates for
2261 buddies that have already signed
2262 off */
2263 if (bs->sound != 1)
2264 gaim_sound_play_event(GAIM_SOUND_BUDDY_LEAVE);
2265 if (blist_options & OPT_BLIST_POPUP)
2266 gdk_window_show(blist->window);
2267 bs->connlist = g_slist_remove(bs->connlist, gc);
2268 update_num_group(gs);
2269 if (bs->log_timer > 0)
2270 g_source_remove(bs->log_timer);
2271 bs->log_timer = g_timeout_add(10000, log_timeout, bs);
2272 pm = gdk_pixmap_create_from_xpm_d(blist->window, &bm, NULL, logout_icon_xpm);
2273 gtk_widget_hide(bs->pix);
2274 gtk_pixmap_set(GTK_PIXMAP(bs->pix), pm, bm);
2275 gtk_widget_show(bs->pix);
2276 gdk_pixmap_unref(pm);
2277 gdk_bitmap_unref(bm);
2278 if ((bs->sound != 1) && (im_options & OPT_IM_LOGON)) {
2279 struct gaim_conversation *c = gaim_find_conversation(b->name);
2280 if (c) {
2281 char tmp[1024];
2282 g_snprintf(tmp, sizeof(tmp), _("%s logged out."),
2283 get_buddy_alias(b));
2284 gaim_conversation_write(c, NULL, tmp, -1,
2285 WFLAG_SYSTEM, time(NULL));
2286 } else if (awayqueue && find_queue_total_by_name(b->name)) {
2287 struct queued_message *qm = g_new0(struct queued_message, 1);
2288 g_snprintf(qm->name, sizeof(qm->name), "%s",
2289 get_buddy_alias(b));
2290 qm->message = g_strdup_printf(_("%s logged out."),
2291 get_buddy_alias(b));
2292 qm->account = gc->account;
2293 qm->tm = time(NULL);
2294 qm->flags = WFLAG_SYSTEM;
2295 qm->len = -1;
2296 message_queue = g_slist_append(message_queue, qm);
2297 }
2298 }
2299
2300 bs->sound = 1;
2301 }
2302 }
2303
2304 static gboolean delayed_save_prefs(gpointer data) {
2305 save_prefs();
2306 return FALSE;
2307 }
2308
2309 static gboolean configure_blist_window(GtkWidget *w, GdkEventConfigure *event, gpointer data) {
2310 /* unfortunately GdkEventConfigure ignores the window gravity, but *
2311 * the only way we have of setting the position doesn't. we have to *
2312 * call get_position and get_size because they do pay attention to *
2313 * the gravity. this is inefficient and I agree it sucks, but it's *
2314 * more likely to work correctly. - Robot101 */
2315 gint x, y;
2316
2317 /* check for visibility because when we aren't visible, this will *
2318 * give us bogus (0,0) coordinates. - xOr */
2319 if (GTK_WIDGET_VISIBLE(blist)) {
2320 gtk_window_get_position(GTK_WINDOW(blist), &x, &y);
2321
2322 if (x != blist_pos.x ||
2323 y != blist_pos.y ||
2324 event->width != blist_pos.width ||
2325 event->height != blist_pos.height) {
2326 blist_pos.x = x;
2327 blist_pos.y = y;
2328 blist_pos.width = event->width;
2329 blist_pos.height = event->height;
2330
2331 if (!g_main_context_find_source_by_user_data(NULL, &delayed_save_prefs)) {
2332 debug_printf("queueing save of blist prefs\n");
2333 g_timeout_add(5000, delayed_save_prefs, &delayed_save_prefs);
2334 }
2335 }
2336 }
2337
2338 return FALSE;
2339 }
2340
2341 static void visibility_blist_window(GtkWidget *w, GdkEventVisibility *event, void *data) {
2342 if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) {
2343 obscured = TRUE;
2344 } else {
2345 obscured = FALSE;
2346 }
2347 }
2348
2349 /*******************************************************************
2350 *
2351 * Helper funs for making the menu
2352 *
2353 *******************************************************************/
2354
2355 void gaim_separator(GtkWidget *menu)
2356 {
2357 GtkWidget *menuitem;
2358
2359 menuitem = gtk_separator_menu_item_new();
2360 gtk_widget_show(menuitem);
2361 gtk_menu_append(GTK_MENU(menu), menuitem);
2362 }
2363
2364
2365 void build_imchat_box(gboolean on)
2366 {
2367 if (on) {
2368 if (imchatbox)
2369 return;
2370
2371 imbutton = gtk_button_new_with_label(_("IM"));
2372 infobutton = gtk_button_new_with_label(_("Info"));
2373 chatbutton = gtk_button_new_with_label(_("Chat"));
2374 awaybutton = gtk_button_new_with_label(_("Away"));
2375
2376 imchatbox = gtk_hbox_new(TRUE, 10);
2377
2378 gtk_button_set_relief(GTK_BUTTON(imbutton), GTK_RELIEF_NONE);
2379 gtk_button_set_relief(GTK_BUTTON(infobutton), GTK_RELIEF_NONE);
2380 gtk_button_set_relief(GTK_BUTTON(chatbutton), GTK_RELIEF_NONE);
2381 gtk_button_set_relief(GTK_BUTTON(awaybutton), GTK_RELIEF_NONE);
2382
2383 /* Put the buttons in the hbox */
2384 gtk_widget_show(imbutton);
2385 gtk_widget_show(infobutton);
2386 gtk_widget_show(chatbutton);
2387 gtk_widget_show(awaybutton);
2388
2389 gtk_box_pack_start(GTK_BOX(imchatbox), imbutton, TRUE, TRUE, 0);
2390 gtk_box_pack_start(GTK_BOX(imchatbox), infobutton, TRUE, TRUE, 0);
2391 gtk_box_pack_start(GTK_BOX(imchatbox), chatbutton, TRUE, TRUE, 0);
2392 gtk_box_pack_start(GTK_BOX(imchatbox), awaybutton, TRUE, TRUE, 0);
2393 gtk_container_border_width(GTK_CONTAINER(imchatbox), 5);
2394
2395 g_signal_connect(GTK_OBJECT(imbutton), "clicked", G_CALLBACK(im_callback),
2396 buddies);
2397 g_signal_connect(GTK_OBJECT(infobutton), "clicked", G_CALLBACK(info_callback),
2398 buddies);
2399 g_signal_connect(GTK_OBJECT(chatbutton), "clicked", G_CALLBACK(chat_callback),
2400 buddies);
2401 g_signal_connect(GTK_OBJECT(awaybutton), "clicked", G_CALLBACK(away_callback),
2402 buddies);
2403
2404 gtk_tooltips_set_tip(tips, infobutton, _("Information on selected Buddy"), "Penguin");
2405 gtk_tooltips_set_tip(tips, imbutton, _("Send Instant Message"), "Penguin");
2406 gtk_tooltips_set_tip(tips, chatbutton, _("Start/join a Buddy Chat"), "Penguin");
2407 gtk_tooltips_set_tip(tips, awaybutton, _("Activate Away Message"), "Penguin");
2408
2409 gtk_box_pack_start(GTK_BOX(buddypane), imchatbox, FALSE, FALSE, 0);
2410
2411 gtk_widget_show(imchatbox);
2412 } else {
2413 if (imchatbox)
2414 gtk_widget_destroy(imchatbox);
2415 imchatbox = NULL;
2416 }
2417 }
2418
2419 extern GtkWidget *debugbutton;
2420 void clicked_debug (GtkWidget *widg, gpointer pntr)
2421 {
2422 if (debugbutton)
2423 gtk_button_clicked(GTK_BUTTON(debugbutton));
2424 else {
2425 misc_options ^= OPT_MISC_DEBUG;
2426 show_debug();
2427 }
2428 }
2429
2430 void
2431 show_xfer_dialog(GtkMenuItem *item, gpointer user_data)
2432 {
2433 struct gaim_gtkxfer_dialog *dialog;
2434
2435 dialog = gaim_get_gtkxfer_dialog();
2436
2437 if (dialog == NULL) {
2438 dialog = gaim_gtkxfer_dialog_new();
2439
2440 gaim_set_gtkxfer_dialog(dialog);
2441 }
2442
2443 gaim_gtkxfer_dialog_show(dialog);
2444 }
2445
2446 void make_buddy_list()
2447 {
2448
2449 /* Build the buddy list, based on *config */
2450
2451 GtkWidget *sw;
2452 GtkWidget *menu;
2453 #ifdef NO_MULTI
2454 GtkWidget *setmenu;
2455 GtkWidget *findmenu;
2456 #endif
2457 GtkWidget *menubar;
2458 GtkWidget *vbox;
2459 GtkWidget *menuitem;
2460 GtkWidget *notebook;
2461 GtkWidget *label;
2462 GtkWidget *bbox;
2463 GtkWidget *tbox;
2464
2465 if (blist) {
2466 return;
2467 }
2468
2469 blist = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2470
2471 gtk_window_set_gravity(GTK_WINDOW(blist), GDK_GRAVITY_NORTH_WEST);
2472 gtk_window_set_policy(GTK_WINDOW(blist), TRUE, TRUE, TRUE);
2473 gtk_window_set_title(GTK_WINDOW(blist), _("Gaim - Buddy List"));
2474 gtk_window_set_role(GTK_WINDOW(blist), "buddy_list");
2475
2476 gtk_widget_realize(blist);
2477
2478 accel = gtk_accel_group_new();
2479 gtk_window_add_accel_group(GTK_WINDOW(blist), accel);
2480
2481 menubar = gtk_menu_bar_new();
2482
2483 menu = gtk_menu_new();
2484 gtk_menu_set_accel_group(GTK_MENU(menu), accel);
2485
2486 menuitem = gaim_new_item(NULL, _("File"));
2487 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2488 gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
2489
2490 gaim_new_item_from_stock(menu, _("_Add A Buddy"), GTK_STOCK_ADD,
2491 G_CALLBACK(add_buddy_callback), NULL, 'b', GDK_CONTROL_MASK, "Ctl+B");
2492 gaim_new_item_from_stock(menu, _("_Join A Chat"), GTK_STOCK_JUMP_TO,
2493 G_CALLBACK(chat_callback), NULL, 'c', GDK_CONTROL_MASK, "Ctl+C");
2494 gaim_new_item_from_stock(menu, _("_New Message"), GTK_STOCK_CONVERT,
2495 G_CALLBACK(show_im_dialog), NULL, 'i', GDK_CONTROL_MASK, "Ctl+I");
2496 gaim_new_item_from_stock(menu, _("_Get User Info"), GTK_STOCK_FIND,
2497 G_CALLBACK(show_info_dialog), NULL, 'j', GDK_CONTROL_MASK, "Ctl+J");
2498
2499 gaim_separator(menu);
2500
2501 gaim_new_item_from_pixbuf(menu, _("Import Buddy List"), "import-menu.png",
2502 G_CALLBACK(import_callback), NULL, 0, 0, 0);
2503
2504 gaim_separator(menu);
2505
2506 gaim_new_item_from_stock(menu, _("Signoff"), NULL,
2507 G_CALLBACK(signoff_all), (void*)1, 'd', GDK_CONTROL_MASK, "Ctl+D");
2508 gaim_new_item_from_stock(menu, _("Hide"), NULL,
2509 G_CALLBACK(hide_buddy_list), NULL, 'h', GDK_CONTROL_MASK, "Ctl+H");
2510 gaim_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT,
2511 G_CALLBACK(do_quit), NULL, 'q', GDK_CONTROL_MASK, "Ctl+Q");
2512
2513 menu = gtk_menu_new();
2514
2515 menuitem = gaim_new_item(NULL, _("Tools"));
2516 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2517 gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
2518
2519 awaymenu = gtk_menu_new();
2520 menuitem = gaim_new_item_from_stock(menu, _("Away"), NULL, NULL, NULL, 0, 0, 0);
2521 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), awaymenu);
2522 do_away_menu();
2523
2524 bpmenu = gtk_menu_new();
2525 menuitem = gaim_new_item_from_stock(menu, _("Buddy Pounce"), NULL, NULL, NULL, 0, 0, 0);
2526 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), bpmenu);
2527 do_bp_menu();
2528
2529 gaim_separator(menu);
2530
2531 #ifndef NO_MULTI
2532 gaim_new_item_from_pixbuf(menu, _("_Accounts..."), "accounts-menu.png",
2533 G_CALLBACK(account_editor), NULL, 'a', GDK_CONTROL_MASK, "Ctl+A");
2534 #endif
2535 gaim_new_item_from_stock(menu, _("_Preferences..."), GTK_STOCK_PREFERENCES,
2536 G_CALLBACK(show_prefs), NULL, 'p', GDK_CONTROL_MASK, "Ctl+P");
2537
2538 gaim_new_item_from_stock(menu, _("_File Transfers..."), GTK_STOCK_REVERT_TO_SAVED,
2539 G_CALLBACK(show_xfer_dialog), NULL, 0, 0, NULL);
2540
2541 gaim_separator(menu);
2542
2543 protomenu = gtk_menu_new();
2544 menuitem = gaim_new_item_from_stock(menu, _("Protocol Actions"), NULL, NULL, NULL, 0, 0, 0);
2545 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), protomenu);
2546 do_proto_menu();
2547
2548 gaim_new_item_from_stock(menu, _("Pr_ivacy..."), NULL,
2549 G_CALLBACK(show_privacy_options), NULL, 0, 0, 0);
2550
2551 gaim_new_item_from_stock(menu, _("_View System Log..."), NULL,
2552 G_CALLBACK(show_syslog), NULL, 0, 0, 0);
2553
2554 menu = gtk_menu_new();
2555
2556 menuitem = gaim_new_item(NULL, _("Help"));
2557 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
2558 gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
2559
2560 gaim_new_item_from_stock(menu, _("Online Help"), GTK_STOCK_HELP, G_CALLBACK(open_url), WEBSITE"documentation.php", GDK_F1, 0, NULL);
2561 gaim_new_item_from_stock(menu, _("Debug Window"), NULL, G_CALLBACK(clicked_debug), NULL, 0, 0, NULL);
2562
2563 gaim_separator(menu);
2564
2565 gaim_new_item_from_pixbuf(menu, _("About Gaim"), "about_menu.png", G_CALLBACK(show_about), NULL, GDK_F1, GDK_CONTROL_MASK, NULL);
2566
2567 gtk_widget_show(menubar);
2568
2569 vbox = gtk_vbox_new(FALSE, 0);
2570
2571 notebook = gtk_notebook_new();
2572
2573 /* Do buddy list stuff */
2574 /* FIXME: spacing on both panes is ad hoc */
2575 buddypane = gtk_vbox_new(FALSE, 1);
2576
2577 buddies = gtk_tree_new();
2578 gtk_tree_set_view_lines(GTK_TREE(buddies), FALSE);
2579 sw = gtk_scrolled_window_new(NULL, NULL);
2580
2581 tips = gtk_tooltips_new();
2582 gtk_object_set_data(GTK_OBJECT(blist), _("Buddy List"), tips);
2583
2584 /* Now the buddy list */
2585 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), buddies);
2586 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
2587 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2588 gtk_widget_set_usize(sw, 200, 200);
2589 gtk_widget_show(buddies);
2590 gtk_widget_show(sw);
2591
2592 gtk_box_pack_start(GTK_BOX(buddypane), sw, TRUE, TRUE, 0);
2593 gtk_widget_show(buddypane);
2594
2595 if (!(blist_options & OPT_BLIST_NO_BUTTONS))
2596 build_imchat_box(TRUE);
2597
2598 /* Swing the edit buddy */
2599 editpane = gtk_vbox_new(FALSE, 1);
2600
2601 edittree = gtk_ctree_new(1, 0);
2602 gtk_ctree_set_line_style(GTK_CTREE(edittree), GTK_CTREE_LINES_SOLID);;
2603 gtk_ctree_set_expander_style(GTK_CTREE(edittree), GTK_CTREE_EXPANDER_SQUARE);
2604 gtk_clist_set_reorderable(GTK_CLIST(edittree), TRUE);
2605 g_signal_connect(GTK_OBJECT(edittree), "button_press_event",
2606 G_CALLBACK(click_edit_tree), NULL);
2607
2608 gtk_ctree_set_drag_compare_func(GTK_CTREE(edittree),
2609 (GtkCTreeCompareDragFunc) edit_drag_compare_func);
2610
2611
2612 g_signal_connect_after(GTK_OBJECT(edittree), "tree_move",
2613 G_CALLBACK(edit_tree_move), NULL);
2614
2615
2616 bbox = gtk_hbox_new(TRUE, 5);
2617 gtk_container_set_border_width(GTK_CONTAINER(bbox), 5);
2618 tbox = gtk_scrolled_window_new(NULL, NULL);
2619
2620 /* buttons */
2621 addbutton = gtk_button_new_with_label(_("Add"));
2622 groupbutton = gtk_button_new_with_label(_("Group"));
2623 rembutton = gtk_button_new_with_label(_("Remove"));
2624
2625 gtk_button_set_relief(GTK_BUTTON(addbutton), GTK_RELIEF_NONE);
2626 gtk_button_set_relief(GTK_BUTTON(groupbutton), GTK_RELIEF_NONE);
2627 gtk_button_set_relief(GTK_BUTTON(rembutton), GTK_RELIEF_NONE);
2628
2629 gtk_box_pack_start(GTK_BOX(bbox), addbutton, TRUE, TRUE, 0);
2630 gtk_box_pack_start(GTK_BOX(bbox), groupbutton, TRUE, TRUE, 0);
2631 gtk_box_pack_start(GTK_BOX(bbox), rembutton, TRUE, TRUE, 0);
2632
2633 gtk_tooltips_set_tip(tips, addbutton, _("Add a new Buddy"), "Penguin");
2634 gtk_tooltips_set_tip(tips, groupbutton, _("Add a new Group"), "Penguin");
2635 gtk_tooltips_set_tip(tips, rembutton, _("Remove selected Buddy/Group"), "Penguin");
2636
2637 g_signal_connect(G_OBJECT(rembutton), "clicked", G_CALLBACK(do_del_buddy), edittree);
2638 g_signal_connect(G_OBJECT(addbutton), "clicked", G_CALLBACK(add_buddy_callback), NULL);
2639 g_signal_connect(G_OBJECT(groupbutton), "clicked", G_CALLBACK(add_group_callback), NULL);
2640
2641 /* And the boxes in the box */
2642 gtk_box_pack_start(GTK_BOX(editpane), tbox, TRUE, TRUE, 0);
2643 gtk_box_pack_start(GTK_BOX(editpane), bbox, FALSE, FALSE, 0);
2644
2645 /* Finish up */
2646 gtk_widget_show(addbutton);
2647 gtk_widget_show(groupbutton);
2648 gtk_widget_show(rembutton);
2649 gtk_widget_show(edittree);
2650 gtk_widget_show(tbox);
2651 gtk_widget_show(bbox);
2652 gtk_widget_show(editpane);
2653
2654 update_button_pix();
2655
2656 label = gtk_label_new(_("Online"));
2657 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buddypane, label);
2658 label = gtk_label_new(_("Edit Buddies"));
2659 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), editpane, label);
2660
2661 if(blist_options & OPT_BLIST_BOTTOM_TAB)
2662 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
2663
2664 gtk_widget_show_all(notebook);
2665
2666 /* Pack things in the vbox */
2667 gtk_widget_show(vbox);
2668 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
2669 gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
2670 gtk_container_add(GTK_CONTAINER(blist), vbox);
2671
2672 g_signal_connect(G_OBJECT(blist), "delete_event", G_CALLBACK(close_buddy_list), NULL);
2673 g_signal_connect(G_OBJECT(blist), "configure_event", G_CALLBACK(configure_blist_window), NULL);
2674 g_signal_connect(G_OBJECT(blist), "visibility_notify_event", G_CALLBACK(visibility_blist_window), NULL);
2675
2676 gtk_widget_add_events(blist, GDK_VISIBILITY_NOTIFY_MASK);
2677
2678 /* The edit tree */
2679 gtk_container_add(GTK_CONTAINER(tbox), edittree);
2680 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tbox),
2681 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2682
2683 #ifdef _WIN32
2684 /* Register newly created window with systray module */
2685 wgaim_created_blistwin(GTK_WIDGET(blist));
2686 #endif
2687
2688 /* Houston, we are go for launch. */
2689 unhide_buddy_list();
2690 }
2691
2692 void show_buddy_list()
2693 {
2694 make_buddy_list();
2695 build_edit_tree();
2696 update_button_pix();
2697 }