# HG changeset patch # User Mark Doliner # Date 1074552017 0 # Node ID 8633dc570442b959c07a4f653e83b35fe79807a6 # Parent f2919ca98e7830270fb95908013cd2f400f7e405 [gaim-migrate @ 8851] sf cvs fing sucks. This is the rest of the accessibility stuff from that cool Nathan guy. It makes shift+f10 bring up context menus. Please check over stuff and make sure neither of us broke anything. Also, someone think of a good way to phrase that thing about this that's in the ChangeLog. Thank you, and have a nice day. committer: Tailor Script diff -r f2919ca98e78 -r 8633dc570442 src/gtkblist.c --- a/src/gtkblist.c Mon Jan 19 22:13:50 2004 +0000 +++ b/src/gtkblist.c Mon Jan 19 22:40:17 2004 +0000 @@ -873,13 +873,178 @@ return FALSE; } -static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) +static GtkWidget * +create_group_menu (GaimBlistNode *node) +{ + GtkWidget *menu; + + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, + G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, + G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Rename"), NULL, + G_CALLBACK(show_rename_group), node, 0, 0, NULL); + return menu; +} + +static GtkWidget * +create_chat_menu (GaimBlistNode *node) +{ + GtkWidget *menu; + gboolean autojoin = gaim_blist_node_get_bool(node, + "gtk-autojoin"); + + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, + G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); + gaim_new_check_item(menu, _("Auto-Join"), + G_CALLBACK(gtk_blist_menu_autojoin_cb), node, + autojoin); + gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_EDIT, + G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + return menu; +} + +static GtkWidget * +create_contact_menu (GaimBlistNode *node) +{ + GtkWidget *menu; + + menu = gtk_menu_new(); + gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_EDIT, + G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, + G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), + node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, + G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + return menu; +} + +static GtkWidget * +create_buddy_menu (GaimBlistNode *node, + GaimBuddy *b, + GaimPlugin *prpl, + GaimPluginProtocolInfo *prpl_info) +{ + struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + GtkWidget *menu; + GtkWidget *menuitem; + gboolean show_offline = gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies"); + + menu = gtk_menu_new(); + make_buddy_menu(menu, prpl_info, b); + + if(GAIM_BLIST_NODE_IS_CONTACT(node)) { + gaim_separator(menu); + + if(gtknode->contact_expanded) { + gaim_new_item_from_stock(menu, _("_Collapse"), + GTK_STOCK_ZOOM_OUT, + G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), + node, 0, 0, NULL); + } else { + gaim_new_item_from_stock(menu, _("_Expand"), + GTK_STOCK_ZOOM_IN, + G_CALLBACK(gaim_gtk_blist_expand_contact_cb), node, + 0, 0, NULL); + } + if(node->child->next) { + GaimBlistNode *bnode; + + for(bnode = node->child; bnode; bnode = bnode->next) { + GaimBuddy *buddy = (GaimBuddy*)bnode; + GtkWidget *submenu; + GtkWidget *image; + + if(buddy == b) + continue; + if(!buddy->account->gc) + continue; + if(!show_offline && !GAIM_BUDDY_IS_ONLINE(buddy)) + continue; + + menuitem = gtk_image_menu_item_new_with_label(buddy->name); + image = gtk_image_new_from_pixbuf( + gaim_gtk_blist_get_status_icon(bnode, + GAIM_STATUS_ICON_SMALL)); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), + image); + gtk_widget_show(image); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + gtk_widget_show(submenu); + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(buddy->account)); + prpl_info = prpl ? GAIM_PLUGIN_PROTOCOL_INFO(prpl) : NULL; + + make_buddy_menu(submenu, prpl_info, buddy); + } + } + } + return menu; +} + +static gboolean +gaim_gtk_blist_show_context_menu(GaimBlistNode *node, + GtkMenuPositionFunc func, + GtkWidget *tv, + guint button, + guint32 time) +{ + struct _gaim_gtk_blist_node *gtknode; + GtkWidget *menu = NULL; + gboolean handled = FALSE; + + gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; + + /* Create a menu based on the thing we right-clicked on */ + if (GAIM_BLIST_NODE_IS_GROUP(node)) { + menu = create_group_menu(node); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + menu = create_chat_menu(node); + } else if ((GAIM_BLIST_NODE_IS_CONTACT(node)) && (gtknode->contact_expanded)) { + menu = create_contact_menu(node); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *b; + GaimPlugin *prpl = NULL; + GaimPluginProtocolInfo *prpl_info = NULL; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + b = gaim_contact_get_priority_buddy((GaimContact*)node); + else + b = (GaimBuddy *)node; + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(b->account)); + if (prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + menu = create_buddy_menu(node, b, prpl, prpl_info); + } + + /* Now display the menu */ + if (menu != NULL) { + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, func, tv, button, time); + handled = TRUE; + } + + return handled; +} + +static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) { GtkTreePath *path; GaimBlistNode *node; GValue val = { 0, }; GtkTreeIter iter; - GtkWidget *menu, *menuitem; GtkTreeSelection *sel; GaimPlugin *prpl = NULL; GaimPluginProtocolInfo *prpl_info = NULL; @@ -890,152 +1055,50 @@ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) return FALSE; gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); node = g_value_get_pointer(&val); gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; - if (GAIM_BLIST_NODE_IS_GROUP(node) && - event->button == 3 && event->type == GDK_BUTTON_PRESS) { - menu = gtk_menu_new(); - gaim_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, - G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, - G_CALLBACK(gaim_gtk_blist_add_chat_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, - G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Rename"), NULL, - G_CALLBACK(show_rename_group), node, 0, 0, NULL); - gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); - - handled = TRUE; - } else if (GAIM_BLIST_NODE_IS_CHAT(node) && - event->button == 3 && event->type == GDK_BUTTON_PRESS) { - GaimChat *chat = (GaimChat *)node; - gboolean autojoin = gaim_blist_node_get_bool((GaimBlistNode*)chat, - "gtk-autojoin"); - - menu = gtk_menu_new(); - gaim_new_item_from_stock(menu, _("_Join"), GAIM_STOCK_CHAT, - G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); - gaim_new_check_item(menu, _("Auto-Join"), - G_CALLBACK(gtk_blist_menu_autojoin_cb), node, - autojoin); - gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_EDIT, - G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, - G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); - - handled = TRUE; - } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && - event->state & GDK_CONTROL_MASK && event->button == 2 && - event->type == GDK_BUTTON_PRESS) { - if(gtknode->contact_expanded) + /* Right click draws a context menu */ + if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) { + handled = gaim_gtk_blist_show_context_menu(node, NULL, tv, 3, event->time); + + /* CTRL+middle click expands or collapse a contact */ + } else if ((event->button == 2) && (event->type == GDK_BUTTON_PRESS) && + (event->state & GDK_CONTROL_MASK) && (GAIM_BLIST_NODE_IS_CONTACT(node))) { + if (gtknode->contact_expanded) gaim_gtk_blist_collapse_contact_cb(NULL, node); else gaim_gtk_blist_expand_contact_cb(NULL, node); handled = TRUE; - } else if (GAIM_BLIST_NODE_IS_CONTACT(node) && gtknode->contact_expanded - && event->button == 3 && event->type == GDK_BUTTON_PRESS) { - menu = gtk_menu_new(); - gaim_new_item_from_stock(menu, _("_Alias..."), GAIM_STOCK_EDIT, - G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, - G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), - node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, - G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); - handled = TRUE; - } else if (GAIM_BLIST_NODE_IS_CONTACT(node) || - GAIM_BLIST_NODE_IS_BUDDY(node)) { + + /* Double middle click gets info */ + } else if ((event->button == 2) && (event->type == GDK_2BUTTON_PRESS) && + ((GAIM_BLIST_NODE_IS_CONTACT(node)) || (GAIM_BLIST_NODE_IS_BUDDY(node)))) { GaimBuddy *b; if(GAIM_BLIST_NODE_IS_CONTACT(node)) b = gaim_contact_get_priority_buddy((GaimContact*)node); else b = (GaimBuddy *)node; - /* Protocol specific options */ prpl = gaim_find_prpl(gaim_account_get_protocol_id(b->account)); - if (prpl != NULL) prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - if(event->button == 2 && event->type == GDK_2BUTTON_PRESS) { - if (prpl && prpl_info->get_info) - serv_get_info(b->account->gc, b->name); - handled = TRUE; - } else if(event->button == 3 && event->type == GDK_BUTTON_PRESS) { - gboolean show_offline = gaim_prefs_get_bool("/gaim/gtk/blist/show_offline_buddies"); - menu = gtk_menu_new(); - make_buddy_menu(menu, prpl_info, b); - - if(GAIM_BLIST_NODE_IS_CONTACT(node)) { - gaim_separator(menu); - - if(gtknode->contact_expanded) { - gaim_new_item_from_stock(menu, _("_Collapse"), - GTK_STOCK_ZOOM_OUT, - G_CALLBACK(gaim_gtk_blist_collapse_contact_cb), - node, 0, 0, NULL); - } else { - gaim_new_item_from_stock(menu, _("_Expand"), - GTK_STOCK_ZOOM_IN, - G_CALLBACK(gaim_gtk_blist_expand_contact_cb), node, - 0, 0, NULL); - } - if(node->child->next) { - GaimBlistNode *bnode; - - for(bnode = node->child; bnode; bnode = bnode->next) { - GaimBuddy *buddy = (GaimBuddy*)bnode; - GtkWidget *submenu; - GtkWidget *image; - - if(buddy == b) - continue; - if(!buddy->account->gc) - continue; - if(!show_offline && !GAIM_BUDDY_IS_ONLINE(buddy)) - continue; - - - menuitem = gtk_image_menu_item_new_with_label(buddy->name); - image = gtk_image_new_from_pixbuf( - gaim_gtk_blist_get_status_icon(bnode, - GAIM_STATUS_ICON_SMALL)); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), - image); - gtk_widget_show(image); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - gtk_widget_show(menuitem); - - submenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - gtk_widget_show(submenu); - - prpl = gaim_find_prpl(gaim_account_get_protocol_id(buddy->account)); - prpl_info = prpl ? GAIM_PLUGIN_PROTOCOL_INFO(prpl) : NULL; - - make_buddy_menu(submenu, prpl_info, buddy); - } - } - } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, - event->time); - - handled = TRUE; - } + if (prpl && prpl_info->get_info) + serv_get_info(b->account->gc, b->name); + handled = TRUE; } -#if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal - * the event propoagates down and somehow gets interpreted as the start of a drag event. */ +#if (1) + /* + * This code only exists because GTK doesn't work. If we return + * FALSE here, as would be normal the event propoagates down and + * somehow gets interpreted as the start of a drag event. + * + * Um, isn't it _normal_ to return TRUE here? Since the event + * was handled? --Mark + */ if(handled) { sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); gtk_tree_selection_select_path(sel, path); @@ -1044,9 +1107,33 @@ } #endif gtk_tree_path_free(path); + return FALSE; } +static gboolean +gaim_gtk_blist_popup_menu_cb(GtkWidget *tv, void *user_data) +{ + GaimBlistNode *node; + GValue val = { 0, }; + GtkTreeIter iter; + GtkTreeSelection *sel; + gboolean handled = FALSE; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); + if (!gtk_tree_selection_get_selected(sel, NULL, &iter)) + return FALSE; + + gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), + &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + + /* Shift+F10 draws a context menu */ + handled = gaim_gtk_blist_show_context_menu(node, gaim_gtk_treeview_popup_menu_position_func, tv, 0, GDK_CURRENT_TIME); + + return handled; +} + static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) { gaim_prefs_set_bool("/gaim/gtk/blist/show_empty_groups", @@ -1864,7 +1951,7 @@ { N_("/_Buddies"), NULL, NULL, 0, "" }, { N_("/Buddies/New Instant _Message..."), "M", show_im_dialog, 0, "", GAIM_STOCK_IM }, { N_("/Buddies/Join a _Chat..."), "C", join_chat, 0, "", GAIM_STOCK_CHAT }, - { N_("/Buddies/Get _User Info..."), "J", show_info_dialog, 0, "", GAIM_STOCK_INFO }, + { N_("/Buddies/Get User _Info..."), "I", show_info_dialog, 0, "", GAIM_STOCK_INFO }, { "/Buddies/sep1", NULL, NULL, 0, "" }, { N_("/Buddies/Show _Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, ""}, { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, ""}, @@ -2668,6 +2755,7 @@ g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "popup-menu", G_CALLBACK(gaim_gtk_blist_popup_menu_cb), NULL); /* Enable CTRL+F searching */ gtk_tree_view_set_search_column(GTK_TREE_VIEW(gtkblist->treeview), NAME_COLUMN); diff -r f2919ca98e78 -r 8633dc570442 src/gtkconv.c --- a/src/gtkconv.c Mon Jan 19 22:13:50 2004 +0000 +++ b/src/gtkconv.c Mon Jan 19 22:40:17 2004 +0000 @@ -1134,12 +1134,120 @@ gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry); } +static GtkWidget * +create_chat_menu(GaimConversation *conv, gchar *who, + GaimPluginProtocolInfo *prpl_info, GaimConnection *gc) +{ + static GtkWidget *menu = NULL; + GtkWidget *button; + + /* + * If a menu already exists, destroy it before creating a new one, + * thus freeing-up the memory it occupied. + */ + if (menu) + gtk_widget_destroy(menu); + + menu = gtk_menu_new(); + + button = gtk_menu_item_new_with_label(_("IM")); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(menu_chat_im_cb), conv); + g_object_set_data(G_OBJECT(button), "user_data", who); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); + gtk_widget_show(button); + + if (gaim_conv_chat_is_user_ignored(GAIM_CONV_CHAT(conv), who)) + button = gtk_menu_item_new_with_label(_("Un-Ignore")); + else + button = gtk_menu_item_new_with_label(_("Ignore")); + + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(ignore_cb), conv); + g_object_set_data(G_OBJECT(button), "user_data", who); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); + gtk_widget_show(button); + + if (gc && prpl_info->get_info) { + button = gtk_menu_item_new_with_label(_("Info")); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(menu_chat_info_cb), conv); + g_object_set_data(G_OBJECT(button), "user_data", who); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); + gtk_widget_show(button); + } + + if (gc && prpl_info->get_cb_away) { + button = gtk_menu_item_new_with_label(_("Get Away Msg")); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(menu_chat_get_away_cb), conv); + g_object_set_data(G_OBJECT(button), "user_data", who); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); + gtk_widget_show(button); + } + + /* Added by Jonas */ + if (gc) { + if (gaim_find_buddy(gc->account, who)) + button = gtk_menu_item_new_with_label(_("Remove")); + else + button = gtk_menu_item_new_with_label(_("Add")); + + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(menu_chat_add_remove_cb), conv); + + g_object_set_data(G_OBJECT(button), "user_data", who); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); + gtk_widget_show(button); + } + /* End Jonas */ + + return menu; +} + + +static gint +gtkconv_chat_popup_menu_cb(GtkWidget *widget, GaimConversation *conv) +{ + GaimGtkConversation *gtkconv; + GaimPluginProtocolInfo *prpl_info = NULL; + GaimGtkChatPane *gtkchat; + GaimConnection *gc; + GaimAccount *account; + GtkTreeSelection *sel; + GtkTreeIter iter; + GtkTreeModel *model; + GtkWidget *menu; + gchar *who; + + gtkconv = GAIM_GTK_CONVERSATION(conv); + gtkchat = gtkconv->u.chat; + account = gaim_conversation_get_account(conv); + gc = account->gc; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); + + if (gc != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list)); + if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) + return FALSE; + + gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &who, -1); + menu = create_chat_menu (conv, who, prpl_info, gc); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gaim_gtk_treeview_popup_menu_position_func, widget, 0, GDK_CURRENT_TIME); + + return TRUE; +} + + static gint right_click_chat_cb(GtkWidget *widget, GdkEventButton *event, GaimConversation *conv) { + GaimGtkConversation *gtkconv; GaimPluginProtocolInfo *prpl_info = NULL; - GaimGtkConversation *gtkconv; GaimGtkChatPane *gtkchat; GaimConnection *gc; GaimAccount *account; @@ -1175,71 +1283,7 @@ if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { chat_do_im(conv, who); } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { - static GtkWidget *menu = NULL; - GtkWidget *button; - - /* - * If a menu already exists, destroy it before creating a new one, - * thus freeing-up the memory it occupied. - */ - - if (menu) - gtk_widget_destroy(menu); - - menu = gtk_menu_new(); - - button = gtk_menu_item_new_with_label(_("IM")); - g_signal_connect(G_OBJECT(button), "activate", - G_CALLBACK(menu_chat_im_cb), conv); - g_object_set_data(G_OBJECT(button), "user_data", who); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); - gtk_widget_show(button); - - if (gaim_conv_chat_is_user_ignored(GAIM_CONV_CHAT(conv), who)) - button = gtk_menu_item_new_with_label(_("Un-Ignore")); - else - button = gtk_menu_item_new_with_label(_("Ignore")); - - g_signal_connect(G_OBJECT(button), "activate", - G_CALLBACK(ignore_cb), conv); - g_object_set_data(G_OBJECT(button), "user_data", who); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); - gtk_widget_show(button); - - if (gc && prpl_info->get_info) { - button = gtk_menu_item_new_with_label(_("Info")); - g_signal_connect(G_OBJECT(button), "activate", - G_CALLBACK(menu_chat_info_cb), conv); - g_object_set_data(G_OBJECT(button), "user_data", who); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); - gtk_widget_show(button); - } - - if (gc && prpl_info->get_cb_away) { - button = gtk_menu_item_new_with_label(_("Get Away Msg")); - g_signal_connect(G_OBJECT(button), "activate", - G_CALLBACK(menu_chat_get_away_cb), conv); - g_object_set_data(G_OBJECT(button), "user_data", who); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); - gtk_widget_show(button); - } - - /* Added by Jonas */ - if (gc) { - if (gaim_find_buddy(gc->account, who)) - button = gtk_menu_item_new_with_label(_("Remove")); - else - button = gtk_menu_item_new_with_label(_("Add")); - - g_signal_connect(G_OBJECT(button), "activate", - G_CALLBACK(menu_chat_add_remove_cb), conv); - - g_object_set_data(G_OBJECT(button), "user_data", who); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); - gtk_widget_show(button); - } - /* End Jonas */ - + GtkWidget *menu = create_chat_menu (conv, who, prpl_info, gc); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time); } @@ -1376,15 +1420,15 @@ break; case GDK_Page_Down: - case '[': - gaim_conv_window_switch_conversation(win, (curconv + numconvs - 1) % numconvs); + case ']': + gaim_conv_window_switch_conversation(win, (curconv + 1) % numconvs); return TRUE; break; case GDK_Page_Up: - case ']': - gaim_conv_window_switch_conversation(win, (curconv + 1) % numconvs); + case '[': + gaim_conv_window_switch_conversation(win, (curconv + numconvs - 1) % numconvs); return TRUE; break; @@ -3588,6 +3632,7 @@ GtkListStore *ls; GtkCellRenderer *rend; GtkTreeViewColumn *col; + GList *focus_chain; gtkconv = GAIM_GTK_CONVERSATION(conv); gtkchat = gtkconv->u.chat; @@ -3695,6 +3740,8 @@ g_signal_connect(G_OBJECT(list), "button_press_event", G_CALLBACK(right_click_chat_cb), conv); + g_signal_connect(G_OBJECT(list), "popup-menu", + G_CALLBACK(gtkconv_chat_popup_menu_cb), conv); gtk_tree_view_append_column(GTK_TREE_VIEW(list), col); @@ -3804,6 +3851,8 @@ setup_chat_buttons(conv, gtkconv->bbox); + focus_chain = g_list_prepend (NULL, sw); + gtk_container_set_focus_chain (GTK_CONTAINER(vbox), focus_chain); return vpaned; } @@ -3816,6 +3865,7 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *sw; + GList *focus_chain; gtkconv = GAIM_GTK_CONVERSATION(conv); gtkim = gtkconv->u.im; @@ -3912,6 +3962,9 @@ setup_im_buttons(conv, gtkconv->bbox); + focus_chain = g_list_prepend (NULL, sw); + gtk_container_set_focus_chain (GTK_CONTAINER(vbox2), focus_chain); + return paned; } @@ -4221,9 +4274,6 @@ gtkconv->show_formatting_toolbar = gaim_prefs_get_bool( "/gaim/gtk/conversations/show_formatting_toolbar"); - g_signal_connect_swapped(G_OBJECT(pane), "focus", - G_CALLBACK(gtk_widget_grab_focus), - gtkconv->entry); } gtkconv->tabby = tabby = gtk_hbox_new(FALSE, 5); diff -r f2919ca98e78 -r 8633dc570442 src/gtkroomlist.c --- a/src/gtkroomlist.c Mon Jan 19 22:13:50 2004 +0000 +++ b/src/gtkroomlist.c Mon Jan 19 22:40:17 2004 +0000 @@ -321,7 +321,7 @@ gtk_widget_show(bbox); /* Get list button */ - button = gtk_button_new_with_mnemonic(_("Get _list")); + button = gtk_button_new_with_mnemonic(_("Get _List")); gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); gtk_widget_show(button); dialog->list_button = button; diff -r f2919ca98e78 -r 8633dc570442 src/gtkutils.c --- a/src/gtkutils.c Mon Jan 19 22:13:50 2004 +0000 +++ b/src/gtkutils.c Mon Jan 19 22:40:17 2004 +0000 @@ -1389,3 +1389,175 @@ atk_relation_set_add (set, relation); g_object_unref (relation); } + +static void +gaim_gtk_menu_position_func(GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer data) +{ + GtkWidget *widget; + GtkRequisition requisition; + GdkScreen *screen; + GdkRectangle monitor; + gint monitor_num; + gint space_left, space_right, space_above, space_below; + gint needed_width; + gint needed_height; + gint xthickness; + gint ythickness; + gboolean rtl; + + g_return_if_fail(GTK_IS_MENU(menu)); + + widget = GTK_WIDGET(menu); + screen = gtk_widget_get_screen(widget); + xthickness = widget->style->xthickness; + ythickness = widget->style->ythickness; + rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL); + + /* + * We need the requisition to figure out the right place to + * popup the menu. In fact, we always need to ask here, since + * if a size_request was queued while we weren't popped up, + * the requisition won't have been recomputed yet. + */ + gtk_widget_size_request (widget, &requisition); + + monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y); + + push_in = FALSE; + + /* + * The placement of popup menus horizontally works like this (with + * RTL in parentheses) + * + * - If there is enough room to the right (left) of the mouse cursor, + * position the menu there. + * + * - Otherwise, if if there is enough room to the left (right) of the + * mouse cursor, position the menu there. + * + * - Otherwise if the menu is smaller than the monitor, position it + * on the side of the mouse cursor that has the most space available + * + * - Otherwise (if there is simply not enough room for the menu on the + * monitor), position it as far left (right) as possible. + * + * Positioning in the vertical direction is similar: first try below + * mouse cursor, then above. + */ + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + space_left = *x - monitor.x; + space_right = monitor.x + monitor.width - *x - 1; + space_above = *y - monitor.y; + space_below = monitor.y + monitor.height - *y - 1; + + /* position horizontally */ + + /* the amount of space we need to position the menu. Note the + * menu is offset "xthickness" pixels + */ + needed_width = requisition.width - xthickness; + + if (needed_width <= space_left || + needed_width <= space_right) + { + if ((rtl && needed_width <= space_left) || + (!rtl && needed_width > space_right)) + { + /* position left */ + *x = *x + xthickness - requisition.width + 1; + } + else + { + /* position right */ + *x = *x - xthickness; + } + + /* x is clamped on-screen further down */ + } + else if (requisition.width <= monitor.width) + { + /* the menu is too big to fit on either side of the mouse + * cursor, but smaller than the monitor. Position it on + * the side that has the most space + */ + if (space_left > space_right) + { + /* left justify */ + *x = monitor.x; + } + else + { + /* right justify */ + *x = monitor.x + monitor.width - requisition.width; + } + } + else /* menu is simply too big for the monitor */ + { + if (rtl) + { + /* right justify */ + *x = monitor.x + monitor.width - requisition.width; + } + else + { + /* left justify */ + *x = monitor.x; + } + } + + /* Position vertically. The algorithm is the same as above, but + * simpler because we don't have to take RTL into account. + */ + needed_height = requisition.height - ythickness; + + if (needed_height <= space_above || + needed_height <= space_below) + { + if (needed_height <= space_below) + *y = *y - ythickness; + else + *y = *y + ythickness - requisition.height + 1; + + *y = CLAMP (*y, monitor.y, + monitor.y + monitor.height - requisition.height); + } + else if (needed_height > space_below && needed_height > space_above) + { + if (space_below >= space_above) + *y = monitor.y + monitor.height - requisition.height; + else + *y = monitor.y; + } + else + { + *y = monitor.y; + } +} + +void +gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer data) +{ + GtkWidget *widget = GTK_WIDGET(data); + GtkTreeView *tv = GTK_TREE_VIEW(data); + GtkTreePath *path; + GtkTreeViewColumn *col; + GdkRectangle rect; + gint ythickness = GTK_WIDGET(menu)->style->ythickness; + + gdk_window_get_origin (widget->window, x, y); + gtk_tree_view_get_cursor (tv, &path, &col); + gtk_tree_view_get_cell_area (tv, path, col, &rect); + + *x += rect.x+rect.width; + *y += rect.y+rect.height+ythickness; + gaim_gtk_menu_position_func (menu, x, y, push_in, data); +} diff -r f2919ca98e78 -r 8633dc570442 src/gtkutils.h --- a/src/gtkutils.h Mon Jan 19 22:13:50 2004 +0000 +++ b/src/gtkutils.h Mon Jan 19 22:40:17 2004 +0000 @@ -353,6 +353,27 @@ * @w The widget that we want to name. * @l A GtkLabel that we want to use as the ATK name for the widget. */ -void gaim_set_accessible_label (GtkWidget *w, GtkWidget *l); +void gaim_set_accessible_label(GtkWidget *w, GtkWidget *l); + +/** + * A valid GtkMenuPositionFunc. This is used to determine where + * to draw context menu's when the menu is activated with the + * keyboard (shift+F10). If the menu is activated with the mouse, + * then you should just use GTK's built-in position function, + * because it does a better job of positioning the menu. + * + * @param menu The menu we are positioning. + * @param x Address of the gint representing the horizontal position + * where the menu shall be drawn. This is an output parameter. + * @param y Address of the gint representing the vertical position + * where the menu shall be drawn. This is an output parameter. + * @param push_in This is an output parameter? + * @param user_data Not used by this particular position function. + */ +void gaim_gtk_treeview_popup_menu_position_func(GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data); #endif /* _GAIM_GTK_UTILS_H_ */ diff -r f2919ca98e78 -r 8633dc570442 src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Mon Jan 19 22:13:50 2004 +0000 +++ b/src/protocols/oscar/oscar.c Mon Jan 19 22:40:17 2004 +0000 @@ -4668,7 +4668,7 @@ } #ifdef NOSSI - aim_add_buddy(od->sess, od->conn, name); + aim_buddylist_addbuddy(od->sess, od->conn, name); #else if ((od->sess->ssi.received_data) && !(aim_ssi_itemlist_exists(od->sess->ssi.local, name))) { GaimBuddy *buddy = gaim_find_buddy(gc->account, name); @@ -4711,7 +4711,7 @@ static void oscar_remove_buddy(GaimConnection *gc, const char *name, const char *group) { OscarData *od = (OscarData *)gc->proto_data; #ifdef NOSSI - aim_remove_buddy(od->sess, od->conn, name); + aim_buddylist_removebuddy(od->sess, od->conn, name); #else if (od->sess->ssi.received_data) { gaim_debug(GAIM_DEBUG_INFO, "oscar", @@ -4726,7 +4726,7 @@ #ifdef NOSSI GList *cur; for (cur=buddies; cur; cur=cur->next) - aim_remove_buddy(od->sess, od->conn, cur->data); + aim_buddylist_removebuddy(od->sess, od->conn, cur->data); #else if (od->sess->ssi.received_data) { while (buddies) { @@ -6047,7 +6047,7 @@ GaimAccount *account = gaim_connection_get_account(gc); OscarData *od = (OscarData *)gc->proto_data; #ifdef NOSSI - GSList *list, *g = gaim_blist_groups(), *g1; + GSList *list; char buf[MAXMSGLEN]; int at; @@ -6076,28 +6076,9 @@ } aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf); break; - case 5: - g1 = g; - at = 0; - while (g1) { - list = gaim_blist_members((struct group *)g->data); - GSList list1 = list; - while (list1) { - struct buddy *b = list1->data; - if(b->account == account) - at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", b->name); - list1 = list1->next; - } - g_slist_free(list); - g1 = g1->next; - } - g_slist_free(g); - aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf); - break; default: break; } - signoff_blocked(gc); #else if (od->sess->ssi.received_data) aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff); @@ -6106,7 +6087,7 @@ static void oscar_add_permit(GaimConnection *gc, const char *who) { #ifdef NOSSI - if (gc->account->permdeny == 3) + if (gc->account->perm_deny == 3) oscar_set_permit_deny(gc); #else OscarData *od = (OscarData *)gc->proto_data; @@ -6118,7 +6099,7 @@ static void oscar_add_deny(GaimConnection *gc, const char *who) { #ifdef NOSSI - if (gc->account->permdeny == 4) + if (gc->account->perm_deny == 4) oscar_set_permit_deny(gc); #else OscarData *od = (OscarData *)gc->proto_data; @@ -6130,7 +6111,7 @@ static void oscar_rem_permit(GaimConnection *gc, const char *who) { #ifdef NOSSI - if (gc->account->permdeny == 3) + if (gc->account->perm_deny == 3) oscar_set_permit_deny(gc); #else OscarData *od = (OscarData *)gc->proto_data; @@ -6142,7 +6123,7 @@ static void oscar_rem_deny(GaimConnection *gc, const char *who) { #ifdef NOSSI - if (gc->account->permdeny == 4) + if (gc->account->perm_deny == 4) oscar_set_permit_deny(gc); #else OscarData *od = (OscarData *)gc->proto_data;