# HG changeset patch # User Elliott Sales de Andrade # Date 1324628523 0 # Node ID 91a46f726cf47bd4d5b712ac70e08df195c8648b # Parent 48d35c0c62241c8806379fbe8de9b8176eb78e9b# Parent 3828a61c44dab3d4b4b7de313af29c186db13df5 merge of '7b6523560e839d3775c83d46b3c466732954bb03' and 'aa17fd919c5b0e1d9405ceccffb4b2e83487d6aa' diff -r 3828a61c44da -r 91a46f726cf4 COPYRIGHT diff -r 3828a61c44da -r 91a46f726cf4 ChangeLog diff -r 3828a61c44da -r 91a46f726cf4 ChangeLog.API diff -r 3828a61c44da -r 91a46f726cf4 configure.ac diff -r 3828a61c44da -r 91a46f726cf4 finch/gntblist.c diff -r 3828a61c44da -r 91a46f726cf4 finch/gntconv.c diff -r 3828a61c44da -r 91a46f726cf4 finch/gntft.c diff -r 3828a61c44da -r 91a46f726cf4 finch/gntnotify.c diff -r 3828a61c44da -r 91a46f726cf4 finch/gntroomlist.c diff -r 3828a61c44da -r 91a46f726cf4 finch/plugins/gntgf.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/account.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/account.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/blist.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/blist.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/certificate.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/certificate.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/connection.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/connection.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/conversation.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/conversation.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/dbus-analyze-functions.py diff -r 3828a61c44da -r 91a46f726cf4 libpurple/dbus-server.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/dnsquery.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/dnssrv.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/ft.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/ft.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/network.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/network.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/notify.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugin.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/log_reader.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/perl/common/BuddyList.xs diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/perl/common/Conversation.xs diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/perl/common/Network.xs diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/perl/common/Util.xs diff -r 3828a61c44da -r 91a46f726cf4 libpurple/plugins/statenotify.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/pounce.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/pounce.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/bonjour/bonjour.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/bonjour/bonjour_ft.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/bonjour/jabber.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/bonjour/jabber.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/bonjour/mdns_avahi.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/Makefile.am diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/gg.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/gg.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/common.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/dcc.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/dcc7.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/events.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/handlers.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/http.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/libgadu-config.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/libgadu.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/libgadu.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/pubdir.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/lib/resolver.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/win32-resolver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/win32-resolver.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,322 @@ +/** + * @file win32-resolver.c + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "win32-resolver.h" + +#include +#include +#include "debug.h" + +#ifndef _WIN32 +#error "win32thread resolver is not supported on current platform" +#endif + +/** + * Deal with the fact that you can't select() on a win32 file fd. + * This makes it practically impossible to tie into purple's event loop. + * + * -This is thanks to Tor Lillqvist. + */ +static int ggp_resolver_win32thread_socket_pipe(int *fds) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + fd_set read_set, write_set; + struct timeval tv; + + purple_debug_misc("gg", "ggp_resolver_win32thread_socket_pipe(&%d)\n", + *fds); + + temp = socket(AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { + goto out0; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { + goto out0; + } + + if (listen(temp, 1) == SOCKET_ERROR) { + goto out0; + } + + len = sizeof(saddr); + if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { + goto out0; + } + + socket1 = socket(AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out1; + } + + if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out1; + } + + FD_ZERO(&read_set); + FD_SET(temp, &read_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { + goto out1; + } + + if (!FD_ISSET(temp, &read_set)) { + goto out1; + } + + socket2 = accept(temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) { + goto out1; + } + + FD_ZERO(&write_set); + FD_SET(socket1, &write_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { + goto out2; + } + + if (!FD_ISSET(socket1, &write_set)) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + errno = EIO; /* XXX */ + + return -1; +} + +struct ggp_resolver_win32thread_data { + char *hostname; + int fd; +}; + +/** + * Copy-paste from gg_resolver_run(). + */ +static DWORD WINAPI ggp_resolver_win32thread_thread(LPVOID arg) +{ + struct ggp_resolver_win32thread_data *data = arg; + struct in_addr addr_ip[2], *addr_list; + int addr_count; + + purple_debug_info("gg", "ggp_resolver_win32thread_thread() host: %s, " + "fd: %i called\n", data->hostname, data->fd); + + if ((addr_ip[0].s_addr = inet_addr(data->hostname)) == INADDR_NONE) { + if (gg_gethostbyname_real(data->hostname, &addr_list, + &addr_count, 0) == -1) { + addr_list = addr_ip; + /* addr_ip[0] już zawiera INADDR_NONE */ + } + } else { + addr_list = addr_ip; + addr_ip[1].s_addr = INADDR_NONE; + addr_count = 1; + } + + purple_debug_misc("gg", "ggp_resolver_win32thread_thread() " + "count = %d\n", addr_count); + + write(data->fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)); + close(data->fd); + + free(data->hostname); + data->hostname = NULL; + + free(data); + + if (addr_list != addr_ip) + free(addr_list); + + purple_debug_misc("gg", "ggp_resolver_win32thread_thread() done\n"); + + return 0; +} + + +int ggp_resolver_win32thread_start(int *fd, void **private_data, + const char *hostname) +{ + struct ggp_resolver_win32thread_data *data = NULL; + HANDLE h; + DWORD dwTId; + int pipes[2], new_errno; + + purple_debug_info("gg", "ggp_resolver_win32thread_start(%p, %p, " + "\"%s\");\n", fd, private_data, hostname); + + if (!private_data || !fd || !hostname) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "invalid arguments\n"); + errno = EFAULT; + return -1; + } + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " + "pipes...\n"); + + if (ggp_resolver_win32thread_socket_pipe(pipes) == -1) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "unable to create pipes (errno=%d, %s)\n", + errno, strerror(errno)); + return -1; + } + + if (!(data = malloc(sizeof(*data)))) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() out " + "of memory\n"); + new_errno = errno; + goto cleanup; + } + + data->hostname = NULL; + + if (!(data->hostname = strdup(hostname))) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() out " + "of memory\n"); + new_errno = errno; + goto cleanup; + } + + data->fd = pipes[1]; + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " + "thread...\n"); + + h = CreateThread(NULL, 0, ggp_resolver_win32thread_thread, data, 0, + &dwTId); + + if (h == NULL) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + *private_data = h; + *fd = pipes[0]; + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() done\n"); + + return 0; + +cleanup: + if (data) { + free(data->hostname); + free(data); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; + +} + +void ggp_resolver_win32thread_cleanup(void **private_data, int force) +{ + struct ggp_resolver_win32thread_data *data; + + purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() force: %i " + "called\n", force); + + if (private_data == NULL || *private_data == NULL) { + purple_debug_error("gg", "ggp_resolver_win32thread_cleanup() " + "private_data: NULL\n"); + return; + } + return; /* XXX */ + + data = (struct ggp_resolver_win32thread_data*) *private_data; + purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() data: " + "%s called\n", data->hostname); + *private_data = NULL; + + if (force) { + purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() " + "force called\n"); + //pthread_cancel(data->thread); + //pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->fd != -1) { + close(data->fd); + data->fd = -1; + } + purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() done\n"); + free(data); +} + +/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/gg/win32-resolver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/win32-resolver.h Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,46 @@ +/** + * @file win32-resolver.h + * + * purple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_GG_WIN32_RESOLVER +#define _PURPLE_GG_WIN32_RESOLVER + +/** + * Starts hostname resolving in new win32 thread. + * + * @param fd Pointer to variable, where pipe descriptor will be saved. + * @param private_data Pointer to variable, where pointer to private data will + * be saved. + * @param hostname Hostname to resolve. + */ +int ggp_resolver_win32thread_start(int *fd, void **private_data, + const char *hostname); + +/** + * Cleans up resources after hostname resolving. + * + * @param private_data Pointer to variable storing pointer to private data. + * @param force TRUE, if resources should be cleaned up even, if + * resolving process didn't finished. + */ +void ggp_resolver_win32thread_cleanup(void **private_data, int force); + +#endif /* _PURPLE_GG_WIN32_RESOLVER */ + +/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/irc/dcc_send.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/irc/irc.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/irc/msgs.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/irc/parse.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/buddy.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/caps.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/chat.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/disco.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/jabber.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/jingle/rtp.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/jingle/session.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/libxmpp.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/message.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/oob.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/presence.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/roster.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/si.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/jabber/usertune.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/httpconn.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/msg.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/msn.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/notification.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/servconn.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/slp.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/slpcall.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/user.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/xfer.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/msn/xfer.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/actions.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/filexfer.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/formcmds.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/login.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/markup.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/mxit.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/protocol.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/mxit/roster.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/myspace/myspace.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/myspace/user.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/myspace/zap.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/novell/novell.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/null/nullprpl.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/family_feedbag.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/flap_connection.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/libaim.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/libicq.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/oft.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/oscar.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/oscar/peer.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/sametime/sametime.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/buddy.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/chat.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/ft.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/ops.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/silc.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/util.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/silc/wb.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/simple/simple.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/libyahoo.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/libyahoojp.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/libymsg.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/libymsg.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoo_aliases.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoo_doodle.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoo_filexfer.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoo_picture.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoo_profile.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/yahoo/yahoochat.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/protocols/zephyr/zephyr.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/proxy.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/proxy.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/prpl.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/prpl.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/request.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/request.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/roomlist.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/roomlist.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/server.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/sslconn.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/status.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/stun.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/tests/test_xmlnode.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/util.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/util.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/whiteboard.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/whiteboard.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/win32/libc_interface.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/win32/libc_interface.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/win32/libc_internal.h diff -r 3828a61c44da -r 91a46f726cf4 libpurple/xmlnode.c diff -r 3828a61c44da -r 91a46f726cf4 libpurple/xmlnode.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/Makefile.am diff -r 3828a61c44da -r 91a46f726cf4 pidgin/Makefile.mingw diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkaccount.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkblist-theme-loader.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkblist-theme.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkblist.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkblist.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkcellrendererexpander.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkcertmgr.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv-theme-loader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkconv-theme-loader.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,280 @@ +/* + * PidginConvThemeLoader for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "pidgin.h" +#include "gtkconv-theme-loader.h" +#include "gtkconv-theme.h" + +#include "xmlnode.h" +#include "debug.h" + +/***************************************************************************** + * Conversation Theme Builder + *****************************************************************************/ + +static GHashTable * +read_info_plist(xmlnode *plist) +{ + GHashTable *info; + xmlnode *key, *value; + gboolean fail = FALSE; + + info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + for (key = xmlnode_get_child(plist, "dict/key"); + key; + key = xmlnode_get_next_twin(key)) { + char *keyname; + GValue *val; + + ; + for (value = key->next; value && value->type != XMLNODE_TYPE_TAG; value = value->next) + ; + if (!value) { + fail = TRUE; + break; + } + + val = g_new0(GValue, 1); + if (g_str_equal(value->name, "string")) { + g_value_init(val, G_TYPE_STRING); + g_value_take_string(val, xmlnode_get_data_unescaped(value)); + + } else if (g_str_equal(value->name, "true")) { + g_value_init(val, G_TYPE_BOOLEAN); + g_value_set_boolean(val, TRUE); + + } else if (g_str_equal(value->name, "false")) { + g_value_init(val, G_TYPE_BOOLEAN); + g_value_set_boolean(val, FALSE); + + } else if (g_str_equal(value->name, "real")) { + char *temp = xmlnode_get_data_unescaped(value); + g_value_init(val, G_TYPE_FLOAT); + g_value_set_float(val, atof(temp)); + g_free(temp); + + } else if (g_str_equal(value->name, "integer")) { + char *temp = xmlnode_get_data_unescaped(value); + g_value_init(val, G_TYPE_INT); + g_value_set_int(val, atoi(temp)); + g_free(temp); + + } else { + /* NOTE: We don't support array, data, date, or dict as values, + since they don't seem to be needed for styles. */ + g_free(val); + fail = TRUE; + break; + } + + keyname = xmlnode_get_data_unescaped(key); + g_hash_table_insert(info, keyname, val); + } + + if (fail) { + g_hash_table_destroy(info); + info = NULL; + } + + return info; +} + +static PurpleTheme * +pidgin_conv_loader_build(const gchar *dir) +{ + PidginConvTheme *theme = NULL; + char *contents; + xmlnode *plist; + GHashTable *info; + GValue *val; + int MessageViewVersion; + const char *CFBundleName; + const char *CFBundleIdentifier; + GDir *variants; + char *variant_dir; + + g_return_val_if_fail(dir != NULL, NULL); + + /* Load Info.plist for theme information */ + contents = g_build_filename(dir, "Contents", NULL); + plist = xmlnode_from_file(contents, "Info.plist", "Info.plist", "gtkconv-theme-loader"); + g_free(contents); + if (plist == NULL) { + purple_debug_error("gtkconv-theme-loader", + "Failed to load Contents/Info.plist in %s\n", dir); + return NULL; + } + + info = read_info_plist(plist); + xmlnode_free(plist); + if (info == NULL) { + purple_debug_error("gtkconv-theme-loader", + "Failed to load Contents/Info.plist in %s\n", dir); + return NULL; + } + + /* Check for required keys: CFBundleName */ + val = g_hash_table_lookup(info, "CFBundleName"); + if (!val || !G_VALUE_HOLDS_STRING(val)) { + purple_debug_error("gtkconv-theme-loader", + "%s/Contents/Info.plist missing required string key CFBundleName.\n", + dir); + g_hash_table_destroy(info); + return NULL; + } + CFBundleName = g_value_get_string(val); + + /* Check for required keys: CFBundleIdentifier */ + val = g_hash_table_lookup(info, "CFBundleIdentifier"); + if (!val || !G_VALUE_HOLDS_STRING(val)) { + purple_debug_error("gtkconv-theme-loader", + "%s/Contents/Info.plist missing required string key CFBundleIdentifier.\n", + dir); + g_hash_table_destroy(info); + return NULL; + } + CFBundleIdentifier = g_value_get_string(val); + + /* Check for required keys: MessageViewVersion */ + val = g_hash_table_lookup(info, "MessageViewVersion"); + if (!val || !G_VALUE_HOLDS_INT(val)) { + purple_debug_error("gtkconv-theme-loader", + "%s/Contents/Info.plist missing required integer key MessageViewVersion.\n", + dir); + g_hash_table_destroy(info); + return NULL; + } + + MessageViewVersion = g_value_get_int(val); + if (MessageViewVersion < 3) { + purple_debug_error("gtkconv-theme-loader", + "%s is a legacy style (version %d) and will not be loaded.\n", + CFBundleName, MessageViewVersion); + g_hash_table_destroy(info); + return NULL; + } + + theme = g_object_new(PIDGIN_TYPE_CONV_THEME, + "type", "conversation", + "name", CFBundleName, + "directory", dir, + "info", info, NULL); + + /* Read list of variants */ + variant_dir = g_build_filename(dir, "Contents", "Resources", "Variants", NULL); + variants = g_dir_open(variant_dir, 0, NULL); + g_free(variant_dir); + + if (variants) { + char *prefname; + const char *default_variant = NULL; + const char *file; + + /* Make sure prefs exist */ + prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s", + CFBundleIdentifier); + purple_prefs_add_none(prefname); + g_free(prefname); + + /* Try user-set variant */ + prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant", + CFBundleIdentifier); + if (purple_prefs_exists(prefname)) + default_variant = purple_prefs_get_string(prefname); + g_free(prefname); + + if (default_variant && *default_variant) { + pidgin_conversation_theme_set_variant(theme, default_variant); + + } else { + /* Try theme default */ + val = g_hash_table_lookup(info, "DefaultVariant"); + if (val && G_VALUE_HOLDS_STRING(val)) { + default_variant = g_value_get_string(val); + if (default_variant && *default_variant) + pidgin_conversation_theme_set_variant(theme, default_variant); + else + default_variant = NULL; + } else + default_variant = NULL; + } + + while ((file = g_dir_read_name(variants)) != NULL) { + const char *end = g_strrstr(file, ".css"); + char *name; + + if ((end == NULL) || (*(end + 4) != '\0')) + continue; + + name = g_strndup(file, end - file); + pidgin_conversation_theme_add_variant(theme, name); + + /* Set variant with first found */ + if (!default_variant) { + pidgin_conversation_theme_set_variant(theme, name); + default_variant = name; + } + } + + g_dir_close(variants); + } + + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_conv_theme_loader_class_init(PidginConvThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = pidgin_conv_loader_build; +} + + +GType +pidgin_conversation_theme_loader_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof(PidginConvThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_conv_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginConvThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME_LOADER, + "PidginConvThemeLoader", &info, 0); + } + return type; +} + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv-theme-loader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkconv-theme-loader.h Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,72 @@ +/** + * @file gtkconv-theme-loader.h Pidgin Conversation Theme Loader Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef PIDGIN_CONV_THEME_LOADER_H +#define PIDGIN_CONV_THEME_LOADER_H + +#include +#include +#include "theme-loader.h" + +/** + * A pidgin conversation theme loader. Extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build conversation themes + * + * PidginConvThemeLoader is a GObject. + */ +typedef struct _PidginConvThemeLoader PidginConvThemeLoader; +typedef struct _PidginConvThemeLoaderClass PidginConvThemeLoaderClass; + +#define PIDGIN_TYPE_CONV_THEME_LOADER (pidgin_conversation_theme_loader_get_type ()) +#define PIDGIN_CONV_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoader)) +#define PIDGIN_CONV_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass)) +#define PIDGIN_IS_CONV_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME_LOADER)) +#define PIDGIN_IS_CONV_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME_LOADER)) +#define PIDGIN_CONV_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass)) + +struct _PidginConvThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PidginConvThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Conversation Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_conversation_theme_loader_get_type(void); + +G_END_DECLS +#endif /* PIDGIN_CONV_THEME_LOADER_H */ + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv-theme.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkconv-theme.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,750 @@ +/* + * Conversation Themes for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "gtkconv-theme.h" + +#include "conversation.h" +#include "debug.h" +#include "prefs.h" +#include "xmlnode.h" + +#include "pidgin.h" +#include "gtkconv.h" +#include "gtkwebview.h" + +#include +#include + +#define PIDGIN_CONV_THEME_GET_PRIVATE(Gobject) \ + (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_CONV_THEME, PidginConvThemePrivate)) + +/****************************************************************************** + * Structs + *****************************************************************************/ + +typedef struct { + /* current config options */ + char *variant; /* allowed to be NULL if there are no variants */ + GList *variants; + + /* Info.plist keys/values */ + GHashTable *info; + + /* caches */ + char *template_html; + char *header_html; + char *footer_html; + char *topic_html; + char *status_html; + char *content_html; + char *incoming_content_html; + char *outgoing_content_html; + char *incoming_next_content_html; + char *outgoing_next_content_html; + char *incoming_context_html; + char *outgoing_context_html; + char *incoming_next_context_html; + char *outgoing_next_context_html; + char *basestyle_css; +} PidginConvThemePrivate; + +/****************************************************************************** + * Enums + *****************************************************************************/ + +enum { + PROP_ZERO = 0, + PROP_INFO, + PROP_VARIANT, + PROP_LAST +}; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; +#if GLIB_CHECK_VERSION(2,26,0) +static GParamSpec *properties[PROP_LAST]; +#endif + +/****************************************************************************** + * Helper Functions + *****************************************************************************/ + +static const GValue * +get_key(PidginConvThemePrivate *priv, const char *key, gboolean specific) +{ + GValue *val = NULL; + + /* Try variant-specific key */ + if (specific && priv->variant) { + char *name = g_strdup_printf("%s:%s", key, priv->variant); + val = g_hash_table_lookup(priv->info, name); + g_free(name); + } + + /* Try generic key */ + if (!val) { + val = g_hash_table_lookup(priv->info, key); + } + + return val; +} + +/* The template path can either come from the theme, or can + * be stock Template.html that comes with Pidgin */ +static char * +get_template_path(const char *dir) +{ + char *file; + + file = g_build_filename(dir, "Contents", "Resources", "Template.html", NULL); + + if (!g_file_test(file, G_FILE_TEST_EXISTS)) { + g_free(file); + file = g_build_filename(DATADIR, "pidgin", "theme", "conversation", "Template.html", NULL); + } + + return file; +} + +static const char * +get_template_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->template_html) + return priv->template_html; + + file = get_template_path(dir); + + if (!g_file_get_contents(file, &priv->template_html, NULL, NULL)) { + purple_debug_error("webkit", "Could not locate a Template.html (%s)\n", file); + priv->template_html = g_strdup(""); + } + g_free(file); + + return priv->template_html; +} + +static const char * +get_basestyle_css(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->basestyle_css) + return priv->basestyle_css; + + file = g_build_filename(dir, "Contents", "Resources", "main.css", NULL); + if (!g_file_get_contents(file, &priv->basestyle_css, NULL, NULL)) + priv->basestyle_css = g_strdup(""); + g_free(file); + + return priv->basestyle_css; +} + +static const char * +get_header_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->header_html) + return priv->header_html; + + file = g_build_filename(dir, "Contents", "Resources", "Header.html", NULL); + if (!g_file_get_contents(file, &priv->header_html, NULL, NULL)) + priv->header_html = g_strdup(""); + g_free(file); + + return priv->header_html; +} + +static const char * +get_footer_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->footer_html) + return priv->footer_html; + + file = g_build_filename(dir, "Contents", "Resources", "Footer.html", NULL); + if (!g_file_get_contents(file, &priv->footer_html, NULL, NULL)) + priv->footer_html = g_strdup(""); + g_free(file); + + return priv->footer_html; +} + +static const char * +get_topic_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->topic_html) + return priv->topic_html; + + file = g_build_filename(dir, "Contents", "Resources", "Topic.html", NULL); + if (!g_file_get_contents(file, &priv->topic_html, NULL, NULL)) { + purple_debug_info("webkit", "%s could not find Resources/Topic.html\n", dir); + priv->topic_html = g_strdup(""); + } + g_free(file); + + return priv->topic_html; +} + +static const char * +get_content_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->content_html) + return priv->content_html; + + file = g_build_filename(dir, "Contents", "Resources", "Content.html", NULL); + if (!g_file_get_contents(file, &priv->content_html, NULL, NULL)) { + purple_debug_info("webkit", "%s did not have a Content.html\n", dir); + priv->content_html = g_strdup(""); + } + g_free(file); + + return priv->content_html; +} + +static const char * +get_status_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->status_html) + return priv->status_html; + + file = g_build_filename(dir, "Contents", "Resources", "Status.html", NULL); + if (!g_file_get_contents(file, &priv->status_html, NULL, NULL)) { + purple_debug_info("webkit", "%s could not find Resources/Status.html\n", dir); + priv->status_html = g_strdup(get_content_html(priv, dir)); + } + g_free(file); + + return priv->status_html; +} + +static const char * +get_incoming_content_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->incoming_content_html) + return priv->incoming_content_html; + + file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Content.html", NULL); + if (!g_file_get_contents(file, &priv->incoming_content_html, NULL, NULL)) { + purple_debug_info("webkit", "%s did not have a Incoming/Content.html\n", dir); + priv->incoming_content_html = g_strdup(get_content_html(priv, dir)); + } + g_free(file); + + return priv->incoming_content_html; +} + +static const char * +get_incoming_next_content_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->incoming_next_content_html) + return priv->incoming_next_content_html; + + file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContent.html", NULL); + if (!g_file_get_contents(file, &priv->incoming_next_content_html, NULL, NULL)) { + priv->incoming_next_content_html = g_strdup(get_incoming_content_html(priv, dir)); + } + g_free(file); + + return priv->incoming_next_content_html; +} + +static const char * +get_incoming_context_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->incoming_context_html) + return priv->incoming_context_html; + + file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Context.html", NULL); + if (!g_file_get_contents(file, &priv->incoming_context_html, NULL, NULL)) { + purple_debug_info("webkit", "%s did not have a Incoming/Context.html\n", dir); + priv->incoming_context_html = g_strdup(get_incoming_content_html(priv, dir)); + } + g_free(file); + + return priv->incoming_context_html; +} + +static const char * +get_incoming_next_context_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->incoming_next_context_html) + return priv->incoming_next_context_html; + + file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContext.html", NULL); + if (!g_file_get_contents(file, &priv->incoming_next_context_html, NULL, NULL)) { + priv->incoming_next_context_html = g_strdup(get_incoming_context_html(priv, dir)); + } + g_free(file); + + return priv->incoming_next_context_html; +} + +static const char * +get_outgoing_content_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->outgoing_content_html) + return priv->outgoing_content_html; + + file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Content.html", NULL); + if (!g_file_get_contents(file, &priv->outgoing_content_html, NULL, NULL)) { + priv->outgoing_content_html = g_strdup(get_incoming_content_html(priv, dir)); + } + g_free(file); + + return priv->outgoing_content_html; +} + +static const char * +get_outgoing_next_content_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->outgoing_next_content_html) + return priv->outgoing_next_content_html; + + file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContent.html", NULL); + if (!g_file_get_contents(file, &priv->outgoing_next_content_html, NULL, NULL)) { + priv->outgoing_next_content_html = g_strdup(get_outgoing_content_html(priv, dir)); + } + + return priv->outgoing_next_content_html; +} + +static const char * +get_outgoing_context_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->outgoing_context_html) + return priv->outgoing_context_html; + + file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Context.html", NULL); + if (!g_file_get_contents(file, &priv->outgoing_context_html, NULL, NULL)) { + priv->outgoing_context_html = g_strdup(get_incoming_context_html(priv, dir)); + } + g_free(file); + + return priv->outgoing_context_html; +} + +static const char * +get_outgoing_next_context_html(PidginConvThemePrivate *priv, const char *dir) +{ + char *file; + + if (priv->outgoing_next_context_html) + return priv->outgoing_next_context_html; + + file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContext.html", NULL); + if (!g_file_get_contents(file, &priv->outgoing_next_context_html, NULL, NULL)) { + priv->outgoing_next_context_html = g_strdup(get_outgoing_context_html(priv, dir)); + } + g_free(file); + + return priv->outgoing_next_context_html; +} + +static void +_set_variant(PidginConvTheme *theme, const char *variant) +{ + PidginConvThemePrivate *priv; + const GValue *val; + char *prefname; + + g_return_if_fail(theme != NULL); + g_return_if_fail(variant != NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + g_free(priv->variant); + priv->variant = g_strdup(variant); + + val = get_key(priv, "CFBundleIdentifier", FALSE); + prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant", + g_value_get_string(val)); + purple_prefs_set_string(prefname, variant); + g_free(prefname); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_conv_theme_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *psec) +{ + PidginConvTheme *theme = PIDGIN_CONV_THEME(obj); + + switch (param_id) { + case PROP_INFO: + g_value_set_boxed(value, (gpointer)pidgin_conversation_theme_get_info(theme)); + break; + + case PROP_VARIANT: + g_value_set_string(value, pidgin_conversation_theme_get_variant(theme)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +pidgin_conv_theme_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *psec) +{ + PidginConvTheme *theme = PIDGIN_CONV_THEME(obj); + + switch (param_id) { + case PROP_INFO: + pidgin_conversation_theme_set_info(theme, g_value_get_boxed(value)); + break; + + case PROP_VARIANT: + _set_variant(theme, g_value_get_string(value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +pidgin_conv_theme_init(GTypeInstance *instance, + gpointer klass) +{ + PidginConvThemePrivate *priv; + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(instance); +} + +static void +pidgin_conv_theme_finalize(GObject *obj) +{ + PidginConvThemePrivate *priv; + GList *list; + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(obj); + + g_free(priv->template_html); + g_free(priv->header_html); + g_free(priv->footer_html); + g_free(priv->topic_html); + g_free(priv->status_html); + g_free(priv->content_html); + g_free(priv->incoming_content_html); + g_free(priv->outgoing_content_html); + g_free(priv->incoming_next_content_html); + g_free(priv->outgoing_next_content_html); + g_free(priv->incoming_context_html); + g_free(priv->outgoing_context_html); + g_free(priv->incoming_next_context_html); + g_free(priv->outgoing_next_context_html); + g_free(priv->basestyle_css); + + if (priv->info) + g_hash_table_destroy(priv->info); + + list = priv->variants; + while (list) { + g_free(list->data); + list = g_list_delete_link(list, list); + } + g_free(priv->variant); + + parent_class->finalize(obj); +} + +static void +pidgin_conv_theme_class_init(PidginConvThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent(klass); + + g_type_class_add_private(klass, sizeof(PidginConvThemePrivate)); + + obj_class->get_property = pidgin_conv_theme_get_property; + obj_class->set_property = pidgin_conv_theme_set_property; + obj_class->finalize = pidgin_conv_theme_finalize; + + /* INFO */ + pspec = g_param_spec_boxed("info", "Info", + "The information about this theme", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property(obj_class, PROP_INFO, pspec); +#if GLIB_CHECK_VERSION(2,26,0) + properties[PROP_INFO] = pspec; +#endif + + /* VARIANT */ + pspec = g_param_spec_string("variant", "Variant", + "The current variant for this theme", + NULL, G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_VARIANT, pspec); +#if GLIB_CHECK_VERSION(2,26,0) + properties[PROP_VARIANT] = pspec; +#endif +} + +GType +pidgin_conversation_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof(PidginConvThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_conv_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(PidginConvTheme), + 0, /* n_preallocs */ + pidgin_conv_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME, + "PidginConvTheme", &info, 0); + } + return type; +} + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +const GHashTable * +pidgin_conversation_theme_get_info(const PidginConvTheme *theme) +{ + PidginConvThemePrivate *priv; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + return priv->info; +} + +void +pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info) +{ + PidginConvThemePrivate *priv; + + g_return_if_fail(theme != NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + if (priv->info) + g_hash_table_destroy(priv->info); + + priv->info = info; +} + +const GValue * +pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific) +{ + PidginConvThemePrivate *priv; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + return get_key(priv, key, specific); +} + +const char * +pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type) +{ + PidginConvThemePrivate *priv; + const char *dir; + const char *html; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + + switch (type) { + case PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN: + html = get_template_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER: + html = get_header_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER: + html = get_footer_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC: + html = get_topic_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS: + html = get_status_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT: + html = get_content_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT: + html = get_incoming_content_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT: + html = get_incoming_next_content_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT: + html = get_incoming_context_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT: + html = get_incoming_next_context_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT: + html = get_outgoing_content_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT: + html = get_outgoing_next_content_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT: + html = get_outgoing_context_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT: + html = get_outgoing_next_context_html(priv, dir); + break; + case PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS: + html = get_basestyle_css(priv, dir); + break; + default: + purple_debug_error("gtkconv-theme", + "Requested invalid template type (%d) for theme %s.\n", + type, purple_theme_get_name(PURPLE_THEME(theme))); + html = NULL; + } + + return html; +} + +void +pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant) +{ + PidginConvThemePrivate *priv; + + g_return_if_fail(theme != NULL); + g_return_if_fail(variant != NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + priv->variants = g_list_prepend(priv->variants, variant); +} + +const char * +pidgin_conversation_theme_get_variant(PidginConvTheme *theme) +{ + PidginConvThemePrivate *priv; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + return priv->variant; +} + +void +pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant) +{ + _set_variant(theme, variant); +#if GLIB_CHECK_VERSION(2,26,0) + g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_VARIANT]); +#else + g_object_notify(G_OBJECT(theme), "variant"); +#endif +} + +const GList * +pidgin_conversation_theme_get_variants(PidginConvTheme *theme) +{ + PidginConvThemePrivate *priv; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + return priv->variants; +} + +char * +pidgin_conversation_theme_get_template_path(PidginConvTheme *theme) +{ + const char *dir; + + g_return_val_if_fail(theme != NULL, NULL); + + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + + return get_template_path(dir); +} + +char * +pidgin_conversation_theme_get_css_path(PidginConvTheme *theme) +{ + PidginConvThemePrivate *priv; + const char *dir; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + if (!priv->variant) { + return g_build_filename(dir, "Contents", "Resources", "main.css", NULL); + } else { + char *file = g_strdup_printf("%s.css", priv->variant); + char *ret = g_build_filename(dir, "Contents", "Resources", "Variants", file, NULL); + g_free(file); + return ret; + } +} + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv-theme.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkconv-theme.h Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,196 @@ +/** + * @file gtkconv-theme.h Pidgin Conversation Theme Class API + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef PIDGIN_CONV_THEME_H +#define PIDGIN_CONV_THEME_H + +#include +#include +#include "conversation.h" +#include "theme.h" + +/** + * extends PurpleTheme (theme.h) + * A pidgin icon theme. + * This object represents a Pidgin icon theme. + * + * PidginConvTheme is a PurpleTheme Object. + */ +typedef struct _PidginConvTheme PidginConvTheme; +typedef struct _PidginConvThemeClass PidginConvThemeClass; + +#define PIDGIN_TYPE_CONV_THEME (pidgin_conversation_theme_get_type ()) +#define PIDGIN_CONV_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvTheme)) +#define PIDGIN_CONV_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass)) +#define PIDGIN_IS_CONV_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME)) +#define PIDGIN_IS_CONV_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME)) +#define PIDGIN_CONV_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass)) + +struct _PidginConvTheme +{ + PurpleTheme parent; +}; + +struct _PidginConvThemeClass +{ + PurpleThemeClass parent_class; +}; + +typedef enum { + PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN, + PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER, + PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER, + PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC, + PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS, + PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT, + PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS + +} PidginConvThemeTemplateType; + +/**************************************************************************/ +/** @name Pidgin Conversation Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_conversation_theme_get_type(void); + +/** + * Get the Info.plist hash table from a conversation theme. + * + * @param theme The conversation theme + * + * @return The hash table. Keys are strings as outlined for message styles, + * values are GValue*s. This is an internal structure. Take a ref if + * necessary, but don't destroy it yourself. + */ +const GHashTable *pidgin_conversation_theme_get_info(const PidginConvTheme *theme); + +/** + * Set the Info.plist hash table for a conversation theme. + * + * @param theme The conversation theme + * @param info The new hash table. The theme will take ownership of this hash + * table. Do not use it yourself afterwards with holding a ref. + * For key and value specifications, @see pidgin_conversation_theme_get_info. + * + */ +void pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info); + +/** + * Lookup a key in a theme + * + * @param theme The conversation theme + * @param key The key to find + * @param specific Whether to search variant-specific keys + * + * @return The key information. If @a specific is @c TRUE, then keys are first + * searched by variant, then by general ones. Otherwise, only general + * key values are returned. + */ +const GValue *pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific); + +/** + * Get the template data from a conversation theme. + * + * @param theme The conversation theme + * @param type The type of template data + * + * @return The template data requested. Fallback is made as required by styles. + * Subsequent calls to this function will return cached values. + */ +const char *pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type); + +/** + * Add an available variant name to a conversation theme. + * + * @param theme The conversation theme + * @param variant The name of the variant + * + * @Note The conversation theme will take ownership of the variant name string. + * This function should normally only be called by the theme loader. + */ +void pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant); + +/** + * Get the currently set variant name for a conversation theme. + * + * @param theme The conversation theme + * + * @return The current variant name. + */ +const char *pidgin_conversation_theme_get_variant(PidginConvTheme *theme); + +/** + * Set the variant name for a conversation theme. + * + * @param theme The conversation theme + * @param variant The name of the variant + * + */ +void pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant); + +/** + * Get a list of available variants for a conversation theme. + * + * @param theme The conversation theme + * + * @return The list of variants. This GList and the string data are owned by + * the theme and should not be freed by the caller. + */ +const GList *pidgin_conversation_theme_get_variants(PidginConvTheme *theme); + +/** + * Get the path to the template HTML file. + * + * @param theme The conversation theme + * + * @return The path to the HTML file. + */ +char *pidgin_conversation_theme_get_template_path(PidginConvTheme *theme); + +/** + * Get the path to the current variant CSS file. + * + * @param theme The conversation theme + * + * @return The path to the CSS file. + */ +char *pidgin_conversation_theme_get_css_path(PidginConvTheme *theme); + +G_END_DECLS +#endif /* PIDGIN_CONV_THEME_H */ + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconv.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkconvwin.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkdebug.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkdialogs.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkdialogs.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkdnd-hints.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkdocklet.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkft.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkidle.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkimhtml.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkimhtml.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkimhtmltoolbar.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkimhtmltoolbar.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtklog.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkmedia.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkmenutray.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtknotify.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkplugin.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkpounce.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkprefs.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkprivacy.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkrequest.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkroomlist.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtksavedstatuses.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkscrollbook.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtksession.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtksmiley.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtksound.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtksourceundomanager.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkstatusbox.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkthemes.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkthemes.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkutils.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkutils.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkwebview.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkwebview.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,419 @@ +/* + * @file gtkwebview.c GTK+ WebKitWebView wrapper class. + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "util.h" +#include "gtkwebview.h" +#include "imgstore.h" + +static WebKitWebViewClass *parent_class = NULL; + +struct GtkWebViewPriv { + GHashTable *images; /**< a map from id to temporary file for the image */ + gboolean empty; /**< whether anything has been appended **/ + + /* JS execute queue */ + GQueue *js_queue; + gboolean is_loading; + GtkAdjustment *vadj; + guint scroll_src; + GTimer *scroll_time; +}; + +GtkWidget * +gtk_webview_new(void) +{ + GtkWebView* ret = GTK_WEBVIEW(g_object_new(gtk_webview_get_type(), NULL)); + return GTK_WIDGET(ret); +} + +static char * +get_image_filename_from_id(GtkWebView* view, int id) +{ + char *filename = NULL; + FILE *file; + PurpleStoredImage* img; + + if (!view->priv->images) + view->priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + filename = (char *)g_hash_table_lookup(view->priv->images, GINT_TO_POINTER(id)); + if (filename) + return filename; + + /* else get from img store */ + file = purple_mkstemp(&filename, TRUE); + + img = purple_imgstore_find_by_id(id); + + fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file); + g_hash_table_insert(view->priv->images, GINT_TO_POINTER(id), filename); + fclose(file); + return filename; +} + +static void +clear_single_image(gpointer key, gpointer value, gpointer userdata) +{ + g_unlink((char *)value); +} + +static void +clear_images(GtkWebView *view) +{ + if (!view->priv->images) + return; + g_hash_table_foreach(view->priv->images, clear_single_image, NULL); + g_hash_table_unref(view->priv->images); +} + +/* + * Replace all tags with . I hoped to never + * write any HTML parsing code, but I'm forced to do this, until + * purple changes the way it works. + */ +static char * +replace_img_id_with_src(GtkWebView *view, const char *html) +{ + GString *buffer = g_string_sized_new(strlen(html)); + const char* cur = html; + char *id; + int nid; + + while (*cur) { + const char *img = strstr(cur, ""); + if (!cur) + cur = strstr(img, ">"); + + if (!cur) { /* invalid html? */ + g_string_printf(buffer, "%s", html); + break; + } + + if (strstr(img, "src=") || !strstr(img, "id=")) { + g_string_printf(buffer, "%s", html); + break; + } + + /* + * if this is valid HTML, then I can be sure that it + * has an id= and does not have an src=, since + * '=' cannot appear in parameters. + */ + + id = strstr(img, "id=") + 3; + + /* *id can't be \0, since a ">" appears after this */ + if (isdigit(*id)) + nid = atoi(id); + else + nid = atoi(id + 1); + + /* let's dump this, tag and then dump the src information */ + g_string_append_len(buffer, img, cur - img); + + g_string_append_printf(buffer, " src='file://%s' ", get_image_filename_from_id(view, nid)); + } + + return g_string_free(buffer, FALSE); +} + +static void +gtk_webview_finalize(GObject *view) +{ + gpointer temp; + + while ((temp = g_queue_pop_head(GTK_WEBVIEW(view)->priv->js_queue))) + g_free(temp); + g_queue_free(GTK_WEBVIEW(view)->priv->js_queue); + + clear_images(GTK_WEBVIEW(view)); + g_free(GTK_WEBVIEW(view)->priv); + G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(view)); +} + +static void +gtk_webview_class_init(GtkWebViewClass *klass, gpointer userdata) +{ + parent_class = g_type_class_ref(webkit_web_view_get_type()); + G_OBJECT_CLASS(klass)->finalize = gtk_webview_finalize; +} + +static gboolean +webview_link_clicked(WebKitWebView *view, + WebKitWebFrame *frame, + WebKitNetworkRequest *request, + WebKitWebNavigationAction *navigation_action, + WebKitWebPolicyDecision *policy_decision) +{ + const gchar *uri; + WebKitWebNavigationReason reason; + + uri = webkit_network_request_get_uri(request); + reason = webkit_web_navigation_action_get_reason(navigation_action); + + if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) { + /* the gtk imhtml way was to create an idle cb, not sure + * why, so right now just using purple_notify_uri directly */ + purple_notify_uri(NULL, uri); + } else + webkit_web_policy_decision_use(policy_decision); + + return TRUE; +} + +static gboolean +process_js_script_queue(GtkWebView *view) +{ + char *script; + if (view->priv->is_loading) + return FALSE; /* we will be called when loaded */ + if (!view->priv->js_queue || g_queue_is_empty(view->priv->js_queue)) + return FALSE; /* nothing to do! */ + + script = g_queue_pop_head(view->priv->js_queue); + webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script); + g_free(script); + + return TRUE; /* there may be more for now */ +} + +static void +webview_load_started(WebKitWebView *view, + WebKitWebFrame *frame, + gpointer userdata) +{ + /* is there a better way to test for is_loading? */ + GTK_WEBVIEW(view)->priv->is_loading = TRUE; +} + +static void +webview_load_finished(WebKitWebView *view, + WebKitWebFrame *frame, + gpointer userdata) +{ + GTK_WEBVIEW(view)->priv->is_loading = FALSE; + g_idle_add((GSourceFunc)process_js_script_queue, view); +} + +void +gtk_webview_safe_execute_script(GtkWebView *view, const char *script) +{ + g_queue_push_tail(view->priv->js_queue, g_strdup(script)); + g_idle_add((GSourceFunc)process_js_script_queue, view); +} + +static void +gtk_webview_init(GtkWebView *view, gpointer userdata) +{ + view->priv = g_new0(struct GtkWebViewPriv, 1); + g_signal_connect(view, "navigation-policy-decision-requested", + G_CALLBACK(webview_link_clicked), + view); + + g_signal_connect(view, "load-started", + G_CALLBACK(webview_load_started), + view); + + g_signal_connect(view, "load-finished", + G_CALLBACK(webview_load_finished), + view); + + view->priv->empty = TRUE; + view->priv->js_queue = g_queue_new(); +} + + +void +gtk_webview_load_html_string_with_imgstore(GtkWebView *view, const char *html) +{ + char *html_imged; + + clear_images(view); + html_imged = replace_img_id_with_src(view, html); + webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(view), html_imged, "file:///"); + g_free(html_imged); +} + +char * +gtk_webview_quote_js_string(const char *text) +{ + GString *str = g_string_new("\""); + const char *cur = text; + + while (cur && *cur) { + switch (*cur) { + case '\\': + g_string_append(str, "\\\\"); + break; + case '\"': + g_string_append(str, "\\\""); + break; + case '\r': + g_string_append(str, "
"); + break; + case '\n': + break; + default: + g_string_append_c(str, *cur); + } + cur++; + } + g_string_append_c(str, '"'); + return g_string_free(str, FALSE); +} + +void +gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj) +{ + webview->priv->vadj = vadj; +} + +/* this is a "hack", my plan is to eventually handle this + * correctly using a signals and a plugin: the plugin will have + * the information as to what javascript function to call. It seems + * wrong to hardcode that here. + */ +void +gtk_webview_append_html(GtkWebView *view, const char *html) +{ + char *escaped = gtk_webview_quote_js_string(html); + char *script = g_strdup_printf("document.write(%s)", escaped); + webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script); + view->priv->empty = FALSE; + gtk_webview_scroll_to_end(view, TRUE); + g_free(script); + g_free(escaped); +} + +gboolean +gtk_webview_is_empty(GtkWebView *view) +{ + return view->priv->empty; +} + +#define MAX_SCROLL_TIME 0.4 /* seconds */ +#define SCROLL_DELAY 33 /* milliseconds */ + +/* + * Smoothly scroll a WebView. + * + * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom. + */ +static gboolean +smooth_scroll_cb(gpointer data) +{ + struct GtkWebViewPriv *priv = data; + GtkAdjustment *adj = priv->vadj; + gdouble max_val = adj->upper - adj->page_size; + gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3); + + g_return_val_if_fail(priv->scroll_time != NULL, FALSE); + + if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) { + /* time's up. jump to the end and kill the timer */ + gtk_adjustment_set_value(adj, max_val); + g_timer_destroy(priv->scroll_time); + priv->scroll_time = NULL; + g_source_remove(priv->scroll_src); + priv->scroll_src = 0; + return FALSE; + } + + /* scroll by 1/3rd the remaining distance */ + gtk_adjustment_set_value(adj, scroll_val); + return TRUE; +} + +static gboolean +scroll_idle_cb(gpointer data) +{ + struct GtkWebViewPriv *priv = data; + GtkAdjustment *adj = priv->vadj; + if (adj) { + gtk_adjustment_set_value(adj, adj->upper - adj->page_size); + } + priv->scroll_src = 0; + return FALSE; +} + +void +gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth) +{ + struct GtkWebViewPriv *priv = webview->priv; + if (priv->scroll_time) + g_timer_destroy(priv->scroll_time); + if (priv->scroll_src) + g_source_remove(priv->scroll_src); + if(smooth) { + priv->scroll_time = g_timer_new(); + priv->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, priv, NULL); + } else { + priv->scroll_time = NULL; + priv->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, priv, NULL); + } +} + +GType +gtk_webview_get_type(void) +{ + static GType mview_type = 0; + if (G_UNLIKELY(mview_type == 0)) { + static const GTypeInfo mview_info = { + sizeof(GtkWebViewClass), + NULL, + NULL, + (GClassInitFunc) gtk_webview_class_init, + NULL, + NULL, + sizeof(GtkWebView), + 0, + (GInstanceInitFunc) gtk_webview_init, + NULL + }; + mview_type = g_type_register_static(webkit_web_view_get_type(), + "GtkWebView", &mview_info, 0); + } + return mview_type; +} + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkwebview.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkwebview.h Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,147 @@ +/** + * @file gtkwebview.h Wrapper over the Gtk WebKitWebView component + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#ifndef _PIDGIN_WEBVIEW_H_ +#define _PIDGIN_WEBVIEW_H_ + +#include +#include +#include + +#include "notify.h" + +#define GTK_TYPE_WEBVIEW (gtk_webview_get_type()) +#define GTK_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_WEBVIEW, GtkWebView)) +#define GTK_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_WEBVIEW, GtkWebViewClass)) +#define GTK_IS_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_WEBVIEW)) +#define GTK_IS_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW)) + + +struct GtkWebViewPriv; + +struct _GtkWebView +{ + WebKitWebView webkit_web_view; + + /*< private >*/ + struct GtkWebViewPriv *priv; +}; + +typedef struct _GtkWebView GtkWebView; + +struct _GtkWebViewClass +{ + WebKitWebViewClass parent; +}; + +typedef struct _GtkWebViewClass GtkWebViewClass; + + +/** + * Returns the GType for a GtkWebView widget + * + * @return The GType for GtkWebView widget + */ +GType gtk_webview_get_type(void); + +/** + * Create a new GtkWebView object + * + * @return A GtkWidget corresponding to the GtkWebView object + */ +GtkWidget *gtk_webview_new(void); + +/** + * Set the vertical adjustment for the GtkWebView. + * + * @param webview The GtkWebView object + * @param vadj The GtkAdjustment that control the webview + */ +void gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj); + +/** + * A very basic routine to append html, which can be considered + * equivalent to a "document.write" using JavaScript. + * + * @param webview The GtkWebView object + * @param markup The html markup to append + */ +void gtk_webview_append_html(GtkWebView *webview, const char *markup); + +/** + * Rather than use webkit_webview_load_string, this routine + * parses and displays the \ tags that make use of the + * Pidgin imgstore. + * + * @param webview The GtkWebView object + * @param html The HTML content to load + */ +void gtk_webview_load_html_string_with_imgstore(GtkWebView *webview, const char *html); + +/** + * TODO WEBKIT: Right now this just tests whether an append has been called + * since the last clear or since the Widget was created. So it does not + * test for load_string's called in between. + * + * @param webview The GtkWebView object + * + * @return gboolean indicating whether the webview is empty + */ +gboolean gtk_webview_is_empty(GtkWebView *webview); + +/** + * Execute the JavaScript only after the webkit_webview_load_string + * loads completely. We also guarantee that the scripts are executed + * in the order they are called here. This is useful to avoid race + * conditions when calling JS functions immediately after opening the + * page. + * + * @param webview The GtkWebView object + * @param script The script to execute + */ +void gtk_webview_safe_execute_script(GtkWebView *webview, const char *script); + +/** + * A convenience routine to quote a string for use as a JavaScript + * string. For instance, "hello 'world'" becomes "'hello \\'world\\''" + * + * @param str The string to escape and quote + * + * @return The quoted string + */ +char *gtk_webview_quote_js_string(const char *str); + +/** + * Scrolls the Webview to the end of its contents. + * + * @param webview The GtkWebView object + * @param smooth A boolean indicating if smooth scrolling should be used + */ +void gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth); + +#endif /* _PIDGIN_WEBVIEW_H_ */ + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkwhiteboard.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/gtkwhiteboard.h diff -r 3828a61c44da -r 91a46f726cf4 pidgin/minidialog.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/pidginstock.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/pidgintooltip.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/Makefile.am diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/contact_priority.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/convcolors.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/disco/gtkdisco.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/gestures/gestures.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/gestures/stroke-draw.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/markerline.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/musicmessaging/musicmessaging.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/perl/common/GtkIMHtml.xs diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/themeedit.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/ticker/gtkticker.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/timestamp.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/timestamp_format.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/vvconfig.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/webkit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/webkit.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,201 @@ +/* + * WebKit - Open the inspector on any WebKit views. + * Copyright (C) 2011 Elliott Sales de Andrade + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "internal.h" + +#include "version.h" + +#include "pidgin.h" + +#include "gtkconv.h" +#include "gtkplugin.h" +#include "gtkwebview.h" + +static WebKitWebView * +create_gtk_window_around_it(WebKitWebInspector *inspector, + WebKitWebView *webview, + PidginConversation *gtkconv) +{ + GtkWidget *win; + GtkWidget *view; + char *title; + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + title = g_strdup_printf(_("%s - Inspector"), + gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); + gtk_window_set_title(GTK_WINDOW(win), title); + g_free(title); + gtk_window_set_default_size(GTK_WINDOW(win), 600, 400); + g_signal_connect_swapped(G_OBJECT(gtkconv->tab_cont), "destroy", G_CALLBACK(gtk_widget_destroy), win); + + view = webkit_web_view_new(); + gtk_container_add(GTK_CONTAINER(win), view); + g_object_set_data(G_OBJECT(webview), "inspector-window", win); + + return WEBKIT_WEB_VIEW(view); +} + +static gboolean +show_inspector_window(WebKitWebInspector *inspector, + GtkWidget *webview) +{ + GtkWidget *win; + + win = g_object_get_data(G_OBJECT(webview), "inspector-window"); + + gtk_widget_show_all(win); + + return TRUE; +} + +static void +setup_inspector(PidginConversation *gtkconv) +{ + GtkWidget *webview = gtkconv->webview; + WebKitWebSettings *settings; + WebKitWebInspector *inspector; + + settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview)); + inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview)); + + g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL); + + g_signal_connect(G_OBJECT(inspector), "inspect-web-view", + G_CALLBACK(create_gtk_window_around_it), gtkconv); + g_signal_connect(G_OBJECT(inspector), "show-window", + G_CALLBACK(show_inspector_window), webview); +} + +static void +remove_inspector(PidginConversation *gtkconv) +{ + GtkWidget *webview = gtkconv->webview; + GtkWidget *win; + WebKitWebSettings *settings; + + win = g_object_get_data(G_OBJECT(webview), "inspector-window"); + gtk_widget_destroy(win); + g_object_set_data(G_OBJECT(webview), "inspector-window", NULL); + + settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview)); + + g_object_set(G_OBJECT(settings), "enable-developer-extras", FALSE, NULL); +} + +static void +conversation_displayed_cb(PidginConversation *gtkconv) +{ + GtkWidget *inspect = NULL; + + inspect = g_object_get_data(G_OBJECT(gtkconv->webview), + "inspector-window"); + if (inspect == NULL) { + setup_inspector(gtkconv); + } +} + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + GList *convs = purple_get_conversations(); + void *gtk_conv_handle = pidgin_conversations_get_handle(); + + purple_signal_connect(gtk_conv_handle, "conversation-displayed", plugin, + PURPLE_CALLBACK(conversation_displayed_cb), NULL); + + while (convs) { + PurpleConversation *conv = (PurpleConversation *)convs->data; + + /* Setup WebKit Inspector */ + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { + setup_inspector(PIDGIN_CONVERSATION(conv)); + } + + convs = convs->next; + } + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin) +{ + GList *convs = purple_get_conversations(); + + while (convs) { + PurpleConversation *conv = (PurpleConversation *)convs->data; + + /* Remove WebKit Inspector */ + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { + remove_inspector(PIDGIN_CONVERSATION(conv)); + } + + convs = convs->next; + } + + return TRUE; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, /**< major version */ + PURPLE_MINOR_VERSION, /**< minor version */ + PURPLE_PLUGIN_STANDARD, /**< type */ + PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + + "gtkwebkit-inspect", /**< id */ + N_("WebKit Development"), /**< name */ + DISPLAY_VERSION, /**< version */ + N_("Enables WebKit Inspector."), /**< summary */ + N_("Enables WebKit's built-in inspector in a " + "conversation window. This may be viewed " + "by right-clicking a WebKit widget and " + "selecting 'Inspect Element'."), /**< description */ + "Elliott Sales de Andrade ", /**< author */ + PURPLE_WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL, /**< actions */ + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(webkit-devel, init_plugin, info) + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/plugins/xmppconsole.c diff -r 3828a61c44da -r 91a46f726cf4 pidgin/smileyparser.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/smileyparser.c Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,169 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include +#include +#include "smileyparser.h" +#include +#include +#include "gtkthemes.h" + +static char * +get_fullpath(const char *filename) +{ + if (g_path_is_absolute(filename)) + return g_strdup(filename); + else + return g_build_path(g_get_current_dir(), filename, NULL); +} + +static void +parse_for_shortcut_plaintext(const char *text, const char *shortcut, const char *file, GString *ret) +{ + const char *tmp = text; + + for (;*tmp;) { + const char *end = strstr(tmp, shortcut); + char *path; + char *escaped_path; + + if (end == NULL) { + g_string_append(ret, tmp); + break; + } + path = get_fullpath(file); + escaped_path = g_markup_escape_text(path, -1); + + g_string_append_len(ret, tmp, end-tmp); + g_string_append_printf(ret,"%s", + shortcut, escaped_path); + g_free(path); + g_free(escaped_path); + g_assert(strlen(tmp) >= strlen(shortcut)); + tmp = end + strlen(shortcut); + } +} + +static char * +parse_for_shortcut(const char *markup, const char *shortcut, const char *file) +{ + GString* ret = g_string_new(""); + char *local_markup = g_strdup(markup); + char *escaped_shortcut = g_markup_escape_text(shortcut, -1); + + char *temp = local_markup; + + for (;*temp;) { + char *end = strchr(temp, '<'); + char *end_of_tag; + + if (!end) { + parse_for_shortcut_plaintext(temp, escaped_shortcut, file, ret); + break; + } + + *end = 0; + parse_for_shortcut_plaintext(temp, escaped_shortcut, file, ret); + *end = '<'; + + /* if this is well-formed, then there should be no '>' within + * the tag. TODO: handle a comment tag better :( */ + end_of_tag = strchr(end, '>'); + if (!end_of_tag) { + g_string_append(ret, end); + break; + } + + g_string_append_len(ret, end, end_of_tag - end + 1); + + temp = end_of_tag + 1; + } + g_free(local_markup); + g_free(escaped_shortcut); + return g_string_free(ret, FALSE); +} + +static char * +parse_for_purple_smiley(const char *markup, PurpleSmiley *smiley) +{ + char *file = purple_smiley_get_full_path(smiley); + char *ret = parse_for_shortcut(markup, purple_smiley_get_shortcut(smiley), file); + g_free(file); + return ret; +} + +static char * +parse_for_smiley_list(const char *markup, GHashTable *smileys) +{ + GHashTableIter iter; + char *key, *value; + char *ret = g_strdup(markup); + + g_hash_table_iter_init(&iter, smileys); + while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) + { + char *temp = parse_for_shortcut(ret, key, value); + g_free(ret); + ret = temp; + } + + return ret; +} + +char * +smiley_parse_markup(const char *markup, const char *proto_id) +{ + GList *smileys = purple_smileys_get_all(); + char *temp = g_strdup(markup), *temp2; + struct smiley_list *list; + const char *proto_name = "default"; + + if (proto_id != NULL) { + PurplePlugin *proto; + proto = purple_find_prpl(proto_id); + proto_name = proto->info->name; + } + + /* unnecessarily slow, but lets manage for now. */ + for (; smileys; smileys = g_list_next(smileys)) { + temp2 = parse_for_purple_smiley(temp, PURPLE_SMILEY(smileys->data)); + g_free(temp); + temp = temp2; + } + + /* now for each theme smiley, observe that this does look nasty */ + if (!current_smiley_theme || !(current_smiley_theme->list)) { + purple_debug_warning("smiley", "theme does not exist\n"); + return temp; + } + + for (list = current_smiley_theme->list; list; list = list->next) { + if (g_str_equal(list->sml, proto_name)) { + temp2 = parse_for_smiley_list(temp, list->files); + g_free(temp); + temp = temp2; + } + } + + return temp; +} + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/smileyparser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/smileyparser.h Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,25 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +char * +smiley_parse_markup(const char *markup, const char *sml); + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/themes/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/themes/Makefile.am Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,6 @@ + +themetemplatedir = $(datadir)/pidgin/theme/conversation +themetemplate_DATA = Template.html + +EXTRA_DIST = $(themetemplate_DATA) + diff -r 3828a61c44da -r 91a46f726cf4 pidgin/themes/Template.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/themes/Template.html Fri Dec 23 08:22:03 2011 +0000 @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + +%@ +
+
+%@ + + diff -r 3828a61c44da -r 91a46f726cf4 po/de.po