--- a/pidgin/gtkconv.c Sat May 26 13:48:41 2007 +0000 +++ b/pidgin/gtkconv.c Sun Jul 01 00:55:03 2007 +0000 @@ -49,6 +49,7 @@ #include "prpl.h" #include "request.h" #include "util.h" +#include "version.h" #include "gtkdnd-hints.h" #include "gtkblist.h" @@ -81,6 +82,12 @@ PIDGIN_CONV_COLORIZE_TITLE = 1 << 6 }PidginConvFields; +enum { + ICON_COLUMN, + TEXT_COLUMN, + NUM_COLUMNS +} PidginInfopaneColumns; + #define PIDGIN_CONV_ALL ((1 << 7) - 1) #define SEND_COLOR "#204a87" @@ -177,6 +184,8 @@ static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields); static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win); static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); +static gboolean infopane_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); +static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) { static GdkColor col; @@ -235,6 +244,10 @@ return FALSE; } + if (gdk_window_get_state(gtkconv->win->window->window) & GDK_WINDOW_STATE_MAXIMIZED) { + return FALSE; + } + /* I find that I resize the window when it has a bunch of conversations in it, mostly so that the * tab bar will fit, but then I don't want new windows taking up the entire screen. I check to see * if there is only one conversation in the window. This way we'll be setting new windows to the @@ -243,19 +256,11 @@ * negate it anyway. --luke */ if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - if (w == gtkconv->imhtml) { - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width", allocation->width); - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height", allocation->height); - } if (w == gtkconv->lower_hbox) purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height", allocation->height); } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { - if (w == gtkconv->imhtml) { - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width", allocation->width); - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height", allocation->height); - } if (w == gtkconv->lower_hbox) purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height); } @@ -267,65 +272,7 @@ default_formatize(PidginConversation *c) { PurpleConversation *conv = c->active_conv; - - if (conv->features & PURPLE_CONNECTION_HTML) - { - char color[8]; - GdkColor fg_color, bg_color; - - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != GTK_IMHTML(c->entry)->edit.bold) - gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry)); - - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != GTK_IMHTML(c->entry)->edit.italic) - gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry)); - - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != GTK_IMHTML(c->entry)->edit.underline) - gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry)); - - gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry), - purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face")); - - if (!(conv->features & PURPLE_CONNECTION_NO_FONTSIZE)) - { - int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size"); - - /* 3 is the default. */ - if (size != 3) - gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), size); - } - - if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0) - { - gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), - &fg_color); - g_snprintf(color, sizeof(color), "#%02x%02x%02x", - fg_color.red / 256, - fg_color.green / 256, - fg_color.blue / 256); - } else - strcpy(color, ""); - - gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color); - - if(!(conv->features & PURPLE_CONNECTION_NO_BGCOLOR) && - strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0) - { - gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), - &bg_color); - g_snprintf(color, sizeof(color), "#%02x%02x%02x", - bg_color.red / 256, - bg_color.green / 256, - bg_color.blue / 256); - } else - strcpy(color, ""); - - gtk_imhtml_toggle_background(GTK_IMHTML(c->entry), color); - - if (conv->features & PURPLE_CONNECTION_FORMATTING_WBFO) - gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), TRUE); - else - gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), FALSE); - } + gtk_imhtml_setup_entry(GTK_IMHTML(c->entry), conv->features); } static void @@ -392,15 +339,28 @@ } } -static PurpleCmdRet -clear_command_cb(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) +static void clear_conversation_scrollback(PurpleConversation *conv) { PidginConversation *gtkconv = NULL; gtkconv = PIDGIN_CONVERSATION(conv); gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); +} + +static PurpleCmdRet +clear_command_cb(PurpleConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + clear_conversation_scrollback(conv); + return PURPLE_CMD_STATUS_OK; +} + +static PurpleCmdRet +clearall_command_cb(PurpleConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + purple_conversation_foreach(clear_conversation_scrollback); return PURPLE_CMD_STATUS_OK; } @@ -473,6 +433,7 @@ char *cmd; const char *prefix; GtkTextIter start; + gboolean retval = FALSE; gtkconv = PIDGIN_CONVERSATION(conv); prefix = pidgin_get_cmd_prefix(); @@ -496,24 +457,50 @@ gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end); markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end); status = purple_cmd_do_command(conv, cmdline, markup, &error); - g_free(cmd); g_free(markup); switch (status) { case PURPLE_CMD_STATUS_OK: - return TRUE; + retval = TRUE; + break; case PURPLE_CMD_STATUS_NOT_FOUND: - return FALSE; + { + PurplePluginProtocolInfo *prpl_info = NULL; + PurpleConnection *gc; + + if ((gc = purple_conversation_get_gc(conv))) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if ((prpl_info != NULL) && (prpl_info->options & OPT_PROTO_SLASH_COMMANDS_NATIVE)) { + char *firstspace; + char *slash; + + firstspace = strchr(cmdline, ' '); + if (firstspace != NULL) { + slash = strrchr(firstspace, '/'); + } else { + slash = strchr(cmdline, '/'); + } + + if (slash == NULL) { + purple_conversation_write(conv, "", _("Unknown command."), PURPLE_MESSAGE_NO_LOG, time(NULL)); + retval = TRUE; + } + } + break; + } case PURPLE_CMD_STATUS_WRONG_ARGS: purple_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments " "to that command."), PURPLE_MESSAGE_NO_LOG, time(NULL)); - return TRUE; + retval = TRUE; + break; case PURPLE_CMD_STATUS_FAILED: purple_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."), PURPLE_MESSAGE_NO_LOG, time(NULL)); g_free(error); - return TRUE; + retval = TRUE; + break; case PURPLE_CMD_STATUS_WRONG_TYPE: if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) purple_conversation_write(conv, "", _("That command only works in chats, not IMs."), @@ -521,16 +508,18 @@ else purple_conversation_write(conv, "", _("That command only works in IMs, not chats."), PURPLE_MESSAGE_NO_LOG, time(NULL)); - return TRUE; + retval = TRUE; + break; case PURPLE_CMD_STATUS_WRONG_PRPL: purple_conversation_write(conv, "", _("That command doesn't work on this protocol."), PURPLE_MESSAGE_NO_LOG, time(NULL)); - return TRUE; + retval = TRUE; + break; } } g_free(cmd); - return FALSE; + return retval; } static void @@ -544,13 +533,6 @@ account = purple_conversation_get_account(conv); - if (!purple_account_is_connected(account)) - return; - - if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && - purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) - return; - if (check_for_and_do_command(conv)) { if (gtkconv->entry_growing) { reset_default_size(gtkconv); @@ -560,6 +542,13 @@ return; } + if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && + purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) + return; + + if (!purple_account_is_connected(account)) + return; + buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); @@ -646,23 +635,10 @@ static void chat_do_info(PidginConversation *gtkconv, const char *who) { PurpleConversation *conv = gtkconv->active_conv; - PurplePluginProtocolInfo *prpl_info = NULL; PurpleConnection *gc; if ((gc = purple_conversation_get_gc(conv))) { - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); - - /* - * If there are special needs for getting info on users in - * buddy chat "rooms"... - */ - if (prpl_info->get_cb_info != NULL) - { - prpl_info->get_cb_info(gc, - purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who); - } - else - prpl_info->get_info(gc, who); + pidgin_retrieve_user_info_in_chat(gc, who, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv))); } } @@ -673,14 +649,8 @@ PurpleConversation *conv = gtkconv->active_conv; if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - PurpleNotifyUserInfo *info = purple_notify_user_info_new(); - purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving...")); - purple_notify_userinfo(conv->account->gc, purple_conversation_get_name(conv), info, NULL, NULL); - purple_notify_user_info_destroy(info); - - serv_get_info(purple_conversation_get_gc(conv), + pidgin_retrieve_user_info(purple_conversation_get_gc(conv), purple_conversation_get_name(conv)); - gtk_widget_grab_focus(gtkconv->entry); } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { /* Get info of the person currently selected in the GtkTreeView */ @@ -1243,6 +1213,37 @@ } static void +menu_insert_link_cb(gpointer data, guint action, GtkWidget *widget) +{ + PidginWindow *win = data; + PidginConversation *gtkconv; + GtkIMHtmlToolbar *toolbar; + + gtkconv = pidgin_conv_window_get_active_gtkconv(win); + toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))); +} + +static void +menu_insert_image_cb(gpointer data, guint action, GtkWidget *widget) +{ + PidginWindow *win = data; + PurpleConversation *conv; + PidginConversation *gtkconv; + GtkIMHtmlToolbar *toolbar; + + gtkconv = pidgin_conv_window_get_active_gtkconv(win); + conv = gtkconv->active_conv; + toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))); +} + + +static void menu_alias_cb(gpointer data, guint action, GtkWidget *widget) { PidginWindow *win = data; @@ -1740,6 +1741,9 @@ gtk_tree_selection_select_path(GTK_TREE_SELECTION( gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkchat->list), + path, NULL, FALSE); + gtk_widget_grab_focus(GTK_WIDGET(gtkchat->list)); gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); @@ -1821,6 +1825,9 @@ if (!gtkconv->send_history) break; + if (gtkconv->entry != entry) + break; + if (!gtkconv->send_history->prev) { GtkTextIter start, end; @@ -1869,6 +1876,9 @@ if (!gtkconv->send_history) break; + if (gtkconv->entry != entry) + break; + if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) { GObject *object; GtkTextIter iter; @@ -1972,6 +1982,8 @@ switch (event->keyval) { case GDK_Tab: + if (gtkconv->entry != entry) + break; return tab_complete(conv); break; @@ -2048,6 +2060,9 @@ return TRUE; } +static void +regenerate_options_items(PidginWindow *win); + void pidgin_conv_switch_active_conversation(PurpleConversation *conv) { @@ -2148,6 +2163,8 @@ gray_stuff_out(gtkconv); update_typing_icon(gtkconv); + g_object_set_data(G_OBJECT(entry), "transient_buddy", NULL); + regenerate_options_items(gtkconv->win); gtk_window_set_title(GTK_WINDOW(gtkconv->win->window), gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); @@ -2224,34 +2241,18 @@ static GList *get_prpl_icon_list(PurpleAccount *account) { GList *l = NULL; - GdkPixbuf *pixbuf; - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account))); - const char *prpl = prpl_info->list_icon(account, NULL); - char *filename, *path; - l = g_hash_table_lookup(prpl_lists, prpl); + PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account)); + PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + const char *prplname = prpl_info->list_icon(account, NULL); + l = g_hash_table_lookup(prpl_lists, prplname); if (l) return l; - filename = g_strdup_printf("%s.png", prpl); - - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", filename, NULL); - pixbuf = gdk_pixbuf_new_from_file(path, NULL); - if (pixbuf) - l = g_list_append(l, pixbuf); - g_free(path); - - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", filename, NULL); - pixbuf = gdk_pixbuf_new_from_file(path, NULL); - if (pixbuf) - l = g_list_append(l, pixbuf); - g_free(path); - - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "48", filename, NULL); - pixbuf = gdk_pixbuf_new_from_file(path, NULL); - if (pixbuf) - l = g_list_append(l, pixbuf); - g_free(path); - - g_hash_table_insert(prpl_lists, g_strdup(prpl), l); + + l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE)); + l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM)); + l = g_list_prepend(l, pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL)); + + g_hash_table_insert(prpl_lists, g_strdup(prplname), l); return l; } @@ -2352,6 +2353,10 @@ gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status); gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status); + gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model), + &(gtkconv->infopane_iter), + ICON_COLUMN, status, -1); + if (status != NULL) g_object_unref(status); @@ -2401,22 +2406,25 @@ return FALSE; } - gtkconv->auto_resize = TRUE; - g_idle_add(reset_auto_resize_cb, gtkconv); - gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL); buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); - pidgin_buddy_icon_get_scale_size(buf, &prpl_info->icon_spec, - PURPLE_ICON_SCALE_DISPLAY, &scale_width, &scale_height); - - /* this code is ugly, and scares me */ - scale = gdk_pixbuf_scale_simple(buf, - MAX(gdk_pixbuf_get_width(buf) * scale_width / - gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), - MAX(gdk_pixbuf_get_height(buf) * scale_height / - gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), + scale_width = gdk_pixbuf_get_width(buf); + scale_height = gdk_pixbuf_get_height(buf); + if (scale_width == scale_height) { + scale_width = scale_height = 32; + } else if (scale_height > scale_width) { + scale_width = 32 * scale_width / scale_height; + scale_height = 32; + } else { + scale_height = 32 * scale_height / scale_width; + scale_width = 32; + } + + scale = gdk_pixbuf_scale_simple(buf, scale_width, scale_height, GDK_INTERP_BILINEAR); + if (pidgin_gdk_pixbuf_is_opaque(scale)) + pidgin_gdk_pixbuf_make_round(scale); gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->u.im->icon), scale); g_object_unref(G_OBJECT(scale)); @@ -2814,6 +2822,14 @@ { "/Conversation/sep3", NULL, NULL, 0, "<Separator>", NULL }, + { N_("/Conversation/Insert Lin_k..."), NULL, menu_insert_link_cb, 0, + "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_LINK }, + { N_("/Conversation/Insert Imag_e..."), NULL, menu_insert_image_cb, 0, + "<StockItem>", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE }, + + { "/Conversation/sep4", NULL, NULL, 0, "<Separator>", NULL }, + + { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0, "<StockItem>", GTK_STOCK_CLOSE }, @@ -2893,14 +2909,59 @@ GList *list; PidginConversation *gtkconv; PurpleConversation *conv; - PurpleBuddy *buddy; + PurpleBlistNode *node = NULL; + PurpleChat *chat = NULL; + PurpleBuddy *buddy = NULL; gtkconv = pidgin_conv_window_get_active_gtkconv(win); conv = gtkconv->active_conv; - buddy = purple_find_buddy(conv->account, conv->name); menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More")); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + chat = purple_blist_find_chat(conv->account, conv->name); + + if ((chat == NULL) && (gtkconv->imhtml != NULL)) { + chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat"); + } + + if ((chat == NULL) && (gtkconv->imhtml != NULL)) { + GHashTable *components; + components = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + g_hash_table_replace(components, g_strdup("channel"), + g_strdup(conv->name)); + chat = purple_chat_new(conv->account, NULL, components); + purple_blist_node_set_flags((PurpleBlistNode *)chat, + PURPLE_BLIST_NODE_FLAG_NO_SAVE); + g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_chat", + chat, (GDestroyNotify)purple_blist_remove_chat); + } + } else { + buddy = purple_find_buddy(conv->account, conv->name); + + /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle + * removing "isolated" buddy nodes well */ + if (purple_version_check(2, 0, 2) == NULL) { + if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { + buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy"); + } + + if ((buddy == NULL) && (gtkconv->imhtml != NULL)) { + buddy = purple_buddy_new(conv->account, conv->name, NULL); + purple_blist_node_set_flags((PurpleBlistNode *)buddy, + PURPLE_BLIST_NODE_FLAG_NO_SAVE); + g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy", + buddy, (GDestroyNotify)purple_blist_remove_buddy); + } + } + } + + if (chat) + node = (PurpleBlistNode *)chat; + else if (buddy) + node = (PurpleBlistNode *)buddy; + /* Remove the previous entries */ for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; ) { @@ -2910,12 +2971,11 @@ } /* Now add the stuff */ - if (buddy) + if (node) { if (purple_account_is_connected(conv->account)) - pidgin_append_blist_node_proto_menu(menu, conv->account->gc, - (PurpleBlistNode *)buddy); - pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy); + pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node); + pidgin_append_blist_node_extended_menu(menu, node); } if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL) @@ -2928,10 +2988,65 @@ gtk_widget_show_all(menu); } +static void +remove_from_list(GtkWidget *widget, PidginWindow *win) +{ + GList *list = g_object_get_data(G_OBJECT(win->window), "plugin-actions"); + list = g_list_remove(list, widget); + g_object_set_data(G_OBJECT(win->window), "plugin-actions", list); +} + +static void +regenerate_plugins_items(PidginWindow *win) +{ + GList *action_items; + GtkWidget *menu; + GList *list; + PidginConversation *gtkconv; + PurpleConversation *conv; + GtkWidget *item; + + if (win->window == NULL || win == hidden_convwin) + return; + + gtkconv = pidgin_conv_window_get_active_gtkconv(win); + if (gtkconv == NULL) + return; + + conv = gtkconv->active_conv; + action_items = g_object_get_data(G_OBJECT(win->window), "plugin-actions"); + + /* Remove the old menuitems */ + while (action_items) { + g_signal_handlers_disconnect_by_func(G_OBJECT(action_items->data), + G_CALLBACK(remove_from_list), win); + gtk_widget_destroy(action_items->data); + action_items = g_list_delete_link(action_items, action_items); + } + + menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options")); + + list = purple_conversation_get_extended_menu(conv); + if (list) { + action_items = g_list_prepend(NULL, (item = pidgin_separator(menu))); + g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win); + } + + for(; list; list = g_list_delete_link(list, list)) { + PurpleMenuAction *act = (PurpleMenuAction *) list->data; + item = pidgin_append_menu_action(menu, act, conv); + action_items = g_list_prepend(action_items, item); + gtk_widget_show_all(item); + g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(remove_from_list), win); + } + g_object_set_data(G_OBJECT(win->window), "plugin-actions", action_items); +} + static void menubar_activated(GtkWidget *item, gpointer data) { PidginWindow *win = data; regenerate_options_items(win); + regenerate_plugins_items(win); /* The following are to make sure the 'More' submenu is not regenerated every time * the focus shifts from 'Conversations' to some other menu and back. */ @@ -3028,6 +3143,18 @@ gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/Remove...")); + /* --- */ + + win->menu.insert_link = + gtk_item_factory_get_widget(win->menu.item_factory, + N_("/Conversation/Insert Link...")); + + win->menu.insert_image = + gtk_item_factory_get_widget(win->menu.item_factory, + N_("/Conversation/Insert Image...")); + + /* --- */ + win->menu.logging = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Options/Enable Logging")); @@ -3549,7 +3676,7 @@ if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name))) is_me = TRUE; - is_buddy = (purple_find_buddy(conv->account, name) != NULL); + is_buddy = cb->buddy; tmp = g_utf8_casefold(alias, -1); alias_key = g_utf8_collate_key(tmp, -1); @@ -3757,7 +3884,7 @@ CHAT_USERS_ALIAS_COLUMN, &alias, -1); - if (strcmp(name, alias)) + if (name && alias && strcmp(name, alias)) tab_complete_process_item(&most_matched, entered, &partial, nick_partial, &matches, FALSE, alias); g_free(name); @@ -4126,57 +4253,27 @@ if (sr.height < height + PIDGIN_HIG_BOX_SPACE) { gtkconv->auto_resize = TRUE; gtkconv->entry_growing = TRUE; - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height + PIDGIN_HIG_BOX_SPACE); + gtk_widget_set_size_request(gtkconv->entry, -1, height); g_idle_add(reset_auto_resize_cb, gtkconv); } } -static GtkWidget * -setup_chat_pane(PidginConversation *gtkconv) -{ - PurplePluginProtocolInfo *prpl_info; +static void +setup_chat_topic(PidginConversation *gtkconv, GtkWidget *vbox) +{ PurpleConversation *conv = gtkconv->active_conv; - PidginChatPane *gtkchat; - PurpleConnection *gc; - GtkWidget *vpaned, *hpaned; - GtkWidget *vbox, *hbox, *frame; - GtkWidget *imhtml_sw; - GtkPolicyType imhtml_sw_hscroll; - GtkWidget *lbox; - GtkWidget *label; - GtkWidget *list; - GtkWidget *sw; - GtkListStore *ls; - GtkCellRenderer *rend; - GtkTreeViewColumn *col; - void *blist_handle = purple_blist_get_handle(); - GList *focus_chain = NULL; - int ul_width; - - gtkchat = gtkconv->u.chat; - gc = purple_conversation_get_gc(conv); - g_return_val_if_fail(gc != NULL, NULL); - g_return_val_if_fail(gc->prpl != NULL, NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); - - /* Setup the outer pane. */ - vpaned = gtk_vpaned_new(); - gtk_widget_show(vpaned); - - /* Setup the top part of the pane. */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, TRUE); - gtk_widget_show(vbox); - + PurpleConnection *gc = purple_conversation_get_gc(conv); + PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); if (prpl_info->options & OPT_PROTO_CHAT_TOPIC) { + GtkWidget *hbox, *label; + PidginChatPane *gtkchat = gtkconv->u.chat; + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show(hbox); label = gtk_label_new(_("Topic:")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - gtk_widget_show(label); gtkchat->topic_text = gtk_entry_new(); @@ -4188,37 +4285,22 @@ } gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0); - gtk_widget_show(gtkchat->topic_text); - } - - /* Setup the horizontal pane. */ - hpaned = gtk_hpaned_new(); - gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); - gtk_widget_show(hpaned); - - /* Setup gtkihmtml. */ - frame = pidgin_create_imhtml(FALSE, >kconv->imhtml, NULL, &imhtml_sw); - gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); - gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), TRUE); - gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); - gtk_widget_show(frame); - gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), - &imhtml_sw_hscroll, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw), - imhtml_sw_hscroll, GTK_POLICY_ALWAYS); - - gtk_widget_set_size_request(gtkconv->imhtml, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"), - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/default_height")); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate", - G_CALLBACK(size_allocate_cb), gtkconv); - - g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", - G_CALLBACK(entry_stop_rclick_cb), NULL); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", - G_CALLBACK(refocus_entry_cb), gtkconv); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", - G_CALLBACK(refocus_entry_cb), gtkconv); + g_signal_connect(G_OBJECT(gtkchat->topic_text), "key_press_event", + G_CALLBACK(entry_key_press_cb), gtkconv); + } +} + +static void +setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned) +{ + PidginChatPane *gtkchat = gtkconv->u.chat; + GtkWidget *lbox, *sw, *list; + GtkListStore *ls; + GtkCellRenderer *rend; + GtkTreeViewColumn *col; + int ul_width; + void *blist_handle = purple_blist_get_handle(); + PurpleConversation *conv = gtkconv->active_conv; /* Build the right pane. */ lbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); @@ -4266,9 +4348,7 @@ G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv); g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv); - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "foreground-set", TRUE, "weight-set", TRUE, @@ -4299,10 +4379,106 @@ gtkchat->list = list; gtk_container_add(GTK_CONTAINER(sw), list); +} + +static GtkWidget * +setup_common_pane(PidginConversation *gtkconv) +{ + GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box; + GtkCellRenderer *rend; + GtkTreePath *path; + PurpleConversation *conv = gtkconv->active_conv; + gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT); + GtkPolicyType imhtml_sw_hscroll; + + paned = gtk_vpaned_new(); + gtk_widget_show(paned); + + /* Setup the top part of the pane */ + vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE); + gtk_widget_show(vbox); + + /* Setup the info pane */ + event_box = gtk_event_box_new(); + gtk_widget_show(event_box); + gtkconv->infopane_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), event_box, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(event_box), gtkconv->infopane_hbox); + gtk_widget_show(gtkconv->infopane_hbox); + gtk_widget_add_events(event_box, + GDK_BUTTON1_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); + g_signal_connect(G_OBJECT(event_box), "button_press_event", + G_CALLBACK(infopane_press_cb), gtkconv); + g_signal_connect(G_OBJECT(event_box), "button_release_event", + G_CALLBACK(infopane_release_cb), gtkconv); + + + gtkconv->infopane = gtk_cell_view_new(); + gtkconv->infopane_model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING); + gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane), + GTK_TREE_MODEL(gtkconv->infopane_model)); + gtk_list_store_append(gtkconv->infopane_model, &(gtkconv->infopane_iter)); + gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), gtkconv->infopane, TRUE, TRUE, 0); + path = gtk_tree_path_new_from_string("0"); + gtk_cell_view_set_displayed_row(GTK_CELL_VIEW(gtkconv->infopane), path); + gtk_tree_path_free(path); + gtk_widget_set_size_request(gtkconv->infopane, -1, 32); + gtk_widget_show(gtkconv->infopane); + + rend = gtk_cell_renderer_pixbuf_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", ICON_COLUMN, NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); + + rend = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "markup", TEXT_COLUMN, NULL); + g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); + +#if GTK_CHECK_VERSION(2, 6, 0) + g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +#endif + + /* Setup the gtkimhtml widget */ + frame = pidgin_create_imhtml(FALSE, >kconv->imhtml, NULL, &imhtml_sw); + if (chat) { + GtkWidget *hpaned; + + /* Add the topic */ + setup_chat_topic(gtkconv, vbox); + + /* Add the gtkimhtml frame */ + hpaned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); + gtk_widget_show(hpaned); + gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); + + /* Now add the userlist */ + setup_chat_userlist(gtkconv, hpaned); + } else { + gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); + } + gtk_widget_show(frame); + + gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); + gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE); + + gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), + &imhtml_sw_hscroll, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw), + imhtml_sw_hscroll, GTK_POLICY_ALWAYS); + + g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", + G_CALLBACK(entry_stop_rclick_cb), NULL); + g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", + G_CALLBACK(refocus_entry_cb), gtkconv); + g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", + G_CALLBACK(refocus_entry_cb), gtkconv); /* Setup the bottom half of the conversation window */ vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_paned_pack2(GTK_PANED(vpaned), vbox, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(paned), vbox, FALSE, TRUE); gtk_widget_show(vbox); gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); @@ -4318,20 +4494,15 @@ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); - g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", - G_CALLBACK(entry_popup_menu_cb), gtkconv); - gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry"); gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), - purple_account_get_protocol_name(conv->account)); + purple_account_get_protocol_name(conv->account)); gtk_widget_set_size_request(gtkconv->lower_hbox, -1, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height")); - gtkconv->entry_buffer = - gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); - g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); - g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed", - G_CALLBACK(resize_imhtml_cb), gtkconv); - + chat ? purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height") : + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height")); + + g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", + G_CALLBACK(entry_popup_menu_cb), gtkconv); g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", G_CALLBACK(entry_key_press_cb), gtkconv); g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", @@ -4341,129 +4512,28 @@ g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate", G_CALLBACK(size_allocate_cb), gtkconv); - default_formatize(gtkconv); - - /* - * Focus for chat windows should be as follows: - * Tab title -> chat topic -> conversation scrollback -> user list -> - * user list buttons -> entry -> buttons at bottom - */ - focus_chain = g_list_prepend(focus_chain, gtkconv->entry); - gtk_container_set_focus_chain(GTK_CONTAINER(vbox), focus_chain); - - return vpaned; -} - -static GtkWidget * -setup_im_pane(PidginConversation *gtkconv) -{ - PurpleConversation *conv = gtkconv->active_conv; - GtkWidget *frame; - GtkWidget *imhtml_sw; - GtkPolicyType imhtml_sw_hscroll; - GtkWidget *paned; - GtkWidget *vbox; - GtkWidget *vbox2; - GList *focus_chain = NULL; - - /* Setup the outer pane */ - paned = gtk_vpaned_new(); - gtk_widget_show(paned); - - /* Setup the top part of the pane */ - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE); - gtk_widget_show(vbox); - - /* Setup the gtkimhtml widget */ - frame = pidgin_create_imhtml(FALSE, >kconv->imhtml, NULL, &imhtml_sw); - gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml"); - gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE); - gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); - gtk_widget_show(frame); - gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw), - &imhtml_sw_hscroll, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(imhtml_sw), - imhtml_sw_hscroll, GTK_POLICY_ALWAYS); - - gtk_widget_set_size_request(gtkconv->imhtml, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width"), - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height")); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate", - G_CALLBACK(size_allocate_cb), gtkconv); - - g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", - G_CALLBACK(entry_stop_rclick_cb), NULL); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", - G_CALLBACK(refocus_entry_cb), gtkconv); - g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", - G_CALLBACK(refocus_entry_cb), gtkconv); - - /* Setup the bottom half of the conversation window */ - vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, TRUE); - gtk_widget_show(vbox2); - - gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->lower_hbox, TRUE, TRUE, 0); - gtk_widget_show(gtkconv->lower_hbox); - - vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox2, TRUE, TRUE, 0); - gtk_widget_show(vbox2); - - /* Setup the toolbar, entry widget and all signals */ - frame = pidgin_create_imhtml(TRUE, >kconv->entry, >kconv->toolbar, NULL); - gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0); - gtk_widget_show(frame); - - g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", - G_CALLBACK(entry_popup_menu_cb), gtkconv); - - gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry"); - gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), - purple_account_get_protocol_name(conv->account)); - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height")); gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); - g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", - G_CALLBACK(entry_key_press_cb), gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", - G_CALLBACK(send_cb), gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", - G_CALLBACK(entry_stop_rclick_cb), NULL); - g_signal_connect(G_OBJECT(gtkconv->lower_hbox), "size-allocate", - G_CALLBACK(size_allocate_cb), gtkconv); - - g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text", - G_CALLBACK(insert_text_cb), gtkconv); - g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range", - G_CALLBACK(delete_text_cb), gtkconv); + if (!chat) { + /* For sending typing notifications for IMs */ + g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text", + G_CALLBACK(insert_text_cb), gtkconv); + g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range", + G_CALLBACK(delete_text_cb), gtkconv); + gtkconv->u.im->typing_timer = 0; + gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons"); + gtkconv->u.im->show_icon = TRUE; + } + g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed", G_CALLBACK(resize_imhtml_cb), gtkconv); - /* had to move this after the imtoolbar is attached so that the - * signals get fired to toggle the buttons on the toolbar as well. - */ default_formatize(gtkconv); - g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear", - G_CALLBACK(clear_formatting_cb), gtkconv); - - gtkconv->u.im->animate = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons"); - gtkconv->u.im->show_icon = TRUE; - - /* - * Focus for IM windows should be as follows: - * Tab title -> conversation scrollback -> entry - */ - focus_chain = g_list_prepend(focus_chain, gtkconv->entry); - gtk_container_set_focus_chain(GTK_CONTAINER(vbox2), focus_chain); - - gtkconv->u.im->typing_timer = 0; + G_CALLBACK(clear_formatting_cb), gtkconv); + return paned; } @@ -4659,12 +4729,10 @@ if (conv_type == PURPLE_CONV_TYPE_IM) { gtkconv->u.im = g_malloc0(sizeof(PidginImPane)); - - pane = setup_im_pane(gtkconv); } else if (conv_type == PURPLE_CONV_TYPE_CHAT) { gtkconv->u.chat = g_malloc0(sizeof(PidginChatPane)); - pane = setup_chat_pane(gtkconv); - } + } + pane = setup_common_pane(gtkconv); gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml), gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE); @@ -5062,7 +5130,11 @@ g_return_if_fail(gc != NULL); /* Make sure URLs are clickable */ - displaying = purple_markup_linkify(message); + if(flags & PURPLE_MESSAGE_NO_LINKIFY) + displaying = g_strdup(message); + else + displaying = purple_markup_linkify(message); + plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( pidgin_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ? "displaying-im-msg" : "displaying-chat-msg"), @@ -5487,7 +5559,7 @@ g_return_if_fail(new_alias != NULL); - cbuddy = purple_conv_chat_cb_new(new_name, new_alias, flags); + cbuddy = purple_conv_chat_cb_find(chat, new_name); add_chat_buddy_common(conv, cbuddy, old_name); } @@ -5893,6 +5965,8 @@ gtk_widget_hide(win->menu.add); } + gtk_widget_show(win->menu.insert_link); + gtk_widget_show(win->menu.insert_image); gtk_widget_show(win->menu.show_icon); } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { /* Show stuff that applies to Chats, hide stuff that applies to IMs */ @@ -5918,6 +5992,8 @@ gtk_widget_show(win->menu.remove); } + gtk_widget_show(win->menu.insert_link); + gtk_widget_show(win->menu.insert_image); } /* @@ -5946,8 +6022,10 @@ buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE; } - if (!(prpl_info->options & OPT_PROTO_IM_IMAGE) || - conv->features & PURPLE_CONNECTION_NO_IMAGES) + if (!(prpl_info->options & OPT_PROTO_IM_IMAGE)) + conv->features |= PURPLE_CONNECTION_NO_IMAGES; + + if(conv->features & PURPLE_CONNECTION_NO_IMAGES) buttons &= ~GTK_IMHTML_IMAGE; gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); @@ -5959,6 +6037,8 @@ gtk_widget_set_sensitive(win->menu.add_pounce, TRUE); gtk_widget_set_sensitive(win->menu.get_info, (prpl_info->get_info != NULL)); gtk_widget_set_sensitive(win->menu.invite, (prpl_info->chat_invite != NULL)); + gtk_widget_set_sensitive(win->menu.insert_link, (conv->features & PURPLE_CONNECTION_HTML)); + gtk_widget_set_sensitive(win->menu.insert_image, !(conv->features & PURPLE_CONNECTION_NO_IMAGES)); if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { @@ -5993,6 +6073,8 @@ gtk_widget_set_sensitive(win->menu.alias, FALSE); gtk_widget_set_sensitive(win->menu.add, FALSE); gtk_widget_set_sensitive(win->menu.remove, FALSE); + gtk_widget_set_sensitive(win->menu.insert_link, TRUE); + gtk_widget_set_sensitive(win->menu.insert_image, FALSE); } /* @@ -6076,11 +6158,13 @@ pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml); if ((fields & PIDGIN_CONV_COLORIZE_TITLE) || - (fields & PIDGIN_CONV_SET_TITLE)) + (fields & PIDGIN_CONV_SET_TITLE) || + (fields & PIDGIN_CONV_TOPIC)) { char *title; PurpleConvIm *im = NULL; PurpleAccount *account = purple_conversation_get_account(conv); + char *markup = NULL; AtkObject *accessibility_obj; /* I think this is a little longer than it needs to be but I'm lazy. */ char style[51]; @@ -6096,6 +6180,27 @@ else title = g_strdup(purple_conversation_get_title(conv)); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + PurpleBuddy *buddy = purple_find_buddy(account, conv->name); + if (buddy) + markup = pidgin_blist_get_name_markup(buddy, FALSE, FALSE); + else + markup = title; + } else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); + const char *topic = purple_conv_chat_get_topic(chat); + markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>", + purple_conversation_get_title(conv), + topic ? "\n" : "", + pidgin_get_dim_grey_string(gtkconv->infopane), + topic ? topic : ""); + } + gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter), + TEXT_COLUMN, markup, -1); + + if (title != markup) + g_free(markup); + *style = '\0'; if (!GTK_WIDGET_REALIZED(gtkconv->tab_label)) @@ -6255,7 +6360,6 @@ GdkPixbuf *buf; GtkWidget *event; - GtkWidget *frame; GdkPixbuf *scale; int scale_width, scale_height; @@ -6354,54 +6458,55 @@ } if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) { + GdkPixbuf *stat; gtkconv->u.im->iter = NULL; - buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); + stat = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); + buf = gdk_pixbuf_add_alpha(stat, FALSE, 0, 0, 0); } else { + GdkPixbuf *stat; gtkconv->u.im->iter = gdk_pixbuf_animation_get_iter(gtkconv->u.im->anim, NULL); /* LEAK */ - buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); + stat = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); + buf = gdk_pixbuf_add_alpha(stat, FALSE, 0, 0, 0); if (gtkconv->u.im->animate) start_anim(NULL, gtkconv); } - pidgin_buddy_icon_get_scale_size(buf, &prpl_info->icon_spec, - PURPLE_ICON_SCALE_DISPLAY, &scale_width, &scale_height); - scale = gdk_pixbuf_scale_simple(buf, - MAX(gdk_pixbuf_get_width(buf) * scale_width / - gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), - MAX(gdk_pixbuf_get_height(buf) * scale_height / - gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), + scale_width = gdk_pixbuf_get_width(buf); + scale_height = gdk_pixbuf_get_height(buf); + if (scale_width == scale_height) { + scale_width = scale_height = 32; + } else if (scale_height > scale_width) { + scale_width = 32 * scale_width / scale_height; + scale_height = 32; + } else { + scale_height = 32 * scale_height / scale_width; + scale_width = 32; + } + scale = gdk_pixbuf_scale_simple(buf, scale_width, scale_height, GDK_INTERP_BILINEAR); - + g_object_unref(buf); + if (pidgin_gdk_pixbuf_is_opaque(scale)) + pidgin_gdk_pixbuf_make_round(scale); gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0); - frame = gtk_frame_new(NULL); - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); - gtk_box_pack_start(GTK_BOX(gtkconv->u.im->icon_container), frame, - FALSE, FALSE, 0); - event = gtk_event_box_new(); - gtk_container_add(GTK_CONTAINER(frame), event); + gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event); g_signal_connect(G_OBJECT(event), "button-press-event", G_CALLBACK(icon_menu), gtkconv); gtk_widget_show(event); gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale); gtkconv->auto_resize = TRUE; - /* Reset the size request to allow the buddy icon to resize */ - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, -1); - g_idle_add(reset_auto_resize_cb, gtkconv); - gtk_widget_set_size_request(gtkconv->u.im->icon, scale_width, scale_height); gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon); gtk_widget_show(gtkconv->u.im->icon); g_object_unref(G_OBJECT(scale)); - gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), + gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), gtkconv->u.im->icon_container, FALSE, FALSE, 0); gtk_widget_show(gtkconv->u.im->icon_container); - gtk_widget_show(frame); /* The buddy icon code needs badly to be fixed. */ if(pidgin_conv_window_is_active_conversation(conv)) @@ -6950,6 +7055,11 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/tabs", TRUE); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/tab_side", GTK_POS_TOP); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/scrollback_lines", 4000); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/x", 0); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/y", 0); + + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font", TRUE); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", ""); /* Conversations -> Chat */ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/chat"); @@ -7092,6 +7202,9 @@ purple_cmd_register("clear", "", PURPLE_CMD_P_DEFAULT, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL, clear_command_cb, _("clear: Clears the conversation scrollback."), NULL); + purple_cmd_register("clearall", "", PURPLE_CMD_P_DEFAULT, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL, + clearall_command_cb, _("clear: Clears all conversation scrollbacks."), NULL); purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL, help_command_cb, _("help <command>: Help on a specific command."), NULL); @@ -7496,6 +7609,45 @@ /* * THANK YOU GALEON! */ + +static gboolean +infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv) +{ + int nb_x, nb_y; + + if (e->button != 1 || e->type != GDK_BUTTON_PRESS) + return FALSE; + + if (gtkconv->win->in_drag) { + purple_debug(PURPLE_DEBUG_WARNING, "gtkconv", + "Already in the middle of a window drag at tab_press_cb\n"); + return TRUE; + } + + gtkconv->win->in_predrag = TRUE; + gtkconv->win->drag_tab = gtk_notebook_page_num(GTK_NOTEBOOK(gtkconv->win->notebook), gtkconv->tab_cont); + + gdk_window_get_origin(gtkconv->infopane_hbox->window, &nb_x, &nb_y); + + gtkconv->win->drag_min_x = gtkconv->infopane_hbox->allocation.x + nb_x; + gtkconv->win->drag_min_y = gtkconv->infopane_hbox->allocation.y + nb_y; + gtkconv->win->drag_max_x = gtkconv->infopane_hbox->allocation.width + gtkconv->win->drag_min_x; + gtkconv->win->drag_max_y = gtkconv->infopane_hbox->allocation.height + gtkconv->win->drag_min_y; + + + /* Connect the new motion signals. */ + gtkconv->win->drag_motion_signal = + g_signal_connect(G_OBJECT(gtkconv->win->notebook), "motion_notify_event", + G_CALLBACK(notebook_motion_cb), gtkconv->win); + + gtkconv->win->drag_leave_signal = + g_signal_connect(G_OBJECT(gtkconv->win->notebook), "leave_notify_event", + G_CALLBACK(notebook_leave_cb), gtkconv->win); + + return FALSE; + +} + static gboolean notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win) { @@ -7585,6 +7737,12 @@ } static gboolean +infopane_release_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv) +{ + return FALSE; +} + +static gboolean notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win) { PidginWindow *dest_win; @@ -7915,11 +8073,18 @@ return FALSE; } + if (!purple_account_is_connected(gtkconv->active_conv->account)) { + /* Do not allow aliasing someone on a disconnected account. */ + return FALSE; + } + /* alias label */ entry = gtk_entry_new(); gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE); gtk_entry_set_width_chars(GTK_ENTRY(entry), 10); +#if GTK_CHECK_VERSION(2,4,0) gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5); +#endif gtk_box_pack_start(GTK_BOX(gtkconv->tabby), entry, TRUE, TRUE, 0); /* after the tab label */ @@ -7964,6 +8129,7 @@ generate_send_to_items(win); regenerate_options_items(win); + regenerate_plugins_items(win); pidgin_conv_switch_active_conversation(conv); @@ -8034,6 +8200,50 @@ prpl_lists = g_hash_table_new(g_str_hash, g_str_equal); } +static void +plugin_changed_cb(PurplePlugin *p, gpointer data) +{ + regenerate_plugins_items(data); +} + +static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) { + int x, y; + + if (GTK_WIDGET_VISIBLE(w)) + gtk_window_get_position(GTK_WINDOW(w), &x, &y); + else + return FALSE; /* carry on normally */ + + /* Workaround for GTK+ bug # 169811 - "configure_event" is fired + * when the window is being maximized */ + if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED) + return FALSE; + + /* don't save if nothing changed */ + if (x == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/x") && + y == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/y") && + event->width == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_width") && + event->height == purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/default_height")) + return FALSE; /* carry on normally */ + + /* don't save off-screen positioning */ + if (x + event->width < 0 || + y + event->height < 0 || + x > gdk_screen_width() || + y > gdk_screen_height()) + return FALSE; /* carry on normally */ + + /* store the position */ + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/x", x); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/y", y); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/width", event->width); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/im/height", event->height); + + /* continue to handle event normally */ + return FALSE; + +} + PidginWindow * pidgin_conv_window_new() { @@ -8047,11 +8257,9 @@ window_list = g_list_append(window_list, win); /* Create the window. */ - win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_role(GTK_WINDOW(win->window), "conversation"); - gtk_window_set_resizable(GTK_WINDOW(win->window), TRUE); - gtk_container_set_border_width(GTK_CONTAINER(win->window), 0); - GTK_WINDOW(win->window)->allow_shrink = TRUE; + win->window = pidgin_create_window(NULL, 0, "conversation", TRUE); + gtk_window_set_default_size(GTK_WINDOW(win->window), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/width"), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/height")); if (available_list == NULL) { create_icon_lists(win->window); @@ -8059,7 +8267,8 @@ g_signal_connect(G_OBJECT(win->window), "delete_event", G_CALLBACK(close_win_cb), win); - + g_signal_connect(G_OBJECT(win->window), "configure_event", + G_CALLBACK(gtk_conv_configure_cb), NULL); g_signal_connect(G_OBJECT(win->window), "focus_in_event", G_CALLBACK(focus_win_cb), win); @@ -8108,6 +8317,13 @@ gtk_widget_show(testidea); + /* Update the plugin actions when plugins are (un)loaded */ + purple_signal_connect(purple_plugins_get_handle(), "plugin-load", + win, PURPLE_CALLBACK(plugin_changed_cb), win); + purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", + win, PURPLE_CALLBACK(plugin_changed_cb), win); + + #ifdef _WIN32 g_signal_connect(G_OBJECT(win->window), "show", G_CALLBACK(winpidgin_ensure_onscreen), win->window); @@ -8147,6 +8363,7 @@ g_object_unref(G_OBJECT(win->menu.item_factory)); purple_notify_close_with_handle(win); + purple_signals_disconnect_by_handle(win); g_free(win); } @@ -8262,8 +8479,7 @@ /* Er, bug in notebooks? Switch to the page manually. */ gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), - purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs")); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE); } else gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE); @@ -8326,7 +8542,9 @@ } ebox = gtk_event_box_new(); +#if GTK_CHECK_VERSION(2,4,0) gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE); +#endif gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby); g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(alias_double_click_cb), gtkconv); @@ -8355,7 +8573,7 @@ gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START); /* show the widgets */ - gtk_widget_show(gtkconv->icon); +/* XXX gtk_widget_show(gtkconv->icon); */ gtk_widget_show(gtkconv->tab_label); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs")) gtk_widget_show(gtkconv->close); @@ -8377,14 +8595,16 @@ gtk_notebook_remove_page(GTK_NOTEBOOK(win->notebook), index); - /* go back to tabless if need be */ + /* go back to tabless */ if (pidgin_conv_window_get_gtkconv_count(win) <= 2) { - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), - purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs")); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE); } win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv); + if (!win->gtkconvs || !win->gtkconvs->next) + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE); + if (!win->gtkconvs && win != hidden_convwin) pidgin_conv_window_destroy(win); }