pidgin/gtkconv.c

changeset 18545
5af898e91ec2
parent 18417
88a1c65eaad3
child 18636
34c1a17a91d7
equal deleted inserted replaced
17398:1edf059a4a26 18545:5af898e91ec2
47 #include "log.h" 47 #include "log.h"
48 #include "notify.h" 48 #include "notify.h"
49 #include "prpl.h" 49 #include "prpl.h"
50 #include "request.h" 50 #include "request.h"
51 #include "util.h" 51 #include "util.h"
52 #include "version.h"
52 53
53 #include "gtkdnd-hints.h" 54 #include "gtkdnd-hints.h"
54 #include "gtkblist.h" 55 #include "gtkblist.h"
55 #include "gtkconv.h" 56 #include "gtkconv.h"
56 #include "gtkconvwin.h" 57 #include "gtkconvwin.h"
78 PIDGIN_CONV_TAB_ICON = 1 << 3, 79 PIDGIN_CONV_TAB_ICON = 1 << 3,
79 PIDGIN_CONV_TOPIC = 1 << 4, 80 PIDGIN_CONV_TOPIC = 1 << 4,
80 PIDGIN_CONV_SMILEY_THEME = 1 << 5, 81 PIDGIN_CONV_SMILEY_THEME = 1 << 5,
81 PIDGIN_CONV_COLORIZE_TITLE = 1 << 6 82 PIDGIN_CONV_COLORIZE_TITLE = 1 << 6
82 }PidginConvFields; 83 }PidginConvFields;
84
85 enum {
86 ICON_COLUMN,
87 TEXT_COLUMN,
88 NUM_COLUMNS
89 } PidginInfopaneColumns;
83 90
84 #define PIDGIN_CONV_ALL ((1 << 7) - 1) 91 #define PIDGIN_CONV_ALL ((1 << 7) - 1)
85 92
86 #define SEND_COLOR "#204a87" 93 #define SEND_COLOR "#204a87"
87 #define RECV_COLOR "#cc0000" 94 #define RECV_COLOR "#cc0000"
175 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background); 182 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background);
176 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast); 183 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
177 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields); 184 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
178 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win); 185 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
179 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); 186 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
187 static gboolean infopane_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
188 static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
180 189
181 static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) { 190 static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) {
182 static GdkColor col; 191 static GdkColor col;
183 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml); 192 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml);
184 float scale; 193 float scale;
230 239
231 if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) 240 if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
232 return FALSE; 241 return FALSE;
233 242
234 if (gtkconv->auto_resize) { 243 if (gtkconv->auto_resize) {
244 return FALSE;
245 }
246
247 if (gdk_window_get_state(gtkconv->win->window->window) & GDK_WINDOW_STATE_MAXIMIZED) {
235 return FALSE; 248 return FALSE;
236 } 249 }
237 250
238 /* I find that I resize the window when it has a bunch of conversations in it, mostly so that the 251 /* I find that I resize the window when it has a bunch of conversations in it, mostly so that the
239 * tab bar will fit, but then I don't want new windows taking up the entire screen. I check to see 252 * tab bar will fit, but then I don't want new windows taking up the entire screen. I check to see
241 * size of the last resized new window. */ 254 * size of the last resized new window. */
242 /* I think that the above justification is not the majority, and that the new tab resizing should 255 /* I think that the above justification is not the majority, and that the new tab resizing should
243 * negate it anyway. --luke */ 256 * negate it anyway. --luke */
244 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) 257 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
245 { 258 {
246 if (w == gtkconv->imhtml) {
247 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width", allocation->width);
248 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height", allocation->height);
249 }
250 if (w == gtkconv->lower_hbox) 259 if (w == gtkconv->lower_hbox)
251 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height); 260 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height);
252 } 261 }
253 else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) 262 else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
254 { 263 {
255 if (w == gtkconv->imhtml) {
256 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", allocation->width);
257 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", allocation->height);
258 }
259 if (w == gtkconv->lower_hbox) 264 if (w == gtkconv->lower_hbox)
260 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height); 265 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height);
261 } 266 }
262 267
263 return FALSE; 268 return FALSE;
265 270
266 static void 271 static void
267 default_formatize(PidginConversation *c) 272 default_formatize(PidginConversation *c)
268 { 273 {
269 PurpleConversation *conv = c->active_conv; 274 PurpleConversation *conv = c->active_conv;
270 275 gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), conv->features);
271 if (conv->features & PURPLE_CONNECTION_HTML)
272 {
273 char color[8];
274 GdkColor fg_color, bg_color;
275
276 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != GTK_IMHTML(c->entry)->edit.bold)
277 gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry));
278
279 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != GTK_IMHTML(c->entry)->edit.italic)
280 gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry));
281
282 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != GTK_IMHTML(c->entry)->edit.underline)
283 gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry));
284
285 gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry),
286 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
287
288 if (!(conv->features & PURPLE_CONNECTION_NO_FONTSIZE))
289 {
290 int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
291
292 /* 3 is the default. */
293 if (size != 3)
294 gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), size);
295 }
296
297 if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
298 {
299 gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
300 &fg_color);
301 g_snprintf(color, sizeof(color), "#%02x%02x%02x",
302 fg_color.red / 256,
303 fg_color.green / 256,
304 fg_color.blue / 256);
305 } else
306 strcpy(color, "");
307
308 gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color);
309
310 if(!(conv->features & PURPLE_CONNECTION_NO_BGCOLOR) &&
311 strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
312 {
313 gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
314 &bg_color);
315 g_snprintf(color, sizeof(color), "#%02x%02x%02x",
316 bg_color.red / 256,
317 bg_color.green / 256,
318 bg_color.blue / 256);
319 } else
320 strcpy(color, "");
321
322 gtk_imhtml_toggle_background(GTK_IMHTML(c->entry), color);
323
324 if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO)
325 gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), TRUE);
326 else
327 gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), FALSE);
328 }
329 } 276 }
330 277
331 static void 278 static void
332 clear_formatting_cb(GtkIMHtml *imhtml, PidginConversation *gtkconv) 279 clear_formatting_cb(GtkIMHtml *imhtml, PidginConversation *gtkconv)
333 { 280 {
390 PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL)); 337 PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
391 return PURPLE_CMD_STATUS_OK; 338 return PURPLE_CMD_STATUS_OK;
392 } 339 }
393 } 340 }
394 341
342 static void clear_conversation_scrollback(PurpleConversation *conv)
343 {
344 PidginConversation *gtkconv = NULL;
345
346 gtkconv = PIDGIN_CONVERSATION(conv);
347
348 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml));
349 }
350
395 static PurpleCmdRet 351 static PurpleCmdRet
396 clear_command_cb(PurpleConversation *conv, 352 clear_command_cb(PurpleConversation *conv,
397 const char *cmd, char **args, char **error, void *data) 353 const char *cmd, char **args, char **error, void *data)
398 { 354 {
399 PidginConversation *gtkconv = NULL; 355 clear_conversation_scrollback(conv);
400 356 return PURPLE_CMD_STATUS_OK;
401 gtkconv = PIDGIN_CONVERSATION(conv); 357 }
402 358
403 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); 359 static PurpleCmdRet
360 clearall_command_cb(PurpleConversation *conv,
361 const char *cmd, char **args, char **error, void *data)
362 {
363 purple_conversation_foreach(clear_conversation_scrollback);
404 return PURPLE_CMD_STATUS_OK; 364 return PURPLE_CMD_STATUS_OK;
405 } 365 }
406 366
407 static PurpleCmdRet 367 static PurpleCmdRet
408 help_command_cb(PurpleConversation *conv, 368 help_command_cb(PurpleConversation *conv,
471 { 431 {
472 PidginConversation *gtkconv; 432 PidginConversation *gtkconv;
473 char *cmd; 433 char *cmd;
474 const char *prefix; 434 const char *prefix;
475 GtkTextIter start; 435 GtkTextIter start;
436 gboolean retval = FALSE;
476 437
477 gtkconv = PIDGIN_CONVERSATION(conv); 438 gtkconv = PIDGIN_CONVERSATION(conv);
478 prefix = pidgin_get_cmd_prefix(); 439 prefix = pidgin_get_cmd_prefix();
479 440
480 cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); 441 cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
494 455
495 gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1)); 456 gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1));
496 gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end); 457 gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end);
497 markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end); 458 markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end);
498 status = purple_cmd_do_command(conv, cmdline, markup, &error); 459 status = purple_cmd_do_command(conv, cmdline, markup, &error);
499 g_free(cmd);
500 g_free(markup); 460 g_free(markup);
501 461
502 switch (status) { 462 switch (status) {
503 case PURPLE_CMD_STATUS_OK: 463 case PURPLE_CMD_STATUS_OK:
504 return TRUE; 464 retval = TRUE;
465 break;
505 case PURPLE_CMD_STATUS_NOT_FOUND: 466 case PURPLE_CMD_STATUS_NOT_FOUND:
506 return FALSE; 467 {
468 PurplePluginProtocolInfo *prpl_info = NULL;
469 PurpleConnection *gc;
470
471 if ((gc = purple_conversation_get_gc(conv)))
472 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
473
474 if ((prpl_info != NULL) && (prpl_info->options & OPT_PROTO_SLASH_COMMANDS_NATIVE)) {
475 char *firstspace;
476 char *slash;
477
478 firstspace = strchr(cmdline, ' ');
479 if (firstspace != NULL) {
480 slash = strrchr(firstspace, '/');
481 } else {
482 slash = strchr(cmdline, '/');
483 }
484
485 if (slash == NULL) {
486 purple_conversation_write(conv, "", _("Unknown command."), PURPLE_MESSAGE_NO_LOG, time(NULL));
487 retval = TRUE;
488 }
489 }
490 break;
491 }
507 case PURPLE_CMD_STATUS_WRONG_ARGS: 492 case PURPLE_CMD_STATUS_WRONG_ARGS:
508 purple_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments " 493 purple_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments "
509 "to that command."), 494 "to that command."),
510 PURPLE_MESSAGE_NO_LOG, time(NULL)); 495 PURPLE_MESSAGE_NO_LOG, time(NULL));
511 return TRUE; 496 retval = TRUE;
497 break;
512 case PURPLE_CMD_STATUS_FAILED: 498 case PURPLE_CMD_STATUS_FAILED:
513 purple_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."), 499 purple_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."),
514 PURPLE_MESSAGE_NO_LOG, time(NULL)); 500 PURPLE_MESSAGE_NO_LOG, time(NULL));
515 g_free(error); 501 g_free(error);
516 return TRUE; 502 retval = TRUE;
503 break;
517 case PURPLE_CMD_STATUS_WRONG_TYPE: 504 case PURPLE_CMD_STATUS_WRONG_TYPE:
518 if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) 505 if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
519 purple_conversation_write(conv, "", _("That command only works in chats, not IMs."), 506 purple_conversation_write(conv, "", _("That command only works in chats, not IMs."),
520 PURPLE_MESSAGE_NO_LOG, time(NULL)); 507 PURPLE_MESSAGE_NO_LOG, time(NULL));
521 else 508 else
522 purple_conversation_write(conv, "", _("That command only works in IMs, not chats."), 509 purple_conversation_write(conv, "", _("That command only works in IMs, not chats."),
523 PURPLE_MESSAGE_NO_LOG, time(NULL)); 510 PURPLE_MESSAGE_NO_LOG, time(NULL));
524 return TRUE; 511 retval = TRUE;
512 break;
525 case PURPLE_CMD_STATUS_WRONG_PRPL: 513 case PURPLE_CMD_STATUS_WRONG_PRPL:
526 purple_conversation_write(conv, "", _("That command doesn't work on this protocol."), 514 purple_conversation_write(conv, "", _("That command doesn't work on this protocol."),
527 PURPLE_MESSAGE_NO_LOG, time(NULL)); 515 PURPLE_MESSAGE_NO_LOG, time(NULL));
528 return TRUE; 516 retval = TRUE;
517 break;
529 } 518 }
530 } 519 }
531 520
532 g_free(cmd); 521 g_free(cmd);
533 return FALSE; 522 return retval;
534 } 523 }
535 524
536 static void 525 static void
537 send_cb(GtkWidget *widget, PidginConversation *gtkconv) 526 send_cb(GtkWidget *widget, PidginConversation *gtkconv)
538 { 527 {
541 PurpleConnection *gc; 530 PurpleConnection *gc;
542 PurpleMessageFlags flags = 0; 531 PurpleMessageFlags flags = 0;
543 char *buf, *clean; 532 char *buf, *clean;
544 533
545 account = purple_conversation_get_account(conv); 534 account = purple_conversation_get_account(conv);
546
547 if (!purple_account_is_connected(account))
548 return;
549
550 if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
551 purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
552 return;
553 535
554 if (check_for_and_do_command(conv)) { 536 if (check_for_and_do_command(conv)) {
555 if (gtkconv->entry_growing) { 537 if (gtkconv->entry_growing) {
556 reset_default_size(gtkconv); 538 reset_default_size(gtkconv);
557 gtkconv->entry_growing = FALSE; 539 gtkconv->entry_growing = FALSE;
558 } 540 }
559 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); 541 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry));
560 return; 542 return;
561 } 543 }
544
545 if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) &&
546 purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
547 return;
548
549 if (!purple_account_is_connected(account))
550 return;
562 551
563 buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); 552 buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry));
564 clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); 553 clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL);
565 554
566 gtk_widget_grab_focus(gtkconv->entry); 555 gtk_widget_grab_focus(gtkconv->entry);
644 } 633 }
645 634
646 static void chat_do_info(PidginConversation *gtkconv, const char *who) 635 static void chat_do_info(PidginConversation *gtkconv, const char *who)
647 { 636 {
648 PurpleConversation *conv = gtkconv->active_conv; 637 PurpleConversation *conv = gtkconv->active_conv;
649 PurplePluginProtocolInfo *prpl_info = NULL;
650 PurpleConnection *gc; 638 PurpleConnection *gc;
651 639
652 if ((gc = purple_conversation_get_gc(conv))) { 640 if ((gc = purple_conversation_get_gc(conv))) {
653 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); 641 pidgin_retrieve_user_info_in_chat(gc, who, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
654
655 /*
656 * If there are special needs for getting info on users in
657 * buddy chat "rooms"...
658 */
659 if (prpl_info->get_cb_info != NULL)
660 {
661 prpl_info->get_cb_info(gc,
662 purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
663 }
664 else
665 prpl_info->get_info(gc, who);
666 } 642 }
667 } 643 }
668 644
669 645
670 static void 646 static void
671 info_cb(GtkWidget *widget, PidginConversation *gtkconv) 647 info_cb(GtkWidget *widget, PidginConversation *gtkconv)
672 { 648 {
673 PurpleConversation *conv = gtkconv->active_conv; 649 PurpleConversation *conv = gtkconv->active_conv;
674 650
675 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { 651 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
676 PurpleNotifyUserInfo *info = purple_notify_user_info_new(); 652 pidgin_retrieve_user_info(purple_conversation_get_gc(conv),
677 purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
678 purple_notify_userinfo(conv->account->gc, purple_conversation_get_name(conv), info, NULL, NULL);
679 purple_notify_user_info_destroy(info);
680
681 serv_get_info(purple_conversation_get_gc(conv),
682 purple_conversation_get_name(conv)); 653 purple_conversation_get_name(conv));
683
684 gtk_widget_grab_focus(gtkconv->entry); 654 gtk_widget_grab_focus(gtkconv->entry);
685 } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { 655 } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
686 /* Get info of the person currently selected in the GtkTreeView */ 656 /* Get info of the person currently selected in the GtkTreeView */
687 PidginChatPane *gtkchat; 657 PidginChatPane *gtkchat;
688 GtkTreeIter iter; 658 GtkTreeIter iter;
1241 pidgin_pounce_editor_show(purple_conversation_get_account(conv), 1211 pidgin_pounce_editor_show(purple_conversation_get_account(conv),
1242 purple_conversation_get_name(conv), NULL); 1212 purple_conversation_get_name(conv), NULL);
1243 } 1213 }
1244 1214
1245 static void 1215 static void
1216 menu_insert_link_cb(gpointer data, guint action, GtkWidget *widget)
1217 {
1218 PidginWindow *win = data;
1219 PidginConversation *gtkconv;
1220 GtkIMHtmlToolbar *toolbar;
1221
1222 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
1223 toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
1224
1225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link),
1226 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link)));
1227 }
1228
1229 static void
1230 menu_insert_image_cb(gpointer data, guint action, GtkWidget *widget)
1231 {
1232 PidginWindow *win = data;
1233 PurpleConversation *conv;
1234 PidginConversation *gtkconv;
1235 GtkIMHtmlToolbar *toolbar;
1236
1237 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
1238 conv = gtkconv->active_conv;
1239 toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar);
1240
1241 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image),
1242 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image)));
1243 }
1244
1245
1246 static void
1246 menu_alias_cb(gpointer data, guint action, GtkWidget *widget) 1247 menu_alias_cb(gpointer data, guint action, GtkWidget *widget)
1247 { 1248 {
1248 PidginWindow *win = data; 1249 PidginWindow *win = data;
1249 PurpleConversation *conv; 1250 PurpleConversation *conv;
1250 PurpleAccount *account; 1251 PurpleAccount *account;
1738 if (path == NULL) 1739 if (path == NULL)
1739 return FALSE; 1740 return FALSE;
1740 1741
1741 gtk_tree_selection_select_path(GTK_TREE_SELECTION( 1742 gtk_tree_selection_select_path(GTK_TREE_SELECTION(
1742 gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path); 1743 gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path);
1744 gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkchat->list),
1745 path, NULL, FALSE);
1746 gtk_widget_grab_focus(GTK_WIDGET(gtkchat->list));
1743 1747
1744 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); 1748 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
1745 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); 1749 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
1746 1750
1747 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { 1751 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
1817 /* If CTRL was held down... */ 1821 /* If CTRL was held down... */
1818 if (event->state & GDK_CONTROL_MASK) { 1822 if (event->state & GDK_CONTROL_MASK) {
1819 switch (event->keyval) { 1823 switch (event->keyval) {
1820 case GDK_Up: 1824 case GDK_Up:
1821 if (!gtkconv->send_history) 1825 if (!gtkconv->send_history)
1826 break;
1827
1828 if (gtkconv->entry != entry)
1822 break; 1829 break;
1823 1830
1824 if (!gtkconv->send_history->prev) { 1831 if (!gtkconv->send_history->prev) {
1825 GtkTextIter start, end; 1832 GtkTextIter start, end;
1826 1833
1865 return TRUE; 1872 return TRUE;
1866 break; 1873 break;
1867 1874
1868 case GDK_Down: 1875 case GDK_Down:
1869 if (!gtkconv->send_history) 1876 if (!gtkconv->send_history)
1877 break;
1878
1879 if (gtkconv->entry != entry)
1870 break; 1880 break;
1871 1881
1872 if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) { 1882 if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) {
1873 GObject *object; 1883 GObject *object;
1874 GtkTextIter iter; 1884 GtkTextIter iter;
1970 else 1980 else
1971 { 1981 {
1972 switch (event->keyval) 1982 switch (event->keyval)
1973 { 1983 {
1974 case GDK_Tab: 1984 case GDK_Tab:
1985 if (gtkconv->entry != entry)
1986 break;
1975 return tab_complete(conv); 1987 return tab_complete(conv);
1976 break; 1988 break;
1977 1989
1978 case GDK_Page_Up: 1990 case GDK_Page_Up:
1979 gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml)); 1991 gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
2046 gtk_widget_event(gtkconv->entry, (GdkEvent *)event); 2058 gtk_widget_event(gtkconv->entry, (GdkEvent *)event);
2047 2059
2048 return TRUE; 2060 return TRUE;
2049 } 2061 }
2050 2062
2063 static void
2064 regenerate_options_items(PidginWindow *win);
2065
2051 void 2066 void
2052 pidgin_conv_switch_active_conversation(PurpleConversation *conv) 2067 pidgin_conv_switch_active_conversation(PurpleConversation *conv)
2053 { 2068 {
2054 PidginConversation *gtkconv; 2069 PidginConversation *gtkconv;
2055 PurpleConversation *old_conv; 2070 PurpleConversation *old_conv;
2146 2161
2147 purple_signal_emit(pidgin_conversations_get_handle(), "conversation-switched", conv); 2162 purple_signal_emit(pidgin_conversations_get_handle(), "conversation-switched", conv);
2148 2163
2149 gray_stuff_out(gtkconv); 2164 gray_stuff_out(gtkconv);
2150 update_typing_icon(gtkconv); 2165 update_typing_icon(gtkconv);
2166 g_object_set_data(G_OBJECT(entry), "transient_buddy", NULL);
2167 regenerate_options_items(gtkconv->win);
2151 2168
2152 gtk_window_set_title(GTK_WINDOW(gtkconv->win->window), 2169 gtk_window_set_title(GTK_WINDOW(gtkconv->win->window),
2153 gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); 2170 gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
2154 } 2171 }
2155 2172
2222 **************************************************************************/ 2239 **************************************************************************/
2223 2240
2224 static GList *get_prpl_icon_list(PurpleAccount *account) 2241 static GList *get_prpl_icon_list(PurpleAccount *account)
2225 { 2242 {
2226 GList *l = NULL; 2243 GList *l = NULL;
2227 GdkPixbuf *pixbuf; 2244 PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
2228 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); 2245 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
2229 const char *prpl = prpl_info->list_icon(account, NULL); 2246 const char *prplname = prpl_info->list_icon(account, NULL);
2230 char *filename, *path; 2247 l = g_hash_table_lookup(prpl_lists, prplname);
2231 l = g_hash_table_lookup(prpl_lists, prpl);
2232 if (l) 2248 if (l)
2233 return l; 2249 return l;
2234 filename = g_strdup_printf("%s.png", prpl); 2250
2235 2251 l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE));
2236 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", filename, NULL); 2252 l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM));
2237 pixbuf = gdk_pixbuf_new_from_file(path, NULL); 2253 l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL));
2238 if (pixbuf) 2254
2239 l = g_list_append(l, pixbuf); 2255 g_hash_table_insert(prpl_lists, g_strdup(prplname), l);
2240 g_free(path);
2241
2242 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", filename, NULL);
2243 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
2244 if (pixbuf)
2245 l = g_list_append(l, pixbuf);
2246 g_free(path);
2247
2248 path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "48", filename, NULL);
2249 pixbuf = gdk_pixbuf_new_from_file(path, NULL);
2250 if (pixbuf)
2251 l = g_list_append(l, pixbuf);
2252 g_free(path);
2253
2254 g_hash_table_insert(prpl_lists, g_strdup(prpl), l);
2255 return l; 2256 return l;
2256 } 2257 }
2257 2258
2258 static GList * 2259 static GList *
2259 pidgin_conv_get_tab_icons(PurpleConversation *conv) 2260 pidgin_conv_get_tab_icons(PurpleConversation *conv)
2350 g_return_if_fail(status != NULL); 2351 g_return_if_fail(status != NULL);
2351 2352
2352 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status); 2353 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status);
2353 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status); 2354 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status);
2354 2355
2356 gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model),
2357 &(gtkconv->infopane_iter),
2358 ICON_COLUMN, status, -1);
2359
2355 if (status != NULL) 2360 if (status != NULL)
2356 g_object_unref(status); 2361 g_object_unref(status);
2357 2362
2358 if (pidgin_conv_window_is_active_conversation(conv) && 2363 if (pidgin_conv_window_is_active_conversation(conv) &&
2359 (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM || 2364 (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM ||
2399 } else { 2404 } else {
2400 gtkconv->u.im->icon_timer = 0; 2405 gtkconv->u.im->icon_timer = 0;
2401 return FALSE; 2406 return FALSE;
2402 } 2407 }
2403 2408
2404 gtkconv->auto_resize = TRUE;
2405 g_idle_add(reset_auto_resize_cb, gtkconv);
2406
2407 gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL); 2409 gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL);
2408 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); 2410 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter);
2409 2411
2410 pidgin_buddy_icon_get_scale_size(buf, &prpl_info->icon_spec, 2412 scale_width = gdk_pixbuf_get_width(buf);
2411 PURPLE_ICON_SCALE_DISPLAY, &scale_width, &scale_height); 2413 scale_height = gdk_pixbuf_get_height(buf);
2412 2414 if (scale_width == scale_height) {
2413 /* this code is ugly, and scares me */ 2415 scale_width = scale_height = 32;
2414 scale = gdk_pixbuf_scale_simple(buf, 2416 } else if (scale_height > scale_width) {
2415 MAX(gdk_pixbuf_get_width(buf) * scale_width / 2417 scale_width = 32 * scale_width / scale_height;
2416 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), 2418 scale_height = 32;
2417 MAX(gdk_pixbuf_get_height(buf) * scale_height / 2419 } else {
2418 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), 2420 scale_height = 32 * scale_height / scale_width;
2421 scale_width = 32;
2422 }
2423
2424 scale = gdk_pixbuf_scale_simple(buf, scale_width, scale_height,
2419 GDK_INTERP_BILINEAR); 2425 GDK_INTERP_BILINEAR);
2426 if (pidgin_gdk_pixbuf_is_opaque(scale))
2427 pidgin_gdk_pixbuf_make_round(scale);
2420 2428
2421 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->u.im->icon), scale); 2429 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->u.im->icon), scale);
2422 g_object_unref(G_OBJECT(scale)); 2430 g_object_unref(G_OBJECT(scale));
2423 gtk_widget_queue_draw(gtkconv->u.im->icon); 2431 gtk_widget_queue_draw(gtkconv->u.im->icon);
2424 2432
2812 { N_("/Conversation/_Remove..."), NULL, menu_add_remove_cb, 0, 2820 { N_("/Conversation/_Remove..."), NULL, menu_add_remove_cb, 0,
2813 "<StockItem>", GTK_STOCK_REMOVE }, 2821 "<StockItem>", GTK_STOCK_REMOVE },
2814 2822
2815 { "/Conversation/sep3", NULL, NULL, 0, "<Separator>", NULL }, 2823 { "/Conversation/sep3", NULL, NULL, 0, "<Separator>", NULL },
2816 2824
2825 { N_("/Conversation/Insert Lin_k..."), NULL, menu_insert_link_cb, 0,
2826 "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_LINK },
2827 { N_("/Conversation/Insert Imag_e..."), NULL, menu_insert_image_cb, 0,
2828 "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE },
2829
2830 { "/Conversation/sep4", NULL, NULL, 0, "<Separator>", NULL },
2831
2832
2817 { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0, 2833 { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0,
2818 "<StockItem>", GTK_STOCK_CLOSE }, 2834 "<StockItem>", GTK_STOCK_CLOSE },
2819 2835
2820 /* Options */ 2836 /* Options */
2821 { N_("/_Options"), NULL, NULL, 0, "<Branch>", NULL }, 2837 { N_("/_Options"), NULL, NULL, 0, "<Branch>", NULL },
2891 { 2907 {
2892 GtkWidget *menu; 2908 GtkWidget *menu;
2893 GList *list; 2909 GList *list;
2894 PidginConversation *gtkconv; 2910 PidginConversation *gtkconv;
2895 PurpleConversation *conv; 2911 PurpleConversation *conv;
2896 PurpleBuddy *buddy; 2912 PurpleBlistNode *node = NULL;
2913 PurpleChat *chat = NULL;
2914 PurpleBuddy *buddy = NULL;
2897 2915
2898 gtkconv = pidgin_conv_window_get_active_gtkconv(win); 2916 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
2899 conv = gtkconv->active_conv; 2917 conv = gtkconv->active_conv;
2900 buddy = purple_find_buddy(conv->account, conv->name);
2901 2918
2902 menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More")); 2919 menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More"));
2920
2921 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
2922 chat = purple_blist_find_chat(conv->account, conv->name);
2923
2924 if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
2925 chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
2926 }
2927
2928 if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
2929 GHashTable *components;
2930 components = g_hash_table_new_full(g_str_hash, g_str_equal,
2931 g_free, g_free);
2932 g_hash_table_replace(components, g_strdup("channel"),
2933 g_strdup(conv->name));
2934 chat = purple_chat_new(conv->account, NULL, components);
2935 purple_blist_node_set_flags((PurpleBlistNode *)chat,
2936 PURPLE_BLIST_NODE_FLAG_NO_SAVE);
2937 g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_chat",
2938 chat, (GDestroyNotify)purple_blist_remove_chat);
2939 }
2940 } else {
2941 buddy = purple_find_buddy(conv->account, conv->name);
2942
2943 /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle
2944 * removing "isolated" buddy nodes well */
2945 if (purple_version_check(2, 0, 2) == NULL) {
2946 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) {
2947 buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
2948 }
2949
2950 if ((buddy == NULL) && (gtkconv->imhtml != NULL)) {
2951 buddy = purple_buddy_new(conv->account, conv->name, NULL);
2952 purple_blist_node_set_flags((PurpleBlistNode *)buddy,
2953 PURPLE_BLIST_NODE_FLAG_NO_SAVE);
2954 g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy",
2955 buddy, (GDestroyNotify)purple_blist_remove_buddy);
2956 }
2957 }
2958 }
2959
2960 if (chat)
2961 node = (PurpleBlistNode *)chat;
2962 else if (buddy)
2963 node = (PurpleBlistNode *)buddy;
2903 2964
2904 /* Remove the previous entries */ 2965 /* Remove the previous entries */
2905 for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; ) 2966 for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; )
2906 { 2967 {
2907 GtkWidget *w = list->data; 2968 GtkWidget *w = list->data;
2908 list = list->next; 2969 list = list->next;
2909 gtk_widget_destroy(w); 2970 gtk_widget_destroy(w);
2910 } 2971 }
2911 2972
2912 /* Now add the stuff */ 2973 /* Now add the stuff */
2913 if (buddy) 2974 if (node)
2914 { 2975 {
2915 if (purple_account_is_connected(conv->account)) 2976 if (purple_account_is_connected(conv->account))
2916 pidgin_append_blist_node_proto_menu(menu, conv->account->gc, 2977 pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node);
2917 (PurpleBlistNode *)buddy); 2978 pidgin_append_blist_node_extended_menu(menu, node);
2918 pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
2919 } 2979 }
2920 2980
2921 if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL) 2981 if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL)
2922 { 2982 {
2923 GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available")); 2983 GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
2926 } 2986 }
2927 2987
2928 gtk_widget_show_all(menu); 2988 gtk_widget_show_all(menu);
2929 } 2989 }
2930 2990
2991 static void
2992 remove_from_list(GtkWidget *widget, PidginWindow *win)
2993 {
2994 GList *list = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
2995 list = g_list_remove(list, widget);
2996 g_object_set_data(G_OBJECT(win->window), "plugin-actions", list);
2997 }
2998
2999 static void
3000 regenerate_plugins_items(PidginWindow *win)
3001 {
3002 GList *action_items;
3003 GtkWidget *menu;
3004 GList *list;
3005 PidginConversation *gtkconv;
3006 PurpleConversation *conv;
3007 GtkWidget *item;
3008
3009 if (win->window == NULL || win == hidden_convwin)
3010 return;
3011
3012 gtkconv = pidgin_conv_window_get_active_gtkconv(win);
3013 if (gtkconv == NULL)
3014 return;
3015
3016 conv = gtkconv->active_conv;
3017 action_items = g_object_get_data(G_OBJECT(win->window), "plugin-actions");
3018
3019 /* Remove the old menuitems */
3020 while (action_items) {
3021 g_signal_handlers_disconnect_by_func(G_OBJECT(action_items->data),
3022 G_CALLBACK(remove_from_list), win);
3023 gtk_widget_destroy(action_items->data);
3024 action_items = g_list_delete_link(action_items, action_items);
3025 }
3026
3027 menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options"));
3028
3029 list = purple_conversation_get_extended_menu(conv);
3030 if (list) {
3031 action_items = g_list_prepend(NULL, (item = pidgin_separator(menu)));
3032 g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
3033 }
3034
3035 for(; list; list = g_list_delete_link(list, list)) {
3036 PurpleMenuAction *act = (PurpleMenuAction *) list->data;
3037 item = pidgin_append_menu_action(menu, act, conv);
3038 action_items = g_list_prepend(action_items, item);
3039 gtk_widget_show_all(item);
3040 g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win);
3041 }
3042 g_object_set_data(G_OBJECT(win->window), "plugin-actions", action_items);
3043 }
3044
2931 static void menubar_activated(GtkWidget *item, gpointer data) 3045 static void menubar_activated(GtkWidget *item, gpointer data)
2932 { 3046 {
2933 PidginWindow *win = data; 3047 PidginWindow *win = data;
2934 regenerate_options_items(win); 3048 regenerate_options_items(win);
3049 regenerate_plugins_items(win);
2935 3050
2936 /* The following are to make sure the 'More' submenu is not regenerated every time 3051 /* The following are to make sure the 'More' submenu is not regenerated every time
2937 * the focus shifts from 'Conversations' to some other menu and back. */ 3052 * the focus shifts from 'Conversations' to some other menu and back. */
2938 g_signal_handlers_block_by_func(G_OBJECT(item), G_CALLBACK(menubar_activated), data); 3053 g_signal_handlers_block_by_func(G_OBJECT(item), G_CALLBACK(menubar_activated), data);
2939 g_signal_connect(G_OBJECT(win->menu.menubar), "deactivate", G_CALLBACK(focus_out_from_menubar), data); 3054 g_signal_connect(G_OBJECT(win->menu.menubar), "deactivate", G_CALLBACK(focus_out_from_menubar), data);
3025 N_("/Conversation/Add...")); 3140 N_("/Conversation/Add..."));
3026 3141
3027 win->menu.remove = 3142 win->menu.remove =
3028 gtk_item_factory_get_widget(win->menu.item_factory, 3143 gtk_item_factory_get_widget(win->menu.item_factory,
3029 N_("/Conversation/Remove...")); 3144 N_("/Conversation/Remove..."));
3145
3146 /* --- */
3147
3148 win->menu.insert_link =
3149 gtk_item_factory_get_widget(win->menu.item_factory,
3150 N_("/Conversation/Insert Link..."));
3151
3152 win->menu.insert_image =
3153 gtk_item_factory_get_widget(win->menu.item_factory,
3154 N_("/Conversation/Insert Image..."));
3155
3156 /* --- */
3030 3157
3031 win->menu.logging = 3158 win->menu.logging =
3032 gtk_item_factory_get_widget(win->menu.item_factory, 3159 gtk_item_factory_get_widget(win->menu.item_factory,
3033 N_("/Options/Enable Logging")); 3160 N_("/Options/Enable Logging"));
3034 win->menu.sounds = 3161 win->menu.sounds =
3547 pixbuf = get_chat_buddy_status_icon(chat, name, flags); 3674 pixbuf = get_chat_buddy_status_icon(chat, name, flags);
3548 3675
3549 if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name))) 3676 if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name)))
3550 is_me = TRUE; 3677 is_me = TRUE;
3551 3678
3552 is_buddy = (purple_find_buddy(conv->account, name) != NULL); 3679 is_buddy = cb->buddy;
3553 3680
3554 tmp = g_utf8_casefold(alias, -1); 3681 tmp = g_utf8_casefold(alias, -1);
3555 alias_key = g_utf8_collate_key(tmp, -1); 3682 alias_key = g_utf8_collate_key(tmp, -1);
3556 g_free(tmp); 3683 g_free(tmp);
3557 3684
3755 gtk_tree_model_get(model, &iter, 3882 gtk_tree_model_get(model, &iter,
3756 CHAT_USERS_NAME_COLUMN, &name, 3883 CHAT_USERS_NAME_COLUMN, &name,
3757 CHAT_USERS_ALIAS_COLUMN, &alias, 3884 CHAT_USERS_ALIAS_COLUMN, &alias,
3758 -1); 3885 -1);
3759 3886
3760 if (strcmp(name, alias)) 3887 if (name && alias && strcmp(name, alias))
3761 tab_complete_process_item(&most_matched, entered, &partial, nick_partial, 3888 tab_complete_process_item(&most_matched, entered, &partial, nick_partial,
3762 &matches, FALSE, alias); 3889 &matches, FALSE, alias);
3763 g_free(name); 3890 g_free(name);
3764 g_free(alias); 3891 g_free(alias);
3765 3892
4124 4251
4125 gtk_widget_size_request(gtkconv->lower_hbox, &sr); 4252 gtk_widget_size_request(gtkconv->lower_hbox, &sr);
4126 if (sr.height < height + PIDGIN_HIG_BOX_SPACE) { 4253 if (sr.height < height + PIDGIN_HIG_BOX_SPACE) {
4127 gtkconv->auto_resize = TRUE; 4254 gtkconv->auto_resize = TRUE;
4128 gtkconv->entry_growing = TRUE; 4255 gtkconv->entry_growing = TRUE;
4129 gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE); 4256 gtk_widget_set_size_request(gtkconv->entry, -1, height);
4130 g_idle_add(reset_auto_resize_cb, gtkconv); 4257 g_idle_add(reset_auto_resize_cb, gtkconv);
4131 } 4258 }
4132 } 4259 }
4133 4260
4134 static GtkWidget * 4261 static void
4135 setup_chat_pane(PidginConversation *gtkconv) 4262 setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox)
4136 { 4263 {
4137 PurplePluginProtocolInfo *prpl_info;
4138 PurpleConversation *conv = gtkconv->active_conv; 4264 PurpleConversation *conv = gtkconv->active_conv;
4139 PidginChatPane *gtkchat; 4265 PurpleConnection *gc = purple_conversation_get_gc(conv);
4140 PurpleConnection *gc; 4266 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
4141 GtkWidget *vpaned, *hpaned;
4142 GtkWidget *vbox, *hbox, *frame;
4143 GtkWidget *imhtml_sw;
4144 GtkPolicyType imhtml_sw_hscroll;
4145 GtkWidget *lbox;
4146 GtkWidget *label;
4147 GtkWidget *list;
4148 GtkWidget *sw;
4149 GtkListStore *ls;
4150 GtkCellRenderer *rend;
4151 GtkTreeViewColumn *col;
4152 void *blist_handle = purple_blist_get_handle();
4153 GList *focus_chain = NULL;
4154 int ul_width;
4155
4156 gtkchat = gtkconv->u.chat;
4157 gc = purple_conversation_get_gc(conv);
4158 g_return_val_if_fail(gc != NULL, NULL);
4159 g_return_val_if_fail(gc->prpl != NULL, NULL);
4160 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
4161
4162 /* Setup the outer pane. */
4163 vpaned = gtk_vpaned_new();
4164 gtk_widget_show(vpaned);
4165
4166 /* Setup the top part of the pane. */
4167 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4168 gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, TRUE);
4169 gtk_widget_show(vbox);
4170
4171 if (prpl_info->options & OPT_PROTO_CHAT_TOPIC) 4267 if (prpl_info->options & OPT_PROTO_CHAT_TOPIC)
4172 { 4268 {
4269 GtkWidget *hbox, *label;
4270 PidginChatPane *gtkchat = gtkconv->u.chat;
4271
4173 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4272 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4174 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); 4273 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
4175 gtk_widget_show(hbox);
4176 4274
4177 label = gtk_label_new(_("Topic:")); 4275 label = gtk_label_new(_("Topic:"));
4178 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); 4276 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
4179 gtk_widget_show(label);
4180 4277
4181 gtkchat->topic_text = gtk_entry_new(); 4278 gtkchat->topic_text = gtk_entry_new();
4182 4279
4183 if(prpl_info->set_chat_topic == NULL) { 4280 if(prpl_info->set_chat_topic == NULL) {
4184 gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE); 4281 gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE);
4186 g_signal_connect(GTK_OBJECT(gtkchat->topic_text), "activate", 4283 g_signal_connect(GTK_OBJECT(gtkchat->topic_text), "activate",
4187 G_CALLBACK(topic_callback), gtkconv); 4284 G_CALLBACK(topic_callback), gtkconv);
4188 } 4285 }
4189 4286
4190 gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0); 4287 gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0);
4191 gtk_widget_show(gtkchat->topic_text); 4288 g_signal_connect(G_OBJECT(gtkchat->topic_text), "key_press_event",
4192 } 4289 G_CALLBACK(entry_key_press_cb), gtkconv);
4193 4290 }
4194 /* Setup the horizontal pane. */ 4291 }
4195 hpaned = gtk_hpaned_new(); 4292
4196 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); 4293 static void
4197 gtk_widget_show(hpaned); 4294 setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
4198 4295 {
4199 /* Setup gtkihmtml. */ 4296 PidginChatPane *gtkchat = gtkconv->u.chat;
4200 frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw); 4297 GtkWidget *lbox, *sw, *list;
4201 gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); 4298 GtkListStore *ls;
4202 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), TRUE); 4299 GtkCellRenderer *rend;
4203 gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); 4300 GtkTreeViewColumn *col;
4204 gtk_widget_show(frame); 4301 int ul_width;
4205 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), 4302 void *blist_handle = purple_blist_get_handle();
4206 &imhtml_sw_hscroll, NULL); 4303 PurpleConversation *conv = gtkconv->active_conv;
4207 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
4208 imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
4209
4210 gtk_widget_set_size_request(gtkconv->imhtml,
4211 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"),
4212 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"));
4213 g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
4214 G_CALLBACK(size_allocate_cb), gtkconv);
4215
4216 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
4217 G_CALLBACK(entry_stop_rclick_cb), NULL);
4218 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
4219 G_CALLBACK(refocus_entry_cb), gtkconv);
4220 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
4221 G_CALLBACK(refocus_entry_cb), gtkconv);
4222 4304
4223 /* Build the right pane. */ 4305 /* Build the right pane. */
4224 lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4306 lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4225 gtk_paned_pack2(GTK_PANED(hpaned), lbox, FALSE, TRUE); 4307 gtk_paned_pack2(GTK_PANED(hpaned), lbox, FALSE, TRUE);
4226 gtk_widget_show(lbox); 4308 gtk_widget_show(lbox);
4264 G_CALLBACK(right_click_chat_cb), gtkconv); 4346 G_CALLBACK(right_click_chat_cb), gtkconv);
4265 g_signal_connect(G_OBJECT(list), "popup-menu", 4347 g_signal_connect(G_OBJECT(list), "popup-menu",
4266 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv); 4348 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv);
4267 g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv); 4349 g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv);
4268 4350
4269
4270 rend = gtk_cell_renderer_text_new(); 4351 rend = gtk_cell_renderer_text_new();
4271
4272 g_object_set(rend, 4352 g_object_set(rend,
4273 "foreground-set", TRUE, 4353 "foreground-set", TRUE,
4274 "weight-set", TRUE, 4354 "weight-set", TRUE,
4275 NULL); 4355 NULL);
4276 col = gtk_tree_view_column_new_with_attributes(NULL, rend, 4356 col = gtk_tree_view_column_new_with_attributes(NULL, rend,
4297 gtk_widget_show(list); 4377 gtk_widget_show(list);
4298 4378
4299 gtkchat->list = list; 4379 gtkchat->list = list;
4300 4380
4301 gtk_container_add(GTK_CONTAINER(sw), list); 4381 gtk_container_add(GTK_CONTAINER(sw), list);
4382 }
4383
4384 static GtkWidget *
4385 setup_common_pane(PidginConversation *gtkconv)
4386 {
4387 GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box;
4388 GtkCellRenderer *rend;
4389 GtkTreePath *path;
4390 PurpleConversation *conv = gtkconv->active_conv;
4391 gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
4392 GtkPolicyType imhtml_sw_hscroll;
4393
4394 paned = gtk_vpaned_new();
4395 gtk_widget_show(paned);
4396
4397 /* Setup the top part of the pane */
4398 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4399 gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
4400 gtk_widget_show(vbox);
4401
4402 /* Setup the info pane */
4403 event_box = gtk_event_box_new();
4404 gtk_widget_show(event_box);
4405 gtkconv->infopane_hbox = gtk_hbox_new(FALSE, 0);
4406 gtk_box_pack_start(GTK_BOX(vbox), event_box, FALSE, FALSE, 0);
4407 gtk_container_add(GTK_CONTAINER(event_box), gtkconv->infopane_hbox);
4408 gtk_widget_show(gtkconv->infopane_hbox);
4409 gtk_widget_add_events(event_box,
4410 GDK_BUTTON1_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
4411 g_signal_connect(G_OBJECT(event_box), "button_press_event",
4412 G_CALLBACK(infopane_press_cb), gtkconv);
4413 g_signal_connect(G_OBJECT(event_box), "button_release_event",
4414 G_CALLBACK(infopane_release_cb), gtkconv);
4415
4416
4417 gtkconv->infopane = gtk_cell_view_new();
4418 gtkconv->infopane_model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
4419 gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane),
4420 GTK_TREE_MODEL(gtkconv->infopane_model));
4421 gtk_list_store_append(gtkconv->infopane_model, &(gtkconv->infopane_iter));
4422 gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), gtkconv->infopane, TRUE, TRUE, 0);
4423 path = gtk_tree_path_new_from_string("0");
4424 gtk_cell_view_set_displayed_row(GTK_CELL_VIEW(gtkconv->infopane), path);
4425 gtk_tree_path_free(path);
4426 gtk_widget_set_size_request(gtkconv->infopane, -1, 32);
4427 gtk_widget_show(gtkconv->infopane);
4428
4429 rend = gtk_cell_renderer_pixbuf_new();
4430 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
4431 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", ICON_COLUMN, NULL);
4432 g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
4433
4434 rend = gtk_cell_renderer_text_new();
4435 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, TRUE);
4436 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "markup", TEXT_COLUMN, NULL);
4437 g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
4438
4439 #if GTK_CHECK_VERSION(2, 6, 0)
4440 g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
4441 #endif
4442
4443 /* Setup the gtkimhtml widget */
4444 frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
4445 if (chat) {
4446 GtkWidget *hpaned;
4447
4448 /* Add the topic */
4449 setup_chat_topic(gtkconv, vbox);
4450
4451 /* Add the gtkimhtml frame */
4452 hpaned = gtk_hpaned_new();
4453 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
4454 gtk_widget_show(hpaned);
4455 gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
4456
4457 /* Now add the userlist */
4458 setup_chat_userlist(gtkconv, hpaned);
4459 } else {
4460 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
4461 }
4462 gtk_widget_show(frame);
4463
4464 gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
4465 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
4466
4467 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
4468 &imhtml_sw_hscroll, NULL);
4469 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
4470 imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
4471
4472 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
4473 G_CALLBACK(entry_stop_rclick_cb), NULL);
4474 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
4475 G_CALLBACK(refocus_entry_cb), gtkconv);
4476 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
4477 G_CALLBACK(refocus_entry_cb), gtkconv);
4302 4478
4303 /* Setup the bottom half of the conversation window */ 4479 /* Setup the bottom half of the conversation window */
4304 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4480 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4305 gtk_paned_pack2(GTK_PANED(vpaned), vbox, FALSE, TRUE); 4481 gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE);
4306 gtk_widget_show(vbox); 4482 gtk_widget_show(vbox);
4307 4483
4308 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); 4484 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4309 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0); 4485 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0);
4310 gtk_widget_show(gtkconv->lower_hbox); 4486 gtk_widget_show(gtkconv->lower_hbox);
4316 /* Setup the toolbar, entry widget and all signals */ 4492 /* Setup the toolbar, entry widget and all signals */
4317 frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL); 4493 frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
4318 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); 4494 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
4319 gtk_widget_show(frame); 4495 gtk_widget_show(frame);
4320 4496
4321 g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
4322 G_CALLBACK(entry_popup_menu_cb), gtkconv);
4323
4324 gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry"); 4497 gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
4325 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), 4498 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
4326 purple_account_get_protocol_name(conv->account)); 4499 purple_account_get_protocol_name(conv->account));
4327 gtk_widget_set_size_request(gtkconv->lower_hbox, -1, 4500 gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
4328 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height")); 4501 chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height") :
4329 gtkconv->entry_buffer = 4502 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
4330 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); 4503
4331 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); 4504 g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
4332 g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed", 4505 G_CALLBACK(entry_popup_menu_cb), gtkconv);
4333 G_CALLBACK(resize_imhtml_cb), gtkconv);
4334
4335 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", 4506 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event",
4336 G_CALLBACK(entry_key_press_cb), gtkconv); 4507 G_CALLBACK(entry_key_press_cb), gtkconv);
4337 g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", 4508 g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send",
4338 G_CALLBACK(send_cb), gtkconv); 4509 G_CALLBACK(send_cb), gtkconv);
4339 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", 4510 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event",
4340 G_CALLBACK(entry_stop_rclick_cb), NULL); 4511 G_CALLBACK(entry_stop_rclick_cb), NULL);
4341 g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate", 4512 g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate",
4342 G_CALLBACK(size_allocate_cb), gtkconv); 4513 G_CALLBACK(size_allocate_cb), gtkconv);
4343 4514
4344 default_formatize(gtkconv);
4345
4346 /*
4347 * Focus for chat windows should be as follows:
4348 * Tab title -> chat topic -> conversation scrollback -> user list ->
4349 * user list buttons -> entry -> buttons at bottom
4350 */
4351 focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
4352 gtk_container_set_focus_chain(GTK_CONTAINER(vbox), focus_chain);
4353
4354 return vpaned;
4355 }
4356
4357 static GtkWidget *
4358 setup_im_pane(PidginConversation *gtkconv)
4359 {
4360 PurpleConversation *conv = gtkconv->active_conv;
4361 GtkWidget *frame;
4362 GtkWidget *imhtml_sw;
4363 GtkPolicyType imhtml_sw_hscroll;
4364 GtkWidget *paned;
4365 GtkWidget *vbox;
4366 GtkWidget *vbox2;
4367 GList *focus_chain = NULL;
4368
4369 /* Setup the outer pane */
4370 paned = gtk_vpaned_new();
4371 gtk_widget_show(paned);
4372
4373 /* Setup the top part of the pane */
4374 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4375 gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE);
4376 gtk_widget_show(vbox);
4377
4378 /* Setup the gtkimhtml widget */
4379 frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
4380 gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
4381 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
4382 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
4383 gtk_widget_show(frame);
4384 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
4385 &imhtml_sw_hscroll, NULL);
4386 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
4387 imhtml_sw_hscroll, GTK_POLICY_ALWAYS);
4388
4389 gtk_widget_set_size_request(gtkconv->imhtml,
4390 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"),
4391 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"));
4392 g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate",
4393 G_CALLBACK(size_allocate_cb), gtkconv);
4394
4395 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
4396 G_CALLBACK(entry_stop_rclick_cb), NULL);
4397 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
4398 G_CALLBACK(refocus_entry_cb), gtkconv);
4399 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
4400 G_CALLBACK(refocus_entry_cb), gtkconv);
4401
4402 /* Setup the bottom half of the conversation window */
4403 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4404 gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, TRUE);
4405 gtk_widget_show(vbox2);
4406
4407 gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4408 gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->lower_hbox, TRUE, TRUE, 0);
4409 gtk_widget_show(gtkconv->lower_hbox);
4410
4411 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
4412 gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox2, TRUE, TRUE, 0);
4413 gtk_widget_show(vbox2);
4414
4415 /* Setup the toolbar, entry widget and all signals */
4416 frame = pidgin_create_imhtml(TRUE, &gtkconv->entry, &gtkconv->toolbar, NULL);
4417 gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0);
4418 gtk_widget_show(frame);
4419
4420 g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
4421 G_CALLBACK(entry_popup_menu_cb), gtkconv);
4422
4423 gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
4424 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry),
4425 purple_account_get_protocol_name(conv->account));
4426 gtk_widget_set_size_request(gtkconv->lower_hbox, -1,
4427 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height"));
4428 gtkconv->entry_buffer = 4515 gtkconv->entry_buffer =
4429 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); 4516 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
4430 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); 4517 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv);
4431 4518
4432 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", 4519 if (!chat) {
4433 G_CALLBACK(entry_key_press_cb), gtkconv); 4520 /* For sending typing notifications for IMs */
4434 g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", 4521 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
4435 G_CALLBACK(send_cb), gtkconv); 4522 G_CALLBACK(insert_text_cb), gtkconv);
4436 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", 4523 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
4437 G_CALLBACK(entry_stop_rclick_cb), NULL); 4524 G_CALLBACK(delete_text_cb), gtkconv);
4438 g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate", 4525 gtkconv->u.im->typing_timer = 0;
4439 G_CALLBACK(size_allocate_cb), gtkconv); 4526 gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
4440 4527 gtkconv->u.im->show_icon = TRUE;
4441 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text", 4528 }
4442 G_CALLBACK(insert_text_cb), gtkconv); 4529
4443 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
4444 G_CALLBACK(delete_text_cb), gtkconv);
4445 g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed", 4530 g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
4446 G_CALLBACK(resize_imhtml_cb), gtkconv); 4531 G_CALLBACK(resize_imhtml_cb), gtkconv);
4447 4532
4448 /* had to move this after the imtoolbar is attached so that the
4449 * signals get fired to toggle the buttons on the toolbar as well.
4450 */
4451 default_formatize(gtkconv); 4533 default_formatize(gtkconv);
4452
4453 g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear", 4534 g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear",
4454 G_CALLBACK(clear_formatting_cb), gtkconv); 4535 G_CALLBACK(clear_formatting_cb), gtkconv);
4455 4536
4456 gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons");
4457 gtkconv->u.im->show_icon = TRUE;
4458
4459 /*
4460 * Focus for IM windows should be as follows:
4461 * Tab title -> conversation scrollback -> entry
4462 */
4463 focus_chain = g_list_prepend(focus_chain, gtkconv->entry);
4464 gtk_container_set_focus_chain(GTK_CONTAINER(vbox2), focus_chain);
4465
4466 gtkconv->u.im->typing_timer = 0;
4467 return paned; 4537 return paned;
4468 } 4538 }
4469 4539
4470 static void 4540 static void
4471 conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, 4541 conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
4657 gtkconv->unseen_state = PIDGIN_UNSEEN_NONE; 4727 gtkconv->unseen_state = PIDGIN_UNSEEN_NONE;
4658 gtkconv->unseen_count = 0; 4728 gtkconv->unseen_count = 0;
4659 4729
4660 if (conv_type == PURPLE_CONV_TYPE_IM) { 4730 if (conv_type == PURPLE_CONV_TYPE_IM) {
4661 gtkconv->u.im = g_malloc0(sizeof(PidginImPane)); 4731 gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
4662
4663 pane = setup_im_pane(gtkconv);
4664 } else if (conv_type == PURPLE_CONV_TYPE_CHAT) { 4732 } else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
4665 gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane)); 4733 gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane));
4666 pane = setup_chat_pane(gtkconv); 4734 }
4667 } 4735 pane = setup_common_pane(gtkconv);
4668 4736
4669 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml), 4737 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
4670 gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE); 4738 gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
4671 4739
4672 if (pane == NULL) { 4740 if (pane == NULL) {
5060 g_return_if_fail(account != NULL); 5128 g_return_if_fail(account != NULL);
5061 gc = purple_account_get_connection(account); 5129 gc = purple_account_get_connection(account);
5062 g_return_if_fail(gc != NULL); 5130 g_return_if_fail(gc != NULL);
5063 5131
5064 /* Make sure URLs are clickable */ 5132 /* Make sure URLs are clickable */
5065 displaying = purple_markup_linkify(message); 5133 if(flags & PURPLE_MESSAGE_NO_LINKIFY)
5134 displaying = g_strdup(message);
5135 else
5136 displaying = purple_markup_linkify(message);
5137
5066 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( 5138 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
5067 pidgin_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ? 5139 pidgin_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ?
5068 "displaying-im-msg" : "displaying-chat-msg"), 5140 "displaying-im-msg" : "displaying-chat-msg"),
5069 account, name, &displaying, conv, flags)); 5141 account, name, &displaying, conv, flags));
5070 if (plugin_return) 5142 if (plugin_return)
5485 if (!purple_conv_chat_find_user(chat, old_name)) 5557 if (!purple_conv_chat_find_user(chat, old_name))
5486 return; 5558 return;
5487 5559
5488 g_return_if_fail(new_alias != NULL); 5560 g_return_if_fail(new_alias != NULL);
5489 5561
5490 cbuddy = purple_conv_chat_cb_new(new_name, new_alias, flags); 5562 cbuddy = purple_conv_chat_cb_find(chat, new_name);
5491 5563
5492 add_chat_buddy_common(conv, cbuddy, old_name); 5564 add_chat_buddy_common(conv, cbuddy, old_name);
5493 } 5565 }
5494 5566
5495 static void 5567 static void
5891 } else { 5963 } else {
5892 gtk_widget_show(win->menu.remove); 5964 gtk_widget_show(win->menu.remove);
5893 gtk_widget_hide(win->menu.add); 5965 gtk_widget_hide(win->menu.add);
5894 } 5966 }
5895 5967
5968 gtk_widget_show(win->menu.insert_link);
5969 gtk_widget_show(win->menu.insert_image);
5896 gtk_widget_show(win->menu.show_icon); 5970 gtk_widget_show(win->menu.show_icon);
5897 } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { 5971 } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
5898 /* Show stuff that applies to Chats, hide stuff that applies to IMs */ 5972 /* Show stuff that applies to Chats, hide stuff that applies to IMs */
5899 5973
5900 /* Deal with menu items */ 5974 /* Deal with menu items */
5916 /* If the chat IS in the buddy list */ 5990 /* If the chat IS in the buddy list */
5917 gtk_widget_hide(win->menu.add); 5991 gtk_widget_hide(win->menu.add);
5918 gtk_widget_show(win->menu.remove); 5992 gtk_widget_show(win->menu.remove);
5919 } 5993 }
5920 5994
5995 gtk_widget_show(win->menu.insert_link);
5996 gtk_widget_show(win->menu.insert_image);
5921 } 5997 }
5922 5998
5923 /* 5999 /*
5924 * Handle graying stuff out based on whether an account is connected 6000 * Handle graying stuff out based on whether an account is connected
5925 * and what features that account supports. 6001 * and what features that account supports.
5944 buttons &= ~GTK_IMHTML_LINKDESC; 6020 buttons &= ~GTK_IMHTML_LINKDESC;
5945 } else { 6021 } else {
5946 buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE; 6022 buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE;
5947 } 6023 }
5948 6024
5949 if (!(prpl_info->options & OPT_PROTO_IM_IMAGE) || 6025 if (!(prpl_info->options & OPT_PROTO_IM_IMAGE))
5950 conv->features & PURPLE_CONNECTION_NO_IMAGES) 6026 conv->features |= PURPLE_CONNECTION_NO_IMAGES;
6027
6028 if(conv->features & PURPLE_CONNECTION_NO_IMAGES)
5951 buttons &= ~GTK_IMHTML_IMAGE; 6029 buttons &= ~GTK_IMHTML_IMAGE;
5952 6030
5953 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); 6031 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons);
5954 if (account != NULL) 6032 if (account != NULL)
5955 gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account)); 6033 gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
5957 /* Deal with menu items */ 6035 /* Deal with menu items */
5958 gtk_widget_set_sensitive(win->menu.view_log, TRUE); 6036 gtk_widget_set_sensitive(win->menu.view_log, TRUE);
5959 gtk_widget_set_sensitive(win->menu.add_pounce, TRUE); 6037 gtk_widget_set_sensitive(win->menu.add_pounce, TRUE);
5960 gtk_widget_set_sensitive(win->menu.get_info, (prpl_info->get_info != NULL)); 6038 gtk_widget_set_sensitive(win->menu.get_info, (prpl_info->get_info != NULL));
5961 gtk_widget_set_sensitive(win->menu.invite, (prpl_info->chat_invite != NULL)); 6039 gtk_widget_set_sensitive(win->menu.invite, (prpl_info->chat_invite != NULL));
6040 gtk_widget_set_sensitive(win->menu.insert_link, (conv->features & PURPLE_CONNECTION_HTML));
6041 gtk_widget_set_sensitive(win->menu.insert_image, !(conv->features & PURPLE_CONNECTION_NO_IMAGES));
5962 6042
5963 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) 6043 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
5964 { 6044 {
5965 gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL)); 6045 gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL));
5966 gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL)); 6046 gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL));
5991 gtk_widget_set_sensitive(win->menu.get_info, FALSE); 6071 gtk_widget_set_sensitive(win->menu.get_info, FALSE);
5992 gtk_widget_set_sensitive(win->menu.invite, FALSE); 6072 gtk_widget_set_sensitive(win->menu.invite, FALSE);
5993 gtk_widget_set_sensitive(win->menu.alias, FALSE); 6073 gtk_widget_set_sensitive(win->menu.alias, FALSE);
5994 gtk_widget_set_sensitive(win->menu.add, FALSE); 6074 gtk_widget_set_sensitive(win->menu.add, FALSE);
5995 gtk_widget_set_sensitive(win->menu.remove, FALSE); 6075 gtk_widget_set_sensitive(win->menu.remove, FALSE);
6076 gtk_widget_set_sensitive(win->menu.insert_link, TRUE);
6077 gtk_widget_set_sensitive(win->menu.insert_image, FALSE);
5996 } 6078 }
5997 6079
5998 /* 6080 /*
5999 * Update the window's icon 6081 * Update the window's icon
6000 */ 6082 */
6074 6156
6075 if (fields & PIDGIN_CONV_SMILEY_THEME) 6157 if (fields & PIDGIN_CONV_SMILEY_THEME)
6076 pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml); 6158 pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
6077 6159
6078 if ((fields & PIDGIN_CONV_COLORIZE_TITLE) || 6160 if ((fields & PIDGIN_CONV_COLORIZE_TITLE) ||
6079 (fields & PIDGIN_CONV_SET_TITLE)) 6161 (fields & PIDGIN_CONV_SET_TITLE) ||
6162 (fields & PIDGIN_CONV_TOPIC))
6080 { 6163 {
6081 char *title; 6164 char *title;
6082 PurpleConvIm *im = NULL; 6165 PurpleConvIm *im = NULL;
6083 PurpleAccount *account = purple_conversation_get_account(conv); 6166 PurpleAccount *account = purple_conversation_get_account(conv);
6167 char *markup = NULL;
6084 AtkObject *accessibility_obj; 6168 AtkObject *accessibility_obj;
6085 /* I think this is a little longer than it needs to be but I'm lazy. */ 6169 /* I think this is a little longer than it needs to be but I'm lazy. */
6086 char style[51]; 6170 char style[51];
6087 6171
6088 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) 6172 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
6093 ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) 6177 ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
6094 && purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))) 6178 && purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))))
6095 title = g_strdup_printf("(%s)", purple_conversation_get_title(conv)); 6179 title = g_strdup_printf("(%s)", purple_conversation_get_title(conv));
6096 else 6180 else
6097 title = g_strdup(purple_conversation_get_title(conv)); 6181 title = g_strdup(purple_conversation_get_title(conv));
6182
6183 if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
6184 PurpleBuddy *buddy = purple_find_buddy(account, conv->name);
6185 if (buddy)
6186 markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE);
6187 else
6188 markup = title;
6189 } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
6190 PurpleConvChat *chat = PURPLE_CONV_CHAT(conv);
6191 const char *topic = purple_conv_chat_get_topic(chat);
6192 markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
6193 purple_conversation_get_title(conv),
6194 topic ? "\n" : "",
6195 pidgin_get_dim_grey_string(gtkconv->infopane),
6196 topic ? topic : "");
6197 }
6198 gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
6199 TEXT_COLUMN, markup, -1);
6200
6201 if (title != markup)
6202 g_free(markup);
6098 6203
6099 *style = '\0'; 6204 *style = '\0';
6100 6205
6101 if (!GTK_WIDGET_REALIZED(gtkconv->tab_label)) 6206 if (!GTK_WIDGET_REALIZED(gtkconv->tab_label))
6102 gtk_widget_realize(gtkconv->tab_label); 6207 gtk_widget_realize(gtkconv->tab_label);
6253 size_t len; 6358 size_t len;
6254 6359
6255 GdkPixbuf *buf; 6360 GdkPixbuf *buf;
6256 6361
6257 GtkWidget *event; 6362 GtkWidget *event;
6258 GtkWidget *frame;
6259 GdkPixbuf *scale; 6363 GdkPixbuf *scale;
6260 int scale_width, scale_height; 6364 int scale_width, scale_height;
6261 6365
6262 PurpleAccount *account; 6366 PurpleAccount *account;
6263 PurplePluginProtocolInfo *prpl_info = NULL; 6367 PurplePluginProtocolInfo *prpl_info = NULL;
6352 "Buddy icon error: %s\n", err->message); 6456 "Buddy icon error: %s\n", err->message);
6353 g_error_free(err); 6457 g_error_free(err);
6354 } 6458 }
6355 6459
6356 if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) { 6460 if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) {
6461 GdkPixbuf *stat;
6357 gtkconv->u.im->iter = NULL; 6462 gtkconv->u.im->iter = NULL;
6358 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); 6463 stat = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
6464 buf = gdk_pixbuf_add_alpha(stat, FALSE, 0, 0, 0);
6359 } else { 6465 } else {
6466 GdkPixbuf *stat;
6360 gtkconv->u.im->iter = 6467 gtkconv->u.im->iter =
6361 gdk_pixbuf_animation_get_iter(gtkconv->u.im->anim, NULL); /* LEAK */ 6468 gdk_pixbuf_animation_get_iter(gtkconv->u.im->anim, NULL); /* LEAK */
6362 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); 6469 stat = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter);
6470 buf = gdk_pixbuf_add_alpha(stat, FALSE, 0, 0, 0);
6363 if (gtkconv->u.im->animate) 6471 if (gtkconv->u.im->animate)
6364 start_anim(NULL, gtkconv); 6472 start_anim(NULL, gtkconv);
6365 } 6473 }
6366 6474
6367 pidgin_buddy_icon_get_scale_size(buf, &prpl_info->icon_spec, 6475 scale_width = gdk_pixbuf_get_width(buf);
6368 PURPLE_ICON_SCALE_DISPLAY, &scale_width, &scale_height); 6476 scale_height = gdk_pixbuf_get_height(buf);
6369 scale = gdk_pixbuf_scale_simple(buf, 6477 if (scale_width == scale_height) {
6370 MAX(gdk_pixbuf_get_width(buf) * scale_width / 6478 scale_width = scale_height = 32;
6371 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), 6479 } else if (scale_height > scale_width) {
6372 MAX(gdk_pixbuf_get_height(buf) * scale_height / 6480 scale_width = 32 * scale_width / scale_height;
6373 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), 6481 scale_height = 32;
6482 } else {
6483 scale_height = 32 * scale_height / scale_width;
6484 scale_width = 32;
6485 }
6486 scale = gdk_pixbuf_scale_simple(buf, scale_width, scale_height,
6374 GDK_INTERP_BILINEAR); 6487 GDK_INTERP_BILINEAR);
6375 6488 g_object_unref(buf);
6489 if (pidgin_gdk_pixbuf_is_opaque(scale))
6490 pidgin_gdk_pixbuf_make_round(scale);
6376 gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0); 6491 gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0);
6377 6492
6378 frame = gtk_frame_new(NULL);
6379 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
6380 gtk_box_pack_start(GTK_BOX(gtkconv->u.im->icon_container), frame,
6381 FALSE, FALSE, 0);
6382
6383 event = gtk_event_box_new(); 6493 event = gtk_event_box_new();
6384 gtk_container_add(GTK_CONTAINER(frame), event); 6494 gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
6385 g_signal_connect(G_OBJECT(event), "button-press-event", 6495 g_signal_connect(G_OBJECT(event), "button-press-event",
6386 G_CALLBACK(icon_menu), gtkconv); 6496 G_CALLBACK(icon_menu), gtkconv);
6387 gtk_widget_show(event); 6497 gtk_widget_show(event);
6388 6498
6389 gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale); 6499 gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale);
6390 gtkconv->auto_resize = TRUE; 6500 gtkconv->auto_resize = TRUE;
6391 /* Reset the size request to allow the buddy icon to resize */
6392 gtk_widget_set_size_request(gtkconv->lower_hbox, -1, -1);
6393 g_idle_add(reset_auto_resize_cb, gtkconv);
6394 gtk_widget_set_size_request(gtkconv->u.im->icon, scale_width, scale_height);
6395 gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon); 6501 gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon);
6396 gtk_widget_show(gtkconv->u.im->icon); 6502 gtk_widget_show(gtkconv->u.im->icon);
6397 6503
6398 g_object_unref(G_OBJECT(scale)); 6504 g_object_unref(G_OBJECT(scale));
6399 6505
6400 gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), 6506 gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox),
6401 gtkconv->u.im->icon_container, FALSE, FALSE, 0); 6507 gtkconv->u.im->icon_container, FALSE, FALSE, 0);
6402 6508
6403 gtk_widget_show(gtkconv->u.im->icon_container); 6509 gtk_widget_show(gtkconv->u.im->icon_container);
6404 gtk_widget_show(frame);
6405 6510
6406 /* The buddy icon code needs badly to be fixed. */ 6511 /* The buddy icon code needs badly to be fixed. */
6407 if(pidgin_conv_window_is_active_conversation(conv)) 6512 if(pidgin_conv_window_is_active_conversation(conv))
6408 { 6513 {
6409 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); 6514 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
6948 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/font_face", ""); 7053 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/font_face", "");
6949 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/font_size", 3); 7054 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/font_size", 3);
6950 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/tabs", TRUE); 7055 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/tabs", TRUE);
6951 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP); 7056 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP);
6952 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000); 7057 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000);
7058 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/x", 0);
7059 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/y", 0);
7060
7061 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font", TRUE);
7062 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", "");
6953 7063
6954 /* Conversations -> Chat */ 7064 /* Conversations -> Chat */
6955 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/chat"); 7065 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/chat");
6956 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", 410); 7066 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", 410);
6957 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", 160); 7067 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", 160);
7090 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL, 7200 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
7091 debug_command_cb, _("debug &lt;option&gt;: Send various debug information to the current conversation."), NULL); 7201 debug_command_cb, _("debug &lt;option&gt;: Send various debug information to the current conversation."), NULL);
7092 purple_cmd_register("clear", "", PURPLE_CMD_P_DEFAULT, 7202 purple_cmd_register("clear", "", PURPLE_CMD_P_DEFAULT,
7093 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL, 7203 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
7094 clear_command_cb, _("clear: Clears the conversation scrollback."), NULL); 7204 clear_command_cb, _("clear: Clears the conversation scrollback."), NULL);
7205 purple_cmd_register("clearall", "", PURPLE_CMD_P_DEFAULT,
7206 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
7207 clearall_command_cb, _("clear: Clears all conversation scrollbacks."), NULL);
7095 purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT, 7208 purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT,
7096 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL, 7209 PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
7097 help_command_cb, _("help &lt;command&gt;: Help on a specific command."), NULL); 7210 help_command_cb, _("help &lt;command&gt;: Help on a specific command."), NULL);
7098 7211
7099 /********************************************************************** 7212 /**********************************************************************
7494 } 7607 }
7495 7608
7496 /* 7609 /*
7497 * THANK YOU GALEON! 7610 * THANK YOU GALEON!
7498 */ 7611 */
7612
7613 static gboolean
7614 infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
7615 {
7616 int nb_x, nb_y;
7617
7618 if (e->button != 1 || e->type != GDK_BUTTON_PRESS)
7619 return FALSE;
7620
7621 if (gtkconv->win->in_drag) {
7622 purple_debug(PURPLE_DEBUG_WARNING, "gtkconv",
7623 "Already in the middle of a window drag at tab_press_cb\n");
7624 return TRUE;
7625 }
7626
7627 gtkconv->win->in_predrag = TRUE;
7628 gtkconv->win->drag_tab = gtk_notebook_page_num(GTK_NOTEBOOK(gtkconv->win->notebook), gtkconv->tab_cont);
7629
7630 gdk_window_get_origin(gtkconv->infopane_hbox->window, &nb_x, &nb_y);
7631
7632 gtkconv->win->drag_min_x = gtkconv->infopane_hbox->allocation.x + nb_x;
7633 gtkconv->win->drag_min_y = gtkconv->infopane_hbox->allocation.y + nb_y;
7634 gtkconv->win->drag_max_x = gtkconv->infopane_hbox->allocation.width + gtkconv->win->drag_min_x;
7635 gtkconv->win->drag_max_y = gtkconv->infopane_hbox->allocation.height + gtkconv->win->drag_min_y;
7636
7637
7638 /* Connect the new motion signals. */
7639 gtkconv->win->drag_motion_signal =
7640 g_signal_connect(G_OBJECT(gtkconv->win->notebook), "motion_notify_event",
7641 G_CALLBACK(notebook_motion_cb), gtkconv->win);
7642
7643 gtkconv->win->drag_leave_signal =
7644 g_signal_connect(G_OBJECT(gtkconv->win->notebook), "leave_notify_event",
7645 G_CALLBACK(notebook_leave_cb), gtkconv->win);
7646
7647 return FALSE;
7648
7649 }
7650
7499 static gboolean 7651 static gboolean
7500 notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win) 7652 notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
7501 { 7653 {
7502 gint nb_x, nb_y, x_rel, y_rel; 7654 gint nb_x, nb_y, x_rel, y_rel;
7503 int tab_clicked; 7655 int tab_clicked;
7579 7731
7580 win->drag_leave_signal = 7732 win->drag_leave_signal =
7581 g_signal_connect(G_OBJECT(widget), "leave_notify_event", 7733 g_signal_connect(G_OBJECT(widget), "leave_notify_event",
7582 G_CALLBACK(notebook_leave_cb), win); 7734 G_CALLBACK(notebook_leave_cb), win);
7583 7735
7736 return FALSE;
7737 }
7738
7739 static gboolean
7740 infopane_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
7741 {
7584 return FALSE; 7742 return FALSE;
7585 } 7743 }
7586 7744
7587 static gboolean 7745 static gboolean
7588 notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win) 7746 notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
7913 if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) { 8071 if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
7914 /* There's already an entry for alias. Let's not create another one. */ 8072 /* There's already an entry for alias. Let's not create another one. */
7915 return FALSE; 8073 return FALSE;
7916 } 8074 }
7917 8075
8076 if (!purple_account_is_connected(gtkconv->active_conv->account)) {
8077 /* Do not allow aliasing someone on a disconnected account. */
8078 return FALSE;
8079 }
8080
7918 /* alias label */ 8081 /* alias label */
7919 entry = gtk_entry_new(); 8082 entry = gtk_entry_new();
7920 gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE); 8083 gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
7921 gtk_entry_set_width_chars(GTK_ENTRY(entry), 10); 8084 gtk_entry_set_width_chars(GTK_ENTRY(entry), 10);
8085 #if GTK_CHECK_VERSION(2,4,0)
7922 gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5); 8086 gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5);
8087 #endif
7923 8088
7924 gtk_box_pack_start(GTK_BOX(gtkconv->tabby), entry, TRUE, TRUE, 0); 8089 gtk_box_pack_start(GTK_BOX(gtkconv->tabby), entry, TRUE, TRUE, 0);
7925 /* after the tab label */ 8090 /* after the tab label */
7926 gtk_box_reorder_child(GTK_BOX(gtkconv->tabby), entry, 2); 8091 gtk_box_reorder_child(GTK_BOX(gtkconv->tabby), entry, 2);
7927 8092
7962 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging), 8127 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging),
7963 purple_conversation_is_logging(conv)); 8128 purple_conversation_is_logging(conv));
7964 8129
7965 generate_send_to_items(win); 8130 generate_send_to_items(win);
7966 regenerate_options_items(win); 8131 regenerate_options_items(win);
8132 regenerate_plugins_items(win);
7967 8133
7968 pidgin_conv_switch_active_conversation(conv); 8134 pidgin_conv_switch_active_conversation(conv);
7969 8135
7970 sound_method = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"); 8136 sound_method = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method");
7971 if (strcmp(sound_method, "none") != 0) 8137 if (strcmp(sound_method, "none") != 0)
8032 offline_list = make_status_icon_list(PIDGIN_STOCK_STATUS_OFFLINE, w); 8198 offline_list = make_status_icon_list(PIDGIN_STOCK_STATUS_OFFLINE, w);
8033 away_list = make_status_icon_list(PIDGIN_STOCK_STATUS_AWAY, w); 8199 away_list = make_status_icon_list(PIDGIN_STOCK_STATUS_AWAY, w);
8034 prpl_lists = g_hash_table_new(g_str_hash, g_str_equal); 8200 prpl_lists = g_hash_table_new(g_str_hash, g_str_equal);
8035 } 8201 }
8036 8202
8203 static void
8204 plugin_changed_cb(PurplePlugin *p, gpointer data)
8205 {
8206 regenerate_plugins_items(data);
8207 }
8208
8209 static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) {
8210 int x, y;
8211
8212 if (GTK_WIDGET_VISIBLE(w))
8213 gtk_window_get_position(GTK_WINDOW(w), &x, &y);
8214 else
8215 return FALSE; /* carry on normally */
8216
8217 /* Workaround for GTK+ bug # 169811 - "configure_event" is fired
8218 * when the window is being maximized */
8219 if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
8220 return FALSE;
8221
8222 /* don't save if nothing changed */
8223 if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x") &&
8224 y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y") &&
8225 event->width == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width") &&
8226 event->height == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height"))
8227 return FALSE; /* carry on normally */
8228
8229 /* don't save off-screen positioning */
8230 if (x + event->width < 0 ||
8231 y + event->height < 0 ||
8232 x > gdk_screen_width() ||
8233 y > gdk_screen_height())
8234 return FALSE; /* carry on normally */
8235
8236 /* store the position */
8237 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x", x);
8238 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y", y);
8239 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width", event->width);
8240 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height);
8241
8242 /* continue to handle event normally */
8243 return FALSE;
8244
8245 }
8246
8037 PidginWindow * 8247 PidginWindow *
8038 pidgin_conv_window_new() 8248 pidgin_conv_window_new()
8039 { 8249 {
8040 PidginWindow *win; 8250 PidginWindow *win;
8041 GtkPositionType pos; 8251 GtkPositionType pos;
8045 win = g_malloc0(sizeof(PidginWindow)); 8255 win = g_malloc0(sizeof(PidginWindow));
8046 8256
8047 window_list = g_list_append(window_list, win); 8257 window_list = g_list_append(window_list, win);
8048 8258
8049 /* Create the window. */ 8259 /* Create the window. */
8050 win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 8260 win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
8051 gtk_window_set_role(GTK_WINDOW(win->window), "conversation"); 8261 gtk_window_set_default_size(GTK_WINDOW(win->window), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"),
8052 gtk_window_set_resizable(GTK_WINDOW(win->window), TRUE); 8262 purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height"));
8053 gtk_container_set_border_width(GTK_CONTAINER(win->window), 0);
8054 GTK_WINDOW(win->window)->allow_shrink = TRUE;
8055 8263
8056 if (available_list == NULL) { 8264 if (available_list == NULL) {
8057 create_icon_lists(win->window); 8265 create_icon_lists(win->window);
8058 } 8266 }
8059 8267
8060 g_signal_connect(G_OBJECT(win->window), "delete_event", 8268 g_signal_connect(G_OBJECT(win->window), "delete_event",
8061 G_CALLBACK(close_win_cb), win); 8269 G_CALLBACK(close_win_cb), win);
8062 8270 g_signal_connect(G_OBJECT(win->window), "configure_event",
8271 G_CALLBACK(gtk_conv_configure_cb), NULL);
8063 g_signal_connect(G_OBJECT(win->window), "focus_in_event", 8272 g_signal_connect(G_OBJECT(win->window), "focus_in_event",
8064 G_CALLBACK(focus_win_cb), win); 8273 G_CALLBACK(focus_win_cb), win);
8065 8274
8066 /* Create the notebook. */ 8275 /* Create the notebook. */
8067 win->notebook = gtk_notebook_new(); 8276 win->notebook = gtk_notebook_new();
8105 gtk_box_pack_start(GTK_BOX(testidea), win->notebook, TRUE, TRUE, 0); 8314 gtk_box_pack_start(GTK_BOX(testidea), win->notebook, TRUE, TRUE, 0);
8106 8315
8107 gtk_container_add(GTK_CONTAINER(win->window), testidea); 8316 gtk_container_add(GTK_CONTAINER(win->window), testidea);
8108 8317
8109 gtk_widget_show(testidea); 8318 gtk_widget_show(testidea);
8319
8320 /* Update the plugin actions when plugins are (un)loaded */
8321 purple_signal_connect(purple_plugins_get_handle(), "plugin-load",
8322 win, PURPLE_CALLBACK(plugin_changed_cb), win);
8323 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload",
8324 win, PURPLE_CALLBACK(plugin_changed_cb), win);
8325
8110 8326
8111 #ifdef _WIN32 8327 #ifdef _WIN32
8112 g_signal_connect(G_OBJECT(win->window), "show", 8328 g_signal_connect(G_OBJECT(win->window), "show",
8113 G_CALLBACK(winpidgin_ensure_onscreen), win->window); 8329 G_CALLBACK(winpidgin_ensure_onscreen), win->window);
8114 #endif 8330 #endif
8145 gtk_widget_destroy(win->window); 8361 gtk_widget_destroy(win->window);
8146 8362
8147 g_object_unref(G_OBJECT(win->menu.item_factory)); 8363 g_object_unref(G_OBJECT(win->menu.item_factory));
8148 8364
8149 purple_notify_close_with_handle(win); 8365 purple_notify_close_with_handle(win);
8366 purple_signals_disconnect_by_handle(win);
8150 8367
8151 g_free(win); 8368 g_free(win);
8152 } 8369 }
8153 8370
8154 void 8371 void
8260 8477
8261 if (pidgin_conv_window_get_gtkconv_count(win) == 1) { 8478 if (pidgin_conv_window_get_gtkconv_count(win) == 1) {
8262 /* Er, bug in notebooks? Switch to the page manually. */ 8479 /* Er, bug in notebooks? Switch to the page manually. */
8263 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0); 8480 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
8264 8481
8265 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), 8482 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
8266 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs"));
8267 } else 8483 } else
8268 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE); 8484 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE);
8269 8485
8270 focus_gtkconv = g_list_nth_data(pidgin_conv_window_get_gtkconvs(win), 8486 focus_gtkconv = g_list_nth_data(pidgin_conv_window_get_gtkconvs(win),
8271 gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook))); 8487 gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)));
8324 first = gtkconv->icon; 8540 first = gtkconv->icon;
8325 third = gtkconv->close; 8541 third = gtkconv->close;
8326 } 8542 }
8327 8543
8328 ebox = gtk_event_box_new(); 8544 ebox = gtk_event_box_new();
8545 #if GTK_CHECK_VERSION(2,4,0)
8329 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); 8546 gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
8547 #endif
8330 gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby); 8548 gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby);
8331 g_signal_connect(G_OBJECT(ebox), "button-press-event", 8549 g_signal_connect(G_OBJECT(ebox), "button-press-event",
8332 G_CALLBACK(alias_double_click_cb), gtkconv); 8550 G_CALLBACK(alias_double_click_cb), gtkconv);
8333 8551
8334 if (gtkconv->tab_label->parent == NULL) { 8552 if (gtkconv->tab_label->parent == NULL) {
8353 } 8571 }
8354 8572
8355 gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START); 8573 gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START);
8356 8574
8357 /* show the widgets */ 8575 /* show the widgets */
8358 gtk_widget_show(gtkconv->icon); 8576 /* XXX gtk_widget_show(gtkconv->icon); */
8359 gtk_widget_show(gtkconv->tab_label); 8577 gtk_widget_show(gtkconv->tab_label);
8360 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs")) 8578 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs"))
8361 gtk_widget_show(gtkconv->close); 8579 gtk_widget_show(gtkconv->close);
8362 gtk_widget_show(gtkconv->tabby); 8580 gtk_widget_show(gtkconv->tabby);
8363 gtk_widget_show(ebox); 8581 gtk_widget_show(ebox);
8375 g_object_ref(gtkconv->tab_cont); 8593 g_object_ref(gtkconv->tab_cont);
8376 gtk_object_sink(GTK_OBJECT(gtkconv->tab_cont)); 8594 gtk_object_sink(GTK_OBJECT(gtkconv->tab_cont));
8377 8595
8378 gtk_notebook_remove_page(GTK_NOTEBOOK(win->notebook), index); 8596 gtk_notebook_remove_page(GTK_NOTEBOOK(win->notebook), index);
8379 8597
8380 /* go back to tabless if need be */ 8598 /* go back to tabless */
8381 if (pidgin_conv_window_get_gtkconv_count(win) <= 2) { 8599 if (pidgin_conv_window_get_gtkconv_count(win) <= 2) {
8382 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), 8600 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
8383 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs"));
8384 } 8601 }
8385 8602
8386 win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv); 8603 win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv);
8604
8605 if (!win->gtkconvs || !win->gtkconvs->next)
8606 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
8387 8607
8388 if (!win->gtkconvs && win != hidden_convwin) 8608 if (!win->gtkconvs && win != hidden_convwin)
8389 pidgin_conv_window_destroy(win); 8609 pidgin_conv_window_destroy(win);
8390 } 8610 }
8391 8611

mercurial