# HG changeset patch # User Etan Reisner # Date 1196480497 0 # Node ID b63b2a5c2d0563ad3803f1a03831db5e0553202c # Parent 14afe5a92fc25be88950d94d95a3c25bc328b172# Parent 50d1ee737a68c6aa1dc51a41d78c94b135800f8f merge of '282b4acb46729e4358221cb7cc78ad9373289199' and '3fe64d635336a6d7f6e520f4b2e041c3316c465d' diff -r 14afe5a92fc2 -r b63b2a5c2d05 COPYRIGHT --- a/COPYRIGHT Mon Nov 19 23:06:16 2007 +0000 +++ b/COPYRIGHT Sat Dec 01 03:41:37 2007 +0000 @@ -95,6 +95,7 @@ Chris Davies Josh Davis Martijn Dekker +Florian Delizy Vinicius Depizzol Philip Derrin Taso N. Devetzis diff -r 14afe5a92fc2 -r b63b2a5c2d05 ChangeLog --- a/ChangeLog Mon Nov 19 23:06:16 2007 +0000 +++ b/ChangeLog Sat Dec 01 03:41:37 2007 +0000 @@ -1,6 +1,11 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.3.0: +version 2.3.1 (??/??/????): + Finch: + * Color is used in the buddylist to indicate status. Look at the sample + gntrc file in the man-page for details. + +version 2.3.0 (11/24/2007): http://developer.pidgin.im/query?status=closed&milestone=2.3.0 NOTE: Some bugs marked fixed in 2.2.1, 2.2.2 or 2.2.3 may not have been fixed until this release (2.3.0). @@ -14,6 +19,13 @@ transfers between libpurple clients and Gajim clients, but will not work with iChat or Adium as they use a different file transfer implementation. + * XMPP password changes that return errors no longer cause the saved + password to be changed. + * XMPP file transfer support has been enhanced to support sending + files through a proxy when the server supports discovering a + a bytestream proxy. This should make file transfers much more + reliable. The next release will add support for manually specifying + a proxy when the server doesn't advertise one. Pidgin: * If a plugin says it can't be unloaded, we now display an error and diff -r 14afe5a92fc2 -r b63b2a5c2d05 ChangeLog.API --- a/ChangeLog.API Mon Nov 19 23:06:16 2007 +0000 +++ b/ChangeLog.API Sat Dec 01 03:41:37 2007 +0000 @@ -1,6 +1,15 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.3.0 (??/??/????): +version 2.3.1 (??/??/????): + Finch: + libgnt: + * Added gnt_tree_set_row_color to set the color for a row in a tree. + * Added gnt_style_get_string_list + * Added gnt_color_add_pair to define a new color. + * Added gnt_colors_get_color to get an ncurses color value from a + string. + +version 2.3.0 (11/24/2007): libpurple: Added: * a PurpleConversation field and an alias field in PurpleConvMessage diff -r 14afe5a92fc2 -r b63b2a5c2d05 ChangeLog.win32 --- a/ChangeLog.win32 Mon Nov 19 23:06:16 2007 +0000 +++ b/ChangeLog.win32 Sat Dec 01 03:41:37 2007 +0000 @@ -1,4 +1,4 @@ -version 2.3.0: +version 2.3.0 (11/24/2007): * Updated GTK+ to 2.12.1 (This was actually included in 2.2.2, but didn't get into the Changelog.) * Upgrade SILC to use the 1.1.5 toolkit @@ -135,7 +135,7 @@ version 0.82 (08/26/2004): * Selecting away messages using the system tray icon works - (Thanks François Gagné) + (Thanks Fran?ois Gagn?) * Transparency plugin will save your settings again (Kevin Stange) * Updated gtk-wimp to 0.6.2 * Updated libpng to 1.2.6 (major security update) diff -r 14afe5a92fc2 -r b63b2a5c2d05 NEWS --- a/NEWS Mon Nov 19 23:06:16 2007 +0000 +++ b/NEWS Sat Dec 01 03:41:37 2007 +0000 @@ -1,5 +1,16 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +2.3.0 (11/20/2007): + Luke: While this does not have the new MSN code, rest assured that + we are working on it and that it is nearing release. This contains + a significant number of fixes, including some that were marked as + fixed for earlier releases. Happy Thanksgiving! + + John: This is my first NEWS entry! So, this time around we have an + updated man page (the old one hadn't been really updated since + before the GTK+ 2.0 migration!), lots of bug fixes, and some new + features for you all. Enjoy! + 2.2.2 (10/23/2007): Luke: Because the main branch of pidgin development is still not ready for public consumption, I have taken some time to try to diff -r 14afe5a92fc2 -r b63b2a5c2d05 configure.ac --- a/configure.ac Mon Nov 19 23:06:16 2007 +0000 +++ b/configure.ac Sat Dec 01 03:41:37 2007 +0000 @@ -46,17 +46,17 @@ m4_define([purple_lt_current], [3]) m4_define([purple_major_version], [2]) m4_define([purple_minor_version], [3]) -m4_define([purple_micro_version], [0]) -m4_define([purple_version_suffix], []) +m4_define([purple_micro_version], [1]) +m4_define([purple_version_suffix], [devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix])) -m4_define([gnt_lt_current], [2]) +m4_define([gnt_lt_current], [3]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [2]) -m4_define([gnt_micro_version], [3]) -m4_define([gnt_version_suffix], []) +m4_define([gnt_minor_version], [3]) +m4_define([gnt_micro_version], [1]) +m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix])) @@ -303,6 +303,9 @@ AC_MSG_ERROR([ You must have the GLib 2.0 development headers installed to build. + +If you have these installed already you may need to install pkg-config so +I can find them. ])]) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) @@ -380,14 +383,14 @@ AC_MSG_ERROR([ You must have the GTK+ 2.0 development headers installed to compile Pidgin. -If you only want to build Finch then specify --disable-gtkui when running configure. +If you want to build only Finch then specify --disable-gtkui when running configure. ])]) AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) dnl We only really need Pango >= 1.4 for decent RTL support - PKG_CHECK_MODULES(pango, [pango >= 1.4.0], + PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0], AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:) dnl ####################################################################### diff -r 14afe5a92fc2 -r b63b2a5c2d05 doc/finch.1.in --- a/doc/finch.1.in Mon Nov 19 23:06:16 2007 +0000 +++ b/doc/finch.1.in Sat Dec 01 03:41:37 2007 +0000 @@ -88,9 +88,8 @@ Start resizing a window. Press the cursor keys to resize the window. When you are done, press \fBEnter\fR or \fBEscape\fR. .TP -.B Alt \+ d -Dump the contents of the screen in HTML format in a file named "dump.html" in -working directory. +.B Alt \+ D +Dump the contents of the screen in HTML format. .TP .B Alt \+ . Move the position of the current window in the window list one place to the @@ -107,8 +106,7 @@ Jump to the 1st, 2nd ... 10th window. .TP .B Ctrl \+ o -Bring up the menu (if there is one) for a window. Note that currently only the -buddylist has a menu. +Bring up the menu (if there is one) for a window. .TP .B Alt \+ / Show a list of available key-bindings for the current widget in focus. @@ -137,6 +135,18 @@ .TP A sample file looks like: .br +[Finch] +.br +color-available = green; black +.br +color-away = blue; black +.br +color-idle = gray; black +.br +color-offline = red; black +.br +#See below for details on color +.br [general] .br shadow = 0 @@ -494,7 +504,7 @@ .SH BUGS Known bugs are listed at .br -\fIhttp://sourceforge.net/tracker/?group_id=235&atid=100235\fR +\fIhttp://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR Before sending a bug report, please verify that you have the latest version of \fBfinch\fR and \fBlibpurple\fR. Many bugs (major and minor) are diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/finch.c --- a/finch/finch.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/finch.c Sat Dec 01 03:41:37 2007 +0000 @@ -297,7 +297,6 @@ if (opt_version) { /* Translators may want to transliterate the name. It is not to be translated. */ - gnt_quit(); printf("%s %s\n", _("Finch"), DISPLAY_VERSION); return 0; } @@ -418,8 +417,6 @@ g_set_application_name(_("Finch")); #endif - gnt_init(); - if (gnt_start(&argc, &argv)) { gnt_main(); diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/gntblist.c --- a/finch/gntblist.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/gntblist.c Sat Dec 01 03:41:37 2007 +0000 @@ -37,6 +37,7 @@ #include "debug.h" #include "gntbox.h" +#include "gntcolors.h" #include "gntcombobox.h" #include "gntentry.h" #include "gntft.h" @@ -46,6 +47,7 @@ #include "gntmenuitem.h" #include "gntmenuitemcheck.h" #include "gntpounce.h" +#include "gntstyle.h" #include "gnttree.h" #include "gntutils.h" #include "gntwindow.h" @@ -124,6 +126,37 @@ static int blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2); static int blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2); +static int color_available; +static int color_away; +static int color_offline; +static int color_idle; + +static int +get_display_color(PurpleBlistNode *node) +{ + PurpleBuddy *buddy; + int color = 0; + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node); + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + return 0; + + buddy = (PurpleBuddy*)node; + if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { + color = color_idle; + } else if (purple_presence_is_available(purple_buddy_get_presence(buddy))) { + color = color_available; + } else if (purple_presence_is_online(purple_buddy_get_presence(buddy)) && + !purple_presence_is_available(purple_buddy_get_presence(buddy))) { + color = color_away; + } else if (!purple_presence_is_online(purple_buddy_get_presence(buddy))) { + color = color_offline; + } + + return color; +} + static gboolean is_contact_online(PurpleContact *contact) { @@ -228,6 +261,7 @@ gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0, get_display_name(node)); gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node)); } if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { @@ -581,11 +615,11 @@ if (node->ui_data) return; - + name = get_display_name(node); if (name == NULL) return; - + group = (PurpleGroup*)node->parent; add_node((PurpleBlistNode*)group, ggblist); @@ -601,6 +635,7 @@ { PurpleContact *contact; PurpleBlistNode *node = (PurpleBlistNode *)buddy; + int color = 0; if (node->ui_data) return; @@ -615,13 +650,11 @@ node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), contact, NULL); - if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, GNT_TEXT_FLAG_DIM); - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, GNT_TEXT_FLAG_DIM); - } else { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, 0); - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, 0); - } + + color = get_display_color((PurpleBlistNode*)buddy); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); } #if 0 @@ -1542,7 +1575,8 @@ { PurpleContact *contact; GntTextFormatFlags bflag = 0, cflag = 0; - + int color = 0; + contact = purple_buddy_get_contact(buddy); gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy)); @@ -1556,19 +1590,17 @@ if (ggblist->tnode == (PurpleBlistNode*)buddy) draw_tooltip(ggblist); - if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag | GNT_TEXT_FLAG_DIM); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag | GNT_TEXT_FLAG_DIM); - else + color = get_display_color((PurpleBlistNode*)buddy); + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color); + + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); + if (buddy == purple_contact_get_priority_buddy(contact)) + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); + + if (buddy != purple_contact_get_priority_buddy(contact)) update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist); - } else { - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); - if (buddy == purple_contact_get_priority_buddy(contact)) - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); - else - update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist); - } } static void @@ -1736,8 +1768,40 @@ draw_tooltip(ggblist); } +static int +get_color(char *key) +{ +#if GLIB_CHECK_VERSION(2,6,0) + int fg = 0, bg = 0; + gsize n; + char **vals; + vals = gnt_style_get_string_list(NULL, key, &n); + if (vals && n == 2) { + fg = gnt_colors_get_color(vals[0]); + bg = gnt_colors_get_color(vals[1]); + return gnt_color_add_pair(fg, bg); + } + return 0; +#else + return 0; +#endif +} + void finch_blist_init() { + color_available = get_color("color-available"); + if (!color_available) + color_available = gnt_color_add_pair(COLOR_GREEN, -1); + color_away = get_color("color-away"); + if (!color_away) + color_away = gnt_color_add_pair(COLOR_BLUE, -1); + color_idle = get_color("color-idle"); + if (!color_idle) + color_idle = gnt_color_add_pair(COLOR_CYAN, -1); + color_offline = get_color("color-offline"); + if (!color_offline) + color_offline = gnt_color_add_pair(COLOR_RED, -1); + purple_prefs_add_none(PREF_ROOT); purple_prefs_add_none(PREF_ROOT "/size"); purple_prefs_add_int(PREF_ROOT "/size/width", 20); @@ -2550,4 +2614,3 @@ { gnt_widget_set_size(ggblist->window, width, height); } - diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/gntconn.c --- a/finch/gntconn.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/gntconn.c Sat Dec 01 03:41:37 2007 +0000 @@ -152,6 +152,10 @@ purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) continue; purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE)); + purple_conversation_write(conv, NULL, _("The account has disconnected and you are no " + "longer in this chat. You will be automatically rejoined in the chat when " + "the account reconnects."), + PURPLE_MESSAGE_SYSTEM, time(NULL)); } } diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/configure.ac --- a/finch/libgnt/configure.ac Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/configure.ac Sat Dec 01 03:41:37 2007 +0000 @@ -24,10 +24,10 @@ # Make sure to update ../../configure.ac with libgnt version changes. # -m4_define([gnt_lt_current], [2]) +m4_define([gnt_lt_current], [3]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [2]) -m4_define([gnt_micro_version], [0]) +m4_define([gnt_minor_version], [3]) +m4_define([gnt_micro_version], [1]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gnt.h --- a/finch/libgnt/gnt.h Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gnt.h Sat Dec 01 03:41:37 2007 +0000 @@ -30,6 +30,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#ifndef GNT_H +#define GNT_H + #include #include "gntwidget.h" #include "gntclipboard.h" @@ -201,3 +204,4 @@ */ gboolean gnt_is_refugee(void); +#endif /* GNT_H */ diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntbox.c --- a/finch/libgnt/gntbox.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntbox.c Sat Dec 01 03:41:37 2007 +0000 @@ -851,8 +851,14 @@ void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget) { - GList *find = g_list_find(box->focus, widget); - gpointer now = box->active; + GList *find; + gpointer now; + + while (GNT_WIDGET(box)->parent) + box = GNT_BOX(GNT_WIDGET(box)->parent); + + find = g_list_find(box->focus, widget); + now = box->active; if (find) box->active = widget; if (now && now != box->active) diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntcolors.c --- a/finch/libgnt/gntcolors.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntcolors.c Sat Dec 01 03:41:37 2007 +0000 @@ -33,6 +33,7 @@ #include static gboolean hascolors; +static int custom_type = GNT_COLORS; static struct { short r, g, b; @@ -137,8 +138,8 @@ } #if GLIB_CHECK_VERSION(2,6,0) -static int -get_color(char *key) +int +gnt_colors_get_color(char *key) { int color; gboolean custom = can_use_custom_color(); @@ -163,8 +164,12 @@ color = COLOR_MAGENTA; else if (strcmp(key, "cyan") == 0) color = COLOR_CYAN; - else + else if (strcmp(key, "default") == 0) color = -1; + else { + g_warning("Invalid color name: %s\n", key); + color = -1; + } return color; } @@ -196,7 +201,7 @@ int color = -1; key = g_ascii_strdown(key, -1); - color = get_color(key); + color = gnt_colors_get_color(key); g_free(key); if (color == -1) continue; @@ -237,8 +242,8 @@ GntColorType type = 0; gchar *fgc = g_ascii_strdown(list[0], -1); gchar *bgc = g_ascii_strdown(list[1], -1); - int fg = get_color(fgc); - int bg = get_color(bgc); + int fg = gnt_colors_get_color(fgc); + int bg = gnt_colors_get_color(bgc); g_free(fgc); g_free(bgc); if (fg == -1 || bg == -1) @@ -287,3 +292,8 @@ pair == GNT_COLOR_TITLE_D || pair == GNT_COLOR_DISABLED) ? 0 : A_STANDOUT)); } +int gnt_color_add_pair(int fg, int bg) +{ + init_pair(custom_type, fg, bg); + return custom_type++; +} diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntcolors.h --- a/finch/libgnt/gntcolors.h Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntcolors.h Sat Dec 01 03:41:37 2007 +0000 @@ -86,6 +86,16 @@ */ void gnt_color_pairs_parse(GKeyFile *kfile); +/** + * Parse a string color + * + * @param kfile The string value + * + * @return A color + * + * @since 2.3.1 (gnt), 2.3.1 (pidgin) + */ +int gnt_colors_get_color(char *key); #endif /** @@ -101,4 +111,15 @@ */ int gnt_color_pair(int color); +/** + * Adds a color definition + * + * @param fg Foreground + * @param bg Background + * + * @return A color pair + * + * @since 2.3.1 + */ +int gnt_color_add_pair(int fg, int bg); #endif diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntentry.c --- a/finch/libgnt/gntentry.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntentry.c Sat Dec 01 03:41:37 2007 +0000 @@ -140,7 +140,6 @@ static gboolean complete_suggest(GntEntry *entry, const char *text) { - gboolean changed = FALSE; int offstart = 0, offend = 0; if (entry->word) { @@ -148,27 +147,22 @@ const char *iter = text; offstart = g_utf8_pointer_to_offset(entry->start, s); while (*iter && toupper(*s) == toupper(*iter)) { - if (*s != *iter) - changed = TRUE; *s++ = *iter++; } if (*iter) { gnt_entry_key_pressed(GNT_WIDGET(entry), iter); - changed = TRUE; } offend = g_utf8_pointer_to_offset(entry->start, entry->cursor); } else { offstart = 0; gnt_entry_set_text_internal(entry, text); - changed = TRUE; offend = g_utf8_strlen(text, -1); } - if (changed) - g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0, - entry->start + offstart, entry->start + offend); + g_signal_emit(G_OBJECT(entry), signals[SIG_COMPLETION], 0, + entry->start + offstart, entry->start + offend); update_kill_ring(entry, ENTRY_JAIL, NULL, 0); - return changed; + return TRUE; } static int @@ -575,15 +569,16 @@ next_begin_word(const char *text, const char *end) { gunichar ch = 0; + + while (text && text < end && g_unichar_isspace(g_utf8_get_char(text))) + text = g_utf8_find_next_char(text, end); + ch = g_utf8_get_char(text); while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) { gunichar cur = g_utf8_get_char(text); if (!SAME(ch, cur)) break; } - - while (text && text < end && g_unichar_isspace(g_utf8_get_char(text))) - text = g_utf8_find_next_char(text, end); return (text ? text : end); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntmain.c --- a/finch/libgnt/gntmain.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntmain.c Sat Dec 01 03:41:37 2007 +0000 @@ -21,7 +21,7 @@ */ #define _GNU_SOURCE -#if defined(__APPLE__) || defined(__unix__) +#if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__) #define _XOPEN_SOURCE_EXTENDED #endif diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntmenu.c --- a/finch/libgnt/gntmenu.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntmenu.c Sat Dec 01 03:41:37 2007 +0000 @@ -47,6 +47,8 @@ static void (*org_size_request)(GntWidget *wid); static gboolean (*org_key_pressed)(GntWidget *w, const char *t); +static void menuitem_activate(GntMenu *menu, GntMenuItem *item); + static void menu_hide_all(GntMenu *menu) { @@ -56,6 +58,20 @@ } static void +show_submenu(GntMenu *menu) +{ + GntMenuItem *item; + + if (menu->type != GNT_MENU_TOPLEVEL) + return; + + item = g_list_nth_data(menu->list, menu->selected); + if (!item || !item->submenu) + return; + menuitem_activate(menu, item); +} + +static void gnt_menu_draw(GntWidget *widget) { GntMenu *menu = GNT_MENU(widget); @@ -276,6 +292,8 @@ gnt_widget_hide(widget); } else gnt_widget_hide(widget); + if (par && par->type == GNT_MENU_TOPLEVEL) + gnt_menu_key_pressed(GNT_WIDGET(par), text); return TRUE; } @@ -297,6 +315,7 @@ GntMenu *sub = menu->submenu; if (sub) gnt_widget_hide(GNT_WIDGET(sub)); + show_submenu(menu); gnt_widget_draw(widget); return TRUE; } diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntstyle.c --- a/finch/libgnt/gntstyle.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntstyle.c Sat Dec 01 03:41:37 2007 +0000 @@ -59,6 +59,21 @@ #endif } +char **gnt_style_get_string_list(const char *group, const char *key, gsize *length) +{ +#if GLIB_CHECK_VERSION(2,6,0) + const char *prg = g_get_prgname(); + if ((group == NULL || *group == '\0') && prg && + g_key_file_has_group(gkfile, prg)) + group = prg; + if (!group) + group = "general"; + return g_key_file_get_string_list(gkfile, group, key, length, NULL); +#else + return NULL; +#endif +} + gboolean gnt_style_get_bool(GntStyle style, gboolean def) { const char * str; diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntstyle.h --- a/finch/libgnt/gntstyle.h Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntstyle.h Sat Dec 01 03:41:37 2007 +0000 @@ -65,6 +65,20 @@ char *gnt_style_get_from_name(const char *group, const char *key); /** + * Get the value of a preference in ~/.gntrc. + * + * @param group The name of the group in the keyfile. If @c NULL, the prgname + * will be used first, if available. Otherwise, "general" will be used. + * @param key The key + * @param length Return location for the number of strings returned, or NULL + * + * @return NULL terminated string array. The array should be freed with g_strfreev(). + * + * @since 2.3.1 (gnt), 2.3.1 (pidgin) + */ +char **gnt_style_get_string_list(const char *group, const char *key, gsize *length); + +/** * Parse a boolean preference. For example, if 'value' is "false" (ignoring case) * or "0", the return value will be @c FALSE, otherwise @c TRUE. * diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gnttree.c --- a/finch/libgnt/gnttree.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gnttree.c Sat Dec 01 03:41:37 2007 +0000 @@ -75,6 +75,7 @@ If choice is true, then child will be NULL */ gboolean isselected; GntTextFormatFlags flags; + int color; GntTreeRow *parent; GntTreeRow *child; @@ -522,9 +523,14 @@ else { if (flags & GNT_TEXT_FLAG_DIM) - attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED)); + if (row->color) + attr |= (A_DIM | gnt_color_pair(row->color)); + else + attr |= (A_DIM | gnt_color_pair(GNT_COLOR_DISABLED)); else if (flags & GNT_TEXT_FLAG_HIGHLIGHT) attr |= (A_DIM | gnt_color_pair(GNT_COLOR_HIGHLIGHT)); + else if (row->color) + attr |= gnt_color_pair(row->color); else attr |= gnt_color_pair(GNT_COLOR_NORMAL); } @@ -1559,6 +1565,16 @@ redraw_tree(tree); /* XXX: It shouldn't be necessary to redraw the whole darned tree */ } +void gnt_tree_set_row_color(GntTree *tree, void *key, int color) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (!row || row->color == color) + return; + + row->color = color; + redraw_tree(tree); +} + void gnt_tree_set_selected(GntTree *tree , void *key) { int dist; diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gnttree.h Sat Dec 01 03:41:37 2007 +0000 @@ -325,6 +325,15 @@ void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags); /** + * Set color for the text in a row in the tree. + * + * @param tree The tree + * @param key The key for the row + * @param color The color + */ +void gnt_tree_set_row_color(GntTree *, void *, int); + +/** * Select a row. * * @param tree The tree diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntwindow.c --- a/finch/libgnt/gntwindow.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntwindow.c Sat Dec 01 03:41:37 2007 +0000 @@ -62,7 +62,8 @@ if (window->menu) gnt_widget_destroy(GNT_WIDGET(window->menu)); if (window->priv) { - g_hash_table_destroy(window->priv->accels); + if (window->priv->accels) + g_hash_table_destroy(window->priv->accels); g_free(window->priv); } org_destroy(widget); diff -r 14afe5a92fc2 -r b63b2a5c2d05 finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Mon Nov 19 23:06:16 2007 +0000 +++ b/finch/libgnt/gntwm.c Sat Dec 01 03:41:37 2007 +0000 @@ -21,13 +21,14 @@ */ #define _GNU_SOURCE -#if defined(__APPLE__) || defined(__unix__) +#if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__) #define _XOPEN_SOURCE_EXTENDED #endif #include "config.h" #include +#include #include #include #include @@ -41,6 +42,7 @@ #include "gntbox.h" #include "gntbutton.h" #include "gntentry.h" +#include "gntfilesel.h" #include "gntlabel.h" #include "gntmenu.h" #include "gnttextview.h" @@ -81,6 +83,7 @@ static int widestringwidth(wchar_t *wide); #endif +static void ensure_normal_mode(GntWM *wm); static gboolean write_already(gpointer data); static int write_timeout; static time_t last_active_time; @@ -392,6 +395,10 @@ if (!wm->cws->ordered || !wm->cws->ordered->next) return; + if (wm->mode != GNT_KP_MODE_NORMAL) { + ensure_normal_mode(wm); + } + w = wm->cws->ordered->data; pos = g_list_index(wm->cws->list, w); pos += direction; @@ -503,6 +510,7 @@ if (wm->cws->ordered) { gnt_widget_destroy(wm->cws->ordered->data); + ensure_normal_mode(wm); } return TRUE; @@ -522,6 +530,7 @@ setup__list(GntWM *wm) { GntWidget *tree, *win; + ensure_normal_mode(wm); win = wm->_list.window = gnt_box_new(FALSE, FALSE); gnt_box_set_toplevel(GNT_BOX(win), TRUE); gnt_box_set_pad(GNT_BOX(win), 0); @@ -656,12 +665,12 @@ return TRUE; } -static gboolean -dump_screen(GntBindable *bindable, GList *null) +static void +dump_file_save(GntFileSel *fs, const char *path, const char *f, gpointer n) { + FILE *file; int x, y; chtype old = 0, now = 0; - FILE *file = fopen("dump.html", "w"); struct { char ascii; char *unicode; @@ -683,6 +692,11 @@ {'\0', NULL} }; + + if ((file = g_fopen(path, "w+")) == NULL) { + return; + } + fprintf(file, "\n \n\n\n"); fprintf(file, "
");
 	for (y = 0; y < getmaxy(stdscr); y++) {
@@ -789,6 +803,24 @@
 	}
 	fprintf(file, "
\n"); fclose(file); + gnt_widget_destroy(GNT_WIDGET(fs)); +} + +static void +dump_file_cancel(GntWidget *w, GntFileSel *fs) +{ + gnt_widget_destroy(GNT_WIDGET(fs)); +} + +static gboolean +dump_screen(GntBindable *b, GList *null) +{ + GntWidget *window = gnt_file_sel_new(); + GntFileSel *sel = GNT_FILE_SEL(window); + gnt_file_sel_set_suggested_filename(sel, "dump.html"); + g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(dump_file_save), NULL); + g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(dump_file_cancel), sel); + gnt_widget_show(window); return TRUE; } @@ -972,6 +1004,16 @@ update_screen(wm); } +static void +ensure_normal_mode(GntWM *wm) +{ + if (wm->mode != GNT_KP_MODE_NORMAL) { + if (wm->cws->ordered) + window_reverse(wm->cws->ordered->data, FALSE, wm); + wm->mode = GNT_KP_MODE_NORMAL; + } +} + static gboolean start_move(GntBindable *bindable, GList *null) { @@ -1349,7 +1391,7 @@ gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list, "\033" "w", NULL); gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen, - "\033" "d", NULL); + "\033" "D", NULL); gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left, "\033" ",", NULL); gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right, @@ -1453,6 +1495,7 @@ if (wm->_list.window) { gnt_widget_destroy(wm->_list.window); } + ensure_normal_mode(wm); gnt_ws_hide(wm->cws, wm->nodes); wm->cws = s; gnt_ws_show(wm->cws, wm->nodes); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/Makefile.am --- a/libpurple/Makefile.am Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/Makefile.am Sat Dec 01 03:41:37 2007 +0000 @@ -156,7 +156,7 @@ dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \ connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \ - savedstatuses.h status.h server.h util.h xmlnode.h + savedstatuses.h status.h server.h util.h xmlnode.h prpl.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \ $(purple_builtheaders) diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/certificate.c --- a/libpurple/certificate.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/certificate.c Sat Dec 01 03:41:37 2007 +0000 @@ -1758,7 +1758,6 @@ gboolean purple_certificate_register_pool(PurpleCertificatePool *pool) { - gboolean success = FALSE; g_return_val_if_fail(pool, FALSE); g_return_val_if_fail(pool->scheme_name, FALSE); g_return_val_if_fail(pool->name, FALSE); @@ -1771,46 +1770,42 @@ /* Initialize the pool if needed */ if (pool->init) { + gboolean success; + success = pool->init(); - } else { - success = TRUE; + if (!success) + return FALSE; } - - if (success) { - /* Register the Pool */ - cert_pools = g_list_prepend(cert_pools, pool); - /* TODO: Emit a signal that the pool got registered */ + /* Register the Pool */ + cert_pools = g_list_prepend(cert_pools, pool); + + /* TODO: Emit a signal that the pool got registered */ - PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool); - purple_signal_register(pool, /* Signals emitted from pool */ - "certificate-stored", - purple_marshal_VOID__POINTER_POINTER, - NULL, /* No callback return value */ - 2, /* Two non-data arguments */ - purple_value_new(PURPLE_TYPE_SUBTYPE, - PURPLE_SUBTYPE_CERTIFICATEPOOL), - purple_value_new(PURPLE_TYPE_STRING)); + PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool); + purple_signal_register(pool, /* Signals emitted from pool */ + "certificate-stored", + purple_marshal_VOID__POINTER_POINTER, + NULL, /* No callback return value */ + 2, /* Two non-data arguments */ + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CERTIFICATEPOOL), + purple_value_new(PURPLE_TYPE_STRING)); - purple_signal_register(pool, /* Signals emitted from pool */ - "certificate-deleted", - purple_marshal_VOID__POINTER_POINTER, - NULL, /* No callback return value */ - 2, /* Two non-data arguments */ - purple_value_new(PURPLE_TYPE_SUBTYPE, - PURPLE_SUBTYPE_CERTIFICATEPOOL), - purple_value_new(PURPLE_TYPE_STRING)); - + purple_signal_register(pool, /* Signals emitted from pool */ + "certificate-deleted", + purple_marshal_VOID__POINTER_POINTER, + NULL, /* No callback return value */ + 2, /* Two non-data arguments */ + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CERTIFICATEPOOL), + purple_value_new(PURPLE_TYPE_STRING)); - purple_debug_info("certificate", - "CertificatePool %s registered\n", - pool->name); - return TRUE; - } else { - return FALSE; - } - - /* Control does not reach this point */ + purple_debug_info("certificate", + "CertificatePool %s registered\n", + pool->name); + + return TRUE; } gboolean diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/network.c --- a/libpurple/network.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/network.c Sat Dec 01 03:41:37 2007 +0000 @@ -182,22 +182,22 @@ /* Make sure the IP address entered by the user is valid */ if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL)) return ip; - } - - /* Check if STUN discovery was already done */ - stun = purple_stun_discover(NULL); - if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) - return stun->publicip; + } else { + /* Check if STUN discovery was already done */ + stun = purple_stun_discover(NULL); + if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) + return stun->publicip; - /* Attempt to get the IP from a NAT device using UPnP */ - ip = purple_upnp_get_public_ip(); - if (ip != NULL) - return ip; + /* Attempt to get the IP from a NAT device using UPnP */ + ip = purple_upnp_get_public_ip(); + if (ip != NULL) + return ip; - /* Attempt to get the IP from a NAT device using NAT-PMP */ - ip = purple_pmp_get_public_ip(); - if (ip != NULL) - return ip; + /* Attempt to get the IP from a NAT device using NAT-PMP */ + ip = purple_pmp_get_public_ip(); + if (ip != NULL) + return ip; + } /* Just fetch the IP of the local system */ return purple_network_get_local_system_ip(fd); @@ -362,7 +362,7 @@ listen_data->cb_data = cb_data; listen_data->socket_type = socket_type; - if (!listen_map_external) + if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports")) { purple_debug_info("network", "Skipping external port mapping.\n"); /* The pmp_map_cb does what we want to do */ @@ -677,11 +677,13 @@ purple_prefs_add_none ("/purple/network"); purple_prefs_add_bool ("/purple/network/auto_ip", TRUE); purple_prefs_add_string("/purple/network/public_ip", ""); + purple_prefs_add_bool ("/purple/network/map_ports", TRUE); purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE); purple_prefs_add_int ("/purple/network/ports_range_start", 1024); purple_prefs_add_int ("/purple/network/ports_range_end", 2048); - purple_upnp_discover(NULL, NULL); + if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip")) + purple_upnp_discover(NULL, NULL); #ifdef HAVE_LIBNM nm_context = libnm_glib_init(); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/ntlm.c --- a/libpurple/ntlm.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/ntlm.c Sat Dec 01 03:41:37 2007 +0000 @@ -187,7 +187,7 @@ { PurpleCipher *cipher; PurpleCipherContext *context; - gsize outlen; + size_t outlen; cipher = purple_ciphers_find_cipher("des"); context = purple_cipher_context_new(cipher, NULL); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/plugins/newline.c --- a/libpurple/plugins/newline.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/plugins/newline.c Sat Dec 01 03:41:37 2007 +0000 @@ -31,6 +31,12 @@ addnewline_msg_cb(PurpleAccount *account, char *sender, char **message, PurpleConversation *conv, int *flags, void *data) { + if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) && + !purple_prefs_get_bool("/plugins/core/newline/im")) || + ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && + !purple_prefs_get_bool("/plugins/core/newline/chat"))) + return FALSE; + if (g_ascii_strncasecmp(*message, "/me ", strlen("/me "))) { char *tmp = g_strdup_printf("
%s", *message); g_free(*message); @@ -40,6 +46,25 @@ return FALSE; } +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) { + PurplePluginPrefFrame *frame; + PurplePluginPref *ppref; + + frame = purple_plugin_pref_frame_new(); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/newline/im", _("Add new line in IMs")); + purple_plugin_pref_frame_add(frame, ppref); + + ppref = purple_plugin_pref_new_with_name_and_label( + "/plugins/core/newline/chat", _("Add new line in Chats")); + purple_plugin_pref_frame_add(frame, ppref); + + return frame; +} + + static gboolean plugin_load(PurplePlugin *plugin) { @@ -53,6 +78,17 @@ return TRUE; } +static PurplePluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL, /* frame (Reserved) */ + /* Padding */ + NULL, + NULL, + NULL, + NULL +}; + static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, /**< magic */ @@ -80,7 +116,7 @@ NULL, /**< ui_info */ NULL, /**< extra_info */ - NULL, /**< prefs_info */ + &prefs_info, /**< prefs_info */ NULL, /**< actions */ /* padding */ @@ -92,6 +128,9 @@ static void init_plugin(PurplePlugin *plugin) { + purple_prefs_add_none("/plugins/core/newline"); + purple_prefs_add_bool("/plugins/core/newline/im", TRUE); + purple_prefs_add_bool("/plugins/core/newline/chat", TRUE); } PURPLE_INIT_PLUGIN(lastseen, init_plugin, info) diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/plugins/ssl/ssl-gnutls.c --- a/libpurple/plugins/ssl/ssl-gnutls.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Sat Dec 01 03:41:37 2007 +0000 @@ -512,9 +512,8 @@ dt.data = (unsigned char *) buf; dt.size = buf_sz; - /* Perform the conversion */ - crt = x509_import_from_datum(dt, - GNUTLS_X509_FMT_PEM); // files should be in PEM format + /* Perform the conversion; files should be in PEM format */ + crt = x509_import_from_datum(dt, GNUTLS_X509_FMT_PEM); /* Cleanup */ g_free(buf); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/bonjour/bonjour.c --- a/libpurple/protocols/bonjour/bonjour.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Sat Dec 01 03:41:37 2007 +0000 @@ -43,14 +43,6 @@ #include "buddy.h" #include "bonjour_ft.h" -/* - * TODO: Should implement an add_buddy callback that removes the buddy - * from the local list. Bonjour manages buddies for you, and - * adding someone locally by hand is stupid. Or, maybe even better, - * if a PRPL does not have an add_buddy callback then do not allow - * users to add buddies. - */ - static char *default_firstname; static char *default_lastname; static char *default_hostname; @@ -260,6 +252,25 @@ g_free(stripped); } +/* + * The add_buddy callback removes the buddy from the local list. + * Bonjour manages buddies for you, and adding someone locally by + * hand is stupid. Perhaps we should change libpurple not to allow adding + * if there is no add_buddy callback. + */ +static void +bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) { + purple_debug_error("bonjour", "Buddy '%s' manually added; removing. " + "Bonjour buddies must be discovered and not manually added.\n", + purple_buddy_get_name(buddy)); + + /* I suppose we could alert the user here, but it seems unnecessary. */ + + /* If this causes problems, it can be moved to an idle callback */ + purple_blist_remove_buddy(buddy); +} + + static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) { if (buddy->proto_data) { bonjour_buddy_delete(buddy->proto_data); @@ -303,7 +314,7 @@ PurpleBuddy *buddy = purple_find_buddy(connection->account, who); BonjourBuddy *bb; - if (buddy == NULL) + if (buddy == NULL || buddy->proto_data == NULL) { /* * This buddy is not in our buddy list, and therefore does not really @@ -370,6 +381,11 @@ if (message != NULL) purple_notify_user_info_add_pair(user_info, _("Message"), message); + if (bb == NULL) { + purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n"); + return; + } + /* Only show first/last name if there is a nickname set (to avoid duplication) */ if (bb->nick != NULL) { if (bb->first != NULL) @@ -425,7 +441,7 @@ bonjour_set_status, /* set_status */ NULL, /* set_idle */ NULL, /* change_passwd */ - NULL, /* add_buddy */ + bonjour_fake_add_buddy, /* add_buddy */ NULL, /* add_buddies */ bonjour_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/bonjour/issues.txt --- a/libpurple/protocols/bonjour/issues.txt Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/bonjour/issues.txt Sat Dec 01 03:41:37 2007 +0000 @@ -2,5 +2,4 @@ ============= Known issues =============== ========================================== -* File transfers * Typing notifications diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/bonjour/jabber.c --- a/libpurple/protocols/bonjour/jabber.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Sat Dec 01 03:41:37 2007 +0000 @@ -19,6 +19,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" + #ifndef _WIN32 #include #include @@ -33,7 +36,6 @@ #include #include -#include "internal.h" #include "network.h" #include "eventloop.h" #include "connection.h" @@ -618,7 +620,7 @@ } memset(&my_addr, 0, sizeof(struct sockaddr_in)); - my_addr.sin_family = PF_INET; + my_addr.sin_family = AF_INET; /* Attempt to find a free port */ bind_successful = FALSE; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/bonjour/mdns_avahi.c --- a/libpurple/protocols/bonjour/mdns_avahi.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Sat Dec 01 03:41:37 2007 +0000 @@ -155,7 +155,7 @@ /* Make sure it isn't us */ if (purple_utf8_strcasecmp(name, account->username) != 0) { if (!avahi_service_resolver_new(avahi_service_browser_get_client(b), - interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, + interface, protocol, name, type, domain, AVAHI_PROTO_INET, 0, _resolver_callback, account)) { purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); @@ -315,14 +315,14 @@ case PUBLISH_START: publish_result = avahi_entry_group_add_service_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, 0, + AVAHI_PROTO_INET, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, NULL, data->port_p2pj, lst); break; case PUBLISH_UPDATE: publish_result = avahi_entry_group_update_service_txt_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, 0, + AVAHI_PROTO_INET, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, lst); break; @@ -354,7 +354,7 @@ g_return_val_if_fail(idata != NULL, FALSE); - idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account); + idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, ICHAT_SERVICE, NULL, 0, _browser_callback, data->account); if (!idata->sb) { purple_debug_error("bonjour", @@ -400,7 +400,7 @@ purple_account_get_username(data->account)); ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, flags, svc_name, + AVAHI_PROTO_INET, flags, svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len); g_free(svc_name); @@ -486,7 +486,7 @@ name = g_strdup_printf("%s." ICHAT_SERVICE "local", buddy->name); idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, + AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, _buddy_icon_record_cb, buddy); g_free(name); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/irc/cmds.c --- a/libpurple/protocols/irc/cmds.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/irc/cmds.c Sat Dec 01 03:41:37 2007 +0000 @@ -294,14 +294,17 @@ ops = g_new0(char *, i * 2 + 1); for (i = 0; nicks[i]; i++) { - if (!*nicks[i]) + if (!*nicks[i]) { + g_free(nicks[i]); continue; + } ops[used++] = mode; ops[used++] = nicks[i]; } irc_do_mode(irc, target, sign, ops); g_free(ops); + g_free(nicks); /* No, not g_strfreev */ return 0; } diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/irc/msgs.c --- a/libpurple/protocols/irc/msgs.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/irc/msgs.c Sat Dec 01 03:41:37 2007 +0000 @@ -138,6 +138,8 @@ irc->mode_chars = g_strdup(val + 1); } } + + g_strfreev(features); } void irc_msg_luser(struct irc_conn *irc, const char *name, const char *from, char **args) diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/irc/parse.c Sat Dec 01 03:41:37 2007 +0000 @@ -64,7 +64,7 @@ { "318", "nt:", irc_msg_endwhois }, /* End of WHOIS */ { "319", "nn:", irc_msg_whois }, /* Whois channels */ { "320", "nn:", irc_msg_whois }, /* Whois (fn ident) */ - { "314", "nnvvv:", irc_msg_whois }, /* Whowas user */ + { "314", "nnnvv:", irc_msg_whois }, /* Whowas user */ { "369", "nt:", irc_msg_endwhois }, /* End of WHOWAS */ { "321", "*", irc_msg_list }, /* Start of list */ { "322", "ncv:", irc_msg_list }, /* List. */ diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/caps.c --- a/libpurple/protocols/jabber/caps.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/caps.c Sat Dec 01 03:41:37 2007 +0000 @@ -257,18 +257,23 @@ /* this function assumes that all information is available locally */ static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const char *ver, GList *ext) { - JabberCapsClientInfo *result = g_new0(JabberCapsClientInfo, 1); + JabberCapsClientInfo *result; JabberCapsKey *key = g_new0(JabberCapsKey, 1); JabberCapsValue *caps; GList *iter; - + key->node = (char *)node; key->ver = (char *)ver; - + caps = g_hash_table_lookup(capstable,key); - + g_free(key); - + + if (caps == NULL) + return NULL; + + result = g_new0(JabberCapsClientInfo, 1); + /* join all information */ for(iter = caps->identities; iter; iter = g_list_next(iter)) { JabberCapsIdentity *id = iter->data; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/disco.c Sat Dec 01 03:41:37 2007 +0000 @@ -44,6 +44,37 @@ xmlnode_set_attrib(feature, "var", x); \ } +static void +jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer data) { + JabberBytestreamsStreamhost *sh = data; + const char *from = xmlnode_get_attrib(packet, "from"); + xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", + "http://jabber.org/protocol/bytestreams"); + + if (from && !strcmp(from, sh->jid) && query != NULL) { + xmlnode *sh_node = xmlnode_get_child(query, "streamhost"); + if (sh_node) { + const char *jid = xmlnode_get_attrib(sh_node, "jid"); + const char *port = xmlnode_get_attrib(sh_node, "port"); + + + if (jid == NULL || strcmp(jid, from) != 0) + purple_debug_error("jabber", "Invalid jid(%s) for bytestream.\n", + jid ? jid : "(null)"); + + sh->host = g_strdup(xmlnode_get_attrib(sh_node, "host")); + sh->zeroconf = g_strdup(xmlnode_get_attrib(sh_node, "zeroconf")); + if (port != NULL) + sh->port = atoi(port); + } + } + + purple_debug_info("jabber", "Discovered bytestream proxy server: " + "jid='%s' host='%s' port='%d' zeroconf='%s'\n", + from ? from : "", sh->host ? sh->host : "", + sh->port, sh->zeroconf ? sh->zeroconf : ""); +} + void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { const char *from = xmlnode_get_attrib(packet, "from"); @@ -191,10 +222,26 @@ if(!strcmp(category, "conference") && !strcmp(type, "text")) { /* we found a groupchat or MUC server, add it to the list */ /* XXX: actually check for protocol/muc or gc-1.0 support */ - js->chat_servers = g_list_append(js->chat_servers, g_strdup(from)); + js->chat_servers = g_list_prepend(js->chat_servers, g_strdup(from)); } else if(!strcmp(category, "directory") && !strcmp(type, "user")) { /* we found a JUD */ - js->user_directories = g_list_append(js->user_directories, g_strdup(from)); + js->user_directories = g_list_prepend(js->user_directories, g_strdup(from)); + } else if(!strcmp(category, "proxy") && !strcmp(type, "bytestreams")) { + /* This is a bytestream proxy */ + JabberIq *iq; + JabberBytestreamsStreamhost *sh; + + purple_debug_info("jabber", "Found bytestream proxy server: %s\n", from); + + sh = g_new0(JabberBytestreamsStreamhost, 1); + sh->jid = g_strdup(from); + js->bs_proxies = g_list_prepend(js->bs_proxies, sh); + + iq = jabber_iq_new_query(js, JABBER_IQ_GET, + "http://jabber.org/protocol/bytestreams"); + xmlnode_set_attrib(iq->node, "to", sh->jid); + jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh); + jabber_iq_send(iq); } } else if(!strcmp(child->name, "feature")) { @@ -344,8 +391,8 @@ g_free(js->server_name); js->server_name = g_strdup(name); if (!strcmp(name, "Google Talk")) { - purple_debug_info("jabber", "Google Talk!\n"); - js->googletalk = TRUE; + purple_debug_info("jabber", "Google Talk!\n"); + js->googletalk = TRUE; } } @@ -422,8 +469,7 @@ jabber_iq_set_callback(iq, jabber_disco_server_items_result_cb, NULL); jabber_iq_send(iq); - iq = jabber_iq_new_query(js, JABBER_IQ_GET, - "http://jabber.org/protocol/disco#info"); + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info"); xmlnode_set_attrib(iq->node, "to", js->user->domain); jabber_iq_set_callback(iq, jabber_disco_server_info_result_cb, NULL); jabber_iq_send(iq); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/google.c --- a/libpurple/protocols/jabber/google.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/google.c Sat Dec 01 03:41:37 2007 +0000 @@ -532,6 +532,6 @@ char *jabber_google_presence_outgoing(PurpleStatus *tune) { - char *ret = g_strdup_printf("♫ %s", purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE)); - return ret; + const char *attr = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); + return attr ? g_strdup_printf("♫ %s", attr) : g_strdup(""); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/iq.c --- a/libpurple/protocols/jabber/iq.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/iq.c Sat Dec 01 03:41:37 2007 +0000 @@ -399,5 +399,6 @@ void jabber_iq_uninit(void) { g_hash_table_destroy(iq_handlers); + iq_handlers = NULL; } diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sat Dec 01 03:41:37 2007 +0000 @@ -1234,20 +1234,31 @@ g_hash_table_destroy(js->buddies); if(js->chats) g_hash_table_destroy(js->chats); + while(js->chat_servers) { g_free(js->chat_servers->data); js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers); } + while(js->user_directories) { g_free(js->user_directories->data); js->user_directories = g_list_delete_link(js->user_directories, js->user_directories); } - if(js->stream_id) - g_free(js->stream_id); + + while(js->bs_proxies) { + JabberBytestreamsStreamhost *sh = js->bs_proxies->data; + g_free(sh->jid); + g_free(sh->host); + g_free(sh->zeroconf); + g_free(sh); + js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies); + } + + g_free(js->stream_id); if(js->user) jabber_id_free(js->user); - if(js->avatar_hash) - g_free(js->avatar_hash); + g_free(js->avatar_hash); + purple_circ_buffer_destroy(js->write_buffer); if(js->writeh) purple_input_remove(js->writeh); @@ -1256,11 +1267,9 @@ sasl_dispose(&js->sasl); if(js->sasl_mechs) g_string_free(js->sasl_mechs, TRUE); - if(js->sasl_cb) - g_free(js->sasl_cb); + g_free(js->sasl_cb); #endif - if(js->serverFQDN) - g_free(js->serverFQDN); + g_free(js->serverFQDN); while(js->commands) { JabberAdHocCommands *cmd = js->commands->data; g_free(cmd->jid); @@ -1272,21 +1281,14 @@ g_free(js->server_name); g_free(js->gmail_last_time); g_free(js->gmail_last_tid); - if(js->old_msg) - g_free(js->old_msg); - if(js->old_avatarhash) - g_free(js->old_avatarhash); - if(js->old_artist) - g_free(js->old_artist); - if(js->old_title) - g_free(js->old_title); - if(js->old_source) - g_free(js->old_source); - if(js->old_uri) - g_free(js->old_uri); - if(js->old_track) - g_free(js->old_track); - + g_free(js->old_msg); + g_free(js->old_avatarhash); + g_free(js->old_artist); + g_free(js->old_title); + g_free(js->old_source); + g_free(js->old_uri); + g_free(js->old_track); + g_free(js); gc->proto_data = NULL; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sat Dec 01 03:41:37 2007 +0000 @@ -117,7 +117,7 @@ GHashTable *disco_callbacks; int next_id; - + GList *bs_proxies; GList *oob_file_transfers; GList *file_transfers; @@ -146,7 +146,7 @@ char *gmail_last_time; char *gmail_last_tid; - char *serverFQDN; + char *serverFQDN; /* OK, this stays at the end of the struct, so plugins can depend * on the rest of the stuff being in the right place @@ -202,6 +202,13 @@ JabberFeatureEnabled *is_enabled; } JabberFeature; +typedef struct _JabberBytestreamsStreamhost { + char *jid; + char *host; + int port; + char *zeroconf; +} JabberBytestreamsStreamhost; + /* what kind of additional features as returned from disco#info are supported? */ extern GList *jabber_features; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sat Dec 01 03:41:37 2007 +0000 @@ -219,25 +219,31 @@ option); option = purple_account_option_bool_new( - _("Allow plaintext auth over unencrypted streams"), - "auth_plain_in_clear", FALSE); + _("Allow plaintext auth over unencrypted streams"), + "auth_plain_in_clear", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - + option); + option = purple_account_option_int_new(_("Connect port"), "port", 5222); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - + option); + option = purple_account_option_string_new(_("Connect server"), - "connect_server", NULL); + "connect_server", NULL); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - + option); + + option = purple_account_option_string_new(_("File transfer proxies"), + "ft_proxies", + /* TODO: Is this an acceptable default? */ + "proxy.jabber.org:7777"); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + jabber_init_plugin(plugin); - + purple_prefs_remove("/plugins/prpl/jabber"); - + /* XXX - If any other plugin wants SASL this won't be good ... */ #ifdef HAVE_CYRUS_SASL #ifdef _WIN32 diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Sat Dec 01 03:41:37 2007 +0000 @@ -99,8 +99,6 @@ { PurpleConnection *gc = NULL; JabberStream *js = NULL; - gboolean disconnected; - int primitive; xmlnode *presence, *x, *photo; char *stripped = NULL; JabberBuddyState state; @@ -108,22 +106,26 @@ const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL; int length = -1; gboolean allowBuzz; - PurplePresence *p = purple_account_get_presence(account); + PurplePresence *p; PurpleStatus *tune; + if (purple_account_is_disconnected(account)) + return; + + p = purple_account_get_presence(account); if (NULL == status) { status = purple_presence_get_active_status(p); } - if(!purple_status_is_active(status)) - return; - - disconnected = purple_account_is_disconnected(account); - - if(disconnected) - return; - - primitive = purple_status_type_get_primitive(purple_status_get_type(status)); + if (purple_status_is_exclusive(status)) { + /* An exclusive status can't be deactivated. You should just + * activate some other exclusive status. */ + if (!purple_status_is_active(status)) + return; + } else { + /* Work with the exclusive status. */ + status = purple_presence_get_active_status(p); + } gc = purple_account_get_connection(account); js = gc->proto_data; @@ -308,8 +310,9 @@ static void authorize_add_cb(gpointer data) { struct _jabber_add_permit *jap = data; - jabber_presence_subscription_set(jap->gc->proto_data, jap->who, - "subscribed"); + if(PURPLE_CONNECTION_IS_VALID(jap->gc)) + jabber_presence_subscription_set(jap->gc->proto_data, + jap->who, "subscribed"); g_free(jap->who); g_free(jap); } @@ -317,9 +320,9 @@ static void deny_add_cb(gpointer data) { struct _jabber_add_permit *jap = data; - jabber_presence_subscription_set(jap->gc->proto_data, jap->who, - "unsubscribed"); - + if(PURPLE_CONNECTION_IS_VALID(jap->gc)) + jabber_presence_subscription_set(jap->gc->proto_data, + jap->who, "unsubscribed"); g_free(jap->who); g_free(jap); } @@ -373,23 +376,26 @@ static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) { JabberPresenceCapabilities *userdata = user_data; GList *iter; - + if(userdata->jbr->caps) jabber_caps_free_clientinfo(userdata->jbr->caps); userdata->jbr->caps = info; - - for(iter = info->features; iter; iter = g_list_next(iter)) { - if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { - JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); - xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); - xmlnode_set_attrib(iq->node, "to", userdata->from); - xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); - - jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); - jabber_iq_send(iq); - break; + + if (info) { + for(iter = info->features; iter; iter = g_list_next(iter)) { + if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { + JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); + xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); + xmlnode_set_attrib(iq->node, "to", userdata->from); + xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); + + jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); + jabber_iq_send(iq); + break; + } } } + g_free(userdata->from); g_free(userdata); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/jabber/si.c --- a/libpurple/protocols/jabber/si.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/jabber/si.c Sat Dec 01 03:41:37 2007 +0000 @@ -36,19 +36,14 @@ #include "iq.h" #include "si.h" -#include "si.h" - -struct bytestreams_streamhost { - char *jid; - char *host; - int port; -}; +#define STREAMHOST_CONNECT_TIMEOUT 15 typedef struct _JabberSIXfer { JabberStream *js; PurpleProxyConnectData *connect_data; PurpleNetworkListenData *listen_data; + guint connect_timeout; gboolean accepted; @@ -99,39 +94,82 @@ JabberSIXfer *jsx = xfer->data; JabberIq *iq; xmlnode *query, *su; - struct bytestreams_streamhost *streamhost = jsx->streamhosts->data; + JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data; purple_proxy_info_destroy(jsx->gpi); + jsx->gpi = NULL; jsx->connect_data = NULL; + if (jsx->connect_timeout > 0) + purple_timeout_remove(jsx->connect_timeout); + jsx->connect_timeout = 0; + if(source < 0) { purple_debug_warning("jabber", "si connection failed, jid was %s, host was %s, error was %s\n", - streamhost->jid, streamhost->host, error_message); + streamhost->jid, streamhost->host, + error_message ? error_message : "(null)"); jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); g_free(streamhost->jid); g_free(streamhost->host); + g_free(streamhost->zeroconf); g_free(streamhost); jabber_si_bytestreams_attempt_connect(xfer); return; } - iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams"); - xmlnode_set_attrib(iq->node, "to", xfer->who); - jabber_iq_set_id(iq, jsx->iq_id); - query = xmlnode_get_child(iq->node, "query"); - su = xmlnode_new_child(query, "streamhost-used"); - xmlnode_set_attrib(su, "jid", streamhost->jid); + /* unknown file transfer type is assumed to be RECEIVE */ + if(xfer->type == PURPLE_XFER_SEND) + { + xmlnode *activate; + iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, "http://jabber.org/protocol/bytestreams"); + xmlnode_set_attrib(iq->node, "to", streamhost->jid); + query = xmlnode_get_child(iq->node, "query"); + xmlnode_set_attrib(query, "sid", jsx->stream_id); + activate = xmlnode_new_child(query, "activate"); + xmlnode_insert_data(activate, xfer->who, -1); + + /* TODO: We need to wait for an activation result before starting */ + } + else + { + iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams"); + xmlnode_set_attrib(iq->node, "to", xfer->who); + jabber_iq_set_id(iq, jsx->iq_id); + query = xmlnode_get_child(iq->node, "query"); + su = xmlnode_new_child(query, "streamhost-used"); + xmlnode_set_attrib(su, "jid", streamhost->jid); + } jabber_iq_send(iq); purple_xfer_start(xfer, source, NULL, -1); } +static gboolean +connect_timeout_cb(gpointer data) +{ + PurpleXfer *xfer = data; + JabberSIXfer *jsx = xfer->data; + + purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT); + + jsx->connect_timeout = 0; + + if (jsx->connect_data != NULL) + purple_proxy_connect_cancel(jsx->connect_data); + jsx->connect_data = NULL; + + /* Trigger the connect error manually */ + jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded."); + + return FALSE; +} + static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer) { JabberSIXfer *jsx = xfer->data; - struct bytestreams_streamhost *streamhost; + JabberBytestreamsStreamhost *streamhost; char *dstaddr, *p; int i; unsigned char hashval[20]; @@ -160,18 +198,28 @@ streamhost = jsx->streamhosts->data; + jsx->connect_data = NULL; + if (jsx->gpi != NULL) + purple_proxy_info_destroy(jsx->gpi); + jsx->gpi = NULL; + dstjid = jabber_id_new(xfer->who); - if(dstjid != NULL) { + /* TODO: Deal with zeroconf */ + + if(dstjid != NULL && streamhost->host && streamhost->port > 0) { jsx->gpi = purple_proxy_info_new(); purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5); purple_proxy_info_set_host(jsx->gpi, streamhost->host); purple_proxy_info_set_port(jsx->gpi, streamhost->port); - - - dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, jsx->js->user->node, - jsx->js->user->domain, jsx->js->user->resource); + /* unknown file transfer type is assumed to be RECEIVE */ + if(xfer->type == PURPLE_XFER_SEND) + dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain, + jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource); + else + dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, + jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource); purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), sizeof(hashval), hashval, NULL); @@ -186,6 +234,11 @@ jabber_si_bytestreams_connect_cb, xfer); g_free(dstaddr); + /* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */ + if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL) + jsx->connect_timeout = purple_timeout_add_seconds( + STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer); + jabber_id_free(dstjid); } @@ -194,6 +247,7 @@ jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); g_free(streamhost->jid); g_free(streamhost->host); + g_free(streamhost->zeroconf); g_free(streamhost); jabber_si_bytestreams_attempt_connect(xfer); } @@ -232,17 +286,19 @@ for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; streamhost = xmlnode_get_next_twin(streamhost)) { - const char *jid, *host, *port; - int portnum; + const char *jid, *host = NULL, *port, *zeroconf; + int portnum = 0; if((jid = xmlnode_get_attrib(streamhost, "jid")) && - (host = xmlnode_get_attrib(streamhost, "host")) && + ((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) || + ((host = xmlnode_get_attrib(streamhost, "host")) && (port = xmlnode_get_attrib(streamhost, "port")) && - (portnum = atoi(port))) { - struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1); + (portnum = atoi(port))))) { + JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1); sh->jid = g_strdup(jid); sh->host = g_strdup(host); sh->port = portnum; + sh->zeroconf = g_strdup(zeroconf); jsx->streamhosts = g_list_append(jsx->streamhosts, sh); } } @@ -351,7 +407,7 @@ jsx->js->user->resource, xfer->who); purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), - sizeof(hashval), hashval, NULL); + sizeof(hashval), hashval, NULL); g_free(dstaddr); dstaddr = g_malloc(41); p = dstaddr; @@ -363,9 +419,12 @@ purple_debug_error("jabber", "someone connected with the wrong info!\n"); close(source); purple_xfer_cancel_remote(xfer); + g_free(dstaddr); return; } + g_free(dstaddr); + g_free(jsx->rxqueue); host = purple_network_get_my_ip(jsx->js->fd); @@ -523,6 +582,32 @@ source, PURPLE_INPUT_WRITE); } +static gint +jabber_si_compare_jid(gconstpointer a, gconstpointer b) +{ + const JabberBytestreamsStreamhost *sh = a; + + if(!a) + return -1; + + return strcmp(sh->jid, (char *)b); +} + + +static void +jabber_si_free_streamhost(gpointer data, gpointer user_data) +{ + JabberBytestreamsStreamhost *sh = data; + + if(!data) + return; + + g_free(sh->jid); + g_free(sh->host); + g_free(sh->zeroconf); + g_free(sh); +} + static void jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond) @@ -537,6 +622,7 @@ return; else if(acceptfd == -1) { purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno)); + /* TODO: This should cancel the ft */ return; } @@ -544,7 +630,61 @@ close(source); xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ, - jabber_si_xfer_bytestreams_send_read_cb, xfer); + jabber_si_xfer_bytestreams_send_read_cb, xfer); +} + +static void +jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet, + gpointer data) +{ + PurpleXfer *xfer = data; + JabberSIXfer *jsx = xfer->data; + xmlnode *query, *streamhost_used; + const char *from, *type, *jid; + GList *matched; + + /* TODO: This need to send errors if we don't see what we're looking for */ + + /* In the case of a direct file transfer, this is expected to return */ + if(!jsx) + return; + + if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) + return; + + if(!(from = xmlnode_get_attrib(packet, "from"))) + return; + + if(!(query = xmlnode_get_child(packet, "query"))) + return; + + if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used"))) + return; + + if(!(jid = xmlnode_get_attrib(streamhost_used, "jid"))) + return; + + if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid))) + { + gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, + jsx->js->user->domain, jsx->js->user->resource); + if (!strcmp(jid, my_jid)) + purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n"); + else + purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n"); + g_free(my_jid); + return; + } + + /* TODO: Clean up the local SOCKS5 proxy - it isn't going to be used.*/ + + jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched); + g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL); + g_list_free(jsx->streamhosts); + + jsx->streamhosts = matched; + + jabber_si_bytestreams_attempt_connect(xfer); } static void @@ -554,7 +694,10 @@ JabberSIXfer *jsx; JabberIq *iq; xmlnode *query, *streamhost; - char *jid, *port; + char *jid, port[6]; + const char *local_ip, *public_ip, *ft_proxies; + GList *tmp; + JabberBytestreamsStreamhost *sh, *sh2; jsx = xfer->data; jsx->listen_data = NULL; @@ -578,27 +721,112 @@ xmlnode_set_attrib(query, "sid", jsx->stream_id); - streamhost = xmlnode_new_child(query, "streamhost"); jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource); - xmlnode_set_attrib(streamhost, "jid", jid); + xfer->local_port = purple_network_get_port_from_fd(sock); + g_snprintf(port, sizeof(port), "%hu", xfer->local_port); + + /* TODO: Should there be an option to not use the local host as a ft proxy? + * (to prevent revealing IP address, etc.) */ + + /* Include the localhost's IP (for in-network transfers) */ + local_ip = purple_network_get_local_system_ip(jsx->js->fd); + if (strcmp(local_ip, "0.0.0.0") != 0) + { + streamhost = xmlnode_new_child(query, "streamhost"); + xmlnode_set_attrib(streamhost, "jid", jid); + xmlnode_set_attrib(streamhost, "host", local_ip); + xmlnode_set_attrib(streamhost, "port", port); + } + + /* Include the public IP (assuming that there is a port mapped somehow) */ + /* TODO: Check that it isn't the same as above and is a valid IP */ + public_ip = purple_network_get_my_ip(jsx->js->fd); + if (strcmp(public_ip, local_ip) != 0) + { + streamhost = xmlnode_new_child(query, "streamhost"); + xmlnode_set_attrib(streamhost, "jid", jid); + xmlnode_set_attrib(streamhost, "host", public_ip); + xmlnode_set_attrib(streamhost, "port", port); + } + g_free(jid); - /* XXX: shouldn't we use the public IP or something? here */ - xmlnode_set_attrib(streamhost, "host", - purple_network_get_my_ip(jsx->js->fd)); - xfer->local_port = purple_network_get_port_from_fd(sock); - port = g_strdup_printf("%hu", xfer->local_port); - xmlnode_set_attrib(streamhost, "port", port); - g_free(port); - + /* The listener for the local proxy */ xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ, jabber_si_xfer_bytestreams_send_connected_cb, xfer); - /* XXX: insert proxies here */ + /* insert proxies here */ + ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL); + if (ft_proxies) { + int i, portnum; + char *tmp; + gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0); + + g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL); + g_list_free(jsx->streamhosts); + jsx->streamhosts = NULL; + + for(i = 0; ft_proxy_list[i]; i++) { + g_strstrip(ft_proxy_list[i]); + if(!(*ft_proxy_list[i])) + continue; + + if((tmp = strchr(ft_proxy_list[i], ':'))) { + portnum = atoi(tmp + 1); + *tmp = '\0'; + } else + portnum = 7777; + + g_snprintf(port, sizeof(port), "%hu", portnum); + + if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL) + continue; + + streamhost = xmlnode_new_child(query, "streamhost"); + xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]); + xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]); + xmlnode_set_attrib(streamhost, "port", port); - /* XXX: callback to find out which streamhost they used, or see if they - * screwed it up */ + sh = g_new0(JabberBytestreamsStreamhost, 1); + sh->jid = g_strdup(ft_proxy_list[i]); + sh->host = g_strdup(ft_proxy_list[i]); + sh->port = portnum; + + jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh); + } + + g_strfreev(ft_proxy_list); + } + + for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) { + sh = tmp->data; + + /* TODO: deal with zeroconf proxies */ + + if (!(sh->jid && sh->host && sh->port > 0)) + continue; + + if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL) + continue; + + streamhost = xmlnode_new_child(query, "streamhost"); + xmlnode_set_attrib(streamhost, "jid", sh->jid); + xmlnode_set_attrib(streamhost, "host", sh->host); + g_snprintf(port, sizeof(port), "%hu", sh->port); + xmlnode_set_attrib(streamhost, "port", port); + + sh2 = g_new0(JabberBytestreamsStreamhost, 1); + sh2->jid = g_strdup(sh->jid); + sh2->host = g_strdup(sh->host); + sh2->zeroconf = g_strdup(sh->zeroconf); + sh2->port = sh->port; + + jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2); + } + + jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer); + jabber_iq_send(iq); } @@ -688,8 +916,7 @@ /* maybe later we'll do hash and date attribs */ feature = xmlnode_new_child(si, "feature"); - xmlnode_set_namespace(feature, - "http://jabber.org/protocol/feature-neg"); + xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = xmlnode_new_child(feature, "x"); xmlnode_set_namespace(x, "jabber:x:data"); xmlnode_set_attrib(x, "type", "form"); @@ -698,8 +925,7 @@ xmlnode_set_attrib(field, "type", "list-single"); option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); - xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", - -1); + xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); /* option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); @@ -729,6 +955,14 @@ if (jsx->iq_id != NULL) jabber_iq_remove_callback_by_id(js, jsx->iq_id); + if (jsx->connect_timeout > 0) + purple_timeout_remove(jsx->connect_timeout); + + if (jsx->streamhosts) { + g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL); + g_list_free(jsx->streamhosts); + } + g_free(jsx->stream_id); g_free(jsx->iq_id); /* XXX: free other stuff */ diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msn/command.c --- a/libpurple/protocols/msn/command.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msn/command.c Sat Dec 01 03:41:37 2007 +0000 @@ -117,6 +117,7 @@ cmd->trId = 0; } + /* khc: Huh! */ /*add payload Length checking*/ msn_set_payload_len(cmd); purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Sat Dec 01 03:41:37 2007 +0000 @@ -1181,14 +1181,27 @@ static void rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - /* TODO: This might be for us too */ - MsnSession *session; + PurpleAccount *account; PurpleConnection *gc; const char *friendly; + char *username; session = cmdproc->session; - gc = session->account->gc; + account = session->account; + username = g_strdup(purple_normalize(account, + purple_account_get_username(account))); + + /* Only set display name if our *own* friendly name changed! */ + if (strcmp(username, purple_normalize(account, cmd->params[2]))) + { + g_free(username); + return; + } + + g_free(username); + + gc = account->gc; friendly = purple_url_decode(cmd->params[3]); purple_connection_set_display_name(gc, friendly); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msnp9/msn.c --- a/libpurple/protocols/msnp9/msn.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msnp9/msn.c Sat Dec 01 03:41:37 2007 +0000 @@ -1920,7 +1920,7 @@ purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n"); g_free(stripped); g_free(url_buffer); - g_free(user_info); + purple_notify_user_info_destroy(user_info); g_free(info_data->name); g_free(info_data); g_free(photo_url_text); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msnp9/nexus.c --- a/libpurple/protocols/msnp9/nexus.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msnp9/nexus.c Sat Dec 01 03:41:37 2007 +0000 @@ -338,7 +338,7 @@ g_strdup(purple_url_encode(purple_account_get_username(session->account))); password = - g_strndup(purple_url_encode(purple_connection_get_password(session->account->gc)), 16); + g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc))); ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msnp9/notification.c --- a/libpurple/protocols/msnp9/notification.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msnp9/notification.c Sat Dec 01 03:41:37 2007 +0000 @@ -235,6 +235,8 @@ /* OK */ const char *friendly = purple_url_decode(cmd->params[3]); + session->passport_info.verified = atoi(cmd->params[4]); + purple_connection_set_display_name(gc, friendly); msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); @@ -780,14 +782,27 @@ static void rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - /* TODO: This might be for us too */ - MsnSession *session; + PurpleAccount *account; PurpleConnection *gc; const char *friendly; + char *username; session = cmdproc->session; - gc = session->account->gc; + account = session->account; + username = g_strdup(purple_normalize(account, + purple_account_get_username(account))); + + /* Only set display name if our *own* friendly name changed! */ + if (strcmp(username, purple_normalize(account, cmd->params[2]))) + { + g_free(username); + return; + } + + g_free(username); + + gc = account->gc; friendly = purple_url_decode(cmd->params[3]); purple_connection_set_display_name(gc, friendly); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msnp9/session.h --- a/libpurple/protocols/msnp9/session.h Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msnp9/session.h Sat Dec 01 03:41:37 2007 +0000 @@ -114,7 +114,7 @@ char *file; char *client_ip; int client_port; - + int verified; } passport_info; }; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/msnp9/user.c --- a/libpurple/protocols/msnp9/user.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/msnp9/user.c Sat Dec 01 03:41:37 2007 +0000 @@ -126,16 +126,23 @@ msn_user_set_friendly_name(MsnUser *user, const char *name) { MsnCmdProc *cmdproc; + MsnSession *session; + const char *encoded; g_return_if_fail(user != NULL); - if (user->friendly_name && strcmp(user->friendly_name, name)) { + encoded = purple_url_encode(name); + session = user->userlist->session; + + if (user->friendly_name && strcmp(user->friendly_name, name) + && (strlen(encoded) < 387) && session->passport_info.verified && + (user->list_op & MSN_LIST_FL_OP)) { /* copy the new name to the server list, but only when new */ /* should we check this more thoroughly? */ - cmdproc = user->userlist->session->notification->cmdproc; + cmdproc = session->notification->cmdproc; msn_cmdproc_send(cmdproc, "REA", "%s %s", user->passport, - purple_url_encode(name)); + encoded); } g_free(user->friendly_name); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/qq/packet_parse.h --- a/libpurple/protocols/qq/packet_parse.h Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/qq/packet_parse.h Sat Dec 01 03:41:37 2007 +0000 @@ -23,7 +23,7 @@ */ #ifndef _QQ_PACKET_PARSE_H_ -#define _QQ_PACKED_PARSE_H_ +#define _QQ_PACKET_PARSE_H_ #include #include diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/protocols/yahoo/yahoo_packet.c --- a/libpurple/protocols/yahoo/yahoo_packet.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.c Sat Dec 01 03:41:37 2007 +0000 @@ -219,13 +219,13 @@ void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data) { - GSList *l = pkt->hash; + GSList *l; int pos = 0; /* This is only called from one place, and the list is * always backwards */ - l = g_slist_reverse(l); + l = pkt->hash = g_slist_reverse(pkt->hash); while (l) { struct yahoo_pair *pair = l->data; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/prpl.c --- a/libpurple/prpl.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/prpl.c Sat Dec 01 03:41:37 2007 +0000 @@ -258,8 +258,8 @@ PurpleStatus *old_status, PurpleStatus *new_status) { g_return_if_fail(account != NULL); - g_return_if_fail(old_status != NULL); g_return_if_fail(new_status != NULL); + g_return_if_fail(!purple_status_is_exclusive(new_status) || old_status != NULL); do_prpl_change_account_status(account, old_status, new_status); diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/tests/Makefile.am --- a/libpurple/tests/Makefile.am Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/tests/Makefile.am Sat Dec 01 03:41:37 2007 +0000 @@ -1,6 +1,9 @@ if HAVE_CHECK TESTS=check_libpurple +clean-local: + -rm -rf libpurple.. + check_PROGRAMS=check_libpurple check_libpurple_SOURCES=\ diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/tests/test_util.c --- a/libpurple/tests/test_util.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/tests/test_util.c Sat Dec 01 03:41:37 2007 +0000 @@ -76,7 +76,7 @@ START_TEST(test_util_str_to_time) { - fail_unless(377185800 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL)); + fail_unless(377182200 == purple_str_to_time("19811214T12:50:00", TRUE, NULL, NULL, NULL)); fail_unless(1175919261 == purple_str_to_time("20070407T04:14:21", TRUE, NULL, NULL, NULL)); } END_TEST diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/util.c --- a/libpurple/util.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/util.c Sat Dec 01 03:41:37 2007 +0000 @@ -753,13 +753,13 @@ struct tm *tm, long *tz_off, const char **rest) { time_t retval = 0; - struct tm *t; + static struct tm t; const char *c = timestamp; int year = 0; long tzoff = PURPLE_NO_TZ_OFF; time(&retval); - t = localtime(&retval); + localtime_r(&retval, &t); /* 4 digit year */ if (sscanf(c, "%04d", &year) && year > 1900) @@ -767,11 +767,11 @@ c += 4; if (*c == '-') c++; - t->tm_year = year - 1900; + t.tm_year = year - 1900; } /* 2 digit month */ - if (!sscanf(c, "%02d", &t->tm_mon)) + if (!sscanf(c, "%02d", &t.tm_mon)) { if (rest != NULL && *c != '\0') *rest = c; @@ -780,10 +780,10 @@ c += 2; if (*c == '-' || *c == '/') c++; - t->tm_mon -= 1; + t.tm_mon -= 1; /* 2 digit day */ - if (!sscanf(c, "%02d", &t->tm_mday)) + if (!sscanf(c, "%02d", &t.tm_mday)) { if (rest != NULL && *c != '\0') *rest = c; @@ -794,13 +794,13 @@ { c++; - if (!sscanf(c, "%04d", &t->tm_year)) + if (!sscanf(c, "%04d", &t.tm_year)) { if (rest != NULL && *c != '\0') *rest = c; return 0; } - t->tm_year -= 1900; + t.tm_year -= 1900; } else if (*c == 'T' || *c == '.') { @@ -808,17 +808,20 @@ /* we have more than a date, keep going */ /* 2 digit hour */ - if ((sscanf(c, "%02d:%02d:%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 8)) || - (sscanf(c, "%02d%02d%02d", &t->tm_hour, &t->tm_min, &t->tm_sec) == 3 && (c = c + 6))) + if ((sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 8)) || + (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3 && (c = c + 6))) { gboolean offset_positive = FALSE; int tzhrs; int tzmins; - t->tm_isdst = -1; - - if (*c == '.' && *(c+1) >= '0' && *(c+1) <= '9') /* dealing with precision we don't care about */ - c += 4; + t.tm_isdst = -1; + + if (*c == '.') { + do { + c++; + } while (*c >= '0' && *c <= '9'); /* dealing with precision we don't care about */ + } if (*c == '+') offset_positive = TRUE; if (((*c == '+' || *c == '-') && (c = c + 1)) && @@ -830,11 +833,23 @@ tzoff *= -1; /* We don't want the C library doing DST calculations * if we know the UTC offset already. */ - t->tm_isdst = 0; + t.tm_isdst = 0; } else if (utc) { - t->tm_isdst = -1; + static struct tm tmptm; + time_t tmp; + tmp = mktime(&t); + /* we care about whether it *was* dst, and the offset, here on this + * date, not whether we are currently observing dst locally *now*. + * This isn't perfect, because we would need to know in advance the + * offset we are trying to work out in advance to be sure this + * works for times around dst transitions but it'll have to do. */ + localtime_r(&tmp, &tmptm); + t.tm_isdst = tmptm.tm_isdst; +#ifdef HAVE_TM_GMTOFF + t.tm_gmtoff = tmptm.tm_gmtoff; +#endif } if (rest != NULL && *c != '\0') @@ -863,7 +878,7 @@ tzoff += sys_tzoff; #else #ifdef HAVE_TM_GMTOFF - tzoff += t->tm_gmtoff; + tzoff += t.tm_gmtoff; #else # ifdef HAVE_TIMEZONE tzset(); /* making sure */ @@ -882,12 +897,12 @@ if (tm != NULL) { - *tm = *t; + *tm = t; tm->tm_isdst = -1; mktime(tm); } - retval = mktime(t); + retval = mktime(&t); if (tzoff != PURPLE_NO_TZ_OFF) retval += tzoff; diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/win32/libc_interface.c --- a/libpurple/win32/libc_interface.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/win32/libc_interface.c Sat Dec 01 03:41:37 2007 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include "config.h" #include "debug.h" #include "libc_internal.h" #if GLIB_CHECK_VERSION(2,6,0) @@ -38,6 +39,26 @@ #define g_stat stat #endif +#ifdef ENABLE_NLS +# include +# include +# define _(String) ((const char *)dgettext(PACKAGE, String)) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# include +# define N_(String) (String) +# ifndef _ +# define _(String) ((const char *)String) +# endif +# define ngettext(Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural)) +# define dngettext(Domain, Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural)) +#endif + + static char errbuf[1024]; /* helpers */ @@ -294,15 +315,25 @@ } /* string.h */ -char* wpurple_strerror( int errornum ) { - if( errornum > WSABASEERR ) { - sprintf( errbuf, "Windows socket error #%d", errornum ); - return errbuf; +char* wpurple_strerror(int errornum) { + if (errornum > WSABASEERR) { + switch(errornum) { + case WSAECONNABORTED: /* 10053 */ + snprintf(errbuf, sizeof(errbuf), _("Connection interrupted by other software on your computer.")); + case WSAECONNRESET: /* 10054 */ + snprintf(errbuf, sizeof(errbuf), _("Remote host closed connection.")); + case WSAETIMEDOUT: /* 10060 */ + snprintf(errbuf, sizeof(errbuf), _("Connection timed out.")); + case WSAECONNREFUSED: /*10061 */ + snprintf(errbuf, sizeof(errbuf), _("Connection refused.")); + default: + snprintf(errbuf, sizeof(errbuf), "Windows socket error #%d", errornum); + } + } else { + const char *tmp = g_strerror(errornum); + snprintf(errbuf, sizeof(errbuf), tmp); } - else - /* Nothing is supposed to modify what is returned by strerror, - but it isn't const for some reason */ - return (char *)g_strerror( errornum ); + return errbuf; } /* unistd.h */ @@ -409,7 +440,7 @@ if (p != 0) { _ftime(&timebuffer); - p->tv_sec = timebuffer.time; /* seconds since 1-1-1970 */ + p->tv_sec = timebuffer.time; /* seconds since 1-1-1970 */ p->tv_usec = timebuffer.millitm*1000; /* microseconds */ } diff -r 14afe5a92fc2 -r b63b2a5c2d05 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Mon Nov 19 23:06:16 2007 +0000 +++ b/libpurple/xmlnode.c Sat Dec 01 03:41:37 2007 +0000 @@ -303,7 +303,7 @@ const char *xmlnode_get_prefix(xmlnode *node) { - g_return_val_if_fail(node != NULL, NULL); + g_return_val_if_fail(node != NULL, NULL); return node->prefix; } @@ -346,6 +346,9 @@ g_free(node->data); g_free(node->xmlns); + if(node->namespace_map) + g_hash_table_destroy(node->namespace_map); + PURPLE_DBUS_UNREGISTER_POINTER(node); g_free(node); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkblist.c Sat Dec 01 03:41:37 2007 +0000 @@ -753,6 +753,11 @@ { case GTK_RESPONSE_OK: do_join_chat(info); + break; + + case 1: + pidgin_roomlist_dialog_show_with_account(info->account); + return; break; } @@ -769,6 +774,8 @@ static void joinchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data) { + PurplePluginProtocolInfo *prpl_info; + PurpleConnection *gc; PidginJoinChatData *data; GList *tmp; const char *text; @@ -789,6 +796,12 @@ } gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive); + + gc = purple_account_get_connection(data->account); + prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; + sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive); } static void @@ -944,6 +957,7 @@ data->window = gtk_dialog_new_with_buttons(_("Join a Chat"), NULL, GTK_DIALOG_NO_SEPARATOR, + _("Room _List"), 1, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, PIDGIN_STOCK_CHAT, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK); @@ -2537,7 +2551,7 @@ * * */ -#define STATUS_SIZE 22 +#define STATUS_SIZE 16 #define TOOLTIP_BORDER 12 #define SMALL_SPACE 6 #define LARGE_SPACE 12 @@ -3451,8 +3465,8 @@ struct _pidgin_blist_node *gtkbuddynode = NULL; PurpleBuddy *buddy = NULL; PurpleChat *chat = NULL; - GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_SMALL : - PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); + GtkIconSize icon_size = gtk_icon_size_from_name((size == PIDGIN_STATUS_ICON_LARGE) ? PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL : + PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { if(!gtknode->contact_expanded) { @@ -4230,7 +4244,7 @@ { PidginBuddyList *gtkblist = (PidginBuddyList *)user_data; int errors = 0; - GList *list; + GList *list = NULL; PidginBuddyListPrivate *priv; priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); @@ -4243,8 +4257,7 @@ errors = g_list_length(GTK_NOTEBOOK(priv->error_scrollbook->notebook)->children); #endif } - if ((list = purple_accounts_get_all_active()) != NULL || errors || - (list = gtk_container_get_children(GTK_CONTAINER(priv->error_scrollbook)))) { + if ((list = purple_accounts_get_all_active()) != NULL || errors) { gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1); g_list_free(list); } else @@ -4441,8 +4454,12 @@ PurpleAccount *account) { GList *l = NULL; - GList *children = gtk_container_get_children(container); + GList *children = NULL; GtkWidget *ret = NULL; + /* XXX: Workaround for the currently incomplete implementation of PidginScrollBook */ + if (PIDGIN_IS_SCROLL_BOOK(container)) + container = GTK_CONTAINER(PIDGIN_SCROLL_BOOK(container)->notebook); + children = gtk_container_get_children(container); l = g_list_find_custom(children, account, (GCompareFunc) find_account_widget); if (l) ret = GTK_WIDGET(l->data); @@ -4456,7 +4473,6 @@ { GtkWidget *widget = find_child_widget_by_account(container, account); if(widget) { - gtk_container_remove(container, widget); gtk_widget_destroy(widget); } } @@ -4478,16 +4494,11 @@ } static void -generic_error_ignore_cb(PurpleAccount *account) -{ - purple_account_clear_current_error(account); -} - -static void generic_error_destroy_cb(GtkObject *dialog, PurpleAccount *account) { g_hash_table_remove(gtkblist->connection_errors, account); + purple_account_clear_current_error(account); } #define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl" @@ -4508,7 +4519,7 @@ gboolean enabled = purple_account_get_enabled(account, purple_core_get_ui()); char *primary; - + if (enabled) primary = g_strdup_printf(_("%s disconnected"), username); else @@ -4520,7 +4531,6 @@ (enabled ? PURPLE_CALLBACK(purple_account_connect) : PURPLE_CALLBACK(generic_error_enable_cb)), _("Modify Account"), PURPLE_CALLBACK(generic_error_modify_cb), - _("Ignore"), PURPLE_CALLBACK(generic_error_ignore_cb), NULL); g_free(primary); @@ -4605,14 +4615,6 @@ } static void -ignore_elsewhere_accounts(PidginMiniDialog *mini_dialog, - GtkButton *button, - gpointer unused) -{ - elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error); -} - -static void ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist) { PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); @@ -4620,16 +4622,13 @@ if(priv->signed_on_elsewhere) return; - + mini_dialog = priv->signed_on_elsewhere = - pidgin_mini_dialog_new(NULL, NULL, PIDGIN_STOCK_DISCONNECT); + pidgin_mini_dialog_new(_("Welcome back!"), NULL, PIDGIN_STOCK_DISCONNECT); pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"), reconnect_elsewhere_accounts, NULL); - pidgin_mini_dialog_add_button(mini_dialog, _("Ignore"), - ignore_elsewhere_accounts, NULL); - add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog)); /* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */ @@ -4649,13 +4648,17 @@ return; accounts = pidgin_mini_dialog_get_num_children(mini_dialog); + if (accounts == 0) { + gtk_widget_destroy(GTK_WIDGET(mini_dialog)); + return; + } title = g_strdup_printf( - ngettext("%d account was disabled because you signed on from another location.", - "%d accounts were disabled because you signed on from another location.", + ngettext("%d account was disabled because you signed on from another location:", + "%d accounts were disabled because you signed on from another location:", accounts), accounts); - pidgin_mini_dialog_set_title(mini_dialog, title); + pidgin_mini_dialog_set_description(mini_dialog, title); g_free(title); } @@ -5737,7 +5740,7 @@ return; status = pidgin_blist_get_status_icon((PurpleBlistNode*)buddy, - PIDGIN_STATUS_ICON_SMALL); + biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); /* Speed it up if we don't want buddy icons. */ if(biglist) @@ -5821,6 +5824,7 @@ PurpleBlistNode *cnode; PurpleContact *contact; PurpleBuddy *buddy; + gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); struct _pidgin_blist_node *gtknode; if (editing_blist) @@ -5856,7 +5860,7 @@ char *mark; status = pidgin_blist_get_status_icon(cnode, - PIDGIN_STATUS_ICON_SMALL); + biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); gtk_tree_store_set(gtkblist->treemodel, &iter, @@ -5935,6 +5939,7 @@ GdkPixbuf *status, *avatar, *emblem, *prpl_icon; char *mark; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); + gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); PidginBlistNode *ui; PurpleConversation *conv; gboolean hidden; @@ -5947,7 +5952,7 @@ hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)); status = pidgin_blist_get_status_icon(node, - PIDGIN_STATUS_ICON_SMALL); + biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); emblem = pidgin_blist_get_emblem(node); /* Speed it up if we don't want buddy icons. */ @@ -6401,6 +6406,10 @@ { add_chat_cb(NULL, data); } + else if (resp == 1) + { + pidgin_roomlist_dialog_show_with_account(data->account); + } else { gtk_widget_destroy(data->window); @@ -6417,6 +6426,8 @@ static void addchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data) { + PurplePluginProtocolInfo *prpl_info; + PurpleConnection *gc; PidginAddChatData *data; GList *tmp; const char *text; @@ -6437,6 +6448,12 @@ } gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive); + + gc = purple_account_get_connection(data->account); + prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; + sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive); } static void @@ -6602,6 +6619,7 @@ data->window = gtk_dialog_new_with_buttons(_("Add Chat"), gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR, + _("Room _List"), 1, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_ADD, GTK_RESPONSE_OK, NULL); @@ -6685,8 +6703,8 @@ pidgin_set_accessible_label (data->group_combo, label); gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); - data->autojoin = gtk_check_button_new_with_mnemonic(_("Autojoin when account becomes online.")); - data->persistent = gtk_check_button_new_with_mnemonic(_("Hide chat when the window is closed.")); + data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online.")); + data->persistent = gtk_check_button_new_with_mnemonic(_("_Hide chat when the window is closed.")); gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0); diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkconv.c Sat Dec 01 03:41:37 2007 +0000 @@ -125,6 +125,7 @@ static GtkWidget *invite_dialog = NULL; static GtkWidget *warn_close_dialog = NULL; +static PidginWindow *hidden_convwin = NULL; static GList *window_list = NULL; /* Lists of status icons at all available sizes for use as window icons */ @@ -235,10 +236,7 @@ switch (purple_conversation_get_type(conv)) { case PURPLE_CONV_TYPE_IM: { - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately")) - close_this_sucker(gtkconv); - else - hide_conv(gtkconv, TRUE); + hide_conv(gtkconv, TRUE); break; } case PURPLE_CONV_TYPE_CHAT: @@ -266,43 +264,6 @@ return FALSE; } -static gboolean -size_allocate_cb(GtkWidget *w, GtkAllocation *allocation, PidginConversation *gtkconv) -{ - PurpleConversation *conv = gtkconv->active_conv; - - if (!GTK_WIDGET_VISIBLE(w)) - return FALSE; - - if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - return FALSE; - if (gtkconv->auto_resize) { - 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 - * size of the last resized new window. */ - /* I think that the above justification is not the majority, and that the new tab resizing should - * negate it anyway. --luke */ - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) - { - 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->lower_hbox) - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height", allocation->height); - } - return FALSE; -} - static void default_formatize(PidginConversation *c) { @@ -452,18 +413,6 @@ gtkconv->send_history = g_list_prepend(first, NULL); } -static void -reset_default_size(PidginConversation *gtkconv) -{ - PurpleConversation *conv = gtkconv->active_conv; - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/entry_height")); - else - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/im/entry_height")); -} - static gboolean check_for_and_do_command(PurpleConversation *conv) { @@ -573,7 +522,6 @@ if (check_for_and_do_command(conv)) { if (gtkconv->entry_growing) { - reset_default_size(gtkconv); gtkconv->entry_growing = FALSE; } gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); @@ -633,7 +581,6 @@ gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); if (gtkconv->entry_growing) { - reset_default_size(gtkconv); gtkconv->entry_growing = FALSE; } gtkconv_set_unseen(gtkconv, PIDGIN_UNSEEN_NONE); @@ -1378,7 +1325,13 @@ timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv); purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer)); } +#if 0 + /* I will miss you */ purple_conversation_set_ui_ops(conv, NULL); +#else + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); +#endif } } @@ -2819,6 +2772,9 @@ if (gtkconv == NULL) { pidgin_conv_attach_to_conversation(conv); gtkconv = PIDGIN_CONVERSATION(conv); + } else if (gtkconv->win == hidden_convwin) { + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); } pidgin_conv_switch_active_conversation(conv); @@ -2851,20 +2807,15 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (gtkconv != NULL && gtkconv->active_conv != conv) + if(gtkconv == NULL || gtkconv->active_conv != conv) continue; - if (gtkconv == NULL) { - if (!purple_conversation_get_data(conv, "unseen-count") || - !purple_conversation_get_data(conv, "unseen-state") || - GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-state"))unseen_state >= min_state + && (!hidden_only || + (hidden_only && gtkconv->win == hidden_convwin))) { + r = g_list_prepend(r, conv); c++; - } else { - if (gtkconv->unseen_state >= min_state && !hidden_only) { - r = g_list_prepend(r, conv); - c++; - } } } @@ -2907,8 +2858,8 @@ GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); GtkWidget *item; gchar *text = g_strdup_printf("%s (%d)", - gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv), - gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))); + gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), + gtkconv->unseen_count); gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); g_object_unref(pbuf); @@ -3169,7 +3120,7 @@ PurpleConversation *conv; GtkWidget *item; - if (win->window == NULL) + if (win->window == NULL || win == hidden_convwin) return; gtkconv = pidgin_conv_window_get_active_gtkconv(win); @@ -4369,7 +4320,7 @@ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1); } -static void resize_imhtml_cb(PidginConversation *gtkconv) +static gboolean resize_imhtml_cb(PidginConversation *gtkconv) { GtkTextBuffer *buffer; GtkTextIter iter; @@ -4406,18 +4357,12 @@ diff = height - gtkconv->entry->allocation.height; - if (diff > 0) { - gtk_widget_size_request(gtkconv->lower_hbox, &sr); - gtkconv->entry_growing = TRUE; - - /* uncomment this to auto resize even after the user manually - resizes - gtk_paned_set_position(GTK_PANED(gtkconv->lower_hbox->parent->parent), - -1); - */ - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, - diff + gtkconv->lower_hbox->allocation.height); - } + gtk_widget_size_request(gtkconv->lower_hbox, &sr); + gtkconv->entry_growing = TRUE; + + gtk_widget_set_size_request(gtkconv->lower_hbox, -1, + diff + gtkconv->lower_hbox->allocation.height); + return FALSE; } static void @@ -4687,19 +4632,15 @@ static GtkWidget * setup_common_pane(PidginConversation *gtkconv) { - GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box; + GtkWidget *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 */ @@ -4794,30 +4735,18 @@ 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(paned), vbox, FALSE, TRUE); - gtk_widget_show(vbox); - gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0); gtk_widget_show(gtkconv->lower_hbox); - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox, TRUE, TRUE, 0); - gtk_widget_show(vbox); - /* Setup the toolbar, entry widget and all signals */ frame = pidgin_create_imhtml(TRUE, >kconv->entry, >kconv->toolbar, NULL); - gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); 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, - 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); @@ -4827,8 +4756,6 @@ 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); gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); @@ -4851,7 +4778,7 @@ default_formatize(gtkconv); g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear", G_CALLBACK(clear_formatting_cb), gtkconv); - return paned; + return vbox; } static void @@ -5025,9 +4952,6 @@ GtkWidget *tab_cont; PurpleBlistNode *convnode; - if (hidden) - return; - if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) { conv->ui_data = gtkconv; if (!g_list_find(gtkconv->convs, conv)) @@ -5111,6 +5035,7 @@ gtk_widget_show(gtkconv->toolbar); else gtk_widget_hide(gtkconv->toolbar); + g_idle_add((GSourceFunc)resize_imhtml_cb, gtkconv); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons")) gtk_widget_show(gtkconv->infopane_hbox); @@ -5126,7 +5051,10 @@ G_CALLBACK(gtk_widget_grab_focus), gtkconv->entry); - pidgin_conv_placement_place(gtkconv); + if (hidden) + pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); + else + pidgin_conv_placement_place(gtkconv); if (nick_colors == NULL) { nbr_nick_colors = NUM_NICK_COLORS; @@ -5134,13 +5062,11 @@ } } -#if 0 static void pidgin_conv_new_hidden(PurpleConversation *conv) { private_gtkconv_new(conv, TRUE); } -#endif void pidgin_conv_new(PurpleConversation *conv) @@ -5156,24 +5082,30 @@ PurpleConversation *conv, PurpleMessageFlags flags) { PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops(); + gboolean hide = FALSE; /* create hidden conv if hide_new pref is always */ - /* or if hide_new pref is away and account is away */ - if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) || - (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && - !purple_status_is_available(purple_account_get_active_status(account)))) { - if (!conv) { - ui_ops->create_conversation = NULL; - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - purple_conversation_set_ui_ops(conv, NULL); - ui_ops->create_conversation = pidgin_conv_new; - } else { - /* TODO: update the unseen_state data on the conv here */ - } - } else { - /* new message for an IM */ - if (conv && conv->type == PURPLE_CONV_TYPE_IM) - pidgin_conv_attach_to_conversation(conv); + if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) + hide = TRUE; + + /* create hidden conv if hide_new pref is away and account is away */ + if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && + !purple_status_is_available(purple_account_get_active_status(account))) + hide = TRUE; + + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv) && !hide) { + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv->win == hidden_convwin) { + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_placement_place(gtkconv); + } + return; + } + + if (hide) { + ui_ops->create_conversation = pidgin_conv_new_hidden; + purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + ui_ops->create_conversation = pidgin_conv_new; } } @@ -5182,9 +5114,6 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (!gtkconv) - return; - gtkconv->convs = g_list_remove(gtkconv->convs, conv); /* Don't destroy ourselves until all our convos are gone */ if (gtkconv->convs) { @@ -6526,7 +6455,6 @@ AtkObject *accessibility_obj; /* I think this is a little longer than it needs to be but I'm lazy. */ char *style; - gboolean bold = FALSE; if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) im = PURPLE_CONV_IM(conv); @@ -6561,7 +6489,7 @@ CONV_TEXT_COLUMN, markup, -1); /* XXX seanegan Why do I have to do this? */ gtk_widget_queue_draw(gtkconv->infopane); - + if (title != markup) g_free(markup); @@ -6572,47 +6500,43 @@ if (im != NULL && purple_conv_im_get_typing_state(im) == PURPLE_TYPING) { atk_object_set_description(accessibility_obj, _("Typing")); - style = "color=\"#4e9a06\""; + style = "tab-label-typing"; } else if (im != NULL && purple_conv_im_get_typing_state(im) == PURPLE_TYPED) { atk_object_set_description(accessibility_obj, _("Stopped Typing")); - style = "color=\"#c4a000\""; + style = "tab-label-typed"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_NICK) { atk_object_set_description(accessibility_obj, _("Nick Said")); - style = "color=\"#204a87\""; + style = "tab-label-attention"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT) { atk_object_set_description(accessibility_obj, _("Unread Messages")); if (gtkconv->active_conv->type == PURPLE_CONV_TYPE_CHAT) style = "color=\"#cc0000\""; else - style = "color=\"#204a87\""; + style = "tab-label-attention"; } else if (gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) { atk_object_set_description(accessibility_obj, _("New Event")); - style = "color=\"#888a85\""; + style = "tab-label-event"; } else { style = NULL; } + gtk_widget_set_name(gtkconv->tab_label, style); + gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title); + gtk_widget_set_state(gtkconv->tab_label, GTK_STATE_ACTIVE); + if (gtkconv->unseen_state == PIDGIN_UNSEEN_TEXT || gtkconv->unseen_state == PIDGIN_UNSEEN_NICK || - gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) - bold = TRUE; - - if (style || bold) - { - char *html_title,*label; - - html_title = g_markup_escape_text(title, -1); - label = g_strdup_printf("%s", - style ? style : "", - bold ? "weight=\"bold\"" : "", - html_title); - g_free(html_title); - gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label); - g_free(label); - } - else - gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title); + gtkconv->unseen_state == PIDGIN_UNSEEN_EVENT) { + PangoAttrList *list = pango_attr_list_new(); + PangoAttribute *attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert(list, attr); + gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), list); + pango_attr_list_unref(list); + } else + gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), NULL); if (pidgin_conv_window_is_active_conversation(conv)) update_typing_icon(gtkconv); @@ -7121,6 +7045,8 @@ gtk_widget_show(gtkconv->toolbar); else gtk_widget_hide(gtkconv->toolbar); + + g_idle_add((GSourceFunc)resize_imhtml_cb,gtkconv); } } @@ -7196,7 +7122,6 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus, PurpleStatus *newstatus) { -#if 0 GList *l; PurpleConversation *conv = NULL; PidginConversation *gtkconv; @@ -7206,7 +7131,27 @@ if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus)) return; -#endif + + while ((l = hidden_convwin->gtkconvs) != NULL) + { + gtkconv = l->data; + + conv = gtkconv->active_conv; + + while(l && !purple_status_is_available( + purple_account_get_active_status( + purple_conversation_get_account(conv)))) + l = l->next; + if (!l) + break; + + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); + + /* TODO: do we need to do anything for any other conversations that are in the same gtkconv here? + * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/ + purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); + } } static void @@ -7214,25 +7159,32 @@ gconstpointer value, gpointer data) { GList *l; + PurpleConversation *conv = NULL; + PidginConversation *gtkconv; gboolean when_away = FALSE; + if(!hidden_convwin) + return; + if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0) return; if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0) when_away = TRUE; - for (l = purple_get_conversations(); l; l = l->next) + while ((l = hidden_convwin->gtkconvs) != NULL) { - PurpleConversation *conv = l->data; - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if (gtkconv) - continue; + gtkconv = l->data; + + conv = gtkconv->active_conv; + if(when_away && !purple_status_is_available( purple_account_get_active_status( purple_conversation_get_account(conv)))) continue; - pidgin_conv_attach_to_conversation(conv); + + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); } } @@ -7513,8 +7465,14 @@ GList *list; PidginConversation *gtkconv; - if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) - return FALSE; + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { + gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv->win != hidden_convwin) + return FALSE; + pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); + pidgin_conv_placement_place(gtkconv); + return TRUE; + } pidgin_conv_attach(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -7621,7 +7579,6 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE); purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never"); - purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", TRUE); #ifdef _WIN32 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE); @@ -7785,6 +7742,9 @@ purple_conversations_set_ui_ops(&conversation_ui_ops); + hidden_convwin = pidgin_conv_window_new(); + window_list = g_list_remove(window_list, hidden_convwin); + purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", handle, PURPLE_CALLBACK(account_status_changed_cb), NULL); @@ -7824,6 +7784,40 @@ PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle, PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); + + { + /* Set default tab colors */ + GString *str = g_string_new(NULL); + GtkSettings *settings = gtk_settings_get_default(); + struct { + const char *stylename; + const char *labelname; + const char *color; + } styles[] = { + {"pidgin_tab_label_typing_default", "tab-label-typing", "#4e9a06"}, + {"pidgin_tab_label_typed_default", "tab-label-typed", "#c4a000"}, + {"pidgin_tab_label_attention_default", "tab-label-attention", "#204a87"}, + {"pidgin_tab_label_event_default", "tab-label-event", "#888a85"}, + {NULL, NULL, NULL} + }; + int iter; + for (iter = 0; styles[iter].stylename; iter++) { + if (!gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE)) + /* Apparently both ACTIVE and NORMAL are required */ + g_string_append_printf(str, "style \"%s\" {\n" + "fg[ACTIVE] = \"%s\"\n" + "}\n" + "widget \"*%s\" style \"%s\"\n", + styles[iter].stylename, + styles[iter].color, + styles[iter].labelname, styles[iter].stylename); + } + gtk_rc_parse_string(str->str); + g_string_free(str, TRUE); +#if GTK_CHECK_VERSION(2,4,0) + gtk_rc_reset_styles(settings); +#endif + } } void @@ -9126,6 +9120,7 @@ /* Tab label. */ gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv)); + gtk_widget_set_name(gtkconv->tab_label, "tab-label"); gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtkconv->menu_label = gtk_label_new(tmp_lab); @@ -9285,7 +9280,7 @@ if (win->gtkconvs && win->gtkconvs->next == NULL) pidgin_conv_tab_pack(win, win->gtkconvs->data); - if (!win->gtkconvs) + if (!win->gtkconvs && win != hidden_convwin) pidgin_conv_window_destroy(win); } @@ -9824,7 +9819,9 @@ gboolean pidgin_conv_is_hidden(PidginConversation *gtkconv) { - return (gtkconv == NULL); + g_return_val_if_fail(gtkconv != NULL, FALSE); + + return (gtkconv->win == hidden_convwin); } @@ -9925,5 +9922,3 @@ return colors; } - - diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkdialogs.c Sat Dec 01 03:41:37 2007 +0000 @@ -349,6 +349,7 @@ AtkObject *obj; char* filename, *tmp; GdkPixbuf *pixbuf; + PidginBuddyList *buddylist; if (about != NULL) { gtk_window_present(GTK_WINDOW(about)); @@ -724,6 +725,11 @@ gtk_widget_grab_default(button); /* Let's give'em something to talk about -- woah woah woah */ + buddylist = pidgin_blist_get_default_gtk_blist(); + if (buddylist) + gtk_window_set_transient_for(GTK_WINDOW(about), + GTK_WINDOW(buddylist->window)); + gtk_widget_show_all(about); gtk_window_present(GTK_WINDOW(about)); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkimhtml.c Sat Dec 01 03:41:37 2007 +0000 @@ -31,6 +31,7 @@ #include "internal.h" #include "pidgin.h" +#include "pidginstock.h" #include "debug.h" #include "util.h" @@ -811,6 +812,7 @@ static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data) { GtkWidget *menuitem; + GtkWidget *mi, *img; menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text")); gtk_widget_show(menuitem); @@ -836,6 +838,28 @@ gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 5); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(clear_formatting_cb), imhtml); + + mi = gtk_menu_item_new(); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Smile!")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Insert")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Font")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); } static char * @@ -1097,6 +1121,38 @@ g_free(text); } + +static void smart_backspace_cb(GtkIMHtml *imhtml, gpointer blah) +{ + GtkTextIter iter; + GtkTextChildAnchor* anchor; + char * text; + gint offset; + + if (!imhtml->editable) + return; + + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer)); + + /* Get the character before the insertion point */ + offset = gtk_text_iter_get_offset(&iter); + if (offset <= 0) + return; + + gtk_text_iter_backward_char(&iter); + anchor = gtk_text_iter_get_child_anchor(&iter); + + if (!anchor) + return; /* No smiley here */ + + text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext"); + if (!text) + return; + + /* ok, then we need to insert the image buffer text before the anchor */ + gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1); +} + static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah) { #ifdef _WIN32 @@ -4052,8 +4108,11 @@ imhtml->format_functions = GTK_IMHTML_ALL; if (editable) + { g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set", - G_CALLBACK(mark_set_cb), imhtml); + G_CALLBACK(mark_set_cb), imhtml); + g_signal_connect(G_OBJECT(imhtml), "backspace", G_CALLBACK(smart_backspace_cb), NULL); + } } void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo) @@ -4222,6 +4281,33 @@ g_object_unref(object); } +static void populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer nul) +{ + GtkWidget *mi, *img; + + mi = gtk_menu_item_new(); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Font")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Insert")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); + + img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU); + mi = gtk_image_menu_item_new_with_label(_("_Smile!")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img); + gtk_widget_show(mi); + gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi); +} + static void imhtml_toggle_bold(GtkIMHtml *imhtml) { GtkTextIter start, end; diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Sat Dec 01 03:41:37 2007 +0000 @@ -777,6 +777,8 @@ /* show everything */ gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!")); gtk_widget_show_all(dialog); + gtk_window_set_transient_for(GTK_WINDOW(dialog), + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar)))); #ifdef _WIN32 winpidgin_ensure_onscreen(dialog); #endif diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkmain.c Sat Dec 01 03:41:37 2007 +0000 @@ -776,6 +776,7 @@ } if (opt_si && !purple_core_ensure_single_instance()) { + purple_debug_info("main", "exiting because another libpurple client is already running\n"); purple_core_quit(); #ifdef HAVE_SIGNAL_H g_free(segfault_message); diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkprefs.c Sat Dec 01 03:41:37 2007 +0000 @@ -995,8 +995,6 @@ pidgin_prefs_checkbox(_("Show _formatting on incoming messages"), PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox); - pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"), - PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox); iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"), PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox); @@ -1213,6 +1211,9 @@ vbox = pidgin_make_frame (ret, _("Ports")); sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + pidgin_prefs_checkbox(_("_Enable automatic router port forwarding"), + "/purple/network/map_ports", vbox); + ports_checkbox = pidgin_prefs_checkbox(_("_Manually specify range of ports to listen on"), "/purple/network/ports_range_use", vbox); @@ -2306,6 +2307,7 @@ purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/close_immediately"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends"); purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes"); diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/gtkscrollbook.c --- a/pidgin/gtkscrollbook.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/gtkscrollbook.c Sat Dec 01 03:41:37 2007 +0000 @@ -1,5 +1,5 @@ /* - * @file gtkscrollbook.c GTK+ Scrolling notebook widget + * @file gtkscrollbook.c GTK+ Scrolling notebook widget * @ingroup pidgin */ @@ -29,9 +29,9 @@ static void pidgin_scroll_book_init (PidginScrollBook *scroll_book); static void pidgin_scroll_book_class_init (PidginScrollBookClass *klass); -static void pidgin_scroll_book_forall (GtkContainer *c, +static void pidgin_scroll_book_forall (GtkContainer *c, gboolean include_internals, - GtkCallback callback, + GtkCallback callback, gpointer user_data); GType @@ -83,8 +83,8 @@ count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook)); #else count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children); -#endif - +#endif + if (index + 1 < count) gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1); } @@ -93,24 +93,30 @@ refresh_scroll_box(PidginScrollBook *scroll_book, int index, int count) { char *label; + gtk_widget_show_all(GTK_WIDGET(scroll_book)); - if (count <= 1) - gtk_widget_hide(GTK_WIDGET(scroll_book->hbox)); - else - gtk_widget_show_all(GTK_WIDGET(scroll_book->hbox)); - - + if (count < 1) + gtk_widget_hide_all(scroll_book->hbox); + else { + gtk_widget_show_all(scroll_book->hbox); + if (count == 1) { + gtk_widget_hide(scroll_book->label); + gtk_widget_hide(scroll_book->left_arrow); + gtk_widget_hide(scroll_book->right_arrow); + } + } + label = g_strdup_printf("(%d/%d)", index+1, count); gtk_label_set_markup(GTK_LABEL(scroll_book->label), label); - g_free(label); + g_free(label); if (index == 0) gtk_widget_set_sensitive(scroll_book->left_arrow, FALSE); else gtk_widget_set_sensitive(scroll_book->left_arrow, TRUE); - - if (index +1== count) + + if (index + 1 == count) gtk_widget_set_sensitive(scroll_book->right_arrow, FALSE); else gtk_widget_set_sensitive(scroll_book->right_arrow, TRUE); @@ -128,7 +134,12 @@ count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children); #endif refresh_scroll_box(scroll_book, index, count); - +} + +static void +scroll_close_cb(PidginScrollBook *scroll_book) +{ + gtk_widget_destroy(gtk_notebook_get_nth_page(GTK_NOTEBOOK(scroll_book->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook)))); } static void @@ -146,17 +157,15 @@ static void pidgin_scroll_book_add(GtkContainer *container, GtkWidget *widget) { - GList *children; PidginScrollBook *scroll_book; g_return_if_fail(GTK_IS_WIDGET (widget)); g_return_if_fail (widget->parent == NULL); scroll_book = PIDGIN_SCROLL_BOOK(container); - children = scroll_book->children; - children = g_list_append(children, widget); + scroll_book->children = g_list_append(scroll_book->children, widget); gtk_widget_show(widget); - gtk_notebook_append_page(GTK_NOTEBOOK(PIDGIN_SCROLL_BOOK(container)->notebook), widget, NULL); + gtk_notebook_append_page(GTK_NOTEBOOK(scroll_book->notebook), widget, NULL); page_count_change_cb(PIDGIN_SCROLL_BOOK(container)); } @@ -164,23 +173,12 @@ pidgin_scroll_book_remove(GtkContainer *container, GtkWidget *widget) { int page; - GList *children; - GtkWidget *child; PidginScrollBook *scroll_book; g_return_if_fail(GTK_IS_WIDGET(widget)); scroll_book = PIDGIN_SCROLL_BOOK(container); - children = scroll_book->children; - - while (children) { - child = children->data; - if (child == widget) { - gtk_widget_unparent (widget); - children = g_list_remove_link (scroll_book->children, children); - g_list_free(children); - break; - } - } + scroll_book->children = g_list_remove(scroll_book->children, widget); + /* gtk_widget_unparent(widget); */ page = gtk_notebook_page_num(GTK_NOTEBOOK(PIDGIN_SCROLL_BOOK(container)->notebook), widget); if (page >= 0) { @@ -194,7 +192,9 @@ GtkCallback callback, gpointer callback_data) { +#if 0 GList *children; +#endif PidginScrollBook *scroll_book; g_return_if_fail(GTK_IS_CONTAINER(container)); @@ -206,6 +206,7 @@ (*callback)(scroll_book->notebook, callback_data); } +#if 0 children = scroll_book->children; while (children) { @@ -214,6 +215,7 @@ children = children->next; (*callback)(child, callback_data); } +#endif } static void @@ -223,26 +225,66 @@ container_class->add = pidgin_scroll_book_add; container_class->remove = pidgin_scroll_book_remove; - container_class->forall = pidgin_scroll_book_forall; - + container_class->forall = pidgin_scroll_book_forall; +} + +static gboolean +close_button_left_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label) +{ + static GdkCursor *ptr = NULL; + if (ptr == NULL) { + ptr = gdk_cursor_new(GDK_LEFT_PTR); + } + + gtk_label_set_markup(label, "×"); + gdk_window_set_cursor(event->window, ptr); + return FALSE; +} + +static gboolean +close_button_entered_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label) +{ + static GdkCursor *hand = NULL; + if (hand == NULL) { + hand = gdk_cursor_new(GDK_HAND2); + } + + gtk_label_set_markup(label, "×"); + gdk_window_set_cursor(event->window, hand); + return FALSE; } static void pidgin_scroll_book_init (PidginScrollBook *scroll_book) { GtkWidget *eb; + GtkWidget *close_button; scroll_book->hbox = gtk_hbox_new(FALSE, 0); + /* Close */ + eb = gtk_event_box_new(); + gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0); + gtk_event_box_set_visible_window(GTK_EVENT_BOX(eb), FALSE); + gtk_widget_set_events(eb, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + close_button = gtk_label_new("×"); + g_signal_connect(G_OBJECT(eb), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_button); + g_signal_connect(G_OBJECT(eb), "leave-notify-event", G_CALLBACK(close_button_left_cb), close_button); + gtk_container_add(GTK_CONTAINER(eb), close_button); + g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_close_cb), scroll_book); + + /* Right arrow */ eb = gtk_event_box_new(); gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0); scroll_book->right_arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(eb), scroll_book->right_arrow); g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_right_cb), scroll_book); + /* Count */ scroll_book->label = gtk_label_new(NULL); gtk_box_pack_end(GTK_BOX(scroll_book->hbox), scroll_book->label, FALSE, FALSE, 0); + /* Left arrow */ eb = gtk_event_box_new(); gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0); scroll_book->left_arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE); @@ -250,20 +292,18 @@ g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_left_cb), scroll_book); gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->hbox, FALSE, FALSE, 0); - + scroll_book->notebook = gtk_notebook_new(); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(scroll_book->notebook), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(scroll_book->notebook), FALSE); - + gtk_box_pack_start(GTK_BOX(scroll_book), scroll_book->notebook, TRUE, TRUE, 0); - + g_signal_connect_swapped(G_OBJECT(scroll_book->notebook), "remove", G_CALLBACK(page_count_change_cb), scroll_book); g_signal_connect(G_OBJECT(scroll_book->notebook), "switch-page", G_CALLBACK(switch_page_cb), scroll_book); gtk_widget_show_all(scroll_book->notebook); } - - GtkWidget * pidgin_scroll_book_new() { diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/pidginstock.c --- a/pidgin/pidginstock.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/pidginstock.c Sat Dec 01 03:41:37 2007 +0000 @@ -347,7 +347,7 @@ size_t i; GtkWidget *win; GtkIconSize microscopic, extra_small, small, medium, large, huge; - + if (stock_initted) return; @@ -372,7 +372,7 @@ { /* GTK+ Stock icon */ iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win), - stock_icons[i].filename); + stock_icons[i].filename); } else { @@ -386,11 +386,11 @@ gtk_icon_source_set_direction_wildcarded(source, TRUE); gtk_icon_source_set_size_wildcarded(source, TRUE); gtk_icon_source_set_state_wildcarded(source, TRUE); - + iconset = gtk_icon_set_new(); gtk_icon_set_add_source(iconset, source); - + gtk_icon_source_free(source); g_free(filename); } @@ -401,7 +401,7 @@ } /* register custom icon sizes */ - + microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11); extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16); small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22); @@ -414,61 +414,41 @@ GtkIconSet *iconset; iconset = gtk_icon_set_new(); - if (sized_stock_icons[i].microscopic) - add_sized_icon(iconset, microscopic, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "11", sized_stock_icons[i].filename); - if (sized_stock_icons[i].extra_small) - add_sized_icon(iconset, extra_small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "16", sized_stock_icons[i].filename); - if (sized_stock_icons[i].small) - add_sized_icon(iconset, small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "22", sized_stock_icons[i].filename); - if (sized_stock_icons[i].medium) - add_sized_icon(iconset, medium, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "32", sized_stock_icons[i].filename); - if (sized_stock_icons[i].large) - add_sized_icon(iconset, large, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "48", sized_stock_icons[i].filename); - if (sized_stock_icons[i].huge) - add_sized_icon(iconset, huge, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "64", sized_stock_icons[i].filename); + +#define ADD_SIZED_ICON(name, size) do { \ + if (sized_stock_icons[i].name) \ + add_sized_icon(iconset, name, \ + sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ + size, sized_stock_icons[i].filename); \ + } while (0) + ADD_SIZED_ICON(microscopic, "11"); + ADD_SIZED_ICON(extra_small, "16"); + ADD_SIZED_ICON(small, "22"); + ADD_SIZED_ICON(medium, "32"); + ADD_SIZED_ICON(large, "48"); + ADD_SIZED_ICON(huge, "64"); +#undef ADD_SIZED_ICON gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset); gtk_icon_set_unref(iconset); if (sized_stock_icons[i].translucent_name) { iconset = gtk_icon_set_new(); - if (sized_stock_icons[i].microscopic) - add_translucent_sized_icon(iconset, microscopic, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "11", sized_stock_icons[i].filename); - if (sized_stock_icons[i].extra_small) - add_translucent_sized_icon(iconset, extra_small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "16", sized_stock_icons[i].filename); - if (sized_stock_icons[i].small) - add_translucent_sized_icon(iconset, small, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "22", sized_stock_icons[i].filename); - if (sized_stock_icons[i].medium) - add_translucent_sized_icon(iconset, medium, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "32", sized_stock_icons[i].filename); - if (sized_stock_icons[i].large) - add_translucent_sized_icon(iconset, large, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "48", sized_stock_icons[i].filename); - if (sized_stock_icons[i].huge) - add_translucent_sized_icon(iconset, huge, - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, - "64", sized_stock_icons[i].filename); - + +#define ADD_TRANS_ICON(name, size) do { \ + if (sized_stock_icons[i].name) \ + add_translucent_sized_icon(iconset, name, \ + sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ + size, sized_stock_icons[i].filename); \ + } while (0) + ADD_TRANS_ICON(microscopic, "11"); + ADD_TRANS_ICON(extra_small, "16"); + ADD_TRANS_ICON(small, "22"); + ADD_TRANS_ICON(medium, "32"); + ADD_TRANS_ICON(large, "48"); + ADD_TRANS_ICON(huge, "64"); +#undef ADD_TRANS_ICON + gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset); gtk_icon_set_unref(iconset); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/plugins/convcolors.c --- a/pidgin/plugins/convcolors.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/plugins/convcolors.c Sat Dec 01 03:41:37 2007 +0000 @@ -48,22 +48,27 @@ #define PREF_SEND PREF_PREFIX "/send" #define PREF_SEND_C PREF_SEND "/color" #define PREF_SEND_F PREF_SEND "/format" +#define PREF_SEND_E PREF_SEND "/enabled" #define PREF_RECV PREF_PREFIX "/recv" #define PREF_RECV_C PREF_RECV "/color" #define PREF_RECV_F PREF_RECV "/format" +#define PREF_RECV_E PREF_RECV "/enabled" #define PREF_SYSTEM PREF_PREFIX "/system" #define PREF_SYSTEM_C PREF_SYSTEM "/color" #define PREF_SYSTEM_F PREF_SYSTEM "/format" +#define PREF_SYSTEM_E PREF_SYSTEM "/enabled" #define PREF_ERROR PREF_PREFIX "/error" #define PREF_ERROR_C PREF_ERROR "/color" #define PREF_ERROR_F PREF_ERROR "/format" +#define PREF_ERROR_E PREF_ERROR "/enabled" #define PREF_NICK PREF_PREFIX "/nick" #define PREF_NICK_C PREF_NICK "/color" #define PREF_NICK_F PREF_NICK "/format" +#define PREF_NICK_E PREF_NICK "/enabled" enum { @@ -104,7 +109,10 @@ if (!formats[i].prefix) return FALSE; - if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM && + g_snprintf(tmp, sizeof(tmp), "%s/enabled", formats[i].prefix); + + if (!purple_prefs_get_bool(tmp) || + (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM && !purple_prefs_get_bool(PREF_IMS)) || (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT && !purple_prefs_get_bool(PREF_CHATS))) @@ -223,17 +231,29 @@ } static void +toggle_enabled(GtkWidget *widget, gpointer data) +{ + const char *prefix = (char *)data; + gboolean e; + char tmp[128]; + + g_snprintf(tmp, sizeof(tmp), "%s/enabled", prefix); + e = purple_prefs_get_bool(tmp); + purple_prefs_set_bool(tmp, !e); +} + +static void toggle_something(const char *prefix, int format) { int f; char tmp[128]; - + g_snprintf(tmp, sizeof(tmp), "%s/format", prefix); f = purple_prefs_get_int(tmp); f ^= format; purple_prefs_set_int(tmp, f); } - + static void toggle_bold(GtkWidget *widget, gpointer data) { @@ -252,6 +272,22 @@ toggle_something(data, FONT_UNDERLINE); } +static void +enable_toggled(const char *name, PurplePrefType type, gconstpointer val, gpointer data) +{ + GtkWidget *widget = (GtkWidget *)data; + + gtk_widget_set_sensitive(widget, GPOINTER_TO_INT(val)); +} + +static void +disconnect_prefs_callbacks(GtkObject *object, gpointer data) +{ + PurplePlugin *plugin = (PurplePlugin *)data; + + purple_prefs_disconnect_by_handle(plugin); +} + static GtkWidget * get_config_frame(PurplePlugin *plugin) { @@ -265,9 +301,14 @@ for (i = 0; formats[i].prefix; i++) { char tmp[128]; + char tmp2[128]; int f; + gboolean e; GtkWidget *vbox, *hbox, *button; + g_snprintf(tmp2, sizeof(tmp2), "%s/enabled", formats[i].prefix); + e = purple_prefs_get_bool(tmp2); + g_snprintf(tmp, sizeof(tmp), "%s/format", formats[i].prefix); f = purple_prefs_get_int(tmp); @@ -278,11 +319,20 @@ hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + button = gtk_check_button_new_with_label(_("Enabled")); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + if (e) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_enabled), + formats[i].prefix); + button = pidgin_pixbuf_button_from_stock(" Color", GTK_STOCK_SELECT_COLOR, PIDGIN_BUTTON_HORIZONTAL); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(set_color), formats[i].prefix); + gtk_widget_set_sensitive(button, e); + purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button); button = gtk_check_button_new_with_label(_("Bold")); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); @@ -290,22 +340,29 @@ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_bold), formats[i].prefix); - + gtk_widget_set_sensitive(button, e); + purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button); + button = gtk_check_button_new_with_label(_("Italic")); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); if (f & FONT_ITALIC) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_italic), formats[i].prefix); - + gtk_widget_set_sensitive(button, e); + purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button); + button = gtk_check_button_new_with_label(_("Underline")); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); if (f & FONT_UNDERLINE) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(toggle_underline), formats[i].prefix); + gtk_widget_set_sensitive(button, e); + purple_prefs_connect_callback(plugin, tmp2, enable_toggled, button); } + g_signal_connect(GTK_OBJECT(ret), "destroy", G_CALLBACK(disconnect_prefs_callbacks), plugin); frame = pidgin_make_frame(ret, _("General")); pidgin_prefs_checkbox(_("Ignore incoming format"), PREF_IGNORE, frame); pidgin_prefs_checkbox(_("Apply in Chats"), PREF_CHATS, frame); @@ -388,6 +445,12 @@ purple_prefs_add_int(PREF_SYSTEM_F, FONT_ITALIC); purple_prefs_add_int(PREF_ERROR_F, FONT_BOLD | FONT_UNDERLINE); purple_prefs_add_int(PREF_NICK_F, FONT_BOLD); + + purple_prefs_add_bool(PREF_SEND_E, TRUE); + purple_prefs_add_bool(PREF_RECV_E, TRUE); + purple_prefs_add_bool(PREF_SYSTEM_E, TRUE); + purple_prefs_add_bool(PREF_ERROR_E, TRUE); + purple_prefs_add_bool(PREF_NICK_E, TRUE); } PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info) diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/plugins/pidginrc.c --- a/pidgin/plugins/pidginrc.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/plugins/pidginrc.c Sat Dec 01 03:41:37 2007 +0000 @@ -494,7 +494,7 @@ gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0); tmp = g_strdup_printf(_("Write settings to %s%sgtkrc-2.0"), - homepath, G_DIR_SEPARATOR_S); + homepath, G_DIR_SEPARATOR_S ".purple" G_DIR_SEPARATOR_S); check = gtk_button_new_with_label(tmp); g_free(tmp); gtk_box_pack_start(GTK_BOX(hbox), check, FALSE, FALSE, 0); diff -r 14afe5a92fc2 -r b63b2a5c2d05 pidgin/plugins/timestamp.c --- a/pidgin/plugins/timestamp.c Mon Nov 19 23:06:16 2007 +0000 +++ b/pidgin/plugins/timestamp.c Sat Dec 01 03:41:37 2007 +0000 @@ -49,11 +49,17 @@ const char *mdate; int y, height; GdkRectangle rect; - + /* display timestamp */ mdate = purple_utf8_strftime(then == 0 ? "%H:%M" : "\n%H:%M", localtime(&now)); gtk_text_buffer_get_end_iter(buffer, &iter); + + if (gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "TIMESTAMP") == NULL) + gtk_text_buffer_create_tag(buffer, "TIMESTAMP", + "foreground", "#888888", "justification", GTK_JUSTIFY_CENTER, + "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, mdate, strlen(mdate), "TIMESTAMP", NULL); @@ -95,17 +101,9 @@ static void timestamp_new_convo(PurpleConversation *conv) { - PidginConversation *gtk_conv = PIDGIN_CONVERSATION(conv); - GtkTextBuffer *buffer; - if (!g_list_find(purple_get_conversations(), conv)) return; - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtk_conv->imhtml)); - gtk_text_buffer_create_tag(buffer, "TIMESTAMP", - "foreground", "#888888", "justification", GTK_JUSTIFY_CENTER, - "weight", PANGO_WEIGHT_BOLD, NULL); - purple_conversation_set_data(conv, "timestamp-last", GINT_TO_POINTER(0)); } diff -r 14afe5a92fc2 -r b63b2a5c2d05 po/ChangeLog --- a/po/ChangeLog Mon Nov 19 23:06:16 2007 +0000 +++ b/po/ChangeLog Sat Dec 01 03:41:37 2007 +0000 @@ -1,5 +1,8 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.3.1 + * German translation updated (Jochen Kemnade, Björn Voigt) + version 2.3.0 * Afrikaans translation added (Friedel Wolff) * Belarusian Latin translation updated (Ihar Hrachyshka) diff -r 14afe5a92fc2 -r b63b2a5c2d05 po/de.po --- a/po/de.po Mon Nov 19 23:06:16 2007 +0000 +++ b/po/de.po Sat Dec 01 03:41:37 2007 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-17 16:16+0100\n" -"PO-Revision-Date: 2007-11-17 16:14+0100\n" -"Last-Translator: Bjoern Voigt \n" +"POT-Creation-Date: 2007-11-30 10:22+0100\n" +"PO-Revision-Date: 2007-11-30 10:21+0100\n" +"Last-Translator: Jochen Kemnade \n" "Language-Team: Deutsch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,6 +21,8 @@ "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: KBabel 1.11.4\n" +#. Translators may want to transliterate the name. +#. It is not to be translated. msgid "Finch" msgstr "Finch" @@ -493,6 +495,14 @@ msgid "Re-enable Account" msgstr "Konten reaktivieren" +msgid "" +"The account has disconnected and you are no longer in this chat. You will be " +"automatically rejoined in the chat when the account reconnects." +msgstr "" +"Das Konto wurde getrennt und Sie sind nicht mehr in diesem Chat. Sie werden " +"automatisch wieder mit dem Chat verbunden, wenn das Konto wieder verbunden " +"ist." + msgid "No such command." msgstr "Es gibt kein solches Kommando." @@ -2326,6 +2336,12 @@ msgid "Loads .NET plugins with Mono." msgstr "Lädt .NET-Plugins mit Mono." +msgid "Add new line in IMs" +msgstr "Neue Zeile in IMs einfügen" + +msgid "Add new line in Chats" +msgstr "Neue Zeile in Chats einfügen" + #. *< magic #. *< major version #. *< minor version @@ -4301,6 +4317,9 @@ msgid "Connect server" msgstr "Verbindungsserver" +msgid "File transfer proxies" +msgstr "Proxys für Dateiübertragungen" + #, c-format msgid "%s has left the conversation." msgstr "%s hat das Gespräch verlassen." @@ -9782,6 +9801,9 @@ msgid "_Merge" msgstr "_Zusammenführen" +msgid "Room _List" +msgstr "Ra_umliste" + msgid "" "Please enter the appropriate information about the chat you would like to " "join.\n" @@ -10056,19 +10078,19 @@ msgid "Re-enable" msgstr "Reaktivieren" -msgid "Ignore" -msgstr "Ignorieren" - -#, c-format -msgid "%d account was disabled because you signed on from another location." +msgid "Welcome back!" +msgstr "Willkommen zurück!" + +#, c-format +msgid "%d account was disabled because you signed on from another location:" msgid_plural "" -"%d accounts were disabled because you signed on from another location." +"%d accounts were disabled because you signed on from another location:" msgstr[0] "" "%d Konto wurde deaktiviert, da Sie sich von einem anderen Ort angemeldet " -"haben." +"haben:" msgstr[1] "" "%d Konten wurden deaktiviert, da Sie sich von einem anderen Ort angemeldet " -"haben." +"haben:" msgid "Username:" msgstr "Benutzername:" @@ -10152,11 +10174,11 @@ "Bitte geben Sie einen Alias und geeignete Informationen über den Chat ein, " "den Sie in Ihre Buddy-Liste aufnehmen wollen.\n" -msgid "Autojoin when account becomes online." -msgstr "Automatisch beitreten, wenn das Konto online geht." - -msgid "Hide chat when the window is closed." -msgstr "Chat verstecken, wenn das Fenster geschlossen wird." +msgid "Auto_join when account becomes online." +msgstr "Automatisch _beitreten, wenn das Konto online geht." + +msgid "_Hide chat when the window is closed." +msgstr "_Chat verstecken, wenn das Fenster geschlossen wird." msgid "Please enter the name of the group to be added." msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll." @@ -10234,6 +10256,9 @@ msgid "Un-Ignore" msgstr "Nicht Ignorieren" +msgid "Ignore" +msgstr "Ignorieren" + msgid "Get Away Message" msgstr "Neue Abwesenheitsnachricht abholen" @@ -11030,6 +11055,15 @@ msgid "_Reset formatting" msgstr "Formatierung _zurücksetzen" +msgid "_Smile!" +msgstr "_Lächeln!" + +msgid "_Insert" +msgstr "_Einfügen" + +msgid "_Font" +msgstr "_Schrift" + msgid "Hyperlink color" msgstr "Hyperlink-Farbe" @@ -11124,9 +11158,6 @@ msgid "Insert Link" msgstr "Link einfügen" -msgid "_Insert" -msgstr "_Einfügen" - #, c-format msgid "Failed to store image: %s\n" msgstr "Speichern des Bildes fehlgeschlagen: %s\n" @@ -11141,9 +11172,6 @@ msgid "Smile!" msgstr "Lächeln!" -msgid "_Font" -msgstr "_Schrift" - msgid "Group Items" msgstr "Elemente gruppieren" @@ -11228,9 +11256,6 @@ msgid "_Horizontal rule" msgstr "_Horizontale Linie" -msgid "_Smile!" -msgstr "_Lächeln!" - #, c-format msgid "" "Are you sure you want to permanently delete the log of the conversation with " @@ -11610,9 +11635,6 @@ msgid "Show _formatting on incoming messages" msgstr "Zeige _Formatierung bei ankommenden Nachrichten" -msgid "Close IMs immediately when the tab is closed" -msgstr "Schließe IMs sofort, wenn der Reiter geschlossen wird" - msgid "Show _detailed information" msgstr "_Detaillierte Informationen anzeigen" @@ -13116,6 +13138,3 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "" "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients." - -msgid "Buddy" -msgstr "Buddy"