comparison src/gtkconv.c @ 4359:5fb47ec9bfe4

[gaim-migrate @ 4625] Wow, okay, where to begin with this one ;) I rewrote the whole conversation backend. It is now core/UI split. Here's how it works.. Every conversation is represented by a gaim_conversation structure. This branches out into gaim_im and gaim_chat structures. Every conversation lives in (well, normally, but it doesn't have to) a gaim_window structure. This is a _CORE_ representation of a window. There can be multiple gaim_window structures around. The gaim_window and gaim_conversation structures have UI-specific operation structures associated with them. At the moment, the only UI is GTK+, and this will be for some time. Don't start thinking you can write a QT UI now. It's just not going to happen. Everything that is done on a conversation is done through the core API. This API does core processing and then calls the UI operations for the rendering and anything else. Now, what does this give the user? - Multiple windows. - Multiple tabs per window. - Draggable tabs. - Send As menu is moved to the menubar. - Menubar for chats. - Some very cool stuff in the future, like replacing, say, IRC chat windows with an X-Chat interface, or whatever. - Later on, customizable window/conversation positioning. For developers: - Fully documented API - Core/UI split - Variable checking and mostly sane handling of incorrect variables. - Logical structure to conversations, both core and UI. - Some very cool stuff in the future, like replacing, say, IRC chat windows with an X-Chat interface, or whatever. - Later on, customizable window/conversation positioning. - Oh yeah, and the beginning of a stock icon system. Now, there are things that aren't there yet. You will see tabs even if you have them turned off. This will be fixed in time. Also, the preferences will change to work with the new structure. I'm starting school in 2 days, so it may not be done immediately, but hopefully in the next week. Enjoy! committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Mon, 20 Jan 2003 09:10:23 +0000
parents
children c435a29370b8
comparison
equal deleted inserted replaced
4358:2b8abf7f9cc1 4359:5fb47ec9bfe4
1 /*
2 * gaim
3 *
4 * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <string.h>
25 #ifndef _WIN32
26 #include <sys/time.h>
27 #include <unistd.h>
28 #include <gdk/gdkx.h>
29 #include <X11/Xlib.h>
30 #else
31 #ifdef small
32 #undef small
33 #endif
34 #endif /*_WIN32*/
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <gtk/gtk.h>
42 #ifdef USE_GTKSPELL
43 #include <gtkspell/gtkspell.h>
44 #endif
45 #include "gtkimhtml.h"
46 #include <gdk/gdkkeysyms.h>
47 #include "prpl.h"
48 #include "gtkimhtml.h"
49 #include "dnd-hints.h"
50
51 static char nick_colors[][8] = {
52 "#ba55d3", /* Medium Orchid */
53 "#ee82ee", /* Violet */
54 "#c715b4", /* Medium Violet Red */
55 "#ff69b4", /* Hot Pink */
56 "#ff6347", /* Tomato */
57 "#fa8c00", /* Dark Orange */
58 "#fa8072", /* Salmon */
59 "#b22222", /* Fire Brick */
60 "#f4a460", /* Sandy Brown */
61 "#cd5c5c", /* Indian Red */
62 "#bc8f8f", /* Rosy Brown */
63 "#f0e68c", /* Khaki */
64 "#bdb76b", /* Dark Khaki */
65 "#228b22", /* Forest Green */
66 "#9acd32", /* Yellow Green */
67 "#32cd32", /* Lime Green */
68 "#3cb371", /* Medium Sea Green */
69 "#2e8b57", /* Sea Green */
70 "#8fbc8f", /* Dark Sea Green */
71 "#66cdaa", /* Medium Aquamarine */
72 "#5f9ea0", /* Cadet Blue */
73 "#48d1cc", /* Medium Turquoise */
74 "#00ced1", /* Dark Turquoise */
75 "#4682b4", /* Stell Blue */
76 "#00bfff", /* Deep Sky Blue */
77 "#1690ff", /* Dodger Blue */
78 "#4169ff", /* Royal Blue */
79 "#6a5acd", /* Slate Blue */
80 "#6495ed", /* Cornflower Blue */
81 "#708090", /* Slate gray */
82 "#ffdead", /* Navajo White */
83 };
84 #define NUM_NICK_COLORS (sizeof(nick_colors) / sizeof(*nick_colors))
85
86 #define SCALE(x) \
87 ((gdk_pixbuf_animation_get_width(x) <= 48 && \
88 gdk_pixbuf_animation_get_height(x) <= 48) ? 48 : 50)
89
90 struct InviteBuddyInfo
91 {
92 GtkWidget *window;
93
94 GtkWidget *entry;
95 GtkWidget *message;
96
97 struct gaim_conversation *conv;
98 };
99
100 char fontface[128] = { 0 };
101 int fontsize = 3;
102
103 static GtkWidget *invite_dialog = NULL;
104 #if 0
105 static GtkWidget *invite_entry;
106 static GtkWidget *invite_mess;
107 #endif
108
109 static volatile gboolean state_lock = FALSE;
110
111 /* Prototypes. <-- because Paco-Paco hates this comment. */
112 static void check_everything(GtkTextBuffer *buffer);
113 static void quiet_set(GtkWidget *tb, gboolean active);
114 static void move_next_tab(struct gaim_conversation *conv);
115 static void do_bold(GtkWidget *bold, struct gaim_gtk_conversation *gtkconv);
116 static void do_italic(GtkWidget *italic, struct gaim_gtk_conversation *gtkconv);
117 static void do_underline(GtkWidget *underline, struct gaim_gtk_conversation *gtkconv);
118 static void do_small(GtkWidget *small, struct gaim_gtk_conversation *gtkconv);
119 static void do_normal(GtkWidget *small, struct gaim_gtk_conversation *gtkconv);
120 static void do_big(GtkWidget *small, struct gaim_gtk_conversation *gtkconv);
121 static void toggle_font(GtkWidget *font, struct gaim_conversation *conv);
122 static void toggle_fg_color(GtkWidget *color, struct gaim_conversation *conv);
123 static void toggle_bg_color(GtkWidget *color, struct gaim_conversation *conv);
124 static void got_typing_keypress(struct gaim_conversation *conv, gboolean first);
125 static GList *generate_invite_user_names(struct gaim_connection *gc);
126 static void add_chat_buddy_common(struct gaim_conversation *conv,
127 const char *name, int pos);
128 static void tab_complete(struct gaim_conversation *conv);
129 static void update_send_as_selection(struct gaim_window *win);
130
131 /**************************************************************************
132 * Callbacks
133 **************************************************************************/
134 static void
135 do_insert_image_cb(GObject *obj, GtkWidget *wid)
136 {
137 struct gaim_conversation *conv;
138 struct gaim_gtk_conversation *gtkconv;
139 struct gaim_im *im;
140 const char *name;
141 const char *filename;
142 char *buf;
143 struct stat st;
144 int id;
145
146 conv = g_object_get_data(obj, "user_data");
147 gtkconv = GAIM_GTK_CONVERSATION(conv);
148 im = GAIM_IM(conv);
149 name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(wid));
150 id = g_slist_length(im->images) + 1;
151
152 if (file_is_dir(name, wid))
153 return;
154
155 gtk_widget_destroy(wid);
156
157 if (!name)
158 return;
159
160 if (stat(name, &st) != 0) {
161 debug_printf("Could not stat %s\n", name);
162 return;
163 }
164
165 filename = name;
166 while (strchr(filename, '/'))
167 filename = strchr(filename, '/') + 1;
168
169 buf = g_strdup_printf("<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
170 filename, id, (int)st.st_size);
171 im->images = g_slist_append(im->images, g_strdup(name));
172 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer),
173 buf, -1);
174 g_free(buf);
175 }
176
177 static gint
178 close_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
179 {
180 struct gaim_window *win = (struct gaim_window *)d;
181
182 gaim_window_destroy(win);
183 }
184
185 static gint
186 close_conv_cb(GtkWidget *w, gpointer d)
187 {
188 struct gaim_conversation *conv = (struct gaim_conversation *)d;
189
190 gaim_conversation_destroy(conv);
191 }
192
193 static void
194 insert_image_cb(GtkWidget *save, struct gaim_conversation *conv)
195 {
196 struct gaim_gtk_conversation *gtkconv;
197 char buf[BUF_LONG];
198 GtkWidget *window;
199
200 if (gaim_gtk_is_state_locked())
201 return;
202
203 gtkconv = GAIM_GTK_CONVERSATION(conv);
204
205 window = gtk_file_selection_new(_("Gaim - Insert Image"));
206 g_snprintf(buf, sizeof(buf), "%s" G_DIR_SEPARATOR_S, gaim_home_dir());
207 gtk_file_selection_set_filename(GTK_FILE_SELECTION(window), buf);
208
209 g_object_set_data(G_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
210 "user_data", conv);
211 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
212 "clicked", G_CALLBACK(do_insert_image_cb), window);
213 g_signal_connect_swapped(
214 G_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
215 "clicked", G_CALLBACK(gtk_widget_destroy), window);
216
217 gtk_widget_show(window);
218
219 gaim_gtk_set_state_lock(TRUE);
220 gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(gtkconv->toolbar.image),
221 FALSE);
222 gaim_gtk_set_state_lock(FALSE);
223 }
224
225 static void
226 insert_link_cb(GtkWidget *w, struct gaim_conversation *conv)
227 {
228 struct gaim_gtk_conversation *gtkconv;
229
230 if (gaim_gtk_is_state_locked())
231 return;
232
233 gtkconv = GAIM_GTK_CONVERSATION(conv);
234
235 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkconv->toolbar.link)))
236 show_insert_link(gtkconv->toolbar.link, conv);
237 else if (gtkconv->dialogs.link)
238 cancel_link(gtkconv->toolbar.link, conv);
239 else
240 gaim_gtk_advance_past(gtkconv, "<A HREF>", "</A>");
241
242 gtk_widget_grab_focus(gtkconv->entry);
243 }
244
245 static void
246 insert_smiley_cb(GtkWidget *smiley, struct gaim_conversation *conv)
247 {
248 struct gaim_gtk_conversation *gtkconv;
249
250 if (gaim_gtk_is_state_locked())
251 return;
252
253 gtkconv = GAIM_GTK_CONVERSATION(conv);
254
255 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley)))
256 show_smiley_dialog(conv, smiley);
257 else if (gtkconv->dialogs.smiley)
258 close_smiley_dialog(smiley, conv);
259
260 gtk_widget_grab_focus(gtkconv->entry);
261 }
262
263 static void
264 menu_save_as_cb(gpointer data, guint action, GtkWidget *widget)
265 {
266 struct gaim_window *win = (struct gaim_window *)data;
267
268 save_convo(NULL, gaim_window_get_active_conversation(win));
269 }
270
271 static void
272 menu_view_history_cb(gpointer data, guint action, GtkWidget *widget)
273 {
274 struct gaim_window *win = (struct gaim_window *)data;
275
276 conv_show_log(NULL, gaim_window_get_active_conversation(win));
277 }
278 static void
279 menu_insert_link_cb(gpointer data, guint action, GtkWidget *widget)
280 {
281 struct gaim_window *win = (struct gaim_window *)data;
282 struct gaim_conversation *conv;
283 struct gaim_gtk_conversation *gtkconv;
284
285 conv = gaim_window_get_active_conversation(win);
286 gtkconv = GAIM_GTK_CONVERSATION(conv);
287
288 show_insert_link(gtkconv->toolbar.link, conv);
289 }
290
291 static void
292 menu_insert_image_cb(gpointer data, guint action, GtkWidget *widget)
293 {
294 struct gaim_window *win = (struct gaim_window *)data;
295
296 insert_image_cb(NULL, gaim_window_get_active_conversation(win));
297 }
298
299 static void
300 menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
301 {
302 struct gaim_window *win = (struct gaim_window *)data;
303
304 close_conv_cb(NULL, gaim_window_get_active_conversation(win));
305 }
306
307 static void
308 menu_logging_cb(gpointer data, guint action, GtkWidget *widget)
309 {
310 struct gaim_window *win = (struct gaim_window *)data;
311 struct gaim_conversation *conv;
312
313 if (gaim_gtk_is_state_locked())
314 return;
315
316 conv = gaim_window_get_active_conversation(win);
317
318 gaim_conversation_set_logging(conv, !gaim_conversation_is_logging(conv));
319 }
320
321 static void
322 menu_sounds_cb(gpointer data, guint action, GtkWidget *widget)
323 {
324 struct gaim_window *win = (struct gaim_window *)data;
325 struct gaim_conversation *conv;
326 struct gaim_gtk_conversation *gtkconv;
327
328 if (gaim_gtk_is_state_locked())
329 return;
330
331 conv = gaim_window_get_active_conversation(win);
332 gtkconv = GAIM_GTK_CONVERSATION(conv);
333
334 gtkconv->make_sound = !gtkconv->make_sound;
335 }
336
337 static gboolean
338 entry_key_pressed_cb_1(GtkTextBuffer *buffer)
339 {
340 check_everything(buffer);
341
342 return FALSE;
343 }
344
345 static void
346 send_cb(GtkWidget *widget, struct gaim_conversation *conv)
347 {
348 struct gaim_gtk_conversation *gtkconv;
349 char *buf, *buf2;
350 GtkTextIter start_iter, end_iter;
351 int limit;
352
353 gtkconv = GAIM_GTK_CONVERSATION(conv);
354
355 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_iter);
356 gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end_iter);
357 buf2 = gtk_text_buffer_get_text(gtkconv->entry_buffer,
358 &start_iter, &end_iter, FALSE);
359
360 quiet_set(gtkconv->toolbar.bold, FALSE);
361 quiet_set(gtkconv->toolbar.italic, FALSE);
362 quiet_set(gtkconv->toolbar.underline, FALSE);
363 quiet_set(gtkconv->toolbar.normal_size, FALSE);
364 quiet_set(gtkconv->toolbar.fgcolor, FALSE);
365 quiet_set(gtkconv->toolbar.bgcolor, FALSE);
366 quiet_set(gtkconv->toolbar.link, FALSE);
367
368 gtk_widget_grab_focus(gtkconv->entry);
369
370 limit = 32 * 1024; /* This will be done again in gaim_im_send. *shrug* */
371
372 buf = g_malloc(limit);
373 strncpy(buf, buf2, limit);
374
375 g_free(buf2);
376
377 if (strlen(buf) == 0) {
378 g_free(buf);
379
380 return;
381 }
382
383 buf2 = g_malloc(limit);
384
385 if (gaim_conversation_get_gc(conv)->flags & OPT_CONN_HTML) {
386 if (font_options & OPT_FONT_BOLD) {
387 g_snprintf(buf2, limit, "<B>%s</B>", buf);
388 strcpy(buf, buf2);
389 }
390
391 if (font_options & OPT_FONT_ITALIC) {
392 g_snprintf(buf2, limit, "<I>%s</I>", buf);
393 strcpy(buf, buf2);
394 }
395
396 if (font_options & OPT_FONT_UNDERLINE) {
397 g_snprintf(buf2, limit, "<U>%s</U>", buf);
398 strcpy(buf, buf2);
399 }
400
401 if (font_options & OPT_FONT_STRIKE) {
402 g_snprintf(buf2, limit, "<STRIKE>%s</STRIKE>", buf);
403 strcpy(buf, buf2);
404 }
405
406 if ((font_options & OPT_FONT_FACE) || gtkconv->has_font) {
407 g_snprintf(buf2, limit,
408 "<FONT FACE=\"%s\">%s</FONT>", gtkconv->fontface, buf);
409 strcpy(buf, buf2);
410 }
411
412 if (font_options & OPT_FONT_SIZE) {
413 g_snprintf(buf2, limit,
414 "<FONT SIZE=\"%d\">%s</FONT>", fontsize, buf);
415 strcpy(buf, buf2);
416 }
417
418 if ((font_options & OPT_FONT_FGCOL) || gtkconv->has_fg) {
419 g_snprintf(buf2, limit,
420 "<FONT COLOR=\"#%02X%02X%02X\">%s</FONT>",
421 gtkconv->fg_color.red / 256,
422 gtkconv->fg_color.green / 256,
423 gtkconv->fg_color.blue / 256, buf);
424 strcpy(buf, buf2);
425 }
426
427 if ((font_options & OPT_FONT_BGCOL) || gtkconv->has_bg) {
428 g_snprintf(buf2, limit,
429 "<BODY BGCOLOR=\"#%02X%02X%02X\">%s</BODY>",
430 gtkconv->fg_color.red / 256,
431 gtkconv->fg_color.green / 256,
432 gtkconv->fg_color.blue / 256, buf);
433 strcpy(buf, buf2);
434 }
435 }
436
437 g_free(buf2);
438
439 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM)
440 gaim_im_send(GAIM_IM(conv), buf);
441 else
442 gaim_chat_send(GAIM_CHAT(conv), buf);
443
444 g_free(buf);
445
446 gtk_text_buffer_set_text(gtkconv->entry_buffer, "", -1);
447 }
448
449 static void
450 add_cb(GtkWidget *widget, struct gaim_conversation *conv)
451 {
452 struct gaim_connection *gc;
453 struct buddy *b;
454 const char *name;
455
456 gc = gaim_conversation_get_gc(conv);
457 name = gaim_conversation_get_name(conv);
458 b = find_buddy(gc->user, name);
459
460 if (b != NULL)
461 show_confirm_del(gc, (char *)name);
462 else if (gc != NULL)
463 show_add_buddy(gc, (char *)name, NULL, NULL);
464
465 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry);
466 }
467
468 static void
469 info_cb(GtkWidget *widget, struct gaim_conversation *conv)
470 {
471 struct gaim_gtk_conversation *gtkconv;
472
473 gtkconv = GAIM_GTK_CONVERSATION(conv);
474
475 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
476 struct gaim_gtk_chat_pane *gtkchat;
477 GtkTreeIter iter;
478 GtkTreeModel *model;
479 GtkTreeSelection *sel;
480 const char *name;
481
482 gtkchat = gtkconv->u.chat;
483
484 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
485 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list));
486
487 if (gtk_tree_selection_get_selected(sel, NULL, &iter))
488 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &name, -1);
489 else
490 return;
491
492 serv_get_info(gaim_conversation_get_gc(conv), (char *)name);
493 }
494 else {
495 serv_get_info(gaim_conversation_get_gc(conv),
496 (char *)gaim_conversation_get_name(conv));
497
498 gtk_widget_grab_focus(gtkconv->entry);
499 }
500 }
501
502 static void
503 warn_cb(GtkWidget *widget, struct gaim_conversation *conv)
504 {
505 show_warn_dialog(gaim_conversation_get_gc(conv),
506 (char *)gaim_conversation_get_name(conv));
507
508 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry);
509 }
510
511 static void
512 block_cb(GtkWidget *widget, struct gaim_conversation *conv)
513 {
514 struct gaim_connection *gc;
515
516 gc = gaim_conversation_get_gc(conv);
517
518 if (gc != NULL)
519 show_add_perm(gc, (char *)gaim_conversation_get_name(conv), FALSE);
520
521 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry);
522 }
523
524 void
525 im_cb(GtkWidget *widget, struct gaim_conversation *conv)
526 {
527 struct gaim_conversation *conv2;
528 struct gaim_gtk_conversation *gtkconv;
529 struct gaim_gtk_chat_pane *gtkchat;
530 GtkTreeIter iter;
531 GtkTreeModel *model;
532 GtkTreeSelection *sel;
533 const char *name;
534
535 gtkconv = GAIM_GTK_CONVERSATION(conv);
536 gtkchat = gtkconv->u.chat;
537
538 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
539 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list));
540
541 if (gtk_tree_selection_get_selected(sel, NULL, &iter))
542 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &name, -1);
543 else
544 return;
545
546 if (*name == '@') name++;
547 if (*name == '+') name++;
548
549 conv2 = gaim_find_conversation(name);
550
551 if (conv2 != NULL)
552 gaim_window_raise(gaim_conversation_get_window(conv2));
553 else
554 conv2 = gaim_conversation_new(GAIM_CONV_IM, name);
555
556 gaim_conversation_set_user(conv2, gaim_conversation_get_user(conv));
557 }
558
559 static void
560 ignore_cb(GtkWidget *w, struct gaim_conversation *conv)
561 {
562 struct gaim_gtk_conversation *gtkconv;
563 struct gaim_gtk_chat_pane *gtkchat;
564 struct gaim_chat *chat;
565 GtkTreeIter iter;
566 GtkTreeModel *model;
567 GtkTreeSelection *sel;
568 const char *name;
569 int pos;
570
571 chat = GAIM_CHAT(conv);
572 gtkconv = GAIM_GTK_CONVERSATION(conv);
573 gtkchat = gtkconv->u.chat;
574
575 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
576 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list));
577
578 if (gtk_tree_selection_get_selected(sel, NULL, &iter)) {
579 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &name, -1);
580 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
581 }
582 else
583 return;
584
585 pos = g_list_index(gaim_chat_get_users(chat), name);
586
587 if (gaim_chat_is_user_ignored(chat, name))
588 gaim_chat_unignore(chat, name);
589 else
590 gaim_chat_ignore(chat, name);
591
592 add_chat_buddy_common(conv, name, pos);
593 }
594
595 static void
596 menu_im_cb(GtkWidget *w, struct gaim_conversation *conv)
597 {
598 const char *who;
599 struct gaim_conversation *conv2;
600
601 who = g_object_get_data(G_OBJECT(w), "user_data");
602
603 conv2 = gaim_find_conversation(who);
604
605 if (conv2 != NULL)
606 gaim_window_show(gaim_conversation_get_window(conv2));
607 else {
608 conv2 = gaim_conversation_new(GAIM_CONV_IM, who);
609 gaim_conversation_set_user(conv2, gaim_conversation_get_user(conv));
610 }
611 }
612
613 static void
614 menu_info_cb(GtkWidget *w, struct gaim_conversation *conv)
615 {
616 struct gaim_connection *gc;
617 char *who;
618
619 gc = gaim_conversation_get_gc(conv);
620 who = g_object_get_data(G_OBJECT(w), "user_data");
621
622 if (gc != NULL) {
623 /*
624 * If there are special needs for getting info on users in
625 * buddy chat "rooms"...
626 */
627 if (gc->prpl->get_cb_info != NULL)
628 gc->prpl->get_cb_info(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
629 else
630 gc->prpl->get_info(gc, who);
631 }
632 }
633
634 static void
635 menu_away_cb(GtkWidget *w, struct gaim_conversation *conv)
636 {
637 struct gaim_connection *gc;
638 char *who;
639
640 gc = gaim_conversation_get_gc(conv);
641 who = g_object_get_data(G_OBJECT(w), "user_data");
642
643 if (gc != NULL) {
644 /*
645 * May want to expand this to work similarly to menu_info_cb?
646 */
647
648 if (gc->prpl->get_cb_away != NULL)
649 gc->prpl->get_cb_away(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
650 }
651 }
652
653 static void
654 menu_add_cb(GtkWidget *w, struct gaim_conversation *conv)
655 {
656 struct gaim_connection *gc;
657 struct buddy *b;
658 char *name;
659
660 gc = gaim_conversation_get_gc(conv);
661 name = g_object_get_data(G_OBJECT(w), "user_data");
662 b = find_buddy(gc->user, name);
663
664 if (b != NULL)
665 show_confirm_del(gc, name);
666 else if (gc != NULL)
667 show_add_buddy(gc, name, NULL, NULL);
668
669 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry);
670 }
671
672 static gint
673 right_click_chat_cb(GtkWidget *widget, GdkEventButton *event,
674 struct gaim_conversation *conv)
675 {
676 struct gaim_gtk_conversation *gtkconv;
677 struct gaim_gtk_chat_pane *gtkchat;
678 struct gaim_connection *gc;
679 struct aim_user *user;
680 GtkTreePath *path;
681 GtkTreeIter iter;
682 GtkTreeModel *model;
683 GtkTreeViewColumn *column;
684 gchar *who;
685 int x, y;
686
687 gtkconv = GAIM_GTK_CONVERSATION(conv);
688 gtkchat = gtkconv->u.chat;
689 user = gaim_conversation_get_user(conv);
690 gc = user->gc;
691
692 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
693
694 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list),
695 event->x, event->y, &path, &column, &x, &y);
696
697 if (path == NULL)
698 return FALSE;
699
700 gtk_tree_selection_select_path(GTK_TREE_SELECTION(
701 gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path);
702
703 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
704 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &who, -1);
705
706 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
707 struct gaim_conversation *c;
708
709 if ((c = gaim_find_conversation(who)) == NULL)
710 c = gaim_conversation_new(GAIM_CONV_IM, who);
711
712 gaim_conversation_set_user(c, user);
713 }
714 else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
715 static GtkWidget *menu = NULL;
716 GtkWidget *button;
717
718 /*
719 * If a menu already exists, destroy it before creating a new one,
720 * thus freeing-up the memory it occupied.
721 */
722
723 if (menu)
724 gtk_widget_destroy(menu);
725
726 menu = gtk_menu_new();
727
728 button = gtk_menu_item_new_with_label(_("IM"));
729 g_signal_connect(G_OBJECT(button), "activate",
730 G_CALLBACK(menu_im_cb), conv);
731 g_object_set_data(G_OBJECT(button), "user_data", who);
732 gtk_menu_append(GTK_MENU(menu), button);
733 gtk_widget_show(button);
734
735 if (gaim_chat_is_user_ignored(GAIM_CHAT(conv), who))
736 button = gtk_menu_item_new_with_label(_("Un-Ignore"));
737 else
738 button = gtk_menu_item_new_with_label(_("Ignore"));
739
740 g_signal_connect(G_OBJECT(button), "activate",
741 G_CALLBACK(ignore_cb), conv);
742 g_object_set_data(G_OBJECT(button), "user_data", who);
743 gtk_menu_append(GTK_MENU(menu), button);
744 gtk_widget_show(button);
745
746 if (gc && gc->prpl->get_info) {
747 button = gtk_menu_item_new_with_label(_("Info"));
748 g_signal_connect(G_OBJECT(button), "activate",
749 G_CALLBACK(menu_info_cb), conv);
750 g_object_set_data(G_OBJECT(button), "user_data", who);
751 gtk_menu_append(GTK_MENU(menu), button);
752 gtk_widget_show(button);
753 }
754
755 if (gc && gc->prpl->get_cb_away) {
756 button = gtk_menu_item_new_with_label(_("Get Away Msg"));
757 g_signal_connect(G_OBJECT(button), "activate",
758 G_CALLBACK(menu_away_cb), conv);
759 g_object_set_data(G_OBJECT(button), "user_data", who);
760 gtk_menu_append(GTK_MENU(menu), button);
761 gtk_widget_show(button);
762 }
763
764 /* Added by Jonas <jonas@birme.se> */
765 if (gc) {
766 if (find_buddy(gc->user, who))
767 button = gtk_menu_item_new_with_label(_("Remove"));
768 else
769 button = gtk_menu_item_new_with_label(_("Add"));
770
771 g_signal_connect(G_OBJECT(button), "activate",
772 G_CALLBACK(menu_add_cb), conv);
773
774 g_object_set_data(G_OBJECT(button), "user_data", who);
775 gtk_menu_append(GTK_MENU(menu), button);
776 gtk_widget_show(button);
777 }
778 /* End Jonas */
779
780 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
781 event->button, event->time);
782 }
783
784 return TRUE;
785 }
786
787 static void
788 do_invite(GtkWidget *w, int resp, struct InviteBuddyInfo *info)
789 {
790 const char *buddy, *message;
791 struct gaim_gtk_conversation *gtkconv;
792
793 gtkconv = GAIM_GTK_CONVERSATION(info->conv);
794
795 if (resp == GTK_RESPONSE_OK) {
796 buddy = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(info->entry)->entry));
797 message = gtk_entry_get_text(GTK_ENTRY(info->message));
798
799 if (!g_strcasecmp(buddy, "")) {
800 g_free(info);
801
802 return;
803 }
804
805 serv_chat_invite(gaim_conversation_get_gc(info->conv),
806 gaim_chat_get_id(GAIM_CHAT(info->conv)),
807 message, buddy);
808 }
809
810 gtk_widget_destroy(invite_dialog);
811 invite_dialog = NULL;
812
813 g_free(info);
814 }
815
816 static void
817 invite_cb(GtkWidget *widget, struct gaim_conversation *conv)
818 {
819 struct InviteBuddyInfo *info = NULL;
820
821 if (invite_dialog == NULL) {
822 struct gaim_connection *gc;
823 struct gaim_window *win;
824 struct gaim_gtk_window *gtkwin;
825 char *filename;
826 GtkWidget *label;
827 GtkWidget *vbox, *hbox;
828 GtkWidget *table;
829 GtkWidget *img;
830
831 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "dialogs",
832 "gaim_question.png", NULL);
833
834 img = gtk_image_new_from_file(filename);
835
836 g_free(filename);
837
838
839 info = g_new0(struct InviteBuddyInfo, 1);
840 info->conv = conv;
841
842 gc = gaim_conversation_get_gc(conv);
843 win = gaim_conversation_get_window(conv);
844 gtkwin = GAIM_GTK_WINDOW(win);
845
846 /* Create the new dialog. */
847 invite_dialog = gtk_dialog_new_with_buttons(
848 _("Gaim - Invite Buddy Into Chat Room"),
849 GTK_WINDOW(gtkwin->window),
850 GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
851 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
852
853 gtk_dialog_set_default_response(GTK_DIALOG(invite_dialog),
854 GTK_RESPONSE_OK);
855 gtk_container_set_border_width(GTK_CONTAINER(invite_dialog), 6);
856 gtk_window_set_resizable(GTK_WINDOW(invite_dialog), FALSE);
857 gtk_dialog_set_has_separator(GTK_DIALOG(invite_dialog), FALSE);
858
859 /* Setup the outside spacing. */
860 vbox = GTK_DIALOG(invite_dialog)->vbox;
861
862 gtk_box_set_spacing(GTK_BOX(vbox), 12);
863 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
864
865 /* Setup the inner hbox and put the dialog's icon in it. */
866 hbox = gtk_hbox_new(FALSE, 12);
867 gtk_container_add(GTK_CONTAINER(vbox), hbox);
868 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
869 gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
870
871 /* Setup the right vbox. */
872 vbox = gtk_vbox_new(FALSE, 0);
873 gtk_container_add(GTK_CONTAINER(hbox), vbox);
874
875 /* Put our happy label in it. */
876 label = gtk_label_new(_("Please enter the name of the user you wish "
877 "to invite, along with an optional invite "
878 "message."));
879 gtk_widget_set_size_request(label, 350, -1);
880 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
881 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
882 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
883
884 /* hbox for the table, and to give it some spacing on the left. */
885 hbox = gtk_hbox_new(FALSE, 6);
886 gtk_container_add(GTK_CONTAINER(vbox), hbox);
887
888 /* Setup the table we're going to use to lay stuff out. */
889 table = gtk_table_new(2, 2, FALSE);
890 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
891 gtk_table_set_col_spacings(GTK_TABLE(table), 6);
892 gtk_container_set_border_width(GTK_CONTAINER(table), 12);
893 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
894
895 /* Now the Buddy label */
896 label = gtk_label_new(NULL);
897 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Buddy:"));
898 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
899 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
900
901 /* Now the Buddy drop-down entry field. */
902 info->entry = gtk_combo_new();
903 gtk_combo_set_case_sensitive(GTK_COMBO(info->entry), FALSE);
904 gtk_entry_set_activates_default(
905 GTK_ENTRY(GTK_COMBO(info->entry)->entry), TRUE);
906
907 gtk_table_attach_defaults(GTK_TABLE(table), info->entry, 1, 2, 0, 1);
908 gtk_label_set_mnemonic_widget(GTK_LABEL(label), info->entry);
909
910 /* Fill in the names. */
911 gtk_combo_set_popdown_strings(GTK_COMBO(info->entry),
912 generate_invite_user_names(gc));
913
914
915 /* Now the label for "Message" */
916 label = gtk_label_new(NULL);
917 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Message:"));
918 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
919 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
920
921
922 /* And finally, the Message entry field. */
923 info->message = gtk_entry_new();
924 gtk_entry_set_activates_default(GTK_ENTRY(info->message), TRUE);
925
926 gtk_table_attach_defaults(GTK_TABLE(table), info->message, 1, 2, 1, 2);
927 gtk_label_set_mnemonic_widget(GTK_LABEL(label), info->message);
928
929 /* Connect the signals. */
930 g_signal_connect(G_OBJECT(invite_dialog), "response",
931 G_CALLBACK(do_invite), info);
932 }
933
934 gtk_widget_show_all(invite_dialog);
935
936 if (info != NULL)
937 gtk_widget_grab_focus(GTK_COMBO(info->entry)->entry);
938 }
939
940 static gboolean
941 entry_key_pressed_cb_2(GtkWidget *entry, GdkEventKey *event, gpointer data)
942 {
943 struct gaim_window *win;
944 struct gaim_conversation *conv;
945 struct gaim_gtk_conversation *gtkconv;
946
947 conv = (struct gaim_conversation *)data;
948 gtkconv = GAIM_GTK_CONVERSATION(conv);
949 win = gaim_conversation_get_window(conv);
950
951 if (event->keyval == GDK_Escape) {
952 if (convo_options & OPT_CONVO_ESC_CAN_CLOSE) {
953 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
954 gaim_conversation_destroy(conv);
955 }
956 }
957 else if (event->keyval == GDK_Page_Up) {
958 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
959
960 if (!(event->state & GDK_CONTROL_MASK))
961 gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
962 }
963 else if (event->keyval == GDK_Page_Down) {
964 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
965
966 if (!(event->state & GDK_CONTROL_MASK))
967 gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
968 }
969 else if ((event->keyval == GDK_F2) &&
970 (convo_options & OPT_CONVO_F2_TOGGLES)) {
971 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
972 !GTK_IMHTML(gtkconv->imhtml)->comments);
973 }
974 else if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
975 if ((event->state & GDK_CONTROL_MASK) &&
976 (convo_options & OPT_CONVO_CTL_ENTER)) {
977
978 send_cb(NULL, conv);
979 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
980
981 return TRUE;
982 }
983 else if (!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) &&
984 (convo_options & OPT_CONVO_ENTER_SENDS)) {
985
986 send_cb(NULL, conv);
987 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
988
989 return TRUE;
990 }
991
992 return FALSE;
993 }
994 else if ((event->state & GDK_CONTROL_MASK) && (event->keyval == 'm')) {
995 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
996 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, "\n", 1);
997 }
998 else if (event->state & GDK_CONTROL_MASK) {
999 switch (event->keyval) {
1000 case GDK_Up:
1001 if (!conv->send_history)
1002 break;
1003
1004 if (!conv->send_history->prev) {
1005 GtkTextIter start, end;
1006
1007 if (conv->send_history->data)
1008 g_free(conv->send_history->data);
1009
1010 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer,
1011 &start);
1012 gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end);
1013
1014 conv->send_history->data =
1015 gtk_text_buffer_get_text(gtkconv->entry_buffer,
1016 &start, &end, FALSE);
1017 }
1018
1019 if (conv->send_history->next &&
1020 conv->send_history->next->data) {
1021
1022 conv->send_history = conv->send_history->next;
1023 gtk_text_buffer_set_text(gtkconv->entry_buffer,
1024 conv->send_history->data, -1);
1025 }
1026
1027 break;
1028
1029 case GDK_Down:
1030 if (!conv->send_history)
1031 break;
1032
1033 if (conv->send_history->prev) {
1034 conv->send_history = conv->send_history->prev;
1035
1036 if (conv->send_history->data)
1037 gtk_text_buffer_set_text(gtkconv->entry_buffer,
1038 conv->send_history->data, -1);
1039 }
1040
1041 break;
1042 }
1043
1044 if (convo_options & OPT_CONVO_CTL_CHARS) {
1045 switch (event->keyval) {
1046 case 'i':
1047 case 'I':
1048 quiet_set(gtkconv->toolbar.italic,
1049 !gtk_toggle_button_get_active(
1050 GTK_TOGGLE_BUTTON(gtkconv->toolbar.italic)));
1051
1052 do_italic(gtkconv->toolbar.italic, gtkconv);
1053
1054 g_signal_stop_emission_by_name(G_OBJECT(entry),
1055 "key_press_event");
1056 break;
1057
1058 case 'u': /* ctrl-u is GDK_Clear, which clears the line. */
1059 case 'U':
1060 quiet_set(gtkconv->toolbar.underline,
1061 !gtk_toggle_button_get_active(
1062 GTK_TOGGLE_BUTTON(gtkconv->toolbar.underline)));
1063
1064 do_underline(gtkconv->toolbar.underline, gtkconv);
1065
1066 g_signal_stop_emission_by_name(G_OBJECT(entry),
1067 "key_press_event");
1068 break;
1069
1070 case 'b': /* ctrl-b is GDK_Left, which moves backwards. */
1071 case 'B':
1072 quiet_set(gtkconv->toolbar.bold,
1073 !gtk_toggle_button_get_active(
1074 GTK_TOGGLE_BUTTON(gtkconv->toolbar.bold)));
1075
1076 do_bold(gtkconv->toolbar.bold, gtkconv);
1077
1078 g_signal_stop_emission_by_name(G_OBJECT(entry),
1079 "key_press_event");
1080 break;
1081
1082 case '-':
1083 do_small(NULL, gtkconv);
1084
1085 g_signal_stop_emission_by_name(G_OBJECT(entry),
1086 "key_press_event");
1087 break;
1088
1089 case '=':
1090 case '+':
1091 do_big(NULL, gtkconv);
1092
1093 g_signal_stop_emission_by_name(G_OBJECT(entry),
1094 "key_press_event");
1095 break;
1096
1097 case '0':
1098 do_normal(NULL, gtkconv);
1099
1100 g_signal_stop_emission_by_name(G_OBJECT(entry),
1101 "key_press_event");
1102 break;
1103
1104 case 'f':
1105 case 'F':
1106 quiet_set(gtkconv->toolbar.normal_size,
1107 !gtk_toggle_button_get_active(
1108 GTK_TOGGLE_BUTTON(gtkconv->toolbar.normal_size)));
1109
1110 toggle_font(gtkconv->toolbar.normal_size, conv);
1111
1112 g_signal_stop_emission_by_name(G_OBJECT(entry),
1113 "key_press_event");
1114 break;
1115 }
1116 }
1117
1118 if (convo_options & OPT_CONVO_CTL_SMILEYS) {
1119 char buf[7];
1120
1121 *buf = '\0';
1122
1123 switch (event->keyval) {
1124 case '1': strcpy(buf, ":-)"); break;
1125 case '2': strcpy(buf, ":-("); break;
1126 case '3': strcpy(buf, ";-)"); break;
1127 case '4': strcpy(buf, ":-P"); break;
1128 case '5': strcpy(buf, "=-O"); break;
1129 case '6': strcpy(buf, ":-*"); break;
1130 case '7': strcpy(buf, ">:o"); break;
1131 case '8': strcpy(buf, "8-)"); break;
1132 case '!': strcpy(buf, ":-$"); break;
1133 case '@': strcpy(buf, ":-!"); break;
1134 case '#': strcpy(buf, ":-["); break;
1135 case '$': strcpy(buf, "O:-)"); break;
1136 case '%': strcpy(buf, ":-/"); break;
1137 case '^': strcpy(buf, ":'("); break;
1138 case '&': strcpy(buf, ":-X"); break;
1139 case '*': strcpy(buf, ":-D"); break;
1140 default: break;
1141 }
1142
1143 if (*buf) {
1144 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer,
1145 buf, -1);
1146 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1147 }
1148 }
1149
1150 if (event->keyval == 'l') {
1151 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml));
1152 g_string_free(conv->history, TRUE);
1153 conv->history = g_string_new("");
1154 }
1155 else if ((event->keyval == 'w') &&
1156 (convo_options & OPT_CONVO_CTL_W_CLOSES)) {
1157
1158 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1159 gaim_conversation_destroy(conv);
1160 return TRUE;
1161 }
1162 else if (event->keyval == 'n') {
1163 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1164
1165 show_im_dialog();
1166 }
1167 else if (event->keyval == 'z') {
1168 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1169
1170 #ifdef _WIN32
1171 XIconifyWindow(GDK_DISPLAY(),
1172 GDK_WINDOW_XWINDOW(gtkwin->window->window),
1173 ((_XPrivDisplay)GDK_DISPALY())->default_screen);
1174 #endif
1175 }
1176 else if (event->keyval == '[') {
1177 gaim_window_switch_conversation(win,
1178 gaim_conversation_get_index(conv) - 1);
1179
1180 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1181 }
1182 else if (event->keyval == ']') {
1183 gaim_window_switch_conversation(win,
1184 gaim_conversation_get_index(conv) + 1);
1185
1186 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1187 }
1188 else if (event->keyval == GDK_Tab) {
1189 move_next_tab(conv);
1190
1191 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1192
1193 return TRUE;
1194 }
1195 }
1196 else if ((event->keyval == GDK_Tab) &&
1197 gaim_conversation_get_type(conv) == GAIM_CONV_CHAT &&
1198 (chat_options & OPT_CHAT_TAB_COMPLETE)) {
1199
1200 tab_complete(conv);
1201
1202 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1203
1204 return TRUE;
1205 }
1206 else if ((event->state & GDK_MOD1_MASK) &&
1207 event->keyval > '0' && event->keyval <= '9') {
1208
1209 gaim_window_switch_conversation(win, event->keyval - '1');
1210
1211 g_signal_stop_emission_by_name(G_OBJECT(entry), "key_press_event");
1212 }
1213
1214 return FALSE;
1215 }
1216
1217 /*
1218 * NOTE:
1219 * This guy just kills a single right click from being propagated any
1220 * further. I have no idea *why* we need this, but we do ... It
1221 * prevents right clicks on the GtkTextView in a convo dialog from
1222 * going all the way down to the notebook. I suspect a bug in
1223 * GtkTextView, but I'm not ready to point any fingers yet.
1224 */
1225 static gboolean
1226 entry_stop_rclick_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
1227 {
1228 if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
1229 /* Right single click */
1230 g_signal_stop_emission_by_name(G_OBJECT(widget), "button_press_event");
1231
1232 return TRUE;
1233 }
1234
1235 return FALSE;
1236 }
1237
1238 static void
1239 menu_conv_sel_send_cb(GObject *m, struct aim_user *user)
1240 {
1241 struct gaim_window *win = g_object_get_data(m, "user_data");
1242 struct gaim_conversation *conv;
1243
1244 if (gaim_gtk_is_state_locked())
1245 return;
1246
1247 conv = gaim_window_get_active_conversation(win);
1248
1249 gaim_conversation_set_user(conv, user);
1250 }
1251
1252 static void
1253 insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *position,
1254 gchar *new_text, gint new_text_length, gpointer user_data)
1255 {
1256 struct gaim_conversation *conv = (struct gaim_conversation *)user_data;
1257
1258 if (conv == NULL)
1259 return;
1260
1261 if (misc_options & OPT_MISC_STEALTH_TYPING)
1262 return;
1263
1264 got_typing_keypress(conv, (gtk_text_iter_is_start(position) &&
1265 gtk_text_iter_is_end(position)));
1266 }
1267
1268 static void
1269 delete_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *start_pos,
1270 GtkTextIter *end_pos, gpointer user_data)
1271 {
1272 struct gaim_conversation *conv = (struct gaim_conversation *)user_data;
1273 struct gaim_im *im;
1274
1275 if (conv == NULL)
1276 return;
1277
1278 if (misc_options & OPT_MISC_STEALTH_TYPING)
1279 return;
1280
1281 im = GAIM_IM(conv);
1282
1283 if (gtk_text_iter_is_start(start_pos) && gtk_text_iter_is_end(end_pos)) {
1284
1285 /* We deleted all the text, so turn off typing. */
1286 if (gaim_im_get_type_again_timeout(im))
1287 gaim_im_stop_type_again_timeout(im);
1288
1289 /* XXX The (char *) should go away! Somebody add consts to stuff! */
1290 serv_send_typing(gaim_conversation_get_gc(conv),
1291 (char *)gaim_conversation_get_name(conv),
1292 NOT_TYPING);
1293 }
1294 else {
1295 /* We're deleting, but not all of it, so it counts as typing. */
1296 got_typing_keypress(conv, FALSE);
1297 }
1298 }
1299
1300 static void
1301 notebook_init_grab(struct gaim_gtk_window *gtkwin, GtkWidget *widget)
1302 {
1303 static GdkCursor *cursor = NULL;
1304
1305 gtkwin->in_drag = TRUE;
1306
1307 if (gtkwin->drag_leave_signal) {
1308 g_signal_handler_disconnect(G_OBJECT(widget),
1309 gtkwin->drag_leave_signal);
1310
1311 gtkwin->drag_leave_signal = 0;
1312 }
1313
1314 if (cursor == NULL)
1315 cursor = gdk_cursor_new(GDK_FLEUR);
1316
1317 /* Grab the pointer */
1318 gtk_grab_add(gtkwin->notebook);
1319 gdk_pointer_grab(gtkwin->notebook->window, FALSE,
1320 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
1321 NULL, cursor, GDK_CURRENT_TIME);
1322 }
1323
1324 static gboolean
1325 notebook_motion_cb(GtkWidget *widget, GdkEventButton *e,
1326 struct gaim_window *win)
1327 {
1328 struct gaim_gtk_window *gtkwin;
1329
1330 gtkwin = GAIM_GTK_WINDOW(win);
1331
1332 /*
1333 * Make sure the user moved the mouse far enough for the
1334 * drag to be initiated.
1335 */
1336 if (gtkwin->in_predrag) {
1337 if (e->x_root < gtkwin->drag_min_x ||
1338 e->x_root >= gtkwin->drag_max_x ||
1339 e->y_root < gtkwin->drag_min_y ||
1340 e->y_root >= gtkwin->drag_max_y) {
1341
1342 gtkwin->in_predrag = FALSE;
1343 notebook_init_grab(gtkwin, widget);
1344 }
1345 }
1346 else { /* Otherwise, draw the arrows. */
1347 struct gaim_window *dest_win;
1348 struct gaim_gtk_window *dest_gtkwin;
1349 GtkNotebook *dest_notebook;
1350 GtkWidget *tab, *last_vis_tab = NULL;
1351 gint nb_x, nb_y, page_num, i, last_vis_tab_loc = -1;
1352 gint arrow1_x, arrow1_y, arrow2_x, arrow2_y;
1353 gboolean horiz_tabs = FALSE, tab_found = FALSE;
1354 GList *l;
1355
1356 /* Get the window that the cursor is over. */
1357 dest_win = gaim_gtkwin_get_at_xy(e->x_root, e->y_root);
1358
1359 if (dest_win == NULL) {
1360 dnd_hints_hide_all();
1361
1362 return TRUE;
1363 }
1364
1365 dest_gtkwin = GAIM_GTK_WINDOW(dest_win);
1366
1367 dest_notebook = GTK_NOTEBOOK(dest_gtkwin->notebook);
1368
1369 gdk_window_get_origin(GTK_WIDGET(dest_notebook)->window, &nb_x, &nb_y);
1370
1371 arrow1_x = arrow2_x = nb_x;
1372 arrow1_y = arrow2_y = nb_y;
1373
1374 page_num = gaim_gtkconv_get_dest_tab_at_xy(dest_win,
1375 e->x_root, e->y_root);
1376
1377 if (gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_TOP ||
1378 gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_BOTTOM) {
1379
1380 horiz_tabs = TRUE;
1381 }
1382
1383 /* Find out where to put the arrows. */
1384 for (l = gaim_window_get_conversations(dest_win), i = 0;
1385 l != NULL;
1386 l = l->next, i++) {
1387
1388 struct gaim_conversation *conv = l->data;
1389
1390 tab = GAIM_GTK_CONVERSATION(conv)->tabby;
1391
1392 /*
1393 * If this is the correct tab, record the positions
1394 * for the arrows.
1395 */
1396 if (i == page_num) {
1397 if (horiz_tabs) {
1398 arrow1_x = arrow2_x = nb_x + tab->allocation.x;
1399 arrow1_y = nb_y + tab->allocation.y;
1400 arrow2_y = nb_y + tab->allocation.y +
1401 tab->allocation.height;
1402 }
1403 else {
1404 arrow1_x = nb_x + tab->allocation.x;
1405 arrow2_x = nb_x + tab->allocation.x +
1406 tab->allocation.width;
1407 arrow1_y = arrow2_y = nb_y + tab->allocation.y;
1408 }
1409
1410 tab_found = TRUE;
1411 break;
1412 }
1413 else { /* Keep track of the right-most tab that we see. */
1414 if (horiz_tabs && tab->allocation.x > last_vis_tab_loc) {
1415 last_vis_tab = tab;
1416 last_vis_tab_loc = tab->allocation.x;
1417 }
1418 else if (!horiz_tabs && tab->allocation.y > last_vis_tab_loc) {
1419 last_vis_tab = tab;
1420 last_vis_tab_loc = tab->allocation.y;
1421 }
1422 }
1423 }
1424
1425 /*
1426 * If we didn't find the tab, then we'll just place the
1427 * arrows to the right/bottom of the last visible tab.
1428 */
1429 if (!tab_found && last_vis_tab) {
1430 if (horiz_tabs) {
1431 arrow1_x = arrow2_x = nb_x + last_vis_tab->allocation.x +
1432 last_vis_tab->allocation.width;
1433 arrow1_y = nb_y + last_vis_tab->allocation.y;
1434 arrow2_y = nb_y + last_vis_tab->allocation.y +
1435 last_vis_tab->allocation.height;
1436 }
1437 else {
1438 arrow1_x = nb_x + last_vis_tab->allocation.x;
1439 arrow2_x = nb_x + last_vis_tab->allocation.x +
1440 last_vis_tab->allocation.width;
1441 arrow1_y = arrow2_y = nb_y + last_vis_tab->allocation.y +
1442 last_vis_tab->allocation.height;
1443 }
1444 }
1445
1446 if (horiz_tabs) {
1447 dnd_hints_show(HINT_ARROW_DOWN, arrow1_x, arrow1_y);
1448 dnd_hints_show(HINT_ARROW_UP, arrow2_x, arrow2_y);
1449 }
1450 else {
1451 dnd_hints_show(HINT_ARROW_RIGHT, arrow1_x, arrow1_y);
1452 dnd_hints_show(HINT_ARROW_LEFT, arrow2_x, arrow2_y);
1453 }
1454 }
1455
1456 return TRUE;
1457 }
1458
1459 static gboolean
1460 notebook_leave_cb(GtkWidget *widget, GdkEventCrossing *e,
1461 struct gaim_window *win)
1462 {
1463 struct gaim_gtk_window *gtkwin;
1464
1465 gtkwin = GAIM_GTK_WINDOW(win);
1466
1467 if (gtkwin->in_drag)
1468 return FALSE;
1469
1470 if (e->x_root < gtkwin->drag_min_x ||
1471 e->x_root >= gtkwin->drag_max_x ||
1472 e->y_root < gtkwin->drag_min_y ||
1473 e->y_root >= gtkwin->drag_max_y) {
1474
1475 gtkwin->in_predrag = FALSE;
1476 notebook_init_grab(gtkwin, widget);
1477 }
1478
1479 return TRUE;
1480 }
1481
1482 /*
1483 * THANK YOU GALEON!
1484 */
1485 static gboolean
1486 notebook_press_cb(GtkWidget *widget, GdkEventButton *e,
1487 struct gaim_window *win)
1488 {
1489 struct gaim_gtk_window *gtkwin;
1490 gint nb_x, nb_y, x_rel, y_rel;
1491 GList *l;
1492 int tab_clicked;
1493
1494 debug_printf("notebook_press_cb\n");
1495 if (e->button != 1 || e->type != GDK_BUTTON_PRESS)
1496 return FALSE;
1497
1498 gtkwin = GAIM_GTK_WINDOW(win);
1499
1500 debug_printf("notebook_press_cb: 1\n");
1501 if (gtkwin->in_drag) {
1502 debug_printf("Already in the middle of a window "
1503 "drag at tab_press_cb\n");
1504 return FALSE;
1505 }
1506
1507 /*
1508 * Make sure a tab was actually clicked. The arrow buttons
1509 * mess things up.
1510 */
1511 tab_clicked = gaim_gtkconv_get_tab_at_xy(win, e->x_root, e->y_root);
1512
1513 printf("tab_clicked == %d\n", tab_clicked);
1514
1515 if (tab_clicked == -1)
1516 return FALSE;
1517
1518 /*
1519 * Get the relative position of the press event, with regards to
1520 * the position of the notebook.
1521 */
1522 gdk_window_get_origin(gtkwin->notebook->window, &nb_x, &nb_y);
1523
1524 x_rel = e->x_root - nb_x;
1525 y_rel = e->y_root - nb_y;
1526
1527 /* Reset the min/max x/y */
1528 gtkwin->drag_min_x = 0;
1529 gtkwin->drag_min_y = 0;
1530 gtkwin->drag_max_x = 0;
1531 gtkwin->drag_max_y = 0;
1532
1533 /* Find out which tab was dragged. */
1534 for (l = gaim_window_get_conversations(win); l != NULL; l = l->next) {
1535 struct gaim_conversation *conv = l->data;
1536 GtkWidget *tab = GAIM_GTK_CONVERSATION(conv)->tabby;
1537
1538 debug_printf("Tab: %d\n", gaim_conversation_get_index(conv));
1539 if (!GTK_WIDGET_VISIBLE(tab))
1540 continue;
1541
1542 debug_printf("Tab Visible.\n");
1543 if (tab->allocation.x > x_rel || tab->allocation.y > y_rel)
1544 break;
1545
1546 /* Save the borders of the tab. */
1547 gtkwin->drag_min_x = tab->allocation.x + nb_x;
1548 gtkwin->drag_min_y = tab->allocation.y + nb_y;
1549 gtkwin->drag_max_x = tab->allocation.width + gtkwin->drag_min_x;
1550 gtkwin->drag_max_y = tab->allocation.height + gtkwin->drag_min_y;
1551 }
1552
1553 debug_printf("notebook_press_cb: 3\n");
1554
1555 /* Make sure the click occurred in the tab. */
1556 if (e->x_root < gtkwin->drag_min_x ||
1557 e->x_root >= gtkwin->drag_max_x ||
1558 e->y_root < gtkwin->drag_min_y ||
1559 e->y_root >= gtkwin->drag_max_y) {
1560
1561 debug_printf("Passing this down.\n");
1562 return FALSE;
1563 }
1564
1565 debug_printf("notebook_press_cb: 4\n");
1566 gtkwin->in_predrag = TRUE;
1567
1568 /* Connect the new motion signals. */
1569 gtkwin->drag_motion_signal =
1570 g_signal_connect(G_OBJECT(widget), "motion_notify_event",
1571 G_CALLBACK(notebook_motion_cb), win);
1572
1573 gtkwin->drag_leave_signal =
1574 g_signal_connect(G_OBJECT(widget), "leave_notify_event",
1575 G_CALLBACK(notebook_leave_cb), win);
1576
1577 debug_printf("notebook_press_cb: 5\n");
1578 return FALSE;
1579 }
1580
1581 static gboolean
1582 notebook_release_cb(GtkWidget *widget, GdkEventButton *e,
1583 struct gaim_window *win)
1584 {
1585 struct gaim_window *dest_win;
1586 struct gaim_gtk_window *gtkwin;
1587 struct gaim_gtk_window *dest_gtkwin;
1588 struct gaim_conversation *conv;
1589 GtkNotebook *dest_notebook;
1590 gint dest_page_num;
1591
1592 /*
1593 * Don't check to make sure that the event's window matches the
1594 * widget's, because we may be getting an event passed on from the
1595 * close button.
1596 */
1597 if (e->button != 1 && e->type != GDK_BUTTON_RELEASE)
1598 return FALSE;
1599
1600 if (gdk_pointer_is_grabbed()) {
1601 gdk_pointer_ungrab(GDK_CURRENT_TIME);
1602 gtk_grab_remove(widget);
1603 }
1604
1605 gtkwin = GAIM_GTK_WINDOW(win);
1606
1607 if (!gtkwin->in_predrag && !gtkwin->in_drag) {
1608 printf("Not in predrag.\n");
1609 return TRUE;
1610 }
1611
1612 /* Disconnect the motion signal. */
1613 if (gtkwin->drag_motion_signal) {
1614 g_signal_handler_disconnect(G_OBJECT(widget),
1615 gtkwin->drag_motion_signal);
1616
1617 gtkwin->drag_motion_signal = 0;
1618 }
1619
1620 /*
1621 * If we're in a pre-drag, we'll also need to disconnect the leave
1622 * signal.
1623 */
1624 if (gtkwin->in_predrag) {
1625 gtkwin->in_predrag = FALSE;
1626
1627 if (gtkwin->drag_leave_signal) {
1628 g_signal_handler_disconnect(G_OBJECT(widget),
1629 gtkwin->drag_leave_signal);
1630
1631 gtkwin->drag_leave_signal = 0;
1632 }
1633 }
1634
1635 /* If we're not in drag... */
1636 /* We're perfectly normal people! */
1637 if (!gtkwin->in_drag) {
1638 debug_printf("Not in drag.\n");
1639 return FALSE;
1640 }
1641
1642 gtkwin->in_drag = FALSE;
1643
1644 dnd_hints_hide_all();
1645
1646 dest_win = gaim_gtkwin_get_at_xy(e->x_root, e->y_root);
1647 dest_gtkwin = GAIM_GTK_WINDOW(dest_win);
1648
1649 conv = gaim_window_get_active_conversation(win);
1650
1651 if (dest_win == NULL) {
1652 if (gaim_window_get_conversation_count(win) < 2)
1653 return FALSE;
1654
1655 if (gaim_window_get_conversation_count(win) > 1) {
1656 /* Make a new window to stick this to. */
1657 struct gaim_window *new_win;
1658
1659 new_win = gaim_window_new();
1660 gaim_window_remove_conversation(win,
1661 gaim_conversation_get_index(conv));
1662 gaim_window_add_conversation(new_win, conv);
1663 gaim_window_show(new_win);
1664 }
1665
1666 return TRUE;
1667 }
1668
1669 /* Get the destination notebook. */
1670 dest_notebook = GTK_NOTEBOOK(gtkwin->notebook);
1671
1672 /* Get the destination page number. */
1673 dest_page_num = gaim_gtkconv_get_dest_tab_at_xy(dest_win,
1674 e->x_root, e->y_root);
1675
1676 if (win == dest_win) {
1677 gaim_window_move_conversation(win,
1678 gaim_conversation_get_index(conv), dest_page_num);
1679 }
1680 else {
1681 size_t pos;
1682
1683 gaim_window_remove_conversation(win,
1684 gaim_conversation_get_index(conv));
1685
1686 pos = gaim_window_add_conversation(dest_win, conv);
1687
1688 gaim_window_move_conversation(dest_win, pos, dest_page_num);
1689
1690 gaim_window_switch_conversation(dest_win, dest_page_num);
1691 }
1692
1693 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry);
1694
1695 debug_printf("release cb returning.\n");
1696 return TRUE;
1697 }
1698
1699 static void
1700 switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
1701 gpointer user_data)
1702 {
1703 struct gaim_window *win;
1704 struct gaim_conversation *conv;
1705 struct gaim_gtk_conversation *gtkconv;
1706 struct gaim_gtk_window *gtkwin;
1707 struct gaim_connection *gc;
1708
1709 if (gaim_gtk_is_state_locked())
1710 return;
1711
1712 debug_printf("Switching conversation\n");
1713
1714 win = (struct gaim_window *)user_data;
1715
1716 conv = gaim_window_get_conversation_at(win, page_num);
1717 gc = gaim_conversation_get_gc(conv);
1718 gtkwin = GAIM_GTK_WINDOW(win);
1719 gtkconv = GAIM_GTK_CONVERSATION(conv);
1720
1721 gaim_conversation_set_unseen(conv, GAIM_UNSEEN_NONE);
1722
1723 /* Update the menubar */
1724 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
1725 gtk_widget_set_sensitive(gtkwin->menu.view_history, TRUE);
1726 gtk_widget_set_sensitive(gtkwin->menu.insert_image,
1727 (gc && gc->prpl->options & OPT_PROTO_IM_IMAGE));
1728
1729 if (gtkwin->menu.send_as != NULL)
1730 update_send_as_selection(win);
1731 }
1732 else {
1733 gtk_widget_set_sensitive(gtkwin->menu.view_history, FALSE);
1734 gtk_widget_set_sensitive(gtkwin->menu.insert_image, FALSE);
1735
1736 if (gtkwin->menu.send_as != NULL)
1737 gtk_widget_hide(gtkwin->menu.send_as);
1738 }
1739
1740 gaim_gtk_set_state_lock(TRUE);
1741
1742 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkwin->menu.logging),
1743 gaim_conversation_is_logging(conv));
1744
1745 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkwin->menu.sounds),
1746 gtkconv->make_sound);
1747
1748 gaim_gtk_set_state_lock(FALSE);
1749
1750 gtk_widget_grab_focus(gtkconv->entry);
1751 }
1752
1753 /**************************************************************************
1754 * Utility functions
1755 **************************************************************************/
1756 static void
1757 do_bold(GtkWidget *bold, struct gaim_gtk_conversation *gtkconv)
1758 {
1759 if (gaim_gtk_is_state_locked())
1760 return;
1761
1762 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bold)))
1763 gaim_gtk_surround(gtkconv, "<B>", "</B>");
1764 else
1765 gaim_gtk_advance_past(gtkconv, "<B>", "</B>");
1766
1767 gtk_widget_grab_focus(gtkconv->entry);
1768 }
1769
1770 static void
1771 do_italic(GtkWidget *italic, struct gaim_gtk_conversation *gtkconv)
1772 {
1773 if (gaim_gtk_is_state_locked())
1774 return;
1775
1776 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(italic)))
1777 gaim_gtk_surround(gtkconv, "<I>", "</I>");
1778 else
1779 gaim_gtk_advance_past(gtkconv, "<I>", "</I>");
1780
1781 gtk_widget_grab_focus(gtkconv->entry);
1782 }
1783
1784 static void
1785 do_underline(GtkWidget *underline, struct gaim_gtk_conversation *gtkconv)
1786 {
1787 if (gaim_gtk_is_state_locked())
1788 return;
1789
1790 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(underline)))
1791 gaim_gtk_surround(gtkconv, "<U>", "</U>");
1792 else
1793 gaim_gtk_advance_past(gtkconv, "<U>", "</U>");
1794
1795 gtk_widget_grab_focus(gtkconv->entry);
1796 }
1797
1798 static void
1799 do_small(GtkWidget *small, struct gaim_gtk_conversation *gtkconv)
1800 {
1801 if (gaim_gtk_is_state_locked())
1802 return;
1803
1804 gaim_gtk_surround(gtkconv, "<FONT SIZE=\"1\">", "</FONT>");
1805
1806 gtk_widget_grab_focus(gtkconv->entry);
1807 }
1808
1809 static void
1810 do_normal(GtkWidget *small, struct gaim_gtk_conversation *gtkconv)
1811 {
1812 if (gaim_gtk_is_state_locked())
1813 return;
1814
1815 gaim_gtk_surround(gtkconv, "<FONT SIZE=\"3\">", "</FONT>");
1816
1817 gtk_widget_grab_focus(gtkconv->entry);
1818 }
1819
1820 static void
1821 do_big(GtkWidget *small, struct gaim_gtk_conversation *gtkconv)
1822 {
1823 if (gaim_gtk_is_state_locked())
1824 return;
1825
1826 gaim_gtk_surround(gtkconv, "<FONT SIZE=\"5\">", "</FONT>");
1827
1828 gtk_widget_grab_focus(gtkconv->entry);
1829 }
1830
1831 static void
1832 toggle_font(GtkWidget *font, struct gaim_conversation *conv)
1833 {
1834 struct gaim_gtk_conversation *gtkconv;
1835
1836 if (gaim_gtk_is_state_locked())
1837 return;
1838
1839 gtkconv = GAIM_GTK_CONVERSATION(conv);
1840
1841 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(font)))
1842 show_font_dialog(conv, font);
1843 else if (gtkconv->dialogs.fg_color != NULL)
1844 cancel_font(font, conv);
1845 else
1846 gaim_gtk_advance_past(gtkconv, "<FONT FACE>", "</FONT>");
1847 }
1848
1849 static void
1850 toggle_fg_color(GtkWidget *color, struct gaim_conversation *conv)
1851 {
1852 struct gaim_gtk_conversation *gtkconv;
1853
1854 if (gaim_gtk_is_state_locked())
1855 return;
1856
1857 gtkconv = GAIM_GTK_CONVERSATION(conv);
1858
1859 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color)))
1860 show_fgcolor_dialog(conv, color);
1861 else if (gtkconv->dialogs.fg_color != NULL)
1862 cancel_fgcolor(color, conv);
1863 else
1864 gaim_gtk_advance_past(gtkconv, "<FONT COLOR>", "</FONT>");
1865 }
1866
1867 static void
1868 toggle_bg_color(GtkWidget *color, struct gaim_conversation *conv)
1869 {
1870 struct gaim_gtk_conversation *gtkconv;
1871
1872 if (gaim_gtk_is_state_locked())
1873 return;
1874
1875 gtkconv = GAIM_GTK_CONVERSATION(conv);
1876
1877 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color)))
1878 show_bgcolor_dialog(conv, color);
1879 else if (gtkconv->dialogs.bg_color != NULL)
1880 cancel_bgcolor(color, conv);
1881 else
1882 gaim_gtk_advance_past(gtkconv, "<BODY BGCOLOR>", "</BODY>");
1883 }
1884
1885 static void
1886 check_everything(GtkTextBuffer *buffer)
1887 {
1888 struct gaim_conversation *conv;
1889 struct gaim_gtk_conversation *gtkconv;
1890
1891 conv = (struct gaim_conversation *)g_object_get_data(G_OBJECT(buffer),
1892 "user_data");
1893
1894 if (conv == NULL)
1895 return;
1896
1897 gtkconv = GAIM_GTK_CONVERSATION(conv);
1898
1899 /* CONV TODO */
1900 }
1901
1902 static void
1903 quiet_set(GtkWidget *tb, gboolean active)
1904 {
1905 gaim_gtk_set_state_lock(TRUE);
1906 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb), active);
1907 gaim_gtk_set_state_lock(FALSE);
1908 }
1909
1910 static void
1911 got_typing_keypress(struct gaim_conversation *conv, gboolean first)
1912 {
1913 struct gaim_im *im;
1914
1915 /*
1916 * We know we got something, so we at least have to make sure we don't
1917 * send TYPED any time soon.
1918 */
1919
1920 im = GAIM_IM(conv);
1921
1922 if (gaim_im_get_type_again_timeout(im))
1923 gaim_im_stop_type_again_timeout(im);
1924
1925 gaim_im_start_type_again_timeout(im);
1926
1927 if (first || (gaim_im_get_type_again(im) != 0 &&
1928 time(NULL) > gaim_im_get_type_again(im))) {
1929
1930 int timeout = serv_send_typing(gaim_conversation_get_gc(conv),
1931 (char *)gaim_conversation_get_name(conv),
1932 TYPING);
1933
1934 if (timeout)
1935 gaim_im_set_type_again(im, time(NULL) + timeout);
1936 else
1937 gaim_im_set_type_again(im, 0);
1938 }
1939 }
1940
1941 static void
1942 update_send_as_selection(struct gaim_window *win)
1943 {
1944 struct aim_user *user;
1945 struct gaim_conversation *conv;
1946 struct gaim_gtk_window *gtkwin;
1947 const char *username;
1948 GtkWidget *menu;
1949 GList *child;
1950
1951 conv = gaim_window_get_active_conversation(win);
1952
1953 if (conv == NULL)
1954 return;
1955
1956 user = gaim_conversation_get_user(conv);
1957 gtkwin = GAIM_GTK_WINDOW(win);
1958
1959 username = (user->gc == NULL ? user->username : user->gc->username);
1960
1961 gtk_widget_show(gtkwin->menu.send_as);
1962
1963 menu = gtk_menu_item_get_submenu(
1964 GTK_MENU_ITEM(gtkwin->menu.send_as));
1965
1966 gaim_gtk_set_state_lock(TRUE);
1967
1968 for (child = gtk_container_get_children(GTK_CONTAINER(menu));
1969 child != NULL;
1970 child = child->next) {
1971
1972 GtkWidget *item = child->data;
1973 GtkWidget *label;
1974 const char *title;
1975
1976 label = gtk_bin_get_child(GTK_BIN(item));
1977
1978 if (!GTK_IS_LABEL(label))
1979 continue;
1980
1981 title = gtk_label_get_text(GTK_LABEL(label));
1982
1983 if (!strcmp(title, username)) {
1984 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1985 break;
1986 }
1987 }
1988
1989 gaim_gtk_set_state_lock(FALSE);
1990 }
1991
1992 static void
1993 generate_send_as_items(struct gaim_window *win)
1994 {
1995 struct gaim_gtk_window *gtkwin;
1996 GtkWidget *menu;
1997 GtkWidget *menuitem;
1998 GSList *gcs;
1999 GList *convs;
2000 GSList *group = NULL;
2001 gboolean first_offline = TRUE;
2002 gboolean found_online = FALSE;
2003
2004 gtkwin = GAIM_GTK_WINDOW(win);
2005
2006 if (gtkwin->menu.send_as != NULL)
2007 gtk_widget_destroy(gtkwin->menu.send_as);
2008
2009 /* See if we have > 1 connection active. */
2010 if (g_slist_length(connections) < 2) {
2011 /* Now make sure we don't have any Offline entries. */
2012 gboolean found_offline = FALSE;
2013
2014 for (convs = gaim_get_conversations();
2015 convs != NULL;
2016 convs = convs->next) {
2017
2018 struct gaim_conversation *conv;
2019 struct aim_user *user;
2020
2021 conv = (struct gaim_conversation *)convs->data;
2022 user = gaim_conversation_get_user(conv);
2023
2024 if (user->gc == NULL) {
2025 found_offline = TRUE;
2026 break;
2027 }
2028 }
2029
2030 if (!found_offline) {
2031 gtkwin->menu.send_as = NULL;
2032 return;
2033 }
2034 }
2035
2036 /* Build the Send As menu */
2037 gtkwin->menu.send_as = gtk_menu_item_new_with_mnemonic(_("_Send As"));
2038 gtk_widget_show(gtkwin->menu.send_as);
2039
2040 menu = gtk_menu_new();
2041
2042 gtk_menu_shell_append(GTK_MENU_SHELL(gtkwin->menu.menubar),
2043 gtkwin->menu.send_as);
2044 gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtkwin->menu.send_as), menu);
2045
2046 gtk_widget_show(menu);
2047
2048 /* Fill it with entries. */
2049 for (gcs = connections; gcs != NULL; gcs = gcs->next) {
2050 struct gaim_connection *gc;
2051
2052 found_online = TRUE;
2053
2054 gc = (struct gaim_connection *)gcs->data;
2055
2056 menuitem = gtk_radio_menu_item_new_with_label(group, gc->username);
2057 group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem));
2058
2059 g_object_set_data(G_OBJECT(menuitem), "user_data", win);
2060
2061 g_signal_connect(G_OBJECT(menuitem), "activate",
2062 G_CALLBACK(menu_conv_sel_send_cb), gc->user);
2063
2064 gtk_widget_show(menuitem);
2065 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2066 }
2067
2068 /*
2069 * Fill it with any accounts that still has an open (yet disabled) window
2070 * (signed off accounts with a window open).
2071 */
2072 for (convs = gaim_get_conversations();
2073 convs != NULL;
2074 convs = convs->next) {
2075
2076 struct gaim_conversation *conv;
2077 struct aim_user *user;
2078
2079 conv = (struct gaim_conversation *)convs->data;
2080 user = gaim_conversation_get_user(conv);
2081
2082 if (user->gc == NULL) {
2083 if (first_offline && found_online) {
2084 menuitem = gtk_separator_menu_item_new();
2085 gtk_widget_show(menuitem);
2086 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2087
2088 first_offline = FALSE;
2089 }
2090
2091 printf("Adding '%s'\n", user->username);
2092
2093 menuitem = gtk_radio_menu_item_new_with_label(group,
2094 user->username);
2095 group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(menuitem));
2096
2097 gtk_widget_set_sensitive(menuitem, FALSE);
2098
2099 gtk_widget_show(menuitem);
2100 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2101 }
2102 }
2103
2104 gtk_widget_show(gtkwin->menu.send_as);
2105 update_send_as_selection(win);
2106 }
2107
2108 static GList *
2109 generate_invite_user_names(struct gaim_connection *gc)
2110 {
2111 GSList *grp;
2112 GSList *bl;
2113 struct group *g;
2114 struct buddy *buddy;
2115 static GList *tmp = NULL;
2116
2117 if (tmp)
2118 g_list_free(tmp);
2119
2120 tmp = g_list_append(NULL, "");
2121
2122 if (gc != NULL) {
2123 for (grp = groups; grp != NULL; grp = grp->next) {
2124 g = (struct group *)grp->data;
2125
2126 for (bl = g->members; bl != NULL; bl = bl->next) {
2127 buddy = (struct buddy *)bl->data;
2128
2129 if (buddy->present)
2130 tmp = g_list_append(tmp, buddy->name);
2131 }
2132 }
2133 }
2134
2135 return tmp;
2136 }
2137
2138 static void
2139 add_chat_buddy_common(struct gaim_conversation *conv, const char *name,
2140 int pos)
2141 {
2142 struct gaim_gtk_conversation *gtkconv;
2143 struct gaim_gtk_chat_pane *gtkchat;
2144 struct gaim_chat *chat;
2145 GtkTreeIter iter;
2146 GtkListStore *ls;
2147
2148 chat = GAIM_CHAT(conv);
2149 gtkconv = GAIM_GTK_CONVERSATION(conv);
2150 gtkchat = gtkconv->u.chat;
2151
2152 ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)));
2153
2154 gtk_list_store_append(ls, &iter);
2155 gtk_list_store_set(ls, &iter, 0,
2156 (gaim_chat_is_user_ignored(chat, name) ? "X" : " "),
2157 1, name, -1);
2158 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls), 1,
2159 GTK_SORT_ASCENDING);
2160 }
2161
2162 static void
2163 tab_complete(struct gaim_conversation *conv)
2164 {
2165 struct gaim_gtk_conversation *gtkconv;
2166 struct gaim_chat *chat;
2167 GtkTextIter cursor, word_start, start_buffer;
2168 int start;
2169 int most_matched = -1;
2170 char *entered, *partial = NULL;
2171 char *text;
2172 GList *matches = NULL;
2173 GList *nicks = NULL;
2174
2175 gtkconv = GAIM_GTK_CONVERSATION(conv);
2176 chat = GAIM_CHAT(conv);
2177
2178 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer);
2179 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
2180 gtk_text_buffer_get_insert(gtkconv->entry_buffer));
2181
2182 word_start = cursor;
2183
2184 /* if there's nothing there just return */
2185 if (!gtk_text_iter_compare(&cursor, &start_buffer))
2186 return;
2187
2188 text = gtk_text_buffer_get_text(gtkconv->entry_buffer, &start_buffer,
2189 &cursor, FALSE);
2190
2191 /* if we're at the end of ": " we need to move back 2 spaces */
2192 start = strlen(text) - 1;
2193
2194 if (strlen(text) >= 2 && !strncmp(&text[start-1], ": ", 2))
2195 gtk_text_iter_backward_chars(&word_start, 2);
2196
2197 /* find the start of the word that we're tabbing */
2198 while (start >= 0 && text[start] != ' ') {
2199 gtk_text_iter_backward_char(&word_start);
2200 start--;
2201 }
2202
2203 g_free(text);
2204
2205 entered = gtk_text_buffer_get_text(gtkconv->entry_buffer, &word_start,
2206 &cursor, FALSE);
2207
2208 if (chat_options & OPT_CHAT_OLD_STYLE_TAB) {
2209 if (strlen(entered) >= 2 &&
2210 !strncmp(": ", entered + strlen(entered) - 2, 2)) {
2211
2212 entered[strlen(entered) - 2] = 0;
2213 }
2214 }
2215
2216 if (!strlen(entered)) {
2217 g_free(entered);
2218 return;
2219 }
2220
2221 for (nicks = gaim_chat_get_users(chat);
2222 nicks != NULL;
2223 nicks = nicks->next) {
2224
2225 char *nick = nicks->data;
2226 /* this checks to see if the current nick could be a completion */
2227 if (g_strncasecmp(nick, entered, strlen(entered))) {
2228 if (nick[0] != '+' && nick[0] != '@')
2229 continue;
2230
2231 if (g_strncasecmp(nick + 1, entered, strlen(entered))) {
2232 if (nick[0] != '@' || nick[1] != '+')
2233 continue;
2234
2235 if (g_strncasecmp(nick + 2, entered, strlen(entered)))
2236 continue;
2237 else
2238 nick += 2;
2239 }
2240 else
2241 nick++;
2242 }
2243
2244 /* if we're here, it's a possible completion */
2245
2246 /* if we're doing old-style, just fill in the completion */
2247 if (chat_options & OPT_CHAT_OLD_STYLE_TAB) {
2248 gtk_text_buffer_delete(gtkconv->entry_buffer,
2249 &word_start, &cursor);
2250
2251 if (strlen(nick) == strlen(entered)) {
2252 nicks = (nicks->next
2253 ? nicks->next
2254 : gaim_chat_get_users(chat));
2255
2256 nick = nicks->data;
2257
2258 if (*nick == '@') nick++;
2259 if (*nick == '+') nick++;
2260 }
2261
2262 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer,
2263 &start_buffer);
2264 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
2265 gtk_text_buffer_get_insert(gtkconv->entry_buffer));
2266
2267 if (!gtk_text_iter_compare(&cursor, &start_buffer)) {
2268 char *tmp = g_strdup_printf("%s: ", nick);
2269 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer,
2270 tmp, -1);
2271 g_free(tmp);
2272 }
2273 else
2274 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer,
2275 nick, -1);
2276
2277 g_free(entered);
2278
2279 return;
2280 }
2281
2282 /* we're only here if we're doing new style */
2283 if (most_matched == -1) {
2284 /*
2285 * this will only get called once, since from now
2286 * on most_matched is >= 0
2287 */
2288 most_matched = strlen(nick);
2289 partial = g_strdup(nick);
2290 }
2291 else if (most_matched) {
2292 while (g_strncasecmp(nick, partial, most_matched))
2293 most_matched--;
2294
2295 partial[most_matched] = 0;
2296 }
2297
2298 matches = g_list_append(matches, nick);
2299 }
2300
2301 /* we're only here if we're doing new style */
2302
2303 /* if there weren't any matches, return */
2304 if (!matches) {
2305 /* if matches isn't set partials won't be either */
2306 g_free(entered);
2307 return;
2308 }
2309
2310 gtk_text_buffer_delete(gtkconv->entry_buffer, &word_start, &cursor);
2311
2312 if (!matches->next) {
2313 /* there was only one match. fill it in. */
2314 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer);
2315 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor,
2316 gtk_text_buffer_get_insert(gtkconv->entry_buffer));
2317
2318 if (!gtk_text_iter_compare(&cursor, &start_buffer)) {
2319 char *tmp = g_strdup_printf("%s: ", (char *)matches->data);
2320 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, tmp, -1);
2321 g_free(tmp);
2322 }
2323 else
2324 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer,
2325 matches->data, -1);
2326
2327 matches = g_list_remove(matches, matches->data);
2328 }
2329 else {
2330 /*
2331 * there were lots of matches, fill in as much as possible
2332 * and display all of them
2333 */
2334 char *addthis = g_malloc0(1);
2335
2336 while (matches) {
2337 char *tmp = addthis;
2338 addthis = g_strconcat(tmp, matches->data, " ", NULL);
2339 g_free(tmp);
2340 matches = g_list_remove(matches, matches->data);
2341 }
2342
2343 gaim_conversation_write(conv, NULL, addthis, -1, WFLAG_NOLOG,
2344 time(NULL));
2345 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, partial, -1);
2346 g_free(addthis);
2347 }
2348
2349 g_free(entered);
2350 g_free(partial);
2351 }
2352
2353 static gboolean
2354 meify(char *message, size_t len)
2355 {
2356 /*
2357 * Read /me-ify: If the message (post-HTML) starts with /me,
2358 * remove the "/me " part of it (including that space) and return TRUE.
2359 */
2360 char *c;
2361 gboolean inside_html = 0;
2362
2363 if (message == NULL)
2364 return FALSE; /* Umm.. this would be very bad if this happens. */
2365
2366 if (len == -1)
2367 len = strlen(message);
2368
2369 for (c = message; *c != '\0'; c++, len--) {
2370 if (inside_html) {
2371 if (*c == '>')
2372 inside_html = FALSE;
2373 }
2374 else {
2375 if (*c == '<')
2376 inside_html = TRUE;
2377 else
2378 break;
2379 }
2380 }
2381
2382 if (*c != '\0' && !g_strncasecmp(c, "/me ", 4)) {
2383 memmove(c, c + 4, len - 3);
2384
2385 return TRUE;
2386 }
2387
2388 return FALSE;
2389 }
2390
2391 static GtkItemFactoryEntry menu_items[] =
2392 {
2393 /* Conversation menu */
2394 { "/_Conversation", NULL, NULL, 0, "<Branch>" },
2395 { "/Conversation/_Save As...", NULL, menu_save_as_cb, 0,
2396 "<StockItem>", GTK_STOCK_SAVE_AS },
2397 { "/Conversation/View _History...", NULL, menu_view_history_cb, 0, NULL },
2398 { "/Conversation/sep1", NULL, NULL, 0, "<Separator>" },
2399 { "/Conversation/Insert _URL...", NULL, menu_insert_link_cb, 0,
2400 "<StockItem>", GAIM_STOCK_LINK },
2401 { "/Conversation/Insert _Image...", NULL, menu_insert_image_cb, 0,
2402 "<StockItem>", GAIM_STOCK_IMAGE },
2403 { "/Conversation/sep2", NULL, NULL, 0, "<Separator>" },
2404 { "/Conversation/_Close", NULL, menu_close_conv_cb, 0,
2405 "<StockItem>", GTK_STOCK_CLOSE },
2406
2407 /* Options */
2408 { "/_Options", NULL, NULL, 0, "<Branch>" },
2409 { "/Options/Enable _Logging", NULL, menu_logging_cb, 0, "<CheckItem>" },
2410 { "/Options/Enable _Sounds", NULL, menu_sounds_cb, 0, "<CheckItem>" },
2411 };
2412
2413 static const int menu_item_count =
2414 sizeof(menu_items) / sizeof(*menu_items);
2415
2416 static GtkWidget *
2417 setup_menubar(struct gaim_window *win)
2418 {
2419 struct gaim_gtk_window *gtkwin;
2420 GtkWidget *hb;
2421 GtkItemFactory *item_factory;
2422
2423 gtkwin = GAIM_GTK_WINDOW(win);
2424
2425 /* Create the handle box. */
2426 hb = gtk_handle_box_new();
2427
2428 item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", NULL);
2429
2430 gtk_item_factory_create_items(item_factory, menu_item_count,
2431 menu_items, win);
2432
2433 gtkwin->menu.menubar = gtk_item_factory_get_widget(item_factory, "<main>");
2434 gtkwin->menu.view_history = gtk_item_factory_get_widget(item_factory,
2435 "/Conversation/View History...");
2436 gtkwin->menu.insert_link = gtk_item_factory_get_widget(item_factory,
2437 "/Conversation/Insert URL...");
2438 gtkwin->menu.insert_image = gtk_item_factory_get_widget(item_factory,
2439 "/Conversation/Insert Image...");
2440 gtkwin->menu.logging = gtk_item_factory_get_widget(item_factory,
2441 "/Options/Enable Logging");
2442 gtkwin->menu.sounds = gtk_item_factory_get_widget(item_factory,
2443 "/Options/Enable Sounds");
2444
2445 generate_send_as_items(win);
2446
2447 gtk_container_add(GTK_CONTAINER(hb), gtkwin->menu.menubar);
2448
2449 gtk_widget_show(gtkwin->menu.menubar);
2450 gtk_widget_show(hb);
2451
2452 return hb;
2453 }
2454
2455 static void
2456 setup_im_buttons(struct gaim_conversation *conv, GtkWidget *parent)
2457 {
2458 struct gaim_connection *gc;
2459 struct gaim_gtk_conversation *gtkconv;
2460 struct gaim_gtk_im_pane *gtkim;
2461 struct gaim_gtk_window *gtkwin;
2462 GaimConversationType type = GAIM_CONV_IM;
2463
2464 gtkconv = GAIM_GTK_CONVERSATION(conv);
2465 gtkim = gtkconv->u.im;
2466 gtkwin = GAIM_GTK_WINDOW(gaim_conversation_get_window(conv));
2467 gc = gaim_conversation_get_gc(conv);
2468
2469 /* From right to left... */
2470
2471 /* Send button */
2472 gtkconv->send = gaim_gtk_change_text(_("Send"), gtkconv->send,
2473 GAIM_STOCK_SEND, type);
2474 gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->send, _("Send"), NULL);
2475
2476 gtk_box_pack_end(GTK_BOX(parent), gtkconv->send, FALSE, FALSE, 0);
2477
2478 /* Separator */
2479 if (gtkim->sep2 != NULL)
2480 gtk_widget_destroy(gtkim->sep2);
2481
2482 gtkim->sep2 = gtk_vseparator_new();
2483 gtk_box_pack_end(GTK_BOX(parent), gtkim->sep2, FALSE, TRUE, 0);
2484 gtk_widget_show(gtkim->sep2);
2485
2486 /* Now, um, just kind of all over the place. Huh? */
2487
2488 /* Add button */
2489 if (find_buddy(gaim_conversation_get_gc(conv)->user,
2490 gaim_conversation_get_name(conv)) == NULL) {
2491 gtkim->add = gaim_gtk_change_text(_("Add"), gtkim->add,
2492 GTK_STOCK_ADD, type);
2493 gtk_tooltips_set_tip(gtkconv->tooltips, gtkim->add,
2494 _("Add buddy"), NULL);
2495 }
2496 else {
2497 gtkim->add = gaim_gtk_change_text(_("Remove"), gtkim->add,
2498 GTK_STOCK_REMOVE, type);
2499 gtk_tooltips_set_tip(gtkconv->tooltips, gtkim->add,
2500 _("Remove buddy"), NULL);
2501 }
2502
2503 gtk_box_pack_start(GTK_BOX(parent), gtkim->add,
2504 FALSE, FALSE, 0);
2505
2506 /* Warn button */
2507 gtkim->warn = gaim_gtk_change_text(_("Warn"), gtkim->warn,
2508 GAIM_STOCK_WARN, type);
2509 gtk_box_pack_start(GTK_BOX(parent), gtkim->warn, FALSE, FALSE, 0);
2510 gtk_tooltips_set_tip(gtkconv->tooltips, gtkim->add,
2511 _("Remove buddy"), NULL);
2512
2513 /* Info button */
2514 gtkconv->info = gaim_gtk_change_text(_("Info"), gtkconv->info,
2515 GAIM_STOCK_INFO, type);
2516 gtk_box_pack_start(GTK_BOX(parent), gtkconv->info, FALSE, FALSE, 0);
2517 gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->info,
2518 _("Get user information"), NULL);
2519
2520 /* Block button */
2521 gtkim->block = gaim_gtk_change_text(_("Block"), gtkim->block,
2522 GAIM_STOCK_BLOCK, type);
2523 gtk_box_pack_start(GTK_BOX(parent), gtkim->block, FALSE, FALSE, 0);
2524 gtk_tooltips_set_tip(gtkconv->tooltips, gtkim->block,
2525 _("Block user"), NULL);
2526
2527 gtk_button_set_relief(GTK_BUTTON(gtkconv->info), GTK_RELIEF_NONE);
2528 gtk_button_set_relief(GTK_BUTTON(gtkim->add), GTK_RELIEF_NONE);
2529 gtk_button_set_relief(GTK_BUTTON(gtkim->warn), GTK_RELIEF_NONE);
2530 gtk_button_set_relief(GTK_BUTTON(gtkconv->send), GTK_RELIEF_NONE);
2531 gtk_button_set_relief(GTK_BUTTON(gtkim->block), GTK_RELIEF_NONE);
2532
2533 gtk_size_group_add_widget(gtkconv->sg, gtkconv->info);
2534 gtk_size_group_add_widget(gtkconv->sg, gtkim->add);
2535 gtk_size_group_add_widget(gtkconv->sg, gtkim->warn);
2536 gtk_size_group_add_widget(gtkconv->sg, gtkconv->send);
2537 gtk_size_group_add_widget(gtkconv->sg, gtkim->block);
2538
2539 gtk_box_reorder_child(GTK_BOX(parent), gtkim->warn, 1);
2540 gtk_box_reorder_child(GTK_BOX(parent), gtkim->block, 2);
2541 gtk_box_reorder_child(GTK_BOX(parent), gtkconv->info, 4);
2542
2543 gaim_gtkconv_update_buttons_by_protocol(conv);
2544
2545 g_signal_connect(G_OBJECT(gtkconv->send), "clicked",
2546 G_CALLBACK(send_cb), conv);
2547 g_signal_connect(G_OBJECT(gtkconv->info), "clicked",
2548 G_CALLBACK(info_cb), conv);
2549 g_signal_connect(G_OBJECT(gtkim->warn), "clicked",
2550 G_CALLBACK(warn_cb), conv);
2551 g_signal_connect(G_OBJECT(gtkim->block), "clicked",
2552 G_CALLBACK(block_cb), conv);
2553 }
2554
2555 static void
2556 setup_chat_buttons(struct gaim_conversation *conv, GtkWidget *parent)
2557 {
2558 struct gaim_connection *gc;
2559 struct gaim_gtk_conversation *gtkconv;
2560 struct gaim_gtk_chat_pane *gtkchat;
2561 struct gaim_gtk_window *gtkwin;
2562 GtkWidget *sep;
2563
2564 gtkconv = GAIM_GTK_CONVERSATION(conv);
2565 gtkchat = gtkconv->u.chat;
2566 gtkwin = GAIM_GTK_WINDOW(gaim_conversation_get_window(conv));
2567 gc = gaim_conversation_get_gc(conv);
2568
2569 /* Send button */
2570 gtkconv->send = gaim_gtk_change_text(_("Send"), gtkconv->send,
2571 GAIM_STOCK_SEND, GAIM_CONV_CHAT);
2572 gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->send, _("Send"), NULL);
2573
2574 gtk_box_pack_end(GTK_BOX(parent), gtkconv->send, FALSE, FALSE, 0);
2575
2576 /* Separator */
2577 sep = gtk_vseparator_new();
2578 gtk_box_pack_end(GTK_BOX(parent), sep, FALSE, TRUE, 0);
2579 gtk_widget_show(sep);
2580
2581 /* Invite */
2582 gtkchat->invite = gaim_gtk_change_text(_("Invite"), gtkchat->invite,
2583 GAIM_STOCK_INVITE, GAIM_CONV_CHAT);
2584 gtk_tooltips_set_tip(gtkconv->tooltips, gtkchat->invite,
2585 _("Invite a user"), NULL);
2586 gtk_box_pack_end(GTK_BOX(parent), gtkchat->invite, FALSE, FALSE, 0);
2587
2588 /* Set the relief on these. */
2589 gtk_button_set_relief(GTK_BUTTON(gtkchat->invite), GTK_RELIEF_NONE);
2590 gtk_button_set_relief(GTK_BUTTON(gtkconv->send), GTK_RELIEF_NONE);
2591
2592 /* Callbacks */
2593 g_signal_connect(G_OBJECT(gtkconv->send), "clicked",
2594 G_CALLBACK(send_cb), conv);
2595 g_signal_connect(G_OBJECT(gtkchat->invite), "clicked",
2596 G_CALLBACK(invite_cb), conv);
2597 }
2598
2599 static GtkWidget *
2600 build_conv_toolbar(struct gaim_conversation *conv)
2601 {
2602 struct gaim_gtk_conversation *gtkconv;
2603 GtkWidget *vbox;
2604 GtkWidget *hbox;
2605 GtkWidget *button;
2606 GtkWidget *sep;
2607 GtkSizeGroup *sg;
2608
2609 gtkconv = GAIM_GTK_CONVERSATION(conv);
2610
2611 sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
2612
2613 vbox = gtk_vbox_new(FALSE, 0);
2614 sep = gtk_hseparator_new();
2615 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
2616
2617 hbox = gtk_hbox_new(FALSE, 5);
2618 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2619
2620 /* Bold */
2621 button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD);
2622 gtk_size_group_add_widget(sg, button);
2623 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2624 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Bold"), NULL);
2625
2626 g_signal_connect(G_OBJECT(button), "clicked",
2627 G_CALLBACK(do_bold), gtkconv);
2628
2629 gtkconv->toolbar.bold = button;
2630
2631 /* Italic */
2632 button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC);
2633 gtk_size_group_add_widget(sg, button);
2634 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2635 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Italic"), NULL);
2636
2637 g_signal_connect(G_OBJECT(button), "clicked",
2638 G_CALLBACK(do_italic), gtkconv);
2639
2640 gtkconv->toolbar.italic = button;
2641
2642 /* Underline */
2643 button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE);
2644 gtk_size_group_add_widget(sg, button);
2645 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2646 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Underline"), NULL);
2647
2648 g_signal_connect(G_OBJECT(button), "clicked",
2649 G_CALLBACK(do_underline), gtkconv);
2650
2651 gtkconv->toolbar.underline = button;
2652
2653 /* Sep */
2654 sep = gtk_vseparator_new();
2655 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
2656
2657 /* Increase font size */
2658 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_BIGGER);
2659 gtk_size_group_add_widget(sg, button);
2660 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2661 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2662 _("Larger font size"), NULL);
2663
2664 g_signal_connect(G_OBJECT(button), "clicked",
2665 G_CALLBACK(do_big), gtkconv);
2666
2667 /* Normal font size */
2668 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_NORMAL);
2669 gtk_size_group_add_widget(sg, button);
2670 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2671 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2672 _("Normal font size"), NULL);
2673
2674 g_signal_connect(G_OBJECT(button), "clicked",
2675 G_CALLBACK(do_normal), gtkconv);
2676
2677 gtkconv->toolbar.normal_size = button;
2678
2679 /* Decrease font size */
2680 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_SMALLER);
2681 gtk_size_group_add_widget(sg, button);
2682 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2683 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2684 _("Smaller font size"), NULL);
2685
2686 g_signal_connect(G_OBJECT(button), "clicked",
2687 G_CALLBACK(do_small), gtkconv);
2688
2689 /* Sep */
2690 sep = gtk_vseparator_new();
2691 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
2692
2693 /* Foreground Color */
2694 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_FGCOLOR);
2695 gtk_size_group_add_widget(sg, button);
2696 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2697 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2698 _("Foreground font color"), NULL);
2699
2700 g_signal_connect(G_OBJECT(button), "clicked",
2701 G_CALLBACK(toggle_fg_color), conv);
2702
2703 gtkconv->toolbar.fgcolor = button;
2704
2705 /* Background Color */
2706 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_BGCOLOR);
2707 gtk_size_group_add_widget(sg, button);
2708 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2709 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2710 _("Background color"), NULL);
2711
2712 g_signal_connect(G_OBJECT(button), "clicked",
2713 G_CALLBACK(toggle_bg_color), conv);
2714
2715 gtkconv->toolbar.bgcolor = button;
2716
2717 /* Sep */
2718 sep = gtk_vseparator_new();
2719 gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
2720
2721 /* Insert IM Image */
2722 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_IMAGE);
2723 gtk_size_group_add_widget(sg, button);
2724 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2725 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Insert image"), NULL);
2726
2727 g_signal_connect(G_OBJECT(button), "clicked",
2728 G_CALLBACK(insert_image_cb), conv);
2729
2730 gtkconv->toolbar.image = button;
2731
2732 /* Insert Link */
2733 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_LINK);
2734 gtk_size_group_add_widget(sg, button);
2735 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2736 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Insert link"), NULL);
2737
2738 g_signal_connect(G_OBJECT(button), "clicked",
2739 G_CALLBACK(insert_link_cb), conv);
2740
2741 gtkconv->toolbar.link = button;
2742
2743 /* Insert Smiley */
2744 button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_SMILEY);
2745 gtk_size_group_add_widget(sg, button);
2746 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
2747 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Insert smiley"), NULL);
2748
2749 g_signal_connect(G_OBJECT(button), "clicked",
2750 G_CALLBACK(insert_smiley_cb), conv);
2751
2752 gtkconv->toolbar.smiley = button;
2753
2754
2755 sep = gtk_hseparator_new();
2756 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
2757
2758 gtk_widget_show_all(vbox);
2759
2760 return vbox;
2761 }
2762
2763 static GtkWidget *
2764 setup_chat_pane(struct gaim_conversation *conv)
2765 {
2766 struct gaim_gtk_conversation *gtkconv;
2767 struct gaim_gtk_chat_pane *gtkchat;
2768 struct gaim_connection *gc;
2769 GtkWidget *vpaned, *hpaned;
2770 GtkWidget *vbox, *hbox;
2771 GtkWidget *lbox, *bbox;
2772 GtkWidget *label;
2773 GtkWidget *sw2;
2774 GtkWidget *list;
2775 GtkWidget *button;
2776 GtkWidget *frame;
2777 GtkListStore *ls;
2778 GtkCellRenderer *rend;
2779 GtkTreeViewColumn *col;
2780
2781 gtkconv = GAIM_GTK_CONVERSATION(conv);
2782 gtkchat = gtkconv->u.chat;
2783 gc = gaim_conversation_get_gc(conv);
2784
2785 /* Setup the outer pane. */
2786 vpaned = gtk_vpaned_new();
2787 gtk_paned_set_gutter_size(GTK_PANED(vpaned), 15);
2788 gtk_widget_show(vpaned);
2789
2790 /* Setup the top part of the pane. */
2791 vbox = gtk_vbox_new(FALSE, 5);
2792 gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, FALSE);
2793 gtk_widget_show(vbox);
2794
2795 if (gc->prpl->options & OPT_PROTO_CHAT_TOPIC)
2796 {
2797 hbox = gtk_hbox_new(FALSE, 0);
2798 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2799 gtk_widget_show(hbox);
2800
2801 label = gtk_label_new(_("Topic:"));
2802 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
2803 gtk_widget_show(label);
2804
2805 gtkchat->topic_text = gtk_entry_new();
2806 gtk_entry_set_editable(GTK_ENTRY(gtkchat->topic_text), FALSE);
2807 gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 5);
2808 gtk_widget_show(gtkchat->topic_text);
2809 }
2810
2811 /* Setup the horizontal pane. */
2812 hpaned = gtk_hpaned_new();
2813 gtk_paned_set_gutter_size(GTK_PANED(hpaned), 15);
2814 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 5);
2815 gtk_widget_show(hpaned);
2816
2817 /* Setup the scrolled window to put gtkimhtml in. */
2818 gtkconv->sw = gtk_scrolled_window_new(NULL, NULL);
2819 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkconv->sw),
2820 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
2821 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkconv->sw),
2822 GTK_SHADOW_IN);
2823 gtk_paned_pack1(GTK_PANED(hpaned), gtkconv->sw, TRUE, TRUE);
2824
2825 gtk_widget_set_size_request(gtkconv->sw,
2826 buddy_chat_size.width, buddy_chat_size.height);
2827 gtk_widget_show(gtkconv->sw);
2828
2829 /* Setup gtkihmtml. */
2830 gtkconv->imhtml = gtk_imhtml_new(NULL, NULL);
2831 gtk_container_add(GTK_CONTAINER(gtkconv->sw), gtkconv->imhtml);
2832
2833 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
2834 (convo_options & OPT_CONVO_SHOW_TIME));
2835
2836 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
2837 G_CALLBACK(entry_stop_rclick_cb), NULL);
2838
2839 gaim_setup_imhtml(gtkconv->imhtml);
2840
2841 gtk_widget_show(gtkconv->imhtml);
2842
2843 /* Build the right pane. */
2844 lbox = gtk_vbox_new(FALSE, 5);
2845 gtk_paned_pack2(GTK_PANED(hpaned), lbox, TRUE, TRUE);
2846 gtk_widget_show(lbox);
2847
2848 /* Setup the label telling how many people are in the room. */
2849 gtkchat->count = gtk_label_new(_("0 people in room"));
2850 gtk_box_pack_start(GTK_BOX(lbox), gtkchat->count, FALSE, FALSE, 0);
2851 gtk_widget_show(gtkchat->count);
2852
2853 /* Setup the list of users. */
2854 sw2 = gtk_scrolled_window_new(NULL, NULL);
2855 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw2),
2856 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2857 gtk_box_pack_start(GTK_BOX(lbox), sw2, TRUE, TRUE, 0);
2858 gtk_widget_show(sw2);
2859
2860 ls = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
2861 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls), 1,
2862 GTK_SORT_ASCENDING);
2863
2864 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls));
2865
2866 rend = gtk_cell_renderer_text_new();
2867 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
2868 "text", 0, NULL);
2869 gtk_tree_view_column_set_clickable(GTK_TREE_VIEW_COLUMN(col), TRUE);
2870
2871 g_signal_connect(G_OBJECT(list), "button_press_event",
2872 G_CALLBACK(right_click_chat_cb), conv);
2873
2874 gtk_tree_view_append_column(GTK_TREE_VIEW(list), col);
2875
2876 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
2877 "text", 1, NULL);
2878 gtk_tree_view_column_set_clickable(GTK_TREE_VIEW_COLUMN(col), TRUE);
2879
2880 #if 0
2881 g_signal_connect(G_OBJECT(list), "button_press_event",
2882 G_CALLBACK(right_click_chat), conv);
2883 #endif
2884
2885 gtk_tree_view_append_column(GTK_TREE_VIEW(list), col);
2886
2887 gtk_widget_set_size_request(list, 150, -1);
2888
2889 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2890 gtk_widget_show(list);
2891
2892 gtkchat->list = list;
2893
2894 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw2), list);
2895
2896 /* Setup the user list toolbar. */
2897 bbox = gtk_hbox_new(TRUE, 5);
2898 gtk_box_pack_start(GTK_BOX(lbox), bbox, FALSE, FALSE, 0);
2899 gtk_widget_show(bbox);
2900
2901 /* IM */
2902 button = gaim_pixbuf_button_from_stock(NULL, GTK_STOCK_REDO,
2903 GAIM_BUTTON_VERTICAL);
2904 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
2905 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2906 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("IM"), NULL);
2907 g_signal_connect(G_OBJECT(button), "clicked",
2908 G_CALLBACK(im_cb), conv);
2909
2910 gtk_widget_show(button);
2911
2912 /* Ignore */
2913 button = gaim_pixbuf_button_from_stock(NULL, GAIM_STOCK_IGNORE,
2914 GAIM_BUTTON_VERTICAL);
2915 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
2916 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2917 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("Ignore user"), NULL);
2918 g_signal_connect(G_OBJECT(button), "clicked",
2919 G_CALLBACK(ignore_cb), conv);
2920 gtk_widget_show(button);
2921
2922 /* Info */
2923 button = gaim_pixbuf_button_from_stock(NULL, GAIM_STOCK_INFO,
2924 GAIM_BUTTON_VERTICAL);
2925 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
2926 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
2927 gtk_tooltips_set_tip(gtkconv->tooltips, button,
2928 _("Get user information"), NULL);
2929 g_signal_connect(G_OBJECT(button), "clicked",
2930 G_CALLBACK(info_cb), conv);
2931
2932 gtk_widget_show(button);
2933
2934 gtkconv->info = button;
2935
2936 /* Build the toolbar. */
2937 vbox = gtk_vbox_new(FALSE, 5);
2938 gtk_paned_pack2(GTK_PANED(vpaned), vbox, TRUE, FALSE);
2939 gtk_widget_show(vbox);
2940
2941 gtkconv->toolbar.toolbar = build_conv_toolbar(conv);
2942 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->toolbar.toolbar,
2943 FALSE, FALSE, 0);
2944
2945 /* Setup the entry widget. */
2946 frame = gtk_frame_new(NULL);
2947 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
2948 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
2949 gtk_widget_show(frame);
2950
2951 gtkconv->entry_buffer = gtk_text_buffer_new(NULL);
2952 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", conv);
2953 gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer);
2954
2955 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD);
2956 gtk_widget_set_size_request(gtkconv->entry, buddy_chat_size.width,
2957 MAX(buddy_chat_size.entry_height, 25));
2958
2959 /* Connect the signal handlers. */
2960 g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "key_press_event",
2961 G_CALLBACK(entry_key_pressed_cb_1),
2962 gtkconv->entry_buffer);
2963 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
2964 G_CALLBACK(entry_stop_rclick_cb), NULL);
2965 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
2966 G_CALLBACK(entry_key_pressed_cb_2), conv);
2967
2968 #ifdef USE_GTKSPELL
2969 if (convo_options & OPT_CONVO_CHECK_SPELLING)
2970 gtkspell_new_attach(GTK_TEXT_VIEW(gtkconv->entry), NULL, NULL);
2971 #endif
2972
2973 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(gtkconv->entry));
2974 gtk_widget_show(gtkconv->entry);
2975
2976 /* Setup the bottom button box. */
2977 gtkconv->bbox = gtk_hbox_new(FALSE, 5);
2978 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->bbox, FALSE, FALSE, 0);
2979 gtk_widget_show(gtkconv->bbox);
2980
2981 setup_chat_buttons(conv, gtkconv->bbox);
2982
2983 return vpaned;
2984 }
2985
2986 static GtkWidget *
2987 setup_im_pane(struct gaim_conversation *conv)
2988 {
2989 struct gaim_gtk_conversation *gtkconv;
2990 struct gaim_gtk_im_pane *gtkim;
2991 GtkWidget *paned;
2992 GtkWidget *vbox;
2993 GtkWidget *vbox2;
2994 GtkWidget *frame;
2995
2996 gtkconv = GAIM_GTK_CONVERSATION(conv);
2997 gtkim = gtkconv->u.im;
2998
2999 /* Setup the outer pane. */
3000 paned = gtk_vpaned_new();
3001 gtk_paned_set_gutter_size(GTK_PANED(paned), 15);
3002 gtk_widget_show(paned);
3003
3004 /* Setup the top part of the pane. */
3005 vbox = gtk_vbox_new(FALSE, 5);
3006 gtk_paned_pack1(GTK_PANED(paned), vbox, FALSE, TRUE);
3007 gtk_widget_show(vbox);
3008
3009 /* Setup the gtkimhtml widget. */
3010 gtkconv->sw = gtk_scrolled_window_new(NULL, NULL);
3011 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkconv->sw),
3012 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
3013 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtkconv->sw),
3014 GTK_SHADOW_IN);
3015 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->sw, TRUE, TRUE, 0);
3016 gtk_widget_set_size_request(gtkconv->sw, conv_size.width, conv_size.height);
3017 gtk_widget_show(gtkconv->sw);
3018
3019 gtkconv->imhtml = gtk_imhtml_new(NULL, NULL);
3020 gtk_container_add(GTK_CONTAINER(gtkconv->sw), gtkconv->imhtml);
3021
3022 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
3023 G_CALLBACK(entry_stop_rclick_cb), NULL);
3024
3025 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
3026 (convo_options & OPT_CONVO_SHOW_TIME));
3027
3028 gaim_setup_imhtml(gtkconv->imhtml);
3029
3030 gtk_widget_show(gtkconv->imhtml);
3031
3032 vbox2 = gtk_vbox_new(FALSE, 5);
3033 gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, FALSE);
3034 gtk_widget_show(vbox2);
3035
3036 /* Build the toolbar. */
3037 gtkconv->toolbar.toolbar = build_conv_toolbar(conv);
3038 gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->toolbar.toolbar,
3039 FALSE, FALSE, 0);
3040
3041 /* Setup the entry widget. */
3042 frame = gtk_frame_new(NULL);
3043 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
3044 gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0);
3045 gtk_widget_show(frame);
3046
3047 gtkconv->entry_buffer = gtk_text_buffer_new(NULL);
3048 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", conv);
3049 gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer);
3050
3051 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD);
3052 gtk_widget_set_size_request(gtkconv->entry, conv_size.width - 20,
3053 MAX(conv_size.entry_height, 25));
3054
3055 /* Connect the signal handlers. */
3056 g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "key_press_event",
3057 G_CALLBACK(entry_key_pressed_cb_1),
3058 gtkconv->entry_buffer);
3059 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
3060 G_CALLBACK(entry_key_pressed_cb_2), conv);
3061 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
3062 G_CALLBACK(entry_stop_rclick_cb), NULL);
3063
3064 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
3065 G_CALLBACK(insert_text_cb), conv);
3066 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
3067 G_CALLBACK(delete_text_cb), conv);
3068
3069 #ifdef USE_GTKSPELL
3070 if (convo_options & OPT_CONVO_CHECK_SPELLING)
3071 gtkspell_new_attach(GTK_TEXT_VIEW(gtkconv->entry), NULL, NULL);
3072 #endif
3073
3074 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(gtkconv->entry));
3075 gtk_widget_show(gtkconv->entry);
3076
3077 gtkconv->bbox = gtk_hbox_new(FALSE, 5);
3078 gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->bbox, FALSE, FALSE, 0);
3079 gtk_widget_show(gtkconv->bbox);
3080
3081 setup_im_buttons(conv, gtkconv->bbox);
3082
3083 return paned;
3084 }
3085
3086 static void
3087 move_next_tab(struct gaim_conversation *conv)
3088 {
3089 struct gaim_conversation *next_conv = NULL;
3090 struct gaim_window *win;
3091 GList *l;
3092 int index, i;
3093
3094 win = gaim_conversation_get_window(conv);
3095 index = gaim_conversation_get_index(conv);
3096
3097 /* First check the tabs after this position. */
3098 for (l = g_list_nth(gaim_window_get_conversations(win), index);
3099 l != NULL;
3100 l = l->next) {
3101
3102 next_conv = (struct gaim_conversation *)l->data;
3103
3104 if (gaim_conversation_get_unseen(next_conv) > 0)
3105 break;
3106
3107 next_conv = NULL;
3108 }
3109
3110 if (next_conv == NULL) {
3111
3112 /* Now check before this position. */
3113 for (l = gaim_window_get_conversations(win), i = 0;
3114 l != NULL && i < index;
3115 l = l->next) {
3116
3117 next_conv = (struct gaim_conversation *)l->data;
3118
3119 if (gaim_conversation_get_unseen(next_conv) > 0)
3120 break;
3121
3122 next_conv = NULL;
3123 }
3124
3125 if (next_conv == NULL) {
3126 /* Okay, just grab the next conversation tab. */
3127 if (index == gaim_window_get_conversation_count(win) - 1)
3128 next_conv = gaim_window_get_conversation_at(win, 0);
3129 else
3130 next_conv = gaim_window_get_conversation_at(win, index + 1);
3131 }
3132 }
3133
3134 if (next_conv != NULL && next_conv != conv) {
3135 gaim_window_switch_conversation(win,
3136 gaim_conversation_get_index(next_conv));
3137 }
3138 }
3139
3140
3141 /**************************************************************************
3142 * GTK+ window ops
3143 **************************************************************************/
3144 static struct gaim_conversation_ops *
3145 gaim_gtk_get_conversation_ops(void)
3146 {
3147 return gaim_get_gtk_conversation_ops();
3148 }
3149
3150 static void
3151 gaim_gtk_new_window(struct gaim_window *win)
3152 {
3153 struct gaim_gtk_window *gtkwin;
3154 GtkPositionType pos;
3155 GtkWidget *testidea;
3156 GtkWidget *menubar;
3157
3158 gtkwin = g_malloc0(sizeof(struct gaim_gtk_window));
3159
3160 win->ui_data = gtkwin;
3161
3162 /* Create the window. */
3163 gtkwin->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3164 gtk_window_set_role(GTK_WINDOW(gtkwin->window), "conversation");
3165 gtk_window_set_policy(GTK_WINDOW(gtkwin->window), TRUE, TRUE, FALSE);
3166 gtk_container_border_width(GTK_CONTAINER(gtkwin->window), 0);
3167 gtk_widget_realize(gtkwin->window);
3168 gtk_window_set_title(GTK_WINDOW(gtkwin->window), _("Gaim - Conversations"));
3169
3170 g_signal_connect(G_OBJECT(gtkwin->window), "delete_event",
3171 G_CALLBACK(close_win_cb), win);
3172
3173 /* Create the notebook. */
3174 gtkwin->notebook = gtk_notebook_new();
3175
3176 pos = ((im_options & OPT_IM_SIDE_TAB)
3177 ? ((im_options & OPT_IM_BR_TAB) ? GTK_POS_RIGHT : GTK_POS_LEFT)
3178 : ((im_options & OPT_IM_BR_TAB) ? GTK_POS_BOTTOM : GTK_POS_TOP));
3179
3180 #if 0
3181 gtk_notebook_set_tab_hborder(GTK_NOTEBOOK(gtkwin->notebook), 0);
3182 gtk_notebook_set_tab_vborder(GTK_NOTEBOOK(gtkwin->notebook), 0);
3183 #endif
3184 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkwin->notebook), pos);
3185 gtk_notebook_set_scrollable(GTK_NOTEBOOK(gtkwin->notebook), TRUE);
3186 gtk_notebook_popup_enable(GTK_NOTEBOOK(gtkwin->notebook));
3187 gtk_widget_show(gtkwin->notebook);
3188
3189 g_signal_connect_after(G_OBJECT(gtkwin->notebook), "switch_page",
3190 G_CALLBACK(switch_conv_cb), win);
3191
3192 /* Setup the tab drag and drop signals. */
3193 gtk_widget_add_events(gtkwin->notebook,
3194 GDK_BUTTON1_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
3195 g_signal_connect(G_OBJECT(gtkwin->notebook), "button_press_event",
3196 G_CALLBACK(notebook_press_cb), win);
3197 g_signal_connect(G_OBJECT(gtkwin->notebook), "button_release_event",
3198 G_CALLBACK(notebook_release_cb), win);
3199
3200 testidea = gtk_vbox_new(FALSE, 0);
3201
3202 /* Setup the menubar. */
3203 menubar = setup_menubar(win);
3204 gtk_box_pack_start(GTK_BOX(testidea), menubar, FALSE, TRUE, 0);
3205
3206 gtk_box_pack_start(GTK_BOX(testidea), gtkwin->notebook, TRUE, TRUE, 0);
3207
3208 gtk_container_add(GTK_CONTAINER(gtkwin->window), testidea);
3209
3210 gtk_widget_show(testidea);
3211 }
3212
3213 static void
3214 gaim_gtk_destroy_window(struct gaim_window *win)
3215 {
3216 struct gaim_gtk_window *gtkwin = GAIM_GTK_WINDOW(win);
3217
3218 gtk_widget_destroy(gtkwin->window);
3219
3220 g_free(gtkwin);
3221 win->ui_data = NULL;
3222 }
3223
3224 static void
3225 gaim_gtk_show(struct gaim_window *win)
3226 {
3227 struct gaim_gtk_window *gtkwin = GAIM_GTK_WINDOW(win);
3228
3229 gtk_widget_show(gtkwin->window);
3230 }
3231
3232 static void
3233 gaim_gtk_hide(struct gaim_window *win)
3234 {
3235 struct gaim_gtk_window *gtkwin = GAIM_GTK_WINDOW(win);
3236
3237 gtk_widget_hide(gtkwin->window);
3238 }
3239
3240 static void
3241 gaim_gtk_raise(struct gaim_window *win)
3242 {
3243 struct gaim_gtk_window *gtkwin = GAIM_GTK_WINDOW(win);
3244
3245 gdk_window_raise(gtkwin->window->window);
3246 }
3247
3248 static void
3249 gaim_gtk_flash(struct gaim_window *win)
3250 {
3251 #ifdef _WIN32
3252 struct gaim_gtk_window *gtkwin = GAIM_GTK_WINDOW(win);
3253
3254 wgaim_im_blink(gtkwin->window);
3255 #endif
3256 }
3257
3258 static void
3259 gaim_gtk_switch_conversation(struct gaim_window *win, unsigned int index)
3260 {
3261 struct gaim_gtk_window *gtkwin;
3262
3263 gtkwin = GAIM_GTK_WINDOW(win);
3264
3265 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkwin->notebook), index);
3266 }
3267
3268 static void
3269 gaim_gtk_add_conversation(struct gaim_window *win,
3270 struct gaim_conversation *conv)
3271 {
3272 struct gaim_gtk_window *gtkwin;
3273 struct gaim_gtk_conversation *gtkconv;
3274 GtkWidget *pane = NULL;
3275 GtkWidget *tab_cont;
3276 GtkWidget *tabby;
3277 gboolean new_ui;
3278
3279 gtkwin = GAIM_GTK_WINDOW(win);
3280
3281 if (conv->ui_data != NULL) {
3282 gtkconv = (struct gaim_gtk_conversation *)conv->ui_data;
3283
3284 tab_cont = gtkconv->tab_cont;
3285
3286 new_ui = FALSE;
3287 }
3288 else {
3289 gtkconv = g_malloc0(sizeof(struct gaim_gtk_conversation));
3290 conv->ui_data = gtkconv;
3291
3292 /* Setup some initial variables. */
3293 gtkconv->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
3294 gtkconv->tooltips = gtk_tooltips_new();
3295
3296 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
3297 gtkconv->u.chat = g_malloc0(sizeof(struct gaim_gtk_chat_pane));
3298
3299 pane = setup_chat_pane(conv);
3300 }
3301 else if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
3302 gtkconv->u.im = g_malloc0(sizeof(struct gaim_gtk_im_pane));
3303 gtkconv->u.im->a_virgin = TRUE;
3304
3305 pane = setup_im_pane(conv);
3306 }
3307
3308 if (pane == NULL) {
3309 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
3310 g_free(gtkconv->u.chat);
3311 }
3312 else if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
3313 g_free(gtkconv->u.im);
3314 };
3315
3316 g_free(gtkconv);
3317 conv->ui_data = NULL;
3318
3319 return;
3320 }
3321
3322 /* Setup the container for the tab. */
3323 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, 5);
3324 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), 5);
3325 gtk_container_add(GTK_CONTAINER(tab_cont), pane);
3326 gtk_widget_show(pane);
3327
3328 new_ui = TRUE;
3329
3330 gtkconv->make_sound = TRUE;
3331 }
3332
3333 gtkconv->tabby = tabby = gtk_hbox_new(FALSE, 5);
3334
3335 /* Close button. */
3336 gtkconv->close = gtk_button_new();
3337 gtk_widget_set_size_request(GTK_WIDGET(gtkconv->close), 16, 16);
3338 gtk_container_add(GTK_CONTAINER(gtkconv->close),
3339 gtk_image_new_from_stock(GTK_STOCK_CLOSE,
3340 GTK_ICON_SIZE_MENU));
3341 gtk_button_set_relief(GTK_BUTTON(gtkconv->close), GTK_RELIEF_NONE);
3342 gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->close,
3343 _("Close conversation"), NULL);
3344
3345 g_signal_connect(G_OBJECT(gtkconv->close), "clicked",
3346 G_CALLBACK(close_conv_cb), conv);
3347
3348 /* Tab label. */
3349 gtkconv->tab_label = gtk_label_new(gaim_conversation_get_title(conv));
3350 #if 0
3351 gtk_misc_set_alignment(GTK_MISC(gtkconv->tab_label), 0.00, 0.5);
3352 gtk_misc_set_padding(GTK_MISC(gtkconv->tab_label), 4, 0);
3353 #endif
3354
3355 /* Pack it all together. */
3356 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->tab_label, TRUE, TRUE, 0);
3357 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->close, FALSE, FALSE, 0);
3358 gtk_widget_show_all(tabby);
3359
3360
3361 /* Add this pane to the conversations notebook. */
3362 gtk_notebook_append_page(GTK_NOTEBOOK(gtkwin->notebook), tab_cont, tabby);
3363 gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(gtkwin->notebook), tab_cont,
3364 gaim_conversation_get_title(conv));
3365
3366 gtk_widget_show(tab_cont);
3367
3368 /* Er, bug in notebooks? Switch to the page manually. */
3369 if (gaim_window_get_conversation_count(win) == 1)
3370 gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkwin->notebook), 0);
3371
3372 debug_printf("*** Current page = %d\n",
3373 gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkwin->notebook)));
3374
3375 if ((gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkwin->notebook)) == 0) ||
3376 (conv == g_list_nth_data(gaim_window_get_conversations(win), 0))) {
3377
3378 gtk_widget_grab_focus(gtkconv->entry);
3379 }
3380
3381 gaim_gtkconv_update_buddy_icon(conv);
3382
3383 if (!new_ui)
3384 g_object_unref(gtkconv->tab_cont);
3385
3386 if (gaim_window_get_conversation_count(win) == 1)
3387 update_send_as_selection(win);
3388 }
3389
3390 static void
3391 gaim_gtk_remove_conversation(struct gaim_window *win,
3392 struct gaim_conversation *conv)
3393 {
3394 struct gaim_gtk_window *gtkwin;
3395 struct gaim_gtk_conversation *gtkconv;
3396 unsigned int index;
3397
3398 index = gaim_conversation_get_index(conv);
3399
3400 gtkwin = GAIM_GTK_WINDOW(win);
3401 gtkconv = GAIM_GTK_CONVERSATION(conv);
3402
3403 g_object_ref(gtkconv->tab_cont);
3404 gtk_object_sink(GTK_OBJECT(gtkconv->tab_cont));
3405
3406 gaim_gtk_set_state_lock(TRUE);
3407
3408 gtk_notebook_remove_page(GTK_NOTEBOOK(gtkwin->notebook), index);
3409
3410 gaim_gtk_set_state_lock(FALSE);
3411
3412 /* If this window is setup with an inactive gc, regenerate the menu. */
3413 debug_printf("remove_conv: user == %p\n", gaim_conversation_get_user(conv));
3414 debug_printf("remove_conv: user->gc == %p\n", gaim_conversation_get_user(conv)->gc);
3415 debug_printf("remove_conv: gc == %p\n", gaim_conversation_get_gc(conv));
3416 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM &&
3417 gaim_conversation_get_gc(conv) == NULL) {
3418
3419 debug_printf("Going to generate send as items.\n");
3420 generate_send_as_items(win);
3421 }
3422 }
3423
3424 static void
3425 gaim_gtk_move_conversation(struct gaim_window *win,
3426 struct gaim_conversation *conv,
3427 unsigned int new_index)
3428 {
3429 struct gaim_gtk_window *gtkwin;
3430 struct gaim_gtk_conversation *gtkconv;
3431
3432 gtkwin = GAIM_GTK_WINDOW(win);
3433 gtkconv = GAIM_GTK_CONVERSATION(conv);
3434
3435 gtk_notebook_reorder_child(GTK_NOTEBOOK(gtkwin->notebook),
3436 gtkconv->tab_cont, new_index);
3437 }
3438
3439 static int
3440 gaim_gtk_get_active_index(const struct gaim_window *win)
3441 {
3442 struct gaim_gtk_window *gtkwin;
3443
3444 gtkwin = GAIM_GTK_WINDOW(win);
3445
3446 return gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkwin->notebook));
3447 }
3448
3449 static struct gaim_window_ops window_ops =
3450 {
3451 gaim_gtk_get_conversation_ops,
3452 gaim_gtk_new_window,
3453 gaim_gtk_destroy_window,
3454 gaim_gtk_show,
3455 gaim_gtk_hide,
3456 gaim_gtk_raise,
3457 gaim_gtk_flash,
3458 gaim_gtk_switch_conversation,
3459 gaim_gtk_add_conversation,
3460 gaim_gtk_remove_conversation,
3461 gaim_gtk_move_conversation,
3462 gaim_gtk_get_active_index
3463 };
3464
3465 static void
3466 update_convo_add_button(struct gaim_conversation *conv)
3467 {
3468 struct gaim_gtk_conversation *gtkconv;
3469 struct gaim_connection *gc;
3470 GaimConversationType type;
3471 GtkWidget *parent;
3472 gboolean rebuild = FALSE;
3473
3474 type = gaim_conversation_get_type(conv);
3475 gc = gaim_conversation_get_gc(conv);
3476 gtkconv = GAIM_GTK_CONVERSATION(conv);
3477 parent = gtk_widget_get_parent(gtkconv->u.im->add);
3478
3479 if (find_buddy(gc->user, gaim_conversation_get_name(conv))) {
3480 if (!g_object_get_data(G_OBJECT(gtkconv->u.im->add), "user_data")) {
3481 gtkconv->u.im->add =
3482 gaim_gtk_change_text(_("Remove"), gtkconv->u.im->add,
3483 GTK_STOCK_REMOVE, type);
3484
3485 rebuild = TRUE;
3486 }
3487
3488 gtk_widget_set_sensitive(gtkconv->u.im->add,
3489 (gc != NULL && gc->prpl->remove_buddy != NULL));
3490
3491 g_object_set_data(G_OBJECT(gtkconv->u.im->add), "user_data", conv);
3492
3493 } else {
3494 if (g_object_get_data(G_OBJECT(gtkconv->u.im->add), "user_data")) {
3495 gtkconv->u.im->add =
3496 gaim_gtk_change_text(_("Add"), gtkconv->u.im->add,
3497 GTK_STOCK_ADD, type);
3498
3499 rebuild = TRUE;
3500 }
3501
3502 gtk_widget_set_sensitive(gtkconv->u.im->add,
3503 (gc != NULL && gc->prpl->add_buddy != NULL));
3504 }
3505
3506 if (rebuild) {
3507 g_signal_connect(G_OBJECT(gtkconv->u.im->add), "clicked",
3508 G_CALLBACK(add_cb), conv);
3509
3510 gtk_box_pack_start(GTK_BOX(parent), gtkconv->u.im->add,
3511 FALSE, FALSE, 0);
3512 gtk_box_reorder_child(GTK_BOX(parent), gtkconv->u.im->add, 3);
3513 gtk_button_set_relief(GTK_BUTTON(gtkconv->u.im->add), GTK_RELIEF_NONE);
3514 gtk_size_group_add_widget(gtkconv->sg, gtkconv->u.im->add);
3515 }
3516 }
3517
3518 struct gaim_window_ops *
3519 gaim_get_gtk_window_ops(void)
3520 {
3521 return &window_ops;
3522 }
3523
3524 /**************************************************************************
3525 * Conversation ops
3526 **************************************************************************/
3527 static void
3528 gaim_gtkconv_destroy(struct gaim_conversation *conv)
3529 {
3530 struct gaim_gtk_conversation *gtkconv = GAIM_GTK_CONVERSATION(conv);
3531
3532 if (gtkconv->dialogs.fg_color != NULL)
3533 gtk_widget_destroy(gtkconv->dialogs.fg_color);
3534
3535 if (gtkconv->dialogs.bg_color != NULL)
3536 gtk_widget_destroy(gtkconv->dialogs.bg_color);
3537
3538 if (gtkconv->dialogs.font != NULL)
3539 gtk_widget_destroy(gtkconv->dialogs.font);
3540
3541 if (gtkconv->dialogs.smiley != NULL)
3542 gtk_widget_destroy(gtkconv->dialogs.smiley);
3543
3544 if (gtkconv->dialogs.link != NULL)
3545 gtk_widget_destroy(gtkconv->dialogs.link);
3546
3547 if (gtkconv->dialogs.log != NULL)
3548 gtk_widget_destroy(gtkconv->dialogs.log);
3549
3550 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
3551 if (gtkconv->u.im->save_icon != NULL)
3552 gtk_widget_destroy(gtkconv->u.im->save_icon);
3553
3554 if (gtkconv->u.im->anim != NULL)
3555 gdk_pixbuf_animation_unref(gtkconv->u.im->anim);
3556
3557 g_free(gtkconv->u.im);
3558 }
3559 else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
3560 g_free(gtkconv->u.chat);
3561 }
3562
3563 g_free(gtkconv);
3564 }
3565
3566 static void
3567 gaim_gtkconv_write_im(struct gaim_conversation *conv, const char *who,
3568 const char *message, size_t len, int flags, time_t mtime)
3569 {
3570 struct gaim_gtk_conversation *gtkconv;
3571
3572 gtkconv = GAIM_GTK_CONVERSATION(conv);
3573
3574 debug_printf("Write IM\n");
3575
3576 if (gtkconv->make_sound) {
3577 if (flags & WFLAG_RECV) {
3578 if (gtkconv->u.im->a_virgin &&
3579 (sound_options & OPT_SOUND_FIRST_RCV)) {
3580
3581 play_sound(SND_FIRST_RECEIVE);
3582 }
3583 else
3584 play_sound(SND_RECEIVE);
3585 }
3586 else {
3587 debug_printf("Playing SND_SEND\n");
3588 play_sound(SND_SEND);
3589 }
3590 }
3591
3592 gtkconv->u.im->a_virgin = FALSE;
3593
3594 gaim_conversation_write(conv, who, message, len, flags, mtime);
3595 }
3596
3597 static void
3598 gaim_gtkconv_write_chat(struct gaim_conversation *conv, const char *who,
3599 const char *message, int flags, time_t mtime)
3600 {
3601 struct gaim_gtk_conversation *gtkconv;
3602
3603 gtkconv = GAIM_GTK_CONVERSATION(conv);
3604
3605 if (gtkconv->make_sound) {
3606 if (!(flags & WFLAG_WHISPER) && (flags & WFLAG_SEND))
3607 play_sound(SND_CHAT_YOU_SAY);
3608 else if (flags & WFLAG_RECV) {
3609 if ((flags & WFLAG_NICK) && (sound_options & OPT_SOUND_CHAT_NICK))
3610 play_sound(SND_CHAT_NICK);
3611 else
3612 play_sound(SND_CHAT_SAY);
3613 }
3614 }
3615
3616 if (chat_options & OPT_CHAT_COLORIZE)
3617 flags |= WFLAG_COLORIZE;
3618
3619 gaim_conversation_write(conv, who, message, -1, flags, mtime);
3620 }
3621
3622 static void
3623 gaim_gtkconv_write_conv(struct gaim_conversation *conv, const char *who,
3624 const char *message, size_t length, int flags,
3625 time_t mtime)
3626 {
3627 struct gaim_gtk_conversation *gtkconv;
3628 struct gaim_connection *gc;
3629 int gtk_font_options = 0;
3630 GString *log_str;
3631 FILE *fd;
3632 char buf[BUF_LONG];
3633 char buf2[BUF_LONG];
3634 char mdate[64];
3635 char color[10];
3636 char *str;
3637 char *with_font_tag;
3638
3639 gtkconv = GAIM_GTK_CONVERSATION(conv);
3640 gc = gaim_conversation_get_gc(conv);
3641
3642 strftime(mdate, sizeof(mdate), "%H:%M:%S", localtime(&mtime));
3643
3644 gtk_font_options ^= GTK_IMHTML_NO_COMMENTS;
3645
3646 if (convo_options & OPT_CONVO_IGNORE_COLOUR)
3647 gtk_font_options ^= GTK_IMHTML_NO_COLOURS;
3648
3649 if (convo_options & OPT_CONVO_IGNORE_FONTS)
3650 gtk_font_options ^= GTK_IMHTML_NO_FONTS;
3651
3652 if (convo_options & OPT_CONVO_IGNORE_SIZES)
3653 gtk_font_options ^= GTK_IMHTML_NO_SIZES;
3654
3655 if (!(logging_options & OPT_LOG_STRIP_HTML))
3656 gtk_font_options ^= GTK_IMHTML_RETURN_LOG;
3657
3658 if (flags & WFLAG_SYSTEM) {
3659 if (convo_options & OPT_CONVO_SHOW_TIME)
3660 g_snprintf(buf, BUF_LONG, "<FONT SIZE=\"2\">(%s) </FONT><B>%s</B>",
3661 mdate, message);
3662 else
3663 g_snprintf(buf, BUF_LONG, "<B>%s</B>", message);
3664
3665 g_snprintf(buf2, sizeof(buf2),
3666 "<FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B><BR>",
3667 mdate, message);
3668
3669 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, -1, 0);
3670
3671 if (logging_options & OPT_LOG_STRIP_HTML) {
3672 char *t1 = strip_html(buf);
3673
3674 conv->history = g_string_append(conv->history, t1);
3675 conv->history = g_string_append(conv->history, "\n");
3676
3677 g_free(t1);
3678 }
3679 else {
3680 conv->history = g_string_append(conv->history, buf);
3681 conv->history = g_string_append(conv->history, "<BR>\n");
3682 }
3683
3684 if (!(flags & WFLAG_NOLOG) && gaim_conversation_is_logging(conv)) {
3685 /*
3686 XXX
3687 ((gaim_conversation_get_type(conv) == GAIM_CONV_CHAT &&
3688 (logging_options & OPT_LOG_CHATS)) ||
3689 (gaim_conversation_get_type(conv) == GAIM_CONV_IM &&
3690 (logging_options & OPT_LOG_CONVOS)) ||
3691 find_log_info(gaim_conversation_get_name(conv)))) {
3692 */
3693
3694 char *t1;
3695 char nm[256];
3696
3697 if (logging_options & OPT_LOG_STRIP_HTML)
3698 t1 = strip_html(buf);
3699 else
3700 t1 = buf;
3701
3702 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)
3703 g_snprintf(nm, sizeof(nm), "%s.chat",
3704 gaim_conversation_get_name(conv));
3705 else
3706 strncpy(nm, gaim_conversation_get_name(conv), sizeof(nm));
3707
3708 fd = open_log_file(nm,
3709 (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT));
3710
3711 if (fd) {
3712 if (logging_options & OPT_LOG_STRIP_HTML)
3713 fprintf(fd, "%s\n", t1);
3714 else
3715 fprintf(fd, "%s<BR>\n", t1);
3716
3717 fclose(fd);
3718 }
3719
3720 if (logging_options & OPT_LOG_STRIP_HTML)
3721 g_free(t1);
3722 }
3723 }
3724 else if (flags & WFLAG_NOLOG) {
3725 g_snprintf(buf, BUF_LONG,
3726 "<B><FONT COLOR=\"#777777\">%s</FONT></B><BR>",
3727 message);
3728
3729 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf, -1, 0);
3730 }
3731 else {
3732 char *new_message = g_strdup(message);
3733
3734 if (flags & WFLAG_WHISPER) {
3735 str = g_malloc(1024);
3736
3737 /* If we're whispering, it's not an autoresponse. */
3738 if (meify(new_message, length)) {
3739 g_snprintf(str, 1024, "***%s", who);
3740 strcpy(color, "#6C2585");
3741 }
3742 else {
3743 g_snprintf(str, 1024, "*%s*:", who);
3744 strcpy(color, "#00FF00");
3745 }
3746 }
3747 else {
3748 if (meify(new_message, length)) {
3749 str = g_malloc(1024);
3750
3751 if (flags & WFLAG_AUTO)
3752 g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, who);
3753 else
3754 g_snprintf(str, 1024, "***%s", who);
3755
3756 if (flags & WFLAG_NICK)
3757 strcpy(color, "#AF7F00");
3758 else
3759 strcpy(color, "#062585");
3760 }
3761 else {
3762 str = g_malloc(1024);
3763
3764 if (flags & WFLAG_AUTO)
3765 g_snprintf(str, 1024, "%s %s", who, AUTO_RESPONSE);
3766 else
3767 g_snprintf(str, 1024, "%s:", who);
3768
3769 if (flags & WFLAG_NICK)
3770 strcpy(color, "#AF7F00");
3771 else if (flags & WFLAG_RECV) {
3772 if (flags & WFLAG_COLORIZE) {
3773 const char *u;
3774 int m = 0;
3775
3776 for (u = who; *u != '\0'; u++)
3777 m += *u;
3778
3779 m = m % NUM_NICK_COLORS;
3780
3781 strcpy(color, nick_colors[m]);
3782 }
3783 else
3784 strcpy(color, "#A82F2F");
3785 }
3786 else if (flags & WFLAG_SEND)
3787 strcpy(color, "#16569E");
3788 }
3789 }
3790
3791 if (convo_options & OPT_CONVO_SHOW_TIME)
3792 g_snprintf(buf, BUF_LONG,
3793 "<FONT COLOR=\"%s\"><FONT SIZE=\"2\">(%s) </FONT>"
3794 "<B>%s</B></FONT> ", color, mdate, str);
3795 else
3796 g_snprintf(buf, BUF_LONG,
3797 "<FONT COLOR=\"%s\"><B>%s</B></FONT> ", color, str);
3798
3799 g_snprintf(buf2, BUF_LONG,
3800 "<FONT COLOR=\"%s\"><FONT SIZE=\"2\"><!--(%s) --></FONT>"
3801 "<B>%s</B></FONT> ",
3802 color, mdate, str);
3803
3804 g_free(str);
3805
3806 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, -1, 0);
3807
3808 with_font_tag = g_strdup_printf("<font sml=\"%s\">%s</font>",
3809 gc->prpl->name, new_message);
3810
3811 log_str = gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
3812 with_font_tag, length,
3813 gtk_font_options);
3814
3815 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", -1, 0);
3816
3817 /* XXX This needs to be updated for the new length argument. */
3818 if (logging_options & OPT_LOG_STRIP_HTML) {
3819 char *t1, *t2;
3820
3821 t1 = strip_html(buf);
3822 t2 = strip_html(new_message);
3823
3824 conv->history = g_string_append(conv->history, t1);
3825 conv->history = g_string_append(conv->history, t2);
3826 conv->history = g_string_append(conv->history, "\n");
3827
3828 g_free(t1);
3829 g_free(t2);
3830 }
3831 else {
3832 char *t1, *t2;
3833
3834 t1 = html_logize(buf);
3835 t2 = html_logize(new_message);
3836
3837 conv->history = g_string_append(conv->history, t1);
3838 conv->history = g_string_append(conv->history, t2);
3839 conv->history = g_string_append(conv->history, "\n");
3840 conv->history = g_string_append(conv->history, log_str->str);
3841 conv->history = g_string_append(conv->history, "<BR>\n");
3842
3843 g_free(t1);
3844 g_free(t2);
3845 }
3846
3847 /* XXX This needs to be updated for the new length argument. */
3848 if (gaim_conversation_is_logging(conv)) {
3849 /*
3850 XXX
3851 if ((gaim_conversation_get_type(conv) == GAIM_CONV_CHAT &&
3852 (logging_options & OPT_LOG_CHATS)) ||
3853 (gaim_conversation_get_type(conv) == GAIM_CONV_IM &&
3854 (logging_options & OPT_LOG_CONVOS)) ||
3855 find_log_info(gaim_conversation_get_name(conv))) {
3856 */
3857
3858 char *t1, *t2;
3859 char nm[256];
3860
3861 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)
3862 g_snprintf(nm, sizeof(nm), "%s.chat",
3863 gaim_conversation_get_name(conv));
3864 else
3865 strncpy(nm, gaim_conversation_get_name(conv), sizeof(nm));
3866
3867 if (logging_options & OPT_LOG_STRIP_HTML) {
3868 t1 = strip_html(buf);
3869 t2 = strip_html(with_font_tag);
3870 }
3871 else {
3872 t1 = html_logize(buf);
3873 t2 = html_logize(with_font_tag);
3874 }
3875
3876 fd = open_log_file(nm,
3877 (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT));
3878
3879 if (fd) {
3880 if (logging_options & OPT_LOG_STRIP_HTML)
3881 fprintf(fd, "%s%s\n", t1, t2);
3882 else {
3883 fprintf(fd, "%s%s%s<BR>\n", t1, t2, log_str->str);
3884 g_string_free(log_str, TRUE);
3885 }
3886
3887 fclose(fd);
3888 }
3889
3890 g_free(t1);
3891 g_free(t2);
3892 }
3893
3894 g_free(with_font_tag);
3895 g_free(new_message);
3896 }
3897 }
3898
3899 static void
3900 gaim_gtkconv_chat_add_user(struct gaim_conversation *conv, const char *user)
3901 {
3902 struct gaim_chat *chat;
3903 struct gaim_gtk_conversation *gtkconv;
3904 struct gaim_gtk_chat_pane *gtkchat;
3905 char tmp[BUF_LONG];
3906 int num_users;
3907 int pos;
3908
3909 chat = GAIM_CHAT(conv);
3910 gtkconv = GAIM_GTK_CONVERSATION(conv);
3911 gtkchat = gtkconv->u.chat;
3912
3913 num_users = g_list_length(gaim_chat_get_users(chat));
3914
3915 g_snprintf(tmp, sizeof(tmp),
3916 ngettext("%d person in room", "%d people in room",
3917 num_users),
3918 num_users);
3919
3920 gtk_label_set_text(GTK_LABEL(gtkchat->count), tmp);
3921
3922 if (gtkconv->make_sound)
3923 play_sound(SND_CHAT_JOIN);
3924
3925 pos = g_list_index(gaim_chat_get_users(chat), user);
3926
3927 add_chat_buddy_common(conv, user, pos);
3928 }
3929
3930 static void
3931 gaim_gtkconv_chat_rename_user(struct gaim_conversation *conv,
3932 const char *old_name, const char *new_name)
3933 {
3934 struct gaim_chat *chat;
3935 struct gaim_gtk_conversation *gtkconv;
3936 struct gaim_gtk_chat_pane *gtkchat;
3937 GtkTreeIter iter;
3938 GtkTreeModel *model;
3939 GList *names;
3940 int pos;
3941 int f = 1;
3942
3943 chat = GAIM_CHAT(conv);
3944 gtkconv = GAIM_GTK_CONVERSATION(conv);
3945 gtkchat = gtkconv->u.chat;
3946
3947 for (names = gaim_chat_get_users(chat);
3948 names != NULL;
3949 names = names->next) {
3950
3951 char *u = (char *)names->data;
3952
3953 if (!g_strcasecmp(u, old_name)) {
3954 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
3955
3956 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
3957 break;
3958
3959 while (f != 0) {
3960 char *val;
3961
3962 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &val, -1);
3963
3964 if (!g_strcasecmp(old_name, val))
3965 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
3966
3967 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
3968
3969 g_free(val);
3970 }
3971
3972 break;
3973 }
3974 }
3975
3976 if (!names)
3977 return;
3978
3979 pos = g_list_index(gaim_chat_get_users(chat), new_name);
3980
3981 add_chat_buddy_common(conv, new_name, pos);
3982 }
3983
3984 static void
3985 gaim_gtkconv_chat_remove_user(struct gaim_conversation *conv, const char *user)
3986 {
3987 struct gaim_chat *chat;
3988 struct gaim_gtk_conversation *gtkconv;
3989 struct gaim_gtk_chat_pane *gtkchat;
3990 GtkTreeIter iter;
3991 GtkTreeModel *model;
3992 GList *names;
3993 char tmp[BUF_LONG];
3994 int num_users;
3995 int f = 1;
3996
3997 chat = GAIM_CHAT(conv);
3998 gtkconv = GAIM_GTK_CONVERSATION(conv);
3999 gtkchat = gtkconv->u.chat;
4000
4001 num_users = g_list_length(gaim_chat_get_users(chat)) - 1;
4002
4003 for (names = gaim_chat_get_users(chat);
4004 names != NULL;
4005 names = names->next) {
4006
4007 char *u = (char *)names->data;
4008
4009 if (!g_strcasecmp(u, user)) {
4010 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
4011
4012 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
4013 break;
4014
4015 while (f != 0) {
4016 char *val;
4017
4018 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &val, -1);
4019
4020 if (!g_strcasecmp(user, val))
4021 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
4022
4023 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
4024
4025 g_free(val);
4026 }
4027
4028 break;
4029 }
4030 }
4031
4032 if (names == NULL)
4033 return;
4034
4035 g_snprintf(tmp, sizeof(tmp),
4036 ngettext("%d person in room", "%d people in room",
4037 num_users), num_users);
4038
4039 gtk_label_set_text(GTK_LABEL(gtkchat->count), tmp);
4040
4041 if (gtkconv->make_sound)
4042 play_sound(SND_CHAT_LEAVE);
4043 }
4044
4045 static void
4046 gaim_gtkconv_set_title(struct gaim_conversation *conv, const char *title)
4047 {
4048 struct gaim_gtk_conversation *gtkconv;
4049
4050 gtkconv = GAIM_GTK_CONVERSATION(conv);
4051
4052 gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title);
4053 }
4054
4055 static void
4056 gaim_gtkconv_updated(struct gaim_conversation *conv, GaimConvUpdateType type)
4057 {
4058 struct gaim_window *win;
4059 struct gaim_gtk_conversation *gtkconv;
4060 struct gaim_gtk_chat_pane *gtkchat;
4061 struct gaim_chat *chat;
4062
4063 win = gaim_conversation_get_window(conv);
4064 gtkconv = GAIM_GTK_CONVERSATION(conv);
4065
4066 if (type == GAIM_CONV_UPDATE_USER) {
4067 gaim_conversation_autoset_title(conv);
4068 gaim_gtkconv_update_buddy_icon(conv);
4069 gaim_gtkconv_update_buttons_by_protocol(conv);
4070
4071 update_send_as_selection(win);
4072
4073 smiley_themeize(gtkconv->imhtml);
4074 }
4075 else if (type == GAIM_CONV_UPDATE_TYPING ||
4076 type == GAIM_CONV_UPDATE_UNSEEN) {
4077
4078 GtkStyle *style;
4079 struct gaim_im *im = NULL;
4080
4081 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM)
4082 im = GAIM_IM(conv);
4083
4084 style = gtk_style_new();
4085
4086 if (!GTK_WIDGET_REALIZED(gtkconv->tab_label))
4087 gtk_widget_realize(gtkconv->tab_label);
4088
4089 gtk_style_set_font(style,
4090 gdk_font_ref(gtk_style_get_font(gtk_widget_get_style(
4091 gtkconv->tab_label))));
4092
4093 if (im != NULL && gaim_im_get_typing_state(im) == TYPING) {
4094 style->fg[0].red = 0x0000;
4095 style->fg[0].green = 0x9999;
4096 style->fg[0].blue = 0x0000;
4097 }
4098 else if (im != NULL && gaim_im_get_typing_state(im) == TYPED) {
4099 style->fg[0].red = 0xFFFF;
4100 style->fg[0].green = 0xBBBB;
4101 style->fg[0].blue = 0x2222;
4102 }
4103 else if (gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_NICK) {
4104 style->fg[0].red = 0x0000;
4105 style->fg[0].green = 0x0000;
4106 style->fg[0].blue = 0xCCCC;
4107 }
4108 else if (gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_TEXT) {
4109 style->fg[0].red = 0xCCCC;
4110 style->fg[0].green = 0x0000;
4111 style->fg[0].blue = 0x0000;
4112 }
4113
4114 gtk_widget_set_style(gtkconv->tab_label, style);
4115 gtk_style_unref(style);
4116 }
4117 else if (type == GAIM_CONV_UPDATE_TOPIC) {
4118 chat = GAIM_CHAT(conv);
4119 gtkchat = gtkconv->u.chat;
4120
4121 gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text),
4122 gaim_chat_get_topic(chat));
4123 }
4124 else if (type == GAIM_CONV_ACCOUNT_ONLINE ||
4125 type == GAIM_CONV_ACCOUNT_OFFLINE) {
4126
4127 generate_send_as_items(win);
4128 }
4129 }
4130
4131 static struct gaim_conversation_ops conversation_ops =
4132 {
4133 gaim_gtkconv_destroy, /* destroy_conversation */
4134 gaim_gtkconv_write_chat, /* write_chat */
4135 gaim_gtkconv_write_im, /* write_im */
4136 gaim_gtkconv_write_conv, /* write_conv */
4137 gaim_gtkconv_chat_add_user, /* chat_add_user */
4138 gaim_gtkconv_chat_rename_user, /* chat_rename_user */
4139 gaim_gtkconv_chat_remove_user, /* chat_remove_user */
4140 gaim_gtkconv_set_title, /* set_title */
4141 NULL, /* update_progress */
4142 gaim_gtkconv_updated /* updated */
4143 };
4144
4145 struct gaim_conversation_ops *
4146 gaim_get_gtk_conversation_ops(void)
4147 {
4148 return &conversation_ops;;
4149 }
4150
4151 /**************************************************************************
4152 * Public conversation utility functions
4153 **************************************************************************/
4154 void
4155 gaim_gtk_set_state_lock(gboolean lock)
4156 {
4157 state_lock = lock;
4158 }
4159
4160 gboolean
4161 gaim_gtk_is_state_locked(void)
4162 {
4163 return state_lock;
4164 }
4165
4166 void
4167 gaim_gtkconv_toggle_smileys(void)
4168 {
4169 GList *cl;
4170 struct gaim_conversation *conv;
4171 struct gaim_gtk_conversation *gtkconv;
4172
4173 for (cl = gaim_get_conversations(); cl != NULL; cl = cl->next) {
4174
4175 conv = (struct gaim_conversation *)cl->data;
4176
4177 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4178 continue;
4179
4180 gtkconv = GAIM_GTK_CONVERSATION(conv);
4181
4182 gtk_imhtml_show_smileys(GTK_IMHTML(gtkconv->imhtml),
4183 (convo_options & OPT_CONVO_SHOW_SMILEY));
4184 }
4185 }
4186
4187 void
4188 gaim_gtkconv_toggle_timestamps(void)
4189 {
4190 GList *cl;
4191 struct gaim_conversation *conv;
4192 struct gaim_gtk_conversation *gtkconv;
4193
4194 for (cl = gaim_get_conversations(); cl != NULL; cl = cl->next) {
4195
4196 conv = (struct gaim_conversation *)cl->data;
4197
4198 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4199 continue;
4200
4201 gtkconv = GAIM_GTK_CONVERSATION(conv);
4202
4203 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
4204 (convo_options & OPT_CONVO_SHOW_TIME));
4205 }
4206 }
4207
4208 void
4209 gaim_gtkconv_toggle_spellchk(void)
4210 {
4211 #ifdef USE_GTKSPELL
4212 GList *cl;
4213 struct gaim_conversation *conv;
4214 struct gaim_gtk_conversation *gtkconv;
4215 GtkSpell *spell;
4216
4217 for (cl = gaim_get_conversations(); cl != NULL; cl = cl->next) {
4218
4219 conv = (struct gaim_conversation *)cl->data;
4220
4221 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4222 continue;
4223
4224 gtkconv = GAIM_GTK_CONVERSATION(conv);
4225
4226 if (convo_options & OPT_CONVO_CHECK_SPELLING)
4227 gtkspell_new_attach(GTK_TEXT_VIEW(gtkconv->entry), NULL, NULL);
4228 else {
4229 spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry));
4230 gtkspell_detach(spell);
4231 }
4232 }
4233 #endif
4234 }
4235
4236 static void
4237 remove_icon(struct gaim_gtk_conversation *gtkconv)
4238 {
4239 if (gtkconv == NULL)
4240 return;
4241
4242 if (gtkconv->u.im->icon != NULL)
4243 gtk_container_remove(GTK_CONTAINER(gtkconv->bbox),
4244 gtkconv->u.im->icon->parent->parent);
4245
4246 if (gtkconv->u.im->anim != NULL)
4247 gdk_pixbuf_animation_unref(gtkconv->u.im->anim);
4248
4249 if (gtkconv->u.im->icon_timer != 0)
4250 g_source_remove(gtkconv->u.im->icon_timer);
4251
4252 if (gtkconv->u.im->iter != NULL)
4253 g_object_unref(G_OBJECT(gtkconv->u.im->iter));
4254
4255 gtkconv->u.im->icon_timer = 0;
4256 gtkconv->u.im->icon = NULL;
4257 gtkconv->u.im->anim = NULL;
4258 gtkconv->u.im->iter = NULL;
4259 }
4260
4261 static gboolean
4262 redraw_icon(gpointer data)
4263 {
4264 struct gaim_conversation *conv = (struct gaim_conversation *)data;
4265 struct gaim_gtk_conversation *gtkconv;
4266
4267 GdkPixbuf *buf;
4268 GdkPixbuf *scale;
4269 GdkPixmap *pm;
4270 GdkBitmap *bm;
4271 gint delay;
4272
4273 if (!g_list_find(gaim_get_ims(), conv)) {
4274 debug_printf("I think this is a bug.\n");
4275 return FALSE;
4276 }
4277
4278 gtkconv = GAIM_GTK_CONVERSATION(conv);
4279
4280 gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL);
4281 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter);
4282
4283 scale = gdk_pixbuf_scale_simple(buf,
4284 MAX(gdk_pixbuf_get_width(buf) * SCALE(gtkconv->u.im->anim) /
4285 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1),
4286 MAX(gdk_pixbuf_get_height(buf) * SCALE(gtkconv->u.im->anim) /
4287 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1),
4288 GDK_INTERP_NEAREST);
4289
4290 gdk_pixbuf_render_pixmap_and_mask(scale, &pm, &bm, 100);
4291 gdk_pixbuf_unref(scale);
4292 gtk_pixmap_set(GTK_PIXMAP(gtkconv->u.im->icon), pm, bm);
4293 gdk_pixmap_unref(pm);
4294 gtk_widget_queue_draw(gtkconv->u.im->icon);
4295
4296 if (bm)
4297 gdk_bitmap_unref(bm);
4298
4299 delay = gdk_pixbuf_animation_iter_get_delay_time(gtkconv->u.im->iter) / 10;
4300
4301 gtkconv->u.im->icon_timer = g_timeout_add(delay * 10, redraw_icon, conv);
4302
4303 return FALSE;
4304 }
4305
4306 static void
4307 start_anim(GtkObject *obj, struct gaim_conversation *conv)
4308 {
4309 struct gaim_gtk_conversation *gtkconv;
4310 int delay;
4311
4312 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4313 return;
4314
4315 gtkconv = GAIM_GTK_CONVERSATION(conv);
4316
4317 delay = gdk_pixbuf_animation_iter_get_delay_time(gtkconv->u.im->iter) / 10;
4318
4319 if (gtkconv->u.im->anim)
4320 gtkconv->u.im->icon_timer = g_timeout_add(delay * 10, redraw_icon,
4321 conv);
4322 }
4323
4324 static void
4325 stop_anim(GtkObject *obj, struct gaim_conversation *conv)
4326 {
4327 struct gaim_gtk_conversation *gtkconv;
4328
4329 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4330 return;
4331
4332 gtkconv = GAIM_GTK_CONVERSATION(conv);
4333
4334 if (gtkconv->u.im->icon_timer != 0)
4335 g_source_remove(gtkconv->u.im->icon_timer);
4336
4337 gtkconv->u.im->icon_timer = 0;
4338 }
4339
4340 static gboolean
4341 icon_menu(GtkObject *obj, GdkEventButton *e, struct gaim_conversation *conv)
4342 {
4343 struct gaim_gtk_conversation *gtkconv;
4344 static GtkWidget *menu = NULL;
4345 GtkWidget *button;
4346
4347 if (e->button != 3 || e->type != GDK_BUTTON_PRESS)
4348 return FALSE;
4349
4350 gtkconv = GAIM_GTK_CONVERSATION(conv);
4351
4352 /*
4353 * If a menu already exists, destroy it before creating a new one,
4354 * thus freeing-up the memory it occupied.
4355 */
4356 if (menu != NULL)
4357 gtk_widget_destroy(menu);
4358
4359 menu = gtk_menu_new();
4360
4361 if (gtkconv->u.im->icon_timer) {
4362 button = gtk_menu_item_new_with_label(_("Disable Animation"));
4363 g_signal_connect(GTK_OBJECT(button), "activate",
4364 G_CALLBACK(stop_anim), conv);
4365 gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
4366 gtk_widget_show(button);
4367 }
4368 else if (gtkconv->u.im->anim &&
4369 !(gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)))
4370 {
4371 button = gtk_menu_item_new_with_label(_("Enable Animation"));
4372 g_signal_connect(GTK_OBJECT(button), "activate",
4373 G_CALLBACK(start_anim), conv);
4374 gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
4375 gtk_widget_show(button);
4376 }
4377
4378 button = gtk_menu_item_new_with_label(_("Hide Icon"));
4379 g_signal_connect_swapped(GTK_OBJECT(button), "activate",
4380 G_CALLBACK(remove_icon), conv);
4381 gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
4382 gtk_widget_show(button);
4383
4384 button = gtk_menu_item_new_with_label(_("Save Icon As..."));
4385 g_signal_connect(GTK_OBJECT(button), "activate",
4386 G_CALLBACK(gaim_gtk_save_icon_dialog), conv);
4387 gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
4388 gtk_widget_show(button);
4389
4390 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time);
4391
4392 return TRUE;
4393 }
4394
4395 void
4396 gaim_gtkconv_update_buddy_icon(struct gaim_conversation *conv)
4397 {
4398 struct gaim_gtk_conversation *gtkconv;
4399
4400 char filename[256];
4401 FILE *file;
4402 GError *err = NULL;
4403
4404 void *data;
4405 int len, delay;
4406
4407 GdkPixbuf *buf;
4408
4409 GtkWidget *event;
4410 GtkWidget *frame;
4411 GdkPixbuf *scale;
4412 GdkPixmap *pm;
4413 GdkBitmap *bm;
4414 int sf = 0;
4415
4416 if (conv == NULL ||
4417 gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops() ||
4418 gaim_conversation_get_type(conv) != GAIM_CONV_IM) {
4419
4420 return;
4421 }
4422
4423 gtkconv = GAIM_GTK_CONVERSATION(conv);
4424
4425 remove_icon(gtkconv);
4426
4427 if (im_options & OPT_IM_HIDE_ICONS)
4428 return;
4429
4430 if (gaim_conversation_get_gc(conv) == NULL)
4431 return;
4432
4433 data = get_icon_data(gaim_conversation_get_gc(conv),
4434 normalize(gaim_conversation_get_name(conv)),
4435 &len);
4436
4437 if (!data)
4438 return;
4439
4440 /* this is such an evil hack, i don't know why i'm even considering it.
4441 * we'll do it differently when gdk-pixbuf-loader isn't leaky anymore. */
4442 g_snprintf(filename, sizeof(filename),
4443 "%s" G_DIR_SEPARATOR_S "gaimicon-%s.%d",
4444 g_get_tmp_dir(), gaim_conversation_get_name(conv), getpid());
4445
4446 if (!(file = fopen(filename, "w")))
4447 return;
4448
4449 fwrite(data, 1, len, file);
4450 fclose(file);
4451
4452 gtkconv->u.im->anim = gdk_pixbuf_animation_new_from_file(filename, &err);
4453
4454 if (err) {
4455 debug_printf("Buddy icon error: %s\n", err->message);
4456 g_error_free(err);
4457 }
4458
4459 /* make sure we remove the file as soon as possible */
4460 unlink(filename);
4461
4462 if (!gtkconv->u.im->anim)
4463 return;
4464
4465 if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) {
4466 gtkconv->u.im->iter = NULL;
4467 delay = 0;
4468 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
4469 } else {
4470 gtkconv->u.im->iter =
4471 gdk_pixbuf_animation_get_iter(gtkconv->u.im->anim, NULL);
4472 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter);
4473 delay = gdk_pixbuf_animation_iter_get_delay_time(gtkconv->u.im->iter);
4474 delay = delay / 10;
4475 }
4476
4477 sf = SCALE(gtkconv->u.im->anim);
4478 scale = gdk_pixbuf_scale_simple(buf,
4479 MAX(gdk_pixbuf_get_width(buf) * sf /
4480 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1),
4481 MAX(gdk_pixbuf_get_height(buf) * sf /
4482 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1),
4483 GDK_INTERP_NEAREST);
4484
4485 if (delay)
4486 gtkconv->u.im->icon_timer = g_timeout_add(delay * 10, redraw_icon,
4487 conv);
4488
4489 gdk_pixbuf_render_pixmap_and_mask(scale, &pm, &bm, 100);
4490 gdk_pixbuf_unref(scale);
4491
4492 frame = gtk_frame_new(NULL);
4493 gtk_frame_set_shadow_type(GTK_FRAME(frame),
4494 (bm ? GTK_SHADOW_NONE : GTK_SHADOW_IN));
4495 gtk_box_pack_start(GTK_BOX(gtkconv->bbox), frame, FALSE, FALSE, 5);
4496 gtk_box_reorder_child(GTK_BOX(gtkconv->bbox), frame, 0);
4497 gtk_widget_show(frame);
4498
4499 event = gtk_event_box_new();
4500 gtk_container_add(GTK_CONTAINER(frame), event);
4501 g_signal_connect(GTK_OBJECT(event), "button-press-event",
4502 G_CALLBACK(icon_menu), conv);
4503 gtk_widget_show(event);
4504
4505 gtkconv->u.im->icon = gtk_pixmap_new(pm, bm);
4506 gtk_widget_set_size_request(gtkconv->u.im->icon, sf, sf);
4507 gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon);
4508 gtk_widget_show(gtkconv->u.im->icon);
4509
4510 if(im_options & OPT_IM_NO_ANIMATION)
4511 stop_anim(NULL, conv);
4512
4513 gdk_pixmap_unref(pm);
4514
4515 if (bm)
4516 gdk_bitmap_unref(bm);
4517 }
4518
4519 void
4520 gaim_gtkconv_hide_buddy_icons(void)
4521 {
4522 gaim_conversation_foreach(gaim_gtkconv_update_buddy_icon);
4523 }
4524
4525 void
4526 gaim_gtkconv_set_anim(void)
4527 {
4528 GList *l;
4529
4530 if (im_options & OPT_IM_HIDE_ICONS)
4531 return;
4532
4533 if (im_options & OPT_IM_NO_ANIMATION) {
4534 for (l = gaim_get_ims(); l != NULL; l = l->next)
4535 stop_anim(NULL, (struct gaim_conversation *)l->data);
4536 } else {
4537 for (l = gaim_get_ims(); l != NULL; l = l->next)
4538 start_anim(NULL, (struct gaim_conversation *)l->data);
4539 }
4540 }
4541
4542 void
4543 gaim_gtkconv_update_font_buttons(void)
4544 {
4545 GList *l;
4546 struct gaim_conversation *conv;
4547 struct gaim_gtk_conversation *gtkconv;
4548
4549 for (l = gaim_get_ims(); l != NULL; l = l->next) {
4550 conv = (struct gaim_conversation *)l->data;
4551
4552 if (gaim_conversation_get_ops(conv) !=
4553 gaim_get_gtk_conversation_ops())
4554 continue;
4555
4556 gtkconv = GAIM_GTK_CONVERSATION(conv);
4557
4558 if (gtkconv->toolbar.bold != NULL)
4559 gtk_widget_set_sensitive(gtkconv->toolbar.bold,
4560 (!(font_options & OPT_FONT_BOLD)));
4561
4562 if (gtkconv->toolbar.italic != NULL)
4563 gtk_widget_set_sensitive(gtkconv->toolbar.italic,
4564 (!(font_options & OPT_FONT_ITALIC)));
4565
4566 if (gtkconv->toolbar.underline != NULL)
4567 gtk_widget_set_sensitive(gtkconv->toolbar.underline,
4568 (!(font_options & OPT_FONT_UNDERLINE)));
4569 }
4570 }
4571
4572 void
4573 gaim_gtkconv_update_tabs(void)
4574 {
4575 GList *l;
4576 GtkPositionType pos;
4577 struct gaim_window *win;
4578 struct gaim_gtk_window *gtkwin;
4579
4580 pos = ((im_options & OPT_IM_SIDE_TAB)
4581 ? ((im_options & OPT_IM_BR_TAB) ? GTK_POS_RIGHT : GTK_POS_LEFT)
4582 : ((im_options & OPT_IM_BR_TAB) ? GTK_POS_BOTTOM : GTK_POS_TOP));
4583
4584 for (l = gaim_get_windows(); l != NULL; l = l->next) {
4585 win = (struct gaim_window *)l->data;
4586
4587 if (gaim_window_get_ops(win) != gaim_get_gtk_window_ops())
4588 continue;
4589
4590 gtkwin = GAIM_GTK_WINDOW(win);
4591
4592 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkwin->notebook), pos);
4593 }
4594 }
4595
4596 void
4597 gaim_gtkconv_update_chat_button_style()
4598 {
4599 GSList *l;
4600 struct gaim_connection *g;
4601 GtkWidget *parent;
4602 GaimConversationType type = GAIM_CONV_CHAT;
4603
4604 for (l = connections; l != NULL; l = l->next) {
4605 GSList *bcs;
4606 struct gaim_conversation *conv;
4607 struct gaim_gtk_conversation *gtkconv;
4608 struct gaim_gtk_window *gtkwin;
4609
4610 g = (struct gaim_connection *)l->data;
4611
4612 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
4613 conv = (struct gaim_conversation *)bcs->data;
4614
4615 if (gaim_conversation_get_type(conv) != GAIM_CONV_CHAT)
4616 continue;
4617
4618 if (gaim_conversation_get_ops(conv) !=
4619 gaim_get_gtk_conversation_ops())
4620 continue;
4621
4622 gtkconv = GAIM_GTK_CONVERSATION(conv);
4623 gtkwin = GAIM_GTK_WINDOW(gaim_conversation_get_window(conv));
4624 parent = gtk_widget_get_parent(gtkconv->send);
4625
4626 gtkconv->send =
4627 gaim_gtk_change_text(_("Send"),
4628 gtkconv->send, GAIM_STOCK_SEND, type);
4629 gtkconv->u.chat->invite =
4630 gaim_gtk_change_text(_("Invite"),
4631 gtkconv->u.chat->invite,
4632 GAIM_STOCK_INVITE, type);
4633
4634 gtk_box_pack_end(GTK_BOX(parent), gtkconv->send, FALSE, FALSE,
4635 type);
4636 gtk_box_pack_end(GTK_BOX(parent), gtkconv->u.chat->invite,
4637 FALSE, FALSE, 0);
4638
4639 g_signal_connect(G_OBJECT(gtkconv->send), "clicked",
4640 G_CALLBACK(send_cb), conv);
4641 g_signal_connect(G_OBJECT(gtkconv->u.chat->invite), "clicked",
4642 G_CALLBACK(invite_cb), conv);
4643
4644 gtk_button_set_relief(GTK_BUTTON(gtkconv->send),
4645 GTK_RELIEF_NONE);
4646 gtk_button_set_relief(GTK_BUTTON(gtkconv->u.chat->invite),
4647 GTK_RELIEF_NONE);
4648
4649 gaim_gtkconv_update_buttons_by_protocol(conv);
4650 }
4651 }
4652 }
4653
4654 void
4655 gaim_gtkconv_update_im_button_style()
4656 {
4657 GList *l;
4658 struct gaim_conversation *conv;
4659 struct gaim_gtk_conversation *gtkconv;
4660
4661 for (l = gaim_get_ims(); l != NULL; l = l->next) {
4662 conv = (struct gaim_conversation *)l->data;
4663 gtkconv = GAIM_GTK_CONVERSATION(conv);
4664
4665 setup_im_buttons(conv, gtk_widget_get_parent(gtkconv->send));
4666 }
4667 }
4668
4669 void
4670 gaim_gtkconv_update_buttons_by_protocol(struct gaim_conversation *conv)
4671 {
4672 struct gaim_window *win;
4673 struct gaim_gtk_window *gtkwin = NULL;
4674 struct gaim_gtk_conversation *gtkconv;
4675 struct gaim_connection *gc;
4676
4677 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
4678 return;
4679
4680 gc = gaim_conversation_get_gc(conv);
4681 win = gaim_conversation_get_window(conv);
4682 gtkconv = GAIM_GTK_CONVERSATION(conv);
4683
4684 if (win != NULL)
4685 gtkwin = GAIM_GTK_WINDOW(win);
4686
4687 if (gc == NULL) {
4688 gtk_widget_set_sensitive(gtkconv->send, FALSE);
4689
4690 if (gaim_window_get_active_conversation(win) == conv) {
4691 gtk_widget_set_sensitive(gtkwin->menu.insert_link, FALSE);
4692 }
4693 }
4694
4695 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
4696 if (gc == NULL) {
4697 gtk_widget_set_sensitive(gtkconv->info, FALSE);
4698 gtk_widget_set_sensitive(gtkconv->u.im->warn, FALSE);
4699 gtk_widget_set_sensitive(gtkconv->u.im->block, FALSE);
4700 gtk_widget_set_sensitive(gtkconv->u.im->add, FALSE);
4701
4702 if (win != NULL &&
4703 gaim_window_get_active_conversation(win) == conv) {
4704
4705 gtk_widget_set_sensitive(gtkwin->menu.insert_image, FALSE);
4706 }
4707
4708 return;
4709 }
4710
4711 gtk_widget_set_sensitive(gtkconv->info,
4712 (gc->prpl->get_info != NULL));
4713
4714 gtk_widget_set_sensitive(gtkconv->toolbar.image,
4715 (gc->prpl->options & OPT_PROTO_IM_IMAGE));
4716
4717 if (win != NULL && gaim_window_get_active_conversation(win) == conv) {
4718 gtk_widget_set_sensitive(gtkwin->menu.insert_image,
4719 (gc->prpl->options & OPT_PROTO_IM_IMAGE));
4720 }
4721
4722 gtk_widget_set_sensitive(gtkconv->u.im->warn,
4723 (gc->prpl->warn != NULL));
4724
4725 gtk_widget_set_sensitive(gtkconv->u.im->block,
4726 (gc->prpl->add_permit != NULL));
4727
4728 update_convo_add_button(conv);
4729 }
4730 else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
4731 if (gc == NULL) {
4732 gtk_widget_set_sensitive(gtkconv->u.chat->whisper, FALSE);
4733 gtk_widget_set_sensitive(gtkconv->u.chat->invite, FALSE);
4734
4735 return;
4736 }
4737
4738 gtk_widget_set_sensitive(gtkconv->send, (gc->prpl->chat_send != NULL));
4739
4740 gtk_widget_set_sensitive(gtkconv->toolbar.image, FALSE);
4741 /* gtk_widget_set_sensitive(gtkwin->menu.insert_image, FALSE); */
4742
4743 gtk_widget_set_sensitive(gtkconv->u.chat->whisper,
4744 (gc->prpl->chat_whisper != NULL));
4745
4746 gtk_widget_set_sensitive(gtkconv->u.chat->invite,
4747 (gc->prpl->chat_invite != NULL));
4748 }
4749 }
4750
4751 struct gaim_window *
4752 gaim_gtkwin_get_at_xy(int x, int y)
4753 {
4754 struct gaim_window *win = NULL;
4755 struct gaim_gtk_window *gtkwin;
4756 GdkWindow *gdkwin;
4757 GList *l;
4758
4759 gdkwin = gdk_window_at_pointer(&x, &y);
4760
4761 if (gdkwin)
4762 gdkwin = gdk_window_get_toplevel(gdkwin);
4763
4764 for (l = gaim_get_windows(); l != NULL; l = l->next) {
4765 win = (struct gaim_window *)l->data;
4766
4767 if (gaim_window_get_ops(win) != gaim_get_gtk_window_ops())
4768 continue;
4769
4770 gtkwin = GAIM_GTK_WINDOW(win);
4771
4772 if (gdkwin == gtkwin->window->window)
4773 return win;
4774 }
4775
4776 return NULL;
4777 }
4778
4779 int
4780 gaim_gtkconv_get_tab_at_xy(struct gaim_window *win, int x, int y)
4781 {
4782 struct gaim_gtk_window *gtkwin;
4783 GList *l;
4784 gint nb_x, nb_y, x_rel, y_rel;
4785 GtkNotebook *notebook;
4786 GtkWidget *tab;
4787 gint i, page_num = 0;
4788 gboolean first_visible = TRUE;
4789
4790 if (gaim_window_get_ops(win) != gaim_get_gtk_window_ops())
4791 return -1;
4792
4793 gtkwin = GAIM_GTK_WINDOW(win);
4794 notebook = GTK_NOTEBOOK(gtkwin->notebook);
4795
4796 gdk_window_get_origin(gtkwin->notebook->window, &nb_x, &nb_y);
4797 x_rel = x - nb_x;
4798 y_rel = y - nb_y;
4799
4800 for (l = gaim_window_get_conversations(win), i = 0;
4801 l != NULL;
4802 l = l->next, i++) {
4803
4804 struct gaim_conversation *conv = l->data;
4805 tab = GAIM_GTK_CONVERSATION(conv)->tab_label;
4806
4807 if (!GTK_WIDGET_MAPPED(tab))
4808 continue;
4809
4810 if (first_visible) {
4811 first_visible = FALSE;
4812
4813 if (x_rel < tab->allocation.x) x_rel = tab->allocation.x;
4814 if (y_rel < tab->allocation.y) y_rel = tab->allocation.y;
4815 }
4816
4817 if (gtk_notebook_get_tab_pos(notebook) == GTK_POS_TOP ||
4818 gtk_notebook_get_tab_pos(notebook) == GTK_POS_BOTTOM) {
4819
4820 if (tab->allocation.x <= x_rel) {
4821 if (tab->allocation.x + tab->allocation.width <= x_rel)
4822 page_num = i + 1;
4823 else
4824 page_num = i;
4825 }
4826 else
4827 break;
4828 }
4829 else {
4830 if (tab->allocation.y <= y_rel) {
4831 if (tab->allocation.y + tab->allocation.height <= y_rel)
4832 page_num = i + 1;
4833 else
4834 page_num = i;
4835 }
4836 else
4837 break;
4838 }
4839 }
4840
4841 if (i == gaim_window_get_conversation_count(win) + 1)
4842 return -1;
4843
4844 return page_num;
4845 }
4846
4847 int
4848 gaim_gtkconv_get_dest_tab_at_xy(struct gaim_window *win, int x, int y)
4849 {
4850 struct gaim_gtk_window *gtkwin;
4851 GList *l;
4852 gint nb_x, nb_y, x_rel, y_rel;
4853 GtkNotebook *notebook;
4854 GtkWidget *tab;
4855 gint i, page_num = 0;
4856
4857 if (gaim_window_get_ops(win) != gaim_get_gtk_window_ops())
4858 return -1;
4859
4860 gtkwin = GAIM_GTK_WINDOW(win);
4861 notebook = GTK_NOTEBOOK(gtkwin->notebook);
4862
4863 gdk_window_get_origin(gtkwin->notebook->window, &nb_x, &nb_y);
4864 x_rel = x - nb_x;
4865 y_rel = y - nb_y;
4866
4867 for (l = gaim_window_get_conversations(win), i = 0;
4868 l != NULL;
4869 l = l->next, i++) {
4870
4871 struct gaim_conversation *conv = l->data;
4872 tab = GAIM_GTK_CONVERSATION(conv)->tab_label;
4873
4874 if (!GTK_WIDGET_MAPPED(tab))
4875 continue;
4876
4877 if (gtk_notebook_get_tab_pos(notebook) == GTK_POS_TOP ||
4878 gtk_notebook_get_tab_pos(notebook) == GTK_POS_BOTTOM) {
4879
4880 if (tab->allocation.x <= x_rel) {
4881 if (tab->allocation.x + (tab->allocation.width / 2) <= x_rel)
4882 page_num = i + 1;
4883 else
4884 page_num = i;
4885 }
4886 else
4887 break;
4888 }
4889 else {
4890 if (tab->allocation.y <= y_rel) {
4891 if (tab->allocation.y + (tab->allocation.height / 2) <= y_rel)
4892 page_num = i + 1;
4893 else
4894 page_num = i;
4895 }
4896 else
4897 break;
4898 }
4899 }
4900
4901 if (i == gaim_window_get_conversation_count(win) + 1)
4902 return -1;
4903
4904 return page_num;
4905 }