diff options
Diffstat (limited to 'gnome-2-22/libempathy-gtk')
110 files changed, 41384 insertions, 0 deletions
diff --git a/gnome-2-22/libempathy-gtk/.gitignore b/gnome-2-22/libempathy-gtk/.gitignore new file mode 100644 index 000000000..3e3f6f051 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/.gitignore @@ -0,0 +1,2 @@ +empathy-gtk-marshal.* +*.gladep diff --git a/gnome-2-22/libempathy-gtk/Makefile.am b/gnome-2-22/libempathy-gtk/Makefile.am new file mode 100644 index 000000000..1c1d5eda7 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/Makefile.am @@ -0,0 +1,211 @@ +AM_CPPFLAGS = \ + -I. \ + -I$(top_srcdir) \ + -DDATADIR=\""$(datadir)"\" \ + $(EMPATHY_CFLAGS) \ + $(WARN_CFLAGS) + +BUILT_SOURCES = \ + empathy-gtk-marshal.h \ + empathy-gtk-marshal.c \ + empathy-gtk-marshal.list \ + empathy-gtk-enum-types.h \ + empathy-gtk-enum-types.c + +lib_LTLIBRARIES = libempathy-gtk.la + +libempathy_gtk_la_SOURCES = \ + ephy-spinner.c ephy-spinner.h \ + empathy-main-window.c \ + empathy-status-icon.c \ + empathy-conf.c \ + empathy-contact-widget.c \ + empathy-contact-dialogs.c \ + empathy-accounts-dialog.c \ + empathy-account-widget.c \ + empathy-account-widget-irc.c \ + empathy-profile-chooser.c \ + empathy-cell-renderer-expander.c \ + empathy-cell-renderer-text.c \ + empathy-cell-renderer-activatable.c \ + empathy-spell.c \ + empathy-spell-dialog.c \ + empathy-contact-list-store.c \ + empathy-contact-list-view.c \ + empathy-preferences.c \ + empathy-theme.c \ + empathy-theme-boxes.c \ + empathy-theme-irc.c \ + empathy-theme-manager.c \ + empathy-smiley-manager.c \ + empathy-chat-window.c \ + empathy-chat.c \ + empathy-chat-view.c \ + empathy-private-chat.c \ + empathy-group-chat.c \ + empathy-geometry.c \ + empathy-presence-chooser.c \ + empathy-about-dialog.c \ + empathy-account-chooser.c \ + empathy-new-chatroom-dialog.c \ + empathy-chatrooms-window.c \ + empathy-log-window.c \ + empathy-call-window.c \ + empathy-avatar-chooser.c \ + empathy-avatar-image.c \ + empathy-ui-utils.c \ + empathy-new-message-dialog.c \ + empathy-irc-network-dialog.c \ + totem-subtitle-encoding.c totem-subtitle-encoding.h + +# do not distribute generated files +nodist_libempathy_gtk_la_SOURCES =\ + $(BUILT_SOURCES) + +libempathy_gtk_la_LIBADD = \ + $(EMPATHY_LIBS) \ + $(top_builddir)/libempathy/libempathy.la + +libempathy_gtk_la_LDFLAGS = \ + -version-info ${LIBEMPATHY_GTK_CURRENT}:${LIBEMPATHY_GTK_REVISION}:${LIBEMPATHY_GTK_AGE} \ + -export-symbols-regex ^empathy_ + +libempathy_gtk_headers = \ + empathy-images.h \ + empathy-main-window.h \ + empathy-status-icon.h \ + empathy-conf.h \ + empathy-contact-widget.h \ + empathy-contact-dialogs.h \ + empathy-accounts-dialog.h \ + empathy-account-widget.h \ + empathy-account-widget-irc.h \ + empathy-profile-chooser.h \ + empathy-cell-renderer-expander.h \ + empathy-cell-renderer-text.h \ + empathy-cell-renderer-activatable.h \ + empathy-spell.h \ + empathy-spell-dialog.h \ + empathy-contact-list-store.h \ + empathy-contact-list-view.h \ + empathy-preferences.h \ + empathy-theme.h \ + empathy-theme-boxes.h \ + empathy-theme-irc.h \ + empathy-theme-manager.h \ + empathy-smiley-manager.h \ + empathy-chat-window.h \ + empathy-chat.h \ + empathy-chat-view.h \ + empathy-private-chat.h \ + empathy-group-chat.h \ + empathy-geometry.h \ + empathy-presence-chooser.h \ + empathy-about-dialog.h \ + empathy-account-chooser.h \ + empathy-new-chatroom-dialog.h \ + empathy-chatrooms-window.h \ + empathy-log-window.h \ + empathy-call-window.h \ + empathy-avatar-chooser.h \ + empathy-avatar-image.h \ + empathy-ui-utils.h \ + empathy-new-message-dialog.h \ + empathy-irc-network-dialog.h + +libempathy_gtk_includedir = $(includedir)/libempathy-gtk/ +libempathy_gtk_include_HEADERS = \ + $(libempathy_gtk_headers) \ + empathy-gtk-enum-types.h + +gladedir = $(datadir)/empathy +glade_DATA = \ + empathy-main-window.glade \ + empathy-status-icon.glade \ + empathy-contact-widget.glade \ + empathy-contact-dialogs.glade \ + empathy-preferences.glade \ + empathy-presence-chooser.glade \ + empathy-accounts-dialog.glade \ + empathy-account-widget-generic.glade \ + empathy-account-widget-jabber.glade \ + empathy-account-widget-msn.glade \ + empathy-account-widget-sip.glade \ + empathy-account-widget-salut.glade \ + empathy-account-widget-irc.glade \ + empathy-account-widget-icq.glade \ + empathy-account-widget-yahoo.glade \ + empathy-new-chatroom-dialog.glade \ + empathy-group-chat.glade \ + empathy-chatrooms-window.glade \ + empathy-spell-dialog.glade \ + empathy-log-window.glade \ + empathy-call-window.glade \ + empathy-chat.glade \ + empathy-new-message-dialog.glade + +empathy-gtk-marshal.list: $(libempathy_gtk_la_SOURCES) Makefile.am + ( cd $(srcdir) && \ + sed -n -e 's/.*empathy_gtk_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \ + $(libempathy_gtk_la_SOURCES) ) \ + | sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp + if cmp -s $@.tmp $@; then \ + rm $@.tmp; \ + else \ + mv $@.tmp $@; \ + fi + +%-marshal.h: %-marshal.list Makefile + $(GLIB_GENMARSHAL) --header --prefix=_$(subst -,_,$*)_marshal $< > $*-marshal.h + +%-marshal.c: %-marshal.list Makefile + echo "#include \"empathy-gtk-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) --body --prefix=_$(subst -,_,$*)_marshal $< >> $*-marshal.c + +empathy-gtk-enum-types.h: stamp-empathy-gtk-enum-types.h + @true +stamp-empathy-gtk-enum-types.h: Makefile $(libempathy_gtk_headers) + (cd $(srcdir) \ + && glib-mkenums \ + --fhead "#ifndef __LIBEMPATHY_GTK_ENUM_TYPES_H__\n" \ + --fhead "#define __LIBEMPATHY_GTK_ENUM_TYPES_H__ 1\n\n" \ + --fhead "#include <glib-object.h>\n\n" \ + --fhead "G_BEGIN_DECLS\n\n" \ + --ftail "G_END_DECLS\n\n" \ + --ftail "#endif /* __LIBEMPATHY_GTK_ENUM_TYPES_H__ */\n" \ + --fprod "#include <libempathy-gtk/@filename@>\n" \ + --eprod "#define EMPATHY_TYPE_@ENUMSHORT@ @enum_name@_get_type()\n" \ + --eprod "GType @enum_name@_get_type (void);\n" \ + $(libempathy_gtk_headers) ) > xgen-gth \ + && (cmp -s xgen-gth empathy-gtk-enum-type.h || cp xgen-gth empathy-gtk-enum-types.h) \ + && rm -f xgen-gth \ + && echo timestamp > $(@F) + +empathy-gtk-enum-types.c: Makefile $(libempathy_gtk_headers) + (cd $(srcdir) \ + && glib-mkenums \ + --fhead "#include <config.h>\n" \ + --fhead "#include <glib-object.h>\n" \ + --fhead "#include \"empathy-gtk-enum-types.h\"\n\n" \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "static const G@Type@Value _@enum_name@_values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n};\n\n" \ + --vtail "GType\n@enum_name@_get_type (void)\n{\n" \ + --vtail " static GType type = 0;\n\n" \ + --vtail " if (!type)\n" \ + --vtail " type = g_@type@_register_static (\"@EnumName@\", _@enum_name@_values);\n\n" \ + --vtail " return type;\n}\n\n" \ + $(libempathy_gtk_headers) ) > xgen-gtc \ + && cp xgen-gtc $(@F) \ + && rm -f xgen-gtc + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libempathy-gtk.pc + +EXTRA_DIST = \ + $(glade_DATA) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + stamp-empathy-gtk-enum-types.h diff --git a/gnome-2-22/libempathy-gtk/empathy-about-dialog.c b/gnome-2-22/libempathy-gtk/empathy-about-dialog.c new file mode 100644 index 000000000..5ecf66749 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-about-dialog.c @@ -0,0 +1,113 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <gtk/gtkaboutdialog.h> +#include <gtk/gtksizegroup.h> +#include <glade/glade.h> + +#include "empathy-about-dialog.h" +#include "empathy-ui-utils.h" + +#define WEB_SITE "http://live.gnome.org/Empathy" + +static void about_dialog_activate_link_cb (GtkAboutDialog *about, + const gchar *link, + gpointer data); + +static const char *authors[] = { + "Mikael Hallendal", + "Richard Hult", + "Martyn Russell", + "Geert-Jan Van den Bogaerde", + "Kevin Dougherty", + "Eitan Isaacson", + "Xavier Claessens", + NULL +}; + +static const char *documenters[] = { + NULL +}; + +static const char *artists[] = { + "Andreas Nilsson <nisses.mail@home.se>", + "Vinicius Depizzol <vdepizzol@gmail.com>", + NULL +}; + +static const char *license[] = { + N_("Empathy 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."), + N_("Empathy 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."), + N_("You should have received a copy of the GNU General Public License " + "along with Empathy; if not, write to the Free Software Foundation, Inc., " + "51 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA") +}; + +static void +about_dialog_activate_link_cb (GtkAboutDialog *about, + const gchar *link, + gpointer data) +{ + empathy_url_show (link); +} + +void +empathy_about_dialog_new (GtkWindow *parent) +{ + gchar *license_trans; + + gtk_about_dialog_set_url_hook (about_dialog_activate_link_cb, NULL, NULL); + + license_trans = g_strconcat (_(license[0]), "\n\n", + _(license[1]), "\n\n", + _(license[2]), "\n\n", + NULL); + + gtk_show_about_dialog (parent, + "artists", artists, + "authors", authors, + "comments", _("An Instant Messaging client for GNOME"), + "license", license_trans, + "wrap-license", TRUE, + "copyright", "Imendio AB 2002-2007\nCollabora Ltd 2007", + "documenters", documenters, + "logo-icon-name", "empathy", + "translator-credits", _("translator-credits"), + "version", PACKAGE_VERSION, + "website", WEB_SITE, + NULL); + + g_free (license_trans); +} + + diff --git a/gnome-2-22/libempathy-gtk/empathy-about-dialog.h b/gnome-2-22/libempathy-gtk/empathy-about-dialog.h new file mode 100644 index 000000000..ff123ab20 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-about-dialog.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_ABOUT_DIALOG_H__ +#define __EMPATHY_ABOUT_DIALOG_H__ + +#include <gtk/gtkwindow.h> + +G_BEGIN_DECLS + +void empathy_about_dialog_new (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_ABOUT_DIALOG_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-account-chooser.c b/gnome-2-22/libempathy-gtk/empathy-account-chooser.c new file mode 100644 index 000000000..b6f6542d7 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-chooser.c @@ -0,0 +1,677 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libtelepathy/tp-conn.h> +#include <libmissioncontrol/mc-account-monitor.h> +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-utils.h> + +#include "empathy-ui-utils.h" +#include "empathy-account-chooser.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserPriv)) + +typedef struct { + MissionControl *mc; + McAccountMonitor *monitor; + gboolean set_active_item; + gboolean has_all_option; + EmpathyAccountChooserFilterFunc filter; + gpointer filter_data; +} EmpathyAccountChooserPriv; + +typedef struct { + EmpathyAccountChooser *chooser; + McAccount *account; + gboolean set; +} SetAccountData; + +enum { + COL_ACCOUNT_IMAGE, + COL_ACCOUNT_TEXT, + COL_ACCOUNT_ENABLED, /* Usually tied to connected state */ + COL_ACCOUNT_POINTER, + COL_ACCOUNT_COUNT +}; + +static void account_chooser_finalize (GObject *object); +static void account_chooser_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void account_chooser_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void account_chooser_setup (EmpathyAccountChooser *chooser); +static void account_chooser_account_created_cb (McAccountMonitor *monitor, + const gchar *unique_name, + EmpathyAccountChooser *chooser); +static void account_chooser_account_add_foreach (McAccount *account, + EmpathyAccountChooser *chooser); +static void account_chooser_account_deleted_cb (McAccountMonitor *monitor, + const gchar *unique_name, + EmpathyAccountChooser *chooser); +static void account_chooser_account_remove_foreach (McAccount *account, + EmpathyAccountChooser *chooser); +static void account_chooser_update_iter (EmpathyAccountChooser *chooser, + GtkTreeIter *iter); +static void account_chooser_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyAccountChooser *chooser); +static gboolean account_chooser_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyAccountChooser *chooser); +static gboolean account_chooser_set_account_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + SetAccountData *data); + +enum { + PROP_0, + PROP_HAS_ALL_OPTION, +}; + +G_DEFINE_TYPE (EmpathyAccountChooser, empathy_account_chooser, GTK_TYPE_COMBO_BOX); + +static void +empathy_account_chooser_class_init (EmpathyAccountChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = account_chooser_finalize; + object_class->get_property = account_chooser_get_property; + object_class->set_property = account_chooser_set_property; + + g_object_class_install_property (object_class, + PROP_HAS_ALL_OPTION, + g_param_spec_boolean ("has-all-option", + "Has All Option", + "Have a separate option in the list to mean ALL accounts", + FALSE, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyAccountChooserPriv)); +} + +static void +empathy_account_chooser_init (EmpathyAccountChooser *chooser) +{ + EmpathyAccountChooserPriv *priv = GET_PRIV (chooser); + + priv->set_active_item = FALSE; + priv->filter = NULL; + priv->filter_data = NULL; +} + +static void +account_chooser_finalize (GObject *object) +{ + EmpathyAccountChooser *chooser; + EmpathyAccountChooserPriv *priv; + + chooser = EMPATHY_ACCOUNT_CHOOSER (object); + priv = GET_PRIV (object); + + g_signal_handlers_disconnect_by_func (priv->monitor, + account_chooser_account_created_cb, + chooser); + g_signal_handlers_disconnect_by_func (priv->monitor, + account_chooser_account_deleted_cb, + chooser); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), + "AccountStatusChanged", + G_CALLBACK (account_chooser_status_changed_cb), + chooser); + g_object_unref (priv->mc); + g_object_unref (priv->monitor); + + G_OBJECT_CLASS (empathy_account_chooser_parent_class)->finalize (object); +} + +static void +account_chooser_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyAccountChooserPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_HAS_ALL_OPTION: + g_value_set_boolean (value, priv->has_all_option); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +account_chooser_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyAccountChooserPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_HAS_ALL_OPTION: + empathy_account_chooser_set_has_all_option (EMPATHY_ACCOUNT_CHOOSER (object), + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +GtkWidget * +empathy_account_chooser_new (void) +{ + EmpathyAccountChooserPriv *priv; + McAccountMonitor *monitor; + GtkWidget *chooser; + + monitor = mc_account_monitor_new (); + chooser = g_object_new (EMPATHY_TYPE_ACCOUNT_CHOOSER, NULL); + + priv = GET_PRIV (chooser); + + priv->mc = empathy_mission_control_new (); + priv->monitor = mc_account_monitor_new (); + + g_signal_connect (priv->monitor, "account-created", + G_CALLBACK (account_chooser_account_created_cb), + chooser); + g_signal_connect (priv->monitor, "account-deleted", + G_CALLBACK (account_chooser_account_deleted_cb), + chooser); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged", + G_CALLBACK (account_chooser_status_changed_cb), + chooser, NULL); + + account_chooser_setup (EMPATHY_ACCOUNT_CHOOSER (chooser)); + + return chooser; +} + +McAccount * +empathy_account_chooser_get_account (EmpathyAccountChooser *chooser) +{ + EmpathyAccountChooserPriv *priv; + McAccount *account; + GtkTreeModel *model; + GtkTreeIter iter; + + g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), NULL); + + priv = GET_PRIV (chooser); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser)); + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser), &iter); + + gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1); + + return account; +} + +gboolean +empathy_account_chooser_set_account (EmpathyAccountChooser *chooser, + McAccount *account) +{ + GtkComboBox *combobox; + GtkTreeModel *model; + GtkTreeIter iter; + SetAccountData data; + + g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE); + + combobox = GTK_COMBO_BOX (chooser); + model = gtk_combo_box_get_model (combobox); + gtk_combo_box_get_active_iter (combobox, &iter); + + data.chooser = chooser; + data.account = account; + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) account_chooser_set_account_foreach, + &data); + + return data.set; +} + +gboolean +empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser) +{ + EmpathyAccountChooserPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE); + + priv = GET_PRIV (chooser); + + return priv->has_all_option; +} + +void +empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser, + gboolean has_all_option) +{ + EmpathyAccountChooserPriv *priv; + GtkComboBox *combobox; + GtkListStore *store; + GtkTreeModel *model; + GtkTreeIter iter; + + g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser)); + + priv = GET_PRIV (chooser); + + if (priv->has_all_option == has_all_option) { + return; + } + + combobox = GTK_COMBO_BOX (chooser); + model = gtk_combo_box_get_model (combobox); + store = GTK_LIST_STORE (model); + + priv->has_all_option = has_all_option; + + /* + * The first 2 options are the ALL and separator + */ + + if (has_all_option) { + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser), + (GtkTreeViewRowSeparatorFunc) + account_chooser_separator_func, + chooser, + NULL); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_ACCOUNT_TEXT, NULL, + COL_ACCOUNT_ENABLED, TRUE, + COL_ACCOUNT_POINTER, NULL, + -1); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_ACCOUNT_TEXT, _("All"), + COL_ACCOUNT_ENABLED, TRUE, + COL_ACCOUNT_POINTER, NULL, + -1); + } else { + if (gtk_tree_model_get_iter_first (model, &iter)) { + if (gtk_list_store_remove (GTK_LIST_STORE (model), &iter)) { + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + } + } + + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser), + (GtkTreeViewRowSeparatorFunc) + NULL, + NULL, + NULL); + } + + g_object_notify (G_OBJECT (chooser), "has-all-option"); +} + +static void +account_chooser_setup (EmpathyAccountChooser *chooser) +{ + EmpathyAccountChooserPriv *priv; + GList *accounts; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkComboBox *combobox; + + priv = GET_PRIV (chooser); + + /* Set up combo box with new store */ + combobox = GTK_COMBO_BOX (chooser); + + gtk_cell_layout_clear (GTK_CELL_LAYOUT (combobox)); + + store = gtk_list_store_new (COL_ACCOUNT_COUNT, + G_TYPE_STRING, /* Image */ + G_TYPE_STRING, /* Name */ + G_TYPE_BOOLEAN, /* Enabled */ + MC_TYPE_ACCOUNT); + + gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, + "icon-name", COL_ACCOUNT_IMAGE, + "sensitive", COL_ACCOUNT_ENABLED, + NULL); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer, + "text", COL_ACCOUNT_TEXT, + "sensitive", COL_ACCOUNT_ENABLED, + NULL); + + /* Populate accounts */ + accounts = mc_accounts_list (); + g_list_foreach (accounts, + (GFunc) account_chooser_account_add_foreach, + chooser); + + mc_accounts_list_free (accounts); + g_object_unref (store); +} + +static void +account_chooser_account_created_cb (McAccountMonitor *monitor, + const gchar *unique_name, + EmpathyAccountChooser *chooser) +{ + McAccount *account; + + account = mc_account_lookup (unique_name); + account_chooser_account_add_foreach (account, chooser); + g_object_unref (account); +} + +static void +account_chooser_account_add_foreach (McAccount *account, + EmpathyAccountChooser *chooser) +{ + GtkListStore *store; + GtkComboBox *combobox; + GtkTreeIter iter; + gint position; + + combobox = GTK_COMBO_BOX (chooser); + store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox)); + + position = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); + gtk_list_store_insert_with_values (store, &iter, position, + COL_ACCOUNT_POINTER, account, + -1); + account_chooser_update_iter (chooser, &iter); +} + +static void +account_chooser_account_deleted_cb (McAccountMonitor *monitor, + const gchar *unique_name, + EmpathyAccountChooser *chooser) +{ + McAccount *account; + + account = mc_account_lookup (unique_name); + account_chooser_account_remove_foreach (account, chooser); + g_object_unref (account); +} + +typedef struct { + McAccount *account; + GtkTreeIter *iter; + gboolean found; +} FindAccountData; + +static gboolean +account_chooser_find_account_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + FindAccountData *data = user_data; + McAccount *account; + + gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1); + + if (empathy_account_equal (account, data->account)) { + data->found = TRUE; + *(data->iter) = *iter; + g_object_unref (account); + + return TRUE; + } + + g_object_unref (account); + + return FALSE; +} + +static gboolean +account_chooser_find_account (EmpathyAccountChooser *chooser, + McAccount *account, + GtkTreeIter *iter) +{ + GtkListStore *store; + GtkComboBox *combobox; + FindAccountData data; + + combobox = GTK_COMBO_BOX (chooser); + store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox)); + + data.account = account; + data.iter = iter; + gtk_tree_model_foreach (GTK_TREE_MODEL (store), + account_chooser_find_account_foreach, + &data); + + return data.found; +} + +static void +account_chooser_account_remove_foreach (McAccount *account, + EmpathyAccountChooser *chooser) +{ + GtkListStore *store; + GtkComboBox *combobox; + GtkTreeIter iter; + + combobox = GTK_COMBO_BOX (chooser); + store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox)); + + if (account_chooser_find_account (chooser, account, &iter)) { + gtk_list_store_remove (store, &iter); + } +} + +static void +account_chooser_update_iter (EmpathyAccountChooser *chooser, + GtkTreeIter *iter) +{ + EmpathyAccountChooserPriv *priv; + GtkListStore *store; + GtkComboBox *combobox; + McAccount *account; + const gchar *icon_name; + gboolean is_enabled = TRUE; + + priv = GET_PRIV (chooser); + + combobox = GTK_COMBO_BOX (chooser); + store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox)); + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + COL_ACCOUNT_POINTER, &account, + -1); + + icon_name = empathy_icon_name_from_account (account); + if (priv->filter) { + is_enabled = priv->filter (account, priv->filter_data); + } + + gtk_list_store_set (store, iter, + COL_ACCOUNT_IMAGE, icon_name, + COL_ACCOUNT_TEXT, mc_account_get_display_name (account), + COL_ACCOUNT_ENABLED, is_enabled, + -1); + + /* set first connected account as active account */ + if (priv->set_active_item == FALSE && is_enabled) { + priv->set_active_item = TRUE; + gtk_combo_box_set_active_iter (combobox, iter); + } + + g_object_unref (account); +} + +static void +account_chooser_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyAccountChooser *chooser) +{ + McAccount *account; + GtkTreeIter iter; + + account = mc_account_lookup (unique_name); + if (account_chooser_find_account (chooser, account, &iter)) { + account_chooser_update_iter (chooser, &iter); + } + g_object_unref (account); +} + +static gboolean +account_chooser_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyAccountChooser *chooser) +{ + EmpathyAccountChooserPriv *priv; + gchar *text; + gboolean is_separator; + + priv = GET_PRIV (chooser); + + if (!priv->has_all_option) { + return FALSE; + } + + gtk_tree_model_get (model, iter, COL_ACCOUNT_TEXT, &text, -1); + is_separator = text == NULL; + g_free (text); + + return is_separator; +} + +static gboolean +account_chooser_set_account_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + SetAccountData *data) +{ + McAccount *account; + gboolean equal; + + gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1); + + /* Special case so we can make it possible to select the All option */ + if (!data->account && !account) { + equal = TRUE; + } + else if ((data->account && !account) || (!data->account && account)) { + equal = FALSE; + } else { + equal = empathy_account_equal (data->account, account); + g_object_unref (account); + } + + if (equal) { + GtkComboBox *combobox; + + combobox = GTK_COMBO_BOX (data->chooser); + gtk_combo_box_set_active_iter (combobox, iter); + + data->set = TRUE; + } + + return equal; +} + +static gboolean +account_chooser_filter_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer chooser) +{ + account_chooser_update_iter (chooser, iter); + return FALSE; +} + +void +empathy_account_chooser_set_filter (EmpathyAccountChooser *chooser, + EmpathyAccountChooserFilterFunc filter, + gpointer user_data) +{ + EmpathyAccountChooserPriv *priv; + GtkTreeModel *model; + + g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser)); + + priv = GET_PRIV (chooser); + + priv->filter = filter; + priv->filter_data = user_data; + + /* Refilter existing data */ + priv->set_active_item = FALSE; + model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser)); + gtk_tree_model_foreach (model, account_chooser_filter_foreach, chooser); +} + +gboolean +empathy_account_chooser_filter_is_connected (McAccount *account, + gpointer user_data) +{ + MissionControl *mc; + TpConnectionStatus status; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), FALSE); + + mc = empathy_mission_control_new (); + status = mission_control_get_connection_status (mc, account, NULL); + g_object_unref (mc); + + return status == TP_CONNECTION_STATUS_CONNECTED; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-account-chooser.h b/gnome-2-22/libempathy-gtk/empathy-account-chooser.h new file mode 100644 index 000000000..528bd83f5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-chooser.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_ACCOUNT_CHOOSER_H__ +#define __EMPATHY_ACCOUNT_CHOOSER_H__ + +#include <gtk/gtkcombobox.h> + +#include <libmissioncontrol/mc-account.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_ACCOUNT_CHOOSER (empathy_account_chooser_get_type ()) +#define EMPATHY_ACCOUNT_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooser)) +#define EMPATHY_ACCOUNT_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserClass)) +#define EMPATHY_IS_ACCOUNT_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_ACCOUNT_CHOOSER)) +#define EMPATHY_IS_ACCOUNT_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_ACCOUNT_CHOOSER)) +#define EMPATHY_ACCOUNT_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserClass)) + +typedef gboolean (* EmpathyAccountChooserFilterFunc) (McAccount *account, + gpointer user_data); + + +typedef struct _EmpathyAccountChooser EmpathyAccountChooser; +typedef struct _EmpathyAccountChooserClass EmpathyAccountChooserClass; + +struct _EmpathyAccountChooser { + GtkComboBox parent; +}; + +struct _EmpathyAccountChooserClass { + GtkComboBoxClass parent_class; +}; + +GType empathy_account_chooser_get_type (void) G_GNUC_CONST; +GtkWidget * empathy_account_chooser_new (void); +McAccount * empathy_account_chooser_get_account (EmpathyAccountChooser *chooser); +gboolean empathy_account_chooser_set_account (EmpathyAccountChooser *chooser, + McAccount *account); +gboolean empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser); +void empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser, + gboolean has_all_option); +void empathy_account_chooser_set_filter (EmpathyAccountChooser *chooser, + EmpathyAccountChooserFilterFunc filter, + gpointer user_data); +gboolean empathy_account_chooser_filter_is_connected(McAccount *account, + gpointer user_data); + +G_END_DECLS + +#endif /* __EMPATHY_ACCOUNT_CHOOSER_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-generic.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-generic.glade new file mode 100644 index 000000000..5278e0a2b --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-generic.glade @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkWindow" id="account_generic_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">generic account settings</property> + <property name="resizable">False</property> + <child> + <widget class="GtkVBox" id="vbox_generic_settings"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkTable" id="table_common_settings"> + <property name="visible">True</property> + <property name="n_columns">3</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <child> + <widget class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_columns">3</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-icq.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-icq.glade new file mode 100644 index 000000000..235587bd8 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-icq.glade @@ -0,0 +1,375 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_icq_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">ICQ account settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_icq_settings"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_common_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkEntry" id="entry_password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_uin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_forget"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Forget password and clear the entry.</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image834"> + <property name="visible">True</property> + <property name="stock">gtk-clear</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_uin"> + <property name="visible">True</property> + <property name="label" translatable="yes">ICQ _UIN:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_uin</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="label" translatable="yes">Pass_word:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_password</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkEntry" id="entry_charset"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_port"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">spinbutton_port</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="spinbutton_port"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">5222 0 65556 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_charset"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Charset:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_charset</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.c b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.c new file mode 100644 index 000000000..5c74e0958 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.c @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2007-2008 Guillaume Desmottes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Guillaume Desmottes <gdesmott@gnome.org> + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-protocol.h> + +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-irc-network-manager.h> + +#include "empathy-irc-network-dialog.h" +#include "empathy-account-widget.h" +#include "empathy-account-widget-irc.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "AccountWidgetIRC" + +#define IRC_NETWORKS_FILENAME "irc-networks.xml" + +typedef struct { + McAccount *account; + EmpathyIrcNetworkManager *network_manager; + + GtkWidget *vbox_settings; + + GtkWidget *combobox_network; + GtkWidget *button_add_network; + GtkWidget *button_network; + GtkWidget *button_remove; +} EmpathyAccountWidgetIrc; + +enum { + COL_NETWORK_OBJ, + COL_NETWORK_NAME, +}; + +static void +account_widget_irc_destroy_cb (GtkWidget *widget, + EmpathyAccountWidgetIrc *settings) +{ + g_object_unref (settings->network_manager); + g_object_unref (settings->account); + g_slice_free (EmpathyAccountWidgetIrc, settings); +} + +static void +unset_server_params (EmpathyAccountWidgetIrc *settings) +{ + empathy_debug (DEBUG_DOMAIN, "Unset server, port and use-ssl"); + mc_account_unset_param (settings->account, "server"); + mc_account_unset_param (settings->account, "port"); + mc_account_unset_param (settings->account, "use-ssl"); +} + +static void +update_server_params (EmpathyAccountWidgetIrc *settings) +{ + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyIrcNetwork *network; + GSList *servers; + gchar *charset; + + if (!gtk_combo_box_get_active_iter ( + GTK_COMBO_BOX (settings->combobox_network), &iter)) + { + unset_server_params (settings); + return; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + gtk_tree_model_get (model, &iter, COL_NETWORK_OBJ, &network, -1); + + g_assert (network != NULL); + + g_object_get (network, "charset", &charset, NULL); + empathy_debug (DEBUG_DOMAIN, "Setting charset to %s", charset); + mc_account_set_param_string (settings->account, "charset", charset); + g_free (charset); + + servers = empathy_irc_network_get_servers (network); + if (g_slist_length (servers) > 0) + { + /* set the first server as CM server */ + EmpathyIrcServer *server = servers->data; + gchar *address; + guint port; + gboolean ssl; + + g_object_get (server, + "address", &address, + "port", &port, + "ssl", &ssl, + NULL); + + empathy_debug (DEBUG_DOMAIN, "Setting server to %s", address); + mc_account_set_param_string (settings->account, "server", address); + empathy_debug (DEBUG_DOMAIN, "Setting port to %u", port); + mc_account_set_param_int (settings->account, "port", port); + empathy_debug (DEBUG_DOMAIN, "Setting use-ssl to %s", + ssl ? "TRUE": "FALSE" ); + mc_account_set_param_boolean (settings->account, "use-ssl", ssl); + + g_free (address); + } + else + { + /* No server. Unset values */ + unset_server_params (settings); + } + + g_slist_foreach (servers, (GFunc) g_object_unref, NULL); + g_slist_free (servers); + g_object_unref (network); +} + +static void +irc_network_dialog_destroy_cb (GtkWidget *widget, + EmpathyAccountWidgetIrc *settings) +{ + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyIrcNetwork *network; + gchar *name; + + /* name could be changed */ + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (settings->combobox_network), + &iter); + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + gtk_tree_model_get (model, &iter, COL_NETWORK_OBJ, &network, -1); + + g_object_get (network, "name", &name, NULL); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_NETWORK_NAME, name, -1); + + update_server_params (settings); + + g_object_unref (network); + g_free (name); +} + +static void +display_irc_network_dialog (EmpathyAccountWidgetIrc *settings, + EmpathyIrcNetwork *network) +{ + GtkWindow *window; + GtkWidget *dialog; + + window = empathy_get_toplevel_window (settings->vbox_settings); + dialog = empathy_irc_network_dialog_show (network, GTK_WIDGET (window)); + g_signal_connect (dialog, "destroy", + G_CALLBACK (irc_network_dialog_destroy_cb), settings); +} + +static void +account_widget_irc_button_edit_network_clicked_cb ( + GtkWidget *button, + EmpathyAccountWidgetIrc *settings) +{ + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyIrcNetwork *network; + + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (settings->combobox_network), + &iter); + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + gtk_tree_model_get (model, &iter, COL_NETWORK_OBJ, &network, -1); + + g_assert (network != NULL); + + display_irc_network_dialog (settings, network); + + g_object_unref (network); +} + +static void +account_widget_irc_button_remove_clicked_cb (GtkWidget *button, + EmpathyAccountWidgetIrc *settings) +{ + EmpathyIrcNetwork *network; + GtkTreeIter iter; + GtkTreeModel *model; + gchar *name; + + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (settings->combobox_network), + &iter); + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + gtk_tree_model_get (model, &iter, COL_NETWORK_OBJ, &network, -1); + + g_assert (network != NULL); + + g_object_get (network, "name", &name, NULL); + empathy_debug (DEBUG_DOMAIN, "Remove network %s", name); + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + empathy_irc_network_manager_remove (settings->network_manager, network); + + /* Select the first network */ + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (settings->combobox_network), &iter); + } + + g_free (name); + g_object_unref (network); +} + +static void +account_widget_irc_button_add_network_clicked_cb (GtkWidget *button, + EmpathyAccountWidgetIrc *settings) +{ + EmpathyIrcNetwork *network; + GtkTreeModel *model; + GtkListStore *store; + gchar *name; + GtkTreeIter iter; + + network = empathy_irc_network_new (_("New Network")); + empathy_irc_network_manager_add (settings->network_manager, network); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + store = GTK_LIST_STORE (model); + + g_object_get (network, "name", &name, NULL); + + gtk_list_store_insert_with_values (store, &iter, -1, + COL_NETWORK_OBJ, network, + COL_NETWORK_NAME, name, + -1); + + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (settings->combobox_network), + &iter); + + display_irc_network_dialog (settings, network); + + g_free (name); + g_object_unref (network); +} + +static void +account_widget_irc_combobox_network_changed_cb (GtkWidget *combobox, + EmpathyAccountWidgetIrc *settings) +{ + update_server_params (settings); +} + +static void +fill_networks_model (EmpathyAccountWidgetIrc *settings, + EmpathyIrcNetwork *network_to_select) +{ + GSList *networks, *l; + GtkTreeModel *model; + GtkListStore *store; + + networks = empathy_irc_network_manager_get_networks ( + settings->network_manager); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (settings->combobox_network)); + store = GTK_LIST_STORE (model); + + for (l = networks; l != NULL; l = g_slist_next (l)) + { + gchar *name; + EmpathyIrcNetwork *network = l->data; + GtkTreeIter iter; + + g_object_get (network, "name", &name, NULL); + + gtk_list_store_insert_with_values (store, &iter, -1, + COL_NETWORK_OBJ, network, + COL_NETWORK_NAME, name, + -1); + + if (network == network_to_select) + { + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (settings->combobox_network), &iter); + } + + g_free (name); + g_object_unref (network); + } + + if (network_to_select == NULL) + { + /* Select the first network */ + GtkTreeIter iter; + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (settings->combobox_network), &iter); + + update_server_params (settings); + } + } + + g_slist_free (networks); +} + +static void +account_widget_irc_setup (EmpathyAccountWidgetIrc *settings) +{ + gchar *nick = NULL; + gchar *fullname = NULL; + gchar *server = NULL; + gint port = 6667; + gchar *charset; + gboolean ssl = FALSE; + EmpathyIrcNetwork *network = NULL; + + mc_account_get_param_string (settings->account, "account", &nick); + mc_account_get_param_string (settings->account, "fullname", &fullname); + mc_account_get_param_string (settings->account, "server", &server); + mc_account_get_param_string (settings->account, "charset", &charset); + mc_account_get_param_int (settings->account, "port", &port); + mc_account_get_param_boolean (settings->account, "use-ssl", &ssl); + + if (!nick) + { + nick = g_strdup (g_get_user_name ()); + mc_account_set_param_string (settings->account, "account", nick); + } + + if (!fullname) + { + fullname = g_strdup (g_get_real_name ()); + if (!fullname) + { + fullname = g_strdup (nick); + } + mc_account_set_param_string (settings->account, "fullname", fullname); + } + + if (server != NULL) + { + GtkListStore *store; + + network = empathy_irc_network_manager_find_network_by_address ( + settings->network_manager, server); + + + store = GTK_LIST_STORE (gtk_combo_box_get_model ( + GTK_COMBO_BOX (settings->combobox_network))); + + if (network != NULL) + { + gchar *name; + + g_object_set (network, "charset", charset, NULL); + + g_object_get (network, "name", &name, NULL); + empathy_debug (DEBUG_DOMAIN, "Account use network %s", name); + + g_free (name); + } + else + { + /* We don't have this network. Let's create it */ + EmpathyIrcServer *srv; + GtkTreeIter iter; + + empathy_debug (DEBUG_DOMAIN, "Create a network %s", server); + network = empathy_irc_network_new (server); + srv = empathy_irc_server_new (server, port, ssl); + + empathy_irc_network_append_server (network, srv); + empathy_irc_network_manager_add (settings->network_manager, network); + + gtk_list_store_insert_with_values (store, &iter, -1, + COL_NETWORK_OBJ, network, + COL_NETWORK_NAME, server, + -1); + + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (settings->combobox_network), &iter); + + g_object_unref (srv); + g_object_unref (network); + } + } + + + fill_networks_model (settings, network); + + g_free (nick); + g_free (fullname); + g_free (server); + g_free (charset); +} + +/** + * empathy_account_widget_irc_new: + * @account: the #McAccount to configure + * + * Creates a new IRC account widget to configure a given #McAccount + * + * Returns: The toplevel container of the configuration widget + */ +GtkWidget * +empathy_account_widget_irc_new (McAccount *account) +{ + EmpathyAccountWidgetIrc *settings; + gchar *dir, *user_file_with_path, *global_file_with_path; + GladeXML *glade; + GtkListStore *store; + GtkCellRenderer *renderer; + + settings = g_slice_new0 (EmpathyAccountWidgetIrc); + settings->account = g_object_ref (account); + + dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL); + g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR); + user_file_with_path = g_build_filename (dir, IRC_NETWORKS_FILENAME, NULL); + g_free (dir); + + global_file_with_path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), + "libempathy-gtk", IRC_NETWORKS_FILENAME, NULL); + if (!g_file_test (global_file_with_path, G_FILE_TEST_EXISTS)) + { + g_free (global_file_with_path); + global_file_with_path = g_build_filename (DATADIR, "empathy", + IRC_NETWORKS_FILENAME, NULL); + } + + settings->network_manager = empathy_irc_network_manager_new ( + global_file_with_path, + user_file_with_path); + + g_free (global_file_with_path); + g_free (user_file_with_path); + + glade = empathy_glade_get_file ("empathy-account-widget-irc.glade", + "vbox_irc_settings", + NULL, + "vbox_irc_settings", &settings->vbox_settings, + "combobox_network", &settings->combobox_network, + "button_network", &settings->button_network, + "button_add_network", &settings->button_add_network, + "button_remove", &settings->button_remove, + NULL); + + /* Fill the networks combobox */ + store = gtk_list_store_new (2, G_TYPE_OBJECT, G_TYPE_STRING); + + gtk_cell_layout_clear (GTK_CELL_LAYOUT (settings->combobox_network)); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (settings->combobox_network), + renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (settings->combobox_network), + renderer, + "text", COL_NETWORK_NAME, + NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_NETWORK_NAME, + GTK_SORT_ASCENDING); + + gtk_combo_box_set_model (GTK_COMBO_BOX (settings->combobox_network), + GTK_TREE_MODEL (store)); + g_object_unref (store); + + account_widget_irc_setup (settings); + + empathy_account_widget_handle_params (account, glade, + "entry_nick", "account", + "entry_fullname", "fullname", + "entry_password", "password", + "entry_quit_message", "quit-message", + NULL); + + empathy_glade_connect (glade, settings, + "vbox_irc_settings", "destroy", account_widget_irc_destroy_cb, + "button_network", "clicked", account_widget_irc_button_edit_network_clicked_cb, + "button_add_network", "clicked", account_widget_irc_button_add_network_clicked_cb, + "button_remove", "clicked", account_widget_irc_button_remove_clicked_cb, + "combobox_network", "changed", account_widget_irc_combobox_network_changed_cb, + NULL); + + g_object_unref (glade); + + return settings->vbox_settings; +} diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.glade new file mode 100644 index 000000000..14c757ce8 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.glade @@ -0,0 +1,451 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.0 on Sun Feb 17 01:08:25 2008 --> +<glade-interface> + <widget class="GtkWindow" id="account_irc_settings"> + <property name="title" translatable="yes">irc account settings</property> + <property name="resizable">False</property> + <child> + <widget class="GtkTable" id="vbox_irc_settings"> + <property name="visible">True</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkLabel" id="label_network"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Network:</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox174"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkComboBox" id="combobox_network"> + <property name="visible">True</property> + <property name="items" translatable="yes"></property> + </widget> + </child> + <child> + <widget class="GtkButton" id="button_add_network"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Create a new IRC network</property> + <property name="response_id">0</property> + <child> + <widget class="GtkImage" id="image2"> + <property name="visible">True</property> + <property name="stock">gtk-add</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_network"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Edit the selected IRC network</property> + <property name="response_id">0</property> + <child> + <widget class="GtkImage" id="image834"> + <property name="visible">True</property> + <property name="stock">gtk-edit</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Remove the selected IRC network</property> + <property name="response_id">0</property> + <child> + <widget class="GtkImage" id="image835"> + <property name="visible">True</property> + <property name="stock">gtk-remove</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_nick"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Nickname:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">entry_nick</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="entry_nick"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_fullname"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Real name:</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="entry_password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">*</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="entry_fullname"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_quit_message"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Quit message:</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="entry_quit_message"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkDialog" id="irc_network_dialog"> + <property name="visible">True</property> + <property name="border_width">5</property> + <property name="title" translatable="yes">Network</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="icon_name">gtk-edit</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="skip_taskbar_hint">True</property> + <property name="skip_pager_hint">True</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox10"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkFrame" id="frame14"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment28"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkTable" id="table14"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkComboBox" id="combobox_charset"> + <property name="visible">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="entry_network"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_charset"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Charset:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_network"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Network:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label_network"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Network</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="frame15"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment29"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkTable" id="table15"> + <property name="visible">True</property> + <property name="n_rows">1</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="spacing">6</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <child> + <widget class="GtkButton" id="button_add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="button_remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_up"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-go-up</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_down"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-go-down</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow19"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTreeView" id="treeview_servers"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="enable_search">False</property> + </widget> + </child> + </widget> + <packing> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label_servers"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Servers</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area10"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="button_close"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.h b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.h new file mode 100644 index 000000000..6ead048eb --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-irc.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007-2008 Guillaume Desmottes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Guillaume Desmottes <gdesmott@gnome.org> + */ + +#ifndef __EMPATHY_ACCOUNT_WIDGET_IRC_H__ +#define __EMPATHY_ACCOUNT_WIDGET_IRC_H__ + +#include <gtk/gtkwidget.h> +#include <libmissioncontrol/mc-account.h> + +G_BEGIN_DECLS + +GtkWidget * empathy_account_widget_irc_new (McAccount *account); + +G_END_DECLS + +#endif /* __EMPATHY_ACCOUNT_WIDGET_IRC_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-jabber.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-jabber.glade new file mode 100644 index 000000000..d691d1418 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-jabber.glade @@ -0,0 +1,566 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_jabber_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">jabber account settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_jabber_settings"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_common_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="label" translatable="yes">Pass_word:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_password</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_forget"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Forget password and clear the entry.</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image834"> + <property name="visible">True</property> + <property name="stock">gtk-clear</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_id"> + <property name="visible">True</property> + <property name="label" translatable="yes">Login I_D:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_id</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_id"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_encryption"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Encryption required (TLS/SSL)</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_resource"> + <property name="visible">True</property> + <property name="label" translatable="yes">Reso_urce:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_resource</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_priority"> + <property name="visible">True</property> + <property name="label" translatable="yes">Pri_ority:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">spinbutton_priority</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_ignore_ssl_errors"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Ignore SSL certificate errors</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="spinbutton_priority"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">False</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">0 -128 127 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_resource"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_port"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">spinbutton_port</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_ssl"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use old SS_L</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="spinbutton_port"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">5222 0 65556 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Override server settings</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-msn.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-msn.glade new file mode 100644 index 000000000..b7b91c5a9 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-msn.glade @@ -0,0 +1,335 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_msn_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">msn account settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_msn_settings"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_common_msn_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkEntry" id="entry_id"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">●</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_id"> + <property name="visible">True</property> + <property name="label" translatable="yes">Login I_D:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_id</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="label" translatable="yes">Pass_word:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkEntry" id="entry_password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">●</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_forget"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Forget password and clear the entry.</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="stock">gtk-clear</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="table_advanced_msn_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkSpinButton" id="spinbutton_port"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">0 0 65536 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">●</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_port"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">spinbutton_port</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-salut.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-salut.glade new file mode 100644 index 000000000..ab725c6ff --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-salut.glade @@ -0,0 +1,396 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_salut_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">salut account settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_salut_settings"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_common_settings"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_first_name"> + <property name="visible">True</property> + <property name="label" translatable="yes">_First Name: </property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_first_name</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_last_name"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Last Name:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_last_name</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_nickname"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Nickname:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_nickname</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_first_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_last_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_nickname"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_email"> + <property name="visible">True</property> + <property name="label" translatable="yes">_E-mail:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_email</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_jid"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Jabber ID:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_jid</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_email"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_jid"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_published"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Published Name:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_published</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_published"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget-yahoo.glade b/gnome-2-22/libempathy-gtk/empathy-account-widget-yahoo.glade new file mode 100644 index 000000000..4d0bcc113 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget-yahoo.glade @@ -0,0 +1,526 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_yahoo_settings"> + <property name="visible">True</property> + <property name="title" translatable="yes">Yahoo! account settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_yahoo_settings"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_common_settings"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_password"> + <property name="visible">True</property> + <property name="label" translatable="yes">Pass_word:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_password</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_forget"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Forget password and clear the entry.</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image834"> + <property name="visible">True</property> + <property name="stock">gtk-clear</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_id"> + <property name="visible">True</property> + <property name="label" translatable="yes">Login I_D:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_id</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_id"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkExpander" id="expander1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="table_advanced_settings"> + <property name="visible">True</property> + <property name="n_rows">7</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_ignore_invites"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Ignore conference and chatroom invitations</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_locale"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Room List locale:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_locale</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_charset"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Charset:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_charset</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_locale"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_charset"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_port"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">spinbutton_port</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="spinbutton_port"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">5050 1 65556 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_serverjp"> + <property name="visible">True</property> + <property name="label" translatable="yes">J_apan server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_serverjp</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_serverjp"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_yahoojp"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use _Yahoo Japan</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Advanced</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget.c b/gnome-2-22/libempathy-gtk/empathy-account-widget.c new file mode 100644 index 000000000..9596a1859 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget.c @@ -0,0 +1,680 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Martyn Russell <martyn@imendio.com> + */ + +#include <config.h> + +#include <string.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-protocol.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-account-widget.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "AccountWidget" + +static gboolean +account_widget_entry_focus_cb (GtkWidget *widget, + GdkEventFocus *event, + McAccount *account) +{ + const gchar *str; + const gchar *param_name; + + str = gtk_entry_get_text (GTK_ENTRY (widget)); + param_name = g_object_get_data (G_OBJECT (widget), "param_name"); + + if (G_STR_EMPTY (str)) { + gchar *value = NULL; + + mc_account_unset_param (account, param_name); + mc_account_get_param_string (account, param_name, &value); + empathy_debug (DEBUG_DOMAIN, "Unset %s and restore to %s", param_name, value); + gtk_entry_set_text (GTK_ENTRY (widget), value ? value : ""); + g_free (value); + } else { + empathy_debug (DEBUG_DOMAIN, "Setting %s to %s", param_name, str); + mc_account_set_param_string (account, param_name, str); + } + + return FALSE; +} + +static void +account_widget_int_changed_cb (GtkWidget *widget, + McAccount *account) +{ + const gchar *param_name; + gint value; + + value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + param_name = g_object_get_data (G_OBJECT (widget), "param_name"); + + if (value == 0) { + mc_account_unset_param (account, param_name); + mc_account_get_param_int (account, param_name, &value); + empathy_debug (DEBUG_DOMAIN, "Unset %s and restore to %d", param_name, value); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } else { + empathy_debug (DEBUG_DOMAIN, "Setting %s to %d", param_name, value); + mc_account_set_param_int (account, param_name, value); + } +} + +static void +account_widget_checkbutton_toggled_cb (GtkWidget *widget, + McAccount *account) +{ + gboolean value; + gboolean default_value; + const gchar *param_name; + + value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + param_name = g_object_get_data (G_OBJECT (widget), "param_name"); + + /* FIXME: This is ugly! checkbox don't have a "not-set" value so we + * always unset the param and set the value if different from the + * default value. */ + mc_account_unset_param (account, param_name); + mc_account_get_param_boolean (account, param_name, &default_value); + + if (default_value == value) { + empathy_debug (DEBUG_DOMAIN, "Unset %s and restore to %d", param_name, default_value); + } else { + empathy_debug (DEBUG_DOMAIN, "Setting %s to %d", param_name, value); + mc_account_set_param_boolean (account, param_name, value); + } +} + +static void +account_widget_forget_clicked_cb (GtkWidget *button, + GtkWidget *entry) +{ + McAccount *account; + const gchar *param_name; + + param_name = g_object_get_data (G_OBJECT (entry), "param_name"); + account = g_object_get_data (G_OBJECT (entry), "account"); + + empathy_debug (DEBUG_DOMAIN, "Unset %s", param_name); + mc_account_unset_param (account, param_name); + gtk_entry_set_text (GTK_ENTRY (entry), ""); +} + +static void +account_widget_password_changed_cb (GtkWidget *entry, + GtkWidget *button) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (entry)); + gtk_widget_set_sensitive (button, !G_STR_EMPTY (str)); +} + +static void +account_widget_jabber_ssl_toggled_cb (GtkWidget *checkbutton_ssl, + GtkWidget *spinbutton_port) +{ + McAccount *account; + gboolean value; + gint port = 0; + + value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton_ssl)); + account = g_object_get_data (G_OBJECT (spinbutton_port), "account"); + mc_account_get_param_int (account, "port", &port); + + if (value) { + if (port == 5222 || port == 0) { + port = 5223; + } + } else { + if (port == 5223 || port == 0) { + port = 5222; + } + } + + gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinbutton_port), port); +} + +static void +account_widget_setup_widget (GtkWidget *widget, + McAccount *account, + const gchar *param_name) +{ + g_object_set_data_full (G_OBJECT (widget), "param_name", + g_strdup (param_name), g_free); + g_object_set_data_full (G_OBJECT (widget), "account", + g_object_ref (account), g_object_unref); + + if (GTK_IS_SPIN_BUTTON (widget)) { + gint value = 0; + + mc_account_get_param_int (account, param_name, &value); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + + g_signal_connect (widget, "value-changed", + G_CALLBACK (account_widget_int_changed_cb), + account); + } + else if (GTK_IS_ENTRY (widget)) { + gchar *str = NULL; + + mc_account_get_param_string (account, param_name, &str); + gtk_entry_set_text (GTK_ENTRY (widget), str ? str : ""); + g_free (str); + + if (strstr (param_name, "password")) { + gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE); + } + + g_signal_connect (widget, "focus-out-event", + G_CALLBACK (account_widget_entry_focus_cb), + account); + } + else if (GTK_IS_TOGGLE_BUTTON (widget)) { + gboolean value = FALSE; + + mc_account_get_param_boolean (account, param_name, &value); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value); + + g_signal_connect (widget, "toggled", + G_CALLBACK (account_widget_checkbutton_toggled_cb), + account); + } else { + empathy_debug (DEBUG_DOMAIN, + "Unknown type of widget for param %s", + param_name); + } +} + +static gchar * +account_widget_generic_format_param_name (const gchar *param_name) +{ + gchar *str; + gchar *p; + + str = g_strdup (param_name); + + if (str && g_ascii_isalpha (str[0])) { + str[0] = g_ascii_toupper (str[0]); + } + + while ((p = strchr (str, '-')) != NULL) { + if (p[1] != '\0' && g_ascii_isalpha (p[1])) { + p[0] = ' '; + p[1] = g_ascii_toupper (p[1]); + } + + p++; + } + + return str; +} + +static void +accounts_widget_generic_setup (McAccount *account, + GtkWidget *table_common_settings, + GtkWidget *table_advanced_settings) +{ + McProtocol *protocol; + McProfile *profile; + GSList *params, *l; + + profile = mc_account_get_profile (account); + protocol = mc_profile_get_protocol (profile); + + if (!protocol) { + /* The CM is not installed, MC shouldn't list them + * see SF bug #1688779 + * FIXME: We should display something asking the user to + * install the CM + */ + g_object_unref (profile); + return; + } + + params = mc_protocol_get_params (protocol); + + for (l = params; l; l = l->next) { + McProtocolParam *param; + GtkWidget *table_settings; + guint n_rows = 0; + GtkWidget *widget = NULL; + gchar *param_name_formatted; + + param = l->data; + if (param->flags & MC_PROTOCOL_PARAM_REQUIRED) { + table_settings = table_common_settings; + } else { + table_settings = table_advanced_settings; + } + param_name_formatted = account_widget_generic_format_param_name (param->name); + g_object_get (table_settings, "n-rows", &n_rows, NULL); + gtk_table_resize (GTK_TABLE (table_settings), ++n_rows, 2); + + if (param->signature[0] == 's') { + gchar *str; + + str = g_strdup_printf (_("%s:"), param_name_formatted); + widget = gtk_label_new (str); + gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); + g_free (str); + + gtk_table_attach (GTK_TABLE (table_settings), + widget, + 0, 1, + n_rows - 1, n_rows, + GTK_FILL, 0, + 0, 0); + + widget = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table_settings), + widget, + 1, 2, + n_rows - 1, n_rows, + GTK_FILL | GTK_EXPAND, 0, + 0, 0); + } + /* int types: ynqiuxt. double type is 'd' */ + else if (param->signature[0] == 'y' || + param->signature[0] == 'n' || + param->signature[0] == 'q' || + param->signature[0] == 'i' || + param->signature[0] == 'u' || + param->signature[0] == 'x' || + param->signature[0] == 't' || + param->signature[0] == 'd') { + gchar *str = NULL; + gdouble minint = 0; + gdouble maxint = 0; + gdouble step = 1; + + switch (param->signature[0]) { + case 'y': minint = G_MININT8; maxint = G_MAXINT8; break; + case 'n': minint = G_MININT16; maxint = G_MAXINT16; break; + case 'q': minint = 0; maxint = G_MAXUINT16; break; + case 'i': minint = G_MININT32; maxint = G_MAXINT32; break; + case 'u': minint = 0; maxint = G_MAXUINT32; break; + case 'x': minint = G_MININT64; maxint = G_MAXINT64; break; + case 't': minint = 0; maxint = G_MAXUINT64; break; + case 'd': minint = G_MININT32; maxint = G_MAXINT32; step = 0.1; break; + } + + str = g_strdup_printf (_("%s:"), param_name_formatted); + widget = gtk_label_new (str); + gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5); + g_free (str); + + gtk_table_attach (GTK_TABLE (table_settings), + widget, + 0, 1, + n_rows - 1, n_rows, + GTK_FILL, 0, + 0, 0); + + widget = gtk_spin_button_new_with_range (minint, maxint, step); + gtk_table_attach (GTK_TABLE (table_settings), + widget, + 1, 2, + n_rows - 1, n_rows, + GTK_FILL | GTK_EXPAND, 0, + 0, 0); + } + else if (param->signature[0] == 'b') { + widget = gtk_check_button_new_with_label (param_name_formatted); + gtk_table_attach (GTK_TABLE (table_settings), + widget, + 0, 2, + n_rows - 1, n_rows, + GTK_FILL | GTK_EXPAND, 0, + 0, 0); + } else { + empathy_debug (DEBUG_DOMAIN, + "Unknown signature for param %s: %s", + param_name_formatted, param->signature); + } + + if (widget) { + account_widget_setup_widget (widget, account, param->name); + } + + g_free (param_name_formatted); + } + + g_slist_free (params); + g_object_unref (profile); + g_object_unref (protocol); +} + +static void +account_widget_handle_params_valist (McAccount *account, + GladeXML *gui, + const gchar *first_widget_name, + va_list args) +{ + GtkWidget *widget; + const gchar *widget_name; + + for (widget_name = first_widget_name; widget_name; widget_name = va_arg (args, gchar*)) { + const gchar *param_name; + + param_name = va_arg (args, gchar*); + + widget = glade_xml_get_widget (gui, widget_name); + + if (!widget) { + g_warning ("Glade is missing widget '%s'.", widget_name); + continue; + } + + account_widget_setup_widget (widget, account, param_name); + } +} + +void +empathy_account_widget_handle_params (McAccount *account, + GladeXML *gui, + const gchar *first_widget_name, + ...) +{ + va_list args; + + g_return_if_fail (MC_IS_ACCOUNT (account)); + + va_start (args, first_widget_name); + account_widget_handle_params_valist (account, gui, + first_widget_name, + args); + va_end (args); +} + +void +empathy_account_widget_add_forget_button (McAccount *account, + GladeXML *glade, + const gchar *button, + const gchar *entry) +{ + GtkWidget *button_forget; + GtkWidget *entry_password; + gchar *password = NULL; + + button_forget = glade_xml_get_widget (glade, button); + entry_password = glade_xml_get_widget (glade, entry); + + mc_account_get_param_string (account, "password", &password); + gtk_widget_set_sensitive (button_forget, !G_STR_EMPTY (password)); + g_free (password); + + g_signal_connect (button_forget, "clicked", + G_CALLBACK (account_widget_forget_clicked_cb), + entry_password); + g_signal_connect (entry_password, "changed", + G_CALLBACK (account_widget_password_changed_cb), + button_forget); +} + +GtkWidget * +empathy_account_widget_generic_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + GtkWidget *table_common_settings; + GtkWidget *table_advanced_settings; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + + glade = empathy_glade_get_file ("empathy-account-widget-generic.glade", + "vbox_generic_settings", + NULL, + "vbox_generic_settings", &widget, + "table_common_settings", &table_common_settings, + "table_advanced_settings", &table_advanced_settings, + NULL); + + accounts_widget_generic_setup (account, table_common_settings, table_advanced_settings); + + g_object_unref (glade); + + gtk_widget_show_all (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_salut_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + + glade = empathy_glade_get_file ("empathy-account-widget-salut.glade", + "vbox_salut_settings", + NULL, + "vbox_salut_settings", &widget, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_published", "published-name", + "entry_nickname", "nickname", + "entry_first_name", "first-name", + "entry_last_name", "last-name", + "entry_email", "email", + "entry_jid", "jid", + NULL); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_msn_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + + glade = empathy_glade_get_file ("empathy-account-widget-msn.glade", + "vbox_msn_settings", + NULL, + "vbox_msn_settings", &widget, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_id", "account", + "entry_password", "password", + "entry_server", "server", + "spinbutton_port", "port", + NULL); + + empathy_account_widget_add_forget_button (account, glade, + "button_forget", + "entry_password"); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_sip_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + + glade = empathy_glade_get_file ("empathy-account-widget-sip.glade", + "vbox_sip_settings", + NULL, + "vbox_sip_settings", &widget, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_userid", "account", + "entry_password", "password", + "entry_registar", "registar", + "entry_proxy","proxy-host", + "spinbutton_port", "port", + "entry_transport", "transport", + "checkbutton_discover-binding","discover-binding", + "entry_keepalive-mechanism", "keepalive-mechanism", + "entry_keepalive-interval", "keepalive-interval", + "checkbutton_discover-stun", "discover-stun", + "entry_stun-server", "stun-server", + "spinbutton_stun-port", "stun-port", + "entry_local-ip", "local-ip-address", + "spinbutton_local-port", "local-port", + "entry_extra-auth-user", "extra-auth-user", + "entry_extra-auth-password", "extra-auth-password", + "checkbutton_avoid-difficult", "avoid-difficult", + NULL); + + empathy_account_widget_add_forget_button (account, glade, + "button_forget", + "entry_password"); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_jabber_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + GtkWidget *spinbutton_port; + GtkWidget *checkbutton_ssl; + + glade = empathy_glade_get_file ("empathy-account-widget-jabber.glade", + "vbox_jabber_settings", + NULL, + "vbox_jabber_settings", &widget, + "spinbutton_port", &spinbutton_port, + "checkbutton_ssl", &checkbutton_ssl, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_id", "account", + "entry_password", "password", + "entry_resource", "resource", + "entry_server", "server", + "spinbutton_port", "port", + "spinbutton_priority", "priority", + "checkbutton_ssl", "old-ssl", + "checkbutton_ignore_ssl_errors", "ignore-ssl-errors", + "checkbutton_encryption", "require-encryption", + NULL); + + empathy_account_widget_add_forget_button (account, glade, + "button_forget", + "entry_password"); + + g_signal_connect (checkbutton_ssl, "toggled", + G_CALLBACK (account_widget_jabber_ssl_toggled_cb), + spinbutton_port); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_icq_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + GtkWidget *spinbutton_port; + + glade = empathy_glade_get_file ("empathy-account-widget-icq.glade", + "vbox_icq_settings", + NULL, + "vbox_icq_settings", &widget, + "spinbutton_port", &spinbutton_port, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_uin", "account", + "entry_password", "password", + "entry_server", "server", + "spinbutton_port", "port", + "entry_charset", "charset", + NULL); + + empathy_account_widget_add_forget_button (account, glade, + "button_forget", + "entry_password"); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + +GtkWidget * +empathy_account_widget_yahoo_new (McAccount *account) +{ + GladeXML *glade; + GtkWidget *widget; + + glade = empathy_glade_get_file ("empathy-account-widget-yahoo.glade", + "vbox_yahoo_settings", + NULL, + "vbox_yahoo_settings", &widget, + NULL); + + empathy_account_widget_handle_params (account, glade, + "entry_id", "account", + "entry_password", "password", + "entry_server", "server", + "entry_serverjp", "serverjp", + "entry_locale", "room-list-locale", + "entry_charset", "charset", + "spinbutton_port", "port", + "checkbutton_yahoojp", "yahoojp", + "checkbutton_ignore_invites", "ignore-invites", + NULL); + + empathy_account_widget_add_forget_button (account, glade, + "button_forget", + "entry_password"); + + g_object_unref (glade); + + gtk_widget_show (widget); + + return widget; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-account-widget.h b/gnome-2-22/libempathy-gtk/empathy-account-widget.h new file mode 100644 index 000000000..b7ebb6742 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-account-widget.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Martyn Russell <martyn@imendio.com> + */ + +#ifndef __EMPATHY_ACCOUNT_WIDGET_GENERIC_H__ +#define __EMPATHY_ACCOUNT_WIDGET_GENERIC_H__ + +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libmissioncontrol/mc-account.h> + +G_BEGIN_DECLS + +void empathy_account_widget_handle_params (McAccount *account, + GladeXML *glade, + const gchar *first_widget_name, + ...); +void empathy_account_widget_add_forget_button (McAccount *account, + GladeXML *glade, + const gchar *button, + const gchar *entry); +GtkWidget *empathy_account_widget_generic_new (McAccount *account); +GtkWidget *empathy_account_widget_salut_new (McAccount *account); +GtkWidget *empathy_account_widget_msn_new (McAccount *account); +GtkWidget *empathy_account_widget_jabber_new (McAccount *account); +GtkWidget *empathy_account_widget_icq_new (McAccount *account); +GtkWidget *empathy_account_widget_yahoo_new (McAccount *account); +GtkWidget *empathy_account_widget_sip_new (McAccount *account); + +G_END_DECLS + +#endif /* __EMPATHY_ACCOUNT_WIDGET_GENERIC_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.c b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.c new file mode 100644 index 000000000..f79db0e03 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.c @@ -0,0 +1,1083 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> +#include <dbus/dbus-glib.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-profile.h> +#include <libmissioncontrol/mission-control.h> +#include <libmissioncontrol/mc-account-monitor.h> +#include <telepathy-glib/util.h> +#include <libtelepathy/tp-constants.h> + +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> +#include <libempathy-gtk/empathy-ui-utils.h> + +#include "empathy-accounts-dialog.h" +#include "empathy-profile-chooser.h" +#include "empathy-account-widget.h" +#include "empathy-account-widget-irc.h" + +#define DEBUG_DOMAIN "AccountDialog" + +/* Flashing delay for icons (milliseconds). */ +#define FLASH_TIMEOUT 500 + +typedef struct { + GtkWidget *window; + + GtkWidget *alignment_settings; + + GtkWidget *vbox_details; + GtkWidget *frame_no_account; + GtkWidget *label_no_account; + GtkWidget *label_no_account_blurb; + + GtkWidget *treeview; + + GtkWidget *button_add; + GtkWidget *button_remove; + + GtkWidget *frame_new_account; + GtkWidget *combobox_profile; + GtkWidget *hbox_type; + GtkWidget *button_create; + GtkWidget *button_back; + + GtkWidget *image_type; + GtkWidget *label_name; + GtkWidget *label_type; + GtkWidget *settings_widget; + + gboolean connecting_show; + guint connecting_id; + gboolean account_changed; + + MissionControl *mc; + McAccountMonitor *monitor; +} EmpathyAccountsDialog; + +enum { + COL_ENABLED, + COL_NAME, + COL_STATUS, + COL_ACCOUNT_POINTER, + COL_COUNT +}; + +static void accounts_dialog_setup (EmpathyAccountsDialog *dialog); +static void accounts_dialog_update_account (EmpathyAccountsDialog *dialog, + McAccount *account); +static void accounts_dialog_model_setup (EmpathyAccountsDialog *dialog); +static void accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog); +static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog); +static void accounts_dialog_model_pixbuf_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyAccountsDialog *dialog); +static McAccount *accounts_dialog_model_get_selected (EmpathyAccountsDialog *dialog); +static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog, + McAccount *account); +static gboolean accounts_dialog_model_remove_selected (EmpathyAccountsDialog *dialog); +static void accounts_dialog_model_selection_changed (GtkTreeSelection *selection, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_add_account (EmpathyAccountsDialog *dialog, + McAccount *account); +static void accounts_dialog_account_added_cb (McAccountMonitor *monitor, + gchar *unique_name, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_account_removed_cb (McAccountMonitor *monitor, + gchar *unique_name, + EmpathyAccountsDialog *dialog); +static gboolean accounts_dialog_row_changed_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static gboolean accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog); +static void accounts_dialog_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_button_create_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_button_back_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_button_add_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_remove_response_cb (GtkWidget *dialog, + gint response, + McAccount *account); +static void accounts_dialog_button_remove_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathyAccountsDialog *dialog); +static void accounts_dialog_destroy_cb (GtkWidget *widget, + EmpathyAccountsDialog *dialog); + +static void +accounts_dialog_setup (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeIter iter; + GList *accounts, *l; + + view = GTK_TREE_VIEW (dialog->treeview); + store = GTK_LIST_STORE (gtk_tree_view_get_model (view)); + + accounts = mc_accounts_list (); + + for (l = accounts; l; l = l->next) { + McAccount *account; + const gchar *name; + TpConnectionStatus status; + gboolean enabled; + + account = l->data; + + name = mc_account_get_display_name (account); + if (!name) { + continue; + } + + status = mission_control_get_connection_status (dialog->mc, account, NULL); + enabled = mc_account_is_enabled (account); + + gtk_list_store_insert_with_values (store, &iter, + -1, + COL_ENABLED, enabled, + COL_NAME, name, + COL_STATUS, status, + COL_ACCOUNT_POINTER, account, + -1); + + accounts_dialog_status_changed_cb (dialog->mc, + status, + MC_PRESENCE_UNSET, + TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, + mc_account_get_unique_name (account), + dialog); + + g_object_unref (account); + } + + g_list_free (accounts); +} + +static void +accounts_dialog_update_account (EmpathyAccountsDialog *dialog, + McAccount *account) +{ + if (dialog->settings_widget) { + gtk_widget_destroy (dialog->settings_widget); + dialog->settings_widget = NULL; + } + + if (!account) { + GtkTreeView *view; + GtkTreeModel *model; + GString *string; + gchar *str; + + gtk_widget_show (dialog->frame_no_account); + gtk_widget_hide (dialog->vbox_details); + + gtk_widget_set_sensitive (dialog->button_remove, FALSE); + + view = GTK_TREE_VIEW (dialog->treeview); + model = gtk_tree_view_get_model (view); + + if (empathy_profile_chooser_n_profiles (dialog->combobox_profile) > 0) { + string = g_string_new (_("To add a new account, you can click on the " + "'Add' button and a new entry will be created " + "for you to start configuring.")); + } else { + string = g_string_new (_("To add a new account, you first have to " + "install a backend for each protocol " + "you want to use.")); + } + + if (gtk_tree_model_iter_n_children (model, NULL) > 0) { + gtk_label_set_markup (GTK_LABEL (dialog->label_no_account), + _("<b>No Account Selected</b>")); + g_string_append (string, _("\n\n" + "If you do not want to add an account, simply " + "click on the account you want to configure in " + "the list on the left.")); + } else { + gtk_label_set_markup (GTK_LABEL (dialog->label_no_account), + _("<b>No Accounts Configured</b>")); + } + + str = g_string_free (string, FALSE); + gtk_label_set_markup (GTK_LABEL (dialog->label_no_account_blurb), + str); + g_free (str); + } else { + McProfile *profile; + const gchar *config_ui; + + gtk_widget_hide (dialog->frame_no_account); + gtk_widget_show (dialog->vbox_details); + + profile = mc_account_get_profile (account); + config_ui = mc_profile_get_configuration_ui (profile); + g_object_unref (profile); + + if (!tp_strdiff (config_ui, "jabber")) { + dialog->settings_widget = + empathy_account_widget_jabber_new (account); + } + else if (!tp_strdiff (config_ui, "msn")) { + dialog ->settings_widget = + empathy_account_widget_msn_new (account); + } + else if (!tp_strdiff (config_ui, "local-xmpp")) { + dialog->settings_widget = + empathy_account_widget_salut_new (account); + } + else if (!tp_strdiff (config_ui, "irc")) { + dialog->settings_widget = + empathy_account_widget_irc_new (account); + } + else if (!tp_strdiff(config_ui, "icq")) { + dialog->settings_widget = + empathy_account_widget_icq_new (account); + } + else if (!tp_strdiff (config_ui, "yahoo")) { + dialog->settings_widget = + empathy_account_widget_yahoo_new (account); + } + else if (!tp_strdiff (config_ui, "sofiasip")) { + dialog->settings_widget = + empathy_account_widget_sip_new (account); + } + else { + dialog->settings_widget = + empathy_account_widget_generic_new (account); + } + } + + if (dialog->settings_widget) { + gtk_container_add (GTK_CONTAINER (dialog->alignment_settings), + dialog->settings_widget); + } + + if (account) { + McProfile *profile; + gchar *text; + + profile = mc_account_get_profile (account); + gtk_image_set_from_icon_name (GTK_IMAGE (dialog->image_type), + mc_profile_get_icon_name (profile), + GTK_ICON_SIZE_DIALOG); + gtk_widget_set_tooltip_text (dialog->image_type, + mc_profile_get_display_name (profile)); + + text = g_strdup_printf ("<big><b>%s</b></big>", mc_account_get_display_name (account)); + gtk_label_set_markup (GTK_LABEL (dialog->label_name), text); + g_free (text); + } +} + +static void +accounts_dialog_model_setup (EmpathyAccountsDialog *dialog) +{ + GtkListStore *store; + GtkTreeSelection *selection; + + store = gtk_list_store_new (COL_COUNT, + G_TYPE_BOOLEAN, /* enabled */ + G_TYPE_STRING, /* name */ + G_TYPE_UINT, /* status */ + MC_TYPE_ACCOUNT); /* account */ + + gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview), + GTK_TREE_MODEL (store)); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + g_signal_connect (selection, "changed", + G_CALLBACK (accounts_dialog_model_selection_changed), + dialog); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_NAME, GTK_SORT_ASCENDING); + + accounts_dialog_model_add_columns (dialog); + + g_object_unref (store); +} + +static void +accounts_dialog_name_edited_cb (GtkCellRendererText *renderer, + gchar *path, + gchar *new_text, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + GtkTreeModel *model; + GtkTreePath *treepath; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview)); + treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (model, &iter, treepath); + gtk_tree_model_get (model, &iter, + COL_ACCOUNT_POINTER, &account, + -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_NAME, new_text, + -1); + gtk_tree_path_free (treepath); + + mc_account_set_display_name (account, new_text); + g_object_unref (account); +} + +static void +accounts_dialog_enable_toggled_cb (GtkCellRendererToggle *cell_renderer, + gchar *path, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + GtkTreeModel *model; + GtkTreePath *treepath; + GtkTreeIter iter; + gboolean enabled; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview)); + treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (model, &iter, treepath); + gtk_tree_model_get (model, &iter, + COL_ACCOUNT_POINTER, &account, + -1); + gtk_tree_path_free (treepath); + + enabled = mc_account_is_enabled (account); + mc_account_set_enabled (account, !enabled); + + empathy_debug (DEBUG_DOMAIN, "%s account %s", + enabled ? "Disabled" : "Enable", + mc_account_get_display_name(account)); + + g_object_unref (account); +} + +static void +accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + + view = GTK_TREE_VIEW (dialog->treeview); + gtk_tree_view_set_headers_visible (view, TRUE); + + /* Enabled column */ + cell = gtk_cell_renderer_toggle_new (); + gtk_tree_view_insert_column_with_attributes (view, -1, + _("Enabled"), + cell, + "active", COL_ENABLED, + NULL); + g_signal_connect (cell, "toggled", + G_CALLBACK (accounts_dialog_enable_toggled_cb), + dialog); + + /* Account column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Accounts")); + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_append_column (view, column); + + /* Icon renderer */ + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_set_cell_data_func (column, cell, + (GtkTreeCellDataFunc) + accounts_dialog_model_pixbuf_data_func, + dialog, + NULL); + + /* Name renderer */ + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "editable", TRUE, + NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME); + g_signal_connect (cell, "edited", + G_CALLBACK (accounts_dialog_name_edited_cb), + dialog); +} + +static void +accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + /* select first */ + view = GTK_TREE_VIEW (dialog->treeview); + model = gtk_tree_view_get_model (view); + + if (gtk_tree_model_get_iter_first (model, &iter)) { + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_select_iter (selection, &iter); + } else { + accounts_dialog_update_account (dialog, NULL); + } +} + +static void +accounts_dialog_model_pixbuf_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + const gchar *icon_name; + GdkPixbuf *pixbuf; + TpConnectionStatus status; + + gtk_tree_model_get (model, iter, + COL_STATUS, &status, + COL_ACCOUNT_POINTER, &account, + -1); + + icon_name = empathy_icon_name_from_account (account); + pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON); + + if (pixbuf) { + if (status == TP_CONNECTION_STATUS_DISCONNECTED || + (status == TP_CONNECTION_STATUS_CONNECTING && + !dialog->connecting_show)) { + GdkPixbuf *modded_pixbuf; + + modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf)); + + gdk_pixbuf_saturate_and_pixelate (pixbuf, + modded_pixbuf, + 1.0, + TRUE); + g_object_unref (pixbuf); + pixbuf = modded_pixbuf; + } + } + + g_object_set (cell, + "visible", TRUE, + "pixbuf", pixbuf, + NULL); + + g_object_unref (account); + if (pixbuf) { + g_object_unref (pixbuf); + } +} + +static gboolean +accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog, + McAccount *account, + GtkTreeIter *iter) +{ + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + gboolean ok; + + /* Update the status in the model */ + view = GTK_TREE_VIEW (dialog->treeview); + selection = gtk_tree_view_get_selection (view); + model = gtk_tree_view_get_model (view); + + for (ok = gtk_tree_model_get_iter_first (model, iter); + ok; + ok = gtk_tree_model_iter_next (model, iter)) { + McAccount *this_account; + gboolean equal; + + gtk_tree_model_get (model, iter, + COL_ACCOUNT_POINTER, &this_account, + -1); + + equal = empathy_account_equal (this_account, account); + g_object_unref (this_account); + + if (equal) { + return TRUE; + } + } + + return FALSE; +} + +static McAccount * +accounts_dialog_model_get_selected (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + McAccount *account; + + view = GTK_TREE_VIEW (dialog->treeview); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return NULL; + } + + gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1); + + return account; +} + +static void +accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog, + McAccount *account) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->treeview)); + if (accounts_dialog_get_account_iter (dialog, account, &iter)) { + gtk_tree_selection_select_iter (selection, &iter); + } +} + +static gboolean +accounts_dialog_model_remove_selected (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + view = GTK_TREE_VIEW (dialog->treeview); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return FALSE; + } + + return gtk_list_store_remove (GTK_LIST_STORE (model), &iter); +} + +static void +accounts_dialog_model_selection_changed (GtkTreeSelection *selection, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean is_selection; + + is_selection = gtk_tree_selection_get_selected (selection, &model, &iter); + + gtk_widget_set_sensitive (dialog->button_add, TRUE); + gtk_widget_set_sensitive (dialog->button_remove, is_selection); + + account = accounts_dialog_model_get_selected (dialog); + accounts_dialog_update_account (dialog, account); + + if (account) { + g_object_unref (account); + } + + /* insure new account frame is hidden when a row is selected*/ + gtk_widget_hide (dialog->frame_new_account); +} + +static void +accounts_dialog_add_account (EmpathyAccountsDialog *dialog, + McAccount *account) +{ + GtkTreeModel *model; + GtkTreeIter iter; + TpConnectionStatus status; + const gchar *name; + gboolean enabled; + + if (accounts_dialog_get_account_iter (dialog, account, &iter)) { + return; + } + + status = mission_control_get_connection_status (dialog->mc, account, NULL); + name = mc_account_get_display_name (account); + enabled = mc_account_is_enabled (account); + + g_return_if_fail (name != NULL); + + empathy_debug (DEBUG_DOMAIN, "Adding new account: %s", name); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview)); + gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, + -1, + COL_ENABLED, enabled, + COL_NAME, name, + COL_STATUS, status, + COL_ACCOUNT_POINTER, account, + -1); +} + +static void +accounts_dialog_account_added_cb (McAccountMonitor *monitor, + gchar *unique_name, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + + account = mc_account_lookup (unique_name); + accounts_dialog_add_account (dialog, account); + g_object_unref (account); +} + +static void +accounts_dialog_account_removed_cb (McAccountMonitor *monitor, + gchar *unique_name, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + + account = mc_account_lookup (unique_name); + + accounts_dialog_model_set_selected (dialog, account); + accounts_dialog_model_remove_selected (dialog); + + g_object_unref (account); +} + +static gboolean +accounts_dialog_row_changed_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + gtk_tree_model_row_changed (model, path, iter); + + return FALSE; +} + +static gboolean +accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeModel *model; + + dialog->connecting_show = !dialog->connecting_show; + + view = GTK_TREE_VIEW (dialog->treeview); + model = gtk_tree_view_get_model (view); + + gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL); + + return TRUE; +} + +static void +accounts_dialog_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyAccountsDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + McAccount *account; + GList *accounts, *l; + gboolean found = FALSE; + + /* Update the status in the model */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview)); + account = mc_account_lookup (unique_name); + + empathy_debug (DEBUG_DOMAIN, "Status changed for account %s: " + "status=%d presence=%d", + unique_name, status, presence); + + if (accounts_dialog_get_account_iter (dialog, account, &iter)) { + GtkTreePath *path; + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_STATUS, status, + -1); + + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_model_row_changed (model, path, &iter); + gtk_tree_path_free (path); + } + g_object_unref (account); + + /* Check if there is still accounts in CONNECTING state */ + accounts = mc_accounts_list (); + for (l = accounts; l; l = l->next) { + McAccount *this_account; + TpConnectionStatus status; + + this_account = l->data; + + status = mission_control_get_connection_status (mc, this_account, NULL); + if (status == TP_CONNECTION_STATUS_CONNECTING) { + found = TRUE; + break; + } + + g_object_unref (this_account); + } + g_list_free (accounts); + + if (!found && dialog->connecting_id) { + g_source_remove (dialog->connecting_id); + dialog->connecting_id = 0; + } + if (found && !dialog->connecting_id) { + dialog->connecting_id = g_timeout_add (FLASH_TIMEOUT, + (GSourceFunc) accounts_dialog_flash_connecting_cb, + dialog); + } +} + +static void +accounts_dialog_account_enabled_cb (McAccountMonitor *monitor, + gchar *unique_name, + EmpathyAccountsDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + McAccount *account; + gboolean enabled; + + /* Update the status in the model */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview)); + account = mc_account_lookup (unique_name); + enabled = mc_account_is_enabled (account); + + empathy_debug (DEBUG_DOMAIN, "Account %s is now %s", + mc_account_get_display_name (account), + enabled ? "enabled" : "disabled"); + + if (accounts_dialog_get_account_iter (dialog, account, &iter)) { + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_ENABLED, enabled, + -1); + } + + g_object_unref (account); +} + +static void +accounts_dialog_button_create_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + McProfile *profile; + McAccount *account; + const gchar *str; + + /* Update widgets */ + gtk_widget_show (dialog->vbox_details); + gtk_widget_hide (dialog->frame_no_account); + gtk_widget_hide (dialog->frame_new_account); + + profile = empathy_profile_chooser_get_selected (dialog->combobox_profile); + + /* Create account */ + account = mc_account_create (profile); + + str = mc_account_get_unique_name (account); + mc_account_set_display_name (account, str); + + accounts_dialog_add_account (dialog, account); + accounts_dialog_model_set_selected (dialog, account); + + g_object_unref (account); + g_object_unref (profile); +} + +static void +accounts_dialog_button_back_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + + gtk_widget_hide (dialog->vbox_details); + gtk_widget_hide (dialog->frame_no_account); + gtk_widget_hide (dialog->frame_new_account); + + gtk_widget_set_sensitive (dialog->button_add, TRUE); + + account = accounts_dialog_model_get_selected (dialog); + accounts_dialog_update_account (dialog, account); +} + +static void +accounts_dialog_button_add_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + GtkTreeView *view; + GtkTreeSelection *selection; + + view = GTK_TREE_VIEW (dialog->treeview); + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_unselect_all (selection); + + gtk_widget_set_sensitive (dialog->button_add, FALSE); + gtk_widget_hide (dialog->vbox_details); + gtk_widget_hide (dialog->frame_no_account); + gtk_widget_show (dialog->frame_new_account); + + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combobox_profile), 0); + gtk_widget_grab_focus (dialog->combobox_profile); +} + +static void +accounts_dialog_remove_response_cb (GtkWidget *dialog, + gint response, + McAccount *account) +{ + if (response == GTK_RESPONSE_YES) { + mc_account_delete (account); + } + + gtk_widget_destroy (dialog); +} + +static void +accounts_dialog_button_remove_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + McAccount *account; + GtkWidget *message_dialog; + + account = accounts_dialog_model_get_selected (dialog); + + if (!mc_account_is_complete (account)) { + accounts_dialog_model_remove_selected (dialog); + return; + } + message_dialog = gtk_message_dialog_new + (GTK_WINDOW (dialog->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("You are about to remove your %s account!\n" + "Are you sure you want to proceed?"), + mc_account_get_display_name (account)); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG (message_dialog), + _("Any associated conversations and chat rooms will NOT be " + "removed if you decide to proceed.\n" + "\n" + "Should you decide to add the account back at a later time, " + "they will still be available.")); + + gtk_dialog_add_button (GTK_DIALOG (message_dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_NO); + gtk_dialog_add_button (GTK_DIALOG (message_dialog), + GTK_STOCK_REMOVE, + GTK_RESPONSE_YES); + + g_signal_connect (message_dialog, "response", + G_CALLBACK (accounts_dialog_remove_response_cb), + account); + + gtk_widget_show (message_dialog); +} + +static void +accounts_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathyAccountsDialog *dialog) +{ + gtk_widget_destroy (widget); +} + +static void +accounts_dialog_destroy_cb (GtkWidget *widget, + EmpathyAccountsDialog *dialog) +{ + GList *accounts, *l; + + /* Disconnect signals */ + g_signal_handlers_disconnect_by_func (dialog->monitor, + accounts_dialog_account_added_cb, + dialog); + g_signal_handlers_disconnect_by_func (dialog->monitor, + accounts_dialog_account_removed_cb, + dialog); + g_signal_handlers_disconnect_by_func (dialog->monitor, + accounts_dialog_account_enabled_cb, + dialog); + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (dialog->mc), + "AccountStatusChanged", + G_CALLBACK (accounts_dialog_status_changed_cb), + dialog); + + /* Delete incomplete accounts */ + accounts = mc_accounts_list (); + for (l = accounts; l; l = l->next) { + McAccount *account; + + account = l->data; + if (!mc_account_is_complete (account)) { + /* FIXME: Warn the user the account is not complete + * and is going to be removed. */ + mc_account_delete (account); + } + + g_object_unref (account); + } + g_list_free (accounts); + + if (dialog->connecting_id) { + g_source_remove (dialog->connecting_id); + } + + g_object_unref (dialog->mc); + g_object_unref (dialog->monitor); + + g_free (dialog); +} + +GtkWidget * +empathy_accounts_dialog_show (GtkWindow *parent) +{ + static EmpathyAccountsDialog *dialog = NULL; + GladeXML *glade; + GtkWidget *bbox; + GtkWidget *button_close; + + if (dialog) { + gtk_window_present (GTK_WINDOW (dialog->window)); + return dialog->window; + } + + dialog = g_new0 (EmpathyAccountsDialog, 1); + + glade = empathy_glade_get_file ("empathy-accounts-dialog.glade", + "accounts_dialog", + NULL, + "accounts_dialog", &dialog->window, + "vbox_details", &dialog->vbox_details, + "frame_no_account", &dialog->frame_no_account, + "label_no_account", &dialog->label_no_account, + "label_no_account_blurb", &dialog->label_no_account_blurb, + "alignment_settings", &dialog->alignment_settings, + "dialog-action_area", &bbox, + "treeview", &dialog->treeview, + "frame_new_account", &dialog->frame_new_account, + "hbox_type", &dialog->hbox_type, + "button_create", &dialog->button_create, + "button_back", &dialog->button_back, + "image_type", &dialog->image_type, + "label_name", &dialog->label_name, + "button_add", &dialog->button_add, + "button_remove", &dialog->button_remove, + "button_close", &button_close, + NULL); + + empathy_glade_connect (glade, + dialog, + "accounts_dialog", "destroy", accounts_dialog_destroy_cb, + "accounts_dialog", "response", accounts_dialog_response_cb, + "button_create", "clicked", accounts_dialog_button_create_clicked_cb, + "button_back", "clicked", accounts_dialog_button_back_clicked_cb, + "button_add", "clicked", accounts_dialog_button_add_clicked_cb, + "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb, + NULL); + + g_object_add_weak_pointer (G_OBJECT (dialog->window), (gpointer) &dialog); + + g_object_unref (glade); + + /* Create profile chooser */ + dialog->combobox_profile = empathy_profile_chooser_new (); + gtk_box_pack_end (GTK_BOX (dialog->hbox_type), + dialog->combobox_profile, + TRUE, TRUE, 0); + gtk_widget_show (dialog->combobox_profile); + if (empathy_profile_chooser_n_profiles (dialog->combobox_profile) <= 0) { + gtk_widget_set_sensitive (dialog->button_add, FALSE); + } + + /* Set up signalling */ + dialog->mc = empathy_mission_control_new (); + dialog->monitor = mc_account_monitor_new (); + + g_signal_connect (dialog->monitor, "account-created", + G_CALLBACK (accounts_dialog_account_added_cb), + dialog); + g_signal_connect (dialog->monitor, "account-deleted", + G_CALLBACK (accounts_dialog_account_removed_cb), + dialog); + g_signal_connect (dialog->monitor, "account-enabled", + G_CALLBACK (accounts_dialog_account_enabled_cb), + dialog); + g_signal_connect (dialog->monitor, "account-disabled", + G_CALLBACK (accounts_dialog_account_enabled_cb), + dialog); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (dialog->mc), "AccountStatusChanged", + G_CALLBACK (accounts_dialog_status_changed_cb), + dialog, NULL); + + accounts_dialog_model_setup (dialog); + accounts_dialog_setup (dialog); + accounts_dialog_model_select_first (dialog); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog->window), + GTK_WINDOW (parent)); + } + + gtk_widget_show (dialog->window); + + return dialog->window; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.glade b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.glade new file mode 100644 index 000000000..9ed29b8da --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.glade @@ -0,0 +1,350 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkDialog" id="accounts_dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Accounts</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkHBox" id="hbox146"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="spacing">18</property> + <child> + <widget class="GtkVBox" id="vbox195"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow17"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTreeView" id="treeview"> + <property name="width_request">250</property> + <property name="height_request">200</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="enable_search">False</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkHBox" id="hbox148"> + <property name="visible">True</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <widget class="GtkButton" id="button_add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="button_remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox214"> + <property name="width_request">415</property> + <property name="visible">True</property> + <property name="spacing">18</property> + <child> + <widget class="GtkVBox" id="vbox_details"> + <property name="visible">True</property> + <property name="spacing">18</property> + <child> + <widget class="GtkHBox" id="hbox183"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkLabel" id="label_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label"><big><b>Gmail</b></big></property> + <property name="use_markup">True</property> + <property name="selectable">True</property> + </widget> + </child> + <child> + <widget class="GtkImage" id="image_type"> + <property name="visible">True</property> + <property name="yalign">0</property> + <property name="stock">gtk-cut</property> + <property name="icon_size">6</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="frame2"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment_settings"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">20</property> + <child> + <placeholder/> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label599"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Settings</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkFrame" id="frame_new_account"> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment29"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">20</property> + <child> + <widget class="GtkVBox" id="vbox216"> + <property name="visible">True</property> + <property name="spacing">12</property> + <child> + <widget class="GtkHBox" id="hbox_type"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkLabel" id="label645"> + <property name="visible">True</property> + <property name="label" translatable="yes">Type:</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <placeholder/> + </child> + </widget> + </child> + <child> + <widget class="GtkHBox" id="hbox181"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkButton" id="button_create"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="response_id">0</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="stock">gtk-new</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Cr_eate</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_back"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-back</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label643"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>New Account</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="frame_no_account"> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment21"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkLabel" id="label_no_account_blurb"> + <property name="visible">True</property> + <property name="label" translatable="yes">To add a new account, you can click on the 'Add' button and a new entry will be created for you to started configuring. + +If you do not want to add an account, simply click on the account you want to configure in the list on the left.</property> + <property name="use_markup">True</property> + <property name="wrap">True</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label_no_account"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>No Account Selected</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="button_close"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.h b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.h new file mode 100644 index 000000000..84395e618 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-accounts-dialog.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_ACCOUNTS_DIALOG_H__ +#define __EMPATHY_ACCOUNTS_DIALOG_H__ + +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +GtkWidget *empathy_accounts_dialog_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_ACCOUNTS_DIALOG_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.c b/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.c new file mode 100644 index 000000000..78bb32938 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.c @@ -0,0 +1,569 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB. + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Based on Novell's e-image-chooser. + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <libgnomevfs/gnome-vfs-ops.h> + +#include <libempathy/empathy-debug.h> + + +#include "empathy-avatar-chooser.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "AvatarChooser" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_AVATAR_CHOOSER, EmpathyAvatarChooserPriv)) + +#define AVATAR_SIZE_SAVE 96 +#define AVATAR_SIZE_VIEW 64 +#define DEFAULT_DIR DATADIR"/pixmaps/faces" + +typedef struct { + gchar *image_data; + gsize image_data_size; +} EmpathyAvatarChooserPriv; + +static void avatar_chooser_finalize (GObject *object); +static void avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser, + gchar *data, + gsize size); +static gboolean avatar_chooser_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EmpathyAvatarChooser *chooser); +static void avatar_chooser_drag_leave_cb (GtkWidget *widget, + GdkDragContext *context, + guint time, + EmpathyAvatarChooser *chooser); +static gboolean avatar_chooser_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EmpathyAvatarChooser *chooser); +static void avatar_chooser_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + EmpathyAvatarChooser *chooser); +static void avatar_chooser_clicked_cb (GtkWidget *button, + EmpathyAvatarChooser *chooser); + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL]; + +G_DEFINE_TYPE (EmpathyAvatarChooser, empathy_avatar_chooser, GTK_TYPE_BUTTON); + +/* + * Drag and drop stuff + */ +#define URI_LIST_TYPE "text/uri-list" + +enum DndTargetType { + DND_TARGET_TYPE_URI_LIST +}; + +static const GtkTargetEntry drop_types[] = { + { URI_LIST_TYPE, 0, DND_TARGET_TYPE_URI_LIST }, +}; + +static void +empathy_avatar_chooser_class_init (EmpathyAvatarChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = avatar_chooser_finalize; + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (object_class, sizeof (EmpathyAvatarChooserPriv)); +} + +static void +empathy_avatar_chooser_init (EmpathyAvatarChooser *chooser) +{ + EmpathyAvatarChooserPriv *priv; + + priv = GET_PRIV (chooser); + + gtk_drag_dest_set (GTK_WIDGET (chooser), + GTK_DEST_DEFAULT_ALL, + drop_types, + G_N_ELEMENTS (drop_types), + GDK_ACTION_COPY); + + g_signal_connect (chooser, "drag-motion", + G_CALLBACK (avatar_chooser_drag_motion_cb), + chooser); + g_signal_connect (chooser, "drag-leave", + G_CALLBACK (avatar_chooser_drag_leave_cb), + chooser); + g_signal_connect (chooser, "drag-drop", + G_CALLBACK (avatar_chooser_drag_drop_cb), + chooser); + g_signal_connect (chooser, "drag-data-received", + G_CALLBACK (avatar_chooser_drag_data_received_cb), + chooser); + g_signal_connect (chooser, "clicked", + G_CALLBACK (avatar_chooser_clicked_cb), + chooser); + + empathy_avatar_chooser_set (chooser, NULL); +} + +static void +avatar_chooser_finalize (GObject *object) +{ + EmpathyAvatarChooserPriv *priv; + + priv = GET_PRIV (object); + + g_free (priv->image_data); + + G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->finalize (object); +} + +static void +avatar_chooser_set_pixbuf (EmpathyAvatarChooser *chooser, + GdkPixbuf *pixbuf) +{ + EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser); + GtkWidget *image; + GdkPixbuf *pixbuf_view = NULL; + GdkPixbuf *pixbuf_save = NULL; + GError *error = NULL; + + g_free (priv->image_data); + priv->image_data = NULL; + priv->image_data_size = 0; + + if (pixbuf) { + pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW); + pixbuf_save = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_SAVE); + + if (!gdk_pixbuf_save_to_buffer (pixbuf_save, + &priv->image_data, + &priv->image_data_size, + "png", + &error, NULL)) { + empathy_debug (DEBUG_DOMAIN, "Failed to save pixbuf: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + image = gtk_image_new_from_pixbuf (pixbuf_view); + + g_object_unref (pixbuf_save); + g_object_unref (pixbuf_view); + } else { + image = gtk_image_new_from_icon_name ("stock_person", + GTK_ICON_SIZE_DIALOG); + } + + gtk_button_set_image (GTK_BUTTON (chooser), image); + g_signal_emit (chooser, signals[CHANGED], 0); +} + +static void +avatar_chooser_set_image_from_file (EmpathyAvatarChooser *chooser, + const gchar *filename) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!(pixbuf = gdk_pixbuf_new_from_file (filename, &error))) { + empathy_debug (DEBUG_DOMAIN, "Failed to load pixbuf from file: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + } + + avatar_chooser_set_pixbuf (chooser, pixbuf); + if (pixbuf) { + g_object_unref (pixbuf); + } +} + +static void +avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser, + gchar *data, + gsize size) +{ + GdkPixbuf *pixbuf; + + pixbuf = empathy_pixbuf_from_data (data, size); + avatar_chooser_set_pixbuf (chooser, pixbuf); + if (pixbuf) { + g_object_unref (pixbuf); + } +} + +static gboolean +avatar_chooser_drag_motion_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EmpathyAvatarChooser *chooser) +{ + EmpathyAvatarChooserPriv *priv; + GList *p; + + priv = GET_PRIV (chooser); + + for (p = context->targets; p != NULL; p = p->next) { + gchar *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gdk_drag_status (context, GDK_ACTION_COPY, time); + + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static void +avatar_chooser_drag_leave_cb (GtkWidget *widget, + GdkDragContext *context, + guint time, + EmpathyAvatarChooser *chooser) +{ +} + +static gboolean +avatar_chooser_drag_drop_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + EmpathyAvatarChooser *chooser) +{ + EmpathyAvatarChooserPriv *priv; + GList *p; + + priv = GET_PRIV (chooser); + + if (context->targets == NULL) { + return FALSE; + } + + for (p = context->targets; p != NULL; p = p->next) { + char *possible_type; + + possible_type = gdk_atom_name (GDK_POINTER_TO_ATOM (p->data)); + if (!strcmp (possible_type, URI_LIST_TYPE)) { + g_free (possible_type); + gtk_drag_get_data (widget, context, + GDK_POINTER_TO_ATOM (p->data), + time); + + return TRUE; + } + + g_free (possible_type); + } + + return FALSE; +} + +static void +avatar_chooser_drag_data_received_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + EmpathyAvatarChooser *chooser) +{ + gchar *target_type; + gboolean handled = FALSE; + + target_type = gdk_atom_name (selection_data->target); + if (!strcmp (target_type, URI_LIST_TYPE)) { + GnomeVFSHandle *handle = NULL; + GnomeVFSResult result; + GnomeVFSFileInfo info; + gchar *uri; + gchar *nl; + gchar *data = NULL; + + nl = strstr (selection_data->data, "\r\n"); + if (nl) { + uri = g_strndup (selection_data->data, + nl - (gchar*) selection_data->data); + } else { + uri = g_strdup (selection_data->data); + } + + result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ); + if (result == GNOME_VFS_OK) { + result = gnome_vfs_get_file_info_from_handle (handle, + &info, + GNOME_VFS_FILE_INFO_DEFAULT); + if (result == GNOME_VFS_OK) { + GnomeVFSFileSize data_size; + + data = g_malloc (info.size); + + result = gnome_vfs_read (handle, data, info.size, &data_size); + if (result == GNOME_VFS_OK) { + avatar_chooser_set_image_from_data (chooser, + data, + data_size); + handled = TRUE; + } else { + g_free (data); + } + } + + gnome_vfs_close (handle); + } + + g_free (uri); + } + + gtk_drag_finish (context, handled, FALSE, time); +} + +static void +avatar_chooser_update_preview_cb (GtkFileChooser *file_chooser, + EmpathyAvatarChooser *chooser) +{ + gchar *filename; + + filename = gtk_file_chooser_get_preview_filename (file_chooser); + + if (filename) { + GtkWidget *image; + GdkPixbuf *pixbuf = NULL; + GdkPixbuf *scaled_pixbuf; + + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); + + image = gtk_file_chooser_get_preview_widget (file_chooser); + + if (pixbuf) { + scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_SAVE); + gtk_image_set_from_pixbuf (GTK_IMAGE (image), scaled_pixbuf); + g_object_unref (scaled_pixbuf); + g_object_unref (pixbuf); + } else { + gtk_image_set_from_stock (GTK_IMAGE (image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + + gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE); +} + +static void +avatar_chooser_response_cb (GtkWidget *widget, + gint response, + EmpathyAvatarChooser *chooser) +{ + if (response == GTK_RESPONSE_OK) { + gchar *filename; + gchar *path; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget)); + avatar_chooser_set_image_from_file (chooser, filename); + g_free (filename); + + path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget)); + if (path) { + empathy_conf_set_string (empathy_conf_get (), + EMPATHY_PREFS_UI_AVATAR_DIRECTORY, + path); + g_free (path); + } + } + else if (response == GTK_RESPONSE_NO) { + avatar_chooser_set_image_from_data (chooser, NULL, 0); + } + + gtk_widget_destroy (widget); +} + +static void +avatar_chooser_clicked_cb (GtkWidget *button, + EmpathyAvatarChooser *chooser) +{ + GtkFileChooser *chooser_dialog; + GtkWidget *image; + gchar *saved_dir = NULL; + const gchar *default_dir = DEFAULT_DIR; + const gchar *pics_dir; + GtkFileFilter *filter; + + chooser_dialog = GTK_FILE_CHOOSER ( + gtk_file_chooser_dialog_new (_("Select Your Avatar Image"), + empathy_get_toplevel_window (GTK_WIDGET (chooser)), + GTK_FILE_CHOOSER_ACTION_OPEN, + _("No Image"), + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, + GTK_RESPONSE_OK, + NULL)); + + /* Get special dirs */ + empathy_conf_get_string (empathy_conf_get (), + EMPATHY_PREFS_UI_AVATAR_DIRECTORY, + &saved_dir); + if (saved_dir && !g_file_test (saved_dir, G_FILE_TEST_IS_DIR)) { + g_free (saved_dir); + saved_dir = NULL; + } + if (!g_file_test (default_dir, G_FILE_TEST_IS_DIR)) { + default_dir = NULL; + } + pics_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); + if (pics_dir && !g_file_test (pics_dir, G_FILE_TEST_IS_DIR)) { + pics_dir = NULL; + } + + /* Set current dir to the last one or to DEFAULT_DIR or to home */ + if (saved_dir) { + gtk_file_chooser_set_current_folder (chooser_dialog, saved_dir); + } + else if (pics_dir) { + gtk_file_chooser_set_current_folder (chooser_dialog, pics_dir); + } + else if (default_dir) { + gtk_file_chooser_set_current_folder (chooser_dialog, default_dir); + } else { + gtk_file_chooser_set_current_folder (chooser_dialog, g_get_home_dir ()); + } + + /* Add shortcuts to special dirs */ + if (saved_dir) { + gtk_file_chooser_add_shortcut_folder (chooser_dialog, saved_dir, NULL); + } + else if (pics_dir) { + gtk_file_chooser_add_shortcut_folder (chooser_dialog, pics_dir, NULL); + } + if (default_dir) { + gtk_file_chooser_add_shortcut_folder (chooser_dialog, default_dir, NULL); + } + + /* Setup preview image */ + image = gtk_image_new (); + gtk_file_chooser_set_preview_widget (chooser_dialog, image); + gtk_widget_set_size_request (image, AVATAR_SIZE_SAVE, AVATAR_SIZE_SAVE); + gtk_widget_show (image); + gtk_file_chooser_set_use_preview_label (chooser_dialog, FALSE); + g_signal_connect (chooser_dialog, "update-preview", + G_CALLBACK (avatar_chooser_update_preview_cb), + chooser); + + /* Setup filers */ + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_chooser_add_filter (chooser_dialog, filter); + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_chooser_add_filter (chooser_dialog, filter); + + /* Setup response */ + gtk_dialog_set_default_response (GTK_DIALOG (chooser_dialog), GTK_RESPONSE_OK); + g_signal_connect (chooser_dialog, "response", + G_CALLBACK (avatar_chooser_response_cb), + chooser); + + gtk_widget_show (GTK_WIDGET (chooser_dialog)); + g_free (saved_dir); +} + +GtkWidget * +empathy_avatar_chooser_new (void) +{ + return g_object_new (EMPATHY_TYPE_AVATAR_CHOOSER, NULL); +} + +void +empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser, + EmpathyAvatar *avatar) +{ + g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser)); + + avatar_chooser_set_image_from_data (chooser, + avatar ? avatar->data : NULL, + avatar ? avatar->len : 0); +} + +void +empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser *chooser, + const gchar **data, + gsize *data_size, + const gchar **mime_type) +{ + EmpathyAvatarChooserPriv *priv; + + g_return_if_fail (EMPATHY_IS_AVATAR_CHOOSER (chooser)); + + priv = GET_PRIV (chooser); + + if (data) { + *data = priv->image_data; + } + if (data_size) { + *data_size = priv->image_data_size; + } + if (mime_type) { + *mime_type = "png"; + } +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.h b/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.h new file mode 100644 index 000000000..83dea2afb --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-avatar-chooser.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB. + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Based on Novell's e-image-chooser. + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_AVATAR_CHOOSER_H__ +#define __EMPATHY_AVATAR_CHOOSER_H__ + +#include <gtk/gtkbutton.h> + +#include <libempathy/empathy-avatar.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_AVATAR_CHOOSER (empathy_avatar_chooser_get_type ()) +#define EMPATHY_AVATAR_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_AVATAR_CHOOSER, EmpathyAvatarChooser)) +#define EMPATHY_AVATAR_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EMPATHY_TYPE_AVATAR_CHOOSER, EmpathyAvatarChooserClass)) +#define EMPATHY_IS_AVATAR_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_AVATAR_CHOOSER)) +#define EMPATHY_IS_AVATAR_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EMPATHY_TYPE_AVATAR_CHOOSER)) + +typedef struct _EmpathyAvatarChooser EmpathyAvatarChooser; +typedef struct _EmpathyAvatarChooserClass EmpathyAvatarChooserClass; +typedef struct _EmpathyAvatarChooserPrivate EmpathyAvatarChooserPrivate; + +struct _EmpathyAvatarChooser { + GtkButton parent; +}; + +struct _EmpathyAvatarChooserClass { + GtkButtonClass parent_class; +}; + +GType empathy_avatar_chooser_get_type (void); +GtkWidget *empathy_avatar_chooser_new (void); +void empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser, + EmpathyAvatar *avatar); +void empathy_avatar_chooser_get_image_data (EmpathyAvatarChooser *chooser, + const gchar **data, + gsize *data_size, + const gchar **mime_type); + +#endif /* __EMPATHY_AVATAR_CHOOSER_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-avatar-image.c b/gnome-2-22/libempathy-gtk/empathy-avatar-image.c new file mode 100644 index 000000000..2aeeba458 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-avatar-image.c @@ -0,0 +1,306 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-avatar-image.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "AvatarImage" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_AVATAR_IMAGE, EmpathyAvatarImagePriv)) + +#define MAX_SMALL 64 +#define MAX_LARGE 400 + +typedef struct { + GtkWidget *image; + GtkWidget *popup; + GdkPixbuf *pixbuf; +} EmpathyAvatarImagePriv; + +static void avatar_image_finalize (GObject *object); +static void avatar_image_add_filter (EmpathyAvatarImage *avatar_image); +static void avatar_image_remove_filter (EmpathyAvatarImage *avatar_image); +static gboolean avatar_image_button_press_event (GtkWidget *widget, + GdkEventButton *event); +static gboolean avatar_image_button_release_event (GtkWidget *widget, + GdkEventButton *event); + +G_DEFINE_TYPE (EmpathyAvatarImage, empathy_avatar_image, GTK_TYPE_EVENT_BOX); + +static void +empathy_avatar_image_class_init (EmpathyAvatarImageClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = avatar_image_finalize; + + widget_class->button_press_event = avatar_image_button_press_event; + widget_class->button_release_event = avatar_image_button_release_event; + + g_type_class_add_private (object_class, sizeof (EmpathyAvatarImagePriv)); +} + +static void +empathy_avatar_image_init (EmpathyAvatarImage *avatar_image) +{ + EmpathyAvatarImagePriv *priv; + + priv = GET_PRIV (avatar_image); + + priv->image = gtk_image_new (); + gtk_container_add (GTK_CONTAINER (avatar_image), priv->image); + empathy_avatar_image_set (avatar_image, NULL); + gtk_widget_show (priv->image); + + avatar_image_add_filter (avatar_image); +} + +static void +avatar_image_finalize (GObject *object) +{ + EmpathyAvatarImagePriv *priv; + + priv = GET_PRIV (object); + + avatar_image_remove_filter (EMPATHY_AVATAR_IMAGE (object)); + + if (priv->popup) { + gtk_widget_destroy (priv->popup); + } + + if (priv->pixbuf) { + g_object_unref (priv->pixbuf); + } + + G_OBJECT_CLASS (empathy_avatar_image_parent_class)->finalize (object); +} + +static GdkFilterReturn +avatar_image_filter_func (GdkXEvent *gdkxevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xevent = gdkxevent; + Atom atom; + EmpathyAvatarImagePriv *priv; + + priv = GET_PRIV (data); + + switch (xevent->type) { + case PropertyNotify: + atom = gdk_x11_get_xatom_by_name ("_NET_CURRENT_DESKTOP"); + if (xevent->xproperty.atom == atom) { + if (priv->popup) { + gtk_widget_destroy (priv->popup); + priv->popup = NULL; + } + } + break; + } + + return GDK_FILTER_CONTINUE; +} + +static void +avatar_image_add_filter (EmpathyAvatarImage *avatar_image) +{ + Window window; + GdkWindow *gdkwindow; + gint mask; + + mask = PropertyChangeMask; + + window = GDK_ROOT_WINDOW (); + gdkwindow = gdk_xid_table_lookup (window); + + gdk_error_trap_push (); + if (gdkwindow) { + XWindowAttributes attrs; + XGetWindowAttributes (gdk_display, window, &attrs); + mask |= attrs.your_event_mask; + } + + XSelectInput (gdk_display, window, mask); + + gdk_error_trap_pop (); + + gdk_window_add_filter (NULL, avatar_image_filter_func, avatar_image); +} + +static void +avatar_image_remove_filter (EmpathyAvatarImage *avatar_image) +{ + gdk_window_remove_filter (NULL, avatar_image_filter_func, avatar_image); +} + +static gboolean +avatar_image_button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + EmpathyAvatarImagePriv *priv; + GtkWidget *popup; + GtkWidget *frame; + GtkWidget *image; + gint x, y; + gint popup_width, popup_height; + gint width, height; + GdkPixbuf *pixbuf; + + priv = GET_PRIV (widget); + + if (priv->popup) { + gtk_widget_destroy (priv->popup); + priv->popup = NULL; + } + + if (event->button != 1 || event->type != GDK_BUTTON_PRESS || !priv->pixbuf) { + return FALSE; + } + + popup_width = gdk_pixbuf_get_width (priv->pixbuf); + popup_height = gdk_pixbuf_get_height (priv->pixbuf); + + width = priv->image->allocation.width; + height = priv->image->allocation.height; + + /* Don't show a popup if the popup is smaller then the currently avatar + * image. + */ + if (popup_height <= height && popup_width <= width) { + return TRUE; + } + + pixbuf = empathy_pixbuf_scale_down_if_necessary (priv->pixbuf, MAX_LARGE); + popup_width = gdk_pixbuf_get_width (pixbuf); + popup_height = gdk_pixbuf_get_height (pixbuf); + + popup = gtk_window_new (GTK_WINDOW_POPUP); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); + + gtk_container_add (GTK_CONTAINER (popup), frame); + + image = gtk_image_new (); + gtk_container_add (GTK_CONTAINER (frame), image); + + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); + + gdk_window_get_origin (priv->image->window, &x, &y); + + x = x - (popup_width - width) / 2; + y = y - (popup_height - height) / 2; + + gtk_window_move (GTK_WINDOW (popup), x, y); + + priv->popup = popup; + + gtk_widget_show_all (popup); + + return TRUE; +} + +static gboolean +avatar_image_button_release_event (GtkWidget *widget, GdkEventButton *event) +{ + EmpathyAvatarImagePriv *priv; + + priv = GET_PRIV (widget); + + if (event->button != 1 || event->type != GDK_BUTTON_RELEASE) { + return FALSE; + } + + if (!priv->popup) { + return TRUE; + } + + gtk_widget_destroy (priv->popup); + priv->popup = NULL; + + return TRUE; +} + +GtkWidget * +empathy_avatar_image_new (void) +{ + EmpathyAvatarImage *avatar_image; + + avatar_image = g_object_new (EMPATHY_TYPE_AVATAR_IMAGE, NULL); + + return GTK_WIDGET (avatar_image); +} + +void +empathy_avatar_image_set (EmpathyAvatarImage *avatar_image, + EmpathyAvatar *avatar) +{ + EmpathyAvatarImagePriv *priv = GET_PRIV (avatar_image); + GdkPixbuf *scaled_pixbuf; + + g_return_if_fail (EMPATHY_IS_AVATAR_IMAGE (avatar_image)); + + if (priv->pixbuf) { + g_object_unref (priv->pixbuf); + priv->pixbuf = NULL; + } + + if (avatar) { + priv->pixbuf = empathy_pixbuf_from_data (avatar->data, avatar->len); + } + + if (!priv->pixbuf) { + gtk_image_set_from_icon_name (GTK_IMAGE (priv->image), + "stock_person", + GTK_ICON_SIZE_DIALOG); + return; + } + + scaled_pixbuf = empathy_pixbuf_scale_down_if_necessary (priv->pixbuf, MAX_SMALL); + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), scaled_pixbuf); + + if (scaled_pixbuf != priv->pixbuf) { + gtk_widget_set_tooltip_text (GTK_WIDGET (avatar_image), + _("Click to enlarge")); + } else { + gtk_widget_set_tooltip_text (GTK_WIDGET (avatar_image), + NULL); + } + + g_object_unref (scaled_pixbuf); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-avatar-image.h b/gnome-2-22/libempathy-gtk/empathy-avatar-image.h new file mode 100644 index 000000000..8a28ccb9a --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-avatar-image.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_AVATAR_IMAGE_H__ +#define __EMPATHY_AVATAR_IMAGE_H__ + +#include <gtk/gtkeventbox.h> + +#include <libempathy/empathy-avatar.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_AVATAR_IMAGE (empathy_avatar_image_get_type ()) +#define EMPATHY_AVATAR_IMAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_AVATAR_IMAGE, EmpathyAvatarImage)) +#define EMPATHY_AVATAR_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_AVATAR_IMAGE, EmpathyAvatarImageClass)) +#define EMPATHY_IS_AVATAR_IMAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_AVATAR_IMAGE)) +#define EMPATHY_IS_AVATAR_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_AVATAR_IMAGE)) +#define EMPATHY_AVATAR_IMAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_AVATAR_IMAGE, EmpathyAvatarImageClass)) + +typedef struct _EmpathyAvatarImage EmpathyAvatarImage; +typedef struct _EmpathyAvatarImageClass EmpathyAvatarImageClass; + +struct _EmpathyAvatarImage { + GtkEventBox parent; +}; + +struct _EmpathyAvatarImageClass { + GtkEventBoxClass parent_class; +}; + +GType empathy_avatar_image_get_type (void) G_GNUC_CONST; +GtkWidget * empathy_avatar_image_new (void); +void empathy_avatar_image_set (EmpathyAvatarImage *avatar_image, + EmpathyAvatar *avatar); + +G_END_DECLS + +#endif /* __EMPATHY_AVATAR_IMAGE_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-call-window.c b/gnome-2-22/libempathy-gtk/empathy-call-window.c new file mode 100644 index 000000000..bb7a84a91 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-call-window.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2007 Elliot Fairweather + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk> + */ + +#include <string.h> + +#include <libtelepathy/tp-chan.h> +#include <libtelepathy/tp-helpers.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-account-monitor.h> +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-tp-call.h> +#include <libempathy/empathy-chandler.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +#include <libempathy-gtk/empathy-call-window.h> +#include <libempathy-gtk/empathy-ui-utils.h> + +#define DEBUG_DOMAIN "CallWindow" + +typedef struct +{ + GtkWidget *window; + GtkWidget *status_label; + GtkWidget *start_call_button; + GtkWidget *end_call_button; + GtkWidget *input_volume_scale; + GtkWidget *output_volume_scale; + GtkWidget *input_mute_button; + GtkWidget *output_mute_button; + GtkWidget *preview_video_frame; + GtkWidget *output_video_frame; + GtkWidget *preview_video_socket; + GtkWidget *output_video_socket; + GtkWidget *video_button; + GtkWidget *output_video_label; + + EmpathyTpCall *call; + + GTimeVal start_time; + guint timeout_event_id; + + gboolean is_drawing; +} EmpathyCallWindow; + +static gboolean +call_window_update_timer (gpointer data) +{ + EmpathyCallWindow *window = data; + GTimeVal current; + gchar *str; + glong now, then; + glong time, seconds, minutes, hours; + + g_get_current_time (¤t); + + now = current.tv_sec; + then = (window->start_time).tv_sec; + + time = now - then; + + seconds = time % 60; + time /= 60; + minutes = time % 60; + time /= 60; + hours = time % 60; + + if (hours > 0) + { + str = g_strdup_printf ("Connected - %02ld : %02ld : %02ld", hours, + minutes, seconds); + } + else + { + str = g_strdup_printf ("Connected - %02ld : %02ld", minutes, seconds); + } + + gtk_label_set_text (GTK_LABEL (window->status_label), str); + + g_free (str); + + return TRUE; +} + +static void +call_window_stop_timeout (EmpathyCallWindow *window) +{ + GMainContext *context; + GSource *source; + + context = g_main_context_default (); + + empathy_debug (DEBUG_DOMAIN, "Timer stopped"); + + if (window->timeout_event_id) + { + source = g_main_context_find_source_by_id (context, + window->timeout_event_id); + g_source_destroy (source); + window->timeout_event_id = 0; + } +} + +static void +call_window_set_output_video_is_drawing (EmpathyCallWindow *window, + gboolean is_drawing) +{ + GtkWidget* child; + + child = gtk_bin_get_child (GTK_BIN (window->output_video_frame)); + + empathy_debug (DEBUG_DOMAIN, + "Setting output video is drawing - %d", is_drawing); + + if (is_drawing) + { + if (!window->is_drawing) + { + if (child) + { + gtk_container_remove (GTK_CONTAINER (window->output_video_frame), + child); + } + gtk_container_add (GTK_CONTAINER (window->output_video_frame), + window->output_video_socket); + gtk_widget_show (window->output_video_socket); + empathy_tp_call_add_output_video (window->call, + gtk_socket_get_id (GTK_SOCKET (window->output_video_socket))); + window->is_drawing = is_drawing; + } + } + else + { + if (window->is_drawing) + { + empathy_tp_call_add_output_video (window->call, 0); + if (child) + { + gtk_container_remove (GTK_CONTAINER (window->output_video_frame), + child); + } + gtk_container_add (GTK_CONTAINER (window->output_video_frame), + window->output_video_label); + gtk_widget_show (window->output_video_label); + window->is_drawing = is_drawing; + } + } +} + +static gboolean +call_window_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyCallWindow *window) +{ + GtkWidget *dialog; + gint result; + guint status; + + empathy_debug (DEBUG_DOMAIN, "Delete event occurred"); + + g_object_get (G_OBJECT (window->call), "status", &status, NULL); + + if (status != EMPATHY_TP_CALL_STATUS_CLOSED) + { + dialog = gtk_message_dialog_new (GTK_WINDOW (window->window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, + "This call will be ended. Continue?"); + + result = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + switch (result) + { + case GTK_RESPONSE_YES: + call_window_stop_timeout (window); + call_window_set_output_video_is_drawing (window, FALSE); + empathy_tp_call_close_channel (window->call); + empathy_tp_call_remove_preview_video (window->call, + gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket))); + return FALSE; + default: + return TRUE; + } + } + else + { + empathy_tp_call_remove_preview_video (window->call, + gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket))); + return FALSE; + } +} + +static void +call_window_video_button_toggled_cb (GtkWidget *button, + EmpathyCallWindow *window) +{ + gboolean is_sending; + + is_sending = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + + empathy_debug (DEBUG_DOMAIN, "Send video toggled - %d", is_sending); + + empathy_tp_call_request_video_stream_direction (window->call, is_sending); +} + +static void +call_window_status_changed_cb (EmpathyTpCall *call, + EmpathyCallWindow *window) +{ + EmpathyContact *contact; + guint status; + guint stream_state; + EmpathyTpCallStream *audio_stream; + EmpathyTpCallStream *video_stream; + gboolean is_incoming; + gchar *title; + + g_object_get (window->call, + "status", &status, + "audio-stream", &audio_stream, + "video-stream", &video_stream, + NULL); + + if (video_stream->state > audio_stream->state) + stream_state = video_stream->state; + else + stream_state = audio_stream->state; + + empathy_debug (DEBUG_DOMAIN, "Status changed - status: %d, stream state: %d", + status, stream_state); + + if (window->timeout_event_id) + call_window_stop_timeout (window); + + if (status == EMPATHY_TP_CALL_STATUS_CLOSED) + { + gtk_label_set_text (GTK_LABEL (window->status_label), "Closed"); + gtk_widget_set_sensitive (window->end_call_button, FALSE); + gtk_widget_set_sensitive (window->start_call_button, FALSE); + + call_window_set_output_video_is_drawing (window, FALSE); + } + else if (stream_state == TP_MEDIA_STREAM_STATE_DISCONNECTED) + gtk_label_set_text (GTK_LABEL (window->status_label), "Disconnected"); + else if (status == EMPATHY_TP_CALL_STATUS_PENDING) + { + g_object_get (G_OBJECT (window->call), "contact", &contact, NULL); + + title = g_strdup_printf ("%s - Empathy Call", + empathy_contact_get_name (contact)); + gtk_window_set_title (GTK_WINDOW (window->window), title); + + gtk_label_set_text (GTK_LABEL (window->status_label), "Ringing"); + gtk_widget_set_sensitive (window->end_call_button, TRUE); + gtk_widget_set_sensitive (window->video_button, TRUE); + + g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL); + if (is_incoming) + gtk_widget_set_sensitive (window->start_call_button, TRUE); + else + g_signal_connect (GTK_OBJECT (window->video_button), "toggled", + G_CALLBACK (call_window_video_button_toggled_cb), + window); + } + else if (status == EMPATHY_TP_CALL_STATUS_ACCEPTED) + { + if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTING) + gtk_label_set_text (GTK_LABEL (window->status_label), "Connecting"); + else if (stream_state == TP_MEDIA_STREAM_STATE_CONNECTED) + { + if ((window->start_time).tv_sec == 0) + g_get_current_time (&(window->start_time)); + window->timeout_event_id = g_timeout_add (1000, + call_window_update_timer, window); + empathy_debug (DEBUG_DOMAIN, "Timer started"); + } + } +} + +static void +call_window_receiving_video_cb (EmpathyTpCall *call, + gboolean receiving_video, + EmpathyCallWindow *window) +{ + empathy_debug (DEBUG_DOMAIN, "Receiving video signal received"); + + call_window_set_output_video_is_drawing (window, receiving_video); +} + +static void +call_window_sending_video_cb (EmpathyTpCall *call, + gboolean sending_video, + EmpathyCallWindow *window) +{ + empathy_debug (DEBUG_DOMAIN, "Sending video signal received"); + + g_signal_handlers_block_by_func (window->video_button, + call_window_video_button_toggled_cb, window); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (window->video_button), + sending_video); + g_signal_handlers_unblock_by_func (window->video_button, + call_window_video_button_toggled_cb, window); +} + +static void +call_window_socket_realized_cb (GtkWidget *widget, + EmpathyCallWindow *window) +{ + if (widget == window->preview_video_socket) + { + empathy_debug (DEBUG_DOMAIN, "Preview socket realized"); + empathy_tp_call_add_preview_video (window->call, + gtk_socket_get_id (GTK_SOCKET (window->preview_video_socket))); + } + else + { + empathy_debug (DEBUG_DOMAIN, "Output socket realized"); + } +} + +static void +call_window_start_call_button_clicked_cb (GtkWidget *widget, + EmpathyCallWindow *window) +{ + gboolean send_video; + gboolean is_incoming; + + empathy_debug (DEBUG_DOMAIN, "Start call clicked"); + + gtk_widget_set_sensitive (window->start_call_button, FALSE); + g_object_get (G_OBJECT (window->call), "is-incoming", &is_incoming, NULL); + if (is_incoming) + { + empathy_tp_call_accept_incoming_call (window->call); + send_video = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON (window->video_button)); + empathy_tp_call_request_video_stream_direction (window->call, send_video); + g_signal_connect (GTK_OBJECT (window->video_button), "toggled", + G_CALLBACK (call_window_video_button_toggled_cb), window); + } +} + +static void +call_window_end_call_button_clicked_cb (GtkWidget *widget, + EmpathyCallWindow *window) +{ + empathy_debug (DEBUG_DOMAIN, "End call clicked"); + + call_window_set_output_video_is_drawing (window, FALSE); + empathy_tp_call_close_channel (window->call); + gtk_widget_set_sensitive (window->end_call_button, FALSE); + gtk_widget_set_sensitive (window->start_call_button, FALSE); +} + +static void +call_window_output_volume_changed_cb (GtkWidget *scale, + EmpathyCallWindow *window) +{ + guint volume; + + volume = (guint) gtk_range_get_value (GTK_RANGE (scale)); + + empathy_debug (DEBUG_DOMAIN, "Output volume changed - %u", volume); + + empathy_tp_call_set_output_volume (window->call, volume); +} + +static void +call_window_output_mute_button_toggled_cb (GtkWidget *button, + EmpathyCallWindow *window) +{ + gboolean is_muted; + + is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + + empathy_debug (DEBUG_DOMAIN, "Mute output toggled - %d", is_muted); + + empathy_tp_call_mute_output (window->call, is_muted); +} + +static void +call_window_input_mute_button_toggled_cb (GtkWidget *button, + EmpathyCallWindow *window) +{ + gboolean is_muted; + + is_muted = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + + empathy_debug (DEBUG_DOMAIN, "Mute input toggled - %d", is_muted); + + empathy_tp_call_mute_input (window->call, is_muted); +} + +static void +call_window_destroy_cb (GtkWidget *widget, + EmpathyCallWindow *window) +{ + g_signal_handlers_disconnect_by_func (window->call, + call_window_status_changed_cb, window); + g_signal_handlers_disconnect_by_func (window->call, + call_window_receiving_video_cb, window); + g_signal_handlers_disconnect_by_func (window->call, + call_window_sending_video_cb, window); + + g_object_unref (window->call); + g_object_unref (window->output_video_socket); + g_object_unref (window->preview_video_socket); + g_object_unref (window->output_video_label); + + g_slice_free (EmpathyCallWindow, window); +} + +GtkWidget * +empathy_call_window_new (EmpathyTpCall *call) +{ + EmpathyCallWindow *window; + GladeXML *glade; + guint status; + + g_return_val_if_fail (EMPATHY_IS_TP_CALL (call), NULL); + + window = g_slice_new0 (EmpathyCallWindow); + window->call = g_object_ref (call); + + glade = empathy_glade_get_file ("empathy-call-window.glade", + "window", + NULL, + "window", &window->window, + "status_label", &window->status_label, + "start_call_button", &window->start_call_button, + "end_call_button", &window->end_call_button, + "input_volume_scale", &window->input_volume_scale, + "output_volume_scale", &window->output_volume_scale, + "input_mute_button", &window->input_mute_button, + "output_mute_button", &window->output_mute_button, + "preview_video_frame", &window->preview_video_frame, + "output_video_frame", &window->output_video_frame, + "video_button", &window->video_button, + NULL); + + empathy_glade_connect (glade, + window, + "window", "destroy", call_window_destroy_cb, + "window", "delete_event", call_window_delete_event_cb, + "input_mute_button", "toggled", call_window_input_mute_button_toggled_cb, + "output_mute_button", "toggled", call_window_output_mute_button_toggled_cb, + "output_volume_scale", "value-changed", call_window_output_volume_changed_cb, + "start_call_button", "clicked", call_window_start_call_button_clicked_cb, + "end_call_button", "clicked", call_window_end_call_button_clicked_cb, + NULL); + + g_object_unref (glade); + + /* Output video label */ + window->output_video_label = g_object_ref (gtk_label_new ("No video output")); + gtk_container_add (GTK_CONTAINER (window->output_video_frame), + window->output_video_label); + gtk_widget_show (window->output_video_label); + + /* Output video socket */ + window->output_video_socket = g_object_ref (gtk_socket_new ()); + g_signal_connect (GTK_OBJECT (window->output_video_socket), "realize", + G_CALLBACK (call_window_socket_realized_cb), window); + gtk_widget_show (window->output_video_socket); + + /* Preview video socket */ + window->preview_video_socket = g_object_ref (gtk_socket_new ()); + g_signal_connect (GTK_OBJECT (window->preview_video_socket), "realize", + G_CALLBACK (call_window_socket_realized_cb), window); + gtk_container_add (GTK_CONTAINER (window->preview_video_frame), + window->preview_video_socket); + gtk_widget_show (window->preview_video_socket); + + g_signal_connect (G_OBJECT (window->call), "status-changed", + G_CALLBACK (call_window_status_changed_cb), + window); + g_signal_connect (G_OBJECT (window->call), "receiving-video", + G_CALLBACK (call_window_receiving_video_cb), + window); + g_signal_connect (G_OBJECT (window->call), "sending-video", + G_CALLBACK (call_window_sending_video_cb), + window); + + window->is_drawing = FALSE; + + g_object_get (G_OBJECT (window->call), "status", &status, NULL); + + if (status == EMPATHY_TP_CALL_STATUS_READYING) + { + gtk_window_set_title (GTK_WINDOW (window->window), "Empathy Call"); + gtk_label_set_text (GTK_LABEL (window->status_label), "Readying"); + } + + gtk_widget_show (window->window); + + return window->window; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-call-window.glade b/gnome-2-22/libempathy-gtk/empathy-call-window.glade new file mode 100644 index 000000000..2e388cf07 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-call-window.glade @@ -0,0 +1,300 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.2 on Fri Apr 4 14:56:48 2008 --> +<glade-interface> + <widget class="GtkWindow" id="window"> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="border_width">10</property> + <property name="spacing">10</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkLabel" id="status_label"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="spacing">10</property> + <child> + <widget class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <child> + <widget class="GtkHBox" id="hbox3"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="homogeneous">True</property> + <child> + <widget class="GtkVBox" id="vbox4"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="extension_events">GDK_EXTENSION_EVENTS_CURSOR</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Input</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + </packing> + </child> + <child> + <widget class="GtkVScale" id="input_volume_scale"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="adjustment">100 0 100 1 0 0</property> + <property name="inverted">True</property> + <property name="draw_value">False</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkToggleButton" id="input_mute_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Mute</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">5</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox5"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Output</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + </packing> + </child> + <child> + <widget class="GtkVScale" id="output_volume_scale"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="adjustment">100 0 100 1 0 0</property> + <property name="inverted">True</property> + <property name="draw_value">False</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkToggleButton" id="output_mute_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Mute</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Volume</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkAspectFrame" id="output_video_frame"> + <property name="width_request">352</property> + <property name="height_request">288</property> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label_xalign">0</property> + <property name="ratio">1.2200000286102295</property> + <property name="obey_child">False</property> + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox3"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkAspectFrame" id="preview_video_frame"> + <property name="width_request">176</property> + <property name="height_request">144</property> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label_xalign">0</property> + <property name="ratio">1.2200000286102295</property> + <property name="obey_child">False</property> + <child> + <placeholder/> + </child> + </widget> + </child> + <child> + <widget class="GtkCheckButton" id="video_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Send Video</property> + <property name="response_id">0</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="padding">10</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator2"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="start_call_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Start Call</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="padding">10</property> + <property name="pack_type">GTK_PACK_END</property> + <property name="position">4</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="end_call_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">End Call</property> + <property name="response_id">0</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">10</property> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-call-window.h b/gnome-2-22/libempathy-gtk/empathy-call-window.h new file mode 100644 index 000000000..7d65aaeae --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-call-window.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007 Elliot Fairweather + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Elliot Fairweather <elliot.fairweather@collabora.co.uk> + */ + +#ifndef __EMPATHY_CALL_WINDOW_H__ +#define __EMPATHY_CALL_WINDOW_H__ + +#include <gtk/gtk.h> + +#include <libempathy/empathy-tp-call.h> + +G_BEGIN_DECLS + +GtkWidget *empathy_call_window_new (EmpathyTpCall *call); + +G_END_DECLS + +#endif /* __EMPATHY_CALL_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.c b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.c new file mode 100644 index 000000000..76e3b3bd7 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.c @@ -0,0 +1,121 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Raphael Slinckx <raphael@slinckx.net> + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Raphael Slinckx <raphael@slinckx.net> + */ + +#include <gtk/gtktreeview.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-cell-renderer-activatable.h" + +#define DEBUG_DOMAIN "CellRendererActivatable" + +static void empathy_cell_renderer_activatable_init (EmpathyCellRendererActivatable *cell); +static void empathy_cell_renderer_activatable_class_init (EmpathyCellRendererActivatableClass *klass); +static gboolean cell_renderer_activatable_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags); + +enum { + PATH_ACTIVATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (EmpathyCellRendererActivatable, empathy_cell_renderer_activatable, GTK_TYPE_CELL_RENDERER_PIXBUF) + +static void +empathy_cell_renderer_activatable_init (EmpathyCellRendererActivatable *cell) +{ + g_object_set (cell, + "xpad", 0, + "ypad", 0, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "follow-state", TRUE, + NULL); +} + +static void +empathy_cell_renderer_activatable_class_init (EmpathyCellRendererActivatableClass *klass) +{ + GtkCellRendererClass *cell_class; + + cell_class = GTK_CELL_RENDERER_CLASS (klass); + cell_class->activate = cell_renderer_activatable_activate; + + signals[PATH_ACTIVATED] = + g_signal_new ("path-activated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); +} + +GtkCellRenderer * +empathy_cell_renderer_activatable_new (void) +{ + return g_object_new (EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE, NULL); +} + +static gboolean +cell_renderer_activatable_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path_string, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + EmpathyCellRendererActivatable *activatable; + gint ex, ey, bx, by, bw, bh; + + activatable = EMPATHY_CELL_RENDERER_ACTIVATABLE (cell); + + if (!GTK_IS_TREE_VIEW (widget) || + event->type != GDK_BUTTON_PRESS) { + return FALSE; + } + + ex = (gint) ((GdkEventButton *) event)->x; + ey = (gint) ((GdkEventButton *) event)->y; + bx = background_area->x; + by = background_area->y; + bw = background_area->width; + bh = background_area->height; + + if (ex < bx || ex > (bx+bw) || ey < by || ey > (by+bh)){ + /* Click wasn't on the icon */ + return FALSE; + } + + g_signal_emit (activatable, signals[PATH_ACTIVATED], 0, path_string); + + return TRUE; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.h b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.h new file mode 100644 index 000000000..6520e29ca --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-activatable.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Raphael Slinckx <raphael@slinckx.net> + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Raphael Slinckx <raphael@slinckx.net> + */ + +#ifndef __EMPATHY_CELL_RENDERER_ACTIVATABLE_H__ +#define __EMPATHY_CELL_RENDERER_ACTIVATABLE_H__ + +#include <gtk/gtkcellrendererpixbuf.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE (empathy_cell_renderer_activatable_get_type ()) +#define EMPATHY_CELL_RENDERER_ACTIVATABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE, EmpathyCellRendererActivatable)) +#define EMPATHY_CELL_RENDERER_ACTIVATABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE, EmpathyCellRendererActivatableClass)) +#define EMPATHY_IS_CELL_RENDERER_ACTIVATABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE)) +#define EMPATHY_IS_CELL_RENDERER_ACTIVATABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE)) +#define EMPATHY_CELL_RENDERER_ACTIVATABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_CELL_RENDERER_ACTIVATABLE, EmpathyCellRendererActivatableClass)) + +typedef struct _EmpathyCellRendererActivatable EmpathyCellRendererActivatable; +typedef struct _EmpathyCellRendererActivatableClass EmpathyCellRendererActivatableClass; + +struct _EmpathyCellRendererActivatable { + GtkCellRendererPixbuf parent; +}; + +struct _EmpathyCellRendererActivatableClass { + GtkCellRendererPixbufClass parent_class; +}; + +GType empathy_cell_renderer_activatable_get_type (void) G_GNUC_CONST; +GtkCellRenderer *empathy_cell_renderer_activatable_new (void); + +G_END_DECLS + +#endif /* __EMPATHY_CELL_RENDERER_ACTIVATABLE_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.c b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.c new file mode 100644 index 000000000..51930ab6a --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.c @@ -0,0 +1,482 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Kristian Rietveld <kris@imendio.com> + */ + +/* To do: + * - should probably cancel animation if model changes + * - need to handle case where node-in-animation is removed + * - it only handles a single animation at a time; but I guess users + * aren't fast enough to trigger two or more animations at once anyway :P + * (could guard for this by just cancelling the "old" animation, and + * start the new one). + */ + +#include <gtk/gtktreeview.h> + +#include "empathy-cell-renderer-expander.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpanderPriv)) + +static void empathy_cell_renderer_expander_init (EmpathyCellRendererExpander *expander); +static void empathy_cell_renderer_expander_class_init (EmpathyCellRendererExpanderClass *klass); +static void empathy_cell_renderer_expander_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void empathy_cell_renderer_expander_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void empathy_cell_renderer_expander_finalize (GObject *object); +static void empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void empathy_cell_renderer_expander_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags); +static gboolean empathy_cell_renderer_expander_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags); + +enum { + PROP_0, + PROP_EXPANDER_STYLE, + PROP_EXPANDER_SIZE, + PROP_ACTIVATABLE +}; + +typedef struct _EmpathyCellRendererExpanderPriv EmpathyCellRendererExpanderPriv; + +struct _EmpathyCellRendererExpanderPriv { + GtkExpanderStyle expander_style; + gint expander_size; + + GtkTreeView *animation_view; + GtkTreeRowReference *animation_node; + GtkExpanderStyle animation_style; + guint animation_timeout; + GdkRectangle animation_area; + + guint activatable : 1; + guint animation_expanding : 1; +}; + +G_DEFINE_TYPE (EmpathyCellRendererExpander, empathy_cell_renderer_expander, GTK_TYPE_CELL_RENDERER) + +static void +empathy_cell_renderer_expander_init (EmpathyCellRendererExpander *expander) +{ + EmpathyCellRendererExpanderPriv *priv; + + priv = GET_PRIV (expander); + + priv->expander_style = GTK_EXPANDER_COLLAPSED; + priv->expander_size = 12; + priv->activatable = TRUE; + priv->animation_node = NULL; + + GTK_CELL_RENDERER (expander)->xpad = 2; + GTK_CELL_RENDERER (expander)->ypad = 2; + GTK_CELL_RENDERER (expander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE; +} + +static void +empathy_cell_renderer_expander_class_init (EmpathyCellRendererExpanderClass *klass) +{ + GObjectClass *object_class; + GtkCellRendererClass *cell_class; + + object_class = G_OBJECT_CLASS (klass); + cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = empathy_cell_renderer_expander_finalize; + + object_class->get_property = empathy_cell_renderer_expander_get_property; + object_class->set_property = empathy_cell_renderer_expander_set_property; + + cell_class->get_size = empathy_cell_renderer_expander_get_size; + cell_class->render = empathy_cell_renderer_expander_render; + cell_class->activate = empathy_cell_renderer_expander_activate; + + g_object_class_install_property (object_class, + PROP_EXPANDER_STYLE, + g_param_spec_enum ("expander-style", + "Expander Style", + "Style to use when painting the expander", + GTK_TYPE_EXPANDER_STYLE, + GTK_EXPANDER_COLLAPSED, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_EXPANDER_SIZE, + g_param_spec_int ("expander-size", + "Expander Size", + "The size of the expander", + 0, + G_MAXINT, + 12, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ACTIVATABLE, + g_param_spec_boolean ("activatable", + "Activatable", + "The expander can be activated", + TRUE, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyCellRendererExpanderPriv)); +} + +static void +empathy_cell_renderer_expander_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyCellRendererExpander *expander; + EmpathyCellRendererExpanderPriv *priv; + + expander = EMPATHY_CELL_RENDERER_EXPANDER (object); + priv = GET_PRIV (expander); + + switch (param_id) { + case PROP_EXPANDER_STYLE: + g_value_set_enum (value, priv->expander_style); + break; + + case PROP_EXPANDER_SIZE: + g_value_set_int (value, priv->expander_size); + break; + + case PROP_ACTIVATABLE: + g_value_set_boolean (value, priv->activatable); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +empathy_cell_renderer_expander_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyCellRendererExpander *expander; + EmpathyCellRendererExpanderPriv *priv; + + expander = EMPATHY_CELL_RENDERER_EXPANDER (object); + priv = GET_PRIV (expander); + + switch (param_id) { + case PROP_EXPANDER_STYLE: + priv->expander_style = g_value_get_enum (value); + break; + + case PROP_EXPANDER_SIZE: + priv->expander_size = g_value_get_int (value); + break; + + case PROP_ACTIVATABLE: + priv->activatable = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +empathy_cell_renderer_expander_finalize (GObject *object) +{ + EmpathyCellRendererExpanderPriv *priv; + + priv = GET_PRIV (object); + + if (priv->animation_timeout) { + g_source_remove (priv->animation_timeout); + priv->animation_timeout = 0; + } + + if (priv->animation_node) { + gtk_tree_row_reference_free (priv->animation_node); + } + + (* G_OBJECT_CLASS (empathy_cell_renderer_expander_parent_class)->finalize) (object); +} + +GtkCellRenderer * +empathy_cell_renderer_expander_new (void) +{ + return g_object_new (EMPATHY_TYPE_CELL_RENDERER_EXPANDER, NULL); +} + +static void +empathy_cell_renderer_expander_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + EmpathyCellRendererExpander *expander; + EmpathyCellRendererExpanderPriv *priv; + + expander = (EmpathyCellRendererExpander*) cell; + priv = GET_PRIV (expander); + + if (cell_area) { + if (x_offset) { + *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad))); + *x_offset = MAX (*x_offset, 0); + } + + if (y_offset) { + *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad))); + *y_offset = MAX (*y_offset, 0); + } + } else { + if (x_offset) + *x_offset = 0; + + if (y_offset) + *y_offset = 0; + } + + if (width) + *width = cell->xpad * 2 + priv->expander_size; + + if (height) + *height = cell->ypad * 2 + priv->expander_size; +} + +static void +empathy_cell_renderer_expander_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags) +{ + EmpathyCellRendererExpander *expander; + EmpathyCellRendererExpanderPriv *priv; + GtkExpanderStyle expander_style; + gint x_offset, y_offset; + + expander = (EmpathyCellRendererExpander*) cell; + priv = GET_PRIV (expander); + + if (priv->animation_node) { + GtkTreePath *path; + GdkRectangle rect; + + /* Not sure if I like this ... */ + path = gtk_tree_row_reference_get_path (priv->animation_node); + gtk_tree_view_get_background_area (priv->animation_view, path, + NULL, &rect); + gtk_tree_path_free (path); + + if (background_area->y == rect.y) + expander_style = priv->animation_style; + else + expander_style = priv->expander_style; + } else + expander_style = priv->expander_style; + + empathy_cell_renderer_expander_get_size (cell, widget, cell_area, + &x_offset, &y_offset, + NULL, NULL); + + gtk_paint_expander (widget->style, + window, + GTK_STATE_NORMAL, + expose_area, + widget, + "treeview", + cell_area->x + x_offset + cell->xpad + priv->expander_size / 2, + cell_area->y + y_offset + cell->ypad + priv->expander_size / 2, + expander_style); +} + +static void +invalidate_node (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GdkWindow *bin_window; + GdkRectangle rect; + + bin_window = gtk_tree_view_get_bin_window (tree_view); + + gtk_tree_view_get_background_area (tree_view, path, NULL, &rect); + + rect.x = 0; + rect.width = GTK_WIDGET (tree_view)->allocation.width; + + gdk_window_invalidate_rect (bin_window, &rect, TRUE); +} + +static gboolean +do_animation (EmpathyCellRendererExpander *expander) +{ + EmpathyCellRendererExpanderPriv *priv; + GtkTreePath *path; + gboolean done = FALSE; + + priv = GET_PRIV (expander); + + if (priv->animation_expanding) { + if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) + priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED; + else if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) { + priv->animation_style = GTK_EXPANDER_EXPANDED; + done = TRUE; + } + } else { + if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) + priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED; + else if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) { + priv->animation_style = GTK_EXPANDER_COLLAPSED; + done = TRUE; + } + } + + path = gtk_tree_row_reference_get_path (priv->animation_node); + invalidate_node (priv->animation_view, path); + gtk_tree_path_free (path); + + if (done) { + gtk_tree_row_reference_free (priv->animation_node); + priv->animation_node = NULL; + priv->animation_timeout = 0; + } + + return !done; +} + +static gboolean +animation_timeout (gpointer data) +{ + gboolean retval; + + GDK_THREADS_ENTER (); + + retval = do_animation (data); + + GDK_THREADS_LEAVE (); + + return retval; +} + +static void +empathy_cell_renderer_expander_start_animation (EmpathyCellRendererExpander *expander, + GtkTreeView *tree_view, + GtkTreePath *path, + gboolean expanding, + GdkRectangle *background_area) +{ + EmpathyCellRendererExpanderPriv *priv; + + priv = GET_PRIV (expander); + + if (expanding) { + priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED; + } else { + priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED; + } + + invalidate_node (tree_view, path); + + priv->animation_expanding = expanding; + priv->animation_view = tree_view; + priv->animation_node = gtk_tree_row_reference_new (gtk_tree_view_get_model (tree_view), path); + priv->animation_timeout = g_timeout_add (50, animation_timeout, expander); +} + +static gboolean +empathy_cell_renderer_expander_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path_string, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + EmpathyCellRendererExpander *expander; + EmpathyCellRendererExpanderPriv *priv; + GtkTreePath *path; + gboolean animate; + gboolean expanding; + + expander = EMPATHY_CELL_RENDERER_EXPANDER (cell); + priv = GET_PRIV (cell); + + if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable) + return FALSE; + + path = gtk_tree_path_new_from_string (path_string); + + if (gtk_tree_path_get_depth (path) > 1) { + gtk_tree_path_free (path); + return TRUE; + } + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)), + "gtk-enable-animations", &animate, + NULL); + + if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) { + gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path); + expanding = FALSE; + } else { + gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE); + expanding = TRUE; + } + + if (animate) { + empathy_cell_renderer_expander_start_animation (expander, + GTK_TREE_VIEW (widget), + path, + expanding, + background_area); + } + + gtk_tree_path_free (path); + + return TRUE; +} diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.h b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.h new file mode 100644 index 000000000..d7e5f74b0 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-expander.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Kristian Rietveld <kris@imendio.com> + */ + +#ifndef __EMPATHY_CELL_RENDERER_EXPANDER_H__ +#define __EMPATHY_CELL_RENDERER_EXPANDER_H__ + +#include <gtk/gtkcellrenderer.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CELL_RENDERER_EXPANDER (empathy_cell_renderer_expander_get_type ()) +#define EMPATHY_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpander)) +#define EMPATHY_CELL_RENDERER_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpanderClass)) +#define EMPATHY_IS_CELL_RENDERER_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_CELL_RENDERER_EXPANDER)) +#define EMPATHY_IS_CELL_RENDERER_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EMPATHY_TYPE_CELL_RENDERER_EXPANDER)) +#define EMPATHY_CELL_RENDERER_EXPANDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_CELL_RENDERER_EXPANDER, EmpathyCellRendererExpanderClass)) + +typedef struct _EmpathyCellRendererExpander EmpathyCellRendererExpander; +typedef struct _EmpathyCellRendererExpanderClass EmpathyCellRendererExpanderClass; + +struct _EmpathyCellRendererExpander { + GtkCellRenderer parent; +}; + +struct _EmpathyCellRendererExpanderClass { + GtkCellRendererClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +GType empathy_cell_renderer_expander_get_type (void) G_GNUC_CONST; +GtkCellRenderer *empathy_cell_renderer_expander_new (void); + +G_END_DECLS + +#endif /* __EMPATHY_CELL_RENDERER_EXPANDER_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.c b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.c new file mode 100644 index 000000000..d48de32b8 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.c @@ -0,0 +1,368 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include "empathy-cell-renderer-text.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextPriv)) + +struct _EmpathyCellRendererTextPriv { + gchar *name; + gchar *status; + gboolean is_group; + + gboolean is_valid; + gboolean is_selected; + + gboolean show_status; +}; + +static void empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass); +static void empathy_cell_renderer_text_init (EmpathyCellRendererText *cell); +static void cell_renderer_text_finalize (GObject *object); +static void cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void cell_renderer_text_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void cell_renderer_text_render (GtkCellRenderer *cell, + GdkDrawable *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags); +static void cell_renderer_text_update_text (EmpathyCellRendererText *cell, + GtkWidget *widget, + gboolean selected); + +/* Properties */ +enum { + PROP_0, + PROP_NAME, + PROP_STATUS, + PROP_IS_GROUP, + PROP_SHOW_STATUS, +}; + +G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT); + +static void +empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass) +{ + GObjectClass *object_class; + GtkCellRendererClass *cell_class; + + object_class = G_OBJECT_CLASS (klass); + cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->finalize = cell_renderer_text_finalize; + + object_class->get_property = cell_renderer_text_get_property; + object_class->set_property = cell_renderer_text_set_property; + + cell_class->get_size = cell_renderer_text_get_size; + cell_class->render = cell_renderer_text_render; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Contact name", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_STATUS, + g_param_spec_string ("status", + "Status", + "Contact status string", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_IS_GROUP, + g_param_spec_boolean ("is_group", + "Is group", + "Whether this cell is a group", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SHOW_STATUS, + g_param_spec_boolean ("show-status", + "Show status", + "Whether to show the status line", + TRUE, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyCellRendererTextPriv)); +} + +static void +empathy_cell_renderer_text_init (EmpathyCellRendererText *cell) +{ + EmpathyCellRendererTextPriv *priv; + + priv = GET_PRIV (cell); + + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + priv->name = g_strdup (""); + priv->status = g_strdup (""); + priv->show_status = TRUE; +} + +static void +cell_renderer_text_finalize (GObject *object) +{ + EmpathyCellRendererText *cell; + EmpathyCellRendererTextPriv *priv; + + cell = EMPATHY_CELL_RENDERER_TEXT (object); + priv = GET_PRIV (cell); + + g_free (priv->name); + g_free (priv->status); + + (G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object); +} + +static void +cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyCellRendererText *cell; + EmpathyCellRendererTextPriv *priv; + + cell = EMPATHY_CELL_RENDERER_TEXT (object); + priv = GET_PRIV (cell); + + switch (param_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_STATUS: + g_value_set_string (value, priv->status); + break; + case PROP_IS_GROUP: + g_value_set_boolean (value, priv->is_group); + break; + case PROP_SHOW_STATUS: + g_value_set_boolean (value, priv->show_status); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyCellRendererText *cell; + EmpathyCellRendererTextPriv *priv; + const gchar *str; + + cell = EMPATHY_CELL_RENDERER_TEXT (object); + priv = GET_PRIV (cell); + + switch (param_id) { + case PROP_NAME: + g_free (priv->name); + str = g_value_get_string (value); + priv->name = g_strdup (str ? str : ""); + g_strdelimit (priv->name, "\n\r\t", ' '); + priv->is_valid = FALSE; + break; + case PROP_STATUS: + g_free (priv->status); + str = g_value_get_string (value); + priv->status = g_strdup (str ? str : ""); + g_strdelimit (priv->status, "\n\r\t", ' '); + priv->is_valid = FALSE; + break; + case PROP_IS_GROUP: + priv->is_group = g_value_get_boolean (value); + priv->is_valid = FALSE; + break; + case PROP_SHOW_STATUS: + priv->show_status = g_value_get_boolean (value); + priv->is_valid = FALSE; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +cell_renderer_text_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + EmpathyCellRendererText *celltext; + EmpathyCellRendererTextPriv *priv; + + celltext = EMPATHY_CELL_RENDERER_TEXT (cell); + priv = GET_PRIV (cell); + + /* Only update if not already valid so we get the right size. */ + cell_renderer_text_update_text (celltext, widget, priv->is_selected); + + (GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->get_size) (cell, widget, + cell_area, + x_offset, y_offset, + width, height); +} + +static void +cell_renderer_text_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags) +{ + EmpathyCellRendererText *celltext; + + celltext = EMPATHY_CELL_RENDERER_TEXT (cell); + + cell_renderer_text_update_text (celltext, + widget, + (flags & GTK_CELL_RENDERER_SELECTED)); + + (GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->render) ( + cell, window, + widget, + background_area, + cell_area, + expose_area, flags); +} + +static void +cell_renderer_text_update_text (EmpathyCellRendererText *cell, + GtkWidget *widget, + gboolean selected) +{ + EmpathyCellRendererTextPriv *priv; + PangoAttrList *attr_list; + PangoAttribute *attr_color, *attr_style, *attr_size; + GtkStyle *style; + gchar *str; + + priv = GET_PRIV (cell); + + if (priv->is_valid && priv->is_selected == selected) { + return; + } + + if (priv->is_group) { + g_object_set (cell, + "visible", TRUE, + "weight", PANGO_WEIGHT_BOLD, + "text", priv->name, + "attributes", NULL, + "xpad", 1, + "ypad", 1, + NULL); + + priv->is_selected = selected; + priv->is_valid = TRUE; + return; + } + + style = gtk_widget_get_style (widget); + + attr_list = pango_attr_list_new (); + + attr_style = pango_attr_style_new (PANGO_STYLE_ITALIC); + attr_style->start_index = strlen (priv->name) + 1; + attr_style->end_index = -1; + pango_attr_list_insert (attr_list, attr_style); + + if (!selected) { + GdkColor color; + + color = style->text_aa[GTK_STATE_NORMAL]; + + attr_color = pango_attr_foreground_new (color.red, color.green, color.blue); + attr_color->start_index = attr_style->start_index; + attr_color->end_index = -1; + pango_attr_list_insert (attr_list, attr_color); + } + + attr_size = pango_attr_size_new (pango_font_description_get_size (style->font_desc) / 1.2); + + attr_size->start_index = attr_style->start_index; + attr_size->end_index = -1; + pango_attr_list_insert (attr_list, attr_size); + + if (!priv->status || !priv->status[0] || !priv->show_status) { + str = g_strdup (priv->name); + } else { + str = g_strdup_printf ("%s\n%s", priv->name, priv->status); + } + + g_object_set (cell, + "visible", TRUE, + "weight", PANGO_WEIGHT_NORMAL, + "text", str, + "attributes", attr_list, + "xpad", 0, + "ypad", 1, + NULL); + + g_free (str); + pango_attr_list_unref (attr_list); + + priv->is_selected = selected; + priv->is_valid = TRUE; +} + +GtkCellRenderer * +empathy_cell_renderer_text_new (void) +{ + return g_object_new (EMPATHY_TYPE_CELL_RENDERER_TEXT, NULL); +} diff --git a/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.h b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.h new file mode 100644 index 000000000..76cef3120 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-cell-renderer-text.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + */ + +#ifndef __EMPATHY_CELL_RENDERER_TEXT_H__ +#define __EMPATHY_CELL_RENDERER_TEXT_H__ + +#include <gtk/gtkcellrenderertext.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CELL_RENDERER_TEXT (empathy_cell_renderer_text_get_type ()) +#define EMPATHY_CELL_RENDERER_TEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererText)) +#define EMPATHY_CELL_RENDERER_TEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextClass)) +#define EMPATHY_IS_CELL_RENDERER_TEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CELL_RENDERER_TEXT)) +#define EMPATHY_IS_CELL_RENDERER_TEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CELL_RENDERER_TEXT)) +#define EMPATHY_CELL_RENDERER_TEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextClass)) + +typedef struct _EmpathyCellRendererText EmpathyCellRendererText; +typedef struct _EmpathyCellRendererTextClass EmpathyCellRendererTextClass; +typedef struct _EmpathyCellRendererTextPriv EmpathyCellRendererTextPriv; + +struct _EmpathyCellRendererText { + GtkCellRendererText parent; + + EmpathyCellRendererTextPriv *priv; +}; + +struct _EmpathyCellRendererTextClass { + GtkCellRendererTextClass parent_class; +}; + +GType empathy_cell_renderer_text_get_type (void) G_GNUC_CONST; +GtkCellRenderer * empathy_cell_renderer_text_new (void); + +G_END_DECLS + +#endif /* __EMPATHY_CELL_RENDERER_TEXT_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-chat-view.c b/gnome-2-22/libempathy-gtk/empathy-chat-view.c new file mode 100644 index 000000000..f4ad7f316 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat-view.c @@ -0,0 +1,1570 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#include "config.h" + +#include <sys/types.h> +#include <string.h> +#include <time.h> + +#include <glib/gi18n.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkimage.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenuitem.h> +#include <gtk/gtkimagemenuitem.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtksizegroup.h> +#include <glade/glade.h> + +#include <telepathy-glib/util.h> +#include <libmissioncontrol/mc-account.h> + +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> + +#include "empathy-chat-view.h" +#include "empathy-chat.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-theme-manager.h" +#include "empathy-ui-utils.h" +#include "empathy-smiley-manager.h" + +#define DEBUG_DOMAIN "ChatView" + +/* Number of seconds between timestamps when using normal mode, 5 minutes. */ +#define TIMESTAMP_INTERVAL 300 + +#define MAX_LINES 800 +#define MAX_SCROLL_TIME 0.4 /* seconds */ +#define SCROLL_DELAY 33 /* milliseconds */ + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewPriv)) + +struct _EmpathyChatViewPriv { + GtkTextBuffer *buffer; + + EmpathyTheme *theme; + + time_t last_timestamp; + EmpathyChatViewBlock last_block_type; + + gboolean allow_scrolling; + guint scroll_timeout; + GTimer *scroll_time; + gboolean is_group_chat; + + GtkTextMark *find_mark_previous; + GtkTextMark *find_mark_next; + gboolean find_wrapped; + gboolean find_last_direction; + + /* This is for the group chat so we know if the "other" last contact + * changed, so we know whether to insert a header or not. + */ + EmpathyContact *last_contact; + + guint notify_system_fonts_id; + guint notify_show_avatars_id; +}; + +static void empathy_chat_view_class_init (EmpathyChatViewClass *klass); +static void empathy_chat_view_init (EmpathyChatView *view); +static void chat_view_finalize (GObject *object); +static gboolean chat_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static void chat_view_size_allocate (GtkWidget *widget, + GtkAllocation *alloc); +static void chat_view_setup_tags (EmpathyChatView *view); +static void chat_view_system_font_update (EmpathyChatView *view); +static void chat_view_notify_system_font_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void chat_view_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void chat_view_populate_popup (EmpathyChatView *view, + GtkMenu *menu, + gpointer user_data); +static gboolean chat_view_event_cb (EmpathyChatView *view, + GdkEventMotion *event, + GtkTextTag *tag); +static gboolean chat_view_url_event_cb (GtkTextTag *tag, + GObject *object, + GdkEvent *event, + GtkTextIter *iter, + GtkTextBuffer *buffer); +static void chat_view_open_address_cb (GtkMenuItem *menuitem, + const gchar *url); +static void chat_view_copy_address_cb (GtkMenuItem *menuitem, + const gchar *url); +static void chat_view_clear_view_cb (GtkMenuItem *menuitem, + EmpathyChatView *view); +static gboolean chat_view_is_scrolled_down (EmpathyChatView *view); +static void chat_view_theme_changed_cb (EmpathyThemeManager *manager, + EmpathyChatView *view); +static void chat_view_theme_notify_cb (EmpathyTheme *theme, + GParamSpec *param, + EmpathyChatView *view); + +G_DEFINE_TYPE (EmpathyChatView, empathy_chat_view, GTK_TYPE_TEXT_VIEW); + +static void +empathy_chat_view_class_init (EmpathyChatViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = chat_view_finalize; + widget_class->size_allocate = chat_view_size_allocate; + widget_class->drag_motion = chat_view_drag_motion; + + g_type_class_add_private (object_class, sizeof (EmpathyChatViewPriv)); +} + +static void +empathy_chat_view_init (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + gboolean show_avatars; + + priv = GET_PRIV (view); + + priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + priv->last_block_type = EMPATHY_CHAT_VIEW_BLOCK_NONE; + priv->last_timestamp = 0; + + priv->allow_scrolling = TRUE; + + priv->is_group_chat = FALSE; + + g_object_set (view, + "wrap-mode", GTK_WRAP_WORD_CHAR, + "editable", FALSE, + "cursor-visible", FALSE, + NULL); + + priv->notify_system_fonts_id = + empathy_conf_notify_add (empathy_conf_get (), + "/desktop/gnome/interface/document_font_name", + chat_view_notify_system_font_cb, + view); + chat_view_system_font_update (view); + + priv->notify_show_avatars_id = + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_UI_SHOW_AVATARS, + chat_view_notify_show_avatars_cb, + view); + + chat_view_setup_tags (view); + + empathy_theme_manager_apply_saved (empathy_theme_manager_get (), view); + + show_avatars = FALSE; + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_SHOW_AVATARS, + &show_avatars); + + empathy_theme_set_show_avatars (priv->theme, show_avatars); + + g_signal_connect (view, + "populate-popup", + G_CALLBACK (chat_view_populate_popup), + NULL); + + g_signal_connect_object (empathy_theme_manager_get (), + "theme-changed", + G_CALLBACK (chat_view_theme_changed_cb), + view, + 0); +} + +static void +chat_view_finalize (GObject *object) +{ + EmpathyChatView *view; + EmpathyChatViewPriv *priv; + + view = EMPATHY_CHAT_VIEW (object); + priv = GET_PRIV (view); + + empathy_debug (DEBUG_DOMAIN, "finalize: %p", object); + + empathy_conf_notify_remove (empathy_conf_get (), priv->notify_system_fonts_id); + empathy_conf_notify_remove (empathy_conf_get (), priv->notify_show_avatars_id); + + if (priv->last_contact) { + g_object_unref (priv->last_contact); + } + if (priv->scroll_time) { + g_timer_destroy (priv->scroll_time); + } + if (priv->scroll_timeout) { + g_source_remove (priv->scroll_timeout); + } + + if (priv->theme) { + g_signal_handlers_disconnect_by_func (priv->theme, + chat_view_theme_notify_cb, + view); + g_object_unref (priv->theme); + } + + G_OBJECT_CLASS (empathy_chat_view_parent_class)->finalize (object); +} + +static gboolean +chat_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + /* Don't handle drag motion, since we don't want the view to scroll as + * the result of dragging something across it. + */ + + return FALSE; +} + +static void +chat_view_size_allocate (GtkWidget *widget, + GtkAllocation *alloc) +{ + gboolean down; + + down = chat_view_is_scrolled_down (EMPATHY_CHAT_VIEW (widget)); + + GTK_WIDGET_CLASS (empathy_chat_view_parent_class)->size_allocate (widget, alloc); + + if (down) { + GtkAdjustment *adj; + + adj = GTK_TEXT_VIEW (widget)->vadjustment; + gtk_adjustment_set_value (adj, adj->upper - adj->page_size); + } +} + +static void +chat_view_setup_tags (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + GtkTextTag *tag; + + priv = GET_PRIV (view); + + gtk_text_buffer_create_tag (priv->buffer, + "cut", + NULL); + + /* FIXME: Move to the theme and come up with something that looks a bit + * nicer. + */ + gtk_text_buffer_create_tag (priv->buffer, + "highlight", + "background", "yellow", + NULL); + + tag = gtk_text_buffer_create_tag (priv->buffer, + "link", + NULL); + + g_signal_connect (tag, + "event", + G_CALLBACK (chat_view_url_event_cb), + priv->buffer); + + g_signal_connect (view, + "motion-notify-event", + G_CALLBACK (chat_view_event_cb), + tag); +} + +static void +chat_view_system_font_update (EmpathyChatView *view) +{ + PangoFontDescription *font_description = NULL; + gchar *font_name; + + if (empathy_conf_get_string (empathy_conf_get (), + "/desktop/gnome/interface/document_font_name", + &font_name) && font_name) { + font_description = pango_font_description_from_string (font_name); + g_free (font_name); + } else { + font_description = NULL; + } + + gtk_widget_modify_font (GTK_WIDGET (view), font_description); + + if (font_description) { + pango_font_description_free (font_description); + } +} + +static void +chat_view_notify_system_font_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyChatView *view; + EmpathyChatViewPriv *priv; + gboolean show_avatars = FALSE; + + view = user_data; + priv = GET_PRIV (view); + + chat_view_system_font_update (view); + + /* Ugly, again, to adjust the vertical position of the nick... Will fix + * this when reworking the theme manager so that view register + * themselves with it instead of the other way around. + */ + empathy_conf_get_bool (conf, + EMPATHY_PREFS_UI_SHOW_AVATARS, + &show_avatars); + + empathy_theme_set_show_avatars (priv->theme, show_avatars); +} + +static void +chat_view_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyChatView *view; + EmpathyChatViewPriv *priv; + gboolean show_avatars = FALSE; + + view = user_data; + priv = GET_PRIV (view); + + empathy_conf_get_bool (conf, key, &show_avatars); + + empathy_theme_set_show_avatars (priv->theme, show_avatars); +} + +static void +chat_view_populate_popup (EmpathyChatView *view, + GtkMenu *menu, + gpointer user_data) +{ + EmpathyChatViewPriv *priv; + GtkTextTagTable *table; + GtkTextTag *tag; + gint x, y; + GtkTextIter iter, start, end; + GtkWidget *item; + gchar *str = NULL; + + priv = GET_PRIV (view); + + /* Clear menu item */ + if (gtk_text_buffer_get_char_count (priv->buffer) > 0) { + item = gtk_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + g_signal_connect (item, + "activate", + G_CALLBACK (chat_view_clear_view_cb), + view); + } + + /* Link context menu items */ + table = gtk_text_buffer_get_tag_table (priv->buffer); + tag = gtk_text_tag_table_lookup (table, "link"); + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), + GTK_TEXT_WINDOW_WIDGET, + x, y, + &x, &y); + + gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y); + + start = end = iter; + + if (gtk_text_iter_backward_to_tag_toggle (&start, tag) && + gtk_text_iter_forward_to_tag_toggle (&end, tag)) { + str = gtk_text_buffer_get_text (priv->buffer, + &start, &end, FALSE); + } + + if (G_STR_EMPTY (str)) { + g_free (str); + return; + } + + /* NOTE: Set data just to get the string freed when not needed. */ + g_object_set_data_full (G_OBJECT (menu), + "url", str, + (GDestroyNotify) g_free); + + item = gtk_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address")); + g_signal_connect (item, + "activate", + G_CALLBACK (chat_view_copy_address_cb), + str); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_menu_item_new_with_mnemonic (_("_Open Link")); + g_signal_connect (item, + "activate", + G_CALLBACK (chat_view_open_address_cb), + str); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); +} + +static gboolean +chat_view_event_cb (EmpathyChatView *view, + GdkEventMotion *event, + GtkTextTag *tag) +{ + static GdkCursor *hand = NULL; + static GdkCursor *beam = NULL; + GtkTextWindowType type; + GtkTextIter iter; + GdkWindow *win; + gint x, y, buf_x, buf_y; + + type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (view), + event->window); + + if (type != GTK_TEXT_WINDOW_TEXT) { + return FALSE; + } + + /* Get where the pointer really is. */ + win = gtk_text_view_get_window (GTK_TEXT_VIEW (view), type); + if (!win) { + return FALSE; + } + + gdk_window_get_pointer (win, &x, &y, NULL); + + /* Get the iter where the cursor is at */ + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), type, + x, y, + &buf_x, &buf_y); + + gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), + &iter, + buf_x, buf_y); + + if (gtk_text_iter_has_tag (&iter, tag)) { + if (!hand) { + hand = gdk_cursor_new (GDK_HAND2); + beam = gdk_cursor_new (GDK_XTERM); + } + gdk_window_set_cursor (win, hand); + } else { + if (!beam) { + beam = gdk_cursor_new (GDK_XTERM); + } + gdk_window_set_cursor (win, beam); + } + + return FALSE; +} + +static gboolean +chat_view_url_event_cb (GtkTextTag *tag, + GObject *object, + GdkEvent *event, + GtkTextIter *iter, + GtkTextBuffer *buffer) +{ + GtkTextIter start, end; + gchar *str; + + /* If the link is being selected, don't do anything. */ + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end)) { + return FALSE; + } + + if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) { + start = end = *iter; + + if (gtk_text_iter_backward_to_tag_toggle (&start, tag) && + gtk_text_iter_forward_to_tag_toggle (&end, tag)) { + str = gtk_text_buffer_get_text (buffer, + &start, + &end, + FALSE); + + empathy_url_show (str); + g_free (str); + } + } + + return FALSE; +} + +static void +chat_view_open_address_cb (GtkMenuItem *menuitem, const gchar *url) +{ + empathy_url_show (url); +} + +static void +chat_view_copy_address_cb (GtkMenuItem *menuitem, const gchar *url) +{ + GtkClipboard *clipboard; + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clipboard, url, -1); + + clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text (clipboard, url, -1); +} + +static void +chat_view_clear_view_cb (GtkMenuItem *menuitem, EmpathyChatView *view) +{ + empathy_chat_view_clear (view); +} + +static gboolean +chat_view_is_scrolled_down (EmpathyChatView *view) +{ + GtkWidget *sw; + + sw = gtk_widget_get_parent (GTK_WIDGET (view)); + if (GTK_IS_SCROLLED_WINDOW (sw)) { + GtkAdjustment *vadj; + + vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw)); + + if (vadj->value + vadj->page_size / 2 < vadj->upper - vadj->page_size) { + return FALSE; + } + } + + return TRUE; +} + +static void +chat_view_maybe_trim_buffer (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + GtkTextIter top, bottom; + gint line; + gint remove; + GtkTextTagTable *table; + GtkTextTag *tag; + + priv = GET_PRIV (view); + + gtk_text_buffer_get_end_iter (priv->buffer, &bottom); + line = gtk_text_iter_get_line (&bottom); + if (line < MAX_LINES) { + return; + } + + remove = line - MAX_LINES; + gtk_text_buffer_get_start_iter (priv->buffer, &top); + + bottom = top; + if (!gtk_text_iter_forward_lines (&bottom, remove)) { + return; + } + + /* Track backwords to a place where we can safely cut, we don't do it in + * the middle of a tag. + */ + table = gtk_text_buffer_get_tag_table (priv->buffer); + tag = gtk_text_tag_table_lookup (table, "cut"); + if (!tag) { + return; + } + + if (!gtk_text_iter_forward_to_tag_toggle (&bottom, tag)) { + return; + } + + if (!gtk_text_iter_equal (&top, &bottom)) { + gtk_text_buffer_delete (priv->buffer, &top, &bottom); + } +} + +static void +chat_view_theme_changed_cb (EmpathyThemeManager *manager, + EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + gboolean show_avatars = FALSE; + gboolean theme_rooms = FALSE; + + priv = GET_PRIV (view); + + priv->last_block_type = EMPATHY_CHAT_VIEW_BLOCK_NONE; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_THEME_CHAT_ROOM, + &theme_rooms); + if (!theme_rooms && priv->is_group_chat) { + empathy_theme_manager_apply (manager, view, NULL); + } else { + empathy_theme_manager_apply_saved (manager, view); + } + + /* Needed for now to update the "rise" property of the names to get it + * vertically centered. + */ + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_SHOW_AVATARS, + &show_avatars); + empathy_theme_set_show_avatars (priv->theme, show_avatars); +} + +/* Pads a pixbuf to the specified size, by centering it in a larger transparent + * pixbuf. Returns a new ref. + */ +static GdkPixbuf * +chat_view_pad_to_size (GdkPixbuf *pixbuf, + gint width, + gint height, + gint extra_padding_right) +{ + gint src_width, src_height; + GdkPixbuf *padded; + gint x_offset, y_offset; + + src_width = gdk_pixbuf_get_width (pixbuf); + src_height = gdk_pixbuf_get_height (pixbuf); + + x_offset = (width - src_width) / 2; + y_offset = (height - src_height) / 2; + + padded = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf), + TRUE, /* alpha */ + gdk_pixbuf_get_bits_per_sample (pixbuf), + width + extra_padding_right, + height); + + gdk_pixbuf_fill (padded, 0); + + gdk_pixbuf_copy_area (pixbuf, + 0, /* source coords */ + 0, + src_width, + src_height, + padded, + x_offset, /* dest coords */ + y_offset); + + return padded; +} + +typedef struct { + GdkPixbuf *pixbuf; + gchar *token; +} AvatarData; + +static void +chat_view_avatar_cache_data_free (gpointer ptr) +{ + AvatarData *data = ptr; + + g_object_unref (data->pixbuf); + g_free (data->token); + g_slice_free (AvatarData, data); +} + +GdkPixbuf * +empathy_chat_view_get_avatar_pixbuf_with_cache (EmpathyContact *contact) +{ + static GHashTable *avatar_cache = NULL; + AvatarData *data; + EmpathyAvatar *avatar; + GdkPixbuf *tmp_pixbuf; + GdkPixbuf *pixbuf = NULL; + + /* Init avatar cache */ + if (!avatar_cache) { + avatar_cache = g_hash_table_new_full (empathy_contact_hash, + empathy_contact_equal, + g_object_unref, + chat_view_avatar_cache_data_free); + } + + /* Check if avatar is in cache and if it's up to date */ + avatar = empathy_contact_get_avatar (contact); + data = g_hash_table_lookup (avatar_cache, contact); + if (data) { + if (avatar && !tp_strdiff (avatar->token, data->token)) { + /* We have the avatar in cache */ + return data->pixbuf; + } + + /* The cache is outdate */ + g_hash_table_remove (avatar_cache, contact); + } + + /* Avatar not in cache, create pixbuf */ + tmp_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32); + if (tmp_pixbuf) { + pixbuf = chat_view_pad_to_size (tmp_pixbuf, 32, 32, 6); + g_object_unref (tmp_pixbuf); + } + if (!pixbuf) { + return NULL; + } + + /* Insert new pixbuf in cache */ + data = g_slice_new0 (AvatarData); + data->token = g_strdup (avatar->token); + data->pixbuf = pixbuf; + + g_hash_table_insert (avatar_cache, + g_object_ref (contact), + data); + + return data->pixbuf; +} + +EmpathyChatView * +empathy_chat_view_new (void) +{ + return g_object_new (EMPATHY_TYPE_CHAT_VIEW, NULL); +} + +void +empathy_chat_view_append_message (EmpathyChatView *view, + EmpathyMessage *msg) +{ + EmpathyChatViewPriv *priv = GET_PRIV (view); + EmpathyContact *sender; + gboolean bottom; + gboolean from_self; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + g_return_if_fail (EMPATHY_IS_MESSAGE (msg)); + + if (!empathy_message_get_body (msg)) { + return; + } + + bottom = chat_view_is_scrolled_down (view); + sender = empathy_message_get_sender (msg); + from_self = empathy_contact_is_user (sender); + + chat_view_maybe_trim_buffer (view); + + empathy_theme_append_message (priv->theme, view, msg); + + if (bottom) { + empathy_chat_view_scroll_down (view); + } +} + +void +empathy_chat_view_append_event (EmpathyChatView *view, + const gchar *str) +{ + EmpathyChatViewPriv *priv; + gboolean bottom; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + g_return_if_fail (!G_STR_EMPTY (str)); + + priv = GET_PRIV (view); + + bottom = chat_view_is_scrolled_down (view); + + chat_view_maybe_trim_buffer (view); + + empathy_theme_append_event (priv->theme, view, str); + + if (bottom) { + empathy_chat_view_scroll_down (view); + } + + priv->last_block_type = EMPATHY_CHAT_VIEW_BLOCK_EVENT; +} + +void +empathy_chat_view_append_button (EmpathyChatView *view, + const gchar *message, + GtkWidget *button1, + GtkWidget *button2) +{ + EmpathyChatViewPriv *priv; + GtkTextChildAnchor *anchor; + GtkTextIter iter; + gboolean bottom; + const gchar *tag; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + g_return_if_fail (button1 != NULL); + + priv = GET_PRIV (view); + + tag = "invite"; + + bottom = chat_view_is_scrolled_down (view); + + empathy_theme_append_timestamp (priv->theme, view, NULL, TRUE, TRUE); + + if (message) { + empathy_theme_append_text (priv->theme, view, message, tag, NULL); + } + + gtk_text_buffer_get_end_iter (priv->buffer, &iter); + + anchor = gtk_text_buffer_create_child_anchor (priv->buffer, &iter); + gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view), button1, anchor); + gtk_widget_show (button1); + + gtk_text_buffer_insert_with_tags_by_name (priv->buffer, + &iter, + " ", + 1, + tag, + NULL); + + if (button2) { + gtk_text_buffer_get_end_iter (priv->buffer, &iter); + + anchor = gtk_text_buffer_create_child_anchor (priv->buffer, &iter); + gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view), button2, anchor); + gtk_widget_show (button2); + + gtk_text_buffer_insert_with_tags_by_name (priv->buffer, + &iter, + " ", + 1, + tag, + NULL); + } + + gtk_text_buffer_get_end_iter (priv->buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (priv->buffer, + &iter, + "\n\n", + 2, + tag, + NULL); + + if (bottom) { + empathy_chat_view_scroll_down (view); + } + + priv->last_block_type = EMPATHY_CHAT_VIEW_BLOCK_INVITE; +} + +void +empathy_chat_view_scroll (EmpathyChatView *view, + gboolean allow_scrolling) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + priv->allow_scrolling = allow_scrolling; + + empathy_debug (DEBUG_DOMAIN, "Scrolling %s", + allow_scrolling ? "enabled" : "disabled"); +} + +/* Code stolen from pidgin/gtkimhtml.c */ +static gboolean +chat_view_scroll_cb (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + GtkAdjustment *adj; + gdouble max_val; + + priv = GET_PRIV (view); + adj = GTK_TEXT_VIEW (view)->vadjustment; + max_val = adj->upper - adj->page_size; + + g_return_val_if_fail (priv->scroll_time != NULL, FALSE); + + if (g_timer_elapsed (priv->scroll_time, NULL) > MAX_SCROLL_TIME) { + /* 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; + priv->scroll_timeout = 0; + return FALSE; + } + + /* scroll by 1/3rd the remaining distance */ + gtk_adjustment_set_value (adj, gtk_adjustment_get_value (adj) + ((max_val - gtk_adjustment_get_value (adj)) / 3)); + return TRUE; +} + +void +empathy_chat_view_scroll_down (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + if (!priv->allow_scrolling) { + return; + } + + empathy_debug (DEBUG_DOMAIN, "Scrolling down"); + + if (priv->scroll_time) { + g_timer_reset (priv->scroll_time); + } else { + priv->scroll_time = g_timer_new(); + } + if (!priv->scroll_timeout) { + priv->scroll_timeout = g_timeout_add (SCROLL_DELAY, + (GSourceFunc) chat_view_scroll_cb, + view); + } +} + +gboolean +empathy_chat_view_get_selection_bounds (EmpathyChatView *view, + GtkTextIter *start, + GtkTextIter *end) +{ + GtkTextBuffer *buffer; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + return gtk_text_buffer_get_selection_bounds (buffer, start, end); +} + +void +empathy_chat_view_clear (EmpathyChatView *view) +{ + GtkTextBuffer *buffer; + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_buffer_set_text (buffer, "", -1); + + /* We set these back to the initial values so we get + * timestamps when clearing the window to know when + * conversations start. + */ + priv = GET_PRIV (view); + + priv->last_block_type = EMPATHY_CHAT_VIEW_BLOCK_NONE; + priv->last_timestamp = 0; +} + +gboolean +empathy_chat_view_find_previous (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search) +{ + EmpathyChatViewPriv *priv; + GtkTextBuffer *buffer; + GtkTextIter iter_at_mark; + GtkTextIter iter_match_start; + GtkTextIter iter_match_end; + gboolean found; + gboolean from_start = FALSE; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE); + g_return_val_if_fail (search_criteria != NULL, FALSE); + + priv = GET_PRIV (view); + + buffer = priv->buffer; + + if (G_STR_EMPTY (search_criteria)) { + if (priv->find_mark_previous) { + gtk_text_buffer_get_start_iter (buffer, &iter_at_mark); + + gtk_text_buffer_move_mark (buffer, + priv->find_mark_previous, + &iter_at_mark); + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), + priv->find_mark_previous, + 0.0, + TRUE, + 0.0, + 0.0); + gtk_text_buffer_select_range (buffer, + &iter_at_mark, + &iter_at_mark); + } + + return FALSE; + } + + if (new_search) { + from_start = TRUE; + } + + if (priv->find_mark_previous) { + gtk_text_buffer_get_iter_at_mark (buffer, + &iter_at_mark, + priv->find_mark_previous); + } else { + gtk_text_buffer_get_end_iter (buffer, &iter_at_mark); + from_start = TRUE; + } + + priv->find_last_direction = FALSE; + + found = empathy_text_iter_backward_search (&iter_at_mark, + search_criteria, + &iter_match_start, + &iter_match_end, + NULL); + + if (!found) { + gboolean result = FALSE; + + if (from_start) { + return result; + } + + /* Here we wrap around. */ + if (!new_search && !priv->find_wrapped) { + priv->find_wrapped = TRUE; + result = empathy_chat_view_find_previous (view, + search_criteria, + FALSE); + priv->find_wrapped = FALSE; + } + + return result; + } + + /* Set new mark and show on screen */ + if (!priv->find_mark_previous) { + priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL, + &iter_match_start, + TRUE); + } else { + gtk_text_buffer_move_mark (buffer, + priv->find_mark_previous, + &iter_match_start); + } + + if (!priv->find_mark_next) { + priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL, + &iter_match_end, + TRUE); + } else { + gtk_text_buffer_move_mark (buffer, + priv->find_mark_next, + &iter_match_end); + } + + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), + priv->find_mark_previous, + 0.0, + TRUE, + 0.5, + 0.5); + + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start); + gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end); + + return TRUE; +} + +gboolean +empathy_chat_view_find_next (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search) +{ + EmpathyChatViewPriv *priv; + GtkTextBuffer *buffer; + GtkTextIter iter_at_mark; + GtkTextIter iter_match_start; + GtkTextIter iter_match_end; + gboolean found; + gboolean from_start = FALSE; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), FALSE); + g_return_val_if_fail (search_criteria != NULL, FALSE); + + priv = GET_PRIV (view); + + buffer = priv->buffer; + + if (G_STR_EMPTY (search_criteria)) { + if (priv->find_mark_next) { + gtk_text_buffer_get_start_iter (buffer, &iter_at_mark); + + gtk_text_buffer_move_mark (buffer, + priv->find_mark_next, + &iter_at_mark); + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), + priv->find_mark_next, + 0.0, + TRUE, + 0.0, + 0.0); + gtk_text_buffer_select_range (buffer, + &iter_at_mark, + &iter_at_mark); + } + + return FALSE; + } + + if (new_search) { + from_start = TRUE; + } + + if (priv->find_mark_next) { + gtk_text_buffer_get_iter_at_mark (buffer, + &iter_at_mark, + priv->find_mark_next); + } else { + gtk_text_buffer_get_start_iter (buffer, &iter_at_mark); + from_start = TRUE; + } + + priv->find_last_direction = TRUE; + + found = empathy_text_iter_forward_search (&iter_at_mark, + search_criteria, + &iter_match_start, + &iter_match_end, + NULL); + + if (!found) { + gboolean result = FALSE; + + if (from_start) { + return result; + } + + /* Here we wrap around. */ + if (!new_search && !priv->find_wrapped) { + priv->find_wrapped = TRUE; + result = empathy_chat_view_find_next (view, + search_criteria, + FALSE); + priv->find_wrapped = FALSE; + } + + return result; + } + + /* Set new mark and show on screen */ + if (!priv->find_mark_next) { + priv->find_mark_next = gtk_text_buffer_create_mark (buffer, NULL, + &iter_match_end, + TRUE); + } else { + gtk_text_buffer_move_mark (buffer, + priv->find_mark_next, + &iter_match_end); + } + + if (!priv->find_mark_previous) { + priv->find_mark_previous = gtk_text_buffer_create_mark (buffer, NULL, + &iter_match_start, + TRUE); + } else { + gtk_text_buffer_move_mark (buffer, + priv->find_mark_previous, + &iter_match_start); + } + + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view), + priv->find_mark_next, + 0.0, + TRUE, + 0.5, + 0.5); + + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &iter_match_start); + gtk_text_buffer_move_mark_by_name (buffer, "insert", &iter_match_end); + + return TRUE; +} + + +void +empathy_chat_view_find_abilities (EmpathyChatView *view, + const gchar *search_criteria, + gboolean *can_do_previous, + gboolean *can_do_next) +{ + EmpathyChatViewPriv *priv; + GtkTextBuffer *buffer; + GtkTextIter iter_at_mark; + GtkTextIter iter_match_start; + GtkTextIter iter_match_end; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + g_return_if_fail (search_criteria != NULL); + g_return_if_fail (can_do_previous != NULL && can_do_next != NULL); + + priv = GET_PRIV (view); + + buffer = priv->buffer; + + if (can_do_previous) { + if (priv->find_mark_previous) { + gtk_text_buffer_get_iter_at_mark (buffer, + &iter_at_mark, + priv->find_mark_previous); + } else { + gtk_text_buffer_get_start_iter (buffer, &iter_at_mark); + } + + *can_do_previous = empathy_text_iter_backward_search (&iter_at_mark, + search_criteria, + &iter_match_start, + &iter_match_end, + NULL); + } + + if (can_do_next) { + if (priv->find_mark_next) { + gtk_text_buffer_get_iter_at_mark (buffer, + &iter_at_mark, + priv->find_mark_next); + } else { + gtk_text_buffer_get_start_iter (buffer, &iter_at_mark); + } + + *can_do_next = empathy_text_iter_forward_search (&iter_at_mark, + search_criteria, + &iter_match_start, + &iter_match_end, + NULL); + } +} + +void +empathy_chat_view_highlight (EmpathyChatView *view, + const gchar *text) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTextIter iter_start; + GtkTextIter iter_end; + GtkTextIter iter_match_start; + GtkTextIter iter_match_end; + gboolean found; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + gtk_text_buffer_get_start_iter (buffer, &iter); + + gtk_text_buffer_get_bounds (buffer, &iter_start, &iter_end); + gtk_text_buffer_remove_tag_by_name (buffer, "highlight", + &iter_start, + &iter_end); + + if (G_STR_EMPTY (text)) { + return; + } + + while (1) { + found = empathy_text_iter_forward_search (&iter, + text, + &iter_match_start, + &iter_match_end, + NULL); + + if (!found) { + break; + } + + gtk_text_buffer_apply_tag_by_name (buffer, "highlight", + &iter_match_start, + &iter_match_end); + + iter = iter_match_end; + gtk_text_iter_forward_char (&iter); + } +} + +void +empathy_chat_view_copy_clipboard (EmpathyChatView *view) +{ + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_copy_clipboard (buffer, clipboard); +} + +EmpathyTheme * +empathy_chat_view_get_theme (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), NULL); + + priv = GET_PRIV (view); + + return priv->theme; +} + +static void +chat_view_theme_notify_cb (EmpathyTheme *theme, + GParamSpec *param, + EmpathyChatView *view) +{ + empathy_theme_update_view (theme, view); +} + +void +empathy_chat_view_set_theme (EmpathyChatView *view, EmpathyTheme *theme) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + g_return_if_fail (EMPATHY_IS_THEME (theme)); + + priv = GET_PRIV (view); + + if (priv->theme) { + g_signal_handlers_disconnect_by_func (priv->theme, + chat_view_theme_notify_cb, + view); + g_object_unref (priv->theme); + } + + priv->theme = g_object_ref (theme); + + empathy_theme_update_view (theme, view); + g_signal_connect (priv->theme, "notify", + G_CALLBACK (chat_view_theme_notify_cb), + view); + + /* FIXME: Redraw all messages using the new theme */ +} + +void +empathy_chat_view_set_margin (EmpathyChatView *view, + gint margin) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + g_object_set (view, + "left-margin", margin, + "right-margin", margin, + NULL); +} + +GtkWidget * +empathy_chat_view_get_smiley_menu (GCallback callback, + gpointer user_data) +{ + EmpathySmileyManager *smiley_manager; + GSList *smileys, *l; + GtkWidget *menu; + gint x = 0; + gint y = 0; + + g_return_val_if_fail (callback != NULL, NULL); + + menu = gtk_menu_new (); + + smiley_manager = empathy_smiley_manager_new (); + smileys = empathy_smiley_manager_get_all (smiley_manager); + for (l = smileys; l; l = l->next) { + EmpathySmiley *smiley; + GtkWidget *item; + GtkWidget *image; + + smiley = l->data; + image = gtk_image_new_from_pixbuf (smiley->pixbuf); + + item = gtk_image_menu_item_new_with_label (""); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + + gtk_menu_attach (GTK_MENU (menu), item, + x, x + 1, y, y + 1); + + gtk_widget_set_tooltip_text (item, smiley->str); + + g_object_set_data (G_OBJECT (item), "smiley_text", smiley->str); + g_signal_connect (item, "activate", callback, user_data); + + if (x > 3) { + y++; + x = 0; + } else { + x++; + } + } + g_object_unref (smiley_manager); + + gtk_widget_show_all (menu); + + return menu; +} + +/* FIXME: Do we really need this? Better to do it internally only at setup time, + * we will never change it on the fly. + */ +void +empathy_chat_view_set_is_group_chat (EmpathyChatView *view, + gboolean is_group_chat) +{ + EmpathyChatViewPriv *priv; + gboolean theme_rooms = FALSE; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + priv->is_group_chat = is_group_chat; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_THEME_CHAT_ROOM, + &theme_rooms); + + if (!theme_rooms && is_group_chat) { + empathy_theme_manager_apply (empathy_theme_manager_get (), + view, + NULL); + } else { + empathy_theme_manager_apply_saved (empathy_theme_manager_get (), + view); + } +} + +time_t +empathy_chat_view_get_last_timestamp (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), 0); + + priv = GET_PRIV (view); + + return priv->last_timestamp; +} + +void +empathy_chat_view_set_last_timestamp (EmpathyChatView *view, + time_t timestamp) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + priv->last_timestamp = timestamp; +} + +EmpathyChatViewBlock +empathy_chat_view_get_last_block_type (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), 0); + + priv = GET_PRIV (view); + + return priv->last_block_type; +} + +void +empathy_chat_view_set_last_block_type (EmpathyChatView *view, + EmpathyChatViewBlock block_type) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + priv->last_block_type = block_type; +} + +EmpathyContact * +empathy_chat_view_get_last_contact (EmpathyChatView *view) +{ + EmpathyChatViewPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT_VIEW (view), NULL); + + priv = GET_PRIV (view); + + return priv->last_contact; +} + +void +empathy_chat_view_set_last_contact (EmpathyChatView *view, EmpathyContact *contact) +{ + EmpathyChatViewPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (view); + + if (priv->last_contact) { + g_object_unref (priv->last_contact); + priv->last_contact = NULL; + } + + if (contact) { + priv->last_contact = g_object_ref (contact); + } +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-chat-view.h b/gnome-2-22/libempathy-gtk/empathy-chat-view.h new file mode 100644 index 000000000..58219ca04 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat-view.h @@ -0,0 +1,119 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#ifndef __EMPATHY_CHAT_VIEW_H__ +#define __EMPATHY_CHAT_VIEW_H__ + +#include <gtk/gtktextview.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-message.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CHAT_VIEW (empathy_chat_view_get_type ()) +#define EMPATHY_CHAT_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatView)) +#define EMPATHY_CHAT_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewClass)) +#define EMPATHY_IS_CHAT_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHAT_VIEW)) +#define EMPATHY_IS_CHAT_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHAT_VIEW)) +#define EMPATHY_CHAT_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHAT_VIEW, EmpathyChatViewClass)) + +typedef struct _EmpathyChatView EmpathyChatView; +typedef struct _EmpathyChatViewClass EmpathyChatViewClass; +typedef struct _EmpathyChatViewPriv EmpathyChatViewPriv; + +#include "empathy-theme.h" + +struct _EmpathyChatView { + GtkTextView parent; +}; + +struct _EmpathyChatViewClass { + GtkTextViewClass parent_class; +}; + +typedef enum { + EMPATHY_CHAT_VIEW_BLOCK_NONE, + EMPATHY_CHAT_VIEW_BLOCK_SELF, + EMPATHY_CHAT_VIEW_BLOCK_OTHER, + EMPATHY_CHAT_VIEW_BLOCK_EVENT, + EMPATHY_CHAT_VIEW_BLOCK_TIME, + EMPATHY_CHAT_VIEW_BLOCK_INVITE +} EmpathyChatViewBlock; + +GType empathy_chat_view_get_type (void) G_GNUC_CONST; +EmpathyChatView *empathy_chat_view_new (void); +void empathy_chat_view_append_message (EmpathyChatView *view, + EmpathyMessage *msg); +void empathy_chat_view_append_event (EmpathyChatView *view, + const gchar *str); +void empathy_chat_view_append_button (EmpathyChatView *view, + const gchar *message, + GtkWidget *button1, + GtkWidget *button2); +void empathy_chat_view_set_margin (EmpathyChatView *view, + gint margin); +void empathy_chat_view_scroll (EmpathyChatView *view, + gboolean allow_scrolling); +void empathy_chat_view_scroll_down (EmpathyChatView *view); +gboolean empathy_chat_view_get_selection_bounds (EmpathyChatView *view, + GtkTextIter *start, + GtkTextIter *end); +void empathy_chat_view_clear (EmpathyChatView *view); +gboolean empathy_chat_view_find_previous (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search); +gboolean empathy_chat_view_find_next (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search); +void empathy_chat_view_find_abilities (EmpathyChatView *view, + const gchar *search_criteria, + gboolean *can_do_previous, + gboolean *can_do_next); +void empathy_chat_view_highlight (EmpathyChatView *view, + const gchar *text); +void empathy_chat_view_copy_clipboard (EmpathyChatView *view); +EmpathyTheme * empathy_chat_view_get_theme (EmpathyChatView *view); +void empathy_chat_view_set_theme (EmpathyChatView *view, + EmpathyTheme *theme); +void empathy_chat_view_set_margin (EmpathyChatView *view, + gint margin); +GtkWidget * empathy_chat_view_get_smiley_menu (GCallback callback, + gpointer user_data); +void empathy_chat_view_set_is_group_chat (EmpathyChatView *view, + gboolean is_group_chat); +time_t empathy_chat_view_get_last_timestamp (EmpathyChatView *view); +void empathy_chat_view_set_last_timestamp (EmpathyChatView *view, + time_t timestamp); +EmpathyChatViewBlock empathy_chat_view_get_last_block_type (EmpathyChatView *view); +void empathy_chat_view_set_last_block_type (EmpathyChatView *view, + EmpathyChatViewBlock block_type); +EmpathyContact * empathy_chat_view_get_last_contact (EmpathyChatView *view); +void empathy_chat_view_set_last_contact (EmpathyChatView *view, + EmpathyContact *contact); +GdkPixbuf * empathy_chat_view_get_avatar_pixbuf_with_cache (EmpathyContact *contact); + +G_END_DECLS + +#endif /* __EMPATHY_CHAT_VIEW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-chat-window.c b/gnome-2-22/libempathy-gtk/empathy-chat-window.c new file mode 100644 index 000000000..5e5a854c5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat-window.c @@ -0,0 +1,1892 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-log-manager.h> +#include <libempathy/empathy-chatroom-manager.h> +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-message.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-chat-window.h" +#include "empathy-images.h" +//#include "empathy-chat-invite.h" +#include "empathy-contact-dialogs.h" +#include "empathy-log-window.h" +#include "empathy-new-chatroom-dialog.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-private-chat.h" +#include "empathy-group-chat.h" +//#include "empathy-sound.h" +#include "empathy-ui-utils.h" +#include "empathy-about-dialog.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowPriv)) + +#define DEBUG_DOMAIN "ChatWindow" + +struct _EmpathyChatWindowPriv { + EmpathyChatroomManager *chatroom_manager; + GList *chats; + GList *chats_new_msg; + GList *chats_composing; + + EmpathyChat *current_chat; + + gboolean page_added; + gboolean dnd_same_window; + + GtkWidget *dialog; + GtkWidget *notebook; + + /* Menu items. */ + GtkWidget *menu_conv_clear; + GtkWidget *menu_conv_insert_smiley; + GtkWidget *menu_conv_call; + GtkWidget *menu_conv_call_separator; + GtkWidget *menu_conv_log; + GtkWidget *menu_conv_separator; + GtkWidget *menu_conv_add_contact; + GtkWidget *menu_conv_info; + GtkWidget *menu_conv_close; + + GtkWidget *menu_room; + GtkWidget *menu_room_set_topic; + GtkWidget *menu_room_join_new; + GtkWidget *menu_room_invite; + GtkWidget *menu_room_add; + GtkWidget *menu_room_show_contacts; + + GtkWidget *menu_edit_cut; + GtkWidget *menu_edit_copy; + GtkWidget *menu_edit_paste; + + GtkWidget *menu_tabs_next; + GtkWidget *menu_tabs_prev; + GtkWidget *menu_tabs_left; + GtkWidget *menu_tabs_right; + GtkWidget *menu_tabs_detach; + + GtkWidget *menu_help_contents; + GtkWidget *menu_help_about; + + guint save_geometry_id; +}; + +static void empathy_chat_window_class_init (EmpathyChatWindowClass *klass); +static void empathy_chat_window_init (EmpathyChatWindow *window); +static void empathy_chat_window_finalize (GObject *object); +static void chat_window_accel_cb (GtkAccelGroup *accelgroup, + GObject *object, + guint key, + GdkModifierType mod, + EmpathyChatWindow *window); +static void chat_window_close_clicked_cb (GtkWidget *button, + EmpathyChat *chat); +static GtkWidget *chat_window_create_label (EmpathyChatWindow *window, + EmpathyChat *chat); +static void chat_window_update_status (EmpathyChatWindow *window, + EmpathyChat *chat); +static void chat_window_update_title (EmpathyChatWindow *window, + EmpathyChat *chat); +static void chat_window_update_menu (EmpathyChatWindow *window); +static gboolean chat_window_save_geometry_timeout_cb (EmpathyChatWindow *window); +static gboolean chat_window_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + EmpathyChatWindow *window); +static void chat_window_conv_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_clear_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_info_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_add_contact_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_call_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_log_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_show_contacts_toggled_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_edit_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_insert_smiley_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_close_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_room_set_topic_activate_cb(GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_room_join_new_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_room_invite_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_room_add_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_cut_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_copy_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_paste_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_tabs_left_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_tabs_right_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_detach_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_help_contents_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static void chat_window_help_about_cb (GtkWidget *menuitem, + EmpathyChatWindow *window); +static gboolean chat_window_delete_event_cb (GtkWidget *dialog, + GdkEvent *event, + EmpathyChatWindow *window); +static void chat_window_status_changed_cb (EmpathyChat *chat, + EmpathyChatWindow *window); +static void chat_window_update_tooltip (EmpathyChatWindow *window, + EmpathyChat *chat); +static void chat_window_name_changed_cb (EmpathyChat *chat, + const gchar *name, + EmpathyChatWindow *window); +static void chat_window_composing_cb (EmpathyChat *chat, + gboolean is_composing, + EmpathyChatWindow *window); +static void chat_window_new_message_cb (EmpathyChat *chat, + EmpathyMessage *message, + gboolean is_backlog, + EmpathyChatWindow *window); +static GtkNotebook* chat_window_detach_hook (GtkNotebook *source, + GtkWidget *page, + gint x, + gint y, + gpointer user_data); +static void chat_window_page_switched_cb (GtkNotebook *notebook, + GtkNotebookPage *page, + gint page_num, + EmpathyChatWindow *window); +static void chat_window_page_reordered_cb (GtkNotebook *notebook, + GtkWidget *widget, + guint page_num, + EmpathyChatWindow *window); +static void chat_window_page_added_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *window); +static void chat_window_page_removed_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *window); +static gboolean chat_window_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChatWindow *window); +static void chat_window_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection, + guint info, + guint time, + EmpathyChatWindow *window); +static void chat_window_set_urgency_hint (EmpathyChatWindow *window, + gboolean urgent); + + +static GList *chat_windows = NULL; + +static const guint tab_accel_keys[] = { + GDK_1, GDK_2, GDK_3, GDK_4, GDK_5, + GDK_6, GDK_7, GDK_8, GDK_9, GDK_0 +}; + +typedef enum { + DND_DRAG_TYPE_CONTACT_ID, + DND_DRAG_TYPE_TAB +} DndDragType; + +static const GtkTargetEntry drag_types_dest[] = { + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, + { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, DND_DRAG_TYPE_TAB }, +}; + +G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT); + +static void +empathy_chat_window_class_init (EmpathyChatWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = empathy_chat_window_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyChatWindowPriv)); + + /* Set up a style for the close button with no focus padding. */ + gtk_rc_parse_string ( + "style \"empathy-close-button-style\"\n" + "{\n" + " GtkWidget::focus-padding = 0\n" + " xthickness = 0\n" + " ythickness = 0\n" + "}\n" + "widget \"*.empathy-close-button\" style \"empathy-close-button-style\""); + + gtk_notebook_set_window_creation_hook (chat_window_detach_hook, NULL, NULL); +} + +static void +empathy_chat_window_init (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + GladeXML *glade; + GtkAccelGroup *accel_group; + GtkWidget *image; + GClosure *closure; + GtkWidget *menu_conv; + GtkWidget *menu; + gint i; + GtkWidget *chat_vbox; + + priv = GET_PRIV (window); + + glade = empathy_glade_get_file ("empathy-chat.glade", + "chat_window", + NULL, + "chat_window", &priv->dialog, + "chat_vbox", &chat_vbox, + "menu_conv", &menu_conv, + "menu_conv_clear", &priv->menu_conv_clear, + "menu_conv_insert_smiley", &priv->menu_conv_insert_smiley, + "menu_conv_call", &priv->menu_conv_call, + "menu_conv_call_separator", &priv->menu_conv_call_separator, + "menu_conv_log", &priv->menu_conv_log, + "menu_conv_separator", &priv->menu_conv_separator, + "menu_conv_add_contact", &priv->menu_conv_add_contact, + "menu_conv_info", &priv->menu_conv_info, + "menu_conv_close", &priv->menu_conv_close, + "menu_room", &priv->menu_room, + "menu_room_set_topic", &priv->menu_room_set_topic, + "menu_room_join_new", &priv->menu_room_join_new, + "menu_room_invite", &priv->menu_room_invite, + "menu_room_add", &priv->menu_room_add, + "menu_room_show_contacts", &priv->menu_room_show_contacts, + "menu_edit_cut", &priv->menu_edit_cut, + "menu_edit_copy", &priv->menu_edit_copy, + "menu_edit_paste", &priv->menu_edit_paste, + "menu_tabs_next", &priv->menu_tabs_next, + "menu_tabs_prev", &priv->menu_tabs_prev, + "menu_tabs_left", &priv->menu_tabs_left, + "menu_tabs_right", &priv->menu_tabs_right, + "menu_tabs_detach", &priv->menu_tabs_detach, + "menu_help_contents", &priv->menu_help_contents, + "menu_help_about", &priv->menu_help_about, + NULL); + + empathy_glade_connect (glade, + window, + "chat_window", "configure-event", chat_window_configure_event_cb, + "menu_conv", "activate", chat_window_conv_activate_cb, + "menu_conv_clear", "activate", chat_window_clear_activate_cb, + "menu_conv_call", "activate", chat_window_call_activate_cb, + "menu_conv_log", "activate", chat_window_log_activate_cb, + "menu_conv_add_contact", "activate", chat_window_add_contact_activate_cb, + "menu_conv_info", "activate", chat_window_info_activate_cb, + "menu_conv_close", "activate", chat_window_close_activate_cb, + "menu_room_set_topic", "activate", chat_window_room_set_topic_activate_cb, + "menu_room_join_new", "activate", chat_window_room_join_new_activate_cb, + "menu_room_invite", "activate", chat_window_room_invite_activate_cb, + "menu_room_add", "activate", chat_window_room_add_activate_cb, + "menu_edit", "activate", chat_window_edit_activate_cb, + "menu_edit_cut", "activate", chat_window_cut_activate_cb, + "menu_edit_copy", "activate", chat_window_copy_activate_cb, + "menu_edit_paste", "activate", chat_window_paste_activate_cb, + "menu_tabs_left", "activate", chat_window_tabs_left_activate_cb, + "menu_tabs_right", "activate", chat_window_tabs_right_activate_cb, + "menu_tabs_detach", "activate", chat_window_detach_activate_cb, + "menu_help_contents", "activate", chat_window_help_contents_cb, + "menu_help_about", "activate", chat_window_help_about_cb, + NULL); + + g_object_unref (glade); + + /* Set up chatroom manager */ + priv->chatroom_manager = empathy_chatroom_manager_new (); + g_signal_connect_swapped (priv->chatroom_manager, "chatroom-added", + G_CALLBACK (chat_window_update_menu), + window); + g_signal_connect_swapped (priv->chatroom_manager, "chatroom-removed", + G_CALLBACK (chat_window_update_menu), + window); + + priv->notebook = gtk_notebook_new (); + gtk_notebook_set_group (GTK_NOTEBOOK (priv->notebook), "EmpathyChatWindow"); + gtk_box_pack_start (GTK_BOX (chat_vbox), priv->notebook, TRUE, TRUE, 0); + gtk_widget_show (priv->notebook); + + /* Set up accels */ + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (priv->dialog), accel_group); + + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { + closure = g_cclosure_new (G_CALLBACK (chat_window_accel_cb), + window, + NULL); + gtk_accel_group_connect (accel_group, + tab_accel_keys[i], + GDK_MOD1_MASK, + 0, + closure); + } + + g_object_unref (accel_group); + + /* Set the contact information menu item image to the Empathy + * stock image + */ + image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (priv->menu_conv_info)); + gtk_image_set_from_icon_name (GTK_IMAGE (image), + EMPATHY_IMAGE_CONTACT_INFORMATION, + GTK_ICON_SIZE_MENU); + + /* Set up smiley menu */ + menu = empathy_chat_view_get_smiley_menu ( + G_CALLBACK (chat_window_insert_smiley_activate_cb), + window); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->menu_conv_insert_smiley), menu); + + /* Set up signals we can't do with glade since we may need to + * block/unblock them at some later stage. + */ + + g_signal_connect (priv->dialog, + "delete_event", + G_CALLBACK (chat_window_delete_event_cb), + window); + + g_signal_connect (priv->menu_room_show_contacts, + "toggled", + G_CALLBACK (chat_window_show_contacts_toggled_cb), + window); + + g_signal_connect_swapped (priv->menu_tabs_prev, + "activate", + G_CALLBACK (gtk_notebook_prev_page), + priv->notebook); + g_signal_connect_swapped (priv->menu_tabs_next, + "activate", + G_CALLBACK (gtk_notebook_next_page), + priv->notebook); + + g_signal_connect (priv->dialog, + "focus_in_event", + G_CALLBACK (chat_window_focus_in_event_cb), + window); + g_signal_connect_after (priv->notebook, + "switch_page", + G_CALLBACK (chat_window_page_switched_cb), + window); + g_signal_connect (priv->notebook, + "page_reordered", + G_CALLBACK (chat_window_page_reordered_cb), + window); + g_signal_connect (priv->notebook, + "page_added", + G_CALLBACK (chat_window_page_added_cb), + window); + g_signal_connect (priv->notebook, + "page_removed", + G_CALLBACK (chat_window_page_removed_cb), + window); + + /* Set up drag and drop */ + gtk_drag_dest_set (GTK_WIDGET (priv->notebook), + GTK_DEST_DEFAULT_ALL, + drag_types_dest, + G_N_ELEMENTS (drag_types_dest), + GDK_ACTION_MOVE); + + g_signal_connect (priv->notebook, + "drag-data-received", + G_CALLBACK (chat_window_drag_data_received), + window); + + chat_windows = g_list_prepend (chat_windows, window); + + /* Set up private details */ + priv->chats = NULL; + priv->chats_new_msg = NULL; + priv->chats_composing = NULL; + priv->current_chat = NULL; +} + +/* Returns the window to open a new tab in if there is only one window + * visble, otherwise, returns NULL indicating that a new window should + * be added. + */ +EmpathyChatWindow * +empathy_chat_window_get_default (void) +{ + GList *l; + gboolean separate_windows = TRUE; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS, + &separate_windows); + + if (separate_windows) { + /* Always create a new window */ + return NULL; + } + + for (l = chat_windows; l; l = l->next) { + EmpathyChatWindow *chat_window; + GtkWidget *dialog; + + chat_window = l->data; + + dialog = empathy_chat_window_get_dialog (chat_window); + if (empathy_window_get_is_visible (GTK_WINDOW (GTK_WINDOW (dialog)))) { + /* Found a visible window on this desktop */ + return chat_window; + } + } + + return NULL; +} + +static void +empathy_chat_window_finalize (GObject *object) +{ + EmpathyChatWindow *window; + EmpathyChatWindowPriv *priv; + + window = EMPATHY_CHAT_WINDOW (object); + priv = GET_PRIV (window); + + empathy_debug (DEBUG_DOMAIN, "Finalized: %p", object); + + if (priv->save_geometry_id != 0) { + g_source_remove (priv->save_geometry_id); + } + + chat_windows = g_list_remove (chat_windows, window); + gtk_widget_destroy (priv->dialog); + + g_signal_handlers_disconnect_by_func (priv->chatroom_manager, + chat_window_update_menu, + window); + g_object_unref (priv->chatroom_manager); + + G_OBJECT_CLASS (empathy_chat_window_parent_class)->finalize (object); +} + +static void +chat_window_accel_cb (GtkAccelGroup *accelgroup, + GObject *object, + guint key, + GdkModifierType mod, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gint num = -1; + gint i; + + priv = GET_PRIV (window); + + for (i = 0; i < G_N_ELEMENTS (tab_accel_keys); i++) { + if (tab_accel_keys[i] == key) { + num = i; + break; + } + } + + if (num != -1) { + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), num); + } +} + +static void +chat_window_close_clicked_cb (GtkWidget *button, + EmpathyChat *chat) +{ + EmpathyChatWindow *window; + + window = empathy_chat_get_window (chat); + empathy_chat_window_remove_chat (window, chat); +} + +static void +chat_window_close_button_style_set_cb (GtkWidget *button, + GtkStyle *previous_style, + gpointer user_data) +{ + gint h, w; + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (button), + GTK_ICON_SIZE_MENU, &w, &h); + + gtk_widget_set_size_request (button, w, h); +} + +static GtkWidget * +chat_window_create_label (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + GtkWidget *hbox; + GtkWidget *name_label; + GtkWidget *status_image; + GtkWidget *close_button; + GtkWidget *close_image; + GtkWidget *event_box; + GtkWidget *event_box_hbox; + PangoAttrList *attr_list; + PangoAttribute *attr; + + priv = GET_PRIV (window); + + /* The spacing between the button and the label. */ + hbox = gtk_hbox_new (FALSE, 0); + + event_box = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); + + name_label = gtk_label_new (empathy_chat_get_name (chat)); + gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_END); + + attr_list = pango_attr_list_new (); + attr = pango_attr_scale_new (1/1.2); + attr->start_index = 0; + attr->end_index = -1; + pango_attr_list_insert (attr_list, attr); + gtk_label_set_attributes (GTK_LABEL (name_label), attr_list); + pango_attr_list_unref (attr_list); + + gtk_misc_set_padding (GTK_MISC (name_label), 2, 0); + gtk_misc_set_alignment (GTK_MISC (name_label), 0.0, 0.5); + g_object_set_data (G_OBJECT (chat), "chat-window-tab-label", name_label); + + status_image = gtk_image_new (); + + /* Spacing between the icon and label. */ + event_box_hbox = gtk_hbox_new (FALSE, 0); + + gtk_box_pack_start (GTK_BOX (event_box_hbox), status_image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (event_box_hbox), name_label, TRUE, TRUE, 0); + + g_object_set_data (G_OBJECT (chat), "chat-window-tab-image", status_image); + g_object_set_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget", event_box); + + close_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); + + /* We don't want focus/keynav for the button to avoid clutter, and + * Ctrl-W works anyway. + */ + GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (close_button, GTK_CAN_DEFAULT); + + /* Set the name to make the special rc style match. */ + gtk_widget_set_name (close_button, "empathy-close-button"); + + close_image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + + gtk_container_add (GTK_CONTAINER (close_button), close_image); + + gtk_container_add (GTK_CONTAINER (event_box), event_box_hbox); + gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0); + gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); + + /* React to theme changes and also used to setup the initial size + * correctly. + */ + g_signal_connect (close_button, + "style-set", + G_CALLBACK (chat_window_close_button_style_set_cb), + chat); + + g_signal_connect (close_button, + "clicked", + G_CALLBACK (chat_window_close_clicked_cb), + chat); + + /* Set up tooltip */ + chat_window_update_tooltip (window, chat); + + gtk_widget_show_all (hbox); + + return hbox; +} + +static void +chat_window_update_status (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + GtkImage *image; + const gchar *icon_name = NULL; + + priv = GET_PRIV (window); + + if (g_list_find (priv->chats_new_msg, chat)) { + icon_name = EMPATHY_IMAGE_MESSAGE; + } + else if (g_list_find (priv->chats_composing, chat)) { + icon_name = EMPATHY_IMAGE_TYPING; + } + else { + icon_name = empathy_chat_get_status_icon_name (chat); + } + image = g_object_get_data (G_OBJECT (chat), "chat-window-tab-image"); + gtk_image_set_from_icon_name (image, icon_name, GTK_ICON_SIZE_MENU); + + chat_window_update_title (window, chat); + chat_window_update_tooltip (window, chat); +} + +static void +chat_window_update_title (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + gint n_chats; + + priv = GET_PRIV (window); + + n_chats = g_list_length (priv->chats); + if (n_chats == 1) { + gtk_window_set_title (GTK_WINDOW (priv->dialog), + empathy_chat_get_name (priv->current_chat)); + } else { + gchar *title; + + title = g_strdup_printf (_("Conversations (%d)"), n_chats); + gtk_window_set_title (GTK_WINDOW (priv->dialog), title); + g_free (title); + } + + if (priv->chats_new_msg) { + gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), + EMPATHY_IMAGE_MESSAGE); + } else { + gtk_window_set_icon_name (GTK_WINDOW (priv->dialog), NULL); + } +} + +static void +chat_window_update_menu (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gboolean first_page; + gboolean last_page; + gboolean is_connected; + gint num_pages; + gint page_num; + + priv = GET_PRIV (window); + + /* Notebook pages */ + page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + num_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + first_page = (page_num == 0); + last_page = (page_num == (num_pages - 1)); + + gtk_widget_set_sensitive (priv->menu_tabs_next, !last_page); + gtk_widget_set_sensitive (priv->menu_tabs_prev, !first_page); + gtk_widget_set_sensitive (priv->menu_tabs_detach, num_pages > 1); + gtk_widget_set_sensitive (priv->menu_tabs_left, !first_page); + gtk_widget_set_sensitive (priv->menu_tabs_right, !last_page); + + is_connected = empathy_chat_is_connected (priv->current_chat); + + if (empathy_chat_is_group_chat (priv->current_chat)) { + EmpathyGroupChat *group_chat; + EmpathyChatroom *chatroom; + gboolean show_contacts; + + group_chat = EMPATHY_GROUP_CHAT (priv->current_chat); + + /* Show / Hide widgets */ + gtk_widget_show (priv->menu_room); + + gtk_widget_hide (priv->menu_conv_add_contact); + gtk_widget_hide (priv->menu_conv_info); + gtk_widget_hide (priv->menu_conv_separator); + + /* Can we add this room to our favourites and are we + * connected to the room? + */ + chatroom = empathy_chatroom_manager_find (priv->chatroom_manager, + empathy_chat_get_account (priv->current_chat), + empathy_chat_get_id (priv->current_chat)); + + gtk_widget_set_sensitive (priv->menu_room_add, chatroom == NULL); + gtk_widget_set_sensitive (priv->menu_conv_insert_smiley, is_connected); + gtk_widget_set_sensitive (priv->menu_room_join_new, is_connected); + gtk_widget_set_sensitive (priv->menu_room_invite, is_connected); + + /* We need to block the signal here because all we are + * really trying to do is check or uncheck the menu + * item. If we don't do this we get funny behaviour + * with 2 or more group chat windows where showing + * contacts doesn't do anything. + */ + show_contacts = empathy_group_chat_get_show_contacts (group_chat); + + g_signal_handlers_block_by_func (priv->menu_room_show_contacts, + chat_window_show_contacts_toggled_cb, + window); + + g_object_set (priv->menu_room_show_contacts, + "active", show_contacts, + NULL); + + g_signal_handlers_unblock_by_func (priv->menu_room_show_contacts, + chat_window_show_contacts_toggled_cb, + window); + } else { + EmpathyPrivateChat *chat; + EmpathyContact *contact; + McPresence presence; + + chat = EMPATHY_PRIVATE_CHAT (priv->current_chat); + + /* Show / Hide widgets */ + gtk_widget_hide (priv->menu_room); + + /* Unset presence means this contact refuses to send us his + * presence. By adding the contact we ask the contact to accept + * to send his presence. */ + contact = empathy_private_chat_get_contact (chat); + presence = empathy_contact_get_presence (contact); + if (presence == MC_PRESENCE_UNSET) { + gtk_widget_show (priv->menu_conv_add_contact); + } else { + gtk_widget_hide (priv->menu_conv_add_contact); + } + + gtk_widget_show (priv->menu_conv_separator); + gtk_widget_show (priv->menu_conv_info); + + /* Are we connected? */ + gtk_widget_set_sensitive (priv->menu_conv_insert_smiley, is_connected); + gtk_widget_set_sensitive (priv->menu_conv_add_contact, is_connected); + gtk_widget_set_sensitive (priv->menu_conv_info, is_connected); + } +} + +static void +chat_window_insert_smiley_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + GtkTextBuffer *buffer; + GtkTextIter iter; + const gchar *smiley; + + priv = GET_PRIV (window); + + chat = priv->current_chat; + + smiley = g_object_get_data (G_OBJECT (menuitem), "smiley_text"); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert (buffer, &iter, + smiley, -1); +} + +static void +chat_window_clear_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + empathy_chat_clear (priv->current_chat); +} + +static void +chat_window_add_contact_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + //EmpathyContact *contact; + + priv = GET_PRIV (window); + + //contact = empathy_chat_get_contact (priv->current_chat); + + // FIXME: empathy_add_contact_dialog_show (NULL, contact); +} + +static void +chat_window_call_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + if (!empathy_chat_is_group_chat (priv->current_chat)) { + EmpathyPrivateChat *chat; + EmpathyContact *contact; + + chat = EMPATHY_PRIVATE_CHAT (priv->current_chat); + contact = empathy_private_chat_get_contact (chat); + + empathy_call_with_contact (contact); + } +} + +static void +chat_window_log_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + empathy_log_window_show (empathy_chat_get_account (priv->current_chat), + empathy_chat_get_id (priv->current_chat), + empathy_chat_is_group_chat (priv->current_chat), + GTK_WINDOW (priv->dialog)); +} + +static void +chat_window_info_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyContact *contact; + + priv = GET_PRIV (window); + + contact = empathy_private_chat_get_contact (EMPATHY_PRIVATE_CHAT (priv->current_chat)); + + empathy_contact_information_dialog_show (contact, + GTK_WINDOW (priv->dialog), + FALSE, FALSE); +} + +static gboolean +chat_window_save_geometry_timeout_cb (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gint x, y, w, h; + + priv = GET_PRIV (window); + + gtk_window_get_size (GTK_WINDOW (priv->dialog), &w, &h); + gtk_window_get_position (GTK_WINDOW (priv->dialog), &x, &y); + + empathy_chat_save_geometry (priv->current_chat, x, y, w, h); + + priv->save_geometry_id = 0; + + return FALSE; +} + +static gboolean +chat_window_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + if (priv->save_geometry_id != 0) { + g_source_remove (priv->save_geometry_id); + } + + priv->save_geometry_id = + g_timeout_add_seconds (1, + (GSourceFunc) chat_window_save_geometry_timeout_cb, + window); + + return FALSE; +} + +static void +chat_window_conv_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyLogManager *manager; + gboolean log_exists = FALSE; + gboolean can_voip = FALSE; + + priv = GET_PRIV (window); + + manager = empathy_log_manager_new (); + log_exists = empathy_log_manager_exists (manager, + empathy_chat_get_account (priv->current_chat), + empathy_chat_get_id (priv->current_chat), + empathy_chat_is_group_chat (priv->current_chat)); + g_object_unref (manager); + + if (!empathy_chat_is_group_chat (priv->current_chat)) { + EmpathyPrivateChat *chat; + EmpathyContact *contact; + + chat = EMPATHY_PRIVATE_CHAT (priv->current_chat); + contact = empathy_private_chat_get_contact (chat); + can_voip = empathy_contact_can_voip (contact); + } + + gtk_widget_set_sensitive (priv->menu_conv_log, log_exists); +#ifdef HAVE_VOIP + gtk_widget_set_sensitive (priv->menu_conv_call, can_voip); +#else + g_object_set (priv->menu_conv_call, "visible", FALSE, NULL); + g_object_set (priv->menu_conv_call_separator, "visible", FALSE, NULL); +#endif +} + +static void +chat_window_show_contacts_toggled_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gboolean show; + + priv = GET_PRIV (window); + + g_return_if_fail (priv->current_chat != NULL); + + show = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (priv->menu_room_show_contacts)); + empathy_group_chat_set_show_contacts (EMPATHY_GROUP_CHAT (priv->current_chat), show); +} + +static void +chat_window_close_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + g_return_if_fail (priv->current_chat != NULL); + + empathy_chat_window_remove_chat (window, priv->current_chat); +} + +static void +chat_window_room_set_topic_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + if (empathy_chat_is_group_chat (priv->current_chat)) { + EmpathyGroupChat *group_chat; + + group_chat = EMPATHY_GROUP_CHAT (priv->current_chat); + empathy_group_chat_set_topic (group_chat); + } +} + +static void +chat_window_room_join_new_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + empathy_new_chatroom_dialog_show (GTK_WINDOW (priv->dialog)); +} + +static void +chat_window_room_invite_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ +/* FIXME: + EmpathyChatWindowPriv *priv; + EmpathyContact *own_contact; + EmpathyChatroomId id = 0; + + priv = GET_PRIV (window); + own_contact = empathy_chat_get_own_contact (priv->current_chat); + + if (empathy_chat_is_group_chat (priv->current_chat)) { + EmpathyGroupChat *group_chat; + + group_chat = EMPATHY_GROUP_CHAT (priv->current_chat); + id = empathy_group_chat_get_chatroom_id (group_chat); + } + + empathy_chat_invite_dialog_show (own_contact, id); +*/ +} + +static void +chat_window_room_add_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChatroomManager *manager; + EmpathyChatroom *chatroom; + + priv = GET_PRIV (window); + + g_return_if_fail (priv->current_chat != NULL); + + if (!empathy_chat_is_group_chat (priv->current_chat)) { + return; + } + + chatroom = empathy_chatroom_new_full (empathy_chat_get_account (priv->current_chat), + empathy_chat_get_id (priv->current_chat), + empathy_chat_get_name (priv->current_chat), + FALSE); + + manager = empathy_chatroom_manager_new (); + empathy_chatroom_manager_add (manager, chatroom); + chat_window_update_menu (window); + + g_object_unref (chatroom); + g_object_unref (manager); +} + +static void +chat_window_edit_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + GtkClipboard *clipboard; + GtkTextBuffer *buffer; + gboolean text_available; + + priv = GET_PRIV (window); + + g_return_if_fail (priv->current_chat != NULL); + + if (!empathy_chat_is_connected (priv->current_chat)) { + gtk_widget_set_sensitive (priv->menu_edit_copy, FALSE); + gtk_widget_set_sensitive (priv->menu_edit_cut, FALSE); + gtk_widget_set_sensitive (priv->menu_edit_paste, FALSE); + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->current_chat->input_text_view)); + if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) { + gtk_widget_set_sensitive (priv->menu_edit_copy, TRUE); + gtk_widget_set_sensitive (priv->menu_edit_cut, TRUE); + } else { + gboolean selection; + + selection = empathy_chat_view_get_selection_bounds (priv->current_chat->view, + NULL, NULL); + + gtk_widget_set_sensitive (priv->menu_edit_cut, FALSE); + gtk_widget_set_sensitive (priv->menu_edit_copy, selection); + } + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + text_available = gtk_clipboard_wait_is_text_available (clipboard); + gtk_widget_set_sensitive (priv->menu_edit_paste, text_available); +} + +static void +chat_window_cut_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + + priv = GET_PRIV (window); + + empathy_chat_cut (priv->current_chat); +} + +static void +chat_window_copy_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + + priv = GET_PRIV (window); + + empathy_chat_copy (priv->current_chat); +} + +static void +chat_window_paste_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (window)); + + priv = GET_PRIV (window); + + empathy_chat_paste (priv->current_chat); +} + +static void +chat_window_tabs_left_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + gint index; + + priv = GET_PRIV (window); + + chat = priv->current_chat; + index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + if (index <= 0) { + return; + } + + gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook), + empathy_chat_get_widget (chat), + index - 1); + + chat_window_update_menu (window); + chat_window_update_status (window, chat); +} + +static void +chat_window_tabs_right_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + gint index; + + priv = GET_PRIV (window); + + chat = priv->current_chat; + index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook)); + + gtk_notebook_reorder_child (GTK_NOTEBOOK (priv->notebook), + empathy_chat_get_widget (chat), + index + 1); + + chat_window_update_menu (window); + chat_window_update_status (window, chat); +} + +static void +chat_window_detach_activate_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChatWindow *new_window; + EmpathyChat *chat; + + priv = GET_PRIV (window); + + chat = priv->current_chat; + new_window = empathy_chat_window_new (); + + empathy_chat_window_move_chat (window, new_window, chat); + + priv = GET_PRIV (new_window); + gtk_widget_show (priv->dialog); +} + +static void +chat_window_help_contents_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + //empathy_help_show (); +} + +static void +chat_window_help_about_cb (GtkWidget *menuitem, + EmpathyChatWindow *window) +{ + empathy_about_dialog_new (GTK_WINDOW (window)); +} + +static gboolean +chat_window_delete_event_cb (GtkWidget *dialog, + GdkEvent *event, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + GList *list; + GList *l; + + priv = GET_PRIV (window); + + empathy_debug (DEBUG_DOMAIN, "Delete event received"); + + list = g_list_copy (priv->chats); + + for (l = list; l; l = l->next) { + empathy_chat_window_remove_chat (window, l->data); + } + + g_list_free (list); + + return TRUE; +} + +static void +chat_window_status_changed_cb (EmpathyChat *chat, + EmpathyChatWindow *window) +{ + chat_window_update_menu (window); + chat_window_update_status (window, chat); +} + +static void +chat_window_update_tooltip (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + GtkWidget *widget; + gchar *current_tooltip; + gchar *str; + + priv = GET_PRIV (window); + + current_tooltip = empathy_chat_get_tooltip (chat); + + if (g_list_find (priv->chats_composing, chat)) { + str = g_strconcat (current_tooltip, "\n", _("Typing a message."), NULL); + g_free (current_tooltip); + } else { + str = current_tooltip; + } + + widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-tooltip-widget"); + gtk_widget_set_tooltip_text (widget, str); + + g_free (str); +} + +static void +chat_window_name_changed_cb (EmpathyChat *chat, + const gchar *name, + EmpathyChatWindow *window) +{ + GtkLabel *label; + + label = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label"); + + gtk_label_set_text (label, name); +} + +static void +chat_window_composing_cb (EmpathyChat *chat, + gboolean is_composing, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + if (is_composing && !g_list_find (priv->chats_composing, chat)) { + priv->chats_composing = g_list_prepend (priv->chats_composing, chat); + } else { + priv->chats_composing = g_list_remove (priv->chats_composing, chat); + } + + chat_window_update_status (window, chat); +} + +static void +chat_window_new_message_cb (EmpathyChat *chat, + EmpathyMessage *message, + gboolean is_backlog, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gboolean has_focus; + gboolean needs_urgency; + + priv = GET_PRIV (window); + + has_focus = empathy_chat_window_has_focus (window); + + if (has_focus && priv->current_chat == chat) { + empathy_debug (DEBUG_DOMAIN, "New message, we have focus"); + return; + } + + empathy_debug (DEBUG_DOMAIN, "New message, no focus"); + + needs_urgency = FALSE; + if (empathy_chat_is_group_chat (chat)) { + if (!is_backlog && + empathy_chat_should_highlight_nick (message)) { + empathy_debug (DEBUG_DOMAIN, "Highlight this nick"); + needs_urgency = TRUE; + } + } else { + needs_urgency = TRUE; + } + + if (needs_urgency && !has_focus) { + chat_window_set_urgency_hint (window, TRUE); + } + + if (!is_backlog && + !g_list_find (priv->chats_new_msg, chat)) { + priv->chats_new_msg = g_list_prepend (priv->chats_new_msg, chat); + chat_window_update_status (window, chat); + } +} + +static GtkNotebook * +chat_window_detach_hook (GtkNotebook *source, + GtkWidget *page, + gint x, + gint y, + gpointer user_data) +{ + EmpathyChatWindowPriv *priv; + EmpathyChatWindow *window, *new_window; + EmpathyChat *chat; + + chat = g_object_get_data (G_OBJECT (page), "chat"); + window = empathy_chat_get_window (chat); + + new_window = empathy_chat_window_new (); + priv = GET_PRIV (new_window); + + empathy_debug (DEBUG_DOMAIN, "Detach hook called"); + + empathy_chat_window_move_chat (window, new_window, chat); + + gtk_window_move (GTK_WINDOW (priv->dialog), x, y); + gtk_widget_show (priv->dialog); + + return NULL; +} + +static void +chat_window_page_switched_cb (GtkNotebook *notebook, + GtkNotebookPage *page, + gint page_num, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + GtkWidget *child; + + empathy_debug (DEBUG_DOMAIN, "Page switched"); + + priv = GET_PRIV (window); + + child = gtk_notebook_get_nth_page (notebook, page_num); + chat = g_object_get_data (G_OBJECT (child), "chat"); + + if (priv->page_added) { + priv->page_added = FALSE; + empathy_chat_scroll_down (chat); + } + else if (priv->current_chat == chat) { + return; + } + + priv->current_chat = chat; + priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat); + + chat_window_update_menu (window); + chat_window_update_status (window, chat); +} + +static void +chat_window_page_reordered_cb (GtkNotebook *notebook, + GtkWidget *widget, + guint page_num, + EmpathyChatWindow *window) +{ + empathy_debug (DEBUG_DOMAIN, "Page reordered"); + + chat_window_update_menu (window); +} + +static void +chat_window_page_added_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + + priv = GET_PRIV (window); + + /* If we just received DND to the same window, we don't want + * to do anything here like removing the tab and then readding + * it, so we return here and in "page-added". + */ + if (priv->dnd_same_window) { + empathy_debug (DEBUG_DOMAIN, "Page added (back to the same window)"); + priv->dnd_same_window = FALSE; + return; + } + + empathy_debug (DEBUG_DOMAIN, "Page added"); + + /* Get chat object */ + chat = g_object_get_data (G_OBJECT (child), "chat"); + + /* Set the chat window */ + empathy_chat_set_window (chat, window); + + /* Connect chat signals for this window */ + g_signal_connect (chat, "status-changed", + G_CALLBACK (chat_window_status_changed_cb), + window); + g_signal_connect (chat, "name-changed", + G_CALLBACK (chat_window_name_changed_cb), + window); + g_signal_connect (chat, "composing", + G_CALLBACK (chat_window_composing_cb), + window); + g_signal_connect (chat, "new-message", + G_CALLBACK (chat_window_new_message_cb), + window); + + /* Set flag so we know to perform some special operations on + * switch page due to the new page being added. + */ + priv->page_added = TRUE; + + /* Get list of chats up to date */ + priv->chats = g_list_append (priv->chats, chat); +} + +static void +chat_window_page_removed_cb (GtkNotebook *notebook, + GtkWidget *child, + guint page_num, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + EmpathyChat *chat; + + priv = GET_PRIV (window); + + /* If we just received DND to the same window, we don't want + * to do anything here like removing the tab and then readding + * it, so we return here and in "page-added". + */ + if (priv->dnd_same_window) { + empathy_debug (DEBUG_DOMAIN, "Page removed (and will be readded to same window)"); + return; + } + + empathy_debug (DEBUG_DOMAIN, "Page removed"); + + /* Get chat object */ + chat = g_object_get_data (G_OBJECT (child), "chat"); + + /* Unset the window associated with a chat */ + empathy_chat_set_window (chat, NULL); + + /* Disconnect all signal handlers for this chat and this window */ + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_status_changed_cb), + window); + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_name_changed_cb), + window); + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_composing_cb), + window); + g_signal_handlers_disconnect_by_func (chat, + G_CALLBACK (chat_window_new_message_cb), + window); + + /* Keep list of chats up to date */ + priv->chats = g_list_remove (priv->chats, chat); + priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat); + priv->chats_composing = g_list_remove (priv->chats_composing, chat); + + if (priv->chats == NULL) { + g_object_unref (window); + } else { + chat_window_update_menu (window); + chat_window_update_title (window, NULL); + } +} + +static gboolean +chat_window_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + empathy_debug (DEBUG_DOMAIN, "Focus in event, updating title"); + + priv = GET_PRIV (window); + + priv->chats_new_msg = g_list_remove (priv->chats_new_msg, priv->current_chat); + + chat_window_set_urgency_hint (window, FALSE); + + /* Update the title, since we now mark all unread messages as read. */ + chat_window_update_status (window, priv->current_chat); + + return FALSE; +} + +static void +chat_window_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection, + guint info, + guint time, + EmpathyChatWindow *window) +{ + if (info == DND_DRAG_TYPE_CONTACT_ID) { + EmpathyContactFactory *factory; + EmpathyContact *contact = NULL; + EmpathyChat *chat; + EmpathyChatWindow *old_window; + McAccount *account; + const gchar *id = NULL; + gchar **strv; + + if (selection) { + id = (const gchar*) selection->data; + } + + empathy_debug (DEBUG_DOMAIN, "DND contact from roster with id:'%s'", id); + + strv = g_strsplit (id, "/", 2); + factory = empathy_contact_factory_new (); + account = mc_account_lookup (strv[0]); + if (account) { + contact = empathy_contact_factory_get_from_id (factory, + account, + strv[1]); + g_object_unref (account); + } + g_object_unref (factory); + g_object_unref (account); + g_strfreev (strv); + + if (!contact) { + empathy_debug (DEBUG_DOMAIN, "DND contact from roster not found"); + return; + } + + account = empathy_contact_get_account (contact); + chat = empathy_chat_window_find_chat (account, id); + + if (!chat) { + empathy_chat_with_contact_id (account, id); + return; + } + + old_window = empathy_chat_get_window (chat); + if (old_window) { + if (old_window == window) { + gtk_drag_finish (context, TRUE, FALSE, time); + return; + } + + empathy_chat_window_move_chat (old_window, window, chat); + } else { + empathy_chat_window_add_chat (window, chat); + } + + /* Added to take care of any outstanding chat events */ + empathy_chat_present (chat); + + /* We should return TRUE to remove the data when doing + * GDK_ACTION_MOVE, but we don't here otherwise it has + * weird consequences, and we handle that internally + * anyway with add_chat() and remove_chat(). + */ + gtk_drag_finish (context, TRUE, FALSE, time); + } + else if (info == DND_DRAG_TYPE_TAB) { + EmpathyChat *chat = NULL; + EmpathyChatWindow *old_window; + GtkWidget **child = NULL; + + empathy_debug (DEBUG_DOMAIN, "DND tab"); + + if (selection) { + child = (void*) selection->data; + } + + if (child) { + chat = g_object_get_data (G_OBJECT (*child), "chat"); + } + + old_window = empathy_chat_get_window (chat); + if (old_window) { + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + if (old_window == window) { + empathy_debug (DEBUG_DOMAIN, "DND tab (within same window)"); + priv->dnd_same_window = TRUE; + gtk_drag_finish (context, TRUE, FALSE, time); + return; + } + + priv->dnd_same_window = FALSE; + } + + /* We should return TRUE to remove the data when doing + * GDK_ACTION_MOVE, but we don't here otherwise it has + * weird consequences, and we handle that internally + * anyway with add_chat() and remove_chat(). + */ + gtk_drag_finish (context, TRUE, FALSE, time); + } else { + empathy_debug (DEBUG_DOMAIN, "DND from unknown source"); + gtk_drag_finish (context, FALSE, FALSE, time); + } +} + +static void +chat_window_set_urgency_hint (EmpathyChatWindow *window, + gboolean urgent) +{ + EmpathyChatWindowPriv *priv; + + priv = GET_PRIV (window); + + empathy_debug (DEBUG_DOMAIN, "Turning %s urgency hint", + urgent ? "on" : "off"); + gtk_window_set_urgency_hint (GTK_WINDOW (priv->dialog), urgent); +} + +EmpathyChatWindow * +empathy_chat_window_new (void) +{ + return EMPATHY_CHAT_WINDOW (g_object_new (EMPATHY_TYPE_CHAT_WINDOW, NULL)); +} + +GtkWidget * +empathy_chat_window_get_dialog (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + + g_return_val_if_fail (window != NULL, NULL); + + priv = GET_PRIV (window); + + return priv->dialog; +} + +void +empathy_chat_window_add_chat (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + GtkWidget *label; + GtkWidget *child; + gint x, y, w, h; + + g_return_if_fail (window != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + priv = GET_PRIV (window); + + /* Reference the chat object */ + g_object_ref (chat); + + /* Set the chat window */ + empathy_chat_set_window (chat, window); + + empathy_chat_load_geometry (chat, &x, &y, &w, &h); + + if (x >= 0 && y >= 0) { + /* Let the window manager position it if we don't have + * good x, y coordinates. + */ + gtk_window_move (GTK_WINDOW (priv->dialog), x, y); + } + + if (w > 0 && h > 0) { + /* Use the defaults from the glade file if we don't have + * good w, h geometry. + */ + gtk_window_resize (GTK_WINDOW (priv->dialog), w, h); + } + + child = empathy_chat_get_widget (chat); + label = chat_window_create_label (window, chat); + + gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), child, label); + gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), child, TRUE); + gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), child, TRUE); + gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (priv->notebook), child, + TRUE, TRUE, GTK_PACK_START); + + empathy_debug (DEBUG_DOMAIN, + "Chat added (%d references)", + G_OBJECT (chat)->ref_count); +} + +void +empathy_chat_window_remove_chat (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + gint position; + + g_return_if_fail (window != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + priv = GET_PRIV (window); + + position = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), + empathy_chat_get_widget (chat)); + gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), position); + + empathy_debug (DEBUG_DOMAIN, + "Chat removed (%d references)", + G_OBJECT (chat)->ref_count - 1); + + g_object_unref (chat); +} + +void +empathy_chat_window_move_chat (EmpathyChatWindow *old_window, + EmpathyChatWindow *new_window, + EmpathyChat *chat) +{ + GtkWidget *widget; + + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (old_window)); + g_return_if_fail (EMPATHY_IS_CHAT_WINDOW (new_window)); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + widget = empathy_chat_get_widget (chat); + + empathy_debug (DEBUG_DOMAIN, + "Chat moving with widget:%p (%d references)", + widget, + G_OBJECT (widget)->ref_count); + + /* We reference here to make sure we don't loose the widget + * and the EmpathyChat object during the move. + */ + g_object_ref (chat); + g_object_ref (widget); + + empathy_chat_window_remove_chat (old_window, chat); + empathy_chat_window_add_chat (new_window, chat); + + g_object_unref (widget); + g_object_unref (chat); +} + +void +empathy_chat_window_switch_to_chat (EmpathyChatWindow *window, + EmpathyChat *chat) +{ + EmpathyChatWindowPriv *priv; + gint page_num; + + g_return_if_fail (window != NULL); + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + priv = GET_PRIV (window); + + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), + empathy_chat_get_widget (chat)); + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), + page_num); +} + +gboolean +empathy_chat_window_has_focus (EmpathyChatWindow *window) +{ + EmpathyChatWindowPriv *priv; + gboolean has_focus; + + g_return_val_if_fail (EMPATHY_IS_CHAT_WINDOW (window), FALSE); + + priv = GET_PRIV (window); + + g_object_get (priv->dialog, "has-toplevel-focus", &has_focus, NULL); + + return has_focus; +} + +EmpathyChat * +empathy_chat_window_find_chat (McAccount *account, + const gchar *id) +{ + GList *l; + + g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL); + g_return_val_if_fail (!G_STR_EMPTY (id), NULL); + + for (l = chat_windows; l; l = l->next) { + EmpathyChatWindowPriv *priv; + EmpathyChatWindow *window; + GList *ll; + + window = l->data; + priv = GET_PRIV (window); + + for (ll = priv->chats; ll; ll = ll->next) { + EmpathyChat *chat; + + chat = ll->data; + + if (empathy_account_equal (account, empathy_chat_get_account (chat)) && + strcmp (id, empathy_chat_get_id (chat)) == 0) { + return chat; + } + } + } + + return NULL; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-chat-window.h b/gnome-2-22/libempathy-gtk/empathy-chat-window.h new file mode 100644 index 000000000..c79f7519e --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat-window.h @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CHAT_WINDOW_H__ +#define __EMPATHY_CHAT_WINDOW_H__ + +#include <glib-object.h> + +#include <libmissioncontrol/mc-account.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CHAT_WINDOW (empathy_chat_window_get_type ()) +#define EMPATHY_CHAT_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindow)) +#define EMPATHY_CHAT_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowClass)) +#define EMPATHY_IS_CHAT_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHAT_WINDOW)) +#define EMPATHY_IS_CHAT_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHAT_WINDOW)) +#define EMPATHY_CHAT_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHAT_WINDOW, EmpathyChatWindowClass)) + +typedef struct _EmpathyChatWindow EmpathyChatWindow; +typedef struct _EmpathyChatWindowClass EmpathyChatWindowClass; +typedef struct _EmpathyChatWindowPriv EmpathyChatWindowPriv; + +#include "empathy-chat.h" + +struct _EmpathyChatWindow { + GObject parent; +}; + +struct _EmpathyChatWindowClass { + GObjectClass parent_class; +}; + +GType empathy_chat_window_get_type (void); +EmpathyChatWindow *empathy_chat_window_get_default (void); + +EmpathyChatWindow *empathy_chat_window_new (void); + +GtkWidget * empathy_chat_window_get_dialog (EmpathyChatWindow *window); + +void empathy_chat_window_add_chat (EmpathyChatWindow *window, + EmpathyChat *chat); +void empathy_chat_window_remove_chat (EmpathyChatWindow *window, + EmpathyChat *chat); +void empathy_chat_window_move_chat (EmpathyChatWindow *old_window, + EmpathyChatWindow *new_window, + EmpathyChat *chat); +void empathy_chat_window_switch_to_chat (EmpathyChatWindow *window, + EmpathyChat *chat); +gboolean empathy_chat_window_has_focus (EmpathyChatWindow *window); +EmpathyChat * empathy_chat_window_find_chat (McAccount *account, + const gchar *id); + +G_END_DECLS + +#endif /* __EMPATHY_CHAT_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-chat.c b/gnome-2-22/libempathy-gtk/empathy-chat.c new file mode 100644 index 000000000..b02452476 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat.c @@ -0,0 +1,1761 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> + +#include <gdk/gdkkeysyms.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-log-manager.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-chat.h" +#include "empathy-chat-window.h" +#include "empathy-geometry.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-spell.h" +#include "empathy-spell-dialog.h" +#include "empathy-ui-utils.h" +#include "empathy-gtk-marshal.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CHAT, EmpathyChatPriv)) + +#define DEBUG_DOMAIN "Chat" + +#define CHAT_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR) +#define CHAT_FILE_CREATE_MODE (S_IRUSR | S_IWUSR) + +#define IS_ENTER(v) (v == GDK_Return || v == GDK_ISO_Enter || v == GDK_KP_Enter) + +#define MAX_INPUT_HEIGHT 150 + +#define COMPOSING_STOP_TIMEOUT 5 + +struct _EmpathyChatPriv { + EmpathyLogManager *log_manager; + EmpathyTpChat *tp_chat; + EmpathyChatWindow *window; + McAccount *account; + MissionControl *mc; + guint composing_stop_timeout_id; + gboolean sensitive; + gchar *id; + GSList *sent_messages; + gint sent_messages_index; + GList *compositors; + guint scroll_idle_id; + gboolean first_tp_chat; + gboolean is_first_char; + guint block_events_timeout_id; + /* Used to automatically shrink a window that has temporarily + * grown due to long input. + */ + gint padding_height; + gint default_window_height; + gint last_input_height; + gboolean vscroll_visible; +}; + +typedef struct { + EmpathyChat *chat; + gchar *word; + + GtkTextIter start; + GtkTextIter end; +} EmpathyChatSpell; + +static void empathy_chat_class_init (EmpathyChatClass *klass); +static void empathy_chat_init (EmpathyChat *chat); +static void chat_finalize (GObject *object); +static void chat_destroy_cb (EmpathyTpChat *tp_chat, + EmpathyChat *chat); +static void chat_send (EmpathyChat *chat, + const gchar *msg); +static void chat_input_text_view_send (EmpathyChat *chat); +static void chat_message_received_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + EmpathyChat *chat); +static void chat_send_error_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + TpChannelTextSendError error_code, + EmpathyChat *chat); +static void chat_sent_message_add (EmpathyChat *chat, + const gchar *str); +static const gchar * chat_sent_message_get_next (EmpathyChat *chat); +static const gchar * chat_sent_message_get_last (EmpathyChat *chat); +static gboolean chat_input_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + EmpathyChat *chat); +static void chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer, + EmpathyChat *chat); +static gboolean chat_text_view_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChat *chat); +static void chat_text_view_scroll_hide_cb (GtkWidget *widget, + EmpathyChat *chat); +static void chat_text_view_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + EmpathyChat *chat); +static void chat_text_view_realize_cb (GtkWidget *widget, + EmpathyChat *chat); +static void chat_text_populate_popup_cb (GtkTextView *view, + GtkMenu *menu, + EmpathyChat *chat); +static void chat_text_check_word_spelling_cb (GtkMenuItem *menuitem, + EmpathyChatSpell *chat_spell); +static EmpathyChatSpell *chat_spell_new (EmpathyChat *chat, + const gchar *word, + GtkTextIter start, + GtkTextIter end); +static void chat_spell_free (EmpathyChatSpell *chat_spell); +static void chat_composing_start (EmpathyChat *chat); +static void chat_composing_stop (EmpathyChat *chat); +static void chat_composing_remove_timeout (EmpathyChat *chat); +static gboolean chat_composing_stop_timeout_cb (EmpathyChat *chat); +static void chat_state_changed_cb (EmpathyTpChat *tp_chat, + EmpathyContact *contact, + TpChannelChatState state, + EmpathyChat *chat); +static void chat_add_logs (EmpathyChat *chat); +static gboolean chat_scroll_down_idle_func (EmpathyChat *chat); + +enum { + COMPOSING, + NEW_MESSAGE, + NAME_CHANGED, + STATUS_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_TP_CHAT +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (EmpathyChat, empathy_chat, G_TYPE_OBJECT); + +static void +chat_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyChatPriv *priv = GET_PRIV (object); + + switch (param_id) { + case PROP_TP_CHAT: + g_value_set_object (value, priv->tp_chat); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +chat_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyChat *chat = EMPATHY_CHAT (object); + + switch (param_id) { + case PROP_TP_CHAT: + empathy_chat_set_tp_chat (chat, + EMPATHY_TP_CHAT (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +chat_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv = GET_PRIV (chat); + McAccount *account; + + account = mc_account_lookup (unique_name); + + if (status == TP_CONNECTION_STATUS_CONNECTED && !priv->tp_chat && + empathy_account_equal (account, priv->account)) { + TpHandleType handle_type; + + empathy_debug (DEBUG_DOMAIN, + "Account reconnected, request a new Text channel"); + + if (empathy_chat_is_group_chat (chat)) { + handle_type = TP_HANDLE_TYPE_ROOM; + } else { + handle_type = TP_HANDLE_TYPE_CONTACT; + } + + mission_control_request_channel_with_string_handle (mc, + priv->account, + TP_IFACE_CHANNEL_TYPE_TEXT, + priv->id, + handle_type, + NULL, NULL); + } + + g_object_unref (account); +} + +static void +empathy_chat_class_init (EmpathyChatClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = chat_finalize; + object_class->get_property = chat_get_property; + object_class->set_property = chat_set_property; + + g_object_class_install_property (object_class, + PROP_TP_CHAT, + g_param_spec_object ("tp-chat", + "Empathy tp chat", + "The tp chat object", + EMPATHY_TYPE_TP_CHAT, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE)); + + signals[COMPOSING] = + g_signal_new ("composing", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + + signals[NEW_MESSAGE] = + g_signal_new ("new-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _empathy_gtk_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, + 2, EMPATHY_TYPE_MESSAGE, G_TYPE_BOOLEAN); + + signals[NAME_CHANGED] = + g_signal_new ("name-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + signals[STATUS_CHANGED] = + g_signal_new ("status-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private (object_class, sizeof (EmpathyChatPriv)); +} + +static void +empathy_chat_init (EmpathyChat *chat) +{ + EmpathyChatPriv *priv = GET_PRIV (chat); + GtkTextBuffer *buffer; + + chat->view = empathy_chat_view_new (); + chat->input_text_view = gtk_text_view_new (); + + priv->is_first_char = TRUE; + + g_object_set (chat->input_text_view, + "pixels-above-lines", 2, + "pixels-below-lines", 2, + "pixels-inside-wrap", 1, + "right-margin", 2, + "left-margin", 2, + "wrap-mode", GTK_WRAP_WORD_CHAR, + NULL); + + priv->log_manager = empathy_log_manager_new (); + priv->default_window_height = -1; + priv->vscroll_visible = FALSE; + priv->sensitive = TRUE; + priv->sent_messages = NULL; + priv->sent_messages_index = -1; + priv->first_tp_chat = TRUE; + priv->mc = empathy_mission_control_new (); + + dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged", + G_CALLBACK (chat_status_changed_cb), + chat, NULL); + + g_signal_connect (chat->input_text_view, + "key_press_event", + G_CALLBACK (chat_input_key_press_event_cb), + chat); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + g_signal_connect (buffer, + "changed", + G_CALLBACK (chat_input_text_buffer_changed_cb), + chat); + g_signal_connect (chat->view, + "focus_in_event", + G_CALLBACK (chat_text_view_focus_in_event_cb), + chat); + + g_signal_connect (chat->input_text_view, + "size_allocate", + G_CALLBACK (chat_text_view_size_allocate_cb), + chat); + + g_signal_connect (chat->input_text_view, + "realize", + G_CALLBACK (chat_text_view_realize_cb), + chat); + + g_signal_connect (GTK_TEXT_VIEW (chat->input_text_view), + "populate_popup", + G_CALLBACK (chat_text_populate_popup_cb), + chat); + + /* create misspelt words identification tag */ + gtk_text_buffer_create_tag (buffer, + "misspelled", + "underline", PANGO_UNDERLINE_ERROR, + NULL); +} + +static void +chat_finalize (GObject *object) +{ + EmpathyChat *chat; + EmpathyChatPriv *priv; + + chat = EMPATHY_CHAT (object); + priv = GET_PRIV (chat); + + empathy_debug (DEBUG_DOMAIN, "Finalized: %p", object); + + g_slist_foreach (priv->sent_messages, (GFunc) g_free, NULL); + g_slist_free (priv->sent_messages); + + g_list_foreach (priv->compositors, (GFunc) g_object_unref, NULL); + g_list_free (priv->compositors); + + chat_composing_remove_timeout (chat); + g_object_unref (priv->log_manager); + + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc), "AccountStatusChanged", + G_CALLBACK (chat_status_changed_cb), + chat); + g_object_unref (priv->mc); + + + if (priv->tp_chat) { + g_object_unref (priv->tp_chat); + } + + if (priv->account) { + g_object_unref (priv->account); + } + + if (priv->scroll_idle_id) { + g_source_remove (priv->scroll_idle_id); + } + + if (priv->block_events_timeout_id) { + g_source_remove (priv->block_events_timeout_id); + } + + g_free (priv->id); + + G_OBJECT_CLASS (empathy_chat_parent_class)->finalize (object); +} + +static void +chat_destroy_cb (EmpathyTpChat *tp_chat, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + if (priv->tp_chat) { + g_object_unref (priv->tp_chat); + priv->tp_chat = NULL; + } + priv->sensitive = FALSE; + + empathy_chat_view_append_event (chat->view, _("Disconnected")); + gtk_widget_set_sensitive (chat->input_text_view, FALSE); + + if (priv->block_events_timeout_id != 0) { + g_source_remove (priv->block_events_timeout_id); + } + + if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) { + EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, NULL); + } +} + +static void +chat_send (EmpathyChat *chat, + const gchar *msg) +{ + EmpathyChatPriv *priv; + EmpathyMessage *message; + + priv = GET_PRIV (chat); + + if (G_STR_EMPTY (msg)) { + return; + } + + chat_sent_message_add (chat, msg); + + if (g_str_has_prefix (msg, "/clear")) { + empathy_chat_view_clear (chat->view); + return; + } + + /* FIXME: add here something to let group/privrate chat handle + * some special messages */ + + message = empathy_message_new (msg); + + empathy_tp_chat_send (priv->tp_chat, message); + + g_object_unref (message); +} + +static void +chat_input_text_view_send (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *msg; + + priv = GET_PRIV (chat); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + + gtk_text_buffer_get_bounds (buffer, &start, &end); + msg = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + + /* clear the input field */ + gtk_text_buffer_set_text (buffer, "", -1); + + chat_send (chat, msg); + + g_free (msg); + + priv->is_first_char = TRUE; +} + +static void +chat_message_received_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + EmpathyContact *sender; + + priv = GET_PRIV (chat); + + sender = empathy_message_get_sender (message); + empathy_debug (DEBUG_DOMAIN, "Appending message ('%s')", + empathy_contact_get_name (sender)); + + empathy_log_manager_add_message (priv->log_manager, + empathy_chat_get_id (chat), + empathy_chat_is_group_chat (chat), + message); + + empathy_chat_view_append_message (chat->view, message); + + if (empathy_chat_should_play_sound (chat)) { + // FIXME: empathy_sound_play (EMPATHY_SOUND_CHAT); + } + + /* We received a message so the contact is no more composing */ + chat_state_changed_cb (tp_chat, sender, + TP_CHANNEL_CHAT_STATE_ACTIVE, + chat); + + g_signal_emit (chat, signals[NEW_MESSAGE], 0, message, FALSE); +} + +static void +chat_send_error_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + TpChannelTextSendError error_code, + EmpathyChat *chat) +{ + const gchar *error; + gchar *str; + + switch (error_code) { + case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE: + error = _("offline"); + break; + case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT: + error = _("invalid contact"); + break; + case TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED: + error = _("permission denied"); + break; + case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG: + error = _("too long message"); + break; + case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED: + error = _("not implemented"); + break; + default: + error = _("unknown"); + break; + } + + str = g_strdup_printf (_("Error sending message '%s': %s"), + empathy_message_get_body (message), + error); + empathy_chat_view_append_event (chat->view, str); + g_free (str); +} + +static void +chat_sent_message_add (EmpathyChat *chat, + const gchar *str) +{ + EmpathyChatPriv *priv; + GSList *list; + GSList *item; + + priv = GET_PRIV (chat); + + /* Save the sent message in our repeat buffer */ + list = priv->sent_messages; + + /* Remove any other occurances of this msg */ + while ((item = g_slist_find_custom (list, str, (GCompareFunc) strcmp)) != NULL) { + list = g_slist_remove_link (list, item); + g_free (item->data); + g_slist_free1 (item); + } + + /* Trim the list to the last 10 items */ + while (g_slist_length (list) > 10) { + item = g_slist_last (list); + if (item) { + list = g_slist_remove_link (list, item); + g_free (item->data); + g_slist_free1 (item); + } + } + + /* Add new message */ + list = g_slist_prepend (list, g_strdup (str)); + + /* Set list and reset the index */ + priv->sent_messages = list; + priv->sent_messages_index = -1; +} + +static const gchar * +chat_sent_message_get_next (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + gint max; + + priv = GET_PRIV (chat); + + if (!priv->sent_messages) { + empathy_debug (DEBUG_DOMAIN, + "No sent messages, next message is NULL"); + return NULL; + } + + max = g_slist_length (priv->sent_messages) - 1; + + if (priv->sent_messages_index < max) { + priv->sent_messages_index++; + } + + empathy_debug (DEBUG_DOMAIN, + "Returning next message index:%d", + priv->sent_messages_index); + + return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); +} + +static const gchar * +chat_sent_message_get_last (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + if (!priv->sent_messages) { + empathy_debug (DEBUG_DOMAIN, + "No sent messages, last message is NULL"); + return NULL; + } + + if (priv->sent_messages_index >= 0) { + priv->sent_messages_index--; + } + + empathy_debug (DEBUG_DOMAIN, + "Returning last message index:%d", + priv->sent_messages_index); + + return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index); +} + +static gboolean +chat_input_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkAdjustment *adj; + gdouble val; + GtkWidget *text_view_sw; + + priv = GET_PRIV (chat); + + /* Catch ctrl+up/down so we can traverse messages we sent */ + if ((event->state & GDK_CONTROL_MASK) && + (event->keyval == GDK_Up || + event->keyval == GDK_Down)) { + GtkTextBuffer *buffer; + const gchar *str; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + + if (event->keyval == GDK_Up) { + str = chat_sent_message_get_next (chat); + } else { + str = chat_sent_message_get_last (chat); + } + + g_signal_handlers_block_by_func (buffer, + chat_input_text_buffer_changed_cb, + chat); + gtk_text_buffer_set_text (buffer, str ? str : "", -1); + g_signal_handlers_unblock_by_func (buffer, + chat_input_text_buffer_changed_cb, + chat); + + return TRUE; + } + + /* Catch enter but not ctrl/shift-enter */ + if (IS_ENTER (event->keyval) && + !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { + GtkTextView *view; + + /* This is to make sure that kinput2 gets the enter. And if + * it's handled there we shouldn't send on it. This is because + * kinput2 uses Enter to commit letters. See: + * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=104299 + */ + + view = GTK_TEXT_VIEW (chat->input_text_view); + if (gtk_im_context_filter_keypress (view->im_context, event)) { + GTK_TEXT_VIEW (chat->input_text_view)->need_im_reset = TRUE; + return TRUE; + } + + chat_input_text_view_send (chat); + return TRUE; + } + + text_view_sw = gtk_widget_get_parent (GTK_WIDGET (chat->view)); + + if (IS_ENTER (event->keyval) && + (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { + /* Newline for shift/control-enter. */ + return FALSE; + } + else if (!(event->state & GDK_CONTROL_MASK) && + event->keyval == GDK_Page_Up) { + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); + gtk_adjustment_set_value (adj, adj->value - adj->page_size); + + return TRUE; + } + else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK && + event->keyval == GDK_Page_Down) { + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw)); + val = MIN (adj->value + adj->page_size, adj->upper - adj->page_size); + gtk_adjustment_set_value (adj, val); + + return TRUE; + } + + if (EMPATHY_CHAT_GET_CLASS (chat)->key_press_event) { + return EMPATHY_CHAT_GET_CLASS (chat)->key_press_event (chat, event); + } + + return FALSE; +} + +static gboolean +chat_text_view_focus_in_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyChat *chat) +{ + gtk_widget_grab_focus (chat->input_text_view); + + return TRUE; +} + +static void +chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkTextIter start, end; + gchar *str; + gboolean spell_checker = FALSE; + + priv = GET_PRIV (chat); + + if (gtk_text_buffer_get_char_count (buffer) == 0) { + chat_composing_stop (chat); + } else { + chat_composing_start (chat); + } + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED, + &spell_checker); + + if (priv->is_first_char) { + GtkRequisition req; + gint window_height; + GtkWidget *dialog; + GtkAllocation *allocation; + + /* Save the window's size */ + dialog = empathy_chat_window_get_dialog (priv->window); + gtk_window_get_size (GTK_WINDOW (dialog), + NULL, &window_height); + + gtk_widget_size_request (chat->input_text_view, &req); + + allocation = >K_WIDGET (chat->view)->allocation; + + priv->default_window_height = window_height; + priv->last_input_height = req.height; + priv->padding_height = window_height - req.height - allocation->height; + + priv->is_first_char = FALSE; + } + + gtk_text_buffer_get_start_iter (buffer, &start); + + if (!spell_checker) { + gtk_text_buffer_get_end_iter (buffer, &end); + gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end); + return; + } + + if (!empathy_spell_supported ()) { + return; + } + + /* NOTE: this is really inefficient, we shouldn't have to + reiterate the whole buffer each time and check each work + every time. */ + while (TRUE) { + gboolean correct = FALSE; + + /* if at start */ + if (gtk_text_iter_is_start (&start)) { + end = start; + + if (!gtk_text_iter_forward_word_end (&end)) { + /* no whole word yet */ + break; + } + } else { + if (!gtk_text_iter_forward_word_end (&end)) { + /* must be the end of the buffer */ + break; + } + + start = end; + gtk_text_iter_backward_word_start (&start); + } + + str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + + /* spell check string */ + if (!empathy_chat_get_is_command (str)) { + correct = empathy_spell_check (str); + } else { + correct = TRUE; + } + + if (!correct) { + gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end); + } else { + gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end); + } + + g_free (str); + + /* set start iter to the end iters position */ + start = end; + } +} + +typedef struct { + GtkWidget *window; + gint width; + gint height; +} ChangeSizeData; + +static gboolean +chat_change_size_in_idle_cb (ChangeSizeData *data) +{ + gtk_window_resize (GTK_WINDOW (data->window), + data->width, data->height); + + return FALSE; +} + +static void +chat_text_view_scroll_hide_cb (GtkWidget *widget, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkWidget *sw; + + priv = GET_PRIV (chat); + + priv->vscroll_visible = FALSE; + g_signal_handlers_disconnect_by_func (widget, chat_text_view_scroll_hide_cb, chat); + + sw = gtk_widget_get_parent (chat->input_text_view); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, + GTK_POLICY_NEVER); + g_object_set (sw, "height-request", -1, NULL); +} + +static void +chat_text_view_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + gint width; + GtkWidget *dialog; + ChangeSizeData *data; + gint window_height; + gint new_height; + GtkAllocation *view_allocation; + gint current_height; + gint diff; + GtkWidget *sw; + + priv = GET_PRIV (chat); + + if (priv->default_window_height <= 0) { + return; + } + + sw = gtk_widget_get_parent (widget); + if (sw->allocation.height >= MAX_INPUT_HEIGHT && !priv->vscroll_visible) { + GtkWidget *vscroll; + + priv->vscroll_visible = TRUE; + gtk_widget_set_size_request (sw, sw->allocation.width, MAX_INPUT_HEIGHT); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + vscroll = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw)); + g_signal_connect (vscroll, "hide", + G_CALLBACK (chat_text_view_scroll_hide_cb), + chat); + } + + if (priv->last_input_height <= allocation->height) { + priv->last_input_height = allocation->height; + return; + } + + diff = priv->last_input_height - allocation->height; + priv->last_input_height = allocation->height; + + view_allocation = >K_WIDGET (chat->view)->allocation; + + dialog = empathy_chat_window_get_dialog (priv->window); + gtk_window_get_size (GTK_WINDOW (dialog), NULL, ¤t_height); + + new_height = view_allocation->height + priv->padding_height + allocation->height - diff; + + if (new_height <= priv->default_window_height) { + window_height = priv->default_window_height; + } else { + window_height = new_height; + } + + if (current_height <= window_height) { + return; + } + + /* Restore the window's size */ + gtk_window_get_size (GTK_WINDOW (dialog), &width, NULL); + + data = g_new0 (ChangeSizeData, 1); + data->window = dialog; + data->width = width; + data->height = window_height; + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + (GSourceFunc) chat_change_size_in_idle_cb, + data, g_free); +} + +static void +chat_text_view_realize_cb (GtkWidget *widget, + EmpathyChat *chat) +{ + empathy_debug (DEBUG_DOMAIN, "Setting focus to the input text view"); + gtk_widget_grab_focus (widget); +} + +static void +chat_insert_smiley_activate_cb (GtkWidget *menuitem, + EmpathyChat *chat) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + const gchar *smiley; + + smiley = g_object_get_data (G_OBJECT (menuitem), "smiley_text"); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert (buffer, &iter, smiley, -1); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert (buffer, &iter, " ", -1); +} + +static void +chat_text_populate_popup_cb (GtkTextView *view, + GtkMenu *menu, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GtkTextBuffer *buffer; + GtkTextTagTable *table; + GtkTextTag *tag; + gint x, y; + GtkTextIter iter, start, end; + GtkWidget *item; + gchar *str = NULL; + EmpathyChatSpell *chat_spell; + GtkWidget *smiley_menu; + + priv = GET_PRIV (chat); + + /* Add the emoticon menu. */ + item = gtk_separator_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_menu_item_new_with_mnemonic (_("Insert Smiley")); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + smiley_menu = empathy_chat_view_get_smiley_menu ( + G_CALLBACK (chat_insert_smiley_activate_cb), + chat); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smiley_menu); + + /* Add the spell check menu item. */ + buffer = gtk_text_view_get_buffer (view); + table = gtk_text_buffer_get_tag_table (buffer); + + tag = gtk_text_tag_table_lookup (table, "misspelled"); + + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), + GTK_TEXT_WINDOW_WIDGET, + x, y, + &x, &y); + + gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y); + + start = end = iter; + + if (gtk_text_iter_backward_to_tag_toggle (&start, tag) && + gtk_text_iter_forward_to_tag_toggle (&end, tag)) { + + str = gtk_text_buffer_get_text (buffer, + &start, &end, FALSE); + } + + if (G_STR_EMPTY (str)) { + return; + } + + chat_spell = chat_spell_new (chat, str, start, end); + + g_object_set_data_full (G_OBJECT (menu), + "chat_spell", chat_spell, + (GDestroyNotify) chat_spell_free); + + item = gtk_separator_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_menu_item_new_with_mnemonic (_("_Check Word Spelling...")); + g_signal_connect (item, + "activate", + G_CALLBACK (chat_text_check_word_spelling_cb), + chat_spell); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); +} + +static void +chat_text_check_word_spelling_cb (GtkMenuItem *menuitem, + EmpathyChatSpell *chat_spell) +{ + empathy_spell_dialog_show (chat_spell->chat, + chat_spell->start, + chat_spell->end, + chat_spell->word); +} + +static EmpathyChatSpell * +chat_spell_new (EmpathyChat *chat, + const gchar *word, + GtkTextIter start, + GtkTextIter end) +{ + EmpathyChatSpell *chat_spell; + + chat_spell = g_new0 (EmpathyChatSpell, 1); + + chat_spell->chat = g_object_ref (chat); + chat_spell->word = g_strdup (word); + chat_spell->start = start; + chat_spell->end = end; + + return chat_spell; +} + +static void +chat_spell_free (EmpathyChatSpell *chat_spell) +{ + g_object_unref (chat_spell->chat); + g_free (chat_spell->word); + g_free (chat_spell); +} + +static void +chat_composing_start (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + if (priv->composing_stop_timeout_id) { + /* Just restart the timeout */ + chat_composing_remove_timeout (chat); + } else { + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_COMPOSING); + } + + priv->composing_stop_timeout_id = g_timeout_add_seconds ( + COMPOSING_STOP_TIMEOUT, + (GSourceFunc) chat_composing_stop_timeout_cb, + chat); +} + +static void +chat_composing_stop (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + chat_composing_remove_timeout (chat); + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_ACTIVE); +} + +static void +chat_composing_remove_timeout (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + if (priv->composing_stop_timeout_id) { + g_source_remove (priv->composing_stop_timeout_id); + priv->composing_stop_timeout_id = 0; + } +} + +static gboolean +chat_composing_stop_timeout_cb (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + priv->composing_stop_timeout_id = 0; + empathy_tp_chat_set_state (priv->tp_chat, + TP_CHANNEL_CHAT_STATE_PAUSED); + + return FALSE; +} + +static void +chat_state_changed_cb (EmpathyTpChat *tp_chat, + EmpathyContact *contact, + TpChannelChatState state, + EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GList *l; + gboolean was_composing; + + priv = GET_PRIV (chat); + + if (empathy_contact_is_user (contact)) { + /* We don't care about our own chat state */ + return; + } + + was_composing = (priv->compositors != NULL); + + /* Find the contact in the list. After that l is the list elem or NULL */ + for (l = priv->compositors; l; l = l->next) { + if (empathy_contact_equal (contact, l->data)) { + break; + } + } + + switch (state) { + case TP_CHANNEL_CHAT_STATE_GONE: + case TP_CHANNEL_CHAT_STATE_INACTIVE: + case TP_CHANNEL_CHAT_STATE_PAUSED: + case TP_CHANNEL_CHAT_STATE_ACTIVE: + /* Contact is not composing */ + if (l) { + priv->compositors = g_list_remove_link (priv->compositors, l); + g_object_unref (l->data); + g_list_free1 (l); + } + break; + case TP_CHANNEL_CHAT_STATE_COMPOSING: + /* Contact is composing */ + if (!l) { + priv->compositors = g_list_prepend (priv->compositors, + g_object_ref (contact)); + } + break; + default: + g_assert_not_reached (); + } + + empathy_debug (DEBUG_DOMAIN, "Was composing: %s now composing: %s", + was_composing ? "yes" : "no", + priv->compositors ? "yes" : "no"); + + if ((was_composing && !priv->compositors) || + (!was_composing && priv->compositors)) { + /* Composing state changed */ + g_signal_emit (chat, signals[COMPOSING], 0, + priv->compositors != NULL); + } +} + +static void +chat_add_logs (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + GList *messages, *l; + guint num_messages; + guint i; + + priv = GET_PRIV (chat); + + /* Turn off scrolling temporarily */ + empathy_chat_view_scroll (chat->view, FALSE); + + /* Add messages from last conversation */ + messages = empathy_log_manager_get_last_messages (priv->log_manager, + priv->account, + empathy_chat_get_id (chat), + empathy_chat_is_group_chat (chat)); + num_messages = g_list_length (messages); + + for (l = messages, i = 0; l; l = l->next, i++) { + EmpathyMessage *message; + + message = l->data; + + /* Only add 10 last messages */ + if (num_messages - i > 10) { + g_object_unref (message); + continue; + } + + empathy_chat_view_append_message (chat->view, message); + + g_object_unref (message); + } + g_list_free (messages); + + /* Turn back on scrolling */ + empathy_chat_view_scroll (chat->view, TRUE); + + /* Scroll to the most recent messages, we reference the chat + * for the duration of the scroll func. + */ + priv->scroll_idle_id = g_idle_add ((GSourceFunc) chat_scroll_down_idle_func, + g_object_ref (chat)); +} + +/* Scroll down after the back-log has been received. */ +static gboolean +chat_scroll_down_idle_func (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + empathy_chat_scroll_down (chat); + g_object_unref (chat); + + priv->scroll_idle_id = 0; + + return FALSE; +} + +gboolean +empathy_chat_get_is_command (const gchar *str) +{ + g_return_val_if_fail (str != NULL, FALSE); + + if (str[0] != '/') { + return FALSE; + } + + if (g_str_has_prefix (str, "/me")) { + return TRUE; + } + else if (g_str_has_prefix (str, "/nick")) { + return TRUE; + } + else if (g_str_has_prefix (str, "/topic")) { + return TRUE; + } + + return FALSE; +} + +void +empathy_chat_correct_word (EmpathyChat *chat, + GtkTextIter start, + GtkTextIter end, + const gchar *new_word) +{ + GtkTextBuffer *buffer; + + g_return_if_fail (chat != NULL); + g_return_if_fail (new_word != NULL); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + + gtk_text_buffer_delete (buffer, &start, &end); + gtk_text_buffer_insert (buffer, &start, + new_word, + -1); +} + +const gchar * +empathy_chat_get_name (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + if (EMPATHY_CHAT_GET_CLASS (chat)->get_name) { + return EMPATHY_CHAT_GET_CLASS (chat)->get_name (chat); + } + + return NULL; +} + +gchar * +empathy_chat_get_tooltip (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + if (EMPATHY_CHAT_GET_CLASS (chat)->get_tooltip) { + return EMPATHY_CHAT_GET_CLASS (chat)->get_tooltip (chat); + } + + return NULL; +} + +const gchar * +empathy_chat_get_status_icon_name (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + if (EMPATHY_CHAT_GET_CLASS (chat)->get_status_icon_name) { + return EMPATHY_CHAT_GET_CLASS (chat)->get_status_icon_name (chat); + } + + return NULL; +} + +GtkWidget * +empathy_chat_get_widget (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL); + + if (EMPATHY_CHAT_GET_CLASS (chat)->get_widget) { + return EMPATHY_CHAT_GET_CLASS (chat)->get_widget (chat); + } + + return NULL; +} + +gboolean +empathy_chat_is_group_chat (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE); + + if (EMPATHY_CHAT_GET_CLASS (chat)->is_group_chat) { + return EMPATHY_CHAT_GET_CLASS (chat)->is_group_chat (chat); + } + + return FALSE; +} + +gboolean +empathy_chat_is_connected (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE); + + priv = GET_PRIV (chat); + + return (priv->tp_chat != NULL); +} + +static const gchar * +chat_get_window_id_for_geometry (EmpathyChat *chat) +{ + gboolean separate_windows; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS, + &separate_windows); + + if (separate_windows) { + return empathy_chat_get_id (chat); + } else { + return "chat-window"; + } +} + +void +empathy_chat_save_geometry (EmpathyChat *chat, + gint x, + gint y, + gint w, + gint h) +{ + empathy_geometry_save (chat_get_window_id_for_geometry (chat), x, y, w, h); +} + +void +empathy_chat_load_geometry (EmpathyChat *chat, + gint *x, + gint *y, + gint *w, + gint *h) +{ + empathy_geometry_load (chat_get_window_id_for_geometry (chat), x, y, w, h); +} + +static gboolean +chat_block_events_timeout_cb (gpointer data) +{ + EmpathyChat *chat = EMPATHY_CHAT (data); + EmpathyChatPriv *priv = GET_PRIV (chat); + + chat->block_events = FALSE; + priv->block_events_timeout_id = 0; + + return FALSE; +} + +void +empathy_chat_set_tp_chat (EmpathyChat *chat, + EmpathyTpChat *tp_chat) +{ + EmpathyChatPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat)); + + priv = GET_PRIV (chat); + + if (tp_chat == priv->tp_chat) { + return; + } + + /* Block events for some time to avoid having "has come online" or + * "joined" messages. */ + chat->block_events = TRUE; + if (priv->block_events_timeout_id != 0) { + g_source_remove (priv->block_events_timeout_id); + } + priv->block_events_timeout_id = + g_timeout_add_seconds (1, chat_block_events_timeout_cb, chat); + + if (priv->tp_chat) { + g_signal_handlers_disconnect_by_func (priv->tp_chat, + chat_message_received_cb, + chat); + g_signal_handlers_disconnect_by_func (priv->tp_chat, + chat_send_error_cb, + chat); + g_signal_handlers_disconnect_by_func (priv->tp_chat, + chat_destroy_cb, + chat); + g_object_unref (priv->tp_chat); + } + if (priv->account) { + g_object_unref (priv->account); + } + + g_free (priv->id); + priv->tp_chat = g_object_ref (tp_chat); + priv->id = g_strdup (empathy_tp_chat_get_id (tp_chat)); + priv->account = g_object_ref (empathy_tp_chat_get_account (tp_chat)); + empathy_tp_chat_set_acknowledge (tp_chat, TRUE); + + if (priv->first_tp_chat) { + chat_add_logs (chat); + priv->first_tp_chat = FALSE; + } + + g_signal_connect (tp_chat, "message-received", + G_CALLBACK (chat_message_received_cb), + chat); + g_signal_connect (tp_chat, "send-error", + G_CALLBACK (chat_send_error_cb), + chat); + g_signal_connect (tp_chat, "chat-state-changed", + G_CALLBACK (chat_state_changed_cb), + chat); + g_signal_connect (tp_chat, "destroy", + G_CALLBACK (chat_destroy_cb), + chat); + + if (!priv->sensitive) { + gtk_widget_set_sensitive (chat->input_text_view, TRUE); + empathy_chat_view_append_event (chat->view, _("Connected")); + priv->sensitive = TRUE; + } + + if (EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat) { + EMPATHY_CHAT_GET_CLASS (chat)->set_tp_chat (chat, tp_chat); + } + + g_object_notify (G_OBJECT (chat), "tp-chat"); +} + +const gchar * +empathy_chat_get_id (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + return priv->id; +} + +McAccount * +empathy_chat_get_account (EmpathyChat *chat) +{ + EmpathyChatPriv *priv = GET_PRIV (chat); + + return priv->account; +} + +void +empathy_chat_clear (EmpathyChat *chat) +{ + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + empathy_chat_view_clear (chat->view); +} + +void +empathy_chat_set_window (EmpathyChat *chat, + EmpathyChatWindow *window) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + priv->window = window; +} + +EmpathyChatWindow * +empathy_chat_get_window (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + priv = GET_PRIV (chat); + + return priv->window; +} + +void +empathy_chat_scroll_down (EmpathyChat *chat) +{ + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + empathy_chat_view_scroll_down (chat->view); +} + +void +empathy_chat_cut (EmpathyChat *chat) +{ + GtkTextBuffer *buffer; + + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) { + GtkClipboard *clipboard; + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_cut_clipboard (buffer, clipboard, TRUE); + } +} + +void +empathy_chat_copy (EmpathyChat *chat) +{ + GtkTextBuffer *buffer; + + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + if (empathy_chat_view_get_selection_bounds (chat->view, NULL, NULL)) { + empathy_chat_view_copy_clipboard (chat->view); + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) { + GtkClipboard *clipboard; + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_copy_clipboard (buffer, clipboard); + } +} + +void +empathy_chat_paste (EmpathyChat *chat) +{ + GtkTextBuffer *buffer; + GtkClipboard *clipboard; + + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view)); + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, TRUE); +} + +void +empathy_chat_present (EmpathyChat *chat) +{ + EmpathyChatPriv *priv; + + g_return_if_fail (EMPATHY_IS_CHAT (chat)); + + priv = GET_PRIV (chat); + + if (priv->window == NULL) { + EmpathyChatWindow *window; + + window = empathy_chat_window_get_default (); + if (!window) { + window = empathy_chat_window_new (); + } + + empathy_chat_window_add_chat (window, chat); + } + + empathy_chat_window_switch_to_chat (priv->window, chat); + empathy_window_present ( + GTK_WINDOW (empathy_chat_window_get_dialog (priv->window)), + TRUE); + + gtk_widget_grab_focus (chat->input_text_view); +} + +gboolean +empathy_chat_should_play_sound (EmpathyChat *chat) +{ + EmpathyChatWindow *window; + gboolean play = TRUE; + + g_return_val_if_fail (EMPATHY_IS_CHAT (chat), FALSE); + + window = empathy_chat_get_window (chat); + if (!window) { + return TRUE; + } + + play = !empathy_chat_window_has_focus (window); + + return play; +} + +gboolean +empathy_chat_should_highlight_nick (EmpathyMessage *message) +{ + EmpathyContact *contact; + const gchar *msg, *to; + gchar *cf_msg, *cf_to; + gchar *ch; + gboolean ret_val; + + g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE); + + empathy_debug (DEBUG_DOMAIN, "Highlighting nickname"); + + ret_val = FALSE; + + msg = empathy_message_get_body (message); + if (!msg) { + return FALSE; + } + + contact = empathy_message_get_receiver (message); + if (!contact || !empathy_contact_is_user (contact)) { + return FALSE; + } + + to = empathy_contact_get_name (contact); + if (!to) { + return FALSE; + } + + cf_msg = g_utf8_casefold (msg, -1); + cf_to = g_utf8_casefold (to, -1); + + ch = strstr (cf_msg, cf_to); + if (ch == NULL) { + goto finished; + } + + if (ch != cf_msg) { + /* Not first in the message */ + if ((*(ch - 1) != ' ') && + (*(ch - 1) != ',') && + (*(ch - 1) != '.')) { + goto finished; + } + } + + ch = ch + strlen (cf_to); + if (ch >= cf_msg + strlen (cf_msg)) { + ret_val = TRUE; + goto finished; + } + + if ((*ch == ' ') || + (*ch == ',') || + (*ch == '.') || + (*ch == ':')) { + ret_val = TRUE; + goto finished; + } + +finished: + g_free (cf_msg); + g_free (cf_to); + + return ret_val; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-chat.glade b/gnome-2-22/libempathy-gtk/empathy-chat.glade new file mode 100644 index 000000000..ca6dd56d0 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat.glade @@ -0,0 +1,756 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="chat_page_window"> + <property name="title" translatable="yes">Chat</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">350</property> + <property name="default_height">250</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="chat_widget"> + <property name="border_width">4</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">3</property> + + <child> + <widget class="GtkScrolledWindow" id="chat_view_sw"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="input_text_view_sw"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkWindow" id="chat_window"> + <property name="title" translatable="yes">Chat</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">350</property> + <property name="default_height">250</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="chat_vbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkMenuBar" id="chats_menubar"> + <property name="visible">True</property> + <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property> + <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property> + + <child> + <widget class="GtkMenuItem" id="menu_conv"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Conversation</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="menu_conv_menu"> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_clear"> + <property name="visible">True</property> + <property name="label" translatable="yes">C_lear</property> + <property name="use_underline">True</property> + <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image262"> + <property name="visible">True</property> + <property name="stock">gtk-clear</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_conv_insert_smiley"> + <property name="visible">True</property> + <property name="label" translatable="yes">Insert _Smiley</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator13"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_call"> + <property name="visible">True</property> + <property name="label" translatable="yes">Ca_ll</property> + <property name="use_underline">True</property> + + <child internal-child="image"> + <widget class="GtkImage" id="menu_conv_call_image"> + <property name="visible">True</property> + <property name="icon_name">gnome-stock-mic</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="menu_conv_call_separator"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_log"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View Previous Conversations</property> + <property name="use_underline">True</property> + <accelerator key="F3" modifiers="0" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image263"> + <property name="visible">True</property> + <property name="icon_name">document-open-recent</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="menu_conv_separator"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_add_contact"> + <property name="label" translatable="yes">_Add Contact...</property> + <property name="use_underline">True</property> + + <child internal-child="image"> + <widget class="GtkImage" id="image264"> + <property name="visible">True</property> + <property name="stock">gtk-add</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_info"> + <property name="visible">True</property> + <property name="label" translatable="yes">Contact Infor_mation</property> + <property name="use_underline">True</property> + + <child internal-child="image"> + <widget class="GtkImage" id="image265"> + <property name="visible">True</property> + <property name="stock">gtk-info</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator7"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_conv_close"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Close</property> + <property name="use_underline">True</property> + <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image266"> + <property name="visible">True</property> + <property name="stock">gtk-close</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_room"> + <property name="label" translatable="yes">_Room</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="menu_room_menu"> + + <child> + <widget class="GtkMenuItem" id="menu_room_set_topic"> + <property name="visible">True</property> + <property name="label" translatable="yes">Change _Topic...</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator12"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_room_join_new"> + <property name="visible">True</property> + <property name="label" translatable="yes">Join _New...</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_room_invite"> + <property name="visible">True</property> + <property name="label" translatable="yes">In_vite...</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator7"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_room_add"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Add To Favorites</property> + <property name="use_underline">True</property> + + <child internal-child="image"> + <widget class="GtkImage" id="image268"> + <property name="visible">True</property> + <property name="stock">gtk-add</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator10"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="menu_room_show_contacts"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Show Contacts</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <accelerator key="F11" modifiers="0" signal="activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_edit"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="menu_edit_menu"> + + <child> + <widget class="GtkImageMenuItem" id="menu_edit_cut"> + <property name="visible">True</property> + <property name="label" translatable="yes">Cu_t</property> + <property name="use_underline">True</property> + <accelerator key="X" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image269"> + <property name="visible">True</property> + <property name="stock">gtk-cut</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_edit_copy"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Copy</property> + <property name="use_underline">True</property> + <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image270"> + <property name="visible">True</property> + <property name="stock">gtk-copy</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_edit_paste"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Paste</property> + <property name="use_underline">True</property> + <accelerator key="V" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image271"> + <property name="visible">True</property> + <property name="stock">gtk-paste</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_tabs"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Tabs</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="menu_tabs_menu"> + + <child> + <widget class="GtkMenuItem" id="menu_tabs_prev"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Previous Tab</property> + <property name="use_underline">True</property> + <accelerator key="Page_Up" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_tabs_next"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Next Tab</property> + <property name="use_underline">True</property> + <accelerator key="Page_Down" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator4"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_tabs_left"> + <property name="visible">True</property> + <property name="label" translatable="yes">Move Tab _Left</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_tabs_right"> + <property name="visible">True</property> + <property name="label" translatable="yes">Move Tab _Right</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_tabs_detach"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Detach Tab</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="menu_help"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="menu_help_menu"> + + <child> + <widget class="GtkImageMenuItem" id="menu_help_contents"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Contents</property> + <property name="use_underline">True</property> + <accelerator key="F1" modifiers="0" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image289"> + <property name="visible">True</property> + <property name="stock">gtk-help</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="menu_help_about"> + <property name="visible">True</property> + <property name="label">gtk-about</property> + <property name="use_stock">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="chat_invite_dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Invite</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">True</property> + <property name="default_width">275</property> + <property name="default_height">225</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="vbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="hbuttonbox1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_invite"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">In_vite</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox7"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkVBox" id="vbox7"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Select who would you like to invite:</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox8"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Invitation _message:</property> + <property name="use_underline">True</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">You have been invited to join a chat conference.</property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + <property name="width_chars">40</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-chat.h b/gnome-2-22/libempathy-gtk/empathy-chat.h new file mode 100644 index 000000000..bcf382e53 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chat.h @@ -0,0 +1,123 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CHAT_H__ +#define __EMPATHY_CHAT_H__ + +#include <glib-object.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-message.h> +#include <libempathy/empathy-tp-chat.h> + +#include "empathy-chat-view.h" +#include "empathy-spell.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CHAT (empathy_chat_get_type ()) +#define EMPATHY_CHAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CHAT, EmpathyChat)) +#define EMPATHY_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_CHAT, EmpathyChatClass)) +#define EMPATHY_IS_CHAT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CHAT)) +#define EMPATHY_IS_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CHAT)) +#define EMPATHY_CHAT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CHAT, EmpathyChatClass)) + +typedef struct _EmpathyChat EmpathyChat; +typedef struct _EmpathyChatClass EmpathyChatClass; +typedef struct _EmpathyChatPriv EmpathyChatPriv; + +#include "empathy-chat-window.h" + +struct _EmpathyChat { + GObject parent; + + /* Protected */ + EmpathyChatView *view; + GtkWidget *input_text_view; + gboolean block_events; +}; + +struct _EmpathyChatClass { + GObjectClass parent; + + /* VTable */ + const gchar * (*get_name) (EmpathyChat *chat); + gchar * (*get_tooltip) (EmpathyChat *chat); + const gchar * (*get_status_icon_name)(EmpathyChat *chat); + GtkWidget * (*get_widget) (EmpathyChat *chat); + gboolean (*is_group_chat) (EmpathyChat *chat); + void (*set_tp_chat) (EmpathyChat *chat, + EmpathyTpChat *tp_chat); + gboolean (*key_press_event) (EmpathyChat *chat, + GdkEventKey *event); +}; + +GType empathy_chat_get_type (void); + +EmpathyChatView * empathy_chat_get_view (EmpathyChat *chat); +EmpathyChatWindow *empathy_chat_get_window (EmpathyChat *chat); +void empathy_chat_set_window (EmpathyChat *chat, + EmpathyChatWindow *window); +void empathy_chat_present (EmpathyChat *chat); +void empathy_chat_clear (EmpathyChat *chat); +void empathy_chat_scroll_down (EmpathyChat *chat); +void empathy_chat_cut (EmpathyChat *chat); +void empathy_chat_copy (EmpathyChat *chat); +void empathy_chat_paste (EmpathyChat *chat); +const gchar * empathy_chat_get_name (EmpathyChat *chat); +gchar * empathy_chat_get_tooltip (EmpathyChat *chat); +const gchar * empathy_chat_get_status_icon_name (EmpathyChat *chat); +GtkWidget * empathy_chat_get_widget (EmpathyChat *chat); +gboolean empathy_chat_is_group_chat (EmpathyChat *chat); +gboolean empathy_chat_is_connected (EmpathyChat *chat); +void empathy_chat_save_geometry (EmpathyChat *chat, + gint x, + gint y, + gint w, + gint h); +void empathy_chat_load_geometry (EmpathyChat *chat, + gint *x, + gint *y, + gint *w, + gint *h); +void empathy_chat_set_tp_chat (EmpathyChat *chat, + EmpathyTpChat *tp_chat); +const gchar * empathy_chat_get_id (EmpathyChat *chat); +McAccount * empathy_chat_get_account (EmpathyChat *chat); + +/* For spell checker dialog to correct the misspelled word. */ +gboolean empathy_chat_get_is_command (const gchar *str); +void empathy_chat_correct_word (EmpathyChat *chat, + GtkTextIter start, + GtkTextIter end, + const gchar *new_word); +gboolean empathy_chat_should_play_sound (EmpathyChat *chat); +gboolean empathy_chat_should_highlight_nick (EmpathyMessage *message); + +G_END_DECLS + +#endif /* __EMPATHY_CHAT_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.c b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.c new file mode 100644 index 000000000..83d305212 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.c @@ -0,0 +1,582 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Martyn Russell <martyn@imendio.com> + * Mikael Hallendal <micke@imendio.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libempathy/empathy-chatroom-manager.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-account-chooser.h" +#include "empathy-chatrooms-window.h" +#include "empathy-new-chatroom-dialog.h" +#include "empathy-ui-utils.h" + +typedef struct { + EmpathyChatroomManager *manager; + + GtkWidget *window; + GtkWidget *hbox_account; + GtkWidget *label_account; + GtkWidget *account_chooser; + GtkWidget *treeview; + GtkWidget *button_remove; + GtkWidget *button_edit; + GtkWidget *button_close; + + gint room_column; +} EmpathyChatroomsWindow; + +static void chatrooms_window_destroy_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window); +static void chatrooms_window_model_setup (EmpathyChatroomsWindow *window); +static void chatrooms_window_model_add_columns (EmpathyChatroomsWindow *window); +static void chatrooms_window_model_refresh_data (EmpathyChatroomsWindow *window, + gboolean first_time); +static void chatrooms_window_model_add (EmpathyChatroomsWindow *window, + EmpathyChatroom *chatroom, + gboolean set_active); +static void chatrooms_window_model_cell_auto_connect_toggled (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyChatroomsWindow *window); +static EmpathyChatroom * chatrooms_window_model_get_selected (EmpathyChatroomsWindow *window); +static void chatrooms_window_model_action_selected (EmpathyChatroomsWindow *window); +static void chatrooms_window_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathyChatroomsWindow *window); +static void chatrooms_window_button_remove_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window); +static void chatrooms_window_button_edit_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window); +static void chatrooms_window_button_close_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window); +static void chatrooms_window_chatroom_added_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyChatroomsWindow *window); +static void chatrooms_window_chatroom_removed_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyChatroomsWindow *window); +static gboolean chatrooms_window_remove_chatroom_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyChatroom *chatroom); +static void chatrooms_window_account_changed_cb (GtkWidget *combo_box, + EmpathyChatroomsWindow *window); + +enum { + COL_IMAGE, + COL_NAME, + COL_ROOM, + COL_AUTO_CONNECT, + COL_POINTER, + COL_COUNT +}; + +void +empathy_chatrooms_window_show (GtkWindow *parent) +{ + static EmpathyChatroomsWindow *window = NULL; + GladeXML *glade; + + if (window) { + gtk_window_present (GTK_WINDOW (window->window)); + return; + } + + window = g_new0 (EmpathyChatroomsWindow, 1); + + glade = empathy_glade_get_file ("empathy-chatrooms-window.glade", + "chatrooms_window", + NULL, + "chatrooms_window", &window->window, + "hbox_account", &window->hbox_account, + "label_account", &window->label_account, + "treeview", &window->treeview, + "button_edit", &window->button_edit, + "button_remove", &window->button_remove, + "button_close", &window->button_close, + NULL); + + empathy_glade_connect (glade, + window, + "chatrooms_window", "destroy", chatrooms_window_destroy_cb, + "button_remove", "clicked", chatrooms_window_button_remove_clicked_cb, + "button_edit", "clicked", chatrooms_window_button_edit_clicked_cb, + "button_close", "clicked", chatrooms_window_button_close_clicked_cb, + NULL); + + g_object_unref (glade); + + g_object_add_weak_pointer (G_OBJECT (window->window), (gpointer) &window); + + /* Get the session and chat room manager */ + window->manager = empathy_chatroom_manager_new (); + + g_signal_connect (window->manager, "chatroom-added", + G_CALLBACK (chatrooms_window_chatroom_added_cb), + window); + g_signal_connect (window->manager, "chatroom-removed", + G_CALLBACK (chatrooms_window_chatroom_removed_cb), + window); + + /* Account chooser for chat rooms */ + window->account_chooser = empathy_account_chooser_new (); + empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser), + empathy_account_chooser_filter_is_connected, + NULL); + empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser), NULL); + g_object_set (window->account_chooser, + "has-all-option", TRUE, + NULL); + + gtk_box_pack_start (GTK_BOX (window->hbox_account), + window->account_chooser, + TRUE, TRUE, 0); + + g_signal_connect (window->account_chooser, "changed", + G_CALLBACK (chatrooms_window_account_changed_cb), + window); + + gtk_widget_show (window->account_chooser); + + /* Set up chatrooms */ + chatrooms_window_model_setup (window); + + /* Set focus */ + gtk_widget_grab_focus (window->treeview); + + /* Last touches */ + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (window->window), + GTK_WINDOW (parent)); + } + + gtk_widget_show (window->window); +} + +static void +chatrooms_window_destroy_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window) +{ + g_signal_handlers_disconnect_by_func (window->manager, + chatrooms_window_chatroom_added_cb, + window); + g_signal_handlers_disconnect_by_func (window->manager, + chatrooms_window_chatroom_removed_cb, + window); + g_object_unref (window->manager); + g_free (window); +} + +static void +chatrooms_window_model_setup (EmpathyChatroomsWindow *window) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + + /* View */ + view = GTK_TREE_VIEW (window->treeview); + + g_signal_connect (view, "row-activated", + G_CALLBACK (chatrooms_window_row_activated_cb), + window); + + /* Store */ + store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* Image */ + G_TYPE_STRING, /* Name */ + G_TYPE_STRING, /* Room */ + G_TYPE_BOOLEAN, /* Auto start */ + EMPATHY_TYPE_CHATROOM); /* Chatroom */ + + gtk_tree_view_set_model (view, GTK_TREE_MODEL (store)); + + /* Selection */ + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + /* Columns */ + chatrooms_window_model_add_columns (window); + + /* Add data */ + chatrooms_window_model_refresh_data (window, TRUE); + + /* Clean up */ + g_object_unref (store); +} + +static void +chatrooms_window_model_add_columns (EmpathyChatroomsWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + gint count; + + view = GTK_TREE_VIEW (window->treeview); + model = gtk_tree_view_get_model (view); + + gtk_tree_view_set_headers_visible (view, TRUE); + gtk_tree_view_set_headers_clickable (view, TRUE); + + /* Name & Status */ + column = gtk_tree_view_column_new (); + count = gtk_tree_view_append_column (view, column); + + gtk_tree_view_column_set_title (column, _("Name")); + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, count - 1); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, "icon-name", COL_IMAGE); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, + "xpad", 4, + "ypad", 1, + NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME); + + /* Room */ + cell = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Room"), cell, + "text", COL_ROOM, + NULL); + count = gtk_tree_view_append_column (view, column); + gtk_tree_view_column_set_sort_column_id (column, count - 1); + window->room_column = count - 1; + + /* Chatroom auto connect */ + cell = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes (_("Auto Connect"), cell, + "active", COL_AUTO_CONNECT, + NULL); + count = gtk_tree_view_append_column (view, column); + gtk_tree_view_column_set_sort_column_id (column, count - 1); + + g_signal_connect (cell, "toggled", + G_CALLBACK (chatrooms_window_model_cell_auto_connect_toggled), + window); + + /* Sort model */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 0, + GTK_SORT_ASCENDING); +} + +static void +chatrooms_window_model_refresh_data (EmpathyChatroomsWindow *window, + gboolean first_time) +{ + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + GtkTreeViewColumn *column; + EmpathyAccountChooser *account_chooser; + McAccount *account; + GList *chatrooms, *l; + + view = GTK_TREE_VIEW (window->treeview); + selection = gtk_tree_view_get_selection (view); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + /* Look up chatrooms */ + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + + chatrooms = empathy_chatroom_manager_get_chatrooms (window->manager, account); + + /* Sort out columns, we only show the server column for + * selected protocol types, such as Jabber. + */ + if (account) { + column = gtk_tree_view_get_column (view, window->room_column); + gtk_tree_view_column_set_visible (column, TRUE); + } else { + column = gtk_tree_view_get_column (view, window->room_column); + gtk_tree_view_column_set_visible (column, FALSE); + } + + /* Clean out the store */ + gtk_list_store_clear (store); + + /* Populate with chatroom list. */ + for (l = chatrooms; l; l = l->next) { + chatrooms_window_model_add (window, l->data, FALSE); + } + + if (gtk_tree_model_get_iter_first (model, &iter)) { + gtk_tree_selection_select_iter (selection, &iter); + } + + if (account) { + g_object_unref (account); + } + + g_list_free (chatrooms); +} + +static void +chatrooms_window_model_add (EmpathyChatroomsWindow *window, + EmpathyChatroom *chatroom, + gboolean set_active) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + + view = GTK_TREE_VIEW (window->treeview); + selection = gtk_tree_view_get_selection (view); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_NAME, empathy_chatroom_get_name (chatroom), + COL_ROOM, empathy_chatroom_get_room (chatroom), + COL_AUTO_CONNECT, empathy_chatroom_get_auto_connect (chatroom), + COL_POINTER, chatroom, + -1); + + if (set_active) { + gtk_tree_selection_select_iter (selection, &iter); + } +} + +static void +chatrooms_window_model_cell_auto_connect_toggled (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyChatroomsWindow *window) +{ + EmpathyChatroom *chatroom; + gboolean enabled; + GtkTreeView *view; + GtkTreeModel *model; + GtkListStore *store; + GtkTreePath *path; + GtkTreeIter iter; + + view = GTK_TREE_VIEW (window->treeview); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + path = gtk_tree_path_new_from_string (path_string); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COL_AUTO_CONNECT, &enabled, + COL_POINTER, &chatroom, + -1); + + enabled = !enabled; + + empathy_chatroom_set_auto_connect (chatroom, enabled); + empathy_chatroom_manager_store (window->manager); + + gtk_list_store_set (store, &iter, COL_AUTO_CONNECT, enabled, -1); + gtk_tree_path_free (path); + g_object_unref (chatroom); +} + +static EmpathyChatroom * +chatrooms_window_model_get_selected (EmpathyChatroomsWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + EmpathyChatroom *chatroom = NULL; + + view = GTK_TREE_VIEW (window->treeview); + selection = gtk_tree_view_get_selection (view); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, COL_POINTER, &chatroom, -1); + } + + return chatroom; +} + +static void +chatrooms_window_model_action_selected (EmpathyChatroomsWindow *window) +{ + EmpathyChatroom *chatroom; + GtkTreeView *view; + GtkTreeModel *model; + + view = GTK_TREE_VIEW (window->treeview); + model = gtk_tree_view_get_model (view); + + chatroom = chatrooms_window_model_get_selected (window); + if (!chatroom) { + return; + } + + //empathy_edit_chatroom_dialog_show (GTK_WINDOW (window->window), chatroom); + + g_object_unref (chatroom); +} + +static void +chatrooms_window_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathyChatroomsWindow *window) +{ + if (GTK_WIDGET_IS_SENSITIVE (window->button_edit)) { + chatrooms_window_model_action_selected (window); + } +} + +static void +chatrooms_window_button_remove_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window) +{ + EmpathyChatroom *chatroom; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + /* Remove from treeview */ + view = GTK_TREE_VIEW (window->treeview); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return; + } + + gtk_tree_model_get (model, &iter, COL_POINTER, &chatroom, -1); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + + /* Remove from config */ + empathy_chatroom_manager_remove (window->manager, chatroom); + + g_object_unref (chatroom); +} + +static void +chatrooms_window_button_edit_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window) +{ + EmpathyChatroom *chatroom; + + chatroom = chatrooms_window_model_get_selected (window); + if (!chatroom) { + return; + } + + //empathy_edit_chatroom_dialog_show (GTK_WINDOW (window->window), chatroom); + + g_object_unref (chatroom); +} + +static void +chatrooms_window_button_close_clicked_cb (GtkWidget *widget, + EmpathyChatroomsWindow *window) +{ + gtk_widget_destroy (window->window); +} + +static void +chatrooms_window_chatroom_added_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyChatroomsWindow *window) +{ + EmpathyAccountChooser *account_chooser; + McAccount *account; + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + + if (!account) { + chatrooms_window_model_add (window, chatroom, FALSE); + } else { + if (empathy_account_equal (account, empathy_chatroom_get_account (chatroom))) { + chatrooms_window_model_add (window, chatroom, FALSE); + } + + g_object_unref (account); + } +} + +static void +chatrooms_window_chatroom_removed_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyChatroomsWindow *window) +{ + GtkTreeModel *model; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->treeview)); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) chatrooms_window_remove_chatroom_foreach, + chatroom); +} + +static gboolean +chatrooms_window_remove_chatroom_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyChatroom *chatroom) +{ + EmpathyChatroom *this_chatroom; + + gtk_tree_model_get (model, iter, COL_POINTER, &this_chatroom, -1); + + if (empathy_chatroom_equal (chatroom, this_chatroom)) { + gtk_list_store_remove (GTK_LIST_STORE (model), iter); + g_object_unref (this_chatroom); + return TRUE; + } + + g_object_unref (this_chatroom); + + return FALSE; +} + +static void +chatrooms_window_account_changed_cb (GtkWidget *combo_box, + EmpathyChatroomsWindow *window) +{ + chatrooms_window_model_refresh_data (window, FALSE); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.glade b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.glade new file mode 100644 index 000000000..6d47c0936 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.glade @@ -0,0 +1,477 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="edit_chatroom_dialog"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Edit Favorite Room</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="icon_name">gtk-edit</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_save"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-save</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table4"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkEntry" id="entry_room"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_nickname"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_room"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Room:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_room</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">S_erver:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_nickname"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Nickname:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_nickname</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_name"> + <property name="visible">True</property> + <property name="label" translatable="yes">N_ame:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_name</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + <property name="width_chars">25</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_auto_connect"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Join this chat room when Empathy starts and you are connected</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Join room on start_up</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkWindow" id="chatrooms_window"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Manage Favorite Rooms</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox12"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkVBox" id="vbox18"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkHBox" id="hbox_account"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_account"> + <property name="visible">True</property> + <property name="label" translatable="yes">Account:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="height_request">150</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHButtonBox" id="hbuttonbox3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="button_remove"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_edit"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_close"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.h b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.h new file mode 100644 index 000000000..179082052 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-chatrooms-window.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + * Martyn Russell <martyn@imendio.com> + * Mikael Hallendal <micke@imendio.com> + */ + +#ifndef __EMPATHY_CHATROOMS_WINDOW_H__ +#define __EMPATHY_CHATROOMS_WINDOW_H__ + +G_BEGIN_DECLS + +void empathy_chatrooms_window_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_CHATROOMS_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-conf.c b/gnome-2-22/libempathy-gtk/empathy-conf.c new file mode 100644 index 000000000..1ec3feeb4 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-conf.c @@ -0,0 +1,373 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gconf/gconf-client.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-conf.h" + +#define DEBUG_DOMAIN "Config" + +#define EMPATHY_CONF_ROOT "/apps/empathy" +#define DESKTOP_INTERFACE_ROOT "/desktop/gnome/interface" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CONF, EmpathyConfPriv)) + +typedef struct { + GConfClient *gconf_client; +} EmpathyConfPriv; + +typedef struct { + EmpathyConf *conf; + EmpathyConfNotifyFunc func; + gpointer user_data; +} EmpathyConfNotifyData; + +static void conf_finalize (GObject *object); + +G_DEFINE_TYPE (EmpathyConf, empathy_conf, G_TYPE_OBJECT); + +static EmpathyConf *global_conf = NULL; + +static void +empathy_conf_class_init (EmpathyConfClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = conf_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyConfPriv)); +} + +static void +empathy_conf_init (EmpathyConf *conf) +{ + EmpathyConfPriv *priv; + + priv = GET_PRIV (conf); + + priv->gconf_client = gconf_client_get_default (); + + gconf_client_add_dir (priv->gconf_client, + EMPATHY_CONF_ROOT, + GCONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + gconf_client_add_dir (priv->gconf_client, + DESKTOP_INTERFACE_ROOT, + GCONF_CLIENT_PRELOAD_NONE, + NULL); +} + +static void +conf_finalize (GObject *object) +{ + EmpathyConfPriv *priv; + + priv = GET_PRIV (object); + + gconf_client_remove_dir (priv->gconf_client, + EMPATHY_CONF_ROOT, + NULL); + gconf_client_remove_dir (priv->gconf_client, + DESKTOP_INTERFACE_ROOT, + NULL); + + g_object_unref (priv->gconf_client); + + G_OBJECT_CLASS (empathy_conf_parent_class)->finalize (object); +} + +EmpathyConf * +empathy_conf_get (void) +{ + if (!global_conf) { + global_conf = g_object_new (EMPATHY_TYPE_CONF, NULL); + } + + return global_conf; +} + +void +empathy_conf_shutdown (void) +{ + if (global_conf) { + g_object_unref (global_conf); + global_conf = NULL; + } +} + +gboolean +empathy_conf_set_int (EmpathyConf *conf, + const gchar *key, + gint value) +{ + EmpathyConfPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + empathy_debug (DEBUG_DOMAIN, "Setting int:'%s' to %d", key, value); + + priv = GET_PRIV (conf); + + return gconf_client_set_int (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +empathy_conf_get_int (EmpathyConf *conf, + const gchar *key, + gint *value) +{ + EmpathyConfPriv *priv; + GError *error = NULL; + + *value = 0; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_int (priv->gconf_client, + key, + &error); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +empathy_conf_set_bool (EmpathyConf *conf, + const gchar *key, + gboolean value) +{ + EmpathyConfPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + empathy_debug (DEBUG_DOMAIN, "Setting bool:'%s' to %d ---> %s", + key, value, value ? "true" : "false"); + + priv = GET_PRIV (conf); + + return gconf_client_set_bool (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +empathy_conf_get_bool (EmpathyConf *conf, + const gchar *key, + gboolean *value) +{ + EmpathyConfPriv *priv; + GError *error = NULL; + + *value = FALSE; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_bool (priv->gconf_client, + key, + &error); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +empathy_conf_set_string (EmpathyConf *conf, + const gchar *key, + const gchar *value) +{ + EmpathyConfPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + empathy_debug (DEBUG_DOMAIN, "Setting string:'%s' to '%s'", + key, value); + + priv = GET_PRIV (conf); + + return gconf_client_set_string (priv->gconf_client, + key, + value, + NULL); +} + +gboolean +empathy_conf_get_string (EmpathyConf *conf, + const gchar *key, + gchar **value) +{ + EmpathyConfPriv *priv; + GError *error = NULL; + + *value = NULL; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_string (priv->gconf_client, + key, + &error); + + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +gboolean +empathy_conf_set_string_list (EmpathyConf *conf, + const gchar *key, + GSList *value) +{ + EmpathyConfPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + return gconf_client_set_list (priv->gconf_client, + key, + GCONF_VALUE_STRING, + value, + NULL); +} + +gboolean +empathy_conf_get_string_list (EmpathyConf *conf, + const gchar *key, + GSList **value) +{ + EmpathyConfPriv *priv; + GError *error = NULL; + + *value = NULL; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + *value = gconf_client_get_list (priv->gconf_client, + key, + GCONF_VALUE_STRING, + &error); + if (error) { + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static void +conf_notify_data_free (EmpathyConfNotifyData *data) +{ + g_object_unref (data->conf); + g_slice_free (EmpathyConfNotifyData, data); +} + +static void +conf_notify_func (GConfClient *client, + guint id, + GConfEntry *entry, + gpointer user_data) +{ + EmpathyConfNotifyData *data; + + data = user_data; + + data->func (data->conf, + gconf_entry_get_key (entry), + data->user_data); +} + +guint +empathy_conf_notify_add (EmpathyConf *conf, + const gchar *key, + EmpathyConfNotifyFunc func, + gpointer user_data) +{ + EmpathyConfPriv *priv; + guint id; + EmpathyConfNotifyData *data; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), 0); + + priv = GET_PRIV (conf); + + data = g_slice_new (EmpathyConfNotifyData); + data->func = func; + data->user_data = user_data; + data->conf = g_object_ref (conf); + + id = gconf_client_notify_add (priv->gconf_client, + key, + conf_notify_func, + data, + (GFreeFunc) conf_notify_data_free, + NULL); + + return id; +} + +gboolean +empathy_conf_notify_remove (EmpathyConf *conf, + guint id) +{ + EmpathyConfPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONF (conf), FALSE); + + priv = GET_PRIV (conf); + + gconf_client_notify_remove (priv->gconf_client, id); + + return TRUE; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-conf.h b/gnome-2-22/libempathy-gtk/empathy-conf.h new file mode 100644 index 000000000..7e8e60e25 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-conf.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_CONF_H__ +#define __EMPATHY_CONF_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CONF (empathy_conf_get_type ()) +#define EMPATHY_CONF(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONF, EmpathyConf)) +#define EMPATHY_CONF_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_CONF, EmpathyConfClass)) +#define EMPATHY_IS_CONF(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONF)) +#define EMPATHY_IS_CONF_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONF)) +#define EMPATHY_CONF_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONF, EmpathyConfClass)) + +typedef struct _EmpathyConf EmpathyConf; +typedef struct _EmpathyConfClass EmpathyConfClass; + +struct _EmpathyConf { + GObject parent; +}; + +struct _EmpathyConfClass { + GObjectClass parent_class; +}; + +typedef void (*EmpathyConfNotifyFunc) (EmpathyConf *conf, + const gchar *key, + gpointer user_data); + +GType empathy_conf_get_type (void) G_GNUC_CONST; +EmpathyConf *empathy_conf_get (void); +void empathy_conf_shutdown (void); +guint empathy_conf_notify_add (EmpathyConf *conf, + const gchar *key, + EmpathyConfNotifyFunc func, + gpointer data); +gboolean empathy_conf_notify_remove (EmpathyConf *conf, + guint id); +gboolean empathy_conf_set_int (EmpathyConf *conf, + const gchar *key, + gint value); +gboolean empathy_conf_get_int (EmpathyConf *conf, + const gchar *key, + gint *value); +gboolean empathy_conf_set_bool (EmpathyConf *conf, + const gchar *key, + gboolean value); +gboolean empathy_conf_get_bool (EmpathyConf *conf, + const gchar *key, + gboolean *value); +gboolean empathy_conf_set_string (EmpathyConf *conf, + const gchar *key, + const gchar *value); +gboolean empathy_conf_get_string (EmpathyConf *conf, + const gchar *key, + gchar **value); +gboolean empathy_conf_set_string_list (EmpathyConf *conf, + const gchar *key, + GSList *value); +gboolean empathy_conf_get_string_list (EmpathyConf *conf, + const gchar *key, + GSList **value); + +G_END_DECLS + +#endif /* __EMPATHY_CONF_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.c b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.c new file mode 100644 index 000000000..e0785c3ce --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.c @@ -0,0 +1,344 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-contact-dialogs.h" +#include "empathy-contact-widget.h" +#include "empathy-ui-utils.h" + +static GList *subscription_dialogs = NULL; +static GList *information_dialogs = NULL; +static GtkWidget *new_contact_dialog = NULL; + + +static gint +contact_dialogs_find (GtkDialog *dialog, + EmpathyContact *contact) +{ + GtkWidget *contact_widget; + EmpathyContact *this_contact; + + contact_widget = g_object_get_data (G_OBJECT (dialog), "contact_widget"); + this_contact = empathy_contact_widget_get_contact (contact_widget); + + return !empathy_contact_equal (contact, this_contact); +} + +/* + * Subscription dialog + */ + +static void +subscription_dialog_response_cb (GtkDialog *dialog, + gint response, + GtkWidget *contact_widget) +{ + EmpathyContactManager *manager; + EmpathyContact *contact; + + manager = empathy_contact_manager_new (); + contact = empathy_contact_widget_get_contact (contact_widget); + + if (response == GTK_RESPONSE_YES) { + empathy_contact_list_add (EMPATHY_CONTACT_LIST (manager), + contact, ""); + } + else if (response == GTK_RESPONSE_NO) { + empathy_contact_list_remove (EMPATHY_CONTACT_LIST (manager), + contact, ""); + } + + subscription_dialogs = g_list_remove (subscription_dialogs, dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_object_unref (manager); +} + +void +empathy_subscription_dialog_show (EmpathyContact *contact, + GtkWindow *parent) +{ + GtkWidget *dialog; + GtkWidget *hbox_subscription; + GtkWidget *contact_widget; + GList *l; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + + l = g_list_find_custom (subscription_dialogs, + contact, + (GCompareFunc) contact_dialogs_find); + if (l) { + gtk_window_present (GTK_WINDOW (l->data)); + return; + } + + empathy_glade_get_file_simple ("empathy-contact-dialogs.glade", + "subscription_request_dialog", + NULL, + "subscription_request_dialog", &dialog, + "hbox_subscription", &hbox_subscription, + NULL); + + contact_widget = empathy_contact_widget_new (contact, + EMPATHY_CONTACT_WIDGET_EDIT_ALIAS | + EMPATHY_CONTACT_WIDGET_EDIT_GROUPS); + gtk_box_pack_end (GTK_BOX (hbox_subscription), + contact_widget, + TRUE, TRUE, + 0); + + g_object_set_data (G_OBJECT (dialog), "contact_widget", contact_widget); + subscription_dialogs = g_list_prepend (subscription_dialogs, dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (subscription_dialog_response_cb), + contact_widget); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + } + + gtk_widget_show (dialog); +} + +/* + * Information dialog + */ + +static void +contact_information_response_cb (GtkDialog *dialog, + gint response, + GtkWidget *contact_widget) +{ + information_dialogs = g_list_remove (information_dialogs, dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +void +empathy_contact_information_dialog_show (EmpathyContact *contact, + GtkWindow *parent, + gboolean edit, + gboolean is_user) +{ + GtkWidget *dialog; + GtkWidget *button; + GtkWidget *contact_widget; + GList *l; + EmpathyContactWidgetFlags flags = 0; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + + l = g_list_find_custom (information_dialogs, + contact, + (GCompareFunc) contact_dialogs_find); + if (l) { + gtk_window_present (GTK_WINDOW (l->data)); + return; + } + + /* Create dialog */ + dialog = gtk_dialog_new (); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + if (is_user) { + gtk_window_set_title (GTK_WINDOW (dialog), _("Personal Information")); + } + else if (edit) { + gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Contact Information")); + } + else { + gtk_window_set_title (GTK_WINDOW (dialog), _("Contact Information")); + } + + /* Close button */ + button = gtk_button_new_with_label (GTK_STOCK_CLOSE); + gtk_button_set_use_stock (GTK_BUTTON (button), TRUE); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), + button, + GTK_RESPONSE_CLOSE); + gtk_widget_show (button); + + /* Contact info widget */ + if (edit) { + flags |= EMPATHY_CONTACT_WIDGET_EDIT_ALIAS; + } + if (is_user) { + flags |= EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT; + flags |= EMPATHY_CONTACT_WIDGET_EDIT_AVATAR; + } + if (!is_user && edit) { + flags |= EMPATHY_CONTACT_WIDGET_EDIT_GROUPS; + } + contact_widget = empathy_contact_widget_new (contact, flags); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + contact_widget, + TRUE, TRUE, 0); + if (flags & EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT) { + empathy_contact_widget_set_account_filter (contact_widget, + empathy_account_chooser_filter_is_connected, + NULL); + } + + g_object_set_data (G_OBJECT (dialog), "contact_widget", contact_widget); + information_dialogs = g_list_prepend (information_dialogs, dialog); + + g_signal_connect (dialog, "response", + G_CALLBACK (contact_information_response_cb), + contact_widget); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + } + + gtk_widget_show (dialog); +} + +/* + * New contact dialog + */ + +static gboolean +can_add_contact_to_account (McAccount *account, + gpointer user_data) +{ + MissionControl *mc; + TpConnectionStatus status; + McProfile *profile; + const gchar *protocol_name; + + mc = empathy_mission_control_new (); + status = mission_control_get_connection_status (mc, account, NULL); + g_object_unref (mc); + if (status != TP_CONNECTION_STATUS_CONNECTED) { + /* Account is disconnected */ + return FALSE; + } + + profile = mc_account_get_profile (account); + protocol_name = mc_profile_get_protocol_name (profile); + if (strcmp (protocol_name, "local-xmpp") == 0) { + /* We can't add accounts to a XMPP LL connection + * FIXME: We should inspect the flags of the contact list group interface + */ + g_object_unref (profile); + return FALSE; + } + + g_object_unref (profile); + return TRUE; +} + +static void +new_contact_response_cb (GtkDialog *dialog, + gint response, + GtkWidget *contact_widget) +{ + EmpathyContactManager *manager; + EmpathyContact *contact; + + manager = empathy_contact_manager_new (); + contact = empathy_contact_widget_get_contact (contact_widget); + + if (contact && response == GTK_RESPONSE_OK) { + empathy_contact_list_add (EMPATHY_CONTACT_LIST (manager), + contact, + _("I would like to add you to my contact list.")); + } + + new_contact_dialog = NULL; + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_object_unref (manager); +} + +void +empathy_new_contact_dialog_show (GtkWindow *parent) +{ + GtkWidget *dialog; + GtkWidget *button; + GtkWidget *contact_widget; + + if (new_contact_dialog) { + gtk_window_present (GTK_WINDOW (new_contact_dialog)); + return; + } + + /* Create dialog */ + dialog = gtk_dialog_new (); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_window_set_title (GTK_WINDOW (dialog), _("New Contact")); + + /* Cancel button */ + button = gtk_button_new_with_label (GTK_STOCK_CANCEL); + gtk_button_set_use_stock (GTK_BUTTON (button), TRUE); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), + button, + GTK_RESPONSE_CANCEL); + gtk_widget_show (button); + + /* Add button */ + button = gtk_button_new_with_label (GTK_STOCK_ADD); + gtk_button_set_use_stock (GTK_BUTTON (button), TRUE); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), + button, + GTK_RESPONSE_OK); + gtk_widget_show (button); + + /* Contact info widget */ + contact_widget = empathy_contact_widget_new (NULL, + EMPATHY_CONTACT_WIDGET_EDIT_ALIAS | + EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT | + EMPATHY_CONTACT_WIDGET_EDIT_ID | + EMPATHY_CONTACT_WIDGET_EDIT_GROUPS); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + contact_widget, + TRUE, TRUE, 0); + empathy_contact_widget_set_account_filter (contact_widget, + can_add_contact_to_account, + NULL); + + new_contact_dialog = dialog; + + g_signal_connect (dialog, "response", + G_CALLBACK (new_contact_response_cb), + contact_widget); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + } + + gtk_widget_show (dialog); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.glade b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.glade new file mode 100644 index 000000000..f8a441057 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.glade @@ -0,0 +1,120 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="subscription_request_dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Subscription Request</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox4"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area4"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button19"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Decide _Later</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button20"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-no</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-9</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button21"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-yes</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-8</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox_subscription"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkImage" id="image5"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-question</property> + <property name="icon_size">6</property> + <property name="xalign">0.5</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.h b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.h new file mode 100644 index 000000000..a03e0d000 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-dialogs.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CONTACT_DIALOGS_H__ +#define __EMPATHY_CONTACT_DIALOGS_H__ + +#include <gtk/gtk.h> + +#include <libempathy/empathy-contact.h> + +G_BEGIN_DECLS + +void empathy_subscription_dialog_show (EmpathyContact *contact, + GtkWindow *parent); +void empathy_contact_information_dialog_show (EmpathyContact *contact, + GtkWindow *parent, + gboolean edit, + gboolean is_user); +void empathy_new_contact_dialog_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_DIALOGS_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-list-store.c b/gnome-2-22/libempathy-gtk/empathy-contact-list-store.c new file mode 100644 index 000000000..cf43bce62 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-list-store.c @@ -0,0 +1,1549 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-contact-list-store.h" +#include "empathy-ui-utils.h" +#include "empathy-gtk-enum-types.h" + +#define DEBUG_DOMAIN "ContactListStore" + +/* Active users are those which have recently changed state + * (e.g. online, offline or from normal to a busy state). + */ + +/* Time in seconds user is shown as active */ +#define ACTIVE_USER_SHOW_TIME 7 + +/* Time in seconds after connecting which we wait before active users are enabled */ +#define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5 + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStorePriv)) + +typedef struct { + EmpathyContactList *list; + gboolean show_offline; + gboolean show_avatars; + gboolean show_groups; + gboolean is_compact; + gboolean show_active; + EmpathyContactListStoreSort sort_criterium; + guint inhibit_active; +} EmpathyContactListStorePriv; + +typedef struct { + GtkTreeIter iter; + const gchar *name; + gboolean found; +} FindGroup; + +typedef struct { + EmpathyContact *contact; + gboolean found; + GList *iters; +} FindContact; + +typedef struct { + EmpathyContactListStore *store; + EmpathyContact *contact; + gboolean remove; +} ShowActiveData; + +static void empathy_contact_list_store_class_init (EmpathyContactListStoreClass *klass); +static void empathy_contact_list_store_init (EmpathyContactListStore *list); +static void contact_list_store_finalize (GObject *object); +static void contact_list_store_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void contact_list_store_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static gboolean contact_list_store_finalize_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static void contact_list_store_setup (EmpathyContactListStore *store); +static gboolean contact_list_store_inibit_active_cb (EmpathyContactListStore *store); +static void contact_list_store_members_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_member, + EmpathyContactListStore *store); +static void contact_list_store_groups_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + gchar *group, + gboolean is_member, + EmpathyContactListStore *store); +static void contact_list_store_add_contact (EmpathyContactListStore *store, + EmpathyContact *contact); +static void contact_list_store_remove_contact (EmpathyContactListStore *store, + EmpathyContact *contact); +static void contact_list_store_contact_update (EmpathyContactListStore *store, + EmpathyContact *contact); +static void contact_list_store_contact_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyContactListStore *store); +static void contact_list_store_contact_set_active (EmpathyContactListStore *store, + EmpathyContact *contact, + gboolean active, + gboolean set_changed); +static ShowActiveData * contact_list_store_contact_active_new (EmpathyContactListStore *store, + EmpathyContact *contact, + gboolean remove); +static void contact_list_store_contact_active_free (ShowActiveData *data); +static gboolean contact_list_store_contact_active_cb (ShowActiveData *data); +static gboolean contact_list_store_get_group_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindGroup *fg); +static void contact_list_store_get_group (EmpathyContactListStore *store, + const gchar *name, + GtkTreeIter *iter_group_to_set, + GtkTreeIter *iter_separator_to_set, + gboolean *created); +static gint contact_list_store_state_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data); +static gint contact_list_store_name_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data); +static gboolean contact_list_store_find_contact_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindContact *fc); +static GList * contact_list_store_find_contact (EmpathyContactListStore *store, + EmpathyContact *contact); +static gboolean contact_list_store_update_list_mode_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyContactListStore *store); + +enum { + PROP_0, + PROP_CONTACT_LIST, + PROP_SHOW_OFFLINE, + PROP_SHOW_AVATARS, + PROP_SHOW_GROUPS, + PROP_IS_COMPACT, + PROP_SORT_CRITERIUM +}; + +G_DEFINE_TYPE (EmpathyContactListStore, empathy_contact_list_store, GTK_TYPE_TREE_STORE); + + +static gboolean +contact_list_store_iface_setup (gpointer user_data) +{ + EmpathyContactListStore *store = user_data; + EmpathyContactListStorePriv *priv = GET_PRIV (store); + GList *contacts, *l; + + /* Signal connection. */ + g_signal_connect (priv->list, + "members-changed", + G_CALLBACK (contact_list_store_members_changed_cb), + store); + g_signal_connect (priv->list, + "groups-changed", + G_CALLBACK (contact_list_store_groups_changed_cb), + store); + + /* Add contacts already created. */ + contacts = empathy_contact_list_get_members (priv->list); + for (l = contacts; l; l = l->next) { + contact_list_store_members_changed_cb (priv->list, l->data, + NULL, 0, NULL, + TRUE, + store); + + g_object_unref (l->data); + } + g_list_free (contacts); + + return FALSE; +} + + +static void +contact_list_store_set_contact_list (EmpathyContactListStore *store, + EmpathyContactList *list_iface) +{ + EmpathyContactListStorePriv *priv = GET_PRIV (store); + + priv->list = g_object_ref (list_iface); + + /* Let a chance to have all properties set before populating */ + g_idle_add (contact_list_store_iface_setup, + store); +} + +static void +empathy_contact_list_store_class_init (EmpathyContactListStoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = contact_list_store_finalize; + object_class->get_property = contact_list_store_get_property; + object_class->set_property = contact_list_store_set_property; + + g_object_class_install_property (object_class, + PROP_CONTACT_LIST, + g_param_spec_object ("contact-list", + "The contact list iface", + "The contact list iface", + EMPATHY_TYPE_CONTACT_LIST, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SHOW_OFFLINE, + g_param_spec_boolean ("show-offline", + "Show Offline", + "Whether contact list should display " + "offline contacts", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SHOW_AVATARS, + g_param_spec_boolean ("show-avatars", + "Show Avatars", + "Whether contact list should display " + "avatars for contacts", + TRUE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_SHOW_GROUPS, + g_param_spec_boolean ("show-groups", + "Show Groups", + "Whether contact list should display " + "contact groups", + TRUE, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_IS_COMPACT, + g_param_spec_boolean ("is-compact", + "Is Compact", + "Whether the contact list is in compact mode or not", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SORT_CRITERIUM, + g_param_spec_enum ("sort-criterium", + "Sort citerium", + "The sort criterium to use for sorting the contact list", + EMPATHY_TYPE_CONTACT_LIST_STORE_SORT, + EMPATHY_CONTACT_LIST_STORE_SORT_NAME, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyContactListStorePriv)); +} + +static void +empathy_contact_list_store_init (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (store); + + priv->show_avatars = TRUE; + priv->show_groups = TRUE; + priv->inhibit_active = g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME, + (GSourceFunc) contact_list_store_inibit_active_cb, + store); + contact_list_store_setup (store); +} + +static void +contact_list_store_finalize (GObject *object) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (object); + + gtk_tree_model_foreach (GTK_TREE_MODEL (object), + (GtkTreeModelForeachFunc) contact_list_store_finalize_foreach, + object); + + if (priv->list) { + g_signal_handlers_disconnect_by_func (priv->list, + G_CALLBACK (contact_list_store_members_changed_cb), + object); + g_signal_handlers_disconnect_by_func (priv->list, + G_CALLBACK (contact_list_store_groups_changed_cb), + object); + g_object_unref (priv->list); + } + + if (priv->inhibit_active) { + g_source_remove (priv->inhibit_active); + } + + G_OBJECT_CLASS (empathy_contact_list_store_parent_class)->finalize (object); +} + +static void +contact_list_store_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_CONTACT_LIST: + g_value_set_object (value, priv->list); + break; + case PROP_SHOW_OFFLINE: + g_value_set_boolean (value, priv->show_offline); + break; + case PROP_SHOW_AVATARS: + g_value_set_boolean (value, priv->show_avatars); + break; + case PROP_SHOW_GROUPS: + g_value_set_boolean (value, priv->show_groups); + break; + case PROP_IS_COMPACT: + g_value_set_boolean (value, priv->is_compact); + break; + case PROP_SORT_CRITERIUM: + g_value_set_enum (value, priv->sort_criterium); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +contact_list_store_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_CONTACT_LIST: + contact_list_store_set_contact_list (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_object (value)); + break; + case PROP_SHOW_OFFLINE: + empathy_contact_list_store_set_show_offline (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_boolean (value)); + break; + case PROP_SHOW_AVATARS: + empathy_contact_list_store_set_show_avatars (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_boolean (value)); + break; + case PROP_SHOW_GROUPS: + empathy_contact_list_store_set_show_groups (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_boolean (value)); + break; + case PROP_IS_COMPACT: + empathy_contact_list_store_set_is_compact (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_boolean (value)); + break; + case PROP_SORT_CRITERIUM: + empathy_contact_list_store_set_sort_criterium (EMPATHY_CONTACT_LIST_STORE (object), + g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +EmpathyContactListStore * +empathy_contact_list_store_new (EmpathyContactList *list_iface) +{ + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list_iface), NULL); + + return g_object_new (EMPATHY_TYPE_CONTACT_LIST_STORE, + "contact-list", list_iface, + NULL); +} + +EmpathyContactList * +empathy_contact_list_store_get_list_iface (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE); + + priv = GET_PRIV (store); + + return priv->list; +} + +gboolean +empathy_contact_list_store_get_show_offline (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE); + + priv = GET_PRIV (store); + + return priv->show_offline; +} + +void +empathy_contact_list_store_set_show_offline (EmpathyContactListStore *store, + gboolean show_offline) +{ + EmpathyContactListStorePriv *priv; + GList *contacts, *l; + gboolean show_active; + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store)); + + priv = GET_PRIV (store); + + priv->show_offline = show_offline; + show_active = priv->show_active; + + /* Disable temporarily. */ + priv->show_active = FALSE; + + contacts = empathy_contact_list_get_members (priv->list); + for (l = contacts; l; l = l->next) { + contact_list_store_contact_update (store, l->data); + + g_object_unref (l->data); + } + g_list_free (contacts); + + /* Restore to original setting. */ + priv->show_active = show_active; + + g_object_notify (G_OBJECT (store), "show-offline"); +} + +gboolean +empathy_contact_list_store_get_show_avatars (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE); + + priv = GET_PRIV (store); + + return priv->show_avatars; +} + +void +empathy_contact_list_store_set_show_avatars (EmpathyContactListStore *store, + gboolean show_avatars) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store)); + + priv = GET_PRIV (store); + + priv->show_avatars = show_avatars; + + model = GTK_TREE_MODEL (store); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) + contact_list_store_update_list_mode_foreach, + store); + + g_object_notify (G_OBJECT (store), "show-avatars"); +} + +gboolean +empathy_contact_list_store_get_show_groups (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE); + + priv = GET_PRIV (store); + + return priv->show_groups; +} + +void +empathy_contact_list_store_set_show_groups (EmpathyContactListStore *store, + gboolean show_groups) +{ + EmpathyContactListStorePriv *priv; + GList *contacts, *l; + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store)); + + priv = GET_PRIV (store); + + if (priv->show_groups == show_groups) { + return; + } + + priv->show_groups = show_groups; + + /* Remove all contacts and add them back, not optimized but that's the + * easy way :) */ + gtk_tree_store_clear (GTK_TREE_STORE (store)); + contacts = empathy_contact_list_get_members (priv->list); + for (l = contacts; l; l = l->next) { + contact_list_store_members_changed_cb (priv->list, l->data, + NULL, 0, NULL, + TRUE, + store); + + g_object_unref (l->data); + } + g_list_free (contacts); + + g_object_notify (G_OBJECT (store), "show-groups"); +} + +gboolean +empathy_contact_list_store_get_is_compact (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE); + + priv = GET_PRIV (store); + + return priv->is_compact; +} + +void +empathy_contact_list_store_set_is_compact (EmpathyContactListStore *store, + gboolean is_compact) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store)); + + priv = GET_PRIV (store); + + priv->is_compact = is_compact; + + model = GTK_TREE_MODEL (store); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) + contact_list_store_update_list_mode_foreach, + store); + + g_object_notify (G_OBJECT (store), "is-compact"); +} + +EmpathyContactListStoreSort +empathy_contact_list_store_get_sort_criterium (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), 0); + + priv = GET_PRIV (store); + + return priv->sort_criterium; +} + +void +empathy_contact_list_store_set_sort_criterium (EmpathyContactListStore *store, + EmpathyContactListStoreSort sort_criterium) +{ + EmpathyContactListStorePriv *priv; + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store)); + + priv = GET_PRIV (store); + + priv->sort_criterium = sort_criterium; + + switch (sort_criterium) { + case EMPATHY_CONTACT_LIST_STORE_SORT_STATE: + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + EMPATHY_CONTACT_LIST_STORE_COL_STATUS, + GTK_SORT_ASCENDING); + break; + + case EMPATHY_CONTACT_LIST_STORE_SORT_NAME: + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + EMPATHY_CONTACT_LIST_STORE_COL_NAME, + GTK_SORT_ASCENDING); + break; + } + + g_object_notify (G_OBJECT (store), "sort-criterium"); +} + +gboolean +empathy_contact_list_store_row_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean is_separator = FALSE; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator, + -1); + + return is_separator; +} + +gchar * +empathy_contact_list_store_get_parent_group (GtkTreeModel *model, + GtkTreePath *path, + gboolean *path_is_group) +{ + GtkTreeIter parent_iter, iter; + gchar *name = NULL; + gboolean is_group; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + + if (path_is_group) { + *path_is_group = FALSE; + } + + if (!gtk_tree_model_get_iter (model, &iter, path)) { + return NULL; + } + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + if (!is_group) { + g_free (name); + name = NULL; + + if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter)) { + return NULL; + } + + iter = parent_iter; + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + if (!is_group) { + g_free (name); + return NULL; + } + } + + if (path_is_group) { + *path_is_group = TRUE; + } + + return name; +} + +gboolean +empathy_contact_list_store_search_equal_func (GtkTreeModel *model, + gint column, + const gchar *key, + GtkTreeIter *iter, + gpointer search_data) +{ + gchar *name, *name_folded; + gchar *key_folded; + gboolean ret; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + if (!key) { + return TRUE; + } + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + if (!name) { + return TRUE; + } + + name_folded = g_utf8_casefold (name, -1); + key_folded = g_utf8_casefold (key, -1); + + if (name_folded && key_folded && + strstr (name_folded, key_folded)) { + ret = FALSE; + } else { + ret = TRUE; + } + + g_free (name); + g_free (name_folded); + g_free (key_folded); + + return ret; +} + +static gboolean +contact_list_store_finalize_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + EmpathyContactListStore *store = user_data; + EmpathyContact *contact = NULL; + + gtk_tree_model_get (GTK_TREE_MODEL (store), iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + if (contact) { + g_signal_handlers_disconnect_by_func (contact, + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + } + + return FALSE; +} + +static void +contact_list_store_setup (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + GType types[] = {G_TYPE_STRING, /* Status icon-name */ + GDK_TYPE_PIXBUF, /* Avatar pixbuf */ + G_TYPE_BOOLEAN, /* Avatar pixbuf visible */ + G_TYPE_STRING, /* Name */ + G_TYPE_STRING, /* Status string */ + G_TYPE_BOOLEAN, /* Show status */ + EMPATHY_TYPE_CONTACT, /* Contact type */ + G_TYPE_BOOLEAN, /* Is group */ + G_TYPE_BOOLEAN, /* Is active */ + G_TYPE_BOOLEAN, /* Is online */ + G_TYPE_BOOLEAN, /* Is separator */ + G_TYPE_BOOLEAN}; /* Can VoIP */ + + priv = GET_PRIV (store); + + gtk_tree_store_set_column_types (GTK_TREE_STORE (store), + EMPATHY_CONTACT_LIST_STORE_COL_COUNT, + types); + + /* Set up sorting */ + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + EMPATHY_CONTACT_LIST_STORE_COL_NAME, + contact_list_store_name_sort_func, + store, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + EMPATHY_CONTACT_LIST_STORE_COL_STATUS, + contact_list_store_state_sort_func, + store, NULL); + + priv->sort_criterium = EMPATHY_CONTACT_LIST_STORE_SORT_NAME; + empathy_contact_list_store_set_sort_criterium (store, priv->sort_criterium); +} + +static gboolean +contact_list_store_inibit_active_cb (EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (store); + + priv->show_active = TRUE; + priv->inhibit_active = 0; + + return FALSE; +} + +static void +contact_list_store_members_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_member, + EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (store); + + empathy_debug (DEBUG_DOMAIN, + "Contact %s (%d) %s", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + is_member ? "added" : "removed"); + + if (is_member) { + g_signal_connect (contact, "notify::presence", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (contact, "notify::presence-message", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (contact, "notify::name", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (contact, "notify::avatar", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + g_signal_connect (contact, "notify::capabilities", + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + + contact_list_store_add_contact (store, contact); + } else { + g_signal_handlers_disconnect_by_func (contact, + G_CALLBACK (contact_list_store_contact_updated_cb), + store); + + contact_list_store_remove_contact (store, contact); + } +} + +static void +contact_list_store_groups_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + gchar *group, + gboolean is_member, + EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + gboolean show_active; + + priv = GET_PRIV (store); + + empathy_debug (DEBUG_DOMAIN, "Updating groups for contact %s (%d)", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact)); + + /* We do this to make sure the groups are correct, if not, we + * would have to check the groups already set up for each + * contact and then see what has been updated. + */ + show_active = priv->show_active; + priv->show_active = FALSE; + contact_list_store_remove_contact (store, contact); + contact_list_store_add_contact (store, contact); + priv->show_active = show_active; +} + +static void +contact_list_store_add_contact (EmpathyContactListStore *store, + EmpathyContact *contact) +{ + EmpathyContactListStorePriv *priv; + GtkTreeIter iter; + GList *groups = NULL, *l; + + priv = GET_PRIV (store); + + if (!priv->show_offline && !empathy_contact_is_online (contact)) { + return; + } + + if (priv->show_groups) { + groups = empathy_contact_list_get_groups (priv->list, contact); + } + + /* If no groups just add it at the top level. */ + if (!groups) { + gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (store), &iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, empathy_contact_can_voip (contact), + -1); + } + + /* Else add to each group. */ + for (l = groups; l; l = l->next) { + GtkTreeIter iter_group; + + contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL); + + gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, + &iter_group, NULL); + gtk_tree_store_set (GTK_TREE_STORE (store), &iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, empathy_contact_can_voip (contact), + -1); + g_free (l->data); + } + g_list_free (groups); + + contact_list_store_contact_update (store, contact); + +} + +static void +contact_list_store_remove_contact (EmpathyContactListStore *store, + EmpathyContact *contact) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + GList *iters, *l; + + priv = GET_PRIV (store); + + iters = contact_list_store_find_contact (store, contact); + if (!iters) { + return; + } + + /* Clean up model */ + model = GTK_TREE_MODEL (store); + + for (l = iters; l; l = l->next) { + GtkTreeIter parent; + + /* NOTE: it is only <= 2 here because we have + * separators after the group name, otherwise it + * should be 1. + */ + if (gtk_tree_model_iter_parent (model, &parent, l->data) && + gtk_tree_model_iter_n_children (model, &parent) <= 2) { + gtk_tree_store_remove (GTK_TREE_STORE (store), &parent); + } else { + gtk_tree_store_remove (GTK_TREE_STORE (store), l->data); + } + } + + g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL); + g_list_free (iters); +} + +static void +contact_list_store_contact_update (EmpathyContactListStore *store, + EmpathyContact *contact) +{ + EmpathyContactListStorePriv *priv; + ShowActiveData *data; + GtkTreeModel *model; + GList *iters, *l; + gboolean in_list; + gboolean should_be_in_list; + gboolean was_online = TRUE; + gboolean now_online = FALSE; + gboolean set_model = FALSE; + gboolean do_remove = FALSE; + gboolean do_set_active = FALSE; + gboolean do_set_refresh = FALSE; + GdkPixbuf *pixbuf_avatar; + + priv = GET_PRIV (store); + + model = GTK_TREE_MODEL (store); + + iters = contact_list_store_find_contact (store, contact); + if (!iters) { + in_list = FALSE; + } else { + in_list = TRUE; + } + + /* Get online state now. */ + now_online = empathy_contact_is_online (contact); + + if (priv->show_offline || now_online) { + should_be_in_list = TRUE; + } else { + should_be_in_list = FALSE; + } + + if (!in_list && !should_be_in_list) { + /* Nothing to do. */ + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' in list:NO, should be:NO", + empathy_contact_get_name (contact)); + + g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL); + g_list_free (iters); + return; + } + else if (in_list && !should_be_in_list) { + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' in list:YES, should be:NO", + empathy_contact_get_name (contact)); + + if (priv->show_active) { + do_remove = TRUE; + do_set_active = TRUE; + do_set_refresh = TRUE; + + set_model = TRUE; + empathy_debug (DEBUG_DOMAIN, "Remove item (after timeout)"); + } else { + empathy_debug (DEBUG_DOMAIN, "Remove item (now)!"); + contact_list_store_remove_contact (store, contact); + } + } + else if (!in_list && should_be_in_list) { + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' in list:NO, should be:YES", + empathy_contact_get_name (contact)); + + contact_list_store_add_contact (store, contact); + + if (priv->show_active) { + do_set_active = TRUE; + + empathy_debug (DEBUG_DOMAIN, "Set active (contact added)"); + } + } else { + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' in list:YES, should be:YES", + empathy_contact_get_name (contact)); + + /* Get online state before. */ + if (iters && g_list_length (iters) > 0) { + gtk_tree_model_get (model, iters->data, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, &was_online, + -1); + } + + /* Is this really an update or an online/offline. */ + if (priv->show_active) { + if (was_online != now_online) { + do_set_active = TRUE; + do_set_refresh = TRUE; + + empathy_debug (DEBUG_DOMAIN, "Set active (contact updated %s)", + was_online ? "online -> offline" : + "offline -> online"); + } else { + /* Was TRUE for presence updates. */ + /* do_set_active = FALSE; */ + do_set_refresh = TRUE; + + empathy_debug (DEBUG_DOMAIN, "Set active (contact updated)"); + } + } + + set_model = TRUE; + } + + pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32); + for (l = iters; l && set_model; l = l->next) { + gtk_tree_store_set (GTK_TREE_STORE (store), l->data, + EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, empathy_icon_name_for_contact (contact), + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, pixbuf_avatar, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, priv->show_avatars, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), + EMPATHY_CONTACT_LIST_STORE_COL_STATUS, empathy_contact_get_status (contact), + EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, now_online, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, empathy_contact_can_voip (contact), + -1); + } + + if (pixbuf_avatar) { + g_object_unref (pixbuf_avatar); + } + + if (priv->show_active && do_set_active) { + contact_list_store_contact_set_active (store, contact, do_set_active, do_set_refresh); + + if (do_set_active) { + data = contact_list_store_contact_active_new (store, contact, do_remove); + g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME, + (GSourceFunc) contact_list_store_contact_active_cb, + data); + } + } + + /* FIXME: when someone goes online then offline quickly, the + * first timeout sets the user to be inactive and the second + * timeout removes the user from the contact list, really we + * should remove the first timeout. + */ + g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL); + g_list_free (iters); +} + +static void +contact_list_store_contact_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyContactListStore *store) +{ + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' updated, checking roster is in sync...", + empathy_contact_get_name (contact)); + + contact_list_store_contact_update (store, contact); +} + +static void +contact_list_store_contact_set_active (EmpathyContactListStore *store, + EmpathyContact *contact, + gboolean active, + gboolean set_changed) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + GList *iters, *l; + + priv = GET_PRIV (store); + model = GTK_TREE_MODEL (store); + + iters = contact_list_store_find_contact (store, contact); + for (l = iters; l; l = l->next) { + GtkTreePath *path; + + gtk_tree_store_set (GTK_TREE_STORE (store), l->data, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, active, + -1); + + empathy_debug (DEBUG_DOMAIN, "Set item %s", active ? "active" : "inactive"); + + if (set_changed) { + path = gtk_tree_model_get_path (model, l->data); + gtk_tree_model_row_changed (model, path, l->data); + gtk_tree_path_free (path); + } + } + + g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL); + g_list_free (iters); + +} + +static ShowActiveData * +contact_list_store_contact_active_new (EmpathyContactListStore *store, + EmpathyContact *contact, + gboolean remove) +{ + ShowActiveData *data; + + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' now active, and %s be removed", + empathy_contact_get_name (contact), + remove ? "WILL" : "WILL NOT"); + + data = g_slice_new0 (ShowActiveData); + + data->store = g_object_ref (store); + data->contact = g_object_ref (contact); + data->remove = remove; + + return data; +} + +static void +contact_list_store_contact_active_free (ShowActiveData *data) +{ + g_object_unref (data->contact); + g_object_unref (data->store); + + g_slice_free (ShowActiveData, data); +} + +static gboolean +contact_list_store_contact_active_cb (ShowActiveData *data) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (data->store); + + if (data->remove && + !priv->show_offline && + !empathy_contact_is_online (data->contact)) { + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' active timeout, removing item", + empathy_contact_get_name (data->contact)); + contact_list_store_remove_contact (data->store, data->contact); + } + + empathy_debug (DEBUG_DOMAIN, + "Contact:'%s' no longer active", + empathy_contact_get_name (data->contact)); + + contact_list_store_contact_set_active (data->store, + data->contact, + FALSE, + TRUE); + + contact_list_store_contact_active_free (data); + + return FALSE; +} + +static gboolean +contact_list_store_get_group_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindGroup *fg) +{ + gchar *str; + gboolean is_group; + + /* Groups are only at the top level. */ + if (gtk_tree_path_get_depth (path) != 1) { + return FALSE; + } + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &str, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + -1); + + if (is_group && strcmp (str, fg->name) == 0) { + fg->found = TRUE; + fg->iter = *iter; + } + + g_free (str); + + return fg->found; +} + +static void +contact_list_store_get_group (EmpathyContactListStore *store, + const gchar *name, + GtkTreeIter *iter_group_to_set, + GtkTreeIter *iter_separator_to_set, + gboolean *created) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + GtkTreeIter iter_group; + GtkTreeIter iter_separator; + FindGroup fg; + + priv = GET_PRIV (store); + + memset (&fg, 0, sizeof (fg)); + + fg.name = name; + + model = GTK_TREE_MODEL (store); + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) contact_list_store_get_group_foreach, + &fg); + + if (!fg.found) { + if (created) { + *created = TRUE; + } + + gtk_tree_store_append (GTK_TREE_STORE (store), &iter_group, NULL); + gtk_tree_store_set (GTK_TREE_STORE (store), &iter_group, + EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, name, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, TRUE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + -1); + + if (iter_group_to_set) { + *iter_group_to_set = iter_group; + } + + gtk_tree_store_append (GTK_TREE_STORE (store), + &iter_separator, + &iter_group); + gtk_tree_store_set (GTK_TREE_STORE (store), &iter_separator, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, TRUE, + -1); + + if (iter_separator_to_set) { + *iter_separator_to_set = iter_separator; + } + } else { + if (created) { + *created = FALSE; + } + + if (iter_group_to_set) { + *iter_group_to_set = fg.iter; + } + + iter_separator = fg.iter; + + if (gtk_tree_model_iter_next (model, &iter_separator)) { + gboolean is_separator; + + gtk_tree_model_get (model, &iter_separator, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator, + -1); + + if (is_separator && iter_separator_to_set) { + *iter_separator_to_set = iter_separator; + } + } + } +} + +static guint +contact_list_store_ordered_presence (McPresence state) +{ + switch (state) { + case MC_PRESENCE_UNSET: + case MC_PRESENCE_OFFLINE: + return 5; + case MC_PRESENCE_AVAILABLE: + return 0; + case MC_PRESENCE_AWAY: + return 2; + case MC_PRESENCE_EXTENDED_AWAY: + return 3; + case MC_PRESENCE_HIDDEN: + return 4; + case MC_PRESENCE_DO_NOT_DISTURB: + return 1; + default: + g_return_val_if_reached (6); + } +} + +static gint +contact_list_store_state_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + gint ret_val = 0; + gchar *name_a, *name_b; + gboolean is_separator_a, is_separator_b; + EmpathyContact *contact_a, *contact_b; + guint presence_a, presence_b; + + gtk_tree_model_get (model, iter_a, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a, + -1); + gtk_tree_model_get (model, iter_b, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b, + -1); + + /* Separator or group? */ + if (is_separator_a || is_separator_b) { + if (is_separator_a) { + ret_val = -1; + } else if (is_separator_b) { + ret_val = 1; + } + } else if (!contact_a && contact_b) { + ret_val = 1; + } else if (contact_a && !contact_b) { + ret_val = -1; + } else if (!contact_a && !contact_b) { + /* Handle groups */ + ret_val = g_utf8_collate (name_a, name_b); + } + + if (ret_val) { + goto free_and_out; + } + + /* If we managed to get this far, we can start looking at + * the presences. + */ + presence_a = empathy_contact_get_presence (EMPATHY_CONTACT (contact_a)); + presence_a = contact_list_store_ordered_presence (presence_a); + presence_b = empathy_contact_get_presence (EMPATHY_CONTACT (contact_b)); + presence_b = contact_list_store_ordered_presence (presence_b); + + if (presence_a < presence_b) { + ret_val = -1; + } else if (presence_a > presence_b) { + ret_val = 1; + } else { + /* Fallback: compare by name */ + ret_val = g_utf8_collate (name_a, name_b); + } + +free_and_out: + g_free (name_a); + g_free (name_b); + + if (contact_a) { + g_object_unref (contact_a); + } + + if (contact_b) { + g_object_unref (contact_b); + } + + return ret_val; +} + +static gint +contact_list_store_name_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + gchar *name_a, *name_b; + EmpathyContact *contact_a, *contact_b; + gboolean is_separator_a, is_separator_b; + gint ret_val; + + gtk_tree_model_get (model, iter_a, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a, + -1); + gtk_tree_model_get (model, iter_b, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b, + -1); + + /* If contact is NULL it means it's a group. */ + + if (is_separator_a || is_separator_b) { + if (is_separator_a) { + ret_val = -1; + } else if (is_separator_b) { + ret_val = 1; + } + } else if (!contact_a && contact_b) { + ret_val = 1; + } else if (contact_a && !contact_b) { + ret_val = -1; + } else { + ret_val = g_utf8_collate (name_a, name_b); + } + + g_free (name_a); + g_free (name_b); + + if (contact_a) { + g_object_unref (contact_a); + } + + if (contact_b) { + g_object_unref (contact_b); + } + + return ret_val; +} + +static gboolean +contact_list_store_find_contact_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindContact *fc) +{ + EmpathyContact *contact; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + if (!contact) { + return FALSE; + } + + if (empathy_contact_equal (contact, fc->contact)) { + fc->found = TRUE; + fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter)); + } + g_object_unref (contact); + + return FALSE; +} + +static GList * +contact_list_store_find_contact (EmpathyContactListStore *store, + EmpathyContact *contact) +{ + EmpathyContactListStorePriv *priv; + GtkTreeModel *model; + GList *l = NULL; + FindContact fc; + + priv = GET_PRIV (store); + + memset (&fc, 0, sizeof (fc)); + + fc.contact = contact; + + model = GTK_TREE_MODEL (store); + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) contact_list_store_find_contact_foreach, + &fc); + + if (fc.found) { + l = fc.iters; + } + + return l; +} + +static gboolean +contact_list_store_update_list_mode_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + gboolean show_avatar = FALSE; + + priv = GET_PRIV (store); + + if (priv->show_avatars && !priv->is_compact) { + show_avatar = TRUE; + } + + gtk_tree_store_set (GTK_TREE_STORE (store), iter, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar, + EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact, + -1); + + return FALSE; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-list-store.h b/gnome-2-22/libempathy-gtk/empathy-contact-list-store.h new file mode 100644 index 000000000..6766a4e97 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-list-store.h @@ -0,0 +1,108 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CONTACT_LIST_STORE_H__ +#define __EMPATHY_CONTACT_LIST_STORE_H__ + +#include <gtk/gtktreestore.h> + +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-contact.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CONTACT_LIST_STORE (empathy_contact_list_store_get_type ()) +#define EMPATHY_CONTACT_LIST_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStore)) +#define EMPATHY_CONTACT_LIST_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStoreClass)) +#define EMPATHY_IS_CONTACT_LIST_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST_STORE)) +#define EMPATHY_IS_CONTACT_LIST_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST_STORE)) +#define EMPATHY_CONTACT_LIST_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStoreClass)) + +typedef struct _EmpathyContactListStore EmpathyContactListStore; +typedef struct _EmpathyContactListStoreClass EmpathyContactListStoreClass; + +typedef enum { + EMPATHY_CONTACT_LIST_STORE_SORT_STATE, + EMPATHY_CONTACT_LIST_STORE_SORT_NAME +} EmpathyContactListStoreSort; + +typedef enum { + EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, + EMPATHY_CONTACT_LIST_STORE_COL_STATUS, + EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, + EMPATHY_CONTACT_LIST_STORE_COL_COUNT +} EmpathyContactListStoreCol; + +struct _EmpathyContactListStore { + GtkTreeStore parent; +}; + +struct _EmpathyContactListStoreClass { + GtkTreeStoreClass parent_class; +}; + +GType empathy_contact_list_store_get_type (void) G_GNUC_CONST; +EmpathyContactListStore * empathy_contact_list_store_new (EmpathyContactList *list_iface); +EmpathyContactList * empathy_contact_list_store_get_list_iface (EmpathyContactListStore *store); +gboolean empathy_contact_list_store_get_show_offline (EmpathyContactListStore *store); +void empathy_contact_list_store_set_show_offline (EmpathyContactListStore *store, + gboolean show_offline); +gboolean empathy_contact_list_store_get_show_avatars (EmpathyContactListStore *store); +void empathy_contact_list_store_set_show_avatars (EmpathyContactListStore *store, + gboolean show_avatars); +gboolean empathy_contact_list_store_get_show_groups (EmpathyContactListStore *store); +void empathy_contact_list_store_set_show_groups (EmpathyContactListStore *store, + gboolean show_groups); +gboolean empathy_contact_list_store_get_is_compact (EmpathyContactListStore *store); +void empathy_contact_list_store_set_is_compact (EmpathyContactListStore *store, + gboolean is_compact); +EmpathyContactListStoreSort empathy_contact_list_store_get_sort_criterium (EmpathyContactListStore *store); +void empathy_contact_list_store_set_sort_criterium (EmpathyContactListStore *store, + EmpathyContactListStoreSort sort_criterium); +gboolean empathy_contact_list_store_row_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data); +gchar * empathy_contact_list_store_get_parent_group (GtkTreeModel *model, + GtkTreePath *path, + gboolean *path_is_group); +gboolean empathy_contact_list_store_search_equal_func (GtkTreeModel *model, + gint column, + const gchar *key, + GtkTreeIter *iter, + gpointer search_data); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_LIST_STORE_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-list-view.c b/gnome-2-22/libempathy-gtk/empathy-contact-list-view.c new file mode 100644 index 000000000..b12387419 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-list-view.c @@ -0,0 +1,1502 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libtelepathy/tp-helpers.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-log-manager.h> +#include <libempathy/empathy-tp-group.h> +#include <libempathy/empathy-contact-groups.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-contact-list-view.h" +#include "empathy-contact-list-store.h" +#include "empathy-images.h" +#include "empathy-cell-renderer-expander.h" +#include "empathy-cell-renderer-text.h" +#include "empathy-cell-renderer-activatable.h" +#include "empathy-ui-utils.h" +#include "empathy-contact-dialogs.h" +//#include "empathy-chat-invite.h" +//#include "empathy-ft-window.h" +#include "empathy-log-window.h" +#include "empathy-gtk-enum-types.h" +#include "empathy-gtk-marshal.h" + +#define DEBUG_DOMAIN "ContactListView" + +/* Flashing delay for icons (milliseconds). */ +#define FLASH_TIMEOUT 500 + +/* Active users are those which have recently changed state + * (e.g. online, offline or from normal to a busy state). + */ + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewPriv)) + +typedef struct { + EmpathyContactListStore *store; + GtkUIManager *ui; + GtkTreeRowReference *drag_row; + EmpathyContactListFeatures features; +} EmpathyContactListViewPriv; + +typedef struct { + EmpathyContactListView *view; + GtkTreePath *path; + guint timeout_id; +} DragMotionData; + +typedef struct { + EmpathyContactListView *view; + EmpathyContact *contact; + gboolean remove; +} ShowActiveData; + +static void empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass); +static void empathy_contact_list_view_init (EmpathyContactListView *list); +static void contact_list_view_finalize (GObject *object); +static void contact_list_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void contact_list_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void contact_list_view_setup (EmpathyContactListView *view); +static void contact_list_view_row_has_child_toggled_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyContactListView *view); +static void contact_list_view_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time); +static gboolean contact_list_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static gboolean contact_list_view_drag_motion_cb (DragMotionData *data); +static void contact_list_view_drag_begin (GtkWidget *widget, + GdkDragContext *context); +static void contact_list_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection, + guint info, + guint time); +static void contact_list_view_drag_end (GtkWidget *widget, + GdkDragContext *context); +static gboolean contact_list_view_drag_drop (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + guint time); +static void contact_list_view_cell_set_background (EmpathyContactListView *view, + GtkCellRenderer *cell, + gboolean is_group, + gboolean is_active); +static void contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view); +#ifdef HAVE_VOIP +static void contact_list_view_voip_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view); +#endif +static void contact_list_view_avatar_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view); +static void contact_list_view_text_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view); +static void contact_list_view_expander_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view); +static GtkWidget * contact_list_view_get_contact_menu (EmpathyContactListView *view, + gboolean can_send_file, + gboolean can_show_log, + gboolean can_voip); +static gboolean contact_list_view_button_press_event_cb (EmpathyContactListView *view, + GdkEventButton *event, + gpointer user_data); +static void contact_list_view_row_activated_cb (EmpathyContactListView *view, + GtkTreePath *path, + GtkTreeViewColumn *col, + gpointer user_data); +#ifdef HAVE_VOIP +static void contact_list_view_voip_activated_cb (EmpathyCellRendererActivatable *cell, + const gchar *path_string, + EmpathyContactListView *view); +#endif +static void contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data); +static void contact_list_view_action_cb (GtkAction *action, + EmpathyContactListView *view); +static void contact_list_view_voip_activated (EmpathyContactListView *view, + EmpathyContact *contact); + +enum { + PROP_0, + PROP_FEATURES +}; + +static const GtkActionEntry entries[] = { + { "ContactMenu", NULL, + N_("_Contact"), NULL, NULL, + NULL + }, + { "GroupMenu", NULL, + N_("_Group"),NULL, NULL, + NULL + }, + { "Chat", EMPATHY_IMAGE_MESSAGE, + N_("_Chat"), NULL, N_("Chat with contact"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Information", EMPATHY_IMAGE_CONTACT_INFORMATION, + N_("Infor_mation"), "<control>I", N_("View contact information"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Rename", NULL, + N_("Re_name"), NULL, N_("Rename"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Edit", GTK_STOCK_EDIT, + N_("_Edit"), NULL, N_("Edit the groups and name for this contact"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Remove", GTK_STOCK_REMOVE, + N_("_Remove"), NULL, N_("Remove contact"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Invite", EMPATHY_IMAGE_GROUP_MESSAGE, + N_("_Invite to Chat Room"), NULL, N_("Invite to a currently open chat room"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "SendFile", NULL, + N_("_Send File..."), NULL, N_("Send a file"), + G_CALLBACK (contact_list_view_action_cb) + }, + { "Log", EMPATHY_IMAGE_LOG, + N_("_View Previous Conversations"), NULL, N_("View previous conversations with this contact"), + G_CALLBACK (contact_list_view_action_cb) + }, +#ifdef HAVE_VOIP + { "Call", EMPATHY_IMAGE_VOIP, + N_("_Call"), NULL, N_("Start a voice or video conversation with this contact"), + G_CALLBACK (contact_list_view_action_cb) + }, +#endif +}; + +static guint n_entries = G_N_ELEMENTS (entries); + +static const gchar *ui_info = + "<ui>" + " <popup name='Contact'>" + " <menuitem action='Chat'/>" +#ifdef HAVE_VOIP + " <menuitem action='Call'/>" +#endif + " <menuitem action='Log'/>" + " <menuitem action='SendFile'/>" + " <separator/>" + " <menuitem action='Invite'/>" + " <separator/>" + " <menuitem action='Edit'/>" + " <menuitem action='Remove'/>" + " <separator/>" + " <menuitem action='Information'/>" + " </popup>" + " <popup name='Group'>" + " <menuitem action='Rename'/>" + " <menuitem action='Remove'/>" + " </popup>" + "</ui>"; + +enum DndDragType { + DND_DRAG_TYPE_CONTACT_ID, + DND_DRAG_TYPE_URL, + DND_DRAG_TYPE_STRING, +}; + +static const GtkTargetEntry drag_types_dest[] = { + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, + { "text/uri-list", 0, DND_DRAG_TYPE_URL }, + { "text/plain", 0, DND_DRAG_TYPE_STRING }, + { "STRING", 0, DND_DRAG_TYPE_STRING }, +}; + +static const GtkTargetEntry drag_types_source[] = { + { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID }, +}; + +static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)]; +static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)]; + +enum { + DRAG_CONTACT_RECEIVED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (EmpathyContactListView, empathy_contact_list_view, GTK_TYPE_TREE_VIEW); + +static void +empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = contact_list_view_finalize; + object_class->get_property = contact_list_view_get_property; + object_class->set_property = contact_list_view_set_property; + + widget_class->drag_data_received = contact_list_view_drag_data_received; + widget_class->drag_drop = contact_list_view_drag_drop; + widget_class->drag_begin = contact_list_view_drag_begin; + widget_class->drag_data_get = contact_list_view_drag_data_get; + widget_class->drag_end = contact_list_view_drag_end; + /* FIXME: noticed but when you drag the row over the treeview + * fast, it seems to stop redrawing itself, if we don't + * connect this signal, all is fine. + */ + widget_class->drag_motion = contact_list_view_drag_motion; + + signals[DRAG_CONTACT_RECEIVED] = + g_signal_new ("drag-contact-received", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + _empathy_gtk_marshal_VOID__OBJECT_STRING_STRING, + G_TYPE_NONE, + 3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_FEATURES, + g_param_spec_flags ("features", + "Features of the view", + "Falgs for all enabled features", + EMPATHY_TYPE_CONTACT_LIST_FEATURES, + 0, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyContactListViewPriv)); +} + +static void +empathy_contact_list_view_init (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv; + GtkActionGroup *action_group; + GError *error = NULL; + + priv = GET_PRIV (view); + + /* Get saved group states. */ + empathy_contact_groups_get_all (); + + /* Set up UI Manager */ + priv->ui = gtk_ui_manager_new (); + + action_group = gtk_action_group_new ("Actions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions (action_group, entries, n_entries, view); + gtk_ui_manager_insert_action_group (priv->ui, action_group, 0); + + if (!gtk_ui_manager_add_ui_from_string (priv->ui, ui_info, -1, &error)) { + g_warning ("Could not build contact menus from string:'%s'", error->message); + g_error_free (error); + } + + g_object_unref (action_group); + + gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), + empathy_contact_list_store_row_separator_func, + NULL, NULL); + + /* Connect to tree view signals rather than override. */ + g_signal_connect (view, + "button-press-event", + G_CALLBACK (contact_list_view_button_press_event_cb), + NULL); + g_signal_connect (view, + "row-activated", + G_CALLBACK (contact_list_view_row_activated_cb), + NULL); + g_signal_connect (view, + "row-expanded", + G_CALLBACK (contact_list_view_row_expand_or_collapse_cb), + GINT_TO_POINTER (TRUE)); + g_signal_connect (view, + "row-collapsed", + G_CALLBACK (contact_list_view_row_expand_or_collapse_cb), + GINT_TO_POINTER (FALSE)); +} + +static void +contact_list_view_finalize (GObject *object) +{ + EmpathyContactListViewPriv *priv; + + priv = GET_PRIV (object); + + if (priv->ui) { + g_object_unref (priv->ui); + } + if (priv->store) { + g_object_unref (priv->store); + } + + G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object); +} + +static void +contact_list_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyContactListViewPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_FEATURES: + g_value_set_flags (value, priv->features); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +contact_list_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyContactListView *view = EMPATHY_CONTACT_LIST_VIEW (object); + EmpathyContactListViewPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_FEATURES: + empathy_contact_list_view_set_features (view, g_value_get_flags (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +EmpathyContactListView * +empathy_contact_list_view_new (EmpathyContactListStore *store, + EmpathyContactListFeatures features) +{ + EmpathyContactListView *view; + EmpathyContactListViewPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), NULL); + + view = g_object_new (EMPATHY_TYPE_CONTACT_LIST_VIEW, + "features", features, + NULL); + + priv = GET_PRIV (view); + priv->store = g_object_ref (store); + contact_list_view_setup (EMPATHY_CONTACT_LIST_VIEW (view)); + + return view; +} + +void +empathy_contact_list_view_set_features (EmpathyContactListView *view, + EmpathyContactListFeatures features) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + + g_return_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view)); + + priv->features = features; + + /* Update DnD source/dest */ + if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DRAG) { + gtk_drag_source_set (GTK_WIDGET (view), + GDK_BUTTON1_MASK, + drag_types_source, + G_N_ELEMENTS (drag_types_source), + GDK_ACTION_MOVE | GDK_ACTION_COPY); + } else { + gtk_drag_source_unset (GTK_WIDGET (view)); + + } + + if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DROP) { + gtk_drag_dest_set (GTK_WIDGET (view), + GTK_DEST_DEFAULT_ALL, + drag_types_dest, + G_N_ELEMENTS (drag_types_dest), + GDK_ACTION_MOVE | GDK_ACTION_COPY); + } else { + /* FIXME: URI could still be droped depending on FT feature */ + gtk_drag_dest_unset (GTK_WIDGET (view)); + } + + g_object_notify (G_OBJECT (view), "features"); +} + +EmpathyContactListFeatures +empathy_contact_list_view_get_features (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), FALSE); + + return priv->features; +} + +EmpathyContact * +empathy_contact_list_view_get_selected (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv; + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyContact *contact; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); + + priv = GET_PRIV (view); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return NULL; + } + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + return contact; +} + +gchar * +empathy_contact_list_view_get_selected_group (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv; + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreeModel *model; + gboolean is_group; + gchar *name; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); + + priv = GET_PRIV (view); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return NULL; + } + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + if (!is_group) { + g_free (name); + return NULL; + } + + return name; +} + +static void +contact_list_view_setup (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv; + GtkCellRenderer *cell; + GtkTreeViewColumn *col; + gint i; + + priv = GET_PRIV (view); + + gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view), + empathy_contact_list_store_search_equal_func, + NULL, NULL); + + g_signal_connect (priv->store, "row-has-child-toggled", + G_CALLBACK (contact_list_view_row_has_child_toggled_cb), + view); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), + GTK_TREE_MODEL (priv->store)); + + /* Setup view */ + g_object_set (view, + "headers-visible", FALSE, + "reorderable", TRUE, + "show-expanders", FALSE, + NULL); + + col = gtk_tree_view_column_new (); + + /* State */ + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func, + view, NULL); + + g_object_set (cell, + "xpad", 5, + "ypad", 1, + "visible", FALSE, + NULL); + + /* Name */ + cell = empathy_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, TRUE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_text_cell_data_func, + view, NULL); + + gtk_tree_view_column_add_attribute (col, cell, + "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME); + gtk_tree_view_column_add_attribute (col, cell, + "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS); + gtk_tree_view_column_add_attribute (col, cell, + "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP); + +#ifdef HAVE_VOIP + /* Voip Capability Icon */ + cell = empathy_cell_renderer_activatable_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_voip_cell_data_func, + view, NULL); + + g_object_set (cell, + "visible", FALSE, + NULL); + + g_signal_connect (cell, "path-activated", + G_CALLBACK (contact_list_view_voip_activated_cb), + view); +#endif + + /* Avatar */ + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func, + view, NULL); + + g_object_set (cell, + "xpad", 0, + "ypad", 0, + "visible", FALSE, + "width", 32, + "height", 32, + NULL); + + /* Expander */ + cell = empathy_cell_renderer_expander_new (); + gtk_tree_view_column_pack_end (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func, + view, NULL); + + /* Actually add the column now we have added all cell renderers */ + gtk_tree_view_append_column (GTK_TREE_VIEW (view), col); + + /* Drag & Drop. */ + for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) { + drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target, + FALSE); + } + + for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) { + drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target, + FALSE); + } +} + +static void +contact_list_view_row_has_child_toggled_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + gboolean is_group = FALSE; + gchar *name = NULL; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + if (!is_group || G_STR_EMPTY (name)) { + g_free (name); + return; + } + + if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) || + empathy_contact_group_get_expanded (name)) { + g_signal_handlers_block_by_func (view, + contact_list_view_row_expand_or_collapse_cb, + GINT_TO_POINTER (TRUE)); + gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE); + g_signal_handlers_unblock_by_func (view, + contact_list_view_row_expand_or_collapse_cb, + GINT_TO_POINTER (TRUE)); + } else { + g_signal_handlers_block_by_func (view, + contact_list_view_row_expand_or_collapse_cb, + GINT_TO_POINTER (FALSE)); + gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path); + g_signal_handlers_unblock_by_func (view, + contact_list_view_row_expand_or_collapse_cb, + GINT_TO_POINTER (FALSE)); + } + + g_free (name); +} + +static void +contact_list_view_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time) +{ + EmpathyContactListViewPriv *priv; + EmpathyContactList *list; + EmpathyContactFactory *factory; + McAccount *account; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeViewDropPosition position; + EmpathyContact *contact = NULL; + const gchar *id; + gchar **strv; + gchar *new_group = NULL; + gchar *old_group = NULL; + gboolean is_row; + + priv = GET_PRIV (widget); + + id = (const gchar*) selection->data; + empathy_debug (DEBUG_DOMAIN, "Received %s%s drag & drop contact from roster with id:'%s'", + context->action == GDK_ACTION_MOVE ? "move" : "", + context->action == GDK_ACTION_COPY ? "copy" : "", + id); + + strv = g_strsplit (id, "/", 2); + factory = empathy_contact_factory_new (); + account = mc_account_lookup (strv[0]); + if (account) { + contact = empathy_contact_factory_get_from_id (factory, + account, + strv[1]); + g_object_unref (account); + } + g_object_unref (factory); + g_strfreev (strv); + + if (!contact) { + empathy_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop"); + return; + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + /* Get source group information. */ + if (priv->drag_row) { + path = gtk_tree_row_reference_get_path (priv->drag_row); + if (path) { + old_group = empathy_contact_list_store_get_parent_group (model, path, NULL); + gtk_tree_path_free (path); + } + } + + /* Get destination group information. */ + is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), + x, + y, + &path, + &position); + + if (is_row) { + new_group = empathy_contact_list_store_get_parent_group (model, path, NULL); + gtk_tree_path_free (path); + } + + empathy_debug (DEBUG_DOMAIN, + "contact %s (%d) dragged from '%s' to '%s'", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + old_group, new_group); + + list = empathy_contact_list_store_get_list_iface (priv->store); + if (new_group) { + empathy_contact_list_add_to_group (list, contact, new_group); + } + if (old_group && context->action == GDK_ACTION_MOVE) { + empathy_contact_list_remove_from_group (list, contact, old_group); + } + + g_free (old_group); + g_free (new_group); + + gtk_drag_finish (context, TRUE, FALSE, GDK_CURRENT_TIME); +} + +static gboolean +contact_list_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + static DragMotionData *dm = NULL; + GtkTreePath *path; + gboolean is_row; + gboolean is_different = FALSE; + gboolean cleanup = TRUE; + + is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + x, + y, + &path, + NULL, + NULL, + NULL); + + cleanup &= (!dm); + + if (is_row) { + cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0); + is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0)); + } else { + cleanup &= FALSE; + } + + if (!is_different && !cleanup) { + return TRUE; + } + + if (dm) { + gtk_tree_path_free (dm->path); + if (dm->timeout_id) { + g_source_remove (dm->timeout_id); + } + + g_free (dm); + + dm = NULL; + } + + if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) { + dm = g_new0 (DragMotionData, 1); + + dm->view = EMPATHY_CONTACT_LIST_VIEW (widget); + dm->path = gtk_tree_path_copy (path); + + dm->timeout_id = g_timeout_add_seconds (1, + (GSourceFunc) contact_list_view_drag_motion_cb, + dm); + } + + return TRUE; +} + +static gboolean +contact_list_view_drag_motion_cb (DragMotionData *data) +{ + gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view), + data->path, + FALSE); + + data->timeout_id = 0; + + return FALSE; +} + +static void +contact_list_view_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + EmpathyContactListViewPriv *priv; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + + priv = GET_PRIV (widget); + + GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget, + context); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return; + } + + path = gtk_tree_model_get_path (model, &iter); + priv->drag_row = gtk_tree_row_reference_new (model, path); + gtk_tree_path_free (path); +} + +static void +contact_list_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection, + guint info, + guint time) +{ + EmpathyContactListViewPriv *priv; + GtkTreePath *src_path; + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyContact *contact; + McAccount *account; + const gchar *contact_id; + const gchar *account_id; + gchar *str; + + priv = GET_PRIV (widget); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + if (!priv->drag_row) { + return; + } + + src_path = gtk_tree_row_reference_get_path (priv->drag_row); + if (!src_path) { + return; + } + + if (!gtk_tree_model_get_iter (model, &iter, src_path)) { + gtk_tree_path_free (src_path); + return; + } + + gtk_tree_path_free (src_path); + + contact = empathy_contact_list_view_get_selected (EMPATHY_CONTACT_LIST_VIEW (widget)); + if (!contact) { + return; + } + + account = empathy_contact_get_account (contact); + account_id = mc_account_get_unique_name (account); + contact_id = empathy_contact_get_id (contact); + g_object_unref (contact); + str = g_strconcat (account_id, "/", contact_id, NULL); + + switch (info) { + case DND_DRAG_TYPE_CONTACT_ID: + gtk_selection_data_set (selection, drag_atoms_source[info], 8, + (guchar*)str, strlen (str) + 1); + break; + } + + g_free (str); +} + +static void +contact_list_view_drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + EmpathyContactListViewPriv *priv; + + priv = GET_PRIV (widget); + + GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget, + context); + + if (priv->drag_row) { + gtk_tree_row_reference_free (priv->drag_row); + priv->drag_row = NULL; + } +} + +static gboolean +contact_list_view_drag_drop (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + guint time) +{ + return FALSE; +} + +static void +contact_list_view_cell_set_background (EmpathyContactListView *view, + GtkCellRenderer *cell, + gboolean is_group, + gboolean is_active) +{ + GdkColor color; + GtkStyle *style; + + style = gtk_widget_get_style (GTK_WIDGET (view)); + + if (!is_group && is_active) { + color = style->bg[GTK_STATE_SELECTED]; + + /* Here we take the current theme colour and add it to + * the colour for white and average the two. This + * gives a colour which is inline with the theme but + * slightly whiter. + */ + color.red = (color.red + (style->white).red) / 2; + color.green = (color.green + (style->white).green) / 2; + color.blue = (color.blue + (style->white).blue) / 2; + + g_object_set (cell, + "cell-background-gdk", &color, + NULL); + } else { + g_object_set (cell, + "cell-background-gdk", NULL, + NULL); + } +} + +static void +contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + gchar *icon_name; + gboolean is_group; + gboolean is_active; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active, + EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name, + -1); + + g_object_set (cell, + "visible", !is_group, + "icon-name", icon_name, + NULL); + + g_free (icon_name); + + contact_list_view_cell_set_background (view, cell, is_group, is_active); +} + +#ifdef HAVE_VOIP +static void +contact_list_view_voip_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + gboolean is_group; + gboolean is_active; + gboolean can_voip; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, &can_voip, + -1); + + g_object_set (cell, + "visible", !is_group && can_voip, + "icon-name", EMPATHY_IMAGE_VOIP, + NULL); + + contact_list_view_cell_set_background (view, cell, is_group, is_active); +} +#endif + +static void +contact_list_view_avatar_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + GdkPixbuf *pixbuf; + gboolean show_avatar; + gboolean is_group; + gboolean is_active; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf, + EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active, + -1); + + g_object_set (cell, + "visible", !is_group && show_avatar, + "pixbuf", pixbuf, + NULL); + + if (pixbuf) { + g_object_unref (pixbuf); + } + + contact_list_view_cell_set_background (view, cell, is_group, is_active); +} + +static void +contact_list_view_text_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + gboolean is_group; + gboolean is_active; + gboolean show_status; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active, + EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, &show_status, + -1); + + g_object_set (cell, + "show-status", show_status, + NULL); + + contact_list_view_cell_set_background (view, cell, is_group, is_active); +} + +static void +contact_list_view_expander_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + gboolean is_group; + gboolean is_active; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active, + -1); + + if (gtk_tree_model_iter_has_child (model, iter)) { + GtkTreePath *path; + gboolean row_expanded; + + path = gtk_tree_model_get_path (model, iter); + row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path); + gtk_tree_path_free (path); + + g_object_set (cell, + "visible", TRUE, + "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED, + NULL); + } else { + g_object_set (cell, "visible", FALSE, NULL); + } + + contact_list_view_cell_set_background (view, cell, is_group, is_active); +} + +static GtkWidget * +contact_list_view_get_contact_menu (EmpathyContactListView *view, + gboolean can_send_file, + gboolean can_show_log, + gboolean can_voip) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + GtkAction *action; + + if (!(priv->features & (EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_EDIT | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_REMOVE))) { + return NULL; + } + + /* Sort out sensitive/visible items */ + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Chat"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT); + +#ifdef HAVE_VOIP + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Call"); + gtk_action_set_sensitive (action, can_voip); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL); +#endif + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Log"); + gtk_action_set_sensitive (action, can_show_log); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG); + + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/SendFile"); + gtk_action_set_visible (action, can_send_file && (priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT)); + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Invite"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE); + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Edit"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_EDIT); + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Information"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO); + + action = gtk_ui_manager_get_action (priv->ui, "/Contact/Remove"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_REMOVE); + + return gtk_ui_manager_get_widget (priv->ui, "/Contact"); +} + +GtkWidget * +empathy_contact_list_view_get_group_menu (EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + GtkAction *action; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); + + if (!(priv->features & (EMPATHY_CONTACT_LIST_FEATURE_GROUPS_RENAME | + EMPATHY_CONTACT_LIST_FEATURE_GROUPS_REMOVE))) { + return NULL; + } + + action = gtk_ui_manager_get_action (priv->ui, "/Group/Rename"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_RENAME); + + action = gtk_ui_manager_get_action (priv->ui, "/Group/Remove"); + gtk_action_set_visible (action, priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_REMOVE); + + return gtk_ui_manager_get_widget (priv->ui, "/Group"); +} + +GtkWidget * +empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view, + EmpathyContact *contact) +{ + EmpathyLogManager *log_manager; + gboolean can_show_log; + gboolean can_send_file; + gboolean can_voip; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + log_manager = empathy_log_manager_new (); + can_show_log = empathy_log_manager_exists (log_manager, + empathy_contact_get_account (contact), + empathy_contact_get_id (contact), + FALSE); + g_object_unref (log_manager); + can_send_file = FALSE; + can_voip = empathy_contact_can_voip (contact); + + return contact_list_view_get_contact_menu (view, + can_send_file, + can_show_log, + can_voip); +} + +static gboolean +contact_list_view_button_press_event_cb (EmpathyContactListView *view, + GdkEventButton *event, + gpointer user_data) +{ + EmpathyContactListViewPriv *priv; + EmpathyContact *contact; + GtkTreePath *path; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean row_exists; + GtkWidget *menu; + + priv = GET_PRIV (view); + + if (event->button != 3) { + return FALSE; + } + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + + gtk_widget_grab_focus (GTK_WIDGET (view)); + + row_exists = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), + event->x, event->y, + &path, + NULL, NULL, NULL); + if (!row_exists) { + return FALSE; + } + + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, path); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + if (contact) { + menu = empathy_contact_list_view_get_contact_menu (view, contact); + g_object_unref (contact); + } else { + menu = empathy_contact_list_view_get_group_menu (view); + } + + if (!menu) { + return FALSE; + } + + gtk_widget_show (menu); + + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, NULL, NULL, + event->button, event->time); + + return TRUE; +} + +static void +contact_list_view_row_activated_cb (EmpathyContactListView *view, + GtkTreePath *path, + GtkTreeViewColumn *col, + gpointer user_data) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + EmpathyContact *contact; + GtkTreeModel *model; + GtkTreeIter iter; + + if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT)) { + return; + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + if (contact) { + empathy_chat_with_contact (contact); + g_object_unref (contact); + } +} + +#ifdef HAVE_VOIP +static void +contact_list_view_voip_activated_cb (EmpathyCellRendererActivatable *cell, + const gchar *path_string, + EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + GtkTreeModel *model; + GtkTreeIter iter; + EmpathyContact *contact; + + if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL)) { + return; + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string)) { + return; + } + + gtk_tree_model_get (model, &iter, + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, + -1); + + if (contact) { + contact_list_view_voip_activated (view, contact); + g_object_unref (contact); + } +} +#endif + + +static void +contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + EmpathyContactListViewPriv *priv = GET_PRIV (view); + GtkTreeModel *model; + gchar *name; + gboolean expanded; + + if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE)) { + return; + } + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + expanded = GPOINTER_TO_INT (user_data); + empathy_contact_group_set_expanded (name, expanded); + + g_free (name); +} + +static void +contact_list_view_action_cb (GtkAction *action, + EmpathyContactListView *view) +{ + EmpathyContactListViewPriv *priv; + EmpathyContact *contact; + const gchar *name; + gchar *group; + GtkWindow *parent; + + priv = GET_PRIV (view); + + name = gtk_action_get_name (action); + if (!name) { + return; + } + + empathy_debug (DEBUG_DOMAIN, "Action:'%s' activated", name); + + contact = empathy_contact_list_view_get_selected (view); + group = empathy_contact_list_view_get_selected_group (view); + parent = empathy_get_toplevel_window (GTK_WIDGET (view)); + + if (contact && strcmp (name, "Chat") == 0) { + empathy_chat_with_contact (contact); + } + else if (contact && strcmp (name, "Call") == 0) { + contact_list_view_voip_activated (view, contact); + } + else if (contact && strcmp (name, "Information") == 0) { + empathy_contact_information_dialog_show (contact, parent, FALSE, FALSE); + } + else if (contact && strcmp (name, "Edit") == 0) { + empathy_contact_information_dialog_show (contact, parent, TRUE, FALSE); + } + else if (contact && strcmp (name, "Remove") == 0) { + /* FIXME: Ask for confirmation */ + EmpathyContactList *list; + + list = empathy_contact_list_store_get_list_iface (priv->store); + empathy_contact_list_remove (list, contact, + _("Sorry, I don't want you in my contact list anymore.")); + } + else if (contact && strcmp (name, "Invite") == 0) { + } + else if (contact && strcmp (name, "SendFile") == 0) { + } + else if (contact && strcmp (name, "Log") == 0) { + empathy_log_window_show (empathy_contact_get_account (contact), + empathy_contact_get_id (contact), + FALSE, + parent); + } + else if (group && strcmp (name, "Rename") == 0) { + } + else if (group && strcmp (name, "Remove") == 0) { + EmpathyContactList *list; + + list = empathy_contact_list_store_get_list_iface (priv->store); + empathy_contact_list_remove_group (list, group); + } + + g_free (group); + if (contact) { + g_object_unref (contact); + } +} + +static void +contact_list_view_voip_activated (EmpathyContactListView *view, + EmpathyContact *contact) +{ + empathy_call_with_contact (contact); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-list-view.h b/gnome-2-22/libempathy-gtk/empathy-contact-list-view.h new file mode 100644 index 000000000..13a685d33 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-list-view.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CONTACT_LIST_VIEW_H__ +#define __EMPATHY_CONTACT_LIST_VIEW_H__ + +#include <gtk/gtktreeview.h> + +#include <libempathy/empathy-contact.h> + +#include "empathy-contact-list-store.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_CONTACT_LIST_VIEW (empathy_contact_list_view_get_type ()) +#define EMPATHY_CONTACT_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListView)) +#define EMPATHY_CONTACT_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewClass)) +#define EMPATHY_IS_CONTACT_LIST_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CONTACT_LIST_VIEW)) +#define EMPATHY_IS_CONTACT_LIST_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CONTACT_LIST_VIEW)) +#define EMPATHY_CONTACT_LIST_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewClass)) + +typedef struct _EmpathyContactListView EmpathyContactListView; +typedef struct _EmpathyContactListViewClass EmpathyContactListViewClass; + +typedef enum { + EMPATHY_CONTACT_LIST_FEATURE_NONE = 0, + EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE = 1 << 0, + EMPATHY_CONTACT_LIST_FEATURE_GROUPS_RENAME = 1 << 1, + EMPATHY_CONTACT_LIST_FEATURE_GROUPS_REMOVE = 1 << 2, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT = 1 << 3, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL = 1 << 4, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG = 1 << 5, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT = 1 << 6, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE = 1 << 7, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_EDIT = 1 << 8, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO = 1 << 9, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_REMOVE = 1 << 10, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DROP = 1 << 11, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DRAG = 1 << 12, + EMPATHY_CONTACT_LIST_FEATURE_ALL = (1 << 13) - 1, +} EmpathyContactListFeatures; + +struct _EmpathyContactListView { + GtkTreeView parent; +}; + +struct _EmpathyContactListViewClass { + GtkTreeViewClass parent_class; +}; + +GType empathy_contact_list_view_get_type (void) G_GNUC_CONST; +EmpathyContactListView * empathy_contact_list_view_new (EmpathyContactListStore *store, + EmpathyContactListFeatures features); +void empathy_contact_list_view_set_features (EmpathyContactListView *view, + EmpathyContactListFeatures features); +EmpathyContactListFeatures empathy_contact_list_view_get_features (EmpathyContactListView *view); +EmpathyContact * empathy_contact_list_view_get_selected (EmpathyContactListView *view); +gchar * empathy_contact_list_view_get_selected_group (EmpathyContactListView *view); +GtkWidget * empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view, + EmpathyContact *contact); +GtkWidget * empathy_contact_list_view_get_group_menu (EmpathyContactListView *view); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_LIST_VIEW_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-widget.c b/gnome-2-22/libempathy-gtk/empathy-contact-widget.c new file mode 100644 index 000000000..0c3f40dc1 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-widget.c @@ -0,0 +1,946 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mc-account.h> + +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-contact-widget.h" +#include "empathy-account-chooser.h" +#include "empathy-avatar-chooser.h" +#include "empathy-avatar-image.h" +#include "empathy-ui-utils.h" + +/* Delay before updating the widget when the id entry changed (seconds) */ +#define ID_CHANGED_TIMEOUT 1 + +typedef struct { + EmpathyContactFactory *factory; + EmpathyContactManager *manager; + EmpathyContact *contact; + EmpathyContactWidgetFlags flags; + GtkCellRenderer *renderer; + guint widget_id_timeout; + + GtkWidget *vbox_contact_widget; + + /* Contact */ + GtkWidget *vbox_contact; + GtkWidget *widget_avatar; + GtkWidget *widget_account; + GtkWidget *widget_id; + GtkWidget *widget_alias; + GtkWidget *label_alias; + GtkWidget *entry_alias; + GtkWidget *hbox_presence; + GtkWidget *image_state; + GtkWidget *label_status; + GtkWidget *table_contact; + GtkWidget *vbox_avatar; + + /* Groups */ + GtkWidget *vbox_groups; + GtkWidget *entry_group; + GtkWidget *button_group; + GtkWidget *treeview_groups; + + /* Details */ + GtkWidget *vbox_details; + GtkWidget *table_details; + GtkWidget *hbox_details_requested; + + /* Client */ + GtkWidget *vbox_client; + GtkWidget *table_client; + GtkWidget *hbow_client_requested; +} EmpathyContactWidget; + +typedef struct { + EmpathyContactWidget *information; + const gchar *name; + gboolean found; + GtkTreeIter found_iter; +} FindName; + +static void contact_widget_destroy_cb (GtkWidget *widget, + EmpathyContactWidget *information); +static void contact_widget_remove_contact (EmpathyContactWidget *information); +static void contact_widget_set_contact (EmpathyContactWidget *information, + EmpathyContact *contact); +static void contact_widget_contact_setup (EmpathyContactWidget *information); +static void contact_widget_contact_update (EmpathyContactWidget *information); +static void contact_widget_change_contact (EmpathyContactWidget *information); +static void contact_widget_avatar_changed_cb (EmpathyAvatarChooser *chooser, + EmpathyContactWidget *information); +static void contact_widget_account_changed_cb (GtkComboBox *widget, + EmpathyContactWidget *information); +static gboolean contact_widget_id_focus_out_cb (GtkWidget *widget, + GdkEventFocus *event, + EmpathyContactWidget *information); +static gboolean contact_widget_entry_alias_focus_event_cb (GtkEditable *editable, + GdkEventFocus *event, + EmpathyContactWidget *information); +static void contact_widget_name_notify_cb (EmpathyContactWidget *information); +static void contact_widget_presence_notify_cb (EmpathyContactWidget *information); +static void contact_widget_avatar_notify_cb (EmpathyContactWidget *information); +static void contact_widget_groups_setup (EmpathyContactWidget *information); +static void contact_widget_groups_update (EmpathyContactWidget *information); +static void contact_widget_model_setup (EmpathyContactWidget *information); +static void contact_widget_model_populate_columns (EmpathyContactWidget *information); +static void contact_widget_groups_populate_data (EmpathyContactWidget *information); +static void contact_widget_groups_notify_cb (EmpathyContactWidget *information); +static gboolean contact_widget_model_find_name (EmpathyContactWidget *information, + const gchar *name, + GtkTreeIter *iter); +static gboolean contact_widget_model_find_name_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindName *data); +static void contact_widget_cell_toggled (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyContactWidget *information); +static void contact_widget_entry_group_changed_cb (GtkEditable *editable, + EmpathyContactWidget *information); +static void contact_widget_entry_group_activate_cb (GtkEntry *entry, + EmpathyContactWidget *information); +static void contact_widget_button_group_clicked_cb (GtkButton *button, + EmpathyContactWidget *information); +static void contact_widget_details_setup (EmpathyContactWidget *information); +static void contact_widget_details_update (EmpathyContactWidget *information); +static void contact_widget_client_setup (EmpathyContactWidget *information); +static void contact_widget_client_update (EmpathyContactWidget *information); + +enum { + COL_NAME, + COL_ENABLED, + COL_EDITABLE, + COL_COUNT +}; + +GtkWidget * +empathy_contact_widget_new (EmpathyContact *contact, + EmpathyContactWidgetFlags flags) +{ + EmpathyContactWidget *information; + GladeXML *glade; + + information = g_slice_new0 (EmpathyContactWidget); + information->flags = flags; + information->factory = empathy_contact_factory_new (); + + glade = empathy_glade_get_file ("empathy-contact-widget.glade", + "vbox_contact_widget", + NULL, + "vbox_contact_widget", &information->vbox_contact_widget, + "vbox_contact", &information->vbox_contact, + "hbox_presence", &information->hbox_presence, + "label_alias", &information->label_alias, + "image_state", &information->image_state, + "label_status", &information->label_status, + "table_contact", &information->table_contact, + "vbox_avatar", &information->vbox_avatar, + "vbox_groups", &information->vbox_groups, + "entry_group", &information->entry_group, + "button_group", &information->button_group, + "treeview_groups", &information->treeview_groups, + "vbox_details", &information->vbox_details, + "table_details", &information->table_details, + "hbox_details_requested", &information->hbox_details_requested, + "vbox_client", &information->vbox_client, + "table_client", &information->table_client, + "hbox_client_requested", &information->hbow_client_requested, + NULL); + + empathy_glade_connect (glade, + information, + "vbox_contact_widget", "destroy", contact_widget_destroy_cb, + "entry_group", "changed", contact_widget_entry_group_changed_cb, + "entry_group", "activate", contact_widget_entry_group_activate_cb, + "button_group", "clicked", contact_widget_button_group_clicked_cb, + NULL); + + g_object_unref (glade); + + g_object_set_data (G_OBJECT (information->vbox_contact_widget), + "EmpathyContactWidget", + information); + + /* Create widgets */ + contact_widget_contact_setup (information); + contact_widget_groups_setup (information); + contact_widget_details_setup (information); + contact_widget_client_setup (information); + + contact_widget_set_contact (information, contact); + + gtk_widget_show (information->vbox_contact_widget); + + return information->vbox_contact_widget; +} + +EmpathyContact * +empathy_contact_widget_get_contact (GtkWidget *widget) +{ + EmpathyContactWidget *information; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget"); + if (!information) { + return NULL; + } + + return information->contact; +} + +void +empathy_contact_widget_set_contact (GtkWidget *widget, + EmpathyContact *contact) +{ + EmpathyContactWidget *information; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + + information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget"); + if (!information) { + return; + } + + contact_widget_set_contact (information, contact); +} + +void +empathy_contact_widget_set_account_filter (GtkWidget *widget, + EmpathyAccountChooserFilterFunc filter, + gpointer user_data) +{ + EmpathyContactWidget *information; + EmpathyAccountChooser *chooser; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget"); + if (!information) { + return; + } + + chooser = EMPATHY_ACCOUNT_CHOOSER (information->widget_account); + if (chooser) { + empathy_account_chooser_set_filter (chooser, filter, user_data); + } +} + +static void +contact_widget_destroy_cb (GtkWidget *widget, + EmpathyContactWidget *information) +{ + contact_widget_remove_contact (information); + + if (information->widget_id_timeout != 0) { + g_source_remove (information->widget_id_timeout); + } + if (information->factory) { + g_object_unref (information->factory); + } + if (information->manager) { + g_object_unref (information->manager); + } + + g_slice_free (EmpathyContactWidget, information); +} + +static void +contact_widget_remove_contact (EmpathyContactWidget *information) +{ + if (information->contact) { + g_signal_handlers_disconnect_by_func (information->contact, + contact_widget_name_notify_cb, + information); + g_signal_handlers_disconnect_by_func (information->contact, + contact_widget_presence_notify_cb, + information); + g_signal_handlers_disconnect_by_func (information->contact, + contact_widget_avatar_notify_cb, + information); + g_signal_handlers_disconnect_by_func (information->contact, + contact_widget_groups_notify_cb, + information); + + g_object_unref (information->contact); + information->contact = NULL; + } +} + +static void +contact_widget_set_contact (EmpathyContactWidget *information, + EmpathyContact *contact) +{ + if (contact == information->contact || + (contact && information->contact && + empathy_contact_equal (contact, information->contact))) { + return; + } + + contact_widget_remove_contact (information); + if (contact) { + information->contact = g_object_ref (contact); + } + + /* Update information for widgets */ + contact_widget_contact_update (information); + contact_widget_groups_update (information); + contact_widget_details_update (information); + contact_widget_client_update (information); +} + +static gboolean +contact_widget_id_activate_timeout (EmpathyContactWidget *self) +{ + contact_widget_change_contact (self); + return FALSE; +} + +static void +contact_widget_id_changed_cb (GtkEntry *entry, + EmpathyContactWidget *self) +{ + if (self->widget_id_timeout != 0) { + g_source_remove (self->widget_id_timeout); + } + + self->widget_id_timeout = + g_timeout_add_seconds (ID_CHANGED_TIMEOUT, + (GSourceFunc) contact_widget_id_activate_timeout, + self); +} + +static void +contact_widget_contact_setup (EmpathyContactWidget *information) +{ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_AVATAR) { + information->widget_avatar = empathy_avatar_chooser_new (); + g_signal_connect (information->widget_avatar, "changed", + G_CALLBACK (contact_widget_avatar_changed_cb), + information); + } else { + information->widget_avatar = empathy_avatar_image_new (); + } + gtk_box_pack_start (GTK_BOX (information->vbox_avatar), + information->widget_avatar, + FALSE, FALSE, + 6); + gtk_widget_show (information->widget_avatar); + + /* Setup account label/chooser */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT) { + information->widget_account = empathy_account_chooser_new (); + + g_signal_connect (information->widget_account, "changed", + G_CALLBACK (contact_widget_account_changed_cb), + information); + } else { + information->widget_account = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (information->widget_account), TRUE); + gtk_misc_set_alignment (GTK_MISC (information->widget_account), 0, 0.5); + } + gtk_table_attach_defaults (GTK_TABLE (information->table_contact), + information->widget_account, + 1, 2, 0, 1); + gtk_widget_show (information->widget_account); + + /* Setup id label/entry */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID) { + information->widget_id = gtk_entry_new (); + g_signal_connect (information->widget_id, "focus-out-event", + G_CALLBACK (contact_widget_id_focus_out_cb), + information); + g_signal_connect (information->widget_id, "changed", + G_CALLBACK (contact_widget_id_changed_cb), + information); + } else { + information->widget_id = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (information->widget_id), TRUE); + gtk_misc_set_alignment (GTK_MISC (information->widget_id), 0, 0.5); + } + gtk_table_attach_defaults (GTK_TABLE (information->table_contact), + information->widget_id, + 1, 2, 1, 2); + gtk_widget_show (information->widget_id); + + /* Setup alias label/entry */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ALIAS) { + information->widget_alias = gtk_entry_new (); + g_signal_connect (information->widget_alias, "focus-out-event", + G_CALLBACK (contact_widget_entry_alias_focus_event_cb), + information); + } else { + information->widget_alias = gtk_label_new (NULL); + gtk_label_set_selectable (GTK_LABEL (information->widget_alias), TRUE); + gtk_misc_set_alignment (GTK_MISC (information->widget_alias), 0, 0.5); + } + gtk_table_attach_defaults (GTK_TABLE (information->table_contact), + information->widget_alias, + 1, 2, 2, 3); + gtk_widget_show (information->widget_alias); +} + +static void +contact_widget_contact_update (EmpathyContactWidget *information) +{ + McAccount *account = NULL; + const gchar *id = NULL; + + /* Connect and get info from new contact */ + if (information->contact) { + g_signal_connect_swapped (information->contact, "notify::name", + G_CALLBACK (contact_widget_name_notify_cb), + information); + g_signal_connect_swapped (information->contact, "notify::presence", + G_CALLBACK (contact_widget_presence_notify_cb), + information); + g_signal_connect_swapped (information->contact, "notify::presence-message", + G_CALLBACK (contact_widget_presence_notify_cb), + information); + g_signal_connect_swapped (information->contact, "notify::avatar", + G_CALLBACK (contact_widget_avatar_notify_cb), + information); + + account = empathy_contact_get_account (information->contact); + id = empathy_contact_get_id (information->contact); + } + + /* Update account widget */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT) { + if (account) { + g_signal_handlers_block_by_func (information->widget_account, + contact_widget_account_changed_cb, + information); + empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account), + account); + g_signal_handlers_unblock_by_func (information->widget_account, + contact_widget_account_changed_cb, + information); + } + } else { + if (account) { + const gchar *name; + + name = mc_account_get_display_name (account); + gtk_label_set_label (GTK_LABEL (information->widget_account), name); + } + } + + /* Update id widget */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID) { + gtk_entry_set_text (GTK_ENTRY (information->widget_id), id ? id : ""); + } else { + gtk_label_set_label (GTK_LABEL (information->widget_id), id ? id : ""); + } + /* Update other widgets */ + if (information->contact) { + contact_widget_name_notify_cb (information); + contact_widget_presence_notify_cb (information); + contact_widget_avatar_notify_cb (information); + + gtk_widget_show (information->label_alias); + gtk_widget_show (information->widget_alias); + gtk_widget_show (information->hbox_presence); + gtk_widget_show (information->widget_avatar); + } else { + gtk_widget_hide (information->label_alias); + gtk_widget_hide (information->widget_alias); + gtk_widget_hide (information->hbox_presence); + gtk_widget_hide (information->widget_avatar); + } +} + +static void +contact_widget_change_contact (EmpathyContactWidget *information) +{ + EmpathyContact *contact; + McAccount *account; + + account = empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account)); + if (!account) { + return; + } + + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID) { + const gchar *id; + + id = gtk_entry_get_text (GTK_ENTRY (information->widget_id)); + if (G_STR_EMPTY (id)) { + return; + } + + contact = empathy_contact_factory_get_from_id (information->factory, + account, id); + } else { + contact = empathy_contact_factory_get_user (information->factory, + account); + } + + if (contact) { + contact_widget_set_contact (information, contact); + g_object_unref (contact); + } +} + +static void +contact_widget_avatar_changed_cb (EmpathyAvatarChooser *chooser, + EmpathyContactWidget *information) +{ + if (information->contact && + empathy_contact_is_user (information->contact)) { + McAccount *account; + const gchar *data; + gsize size; + const gchar *mime_type; + + account = empathy_contact_get_account (information->contact); + empathy_avatar_chooser_get_image_data (EMPATHY_AVATAR_CHOOSER (information->widget_avatar), + &data, &size, &mime_type); + empathy_contact_factory_set_avatar (information->factory, + account, + data, size, mime_type); + } +} + +static void +contact_widget_account_changed_cb (GtkComboBox *widget, + EmpathyContactWidget *information) +{ + contact_widget_change_contact (information); +} + +static gboolean +contact_widget_id_focus_out_cb (GtkWidget *widget, + GdkEventFocus *event, + EmpathyContactWidget *information) +{ + contact_widget_change_contact (information); + return FALSE; +} + +static gboolean +contact_widget_entry_alias_focus_event_cb (GtkEditable *editable, + GdkEventFocus *event, + EmpathyContactWidget *information) +{ + if (information->contact) { + const gchar *alias; + + alias = gtk_entry_get_text (GTK_ENTRY (editable)); + empathy_contact_factory_set_alias (information->factory, + information->contact, + alias); + } + + return FALSE; +} + +static void +contact_widget_name_notify_cb (EmpathyContactWidget *information) +{ + if (GTK_IS_ENTRY (information->widget_alias)) { + gtk_entry_set_text (GTK_ENTRY (information->widget_alias), + empathy_contact_get_name (information->contact)); + } else { + gtk_label_set_label (GTK_LABEL (information->widget_alias), + empathy_contact_get_name (information->contact)); + } +} + +static void +contact_widget_presence_notify_cb (EmpathyContactWidget *information) +{ + gtk_label_set_text (GTK_LABEL (information->label_status), + empathy_contact_get_status (information->contact)); + gtk_image_set_from_icon_name (GTK_IMAGE (information->image_state), + empathy_icon_name_for_contact (information->contact), + GTK_ICON_SIZE_BUTTON); + +} + +static void +contact_widget_avatar_notify_cb (EmpathyContactWidget *information) +{ + EmpathyAvatar *avatar = NULL; + + if (information->contact) { + avatar = empathy_contact_get_avatar (information->contact); + } + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_AVATAR) { + g_signal_handlers_block_by_func (information->widget_avatar, + contact_widget_avatar_changed_cb, + information); + empathy_avatar_chooser_set (EMPATHY_AVATAR_CHOOSER (information->widget_avatar), + avatar); + g_signal_handlers_unblock_by_func (information->widget_avatar, + contact_widget_avatar_changed_cb, + information); + } else { + empathy_avatar_image_set (EMPATHY_AVATAR_IMAGE (information->widget_avatar), + avatar); + } +} + +static void +contact_widget_groups_setup (EmpathyContactWidget *information) +{ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_GROUPS) { + information->manager = empathy_contact_manager_new (); + contact_widget_model_setup (information); + } +} + +static void +contact_widget_groups_update (EmpathyContactWidget *information) +{ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_GROUPS && + information->contact) { + g_signal_connect_swapped (information->contact, "notify::groups", + G_CALLBACK (contact_widget_groups_notify_cb), + information); + contact_widget_groups_populate_data (information); + + gtk_widget_show (information->vbox_groups); + } else { + gtk_widget_hide (information->vbox_groups); + } +} + +static void +contact_widget_model_setup (EmpathyContactWidget *information) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + + view = GTK_TREE_VIEW (information->treeview_groups); + + store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* name */ + G_TYPE_BOOLEAN, /* enabled */ + G_TYPE_BOOLEAN); /* editable */ + + gtk_tree_view_set_model (view, GTK_TREE_MODEL (store)); + + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + contact_widget_model_populate_columns (information); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_NAME, GTK_SORT_ASCENDING); + + g_object_unref (store); +} + +static void +contact_widget_model_populate_columns (EmpathyContactWidget *information) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + guint col_offset; + + view = GTK_TREE_VIEW (information->treeview_groups); + model = gtk_tree_view_get_model (view); + + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (renderer, "toggled", + G_CALLBACK (contact_widget_cell_toggled), + information); + + column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer, + "active", COL_ENABLED, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, 50); + gtk_tree_view_append_column (view, column); + + renderer = gtk_cell_renderer_text_new (); + col_offset = gtk_tree_view_insert_column_with_attributes (view, + -1, _("Group"), + renderer, + "text", COL_NAME, + /* "editable", COL_EDITABLE, */ + NULL); + + g_object_set_data (G_OBJECT (renderer), + "column", GINT_TO_POINTER (COL_NAME)); + + column = gtk_tree_view_get_column (view, col_offset - 1); + gtk_tree_view_column_set_sort_column_id (column, COL_NAME); + gtk_tree_view_column_set_resizable (column,FALSE); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); + + if (information->renderer) { + g_object_unref (information->renderer); + } + + information->renderer = g_object_ref (renderer); +} + +static void +contact_widget_groups_populate_data (EmpathyContactWidget *information) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeIter iter; + GList *my_groups, *l; + GList *all_groups; + + view = GTK_TREE_VIEW (information->treeview_groups); + store = GTK_LIST_STORE (gtk_tree_view_get_model (view)); + gtk_list_store_clear (store); + + all_groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (information->manager)); + my_groups = empathy_contact_list_get_groups (EMPATHY_CONTACT_LIST (information->manager), + information->contact); + + for (l = all_groups; l; l = l->next) { + const gchar *group_str; + gboolean enabled; + + group_str = l->data; + + enabled = g_list_find_custom (my_groups, + group_str, + (GCompareFunc) strcmp) != NULL; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_NAME, group_str, + COL_EDITABLE, TRUE, + COL_ENABLED, enabled, + -1); + } + + g_list_foreach (all_groups, (GFunc) g_free, NULL); + g_list_foreach (my_groups, (GFunc) g_free, NULL); + g_list_free (all_groups); + g_list_free (my_groups); +} + +static void +contact_widget_groups_notify_cb (EmpathyContactWidget *information) +{ + /* FIXME: not implemented */ +} + +static gboolean +contact_widget_model_find_name (EmpathyContactWidget *information, + const gchar *name, + GtkTreeIter *iter) +{ + GtkTreeView *view; + GtkTreeModel *model; + FindName data; + + if (G_STR_EMPTY (name)) { + return FALSE; + } + + data.information = information; + data.name = name; + data.found = FALSE; + + view = GTK_TREE_VIEW (information->treeview_groups); + model = gtk_tree_view_get_model (view); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) contact_widget_model_find_name_foreach, + &data); + + if (data.found == TRUE) { + *iter = data.found_iter; + return TRUE; + } + + return FALSE; +} + +static gboolean +contact_widget_model_find_name_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + FindName *data) +{ + gchar *name; + + gtk_tree_model_get (model, iter, + COL_NAME, &name, + -1); + + if (!name) { + return FALSE; + } + + if (data->name && strcmp (data->name, name) == 0) { + data->found = TRUE; + data->found_iter = *iter; + + g_free (name); + + return TRUE; + } + + g_free (name); + + return FALSE; +} + +static void +contact_widget_cell_toggled (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyContactWidget *information) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkListStore *store; + GtkTreePath *path; + GtkTreeIter iter; + gboolean enabled; + gchar *group; + + view = GTK_TREE_VIEW (information->treeview_groups); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + path = gtk_tree_path_new_from_string (path_string); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COL_ENABLED, &enabled, + COL_NAME, &group, + -1); + + gtk_list_store_set (store, &iter, COL_ENABLED, !enabled, -1); + gtk_tree_path_free (path); + + if (group) { + if (enabled) { + empathy_contact_list_remove_from_group (EMPATHY_CONTACT_LIST (information->manager), + information->contact, + group); + } else { + empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager), + information->contact, + group); + } + + g_free (group); + } +} + +static void +contact_widget_entry_group_changed_cb (GtkEditable *editable, + EmpathyContactWidget *information) +{ + GtkTreeIter iter; + const gchar *group; + + group = gtk_entry_get_text (GTK_ENTRY (information->entry_group)); + + if (contact_widget_model_find_name (information, group, &iter)) { + gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), FALSE); + + } else { + gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), + !G_STR_EMPTY (group)); + } +} + +static void +contact_widget_entry_group_activate_cb (GtkEntry *entry, + EmpathyContactWidget *information) +{ + gtk_widget_activate (GTK_WIDGET (information->button_group)); +} + +static void +contact_widget_button_group_clicked_cb (GtkButton *button, + EmpathyContactWidget *information) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeIter iter; + const gchar *group; + + view = GTK_TREE_VIEW (information->treeview_groups); + store = GTK_LIST_STORE (gtk_tree_view_get_model (view)); + + group = gtk_entry_get_text (GTK_ENTRY (information->entry_group)); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_NAME, group, + COL_ENABLED, TRUE, + -1); + + empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager), + information->contact, + group); +} + +static void +contact_widget_details_setup (EmpathyContactWidget *information) +{ + /* FIXME: Needs new telepathy spec */ + gtk_widget_hide (information->vbox_details); +} + +static void +contact_widget_details_update (EmpathyContactWidget *information) +{ + /* FIXME: Needs new telepathy spec */ +} + +static void +contact_widget_client_setup (EmpathyContactWidget *information) +{ + /* FIXME: Needs new telepathy spec */ + gtk_widget_hide (information->vbox_client); +} + +static void +contact_widget_client_update (EmpathyContactWidget *information) +{ + /* FIXME: Needs new telepathy spec */ +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-widget.glade b/gnome-2-22/libempathy-gtk/empathy-contact-widget.glade new file mode 100644 index 000000000..4b204b7b6 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-widget.glade @@ -0,0 +1,983 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="contact_window"> + <property name="visible">True</property> + <property name="title" translatable="yes">Contact information</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox_contact_widget"> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkVBox" id="vbox_contact"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label654"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Contact</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment31"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox189"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkVBox" id="vbox225"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_contact"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkLabel" id="label_alias"> + <property name="visible">True</property> + <property name="label" translatable="yes">Alias:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label655"> + <property name="visible">True</property> + <property name="label" translatable="yes">Identifier:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label680"> + <property name="visible">True</property> + <property name="label" translatable="yes">Account:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox_presence"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkImage" id="image_state"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_status"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_avatar"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_groups"> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label672"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Groups</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment33"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox224"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label679"> + <property name="visible">True</property> + <property name="label" translatable="yes">Select the groups you want this contact to appear in, you can select more than one group or no groups.</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox188"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkEntry" id="entry_group"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_group"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="label">_Add Group</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow17"> + <property name="height_request">100</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview_groups"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_details"> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label649"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Contact Details</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment30"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox218"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_details"> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label652"> + <property name="label" translatable="yes">Birthday:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label651"> + <property name="label" translatable="yes">Web site:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label650"> + <property name="label" translatable="yes">Email:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label670"> + <property name="label" translatable="yes">Fullname:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox_details_requested"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkImage" id="image885"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-info</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label653"> + <property name="visible">True</property> + <property name="label" translatable="yes">Information requested...</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_client"> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label662"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Client Information</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment32"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox222"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkTable" id="table_client"> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label_os"> + <property name="can_focus">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">2</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_version"> + <property name="can_focus">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">2</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_client"> + <property name="can_focus">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">2</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label666"> + <property name="label" translatable="yes">Client:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label667"> + <property name="label" translatable="yes">Version:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label668"> + <property name="label" translatable="yes">OS:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox_client_requested"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkImage" id="image887"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-info</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label669"> + <property name="visible">True</property> + <property name="label" translatable="yes">Information requested...</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-contact-widget.h b/gnome-2-22/libempathy-gtk/empathy-contact-widget.h new file mode 100644 index 000000000..643867ef5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-contact-widget.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_CONTACT_WIDGET_H__ +#define __EMPATHY_CONTACT_WIDGET_H__ + +#include <gtk/gtk.h> + +#include <libempathy/empathy-contact.h> +#include "empathy-account-chooser.h" + +G_BEGIN_DECLS + +typedef enum { + EMPATHY_CONTACT_WIDGET_EDIT_NONE = 0, + EMPATHY_CONTACT_WIDGET_EDIT_ALIAS = 1 << 0, + EMPATHY_CONTACT_WIDGET_EDIT_AVATAR = 1 << 1, + EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT = 1 << 2, + EMPATHY_CONTACT_WIDGET_EDIT_ID = 1 << 3, + EMPATHY_CONTACT_WIDGET_EDIT_GROUPS = 1 << 4, +} EmpathyContactWidgetFlags; + +GtkWidget * empathy_contact_widget_new (EmpathyContact *contact, + EmpathyContactWidgetFlags flags); +EmpathyContact *empathy_contact_widget_get_contact (GtkWidget *widget); +void empathy_contact_widget_set_contact (GtkWidget *widget, + EmpathyContact *contact); +void empathy_contact_widget_set_account_filter (GtkWidget *widget, + EmpathyAccountChooserFilterFunc filter, + gpointer user_data); + +G_END_DECLS + +#endif /* __EMPATHY_CONTACT_WIDGET_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-geometry.c b/gnome-2-22/libempathy-gtk/empathy-geometry.c new file mode 100644 index 000000000..c8bbd2b21 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-geometry.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <sys/stat.h> + +#include <glib.h> +#include <gdk/gdk.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-geometry.h" + +#define DEBUG_DOMAIN "Geometry" + +#define GEOMETRY_DIR_CREATE_MODE (S_IRUSR | S_IWUSR | S_IXUSR) +#define GEOMETRY_FILE_CREATE_MODE (S_IRUSR | S_IWUSR) + +#define GEOMETRY_KEY_FILENAME "geometry.ini" +#define GEOMETRY_FORMAT "%d,%d,%d,%d" +#define GEOMETRY_GROUP_NAME "geometry" + +static gchar *geometry_get_filename (void); + +static gchar * +geometry_get_filename (void) +{ + gchar *dir; + gchar *filename; + + dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL); + if (!g_file_test (dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + empathy_debug (DEBUG_DOMAIN, "Creating directory:'%s'", dir); + g_mkdir_with_parents (dir, GEOMETRY_DIR_CREATE_MODE); + } + + filename = g_build_filename (dir, GEOMETRY_KEY_FILENAME, NULL); + g_free (dir); + + return filename; +} + +void +empathy_geometry_save (const gchar *name, + gint x, + gint y, + gint w, + gint h) +{ + GError *error = NULL; + GKeyFile *key_file; + gchar *filename; + GdkScreen *screen; + gint max_width; + gint max_height; + gchar *content; + gsize length; + gchar *str; + + empathy_debug (DEBUG_DOMAIN, "Saving window geometry: x:%d, y:%d, w:%d, h:%d\n", + x, y, w, h); + + screen = gdk_screen_get_default (); + max_width = gdk_screen_get_width (screen); + max_height = gdk_screen_get_height (screen); + + w = CLAMP (w, 100, max_width); + h = CLAMP (h, 100, max_height); + + x = CLAMP (x, 0, max_width - w); + y = CLAMP (y, 0, max_height - h); + + str = g_strdup_printf (GEOMETRY_FORMAT, x, y, w, h); + + key_file = g_key_file_new (); + + filename = geometry_get_filename (); + + g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL); + g_key_file_set_string (key_file, GEOMETRY_GROUP_NAME, name, str); + + g_free (str); + + content = g_key_file_to_data (key_file, &length, NULL); + if (!g_file_set_contents (filename, content, length, &error)) { + g_warning ("Couldn't save window geometry, error:%d->'%s'", + error->code, error->message); + g_error_free (error); + } + + g_free (content); + g_free (filename); + g_key_file_free (key_file); +} + +void +empathy_geometry_load (const gchar *name, + gint *x, + gint *y, + gint *w, + gint *h) +{ + GKeyFile *key_file; + gchar *filename; + gchar *str = NULL; + + if (x) { + *x = -1; + } + + if (y) { + *y = -1; + } + + if (w) { + *w = -1; + } + + if (h) { + *h = -1; + } + + key_file = g_key_file_new (); + + filename = geometry_get_filename (); + + if (g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL)) { + str = g_key_file_get_string (key_file, GEOMETRY_GROUP_NAME, name, NULL); + } + + if (str) { + gint tmp_x, tmp_y, tmp_w, tmp_h; + + sscanf (str, GEOMETRY_FORMAT, &tmp_x, &tmp_y, &tmp_w, &tmp_h); + + if (x) { + *x = tmp_x; + } + + if (y) { + *y = tmp_y; + } + + if (w) { + *w = tmp_w; + } + + if (h) { + *h = tmp_h; + } + + g_free (str); + } + + empathy_debug (DEBUG_DOMAIN, "Loading window geometry: x:%d, y:%d, w:%d, h:%d\n", + x ? *x : -1, + y ? *y : -1, + w ? *w : -1, + h ? *h : -1); + + g_free (filename); + g_key_file_free (key_file); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-geometry.h b/gnome-2-22/libempathy-gtk/empathy-geometry.h new file mode 100644 index 000000000..512b64696 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-geometry.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_GEOMETRY_H__ +#define __EMPATHY_GEOMETRY_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +void empathy_geometry_save (const gchar *name, + gint x, + gint y, + gint w, + gint h); +void empathy_geometry_load (const gchar *name, + gint *x, + gint *y, + gint *w, + gint *h); + +G_END_DECLS + +#endif /* __EMPATHY_GEOMETRY_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-group-chat.c b/gnome-2-22/libempathy-gtk/empathy-group-chat.c new file mode 100644 index 000000000..16fb841be --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-group-chat.c @@ -0,0 +1,700 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <telepathy-glib/util.h> + +#include <libempathy/empathy-tp-chat.h> +#include <libempathy/empathy-tp-chatroom.h> +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> + +#include "empathy-group-chat.h" +#include "empathy-chat.h" +#include "empathy-chat-view.h" +#include "empathy-contact-list-store.h" +#include "empathy-contact-list-view.h" +//#include "empathy-chat-invite.h" +//#include "empathy-sound.h" +#include "empathy-images.h" +#include "empathy-ui-utils.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" + +#define DEBUG_DOMAIN "GroupChat" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatPriv)) + +struct _EmpathyGroupChatPriv { + EmpathyContactListStore *store; + EmpathyContactListView *view; + EmpathyTpChatroom *tp_chat; + + GtkWidget *widget; + GtkWidget *hpaned; + GtkWidget *vbox_left; + GtkWidget *scrolled_window_chat; + GtkWidget *scrolled_window_input; + GtkWidget *scrolled_window_contacts; + GtkWidget *hbox_topic; + GtkWidget *label_topic; + + gchar *topic; + gchar *name; + GCompletion *completion; + + gint contacts_width; + gboolean contacts_visible; +}; + +static void group_chat_finalize (GObject *object); +static void group_chat_create_ui (EmpathyGroupChat *chat); +static void group_chat_widget_destroy_cb (GtkWidget *widget, + EmpathyGroupChat *chat); +static void group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_member, + EmpathyGroupChat *chat); +static void group_chat_topic_entry_activate_cb (GtkWidget *entry, + GtkDialog *dialog); +static void group_chat_topic_response_cb (GtkWidget *dialog, + gint response, + EmpathyGroupChat *chat); +static const gchar * group_chat_get_name (EmpathyChat *chat); +static gchar * group_chat_get_tooltip (EmpathyChat *chat); +static const gchar * group_chat_get_status_icon_name (EmpathyChat *chat); +static GtkWidget * group_chat_get_widget (EmpathyChat *chat); +static gboolean group_chat_is_group_chat (EmpathyChat *chat); +static void group_chat_set_tp_chat (EmpathyChat *chat, + EmpathyTpChat *tp_chat); +static void group_chat_subject_notify_cb (EmpathyTpChat *tp_chat, + GParamSpec *param, + EmpathyGroupChat *chat); +static void group_chat_name_notify_cb (EmpathyTpChat *tp_chat, + GParamSpec *param, + EmpathyGroupChat *chat); +static gboolean group_chat_key_press_event (EmpathyChat *chat, + GdkEventKey *event); +static gint group_chat_contacts_completion_func (const gchar *s1, + const gchar *s2, + gsize n); + +G_DEFINE_TYPE (EmpathyGroupChat, empathy_group_chat, EMPATHY_TYPE_CHAT) + +static void +empathy_group_chat_class_init (EmpathyGroupChatClass *klass) +{ + GObjectClass *object_class; + EmpathyChatClass *chat_class; + + object_class = G_OBJECT_CLASS (klass); + chat_class = EMPATHY_CHAT_CLASS (klass); + + object_class->finalize = group_chat_finalize; + + chat_class->get_name = group_chat_get_name; + chat_class->get_tooltip = group_chat_get_tooltip; + chat_class->get_status_icon_name = group_chat_get_status_icon_name; + chat_class->get_widget = group_chat_get_widget; + chat_class->is_group_chat = group_chat_is_group_chat; + chat_class->set_tp_chat = group_chat_set_tp_chat; + chat_class->key_press_event = group_chat_key_press_event; + + g_type_class_add_private (object_class, sizeof (EmpathyGroupChatPriv)); +} + +static void +empathy_group_chat_init (EmpathyGroupChat *chat) +{ + EmpathyGroupChatPriv *priv; + EmpathyChatView *chatview; + + priv = GET_PRIV (chat); + + priv->contacts_visible = TRUE; + + chatview = EMPATHY_CHAT_VIEW (EMPATHY_CHAT (chat)->view); + empathy_chat_view_set_is_group_chat (chatview, TRUE); + + group_chat_create_ui (chat); +} + +static void +group_chat_finalize (GObject *object) +{ + EmpathyGroupChat *chat; + EmpathyGroupChatPriv *priv; + + empathy_debug (DEBUG_DOMAIN, "Finalized:%p", object); + + chat = EMPATHY_GROUP_CHAT (object); + priv = GET_PRIV (chat); + + g_free (priv->name); + g_free (priv->topic); + g_object_unref (priv->store); + g_object_unref (priv->tp_chat); + g_completion_free (priv->completion); + + G_OBJECT_CLASS (empathy_group_chat_parent_class)->finalize (object); +} + +EmpathyGroupChat * +empathy_group_chat_new (EmpathyTpChatroom *tp_chat) +{ + EmpathyGroupChat *chat; + + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL); + + chat = g_object_new (EMPATHY_TYPE_GROUP_CHAT, + "tp-chat", tp_chat, + NULL); + + return chat; +} + +gboolean +empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + return priv->contacts_visible; +} + +void +empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat, + gboolean show) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat)); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + priv->contacts_visible = show; + + if (show) { + gtk_widget_show (priv->scrolled_window_contacts); + gtk_paned_set_position (GTK_PANED (priv->hpaned), + priv->contacts_width); + } else { + priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned)); + gtk_widget_hide (priv->scrolled_window_contacts); + } +} + +void +empathy_group_chat_set_topic (EmpathyGroupChat *chat) +{ + EmpathyGroupChatPriv *priv; + EmpathyChatWindow *chat_window; + GtkWidget *chat_dialog; + GtkWidget *dialog; + GtkWidget *entry; + GtkWidget *hbox; + const gchar *topic; + + g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat)); + + priv = GET_PRIV (chat); + + chat_window = empathy_chat_get_window (EMPATHY_CHAT (chat)); + chat_dialog = empathy_chat_window_get_dialog (chat_window); + + dialog = gtk_message_dialog_new (GTK_WINDOW (chat_dialog), + 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_OK_CANCEL, + _("Enter the new topic you want to set for this room:")); + + topic = gtk_label_get_text (GTK_LABEL (priv->label_topic)); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), + hbox, FALSE, TRUE, 4); + + entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (entry), topic); + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + + gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4); + + g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL); + g_object_set_data (G_OBJECT (dialog), "entry", entry); + + g_signal_connect (entry, "activate", + G_CALLBACK (group_chat_topic_entry_activate_cb), + dialog); + g_signal_connect (dialog, "response", + G_CALLBACK (group_chat_topic_response_cb), + chat); + + gtk_widget_show_all (dialog); +} + +static void +group_chat_create_ui (EmpathyGroupChat *chat) +{ + EmpathyGroupChatPriv *priv; + GladeXML *glade; + GList *list = NULL; + + priv = GET_PRIV (chat); + + glade = empathy_glade_get_file ("empathy-group-chat.glade", + "group_chat_widget", + NULL, + "group_chat_widget", &priv->widget, + "hpaned", &priv->hpaned, + "vbox_left", &priv->vbox_left, + "scrolled_window_chat", &priv->scrolled_window_chat, + "scrolled_window_input", &priv->scrolled_window_input, + "hbox_topic", &priv->hbox_topic, + "label_topic", &priv->label_topic, + "scrolled_window_contacts", &priv->scrolled_window_contacts, + NULL); + + empathy_glade_connect (glade, + chat, + "group_chat_widget", "destroy", group_chat_widget_destroy_cb, + NULL); + + g_object_unref (glade); + + g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat)); + + /* Add room GtkTextView. */ + gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat), + GTK_WIDGET (EMPATHY_CHAT (chat)->view)); + gtk_widget_show (GTK_WIDGET (EMPATHY_CHAT (chat)->view)); + + /* Add input GtkTextView */ + gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input), + EMPATHY_CHAT (chat)->input_text_view); + gtk_widget_show (EMPATHY_CHAT (chat)->input_text_view); + + /* Add nick name completion */ + priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name); + g_completion_set_compare (priv->completion, + group_chat_contacts_completion_func); + + /* Set widget focus order */ + list = g_list_append (NULL, priv->scrolled_window_input); + gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list); + g_list_free (list); + + list = g_list_append (NULL, priv->vbox_left); + list = g_list_append (list, priv->scrolled_window_contacts); + gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list); + g_list_free (list); + + list = g_list_append (NULL, priv->hpaned); + list = g_list_append (list, priv->hbox_topic); + gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list); + g_list_free (list); +} + +static void +group_chat_widget_destroy_cb (GtkWidget *widget, + EmpathyGroupChat *chat) +{ + empathy_debug (DEBUG_DOMAIN, "Destroyed"); + + g_object_unref (chat); +} + +static void +group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_member, + EmpathyGroupChat *chat) +{ + if (!EMPATHY_CHAT (chat)->block_events) { + gchar *str; + if (is_member) { + str = g_strdup_printf (_("%s has joined the room"), + empathy_contact_get_name (contact)); + } else { + str = g_strdup_printf (_("%s has left the room"), + empathy_contact_get_name (contact)); + } + empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str); + g_free (str); + } +} + +static void +group_chat_topic_entry_activate_cb (GtkWidget *entry, + GtkDialog *dialog) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_OK); +} + +static void +group_chat_topic_response_cb (GtkWidget *dialog, + gint response, + EmpathyGroupChat *chat) +{ + if (response == GTK_RESPONSE_OK) { + GtkWidget *entry; + const gchar *topic; + + entry = g_object_get_data (G_OBJECT (dialog), "entry"); + topic = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (!G_STR_EMPTY (topic)) { + EmpathyGroupChatPriv *priv; + + priv = GET_PRIV (chat); + + empathy_tp_chatroom_set_topic (priv->tp_chat, topic); + } + } + + gtk_widget_destroy (dialog); +} + +static const gchar * +group_chat_get_name (EmpathyChat *chat) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + if (!priv->name) { + const gchar *id; + const gchar *server; + + id = empathy_chat_get_id (chat); + server = strstr (id, "@"); + + if (server) { + priv->name = g_strndup (id, server - id); + } else { + priv->name = g_strdup (id); + } + } + + return priv->name; +} + +static gchar * +group_chat_get_tooltip (EmpathyChat *chat) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + if (priv->topic) { + gchar *topic, *tmp; + + topic = g_strdup_printf (_("Topic: %s"), priv->topic); + tmp = g_strdup_printf ("%s\n%s", priv->name, topic); + g_free (topic); + + return tmp; + } + + return g_strdup (priv->name); +} + +static const gchar * +group_chat_get_status_icon_name (EmpathyChat *chat) +{ + return EMPATHY_IMAGE_GROUP_MESSAGE; +} + +static GtkWidget * +group_chat_get_widget (EmpathyChat *chat) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + return priv->widget; +} + +static gboolean +group_chat_is_group_chat (EmpathyChat *chat) +{ + g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE); + + return TRUE; +} + +static void +group_chat_set_tp_chat (EmpathyChat *chat, + EmpathyTpChat *tp_chat) +{ + EmpathyGroupChat *group_chat; + EmpathyGroupChatPriv *priv; + + g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat)); + + group_chat = EMPATHY_GROUP_CHAT (chat); + priv = GET_PRIV (group_chat); + + /* Free all resources related to tp_chat */ + if (priv->tp_chat) { + g_object_unref (priv->tp_chat); + priv->tp_chat = NULL; + } + if (priv->view) { + gtk_widget_destroy (GTK_WIDGET (priv->view)); + g_object_unref (priv->store); + } + g_free (priv->name); + g_free (priv->topic); + priv->name = NULL; + priv->topic = NULL; + + if (!tp_chat) { + /* We are no more connected */ + gtk_widget_set_sensitive (priv->hbox_topic, FALSE); + gtk_widget_set_sensitive (priv->scrolled_window_contacts, FALSE); + return; + } + + /* We are connected */ + gtk_widget_set_sensitive (priv->hbox_topic, TRUE); + gtk_widget_set_sensitive (priv->scrolled_window_contacts, TRUE); + + priv->tp_chat = g_object_ref (tp_chat); + + if (empathy_tp_chatroom_get_invitation (priv->tp_chat, NULL, NULL)) { + empathy_tp_chatroom_accept_invitation (priv->tp_chat); + } + + /* Create contact list */ + priv->store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat)); + priv->view = empathy_contact_list_view_new (priv->store, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE | + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO); + + gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts), + GTK_WIDGET (priv->view)); + gtk_widget_show (GTK_WIDGET (priv->view)); + + /* Connect signals */ + g_signal_connect (priv->tp_chat, "members-changed", + G_CALLBACK (group_chat_members_changed_cb), + chat); + g_signal_connect (priv->tp_chat, "notify::subject", + G_CALLBACK (group_chat_subject_notify_cb), + chat); + g_signal_connect (priv->tp_chat, "notify::name", + G_CALLBACK (group_chat_name_notify_cb), + chat); +} + +static void +group_chat_subject_notify_cb (EmpathyTpChat *tp_chat, + GParamSpec *param, + EmpathyGroupChat *chat) +{ + EmpathyGroupChatPriv *priv; + gchar *str = NULL; + + priv = GET_PRIV (chat); + + g_object_get (priv->tp_chat, "subject", &str, NULL); + if (!tp_strdiff (priv->topic, str)) { + g_free (str); + return; + } + + g_free (priv->topic); + priv->topic = str; + gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->topic); + + if (!EMPATHY_CHAT (chat)->block_events) { + if (!G_STR_EMPTY (priv->topic)) { + str = g_strdup_printf (_("Topic set to: %s"), priv->topic); + } else { + str = g_strdup (_("No topic defined")); + } + empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str); + g_free (str); + } +} + +static void +group_chat_name_notify_cb (EmpathyTpChat *tp_chat, + GParamSpec *param, + EmpathyGroupChat *chat) +{ + EmpathyGroupChatPriv *priv; + + priv = GET_PRIV (chat); + + g_free (priv->name); + g_object_get (priv->tp_chat, "name", &priv->name, NULL); +} + +static gboolean +group_chat_key_press_event (EmpathyChat *chat, + GdkEventKey *event) +{ + EmpathyGroupChatPriv *priv = GET_PRIV (chat); + + if (!(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) && + event->keyval == GDK_Tab) { + GtkTextBuffer *buffer; + GtkTextIter start, current; + gchar *nick, *completed; + GList *list, *completed_list; + gboolean is_start_of_buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (EMPATHY_CHAT (chat)->input_text_view)); + gtk_text_buffer_get_iter_at_mark (buffer, ¤t, gtk_text_buffer_get_insert (buffer)); + + /* Get the start of the nick to complete. */ + gtk_text_buffer_get_iter_at_mark (buffer, &start, gtk_text_buffer_get_insert (buffer)); + gtk_text_iter_backward_word_start (&start); + is_start_of_buffer = gtk_text_iter_is_start (&start); + + list = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (priv->tp_chat)); + g_completion_add_items (priv->completion, list); + + nick = gtk_text_buffer_get_text (buffer, &start, ¤t, FALSE); + completed_list = g_completion_complete (priv->completion, + nick, + &completed); + + g_free (nick); + + if (completed) { + guint len; + const gchar *text; + gchar *complete_char = NULL; + + gtk_text_buffer_delete (buffer, &start, ¤t); + + len = g_list_length (completed_list); + + if (len == 1) { + /* If we only have one hit, use that text + * instead of the text in completed since the + * completed text will use the typed string + * which might be cased all wrong. + * Fixes #120876 + * */ + text = empathy_contact_get_name (completed_list->data); + } else { + text = completed; + } + + gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text)); + + if (len == 1 && is_start_of_buffer && + empathy_conf_get_string (empathy_conf_get (), + EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR, + &complete_char) && + complete_char != NULL) { + gtk_text_buffer_insert_at_cursor (buffer, + complete_char, + strlen (complete_char)); + gtk_text_buffer_insert_at_cursor (buffer, " ", 1); + g_free (complete_char); + } + + g_free (completed); + } + + g_completion_clear_items (priv->completion); + + g_list_foreach (list, (GFunc) g_object_unref, NULL); + g_list_free (list); + + return TRUE; + } + + return FALSE; +} + +static gint +group_chat_contacts_completion_func (const gchar *s1, + const gchar *s2, + gsize n) +{ + gchar *tmp, *nick1, *nick2; + gint ret; + + tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT); + nick1 = g_utf8_casefold (tmp, -1); + g_free (tmp); + + tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT); + nick2 = g_utf8_casefold (tmp, -1); + g_free (tmp); + + ret = strncmp (nick1, nick2, n); + + g_free (nick1); + g_free (nick2); + + return ret; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-group-chat.glade b/gnome-2-22/libempathy-gtk/empathy-group-chat.glade new file mode 100644 index 000000000..5b40dc375 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-group-chat.glade @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkWindow" id="group_chat_window"> + <property name="border_width">6</property> + <property name="title" translatable="yes">Group Chat</property> + <property name="default_width">1000</property> + <property name="default_height">800</property> + <property name="icon_name">system-users</property> + <child> + <widget class="GtkVBox" id="group_chat_widget"> + <property name="visible">True</property> + <property name="border_width">4</property> + <property name="spacing">6</property> + <child> + <widget class="GtkHBox" id="hbox_topic"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkLabel" id="label80"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="label" translatable="yes"><b>Topic:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_topic"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="use_markup">True</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="ellipsize">PANGO_ELLIPSIZE_END</property> + <property name="single_line_mode">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="padding">2</property> + </packing> + </child> + <child> + <widget class="GtkHPaned" id="hpaned"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <widget class="GtkVBox" id="vbox_left"> + <property name="width_request">600</property> + <property name="height_request">500</property> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkScrolledWindow" id="scrolled_window_chat"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <placeholder/> + </child> + </widget> + </child> + <child> + <widget class="GtkScrolledWindow" id="scrolled_window_input"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="scrolled_window_contacts"> + <property name="width_request">200</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-group-chat.h b/gnome-2-22/libempathy-gtk/empathy-group-chat.h new file mode 100644 index 000000000..e0ce79462 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-group-chat.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_GROUP_CHAT_H__ +#define __EMPATHY_GROUP_CHAT_H__ + +G_BEGIN_DECLS + +#include <libempathy/empathy-tp-chatroom.h> +#include <libempathy/empathy-contact.h> + +#define EMPATHY_TYPE_GROUP_CHAT (empathy_group_chat_get_type ()) +#define EMPATHY_GROUP_CHAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChat)) +#define EMPATHY_GROUP_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatClass)) +#define EMPATHY_IS_GROUP_CHAT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_GROUP_CHAT)) +#define EMPATHY_IS_GROUP_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_GROUP_CHAT)) +#define EMPATHY_GROUP_CHAT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatClass)) + +typedef struct _EmpathyGroupChat EmpathyGroupChat; +typedef struct _EmpathyGroupChatClass EmpathyGroupChatClass; +typedef struct _EmpathyGroupChatPriv EmpathyGroupChatPriv; + +#include "empathy-chat.h" + +struct _EmpathyGroupChat { + EmpathyChat parent; + + EmpathyGroupChatPriv *priv; +}; + +struct _EmpathyGroupChatClass { + EmpathyChatClass parent_class; +}; + +GType empathy_group_chat_get_type (void) G_GNUC_CONST; +EmpathyGroupChat *empathy_group_chat_new (EmpathyTpChatroom *tp_chat); +gboolean empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat); +void empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat, + gboolean show); +void empathy_group_chat_set_topic (EmpathyGroupChat *chat); + +G_END_DECLS + +#endif /* __EMPATHY_GROUP_CHAT_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-images.h b/gnome-2-22/libempathy-gtk/empathy-images.h new file mode 100644 index 000000000..254467819 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-images.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_IMAGES_H__ +#define __EMPATHY_IMAGES_H__ + +G_BEGIN_DECLS + +#define EMPATHY_IMAGE_OFFLINE "empathy-offline" +#define EMPATHY_IMAGE_HIDDEN "empathy-offline" +#define EMPATHY_IMAGE_AVAILABLE "empathy-available" +#define EMPATHY_IMAGE_BUSY "empathy-busy" +#define EMPATHY_IMAGE_AWAY "empathy-away" +#define EMPATHY_IMAGE_EXT_AWAY "empathy-extended-away" +#define EMPATHY_IMAGE_UNKNOWN "empathy-pending" + +#define EMPATHY_IMAGE_MESSAGE "im-message" +#define EMPATHY_IMAGE_NEW_MESSAGE "im-message-new" +#define EMPATHY_IMAGE_TYPING "user-typing" +#define EMPATHY_IMAGE_CONTACT_INFORMATION "gtk-info" +#define EMPATHY_IMAGE_GROUP_MESSAGE "system-users" +#define EMPATHY_IMAGE_VOIP "gnome-stock-mic" +#define EMPATHY_IMAGE_LOG "document-open-recent" + +G_END_DECLS + +#endif /* __EMPATHY_IMAGES_ICONS_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.c b/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.c new file mode 100644 index 000000000..9a42c649c --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.c @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2007-2008 Guillaume Desmottes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Guillaume Desmottes <gdesmott@gnome.org> + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-protocol.h> + +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> + +#include <libempathy/empathy-irc-network-manager.h> +#include "empathy-ui-utils.h" +#include "totem-subtitle-encoding.h" + +#include "empathy-irc-network-dialog.h" + +#define DEBUG_DOMAIN "AccountWidgetIRC" + +typedef struct { + EmpathyIrcNetwork *network; + + GtkWidget *dialog; + GtkWidget *button_close; + + GtkWidget *entry_network; + GtkWidget *combobox_charset; + + GtkWidget *treeview_servers; + GtkWidget *button_add; + GtkWidget *button_remove; + GtkWidget *button_up; + GtkWidget *button_down; +} EmpathyIrcNetworkDialog; + +static void +irc_network_dialog_destroy_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + g_object_unref (dialog->network); + + g_slice_free (EmpathyIrcNetworkDialog, dialog); +} + +static void +irc_network_dialog_close_clicked_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + gtk_widget_destroy (dialog->dialog); +} + +enum { + COL_SRV_OBJ, + COL_ADR, + COL_PORT, + COL_SSL +}; + +static void +add_server_to_store (GtkListStore *store, + EmpathyIrcServer *server, + GtkTreeIter *iter) +{ + gchar *address; + guint port; + gboolean ssl; + + g_object_get (server, + "address", &address, + "port", &port, + "ssl", &ssl, + NULL); + + gtk_list_store_insert_with_values (store, iter, -1, + COL_SRV_OBJ, server, + COL_ADR, address, + COL_PORT, port, + COL_SSL, ssl, + -1); + + g_free (address); +} + +static void +irc_network_dialog_setup (EmpathyIrcNetworkDialog *dialog) +{ + gchar *name, *charset; + GSList *servers, *l; + GtkListStore *store; + + g_object_get (dialog->network, + "name", &name, + "charset", &charset, + NULL); + gtk_entry_set_text (GTK_ENTRY (dialog->entry_network), name); + + store = GTK_LIST_STORE (gtk_tree_view_get_model ( + GTK_TREE_VIEW (dialog->treeview_servers))); + + servers = empathy_irc_network_get_servers (dialog->network); + for (l = servers; l != NULL; l = g_slist_next (l)) + { + EmpathyIrcServer *server = l->data; + GtkTreeIter iter; + + add_server_to_store (store, server, &iter); + } + + totem_subtitle_encoding_set (GTK_COMBO_BOX (dialog->combobox_charset), + charset); + + g_slist_foreach (servers, (GFunc) g_object_unref, NULL); + g_slist_free (servers); + g_free (name); + g_free (charset); +} + +static void +irc_network_dialog_address_edited_cb (GtkCellRendererText *renderer, + gchar *path, + gchar *new_text, + EmpathyIrcNetworkDialog *dialog) +{ + EmpathyIrcServer *server; + GtkTreeModel *model; + GtkTreePath *treepath; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview_servers)); + treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (model, &iter, treepath); + gtk_tree_model_get (model, &iter, + COL_SRV_OBJ, &server, + -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_ADR, new_text, + -1); + + g_object_set (server, "address", new_text, NULL); + + gtk_tree_path_free (treepath); + g_object_unref (server); +} + +static void +irc_network_dialog_port_edited_cb (GtkCellRendererText *renderer, + gchar *path, + gchar *new_text, + EmpathyIrcNetworkDialog *dialog) +{ + EmpathyIrcServer *server; + GtkTreeModel *model; + GtkTreePath *treepath; + GtkTreeIter iter; + guint port; + + port = strtoul (new_text, NULL, 10); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview_servers)); + treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (model, &iter, treepath); + gtk_tree_model_get (model, &iter, + COL_SRV_OBJ, &server, + -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_PORT, port, + -1); + + g_object_set (server, "port", port, NULL); + + gtk_tree_path_free (treepath); + g_object_unref (server); +} + +static void +irc_network_dialog_ssl_toggled_cb (GtkCellRendererText *renderer, + gchar *path, + EmpathyIrcNetworkDialog *dialog) +{ + EmpathyIrcServer *server; + GtkTreeModel *model; + GtkTreePath *treepath; + GtkTreeIter iter; + gboolean ssl; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview_servers)); + treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (model, &iter, treepath); + gtk_tree_model_get (model, &iter, + COL_SRV_OBJ, &server, + COL_SSL, &ssl, + -1); + ssl = !ssl; + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + COL_SSL, ssl, + -1); + + g_object_set (server, "ssl", ssl, NULL); + + gtk_tree_path_free (treepath); + g_object_unref (server); +} + +static gboolean +irc_network_dialog_network_focus_cb (GtkWidget *widget, + GdkEventFocus *event, + EmpathyIrcNetworkDialog *dialog) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (widget)); + + g_object_set (dialog->network, "name", str, NULL); + + return FALSE; +} + +static void +irc_network_dialog_network_update_buttons (EmpathyIrcNetworkDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + gboolean can_remove = FALSE, can_move_up = FALSE, can_move_down = FALSE; + int selected; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW ( + dialog->treeview_servers)); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + path = gtk_tree_model_get_path (model, &iter); + + selected = gtk_tree_path_get_indices (path)[0]; + + can_remove = TRUE; + can_move_up = selected > 0; + can_move_down = + selected < gtk_tree_model_iter_n_children (model, NULL) - 1; + + gtk_tree_path_free (path); + } + + gtk_widget_set_sensitive (dialog->button_remove, can_remove); + gtk_widget_set_sensitive (dialog->button_up, can_move_up); + gtk_widget_set_sensitive (dialog->button_down, can_move_down); +} + +static void +irc_network_dialog_button_add_clicked_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + EmpathyIrcServer *server; + GtkListStore *store; + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column; + + store = GTK_LIST_STORE (gtk_tree_view_get_model ( + GTK_TREE_VIEW (dialog->treeview_servers))); + + server = empathy_irc_server_new (_("new server"), 6667, FALSE); + empathy_irc_network_append_server (dialog->network, server); + add_server_to_store (store, server, &iter); + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->treeview_servers), + 0); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->treeview_servers), path, + column, TRUE); + + irc_network_dialog_network_update_buttons (dialog); + + gtk_tree_path_free (path); + g_object_unref (server); +} + +static void +irc_network_dialog_button_remove_clicked_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + EmpathyIrcServer *server; + + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (dialog->treeview_servers)); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + gtk_tree_model_get (model, &iter, COL_SRV_OBJ, &server, -1); + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + empathy_irc_network_remove_server (dialog->network, server); + + irc_network_dialog_network_update_buttons (dialog); + + g_object_unref (server); +} + +static void +irc_network_dialog_button_up_clicked_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter, iter_prev; + GtkTreePath *path; + gint *pos; + EmpathyIrcServer *server; + + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (dialog->treeview_servers)); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + path = gtk_tree_model_get_path (model, &iter); + + if (!gtk_tree_path_prev (path)) + { + gtk_tree_path_free (path); + return; + } + + gtk_tree_model_get (model, &iter, COL_SRV_OBJ, &server, -1); + + gtk_tree_model_get_iter (model, &iter_prev, path); + gtk_list_store_swap (GTK_LIST_STORE (model), &iter_prev, &iter); + + pos = gtk_tree_path_get_indices (path); + empathy_irc_network_set_server_position (dialog->network, server, *pos); + + irc_network_dialog_network_update_buttons (dialog); + + g_object_unref (server); + gtk_tree_path_free (path); +} + +static void +irc_network_dialog_button_down_clicked_cb (GtkWidget *widget, + EmpathyIrcNetworkDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter, iter_prev; + GtkTreePath *path; + EmpathyIrcServer *server; + gint *pos; + + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (dialog->treeview_servers)); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + path = gtk_tree_model_get_path (model, &iter); + + gtk_tree_path_next (path); + if (!gtk_tree_model_get_iter (model, &iter_prev, path)) + { + gtk_tree_path_free (path); + return; + } + + gtk_tree_model_get (model, &iter, COL_SRV_OBJ, &server, -1); + + gtk_list_store_swap (GTK_LIST_STORE (model), &iter_prev, &iter); + + pos = gtk_tree_path_get_indices (path); + empathy_irc_network_set_server_position (dialog->network, server, *pos); + + irc_network_dialog_network_update_buttons (dialog); + + gtk_tree_path_free (path); +} + +static void +irc_network_dialog_selection_changed_cb (GtkTreeSelection *treeselection, + EmpathyIrcNetworkDialog *dialog) +{ + irc_network_dialog_network_update_buttons (dialog); +} + +static void +irc_network_dialog_combobox_charset_changed_cb (GtkWidget *combobox, + EmpathyIrcNetworkDialog *dialog) +{ + const gchar *charset; + + charset = totem_subtitle_encoding_get_selected (GTK_COMBO_BOX (combobox)); + g_object_set (dialog->network, "charset", charset, NULL); +} + +static void +change_network (EmpathyIrcNetworkDialog *dialog, + EmpathyIrcNetwork *network) +{ + GtkListStore *store; + + if (dialog->network == network) + /* No need to change */ + return; + + if (dialog->network != NULL) + { + g_object_unref (dialog->network); + } + + dialog->network = network; + g_object_ref (network); + + store = GTK_LIST_STORE (gtk_tree_view_get_model ( + GTK_TREE_VIEW (dialog->treeview_servers))); + gtk_list_store_clear (store); + + irc_network_dialog_setup (dialog); +} + +/** + * empathy_irc_network_dialog_show: + * @network: the #EmpathyIrcNetwork to configure + * @parent: the parent of this dialog + * + * Display a dialog to configure a given #EmpathyIrcNetwork. + * This function is a singleton so if a configuration dialog already + * exists we use this one to edit the network. + * + * Returns: The displayed #GtkDialog + */ +GtkWidget * +empathy_irc_network_dialog_show (EmpathyIrcNetwork *network, + GtkWidget *parent) +{ + static EmpathyIrcNetworkDialog *dialog = NULL; + GladeXML *glade; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkAdjustment *adjustment; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + + g_return_val_if_fail (network != NULL, NULL); + + if (dialog != NULL) + { + change_network (dialog, network); + gtk_window_present (GTK_WINDOW (dialog->dialog)); + + return dialog->dialog; + } + + dialog = g_slice_new0 (EmpathyIrcNetworkDialog); + + dialog->network = network; + g_object_ref (dialog->network); + + glade = empathy_glade_get_file ("empathy-account-widget-irc.glade", + "irc_network_dialog", + NULL, + "irc_network_dialog", &dialog->dialog, + "button_close", &dialog->button_close, + "entry_network", &dialog->entry_network, + "combobox_charset", &dialog->combobox_charset, + "treeview_servers", &dialog->treeview_servers, + "button_add", &dialog->button_add, + "button_remove", &dialog->button_remove, + "button_up", &dialog->button_up, + "button_down", &dialog->button_down, + NULL); + + store = gtk_list_store_new (4, G_TYPE_OBJECT, G_TYPE_STRING, + G_TYPE_UINT, G_TYPE_BOOLEAN); + gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview_servers), + GTK_TREE_MODEL (store)); + g_object_unref (store); + + /* address */ + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "editable", TRUE, NULL); + g_signal_connect (renderer, "edited", + G_CALLBACK (irc_network_dialog_address_edited_cb), dialog); + gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (dialog->treeview_servers), + -1, _("Server"), renderer, "text", COL_ADR, + NULL); + + /* port */ + adjustment = (GtkAdjustment *) gtk_adjustment_new (6667, 1, G_MAXUINT16, + 1, 10, 0); + renderer = gtk_cell_renderer_spin_new (); + g_object_set (renderer, + "editable", TRUE, + "adjustment", adjustment, + NULL); + g_signal_connect (renderer, "edited", + G_CALLBACK (irc_network_dialog_port_edited_cb), dialog); + gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (dialog->treeview_servers), + -1, _("Port"), renderer, "text", COL_PORT, + NULL); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->treeview_servers), + 1); + gtk_tree_view_column_set_expand (column, TRUE); + + /* SSL */ + renderer = gtk_cell_renderer_toggle_new (); + g_object_set (renderer, "activatable", TRUE, NULL); + g_signal_connect (renderer, "toggled", + G_CALLBACK (irc_network_dialog_ssl_toggled_cb), dialog); + gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (dialog->treeview_servers), + -1, _("SSL"), renderer, "active", COL_SSL, + NULL); + + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (dialog->treeview_servers)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + /* charset */ + totem_subtitle_encoding_init (GTK_COMBO_BOX (dialog->combobox_charset)); + + irc_network_dialog_setup (dialog); + + empathy_glade_connect (glade, dialog, + "irc_network_dialog", "destroy", irc_network_dialog_destroy_cb, + "button_close", "clicked", irc_network_dialog_close_clicked_cb, + "entry_network", "focus-out-event", irc_network_dialog_network_focus_cb, + "button_add", "clicked", irc_network_dialog_button_add_clicked_cb, + "button_remove", "clicked", irc_network_dialog_button_remove_clicked_cb, + "button_up", "clicked", irc_network_dialog_button_up_clicked_cb, + "button_down", "clicked", irc_network_dialog_button_down_clicked_cb, + "combobox_charset", "changed", irc_network_dialog_combobox_charset_changed_cb, + NULL); + + g_object_unref (glade); + + g_object_add_weak_pointer (G_OBJECT (dialog->dialog), + (gpointer) &dialog); + + g_signal_connect (selection, "changed", + G_CALLBACK (irc_network_dialog_selection_changed_cb), + dialog); + + gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), + GTK_WINDOW (parent)); + gtk_window_set_modal (GTK_WINDOW (dialog->dialog), TRUE); + + irc_network_dialog_network_update_buttons (dialog); + + return dialog->dialog; +} diff --git a/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.h b/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.h new file mode 100644 index 000000000..985849696 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-irc-network-dialog.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007-2008 Guillaume Desmottes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Guillaume Desmottes <gdesmott@gnome.org> + */ + +#ifndef __EMPATHY_IRC_NETWORK_DIALOG_H__ +#define __EMPATHY_IRC_NETWORK_DIALOG_H__ + +#include <gtk/gtkwidget.h> + +#include <libempathy/empathy-irc-network.h> + +G_BEGIN_DECLS + +GtkWidget * empathy_irc_network_dialog_show (EmpathyIrcNetwork *network, + GtkWidget *parent); + +G_END_DECLS + +#endif /* __EMPATHY_IRC_NETWORK_DIALOG_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-log-window.c b/gnome-2-22/libempathy-gtk/empathy-log-window.c new file mode 100644 index 000000000..c239ef5c3 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-log-window.c @@ -0,0 +1,1104 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libempathy/empathy-log-manager.h> +#include <libempathy/empathy-chatroom-manager.h> +#include <libempathy/empathy-chatroom.h> +#include <libempathy/empathy-message.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-time.h> + +#include "empathy-log-window.h" +#include "empathy-account-chooser.h" +#include "empathy-chat-view.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "LogWindow" + +typedef struct { + GtkWidget *window; + + GtkWidget *notebook; + + GtkWidget *entry_find; + GtkWidget *button_find; + GtkWidget *treeview_find; + GtkWidget *scrolledwindow_find; + EmpathyChatView *chatview_find; + GtkWidget *button_previous; + GtkWidget *button_next; + + GtkWidget *vbox_chats; + GtkWidget *account_chooser_chats; + GtkWidget *entry_chats; + GtkWidget *calendar_chats; + GtkWidget *treeview_chats; + GtkWidget *scrolledwindow_chats; + EmpathyChatView *chatview_chats; + + gchar *last_find; + + EmpathyLogManager *log_manager; +} EmpathyLogWindow; + +static void log_window_destroy_cb (GtkWidget *widget, + EmpathyLogWindow *window); +static void log_window_entry_find_changed_cb (GtkWidget *entry, + EmpathyLogWindow *window); +static void log_window_find_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window); +static void log_window_find_populate (EmpathyLogWindow *window, + const gchar *search_criteria); +static void log_window_find_setup (EmpathyLogWindow *window); +static void log_window_button_find_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window); +static void log_window_button_next_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window); +static void log_window_button_previous_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window); +static void log_window_chats_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window); +static void log_window_chats_populate (EmpathyLogWindow *window); +static void log_window_chats_setup (EmpathyLogWindow *window); +static void log_window_chats_accounts_changed_cb (GtkWidget *combobox, + EmpathyLogWindow *window); +static void log_window_chats_new_message_cb (EmpathyContact *own_contact, + EmpathyMessage *message, + EmpathyLogWindow *window); +static void log_window_chats_set_selected (EmpathyLogWindow *window, + McAccount *account, + const gchar *chat_id, + gboolean is_chatroom); +static gboolean log_window_chats_get_selected (EmpathyLogWindow *window, + McAccount **account, + gchar **chat_id, + gboolean *is_chatroom); +static void log_window_chats_get_messages (EmpathyLogWindow *window, + const gchar *date_to_show); +static void log_window_calendar_chats_day_selected_cb (GtkWidget *calendar, + EmpathyLogWindow *window); +static void log_window_calendar_chats_month_changed_cb (GtkWidget *calendar, + EmpathyLogWindow *window); +static void log_window_entry_chats_changed_cb (GtkWidget *entry, + EmpathyLogWindow *window); +static void log_window_entry_chats_activate_cb (GtkWidget *entry, + EmpathyLogWindow *window); + +enum { + COL_FIND_ACCOUNT_ICON, + COL_FIND_ACCOUNT_NAME, + COL_FIND_ACCOUNT, + COL_FIND_CHAT_NAME, + COL_FIND_CHAT_ID, + COL_FIND_IS_CHATROOM, + COL_FIND_DATE, + COL_FIND_DATE_READABLE, + COL_FIND_COUNT +}; + +enum { + COL_CHAT_ICON, + COL_CHAT_NAME, + COL_CHAT_ACCOUNT, + COL_CHAT_ID, + COL_CHAT_IS_CHATROOM, + COL_CHAT_COUNT +}; + +GtkWidget * +empathy_log_window_show (McAccount *account, + const gchar *chat_id, + gboolean is_chatroom, + GtkWindow *parent) +{ + static EmpathyLogWindow *window = NULL; + EmpathyAccountChooser *account_chooser; + GList *accounts; + gint account_num; + GladeXML *glade; + + if (window) { + gtk_window_present (GTK_WINDOW (window->window)); + + if (account && chat_id) { + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1); + log_window_chats_set_selected (window, account, + chat_id, is_chatroom); + } + + return window->window; + } + + window = g_new0 (EmpathyLogWindow, 1); + window->log_manager = empathy_log_manager_new (); + + glade = empathy_glade_get_file ("empathy-log-window.glade", + "log_window", + NULL, + "log_window", &window->window, + "notebook", &window->notebook, + "entry_find", &window->entry_find, + "button_find", &window->button_find, + "treeview_find", &window->treeview_find, + "scrolledwindow_find", &window->scrolledwindow_find, + "button_previous", &window->button_previous, + "button_next", &window->button_next, + "entry_chats", &window->entry_chats, + "calendar_chats", &window->calendar_chats, + "vbox_chats", &window->vbox_chats, + "treeview_chats", &window->treeview_chats, + "scrolledwindow_chats", &window->scrolledwindow_chats, + NULL); + empathy_glade_connect (glade, + window, + "log_window", "destroy", log_window_destroy_cb, + "entry_find", "changed", log_window_entry_find_changed_cb, + "button_previous", "clicked", log_window_button_previous_clicked_cb, + "button_next", "clicked", log_window_button_next_clicked_cb, + "button_find", "clicked", log_window_button_find_clicked_cb, + "entry_chats", "changed", log_window_entry_chats_changed_cb, + "entry_chats", "activate", log_window_entry_chats_activate_cb, + NULL); + + g_object_unref (glade); + + g_object_add_weak_pointer (G_OBJECT (window->window), + (gpointer) &window); + + /* We set this up here so we can block it when needed. */ + g_signal_connect (window->calendar_chats, "day-selected", + G_CALLBACK (log_window_calendar_chats_day_selected_cb), + window); + g_signal_connect (window->calendar_chats, "month-changed", + G_CALLBACK (log_window_calendar_chats_month_changed_cb), + window); + + /* Configure Search EmpathyChatView */ + window->chatview_find = empathy_chat_view_new (); + gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find), + GTK_WIDGET (window->chatview_find)); + gtk_widget_show (GTK_WIDGET (window->chatview_find)); + + /* Configure Contacts EmpathyChatView */ + window->chatview_chats = empathy_chat_view_new (); + gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats), + GTK_WIDGET (window->chatview_chats)); + gtk_widget_show (GTK_WIDGET (window->chatview_chats)); + + /* Account chooser for chats */ + window->account_chooser_chats = empathy_account_chooser_new (); + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats); + + gtk_box_pack_start (GTK_BOX (window->vbox_chats), + window->account_chooser_chats, + FALSE, TRUE, 0); + + g_signal_connect (window->account_chooser_chats, "changed", + G_CALLBACK (log_window_chats_accounts_changed_cb), + window); + + /* Populate */ + accounts = mc_accounts_list (); + account_num = g_list_length (accounts); + mc_accounts_list_free (accounts); + + if (account_num > 1) { + gtk_widget_show (window->vbox_chats); + gtk_widget_show (window->account_chooser_chats); + } else { + gtk_widget_hide (window->vbox_chats); + gtk_widget_hide (window->account_chooser_chats); + } + + /* Search List */ + log_window_find_setup (window); + + /* Contacts */ + log_window_chats_setup (window); + log_window_chats_populate (window); + + /* Select chat */ + if (account && chat_id) { + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1); + log_window_chats_set_selected (window, account, + chat_id, is_chatroom); + } + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (window->window), + GTK_WINDOW (parent)); + } + + gtk_widget_show (window->window); + + return window->window; +} + +static void +log_window_destroy_cb (GtkWidget *widget, + EmpathyLogWindow *window) +{ + g_signal_handlers_disconnect_by_func (window->log_manager, + log_window_chats_new_message_cb, + window); + + g_free (window->last_find); + g_object_unref (window->log_manager); + + g_free (window); +} + +/* + * Search code. + */ +static void +log_window_entry_find_changed_cb (GtkWidget *entry, + EmpathyLogWindow *window) +{ + const gchar *str; + gboolean is_sensitive = TRUE; + + str = gtk_entry_get_text (GTK_ENTRY (window->entry_find)); + + is_sensitive &= !G_STR_EMPTY (str); + is_sensitive &= + !window->last_find || + (window->last_find && strcmp (window->last_find, str) != 0); + + gtk_widget_set_sensitive (window->button_find, is_sensitive); +} + +static void +log_window_find_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeIter iter; + McAccount *account; + gchar *chat_id; + gboolean is_chatroom; + gchar *date; + EmpathyMessage *message; + GList *messages; + GList *l; + gboolean can_do_previous; + gboolean can_do_next; + + /* Get selected information */ + view = GTK_TREE_VIEW (window->treeview_find); + model = gtk_tree_view_get_model (view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { + gtk_widget_set_sensitive (window->button_previous, FALSE); + gtk_widget_set_sensitive (window->button_next, FALSE); + + empathy_chat_view_clear (window->chatview_find); + + return; + } + + gtk_widget_set_sensitive (window->button_previous, TRUE); + gtk_widget_set_sensitive (window->button_next, TRUE); + + gtk_tree_model_get (model, &iter, + COL_FIND_ACCOUNT, &account, + COL_FIND_CHAT_ID, &chat_id, + COL_FIND_IS_CHATROOM, &is_chatroom, + COL_FIND_DATE, &date, + -1); + + /* Clear all current messages shown in the textview */ + empathy_chat_view_clear (window->chatview_find); + + /* Turn off scrolling temporarily */ + empathy_chat_view_scroll (window->chatview_find, FALSE); + + /* Get messages */ + messages = empathy_log_manager_get_messages_for_date (window->log_manager, + account, + chat_id, + is_chatroom, + date); + g_object_unref (account); + g_free (date); + g_free (chat_id); + + for (l = messages; l; l = l->next) { + message = l->data; + empathy_chat_view_append_message (window->chatview_find, message); + g_object_unref (message); + } + g_list_free (messages); + + /* Scroll to the most recent messages */ + empathy_chat_view_scroll (window->chatview_find, TRUE); + + /* Highlight and find messages */ + empathy_chat_view_highlight (window->chatview_find, + window->last_find); + empathy_chat_view_find_next (window->chatview_find, + window->last_find, + TRUE); + empathy_chat_view_find_abilities (window->chatview_find, + window->last_find, + &can_do_previous, + &can_do_next); + gtk_widget_set_sensitive (window->button_previous, can_do_previous); + gtk_widget_set_sensitive (window->button_next, can_do_next); + gtk_widget_set_sensitive (window->button_find, FALSE); +} + +static void +log_window_find_populate (EmpathyLogWindow *window, + const gchar *search_criteria) +{ + GList *hits, *l; + + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + + view = GTK_TREE_VIEW (window->treeview_find); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + store = GTK_LIST_STORE (model); + + empathy_chat_view_clear (window->chatview_find); + + gtk_list_store_clear (store); + + if (G_STR_EMPTY (search_criteria)) { + /* Just clear the search. */ + return; + } + + hits = empathy_log_manager_search_new (window->log_manager, search_criteria); + + for (l = hits; l; l = l->next) { + EmpathyLogSearchHit *hit; + const gchar *account_name; + const gchar *account_icon; + gchar *date_readable; + + hit = l->data; + + /* Protect against invalid data (corrupt or old log files. */ + if (!hit->account || !hit->chat_id) { + continue; + } + + date_readable = empathy_log_manager_get_date_readable (hit->date); + account_name = mc_account_get_display_name (hit->account); + account_icon = empathy_icon_name_from_account (hit->account); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_FIND_ACCOUNT_ICON, account_icon, + COL_FIND_ACCOUNT_NAME, account_name, + COL_FIND_ACCOUNT, hit->account, + COL_FIND_CHAT_NAME, hit->chat_id, /* FIXME */ + COL_FIND_CHAT_ID, hit->chat_id, + COL_FIND_IS_CHATROOM, hit->is_chatroom, + COL_FIND_DATE, hit->date, + COL_FIND_DATE_READABLE, date_readable, + -1); + + g_free (date_readable); + + /* FIXME: Update COL_FIND_CHAT_NAME */ + if (hit->is_chatroom) { + } else { + } + } + + if (hits) { + empathy_log_manager_search_free (hits); + } +} + +static void +log_window_find_setup (EmpathyLogWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeSortable *sortable; + GtkTreeViewColumn *column; + GtkListStore *store; + GtkCellRenderer *cell; + gint offset; + + view = GTK_TREE_VIEW (window->treeview_find); + selection = gtk_tree_view_get_selection (view); + + /* New store */ + store = gtk_list_store_new (COL_FIND_COUNT, + G_TYPE_STRING, /* account icon name */ + G_TYPE_STRING, /* account name */ + MC_TYPE_ACCOUNT, /* account */ + G_TYPE_STRING, /* chat name */ + G_TYPE_STRING, /* chat id */ + G_TYPE_BOOLEAN, /* is chatroom */ + G_TYPE_STRING, /* date */ + G_TYPE_STRING); /* date_readable */ + + model = GTK_TREE_MODEL (store); + sortable = GTK_TREE_SORTABLE (store); + + gtk_tree_view_set_model (view, model); + + /* New column */ + column = gtk_tree_view_column_new (); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, + "icon-name", + COL_FIND_ACCOUNT_ICON); + + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, + "text", + COL_FIND_ACCOUNT_NAME); + + gtk_tree_view_column_set_title (column, _("Account")); + gtk_tree_view_append_column (view, column); + + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_clickable (column, TRUE); + + cell = gtk_cell_renderer_text_new (); + offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"), + cell, "text", COL_FIND_CHAT_NAME, + NULL); + + column = gtk_tree_view_get_column (view, offset - 1); + gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_clickable (column, TRUE); + + cell = gtk_cell_renderer_text_new (); + offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"), + cell, "text", COL_FIND_DATE_READABLE, + NULL); + + column = gtk_tree_view_get_column (view, offset - 1); + gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_clickable (column, TRUE); + + /* Set up treeview properties */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_sortable_set_sort_column_id (sortable, + COL_FIND_DATE, + GTK_SORT_ASCENDING); + + /* Set up signals */ + g_signal_connect (selection, "changed", + G_CALLBACK (log_window_find_changed_cb), + window); + + g_object_unref (store); +} + +static void +log_window_button_find_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (window->entry_find)); + + /* Don't find the same crap again */ + if (window->last_find && strcmp (window->last_find, str) == 0) { + return; + } + + g_free (window->last_find); + window->last_find = g_strdup (str); + + log_window_find_populate (window, str); +} + +static void +log_window_button_next_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window) +{ + if (window->last_find) { + gboolean can_do_previous; + gboolean can_do_next; + + empathy_chat_view_find_next (window->chatview_find, + window->last_find, + FALSE); + empathy_chat_view_find_abilities (window->chatview_find, + window->last_find, + &can_do_previous, + &can_do_next); + gtk_widget_set_sensitive (window->button_previous, can_do_previous); + gtk_widget_set_sensitive (window->button_next, can_do_next); + } +} + +static void +log_window_button_previous_clicked_cb (GtkWidget *widget, + EmpathyLogWindow *window) +{ + if (window->last_find) { + gboolean can_do_previous; + gboolean can_do_next; + + empathy_chat_view_find_previous (window->chatview_find, + window->last_find, + FALSE); + empathy_chat_view_find_abilities (window->chatview_find, + window->last_find, + &can_do_previous, + &can_do_next); + gtk_widget_set_sensitive (window->button_previous, can_do_previous); + gtk_widget_set_sensitive (window->button_next, can_do_next); + } +} + +/* + * Chats Code + */ + +static void +log_window_chats_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) +{ + /* Use last date by default */ + gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats)); + + log_window_chats_get_messages (window, NULL); +} + +static void +log_window_chats_populate (EmpathyLogWindow *window) +{ + EmpathyAccountChooser *account_chooser; + McAccount *account; + GList *chats, *l; + + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats); + account = empathy_account_chooser_get_account (account_chooser); + + view = GTK_TREE_VIEW (window->treeview_chats); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + store = GTK_LIST_STORE (model); + + /* Block signals to stop the logs being retrieved prematurely */ + g_signal_handlers_block_by_func (selection, + log_window_chats_changed_cb, + window); + + gtk_list_store_clear (store); + + chats = empathy_log_manager_get_chats (window->log_manager, account); + for (l = chats; l; l = l->next) { + EmpathyLogSearchHit *hit; + + hit = l->data; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_CHAT_ICON, "empathy-available", /* FIXME */ + COL_CHAT_NAME, hit->chat_id, + COL_CHAT_ACCOUNT, account, + COL_CHAT_ID, hit->chat_id, + COL_CHAT_IS_CHATROOM, hit->is_chatroom, + -1); + + /* FIXME: Update COL_CHAT_ICON/NAME */ + if (hit->is_chatroom) { + } else { + } + } + empathy_log_manager_search_free (chats); + + /* Unblock signals */ + g_signal_handlers_unblock_by_func (selection, + log_window_chats_changed_cb, + window); + + + g_object_unref (account); +} + +static void +log_window_chats_setup (EmpathyLogWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeSortable *sortable; + GtkTreeViewColumn *column; + GtkListStore *store; + GtkCellRenderer *cell; + + view = GTK_TREE_VIEW (window->treeview_chats); + selection = gtk_tree_view_get_selection (view); + + /* new store */ + store = gtk_list_store_new (COL_CHAT_COUNT, + G_TYPE_STRING, /* icon */ + G_TYPE_STRING, /* name */ + MC_TYPE_ACCOUNT, /* account */ + G_TYPE_STRING, /* id */ + G_TYPE_BOOLEAN); /* is chatroom */ + + model = GTK_TREE_MODEL (store); + sortable = GTK_TREE_SORTABLE (store); + + gtk_tree_view_set_model (view, model); + + /* new column */ + column = gtk_tree_view_column_new (); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, + "icon-name", + COL_CHAT_ICON); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, + "text", + COL_CHAT_NAME); + + gtk_tree_view_append_column (view, column); + + /* set up treeview properties */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_sortable_set_sort_column_id (sortable, + COL_CHAT_NAME, + GTK_SORT_ASCENDING); + + /* set up signals */ + g_signal_connect (selection, "changed", + G_CALLBACK (log_window_chats_changed_cb), + window); + + g_object_unref (store); +} + +static void +log_window_chats_accounts_changed_cb (GtkWidget *combobox, + EmpathyLogWindow *window) +{ + /* Clear all current messages shown in the textview */ + empathy_chat_view_clear (window->chatview_chats); + + log_window_chats_populate (window); +} + +static void +log_window_chats_new_message_cb (EmpathyContact *own_contact, + EmpathyMessage *message, + EmpathyLogWindow *window) +{ + empathy_chat_view_append_message (window->chatview_chats, message); + + /* Scroll to the most recent messages */ + empathy_chat_view_scroll_down (window->chatview_chats); +} + +static void +log_window_chats_set_selected (EmpathyLogWindow *window, + McAccount *account, + const gchar *chat_id, + gboolean is_chatroom) +{ + EmpathyAccountChooser *account_chooser; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreePath *path; + gboolean ok; + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats); + empathy_account_chooser_set_account (account_chooser, account); + + view = GTK_TREE_VIEW (window->treeview_chats); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_model_get_iter_first (model, &iter)) { + return; + } + + for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) { + McAccount *this_account; + gchar *this_chat_id; + gboolean this_is_chatroom; + + gtk_tree_model_get (model, &iter, + COL_CHAT_ACCOUNT, &this_account, + COL_CHAT_ID, &this_chat_id, + COL_CHAT_IS_CHATROOM, &this_is_chatroom, + -1); + + if (empathy_account_equal (this_account, account) && + strcmp (this_chat_id, chat_id) == 0 && + this_is_chatroom == is_chatroom) { + gtk_tree_selection_select_iter (selection, &iter); + path = gtk_tree_model_get_path (model, &iter); + gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0); + gtk_tree_path_free (path); + g_object_unref (this_account); + g_free (this_chat_id); + break; + } + + g_object_unref (this_account); + g_free (this_chat_id); + } +} + +static gboolean +log_window_chats_get_selected (EmpathyLogWindow *window, + McAccount **account, + gchar **chat_id, + gboolean *is_chatroom) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + gchar *id = NULL; + McAccount *acc = NULL; + gboolean room = FALSE; + + view = GTK_TREE_VIEW (window->treeview_chats); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { + return FALSE; + } + + gtk_tree_model_get (model, &iter, + COL_CHAT_ACCOUNT, &acc, + COL_CHAT_ID, &id, + COL_CHAT_IS_CHATROOM, &room, + -1); + + if (chat_id) { + *chat_id = id; + } else { + g_free (id); + } + if (account) { + *account = acc; + } else { + g_object_unref (acc); + } + if (is_chatroom) { + *is_chatroom = room; + } + + return TRUE; +} + +static void +log_window_chats_get_messages (EmpathyLogWindow *window, + const gchar *date_to_show) +{ + McAccount *account; + gchar *chat_id; + gboolean is_chatroom; + EmpathyMessage *message; + GList *messages; + GList *dates = NULL; + GList *l; + const gchar *date; + guint year_selected; + guint year; + guint month; + guint month_selected; + guint day; + + if (!log_window_chats_get_selected (window, &account, + &chat_id, &is_chatroom)) { + return; + } + + g_signal_handlers_block_by_func (window->calendar_chats, + log_window_calendar_chats_day_selected_cb, + window); + + /* Either use the supplied date or get the last */ + date = date_to_show; + if (!date) { + gboolean day_selected = FALSE; + + /* Get a list of dates and show them on the calendar */ + dates = empathy_log_manager_get_dates (window->log_manager, + account, chat_id, + is_chatroom); + + for (l = dates; l; l = l->next) { + const gchar *str; + + str = l->data; + if (!str) { + continue; + } + + sscanf (str, "%4d%2d%2d", &year, &month, &day); + gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats), + &year_selected, + &month_selected, + NULL); + + month_selected++; + + if (!l->next) { + date = str; + } + + if (year != year_selected || month != month_selected) { + continue; + } + + + empathy_debug (DEBUG_DOMAIN, "Marking date:'%s'", str); + gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), day); + + if (l->next) { + continue; + } + + day_selected = TRUE; + + gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day); + } + + if (!day_selected) { + /* Unselect the day in the calendar */ + gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0); + } + } else { + sscanf (date, "%4d%2d%2d", &year, &month, &day); + gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats), + &year_selected, + &month_selected, + NULL); + + month_selected++; + + if (year != year_selected && month != month_selected) { + day = 0; + } + + gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day); + } + + g_signal_handlers_unblock_by_func (window->calendar_chats, + log_window_calendar_chats_day_selected_cb, + window); + + if (!date) { + goto OUT; + } + + /* Clear all current messages shown in the textview */ + empathy_chat_view_clear (window->chatview_chats); + + /* Turn off scrolling temporarily */ + empathy_chat_view_scroll (window->chatview_find, FALSE); + + /* Get messages */ + messages = empathy_log_manager_get_messages_for_date (window->log_manager, + account, chat_id, + is_chatroom, + date); + + for (l = messages; l; l = l->next) { + message = l->data; + + empathy_chat_view_append_message (window->chatview_chats, + message); + g_object_unref (message); + } + g_list_free (messages); + + /* Turn back on scrolling */ + empathy_chat_view_scroll (window->chatview_find, TRUE); + + /* Scroll to the most recent messages */ + empathy_chat_view_scroll_down (window->chatview_chats); + + /* Give the search entry main focus */ + gtk_widget_grab_focus (window->entry_chats); + +OUT: + g_list_foreach (dates, (GFunc) g_free, NULL); + g_list_free (dates); + g_object_unref (account); + g_free (chat_id); +} + +static void +log_window_calendar_chats_day_selected_cb (GtkWidget *calendar, + EmpathyLogWindow *window) +{ + guint year; + guint month; + guint day; + + gchar *date; + + gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day); + + /* We need this hear because it appears that the months start from 0 */ + month++; + + date = g_strdup_printf ("%4.4d%2.2d%2.2d", year, month, day); + + empathy_debug (DEBUG_DOMAIN, "Currently selected date is:'%s'", date); + + log_window_chats_get_messages (window, date); + + g_free (date); +} + +static void +log_window_calendar_chats_month_changed_cb (GtkWidget *calendar, + EmpathyLogWindow *window) +{ + McAccount *account; + gchar *chat_id; + gboolean is_chatroom; + guint year_selected; + guint month_selected; + + GList *dates; + GList *l; + + gtk_calendar_clear_marks (GTK_CALENDAR (calendar)); + + if (!log_window_chats_get_selected (window, &account, + &chat_id, &is_chatroom)) { + empathy_debug (DEBUG_DOMAIN, "No chat selected to get dates for..."); + return; + } + + g_object_get (calendar, + "month", &month_selected, + "year", &year_selected, + NULL); + + /* We need this hear because it appears that the months start from 0 */ + month_selected++; + + /* Get the log object for this contact */ + dates = empathy_log_manager_get_dates (window->log_manager, account, + chat_id, is_chatroom); + g_object_unref (account); + g_free (chat_id); + + for (l = dates; l; l = l->next) { + const gchar *str; + guint year; + guint month; + guint day; + + str = l->data; + if (!str) { + continue; + } + + sscanf (str, "%4d%2d%2d", &year, &month, &day); + + if (year == year_selected && month == month_selected) { + empathy_debug (DEBUG_DOMAIN, "Marking date:'%s'", str); + gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), day); + } + } + + g_list_foreach (dates, (GFunc) g_free, NULL); + g_list_free (dates); + + empathy_debug (DEBUG_DOMAIN, + "Currently showing month %d and year %d", + month_selected, year_selected); +} + +static void +log_window_entry_chats_changed_cb (GtkWidget *entry, + EmpathyLogWindow *window) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats)); + empathy_chat_view_highlight (window->chatview_chats, str); + + if (str) { + empathy_chat_view_find_next (window->chatview_chats, + str, + TRUE); + } +} + +static void +log_window_entry_chats_activate_cb (GtkWidget *entry, + EmpathyLogWindow *window) +{ + const gchar *str; + + str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats)); + + if (str) { + empathy_chat_view_find_next (window->chatview_chats, + str, + FALSE); + } +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-log-window.glade b/gnome-2-22/libempathy-gtk/empathy-log-window.glade new file mode 100644 index 000000000..8dfd59c99 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-log-window.glade @@ -0,0 +1,470 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="log_window"> + <property name="title" translatable="yes">Previous Conversations</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">640</property> + <property name="default_height">450</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="icon_name">document-open-recent</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkNotebook" id="notebook"> + <property name="border_width">2</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkVBox" id="vbox192"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox144"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label628"> + <property name="visible">True</property> + <property name="label" translatable="yes">_For:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_find</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_find"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_find"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-find</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">120</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow14"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview_find"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox215"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow_find"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox171"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <placeholder/> + </child> + + <child> + <widget class="GtkButton" id="button_next"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-forward</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="button_previous"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-back</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label595"> + <property name="visible">True</property> + <property name="label" translatable="yes">Search</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table7"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox143"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkImage" id="image247"> + <property name="visible">True</property> + <property name="stock">gtk-find</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_chats"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox191"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow13"> + <property name="width_request">150</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview_chats"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkCalendar" id="calendar_chats"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="display_options">GTK_CALENDAR_SHOW_HEADING|GTK_CALENDAR_SHOW_DAY_NAMES</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow_chats"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_chats"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label596"> + <property name="visible">True</property> + <property name="label" translatable="yes">Conversations</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-log-window.h b/gnome-2-22/libempathy-gtk/empathy-log-window.h new file mode 100644 index 000000000..a45cbbb84 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-log-window.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_LOG_WINDOW_H__ +#define __EMPATHY_LOG_WINDOW_H__ + +#include <libmissioncontrol/mc-account.h> + +G_BEGIN_DECLS + +GtkWidget * empathy_log_window_show (McAccount *account, + const gchar *chat_id, + gboolean chatroom, + GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_LOG_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-main-window.c b/gnome-2-22/libempathy-gtk/empathy-main-window.c new file mode 100644 index 000000000..ca8a16abf --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-main-window.c @@ -0,0 +1,1160 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <sys/stat.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-chatroom-manager.h> +#include <libempathy/empathy-chatroom.h> +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-status-presets.h> + +#include "empathy-main-window.h" +#include "empathy-contact-dialogs.h" +#include "ephy-spinner.h" +#include "empathy-contact-list-store.h" +#include "empathy-contact-list-view.h" +#include "empathy-presence-chooser.h" +#include "empathy-ui-utils.h" +#include "empathy-geometry.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-accounts-dialog.h" +#include "empathy-about-dialog.h" +#include "empathy-new-chatroom-dialog.h" +#include "empathy-chatrooms-window.h" +#include "empathy-log-window.h" +#include "empathy-new-message-dialog.h" +#include "empathy-gtk-enum-types.h" + +#define DEBUG_DOMAIN "MainWindow" + +/* Minimum width of roster window if something goes wrong. */ +#define MIN_WIDTH 50 + +/* Accels (menu shortcuts) can be configured and saved */ +#define ACCELS_FILENAME "accels.txt" + +/* Name in the geometry file */ +#define GEOMETRY_NAME "main-window" + +typedef struct { + EmpathyContactListView *list_view; + EmpathyContactListStore *list_store; + MissionControl *mc; + EmpathyChatroomManager *chatroom_manager; + + GtkWidget *window; + GtkWidget *main_vbox; + GtkWidget *throbber; + GtkWidget *presence_toolbar; + GtkWidget *presence_chooser; + GtkWidget *errors_vbox; + + GtkWidget *room; + GtkWidget *room_menu; + GtkWidget *room_sep; + GtkWidget *room_join_favorites; + GtkWidget *edit_context; + GtkWidget *edit_context_separator; + + guint size_timeout_id; + GHashTable *errors; + + /* Widgets that are enabled when there is... */ + GList *widgets_connected; /* ... connected accounts */ + GList *widgets_disconnected; /* ... disconnected accounts */ +} EmpathyMainWindow; + +static void main_window_destroy_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_favorite_chatroom_menu_setup (EmpathyMainWindow *window); +static void main_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyMainWindow *window); +static void main_window_favorite_chatroom_menu_removed_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyMainWindow *window); +static void main_window_favorite_chatroom_menu_activate_cb (GtkMenuItem *menu_item, + EmpathyChatroom *chatroom); +static void main_window_favorite_chatroom_menu_update (EmpathyMainWindow *window); +static void main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window, + EmpathyChatroom *chatroom); +static void main_window_favorite_chatroom_join (EmpathyChatroom *chatroom); +static void main_window_chat_quit_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_chat_new_message_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_chat_history_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_room_join_new_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_room_join_favorites_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_room_manage_favorites_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_chat_add_contact_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_chat_show_offline_cb (GtkCheckMenuItem *item, + EmpathyMainWindow *window); +static gboolean main_window_edit_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + EmpathyMainWindow *window); +static void main_window_edit_accounts_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_edit_personal_information_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_edit_preferences_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_help_about_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static void main_window_help_contents_cb (GtkWidget *widget, + EmpathyMainWindow *window); +static gboolean main_window_throbber_button_press_event_cb (GtkWidget *throbber_ebox, + GdkEventButton *event, + EmpathyMainWindow *window); +static void main_window_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyMainWindow *window); +static void main_window_update_status (EmpathyMainWindow *window); +static void main_window_accels_load (void); +static void main_window_accels_save (void); +static void main_window_connection_items_setup (EmpathyMainWindow *window, + GladeXML *glade); +static gboolean main_window_configure_event_timeout_cb (EmpathyMainWindow *window); +static gboolean main_window_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + EmpathyMainWindow *window); +static void main_window_notify_show_offline_cb (EmpathyConf *conf, + const gchar *key, + gpointer check_menu_item); +static void main_window_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window); +static void main_window_notify_compact_contact_list_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window); +static void main_window_notify_sort_criterium_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window); + +GtkWidget * +empathy_main_window_show (void) +{ + static EmpathyMainWindow *window = NULL; + EmpathyContactList *list_iface; + GladeXML *glade; + EmpathyConf *conf; + GtkWidget *sw; + GtkWidget *show_offline_widget; + GtkWidget *ebox; + GtkToolItem *item; + gboolean show_offline; + gboolean show_avatars; + gboolean compact_contact_list; + gint x, y, w, h; + + if (window) { + empathy_window_present (GTK_WINDOW (window->window), TRUE); + return window->window; + } + + window = g_new0 (EmpathyMainWindow, 1); + + /* Set up interface */ + glade = empathy_glade_get_file ("empathy-main-window.glade", + "main_window", + NULL, + "main_window", &window->window, + "main_vbox", &window->main_vbox, + "errors_vbox", &window->errors_vbox, + "chat_show_offline", &show_offline_widget, + "room", &window->room, + "room_sep", &window->room_sep, + "room_join_favorites", &window->room_join_favorites, + "edit_context", &window->edit_context, + "edit_context_separator", &window->edit_context_separator, + "presence_toolbar", &window->presence_toolbar, + "roster_scrolledwindow", &sw, + NULL); + + empathy_glade_connect (glade, + window, + "main_window", "destroy", main_window_destroy_cb, + "main_window", "configure_event", main_window_configure_event_cb, + "chat_quit", "activate", main_window_chat_quit_cb, + "chat_new_message", "activate", main_window_chat_new_message_cb, + "chat_history", "activate", main_window_chat_history_cb, + "room_join_new", "activate", main_window_room_join_new_cb, + "room_join_favorites", "activate", main_window_room_join_favorites_cb, + "room_manage_favorites", "activate", main_window_room_manage_favorites_cb, + "chat_add_contact", "activate", main_window_chat_add_contact_cb, + "chat_show_offline", "toggled", main_window_chat_show_offline_cb, + "edit", "button-press-event", main_window_edit_button_press_event_cb, + "edit_accounts", "activate", main_window_edit_accounts_cb, + "edit_personal_information", "activate", main_window_edit_personal_information_cb, + "edit_preferences", "activate", main_window_edit_preferences_cb, + "help_about", "activate", main_window_help_about_cb, + "help_contents", "activate", main_window_help_contents_cb, + NULL); + + /* Set up connection related widgets. */ + main_window_connection_items_setup (window, glade); + g_object_unref (glade); + + window->mc = empathy_mission_control_new (); + dbus_g_proxy_connect_signal (DBUS_G_PROXY (window->mc), "AccountStatusChanged", + G_CALLBACK (main_window_status_changed_cb), + window, NULL); + + window->errors = g_hash_table_new_full (empathy_account_hash, + empathy_account_equal, + g_object_unref, + NULL); + + /* Set up menu */ + main_window_favorite_chatroom_menu_setup (window); + + gtk_widget_hide (window->edit_context); + gtk_widget_hide (window->edit_context_separator); + + /* Set up presence chooser */ + window->presence_chooser = empathy_presence_chooser_new (); + gtk_widget_show (window->presence_chooser); + item = gtk_tool_item_new (); + gtk_widget_show (GTK_WIDGET (item)); + gtk_container_add (GTK_CONTAINER (item), window->presence_chooser); + gtk_tool_item_set_is_important (item, TRUE); + gtk_tool_item_set_expand (item, TRUE); + gtk_toolbar_insert (GTK_TOOLBAR (window->presence_toolbar), item, -1); + + /* Set up the throbber */ + ebox = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (ebox), FALSE); + gtk_widget_set_tooltip_text (ebox, _("Show and edit accounts")); + g_signal_connect (ebox, + "button-press-event", + G_CALLBACK (main_window_throbber_button_press_event_cb), + window); + gtk_widget_show (ebox); + + window->throbber = ephy_spinner_new (); + ephy_spinner_set_size (EPHY_SPINNER (window->throbber), GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_container_add (GTK_CONTAINER (ebox), window->throbber); + gtk_widget_show (window->throbber); + + item = gtk_tool_item_new (); + gtk_container_add (GTK_CONTAINER (item), ebox); + gtk_toolbar_insert (GTK_TOOLBAR (window->presence_toolbar), item, -1); + gtk_widget_show (GTK_WIDGET (item)); + + /* Set up contact list. */ + empathy_status_presets_get_all (); + + list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_new ()); + window->list_store = empathy_contact_list_store_new (list_iface); + window->list_view = empathy_contact_list_view_new (window->list_store, + EMPATHY_CONTACT_LIST_FEATURE_ALL); + g_object_unref (list_iface); + + gtk_widget_show (GTK_WIDGET (window->list_view)); + gtk_container_add (GTK_CONTAINER (sw), + GTK_WIDGET (window->list_view)); + + /* Load user-defined accelerators. */ + main_window_accels_load (); + + /* Set window size. */ + empathy_geometry_load (GEOMETRY_NAME, &x, &y, &w, &h); + + if (w >= 1 && h >= 1) { + /* Use the defaults from the glade file if we + * don't have good w, h geometry. + */ + empathy_debug (DEBUG_DOMAIN, "Configuring window default size w:%d, h:%d", w, h); + gtk_window_set_default_size (GTK_WINDOW (window->window), w, h); + } + + if (x >= 0 && y >= 0) { + /* Let the window manager position it if we + * don't have good x, y coordinates. + */ + empathy_debug (DEBUG_DOMAIN, "Configuring window default position x:%d, y:%d", x, y); + gtk_window_move (GTK_WINDOW (window->window), x, y); + } + + conf = empathy_conf_get (); + + /* Show offline ? */ + empathy_conf_get_bool (conf, + EMPATHY_PREFS_CONTACTS_SHOW_OFFLINE, + &show_offline); + empathy_conf_notify_add (conf, + EMPATHY_PREFS_CONTACTS_SHOW_OFFLINE, + main_window_notify_show_offline_cb, + show_offline_widget); + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (show_offline_widget), + show_offline); + + /* Show avatars ? */ + empathy_conf_get_bool (conf, + EMPATHY_PREFS_UI_SHOW_AVATARS, + &show_avatars); + empathy_conf_notify_add (conf, + EMPATHY_PREFS_UI_SHOW_AVATARS, + (EmpathyConfNotifyFunc) main_window_notify_show_avatars_cb, + window); + empathy_contact_list_store_set_show_avatars (window->list_store, show_avatars); + + /* Is compact ? */ + empathy_conf_get_bool (conf, + EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, + &compact_contact_list); + empathy_conf_notify_add (conf, + EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, + (EmpathyConfNotifyFunc) main_window_notify_compact_contact_list_cb, + window); + empathy_contact_list_store_set_is_compact (window->list_store, compact_contact_list); + + /* Sort criterium */ + empathy_conf_notify_add (conf, + EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, + (EmpathyConfNotifyFunc) main_window_notify_sort_criterium_cb, + window); + main_window_notify_sort_criterium_cb (conf, + EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, + window); + + main_window_update_status (window); + + return window->window; +} + +static void +main_window_destroy_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + /* Save user-defined accelerators. */ + main_window_accels_save (); + + dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (window->mc), "AccountStatusChanged", + G_CALLBACK (main_window_status_changed_cb), + window); + + if (window->size_timeout_id) { + g_source_remove (window->size_timeout_id); + } + + g_list_free (window->widgets_connected); + g_list_free (window->widgets_disconnected); + + g_object_unref (window->mc); + g_object_unref (window->list_store); + g_hash_table_destroy (window->errors); + + g_free (window); +} + +static void +main_window_favorite_chatroom_menu_setup (EmpathyMainWindow *window) +{ + GList *chatrooms, *l; + + window->chatroom_manager = empathy_chatroom_manager_new (); + chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL); + window->room_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (window->room)); + + for (l = chatrooms; l; l = l->next) { + main_window_favorite_chatroom_menu_add (window, l->data); + } + + if (!chatrooms) { + gtk_widget_hide (window->room_sep); + } + + gtk_widget_set_sensitive (window->room_join_favorites, chatrooms != NULL); + + g_signal_connect (window->chatroom_manager, "chatroom-added", + G_CALLBACK (main_window_favorite_chatroom_menu_added_cb), + window); + g_signal_connect (window->chatroom_manager, "chatroom-removed", + G_CALLBACK (main_window_favorite_chatroom_menu_removed_cb), + window); + + g_list_free (chatrooms); +} + +static void +main_window_favorite_chatroom_menu_added_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyMainWindow *window) +{ + main_window_favorite_chatroom_menu_add (window, chatroom); + gtk_widget_show (window->room_sep); + gtk_widget_set_sensitive (window->room_join_favorites, TRUE); +} + +static void +main_window_favorite_chatroom_menu_removed_cb (EmpathyChatroomManager *manager, + EmpathyChatroom *chatroom, + EmpathyMainWindow *window) +{ + GtkWidget *menu_item; + + menu_item = g_object_get_data (G_OBJECT (chatroom), "menu_item"); + + g_object_set_data (G_OBJECT (chatroom), "menu_item", NULL); + gtk_widget_destroy (menu_item); + + main_window_favorite_chatroom_menu_update (window); +} + +static void +main_window_favorite_chatroom_menu_activate_cb (GtkMenuItem *menu_item, + EmpathyChatroom *chatroom) +{ + main_window_favorite_chatroom_join (chatroom); +} + +static void +main_window_favorite_chatroom_menu_update (EmpathyMainWindow *window) +{ + GList *chatrooms; + + chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL); + + if (chatrooms) { + gtk_widget_show (window->room_sep); + } else { + gtk_widget_hide (window->room_sep); + } + + gtk_widget_set_sensitive (window->room_join_favorites, chatrooms != NULL); + g_list_free (chatrooms); +} + +static void +main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window, + EmpathyChatroom *chatroom) +{ + GtkWidget *menu_item; + const gchar *name; + + if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) { + return; + } + + name = empathy_chatroom_get_name (chatroom); + menu_item = gtk_menu_item_new_with_label (name); + + g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item); + g_signal_connect (menu_item, "activate", + G_CALLBACK (main_window_favorite_chatroom_menu_activate_cb), + chatroom); + + gtk_menu_shell_insert (GTK_MENU_SHELL (window->room_menu), + menu_item, 3); + + gtk_widget_show (menu_item); +} + +static void +main_window_favorite_chatroom_join (EmpathyChatroom *chatroom) +{ + MissionControl *mc; + McAccount *account; + const gchar *room; + + mc = empathy_mission_control_new (); + account = empathy_chatroom_get_account (chatroom); + room = empathy_chatroom_get_room (chatroom); + + empathy_debug (DEBUG_DOMAIN, "Requesting channel for '%s'", room); + + mission_control_request_channel_with_string_handle (mc, + account, + TP_IFACE_CHANNEL_TYPE_TEXT, + room, + TP_HANDLE_TYPE_ROOM, + NULL, NULL); + g_object_unref (mc); +} + +static void +main_window_chat_quit_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + gtk_main_quit (); +} + +static void +main_window_chat_new_message_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_new_message_dialog_show (GTK_WINDOW (window->window)); +} + +static void +main_window_chat_history_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window->window)); +} + +static void +main_window_room_join_new_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_new_chatroom_dialog_show (GTK_WINDOW (window->window)); +} + +static void +main_window_room_join_favorites_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + GList *chatrooms, *l; + + chatrooms = empathy_chatroom_manager_get_chatrooms (window->chatroom_manager, NULL); + for (l = chatrooms; l; l = l->next) { + main_window_favorite_chatroom_join (l->data); + } + g_list_free (chatrooms); +} + +static void +main_window_room_manage_favorites_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_chatrooms_window_show (GTK_WINDOW (window->window)); +} + +static void +main_window_chat_add_contact_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_new_contact_dialog_show (GTK_WINDOW (window->window)); +} + +static void +main_window_chat_show_offline_cb (GtkCheckMenuItem *item, + EmpathyMainWindow *window) +{ + gboolean current; + + current = gtk_check_menu_item_get_active (item); + + empathy_conf_set_bool (empathy_conf_get (), + EMPATHY_PREFS_CONTACTS_SHOW_OFFLINE, + current); + + /* Turn off sound just while we alter the contact list. */ + // FIXME: empathy_sound_set_enabled (FALSE); + empathy_contact_list_store_set_show_offline (window->list_store, current); + //empathy_sound_set_enabled (TRUE); +} + +static gboolean +main_window_edit_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + EmpathyMainWindow *window) +{ + EmpathyContact *contact; + gchar *group; + + if (!event->button == 1) { + return FALSE; + } + + group = empathy_contact_list_view_get_selected_group (window->list_view); + if (group) { + GtkMenuItem *item; + GtkWidget *label; + GtkWidget *submenu; + + item = GTK_MENU_ITEM (window->edit_context); + label = gtk_bin_get_child (GTK_BIN (item)); + gtk_label_set_text (GTK_LABEL (label), _("Group")); + + gtk_widget_show (window->edit_context); + gtk_widget_show (window->edit_context_separator); + + submenu = empathy_contact_list_view_get_group_menu (window->list_view); + gtk_menu_item_set_submenu (item, submenu); + + g_free (group); + + return FALSE; + } + + contact = empathy_contact_list_view_get_selected (window->list_view); + if (contact) { + GtkMenuItem *item; + GtkWidget *label; + GtkWidget *submenu; + + item = GTK_MENU_ITEM (window->edit_context); + label = gtk_bin_get_child (GTK_BIN (item)); + gtk_label_set_text (GTK_LABEL (label), _("Contact")); + + gtk_widget_show (window->edit_context); + gtk_widget_show (window->edit_context_separator); + + submenu = empathy_contact_list_view_get_contact_menu (window->list_view, + contact); + gtk_menu_item_set_submenu (item, submenu); + + g_object_unref (contact); + + return FALSE; + } + + gtk_widget_hide (window->edit_context); + gtk_widget_hide (window->edit_context_separator); + + return FALSE; +} + +static void +main_window_edit_accounts_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_accounts_dialog_show (GTK_WINDOW (window->window)); +} + +static void +main_window_edit_personal_information_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + GSList *accounts; + + accounts = mission_control_get_online_connections (window->mc, NULL); + if (accounts) { + EmpathyContactFactory *factory; + EmpathyContact *contact; + McAccount *account; + + account = accounts->data; + factory = empathy_contact_factory_new (); + contact = empathy_contact_factory_get_user (factory, account); + empathy_contact_information_dialog_show (contact, + GTK_WINDOW (window->window), + TRUE, TRUE); + g_slist_foreach (accounts, (GFunc) g_object_unref, NULL); + g_slist_free (accounts); + g_object_unref (factory); + g_object_unref (contact); + } +} + +static void +main_window_edit_preferences_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_preferences_show (GTK_WINDOW (window->window)); +} + +static void +main_window_help_about_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + empathy_about_dialog_new (GTK_WINDOW (window->window)); +} + +static void +main_window_help_contents_cb (GtkWidget *widget, + EmpathyMainWindow *window) +{ + //empathy_help_show (); +} + +static gboolean +main_window_throbber_button_press_event_cb (GtkWidget *throbber_ebox, + GdkEventButton *event, + EmpathyMainWindow *window) +{ + if (event->type != GDK_BUTTON_PRESS || + event->button != 1) { + return FALSE; + } + + empathy_accounts_dialog_show (GTK_WINDOW (window->window)); + + return FALSE; +} + +static void +main_window_error_edit_clicked_cb (GtkButton *button, + EmpathyMainWindow *window) +{ + McAccount *account; + GtkWidget *error_widget; + + empathy_accounts_dialog_show (GTK_WINDOW (window->window)); + + account = g_object_get_data (G_OBJECT (button), "account"); + error_widget = g_hash_table_lookup (window->errors, account); + gtk_widget_destroy (error_widget); + g_hash_table_remove (window->errors, account); +} + +static void +main_window_error_clear_clicked_cb (GtkButton *button, + EmpathyMainWindow *window) +{ + McAccount *account; + GtkWidget *error_widget; + + account = g_object_get_data (G_OBJECT (button), "account"); + error_widget = g_hash_table_lookup (window->errors, account); + gtk_widget_destroy (error_widget); + g_hash_table_remove (window->errors, account); +} + +static void +main_window_error_display (EmpathyMainWindow *window, + McAccount *account, + const gchar *message) +{ + GtkWidget *child; + GtkWidget *table; + GtkWidget *image; + GtkWidget *button_edit; + GtkWidget *alignment; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *fixed; + GtkWidget *vbox; + GtkWidget *button_close; + gchar *str; + + child = g_hash_table_lookup (window->errors, account); + if (child) { + label = g_object_get_data (G_OBJECT (child), "label"); + + /* Just set the latest error and return */ + str = g_markup_printf_escaped ("<b>%s</b>\n%s", + mc_account_get_display_name (account), + message); + gtk_label_set_markup (GTK_LABEL (label), str); + g_free (str); + + return; + } + + child = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (window->errors_vbox), child, FALSE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (child), 6); + gtk_widget_show (child); + + table = gtk_table_new (2, 4, FALSE); + gtk_widget_show (table); + gtk_box_pack_start (GTK_BOX (child), table, TRUE, TRUE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table), 12); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + + image = gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU); + gtk_widget_show (image); + gtk_table_attach (GTK_TABLE (table), image, 0, 1, 0, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0); + + button_edit = gtk_button_new (); + gtk_widget_show (button_edit); + gtk_table_attach (GTK_TABLE (table), button_edit, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + alignment = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_widget_show (alignment); + gtk_container_add (GTK_CONTAINER (button_edit), alignment); + + hbox = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox); + gtk_container_add (GTK_CONTAINER (alignment), hbox); + + image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic (_("_Edit account")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + fixed = gtk_fixed_new (); + gtk_widget_show (fixed); + gtk_table_attach (GTK_TABLE (table), fixed, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_table_attach (GTK_TABLE (table), vbox, 3, 4, 0, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + button_close = gtk_button_new (); + gtk_widget_show (button_close); + gtk_box_pack_start (GTK_BOX (vbox), button_close, FALSE, FALSE, 0); + gtk_button_set_relief (GTK_BUTTON (button_close), GTK_RELIEF_NONE); + + + image = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU); + gtk_widget_show (image); + gtk_container_add (GTK_CONTAINER (button_close), image); + + label = gtk_label_new (""); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (table), label, 1, 3, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 0); + gtk_widget_set_size_request (label, 175, -1); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0); + + str = g_markup_printf_escaped ("<b>%s</b>\n%s", + mc_account_get_display_name (account), + message); + gtk_label_set_markup (GTK_LABEL (label), str); + g_free (str); + + g_object_set_data (G_OBJECT (child), "label", label); + g_object_set_data_full (G_OBJECT (button_edit), + "account", g_object_ref (account), + g_object_unref); + g_object_set_data_full (G_OBJECT (button_close), + "account", g_object_ref (account), + g_object_unref); + + g_signal_connect (button_edit, "clicked", + G_CALLBACK (main_window_error_edit_clicked_cb), + window); + + g_signal_connect (button_close, "clicked", + G_CALLBACK (main_window_error_clear_clicked_cb), + window); + + gtk_widget_show (window->errors_vbox); + + g_hash_table_insert (window->errors, g_object_ref (account), child); +} + +static void +main_window_status_changed_cb (MissionControl *mc, + TpConnectionStatus status, + McPresence presence, + TpConnectionStatusReason reason, + const gchar *unique_name, + EmpathyMainWindow *window) +{ + McAccount *account; + + main_window_update_status (window); + + account = mc_account_lookup (unique_name); + + if (status == TP_CONNECTION_STATUS_DISCONNECTED && + reason > TP_CONNECTION_STATUS_REASON_REQUESTED) { + const gchar *message; + + switch (reason) { + case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR: + message = _("Network error"); + break; + case TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED: + message = _("Authentication failed"); + break; + case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR: + message = _("Encryption error"); + break; + case TP_CONNECTION_STATUS_REASON_NAME_IN_USE: + message = _("Name in use"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED: + message = _("Certificate not provided"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED: + message = _("Certificate untrusted"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED: + message = _("Certificate expired"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED: + message = _("Certificate not activated"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH: + message = _("Certificate hostname mismatch"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH: + message = _("Certificate fingerprint mismatch"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED: + message = _("Certificate self signed"); + break; + case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR: + message = _("Certificate error"); + break; + default: + message = _("Unknown error"); + break; + } + + main_window_error_display (window, account, message); + } + + if (status == TP_CONNECTION_STATUS_CONNECTED) { + GtkWidget *error_widget; + + /* Account connected without error, remove error message if any */ + error_widget = g_hash_table_lookup (window->errors, account); + if (error_widget) { + gtk_widget_destroy (error_widget); + g_hash_table_remove (window->errors, account); + } + } + + g_object_unref (account); +} + +static void +main_window_update_status (EmpathyMainWindow *window) +{ + GList *accounts, *l; + guint connected = 0; + guint connecting = 0; + guint disconnected = 0; + + /* Count number of connected/connecting/disconnected accounts */ + accounts = mc_accounts_list (); + for (l = accounts; l; l = l->next) { + McAccount *account; + guint status; + + account = l->data; + + status = mission_control_get_connection_status (window->mc, + account, + NULL); + + if (status == 0) { + connected++; + } else if (status == 1) { + connecting++; + } else if (status == 2) { + disconnected++; + } + + g_object_unref (account); + } + g_list_free (accounts); + + /* Update the spinner state */ + if (connecting > 0) { + ephy_spinner_start (EPHY_SPINNER (window->throbber)); + } else { + ephy_spinner_stop (EPHY_SPINNER (window->throbber)); + } + + /* Update widgets sensibility */ + for (l = window->widgets_connected; l; l = l->next) { + gtk_widget_set_sensitive (l->data, (connected > 0)); + } + + for (l = window->widgets_disconnected; l; l = l->next) { + gtk_widget_set_sensitive (l->data, (disconnected > 0)); + } +} + +/* + * Accels + */ +static void +main_window_accels_load (void) +{ + gchar *filename; + + filename = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, ACCELS_FILENAME, NULL); + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + empathy_debug (DEBUG_DOMAIN, "Loading from:'%s'", filename); + gtk_accel_map_load (filename); + } + + g_free (filename); +} + +static void +main_window_accels_save (void) +{ + gchar *dir; + gchar *file_with_path; + + dir = g_build_filename (g_get_home_dir (), ".gnome2", PACKAGE_NAME, NULL); + g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR); + file_with_path = g_build_filename (dir, ACCELS_FILENAME, NULL); + g_free (dir); + + empathy_debug (DEBUG_DOMAIN, "Saving to:'%s'", file_with_path); + gtk_accel_map_save (file_with_path); + + g_free (file_with_path); +} + +static void +main_window_connection_items_setup (EmpathyMainWindow *window, + GladeXML *glade) +{ + GList *list; + GtkWidget *w; + gint i; + const gchar *widgets_connected[] = { + "room", + "chat_new_message", + "chat_add_contact", + "edit_personal_information" + }; + const gchar *widgets_disconnected[] = { + }; + + for (i = 0, list = NULL; i < G_N_ELEMENTS (widgets_connected); i++) { + w = glade_xml_get_widget (glade, widgets_connected[i]); + list = g_list_prepend (list, w); + } + + window->widgets_connected = list; + + for (i = 0, list = NULL; i < G_N_ELEMENTS (widgets_disconnected); i++) { + w = glade_xml_get_widget (glade, widgets_disconnected[i]); + list = g_list_prepend (list, w); + } + + window->widgets_disconnected = list; +} + +static gboolean +main_window_configure_event_timeout_cb (EmpathyMainWindow *window) +{ + gint x, y, w, h; + + gtk_window_get_size (GTK_WINDOW (window->window), &w, &h); + gtk_window_get_position (GTK_WINDOW (window->window), &x, &y); + + empathy_geometry_save (GEOMETRY_NAME, x, y, w, h); + + window->size_timeout_id = 0; + + return FALSE; +} + +static gboolean +main_window_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + EmpathyMainWindow *window) +{ + if (window->size_timeout_id) { + g_source_remove (window->size_timeout_id); + } + + window->size_timeout_id = g_timeout_add_seconds (1, + (GSourceFunc) main_window_configure_event_timeout_cb, + window); + + return FALSE; +} + +static void +main_window_notify_show_offline_cb (EmpathyConf *conf, + const gchar *key, + gpointer check_menu_item) +{ + gboolean show_offline; + + if (empathy_conf_get_bool (conf, key, &show_offline)) { + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (check_menu_item), + show_offline); + } +} + +static void +main_window_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window) +{ + gboolean show_avatars; + + if (empathy_conf_get_bool (conf, key, &show_avatars)) { + empathy_contact_list_store_set_show_avatars (window->list_store, + show_avatars); + } +} + +static void +main_window_notify_compact_contact_list_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window) +{ + gboolean compact_contact_list; + + if (empathy_conf_get_bool (conf, key, &compact_contact_list)) { + empathy_contact_list_store_set_is_compact (window->list_store, + compact_contact_list); + } +} + +static void +main_window_notify_sort_criterium_cb (EmpathyConf *conf, + const gchar *key, + EmpathyMainWindow *window) +{ + gchar *str = NULL; + + if (empathy_conf_get_string (conf, key, &str) && str) { + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + + type = empathy_contact_list_store_sort_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_peek (type)); + enum_value = g_enum_get_value_by_nick (enum_class, str); + g_free (str); + + if (enum_value) { + empathy_contact_list_store_set_sort_criterium (window->list_store, + enum_value->value); + } + } +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-main-window.glade b/gnome-2-22/libempathy-gtk/empathy-main-window.glade new file mode 100644 index 000000000..02f69a38d --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-main-window.glade @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkWindow" id="main_window"> + <property name="title" translatable="yes">Contact List</property> + <property name="default_width">225</property> + <property name="default_height">325</property> + <child> + <widget class="GtkVBox" id="main_vbox"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuBar" id="menubar2"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="chat"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Chat</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="chat_menu"> + <child> + <widget class="GtkImageMenuItem" id="chat_new_message"> + <property name="visible">True</property> + <property name="label" translatable="yes">_New Conversation...</property> + <property name="use_underline">True</property> + <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image885"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">im-message-new</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="chat_history"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View Previous Conversations</property> + <property name="use_underline">True</property> + <accelerator key="F3" modifiers="" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image886"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">document-open-recent</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator5"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="chat_add_contact"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Add Contact...</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image887"> + <property name="visible">True</property> + <property name="stock">gtk-add</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator3"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkCheckMenuItem" id="chat_show_offline"> + <property name="visible">True</property> + <property name="label" translatable="yes">Show _Offline Contacts</property> + <property name="use_underline">True</property> + <accelerator key="H" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator6"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="chat_quit"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Quit</property> + <property name="use_underline">True</property> + <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image888"> + <property name="visible">True</property> + <property name="stock">gtk-quit</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="edit"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="edit_menu"> + <child> + <widget class="GtkMenuItem" id="edit_context"> + <property name="visible">True</property> + <property name="label" translatable="yes">Context</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="edit_context_separator"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="edit_accounts"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Accounts</property> + <property name="use_underline">True</property> + <accelerator key="F4" modifiers="" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="edit_personal_information"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Personal Information</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image894"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">user-info</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator2"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="edit_preferences"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Preferences</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image891"> + <property name="visible">True</property> + <property name="stock">gtk-preferences</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="room"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Room</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="room_menu"> + <child> + <widget class="GtkMenuItem" id="room_join_new"> + <property name="visible">True</property> + <property name="label" translatable="yes">Join _New...</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="room_join_favorites"> + <property name="visible">True</property> + <property name="label" translatable="yes">Join _Favorites</property> + <property name="use_underline">True</property> + <accelerator key="F5" modifiers="" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="room_sep"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="room_sep2"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="room_manage_favorites"> + <property name="visible">True</property> + <property name="label" translatable="yes">Manage Favorites</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image890"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">system-users</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="help"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="help_menu"> + <child> + <widget class="GtkImageMenuItem" id="help_contents"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Contents</property> + <property name="use_underline">True</property> + <accelerator key="F1" modifiers="" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image892"> + <property name="visible">True</property> + <property name="stock">gtk-help</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="help_about"> + <property name="visible">True</property> + <property name="label" translatable="yes">_About</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image893"> + <property name="visible">True</property> + <property name="stock">gtk-about</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkToolbar" id="presence_toolbar"> + <property name="visible">True</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="errors_vbox"> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="roster_scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="position">3</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-main-window.h b/gnome-2-22/libempathy-gtk/empathy-main-window.h new file mode 100644 index 000000000..669a97872 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-main-window.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_MAIN_WINDOW_H__ +#define __EMPATHY_MAIN_WINDOW_H__ + +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +GtkWidget *empathy_main_window_show (void); + +G_END_DECLS + +#endif /* __EMPATHY_MAIN_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.c b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.c new file mode 100644 index 000000000..b33439407 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.c @@ -0,0 +1,566 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mission-control.h> +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-profile.h> + +#include <libempathy/empathy-tp-roomlist.h> +#include <libempathy/empathy-chatroom.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> + +#include "empathy-new-chatroom-dialog.h" +#include "empathy-account-chooser.h" +#include "empathy-ui-utils.h" +#include "ephy-spinner.h" + +#define DEBUG_DOMAIN "NewChatroomDialog" + +typedef struct { + EmpathyTpRoomlist *room_list; + + GtkWidget *window; + GtkWidget *vbox_widgets; + GtkWidget *table_info; + GtkWidget *label_account; + GtkWidget *account_chooser; + GtkWidget *label_server; + GtkWidget *entry_server; + GtkWidget *togglebutton_refresh; + GtkWidget *label_room; + GtkWidget *entry_room; + GtkWidget *vbox_browse; + GtkWidget *image_status; + GtkWidget *label_status; + GtkWidget *hbox_status; + GtkWidget *throbber; + GtkWidget *treeview; + GtkTreeModel *model; + GtkWidget *button_join; + GtkWidget *button_close; +} EmpathyNewChatroomDialog; + +enum { + COL_NAME, + COL_ROOM, + COL_COUNT +}; + +static void new_chatroom_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_destroy_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_model_setup (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_model_add_columns (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_update_widgets (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_account_changed_cb (GtkComboBox *combobox, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_roomlist_destroy_cb (EmpathyTpRoomlist *room_list, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_new_room_cb (EmpathyTpRoomlist *room_list, + EmpathyChatroom *chatroom, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_listing_cb (EmpathyTpRoomlist *room_list, + gboolean listing, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_model_clear (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_model_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_model_selection_changed (GtkTreeSelection *selection, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_join (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_entry_changed_cb (GtkWidget *entry, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_browse_start (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_browse_stop (EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_entry_server_activate_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog); +static void new_chatroom_dialog_togglebutton_refresh_toggled_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog); + +static EmpathyNewChatroomDialog *dialog_p = NULL; + +void +empathy_new_chatroom_dialog_show (GtkWindow *parent) +{ + EmpathyNewChatroomDialog *dialog; + GladeXML *glade; + GtkSizeGroup *size_group; + + if (dialog_p) { + gtk_window_present (GTK_WINDOW (dialog_p->window)); + return; + } + + dialog_p = dialog = g_new0 (EmpathyNewChatroomDialog, 1); + + glade = empathy_glade_get_file ("empathy-new-chatroom-dialog.glade", + "new_chatroom_dialog", + NULL, + "new_chatroom_dialog", &dialog->window, + "table_info", &dialog->table_info, + "label_account", &dialog->label_account, + "label_server", &dialog->label_server, + "label_room", &dialog->label_room, + "entry_server", &dialog->entry_server, + "entry_room", &dialog->entry_room, + "togglebutton_refresh", &dialog->togglebutton_refresh, + "vbox_browse", &dialog->vbox_browse, + "image_status", &dialog->image_status, + "label_status", &dialog->label_status, + "hbox_status", &dialog->hbox_status, + "treeview", &dialog->treeview, + "button_join", &dialog->button_join, + NULL); + + empathy_glade_connect (glade, + dialog, + "new_chatroom_dialog", "response", new_chatroom_dialog_response_cb, + "new_chatroom_dialog", "destroy", new_chatroom_dialog_destroy_cb, + "entry_server", "changed", new_chatroom_dialog_entry_changed_cb, + "entry_server", "activate", new_chatroom_dialog_entry_server_activate_cb, + "entry_room", "changed", new_chatroom_dialog_entry_changed_cb, + "togglebutton_refresh", "toggled", new_chatroom_dialog_togglebutton_refresh_toggled_cb, + NULL); + + g_object_unref (glade); + + g_object_add_weak_pointer (G_OBJECT (dialog->window), (gpointer) &dialog_p); + + /* Label alignment */ + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + gtk_size_group_add_widget (size_group, dialog->label_account); + gtk_size_group_add_widget (size_group, dialog->label_server); + gtk_size_group_add_widget (size_group, dialog->label_room); + + g_object_unref (size_group); + + /* Set up chatrooms treeview */ + new_chatroom_dialog_model_setup (dialog); + + /* Add throbber */ + dialog->throbber = ephy_spinner_new (); + ephy_spinner_set_size (EPHY_SPINNER (dialog->throbber), GTK_ICON_SIZE_LARGE_TOOLBAR); + gtk_widget_show (dialog->throbber); + + gtk_box_pack_start (GTK_BOX (dialog->hbox_status), dialog->throbber, + FALSE, FALSE, 0); + + /* Account chooser for custom */ + dialog->account_chooser = empathy_account_chooser_new (); + empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser), + empathy_account_chooser_filter_is_connected, + NULL); + gtk_table_attach_defaults (GTK_TABLE (dialog->table_info), + dialog->account_chooser, + 1, 3, 0, 1); + gtk_widget_show (dialog->account_chooser); + + g_signal_connect (GTK_COMBO_BOX (dialog->account_chooser), "changed", + G_CALLBACK (new_chatroom_dialog_account_changed_cb), + dialog); + new_chatroom_dialog_account_changed_cb (GTK_COMBO_BOX (dialog->account_chooser), + dialog); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog->window), + GTK_WINDOW (parent)); + } + + gtk_widget_show (dialog->window); +} + +static void +new_chatroom_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathyNewChatroomDialog *dialog) +{ + if (response == GTK_RESPONSE_OK) { + new_chatroom_dialog_join (dialog); + } + + gtk_widget_destroy (widget); +} + +static void +new_chatroom_dialog_destroy_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog) +{ + if (dialog->room_list) { + g_object_unref (dialog->room_list); + } + g_object_unref (dialog->model); + + g_free (dialog); +} + +static void +new_chatroom_dialog_model_setup (EmpathyNewChatroomDialog *dialog) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + + /* View */ + view = GTK_TREE_VIEW (dialog->treeview); + + g_signal_connect (view, "row-activated", + G_CALLBACK (new_chatroom_dialog_model_row_activated_cb), + dialog); + + /* Store/Model */ + store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* Image */ + G_TYPE_STRING, /* Text */ + G_TYPE_STRING); /* Room */ + + dialog->model = GTK_TREE_MODEL (store); + gtk_tree_view_set_model (view, dialog->model); + + /* Selection */ + selection = gtk_tree_view_get_selection (view); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_NAME, GTK_SORT_ASCENDING); + + g_signal_connect (selection, "changed", + G_CALLBACK (new_chatroom_dialog_model_selection_changed), + dialog); + + /* Columns */ + new_chatroom_dialog_model_add_columns (dialog); +} + +static void +new_chatroom_dialog_model_add_columns (EmpathyNewChatroomDialog *dialog) +{ + GtkTreeView *view; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + + view = GTK_TREE_VIEW (dialog->treeview); + gtk_tree_view_set_headers_visible (view, FALSE); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, + "xpad", (guint) 4, + "ypad", (guint) 1, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + + column = gtk_tree_view_column_new_with_attributes (_("Chat Rooms"), + cell, + "text", COL_NAME, + NULL); + + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_append_column (view, column); +} + +static void +new_chatroom_dialog_update_widgets (EmpathyNewChatroomDialog *dialog) +{ + EmpathyAccountChooser *account_chooser; + McAccount *account; + McProfile *profile; + const gchar *protocol; + const gchar *room; + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + profile = mc_account_get_profile (account); + protocol = mc_profile_get_protocol_name (profile); + + gtk_entry_set_text (GTK_ENTRY (dialog->entry_server), ""); + + /* hardcode here known protocols */ + if (strcmp (protocol, "jabber") == 0) { + gtk_widget_set_sensitive (dialog->entry_server, TRUE); + gtk_widget_show (dialog->vbox_browse); + + } + else if (strcmp (protocol, "local-xmpp") == 0) { + gtk_widget_set_sensitive (dialog->entry_server, FALSE); + gtk_widget_show (dialog->vbox_browse); + } + else if (strcmp (protocol, "irc") == 0) { + gtk_widget_set_sensitive (dialog->entry_server, FALSE); + gtk_widget_show (dialog->vbox_browse); + } else { + gtk_widget_set_sensitive (dialog->entry_server, TRUE); + gtk_widget_show (dialog->vbox_browse); + } + + room = gtk_entry_get_text (GTK_ENTRY (dialog->entry_room)); + gtk_widget_set_sensitive (dialog->button_join, !G_STR_EMPTY (room)); + + /* Final set up of the dialog */ + gtk_widget_grab_focus (dialog->entry_room); + + g_object_unref (account); + g_object_unref (profile); +} + +static void +new_chatroom_dialog_account_changed_cb (GtkComboBox *combobox, + EmpathyNewChatroomDialog *dialog) +{ + EmpathyAccountChooser *account_chooser; + McAccount *account; + gboolean listing = FALSE; + + if (dialog->room_list) { + g_object_unref (dialog->room_list); + } + + ephy_spinner_stop (EPHY_SPINNER (dialog->throbber)); + new_chatroom_dialog_model_clear (dialog); + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + dialog->room_list = empathy_tp_roomlist_new (account); + + if (dialog->room_list) { + g_signal_connect (dialog->room_list, "destroy", + G_CALLBACK (new_chatroom_dialog_roomlist_destroy_cb), + dialog); + g_signal_connect (dialog->room_list, "new-room", + G_CALLBACK (new_chatroom_dialog_new_room_cb), + dialog); + g_signal_connect (dialog->room_list, "listing", + G_CALLBACK (new_chatroom_dialog_listing_cb), + dialog); + + listing = empathy_tp_roomlist_is_listing (dialog->room_list); + if (listing) { + ephy_spinner_start (EPHY_SPINNER (dialog->throbber)); + } + } + + new_chatroom_dialog_update_widgets (dialog); +} + +static void +new_chatroom_dialog_roomlist_destroy_cb (EmpathyTpRoomlist *room_list, + EmpathyNewChatroomDialog *dialog) +{ + g_object_unref (dialog->room_list); + dialog->room_list = NULL; +} + +static void +new_chatroom_dialog_new_room_cb (EmpathyTpRoomlist *room_list, + EmpathyChatroom *chatroom, + EmpathyNewChatroomDialog *dialog) +{ + GtkTreeView *view; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + + empathy_debug (DEBUG_DOMAIN, "New chatroom listed: %s (%s)", + empathy_chatroom_get_name (chatroom), + empathy_chatroom_get_room (chatroom)); + + /* Add to model */ + view = GTK_TREE_VIEW (dialog->treeview); + selection = gtk_tree_view_get_selection (view); + store = GTK_LIST_STORE (dialog->model); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_NAME, empathy_chatroom_get_name (chatroom), + COL_ROOM, empathy_chatroom_get_room (chatroom), + -1); +} + +static void +new_chatroom_dialog_listing_cb (EmpathyTpRoomlist *room_list, + gboolean listing, + EmpathyNewChatroomDialog *dialog) +{ + /* Update the throbber */ + if (listing) { + ephy_spinner_start (EPHY_SPINNER (dialog->throbber)); + } else { + ephy_spinner_stop (EPHY_SPINNER (dialog->throbber)); + } + + /* Update the refresh toggle button */ + g_signal_handlers_block_by_func (dialog->togglebutton_refresh, + new_chatroom_dialog_togglebutton_refresh_toggled_cb, + dialog); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->togglebutton_refresh), + listing); + g_signal_handlers_unblock_by_func (dialog->togglebutton_refresh, + new_chatroom_dialog_togglebutton_refresh_toggled_cb, + dialog); +} + +static void +new_chatroom_dialog_model_clear (EmpathyNewChatroomDialog *dialog) +{ + GtkListStore *store; + + store = GTK_LIST_STORE (dialog->model); + gtk_list_store_clear (store); +} + +static void +new_chatroom_dialog_model_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathyNewChatroomDialog *dialog) +{ + gtk_widget_activate (dialog->button_join); +} + +static void +new_chatroom_dialog_model_selection_changed (GtkTreeSelection *selection, + EmpathyNewChatroomDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar *room = NULL; + gchar *server = NULL; + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return; + } + + gtk_tree_model_get (model, &iter, COL_ROOM, &room, -1); + server = strstr (room, "@"); + if (server) { + *server = '\0'; + server++; + } + + gtk_entry_set_text (GTK_ENTRY (dialog->entry_server), server ? server : ""); + gtk_entry_set_text (GTK_ENTRY (dialog->entry_room), room ? room : ""); + + g_free (room); +} + +static void +new_chatroom_dialog_join (EmpathyNewChatroomDialog *dialog) +{ + McAccount *account; + EmpathyAccountChooser *account_chooser; + MissionControl *mc; + const gchar *room; + const gchar *server = NULL; + gchar *room_name = NULL; + + room = gtk_entry_get_text (GTK_ENTRY (dialog->entry_room)); + server = gtk_entry_get_text (GTK_ENTRY (dialog->entry_server)); + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + + if (!G_STR_EMPTY (server)) { + room_name = g_strconcat (room, "@", server, NULL); + } else { + room_name = g_strdup (room); + } + + empathy_debug (DEBUG_DOMAIN, "Requesting channel for '%s'", room_name); + + mc = empathy_mission_control_new (); + mission_control_request_channel_with_string_handle (mc, + account, + TP_IFACE_CHANNEL_TYPE_TEXT, + room_name, + TP_HANDLE_TYPE_ROOM, + NULL, NULL); + g_free (room_name); + g_object_unref (mc); +} + +static void +new_chatroom_dialog_entry_changed_cb (GtkWidget *entry, + EmpathyNewChatroomDialog *dialog) +{ + if (entry == dialog->entry_room) { + const gchar *room; + + room = gtk_entry_get_text (GTK_ENTRY (dialog->entry_room)); + gtk_widget_set_sensitive (dialog->button_join, !G_STR_EMPTY (room)); + /* FIXME: Select the room in the list */ + } +} + +static void +new_chatroom_dialog_browse_start (EmpathyNewChatroomDialog *dialog) +{ + new_chatroom_dialog_model_clear (dialog); + if (dialog->room_list) { + empathy_tp_roomlist_start (dialog->room_list); + } +} + +static void +new_chatroom_dialog_browse_stop (EmpathyNewChatroomDialog *dialog) +{ + if (dialog->room_list) { + empathy_tp_roomlist_stop (dialog->room_list); + } +} + +static void +new_chatroom_dialog_entry_server_activate_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog) +{ + new_chatroom_dialog_togglebutton_refresh_toggled_cb (dialog->togglebutton_refresh, + dialog); +} + +static void +new_chatroom_dialog_togglebutton_refresh_toggled_cb (GtkWidget *widget, + EmpathyNewChatroomDialog *dialog) +{ + gboolean toggled; + + toggled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + if (toggled) { + new_chatroom_dialog_browse_start (dialog); + } else { + new_chatroom_dialog_browse_stop (dialog); + } +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.glade b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.glade new file mode 100644 index 000000000..6b99dc3e1 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.glade @@ -0,0 +1,437 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="new_chatroom_dialog"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Join New</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">350</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox4"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area4"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_join"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + + <child> + <widget class="GtkAlignment" id="alignment4"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox29"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image4"> + <property name="visible">True</property> + <property name="stock">gtk-execute</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label79"> + <property name="visible">True</property> + <property name="label" translatable="yes">Join</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_widgets"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkTable" id="table_info"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkLabel" id="label_account"> + <property name="visible">True</property> + <property name="label" translatable="yes">Account:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_server"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Server:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_server</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_room"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Room:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">entry_room</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_server"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Enter the server which hosts the room, or leave it empty if the room is on the current account's server</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">False</property> + <property name="width_chars">25</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkToggleButton" id="togglebutton_refresh"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Re_fresh</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="entry_room"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Enter the room name to join here or click on one or more rooms in the list.</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + <property name="width_chars">32</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox_browse"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox_status"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox35"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">3</property> + + <child> + <widget class="GtkImage" id="image_status"> + <property name="visible">True</property> + <property name="icon_size">2</property> + <property name="icon_name">gtk-find</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label_status"> + <property name="visible">True</property> + <property name="label" translatable="yes">Browse:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="height_request">150</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">This list represents all chat rooms hosted on the server you have entered.</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.h b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.h new file mode 100644 index 000000000..2a126085d --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-chatroom-dialog.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2006-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_NEW_CHATROOMS_WINDOW_H__ +#define __EMPATHY_NEW_CHATROOMS_WINDOW_H__ + +G_BEGIN_DECLS + +void empathy_new_chatroom_dialog_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_NEW_CHATROOMS_WINDOW_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.c b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.c new file mode 100644 index 000000000..6c588a660 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.c @@ -0,0 +1,164 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +#include <libempathy-gtk/empathy-ui-utils.h> + +#include "empathy-new-message-dialog.h" +#include "empathy-account-chooser.h" + +#define DEBUG_DOMAIN "NewMessageDialog" + +typedef struct { + GtkWidget *dialog; + GtkWidget *table_contact; + GtkWidget *account_chooser; + GtkWidget *entry_id; + GtkWidget *button_chat; + GtkWidget *button_call; +} EmpathyNewMessageDialog; + +static void +new_message_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathyNewMessageDialog *dialog) +{ + McAccount *account; + const gchar *id; + + account = empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser)); + id = gtk_entry_get_text (GTK_ENTRY (dialog->entry_id)); + if (!account || G_STR_EMPTY (id)) { + if (account) { + g_object_unref (account); + } + gtk_widget_destroy (widget); + return; + } + + if (response == 1) { + empathy_call_with_contact_id (account, id); + } + else if (response == 2) { + empathy_chat_with_contact_id (account, id); + } + + g_object_unref (account); + gtk_widget_destroy (widget); +} + +static void +new_message_change_state_button_cb (GtkEditable *editable, + EmpathyNewMessageDialog *dialog) +{ + const gchar *id; + gboolean sensitive; + + id = gtk_entry_get_text (GTK_ENTRY (editable)); + sensitive = !G_STR_EMPTY (id); + + gtk_widget_set_sensitive (dialog->button_chat, sensitive); + gtk_widget_set_sensitive (dialog->button_call, sensitive); +} + +static void +new_message_dialog_destroy_cb (GtkWidget *widget, + EmpathyNewMessageDialog *dialog) +{ + g_free (dialog); +} + +GtkWidget * +empathy_new_message_dialog_show (GtkWindow *parent) +{ + static EmpathyNewMessageDialog *dialog = NULL; + GladeXML *glade; + + if (dialog) { + gtk_window_present (GTK_WINDOW (dialog->dialog)); + return dialog->dialog; + } + + dialog = g_new0 (EmpathyNewMessageDialog, 1); + + glade = empathy_glade_get_file ("empathy-new-message-dialog.glade", + "new_message_dialog", + NULL, + "new_message_dialog", &dialog->dialog, + "table_contact", &dialog->table_contact, + "entry_id", &dialog->entry_id, + "button_chat", &dialog->button_chat, + "button_call",&dialog->button_call, + NULL); + + empathy_glade_connect (glade, + dialog, + "new_message_dialog", "destroy", new_message_dialog_destroy_cb, + "new_message_dialog", "response", new_message_dialog_response_cb, + "entry_id", "changed", new_message_change_state_button_cb, + NULL); + + g_object_add_weak_pointer (G_OBJECT (dialog->dialog), (gpointer) &dialog); + + g_object_unref (glade); + + /* Create account chooser */ + dialog->account_chooser = empathy_account_chooser_new (); + gtk_table_attach_defaults (GTK_TABLE (dialog->table_contact), + dialog->account_chooser, + 1, 2, 0, 1); + empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser), + empathy_account_chooser_filter_is_connected, + NULL); + gtk_widget_show (dialog->account_chooser); + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), + GTK_WINDOW (parent)); + } + + gtk_widget_set_sensitive (dialog->button_chat, FALSE); + gtk_widget_set_sensitive (dialog->button_call, FALSE); + +#ifndef HAVE_VOIP + gtk_widget_hide (dialog->button_call); +#endif + + gtk_widget_show (dialog->dialog); + + return dialog->dialog; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.glade b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.glade new file mode 100644 index 000000000..25f4b2254 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.glade @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkDialog" id="new_message_dialog"> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="border_width">5</property> + <property name="title" translatable="yes">New Conversation</property> + <property name="resizable">False</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkTable" id="table_contact"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <placeholder/> + </child> + <child> + <widget class="GtkEntry" id="entry_id"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Contact ID:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Account:</property> + </widget> + <packing> + <property name="x_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="button1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="button_call"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="response_id">1</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="icon_name">gnome-stock-mic</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Call</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="button_chat"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="response_id">2</property> + <child> + <widget class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image2"> + <property name="visible">True</property> + <property name="icon_name">im-message-new</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Chat</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.h b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.h new file mode 100644 index 000000000..27e461f9b --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-new-message-dialog.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_NEW_MESSAGE_DIALOG_H__ +#define __EMPATHY_NEW_MESSAGE_DIALOG_H__ + +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +GtkWidget *empathy_new_message_dialog_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_NEW_MESSAGE_DIALOG_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-preferences.c b/gnome-2-22/libempathy-gtk/empathy-preferences.c new file mode 100644 index 000000000..50c9d5660 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-preferences.c @@ -0,0 +1,990 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-ui-utils.h" +#include "empathy-theme-manager.h" +#include "empathy-spell.h" +#include "empathy-contact-list-store.h" +#include "empathy-gtk-enum-types.h" + +typedef struct { + GtkWidget *dialog; + + GtkWidget *notebook; + + GtkWidget *checkbutton_show_avatars; + GtkWidget *checkbutton_compact_contact_list; + GtkWidget *checkbutton_show_smileys; + GtkWidget *combobox_chat_theme; + GtkWidget *checkbutton_theme_chat_room; + GtkWidget *checkbutton_separate_chat_windows; + GtkWidget *checkbutton_autoconnect; + GtkWidget *radiobutton_contact_list_sort_by_name; + GtkWidget *radiobutton_contact_list_sort_by_state; + + GtkWidget *checkbutton_sounds_for_messages; + GtkWidget *checkbutton_sounds_when_busy; + GtkWidget *checkbutton_sounds_when_away; + GtkWidget *checkbutton_popups_when_available; + + GtkWidget *treeview_spell_checker; + + GList *notify_ids; +} EmpathyPreferences; + +static void preferences_setup_widgets (EmpathyPreferences *preferences); +static void preferences_languages_setup (EmpathyPreferences *preferences); +static void preferences_languages_add (EmpathyPreferences *preferences); +static void preferences_languages_save (EmpathyPreferences *preferences); +static gboolean preferences_languages_save_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gchar **languages); +static void preferences_languages_load (EmpathyPreferences *preferences); +static gboolean preferences_languages_load_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gchar **languages); +static void preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyPreferences *preferences); +static void preferences_themes_setup (EmpathyPreferences *preferences); +static void preferences_widget_sync_bool (const gchar *key, + GtkWidget *widget); +static void preferences_widget_sync_int (const gchar *key, + GtkWidget *widget); +static void preferences_widget_sync_string (const gchar *key, + GtkWidget *widget); +static void preferences_widget_sync_string_combo (const gchar *key, + GtkWidget *widget); +static void preferences_notify_int_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void preferences_notify_string_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void preferences_notify_string_combo_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void preferences_notify_bool_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void preferences_notify_sensitivity_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void preferences_hookup_spin_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_hookup_entry (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_hookup_toggle_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_hookup_radio_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_hookup_string_combo (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_hookup_sensitivity (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget); +static void preferences_spin_button_value_changed_cb (GtkWidget *button, + gpointer user_data); +static void preferences_entry_value_changed_cb (GtkWidget *entry, + gpointer user_data); +static void preferences_toggle_button_toggled_cb (GtkWidget *button, + gpointer user_data); +static void preferences_radio_button_toggled_cb (GtkWidget *button, + gpointer user_data); +static void preferences_string_combo_changed_cb (GtkWidget *button, + gpointer user_data); +static void preferences_destroy_cb (GtkWidget *widget, + EmpathyPreferences *preferences); +static void preferences_response_cb (GtkWidget *widget, + gint response, + EmpathyPreferences *preferences); + +enum { + COL_LANG_ENABLED, + COL_LANG_CODE, + COL_LANG_NAME, + COL_LANG_COUNT +}; + +enum { + COL_COMBO_VISIBLE_NAME, + COL_COMBO_NAME, + COL_COMBO_COUNT +}; + +static void +preferences_setup_widgets (EmpathyPreferences *preferences) +{ + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_SOUNDS_FOR_MESSAGES, + preferences->checkbutton_sounds_for_messages); + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_SOUNDS_WHEN_AWAY, + preferences->checkbutton_sounds_when_away); + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_SOUNDS_WHEN_BUSY, + preferences->checkbutton_sounds_when_busy); + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_POPUPS_WHEN_AVAILABLE, + preferences->checkbutton_popups_when_available); + + preferences_hookup_sensitivity (preferences, + EMPATHY_PREFS_SOUNDS_FOR_MESSAGES, + preferences->checkbutton_sounds_when_away); + preferences_hookup_sensitivity (preferences, + EMPATHY_PREFS_SOUNDS_FOR_MESSAGES, + preferences->checkbutton_sounds_when_busy); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS, + preferences->checkbutton_separate_chat_windows); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_UI_SHOW_AVATARS, + preferences->checkbutton_show_avatars); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST, + preferences->checkbutton_compact_contact_list); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_CHAT_SHOW_SMILEYS, + preferences->checkbutton_show_smileys); + + preferences_hookup_string_combo (preferences, + EMPATHY_PREFS_CHAT_THEME, + preferences->combobox_chat_theme); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_CHAT_THEME_CHAT_ROOM, + preferences->checkbutton_theme_chat_room); + + preferences_hookup_radio_button (preferences, + EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM, + preferences->radiobutton_contact_list_sort_by_name); + + preferences_hookup_toggle_button (preferences, + EMPATHY_PREFS_AUTOCONNECT, + preferences->checkbutton_autoconnect); +} + +static void +preferences_languages_setup (EmpathyPreferences *preferences) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + guint col_offset; + + view = GTK_TREE_VIEW (preferences->treeview_spell_checker); + + store = gtk_list_store_new (COL_LANG_COUNT, + G_TYPE_BOOLEAN, /* enabled */ + G_TYPE_STRING, /* code */ + G_TYPE_STRING); /* name */ + + gtk_tree_view_set_model (view, GTK_TREE_MODEL (store)); + + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + model = GTK_TREE_MODEL (store); + + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (renderer, "toggled", + G_CALLBACK (preferences_languages_cell_toggled_cb), + preferences); + + column = gtk_tree_view_column_new_with_attributes (NULL, renderer, + "active", COL_LANG_ENABLED, + NULL); + + gtk_tree_view_append_column (view, column); + + renderer = gtk_cell_renderer_text_new (); + col_offset = gtk_tree_view_insert_column_with_attributes (view, + -1, _("Language"), + renderer, + "text", COL_LANG_NAME, + NULL); + + g_object_set_data (G_OBJECT (renderer), + "column", GINT_TO_POINTER (COL_LANG_NAME)); + + column = gtk_tree_view_get_column (view, col_offset - 1); + gtk_tree_view_column_set_sort_column_id (column, COL_LANG_NAME); + gtk_tree_view_column_set_resizable (column, FALSE); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); + + g_object_unref (store); +} + +static void +preferences_languages_add (EmpathyPreferences *preferences) +{ + GtkTreeView *view; + GtkListStore *store; + GList *codes, *l; + + view = GTK_TREE_VIEW (preferences->treeview_spell_checker); + store = GTK_LIST_STORE (gtk_tree_view_get_model (view)); + + codes = empathy_spell_get_language_codes (); + + empathy_conf_set_bool (empathy_conf_get(), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED, + codes != NULL); + if (!codes) { + gtk_widget_set_sensitive (preferences->treeview_spell_checker, FALSE); + } + + for (l = codes; l; l = l->next) { + GtkTreeIter iter; + const gchar *code; + const gchar *name; + + code = l->data; + name = empathy_spell_get_language_name (code); + if (!name) { + continue; + } + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_LANG_CODE, code, + COL_LANG_NAME, name, + -1); + } + + empathy_spell_free_language_codes (codes); +} + +static void +preferences_languages_save (EmpathyPreferences *preferences) +{ + GtkTreeView *view; + GtkTreeModel *model; + + gchar *languages = NULL; + + view = GTK_TREE_VIEW (preferences->treeview_spell_checker); + model = gtk_tree_view_get_model (view); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) preferences_languages_save_foreach, + &languages); + + /* if user selects no languages, we don't want spell check */ + empathy_conf_set_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED, + languages != NULL); + + empathy_conf_set_string (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES, + languages ? languages : ""); + + g_free (languages); +} + +static gboolean +preferences_languages_save_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gchar **languages) +{ + gboolean enabled; + gchar *code; + + if (!languages) { + return TRUE; + } + + gtk_tree_model_get (model, iter, COL_LANG_ENABLED, &enabled, -1); + if (!enabled) { + return FALSE; + } + + gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1); + if (!code) { + return FALSE; + } + + if (!(*languages)) { + *languages = g_strdup (code); + } else { + gchar *str = *languages; + *languages = g_strdup_printf ("%s,%s", str, code); + g_free (str); + } + + g_free (code); + + return FALSE; +} + +static void +preferences_languages_load (EmpathyPreferences *preferences) +{ + GtkTreeView *view; + GtkTreeModel *model; + gchar *value; + gchar **vlanguages; + + if (!empathy_conf_get_string (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES, + &value) || !value) { + return; + } + + vlanguages = g_strsplit (value, ",", -1); + g_free (value); + + view = GTK_TREE_VIEW (preferences->treeview_spell_checker); + model = gtk_tree_view_get_model (view); + + gtk_tree_model_foreach (model, + (GtkTreeModelForeachFunc) preferences_languages_load_foreach, + vlanguages); + + g_strfreev (vlanguages); +} + +static gboolean +preferences_languages_load_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gchar **languages) +{ + gchar *code; + gchar *lang; + gint i; + gboolean found = FALSE; + + if (!languages) { + return TRUE; + } + + gtk_tree_model_get (model, iter, COL_LANG_CODE, &code, -1); + if (!code) { + return FALSE; + } + + for (i = 0, lang = languages[i]; lang; lang = languages[++i]) { + if (strcmp (lang, code) == 0) { + found = TRUE; + } + } + + gtk_list_store_set (GTK_LIST_STORE (model), iter, COL_LANG_ENABLED, found, -1); + return FALSE; +} + +static void +preferences_languages_cell_toggled_cb (GtkCellRendererToggle *cell, + gchar *path_string, + EmpathyPreferences *preferences) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkListStore *store; + GtkTreePath *path; + GtkTreeIter iter; + gboolean enabled; + + view = GTK_TREE_VIEW (preferences->treeview_spell_checker); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + path = gtk_tree_path_new_from_string (path_string); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, COL_LANG_ENABLED, &enabled, -1); + + enabled ^= 1; + + gtk_list_store_set (store, &iter, COL_LANG_ENABLED, enabled, -1); + gtk_tree_path_free (path); + + preferences_languages_save (preferences); +} + +static void +preferences_themes_setup (EmpathyPreferences *preferences) +{ + GtkComboBox *combo; + GtkListStore *model; + GtkTreeIter iter; + const gchar **themes; + gint i; + + combo = GTK_COMBO_BOX (preferences->combobox_chat_theme); + + model = gtk_list_store_new (COL_COMBO_COUNT, + G_TYPE_STRING, + G_TYPE_STRING); + + themes = empathy_theme_manager_get_themes (); + for (i = 0; themes[i]; i += 2) { + gtk_list_store_append (model, &iter); + gtk_list_store_set (model, &iter, + COL_COMBO_VISIBLE_NAME, _(themes[i + 1]), + COL_COMBO_NAME, themes[i], + -1); + } + + gtk_combo_box_set_model (combo, GTK_TREE_MODEL (model)); + g_object_unref (model); +} + +static void +preferences_widget_sync_bool (const gchar *key, GtkWidget *widget) +{ + gboolean value; + + if (empathy_conf_get_bool (empathy_conf_get (), key, &value)) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value); + } +} + +static void +preferences_widget_sync_int (const gchar *key, GtkWidget *widget) +{ + gint value; + + if (empathy_conf_get_int (empathy_conf_get (), key, &value)) { + if (GTK_IS_SPIN_BUTTON (widget)) { + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } + } +} + +static void +preferences_widget_sync_string (const gchar *key, GtkWidget *widget) +{ + gchar *value; + + if (empathy_conf_get_string (empathy_conf_get (), key, &value) && value) { + if (GTK_IS_ENTRY (widget)) { + gtk_entry_set_text (GTK_ENTRY (widget), value); + } else if (GTK_IS_RADIO_BUTTON (widget)) { + if (strcmp (key, EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM) == 0) { + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + GSList *list; + GtkWidget *toggle_widget; + + /* Get index from new string */ + type = empathy_contact_list_store_sort_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_peek (type)); + enum_value = g_enum_get_value_by_nick (enum_class, value); + + if (enum_value) { + list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget)); + toggle_widget = g_slist_nth_data (list, enum_value->value); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_widget), TRUE); + } + } else { + g_warning ("Unhandled key:'%s' just had string change", key); + } + } + + g_free (value); + } +} + +static void +preferences_widget_sync_string_combo (const gchar *key, GtkWidget *widget) +{ + gchar *value; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean found; + + if (!empathy_conf_get_string (empathy_conf_get (), key, &value)) { + return; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + + found = FALSE; + if (value && gtk_tree_model_get_iter_first (model, &iter)) { + gchar *name; + + do { + gtk_tree_model_get (model, &iter, + COL_COMBO_NAME, &name, + -1); + + if (strcmp (name, value) == 0) { + found = TRUE; + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter); + break; + } else { + found = FALSE; + } + + g_free (name); + } while (gtk_tree_model_iter_next (model, &iter)); + } + + /* Fallback to the first one. */ + if (!found) { + if (gtk_tree_model_get_iter_first (model, &iter)) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter); + } + } + + g_free (value); +} + +static void +preferences_notify_int_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + preferences_widget_sync_int (key, user_data); +} + +static void +preferences_notify_string_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + preferences_widget_sync_string (key, user_data); +} + +static void +preferences_notify_string_combo_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + preferences_widget_sync_string_combo (key, user_data); +} + +static void +preferences_notify_bool_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + preferences_widget_sync_bool (key, user_data); +} + +static void +preferences_notify_sensitivity_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + gboolean value; + + if (empathy_conf_get_bool (conf, key, &value)) { + gtk_widget_set_sensitive (GTK_WIDGET (user_data), value); + } +} + +static void +preferences_add_id (EmpathyPreferences *preferences, guint id) +{ + preferences->notify_ids = g_list_prepend (preferences->notify_ids, + GUINT_TO_POINTER (id)); +} + +static void +preferences_hookup_spin_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + guint id; + + /* Silence warning. */ + if (0) { + preferences_hookup_spin_button (preferences, key, widget); + } + + preferences_widget_sync_int (key, widget); + + g_object_set_data_full (G_OBJECT (widget), "key", + g_strdup (key), g_free); + + g_signal_connect (widget, + "value_changed", + G_CALLBACK (preferences_spin_button_value_changed_cb), + NULL); + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_int_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_hookup_entry (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + guint id; + + if (0) { /* Silent warning before we use this function. */ + preferences_hookup_entry (preferences, key, widget); + } + + preferences_widget_sync_string (key, widget); + + g_object_set_data_full (G_OBJECT (widget), "key", + g_strdup (key), g_free); + + g_signal_connect (widget, + "changed", + G_CALLBACK (preferences_entry_value_changed_cb), + NULL); + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_string_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_hookup_toggle_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + guint id; + + preferences_widget_sync_bool (key, widget); + + g_object_set_data_full (G_OBJECT (widget), "key", + g_strdup (key), g_free); + + g_signal_connect (widget, + "toggled", + G_CALLBACK (preferences_toggle_button_toggled_cb), + NULL); + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_bool_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_hookup_radio_button (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + GSList *group, *l; + guint id; + + preferences_widget_sync_string (key, widget); + + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget)); + for (l = group; l; l = l->next) { + g_signal_connect (l->data, + "toggled", + G_CALLBACK (preferences_radio_button_toggled_cb), + NULL); + + g_object_set_data_full (G_OBJECT (l->data), "key", + g_strdup (key), g_free); + } + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_string_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_hookup_string_combo (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + guint id; + + preferences_widget_sync_string_combo (key, widget); + + g_object_set_data_full (G_OBJECT (widget), "key", + g_strdup (key), g_free); + + g_signal_connect (widget, + "changed", + G_CALLBACK (preferences_string_combo_changed_cb), + NULL); + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_string_combo_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_hookup_sensitivity (EmpathyPreferences *preferences, + const gchar *key, + GtkWidget *widget) +{ + gboolean value; + guint id; + + if (empathy_conf_get_bool (empathy_conf_get (), key, &value)) { + gtk_widget_set_sensitive (widget, value); + } + + id = empathy_conf_notify_add (empathy_conf_get (), + key, + preferences_notify_sensitivity_cb, + widget); + if (id) { + preferences_add_id (preferences, id); + } +} + +static void +preferences_spin_button_value_changed_cb (GtkWidget *button, + gpointer user_data) +{ + const gchar *key; + + key = g_object_get_data (G_OBJECT (button), "key"); + + empathy_conf_set_int (empathy_conf_get (), + key, + gtk_spin_button_get_value (GTK_SPIN_BUTTON (button))); +} + +static void +preferences_entry_value_changed_cb (GtkWidget *entry, + gpointer user_data) +{ + const gchar *key; + + key = g_object_get_data (G_OBJECT (entry), "key"); + + empathy_conf_set_string (empathy_conf_get (), + key, + gtk_entry_get_text (GTK_ENTRY (entry))); +} + +static void +preferences_toggle_button_toggled_cb (GtkWidget *button, + gpointer user_data) +{ + const gchar *key; + + key = g_object_get_data (G_OBJECT (button), "key"); + + empathy_conf_set_bool (empathy_conf_get (), + key, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))); +} + +static void +preferences_radio_button_toggled_cb (GtkWidget *button, + gpointer user_data) +{ + const gchar *key; + const gchar *value = NULL; + + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) { + return; + } + + key = g_object_get_data (G_OBJECT (button), "key"); + + if (key && strcmp (key, EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM) == 0) { + GSList *group; + GType type; + GEnumClass *enum_class; + GEnumValue *enum_value; + + group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)); + + /* Get string from index */ + type = empathy_contact_list_store_sort_get_type (); + enum_class = G_ENUM_CLASS (g_type_class_peek (type)); + enum_value = g_enum_get_value (enum_class, g_slist_index (group, button)); + + if (!enum_value) { + g_warning ("No GEnumValue for EmpathyContactListSort with GtkRadioButton index:%d", + g_slist_index (group, button)); + return; + } + + value = enum_value->value_nick; + } + + empathy_conf_set_string (empathy_conf_get (), key, value); +} + +static void +preferences_string_combo_changed_cb (GtkWidget *combo, + gpointer user_data) +{ + const gchar *key; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + key = g_object_get_data (G_OBJECT (combo), "key"); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) { + model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); + + gtk_tree_model_get (model, &iter, + COL_COMBO_NAME, &name, + -1); + empathy_conf_set_string (empathy_conf_get (), key, name); + g_free (name); + } +} + +static void +preferences_response_cb (GtkWidget *widget, + gint response, + EmpathyPreferences *preferences) +{ + gtk_widget_destroy (widget); +} + +static void +preferences_destroy_cb (GtkWidget *widget, + EmpathyPreferences *preferences) +{ + GList *l; + + for (l = preferences->notify_ids; l; l = l->next) { + guint id; + + id = GPOINTER_TO_UINT (l->data); + empathy_conf_notify_remove (empathy_conf_get (), id); + } + + g_list_free (preferences->notify_ids); + g_free (preferences); +} + +GtkWidget * +empathy_preferences_show (GtkWindow *parent) +{ + static EmpathyPreferences *preferences; + GladeXML *glade; + + if (preferences) { + gtk_window_present (GTK_WINDOW (preferences->dialog)); + return preferences->dialog; + } + + preferences = g_new0 (EmpathyPreferences, 1); + + glade = empathy_glade_get_file ( + "empathy-preferences.glade", + "preferences_dialog", + NULL, + "preferences_dialog", &preferences->dialog, + "notebook", &preferences->notebook, + "checkbutton_show_avatars", &preferences->checkbutton_show_avatars, + "checkbutton_compact_contact_list", &preferences->checkbutton_compact_contact_list, + "checkbutton_show_smileys", &preferences->checkbutton_show_smileys, + "combobox_chat_theme", &preferences->combobox_chat_theme, + "checkbutton_theme_chat_room", &preferences->checkbutton_theme_chat_room, + "checkbutton_separate_chat_windows", &preferences->checkbutton_separate_chat_windows, + "checkbutton_autoconnect", &preferences->checkbutton_autoconnect, + "radiobutton_contact_list_sort_by_name", &preferences->radiobutton_contact_list_sort_by_name, + "radiobutton_contact_list_sort_by_state", &preferences->radiobutton_contact_list_sort_by_state, + "checkbutton_sounds_for_messages", &preferences->checkbutton_sounds_for_messages, + "checkbutton_sounds_when_busy", &preferences->checkbutton_sounds_when_busy, + "checkbutton_sounds_when_away", &preferences->checkbutton_sounds_when_away, + "checkbutton_popups_when_available", &preferences->checkbutton_popups_when_available, + "treeview_spell_checker", &preferences->treeview_spell_checker, + NULL); + + empathy_glade_connect (glade, + preferences, + "preferences_dialog", "destroy", preferences_destroy_cb, + "preferences_dialog", "response", preferences_response_cb, + NULL); + + g_object_unref (glade); + + g_object_add_weak_pointer (G_OBJECT (preferences->dialog), (gpointer) &preferences); + + preferences_themes_setup (preferences); + + preferences_setup_widgets (preferences); + + preferences_languages_setup (preferences); + preferences_languages_add (preferences); + preferences_languages_load (preferences); + + if (empathy_spell_supported ()) { + GtkWidget *page; + + page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (preferences->notebook), 2); + gtk_widget_show (page); + } + + if (parent) { + gtk_window_set_transient_for (GTK_WINDOW (preferences->dialog), + GTK_WINDOW (parent)); + } + + gtk_widget_show (preferences->dialog); + + return preferences->dialog; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-preferences.glade b/gnome-2-22/libempathy-gtk/empathy-preferences.glade new file mode 100644 index 000000000..725844491 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-preferences.glade @@ -0,0 +1,1063 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="preferences_dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Preferences</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="icon_name">gtk-preferences</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button_close"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkNotebook" id="notebook"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkVBox" id="vbox197"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkFrame" id="frame3"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment11"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox199"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_show_avatars"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Avatars are user chosen images shown in the contact list</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show _avatars</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_compact_contact_list"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show co_mpact contact list</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_show_smileys"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show _smileys as images</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label611"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Appearance</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame4"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment12"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox218"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_separate_chat_windows"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Open new chats in separate windows</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_autoconnect"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Automatically _connect on startup </property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label612"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Behaviour</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame13"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment31"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox217"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkRadioButton" id="radiobutton_contact_list_sort_by_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Sort by _name</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkRadioButton" id="radiobutton_contact_list_sort_by_state"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Sort by s_tate</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <property name="group">radiobutton_contact_list_sort_by_name</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label644"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Contact List</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label602"> + <property name="visible">True</property> + <property name="label" translatable="yes">General</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="outer_vbox"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkFrame" id="frame5"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment13"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox106"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_sounds_for_messages"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Play sound when messages arrive</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_sounds_when_busy"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Enable sounds when _busy</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_sounds_when_away"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Enable sounds when _away</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label613"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Audio</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame6"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment14"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_popups_when_available"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Display notifications when contacts come _online</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label614"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Visual</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label607"> + <property name="visible">True</property> + <property name="label" translatable="yes">Notifications</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox168"> + <property name="border_width">12</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkFrame" id="frame7"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment15"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox201"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox153"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkHBox" id="hbox154"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow18"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview_spell_checker"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox155"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkImage" id="image422"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-info</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label616"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small>The list of languages reflects only the languages for which you have a dictionary installed.</small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label615"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Enable spell checking for languages:</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label567"> + <property name="visible">True</property> + <property name="label" translatable="yes">Spell Checking</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox206"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkFrame" id="frame11"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment19"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox207"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox139"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label586"> + <property name="visible">True</property> + <property name="label" translatable="yes">Chat Th_eme:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">combobox_chat_theme</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="combobox_chat_theme"> + <property name="visible">True</property> + <property name="items" translatable="yes"></property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label626"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Appearance</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame12"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment20"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">6</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkCheckButton" id="checkbutton_theme_chat_room"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Use for chat rooms</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label627"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Options</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label624"> + <property name="visible">True</property> + <property name="label" translatable="yes">Themes</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-preferences.h b/gnome-2-22/libempathy-gtk/empathy-preferences.h new file mode 100644 index 000000000..750666b09 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-preferences.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2003-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + */ + +#ifndef __EMPATHY_PREFERENCES_H__ +#define __EMPATHY_PREFERENCES_H__ + +#include <gtk/gtkwindow.h> + +G_BEGIN_DECLS + +#define EMPATHY_PREFS_PATH "/apps/empathy" + +#define EMPATHY_PREFS_SOUNDS_FOR_MESSAGES EMPATHY_PREFS_PATH "/notifications/sounds_for_messages" +#define EMPATHY_PREFS_SOUNDS_WHEN_AWAY EMPATHY_PREFS_PATH "/notifications/sounds_when_away" +#define EMPATHY_PREFS_SOUNDS_WHEN_BUSY EMPATHY_PREFS_PATH "/notifications/sounds_when_busy" +#define EMPATHY_PREFS_POPUPS_WHEN_AVAILABLE EMPATHY_PREFS_PATH "/notifications/popups_when_available" +#define EMPATHY_PREFS_CHAT_SHOW_SMILEYS EMPATHY_PREFS_PATH "/conversation/graphical_smileys" +#define EMPATHY_PREFS_CHAT_THEME EMPATHY_PREFS_PATH "/conversation/theme" +#define EMPATHY_PREFS_CHAT_THEME_CHAT_ROOM EMPATHY_PREFS_PATH "/conversation/theme_chat_room" +#define EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES EMPATHY_PREFS_PATH "/conversation/spell_checker_languages" +#define EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED EMPATHY_PREFS_PATH "/conversation/spell_checker_enabled" +#define EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR EMPATHY_PREFS_PATH "/conversation/nick_completion_char" +#define EMPATHY_PREFS_UI_SEPARATE_CHAT_WINDOWS EMPATHY_PREFS_PATH "/ui/separate_chat_windows" +#define EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN EMPATHY_PREFS_PATH "/ui/main_window_hidden" +#define EMPATHY_PREFS_UI_AVATAR_DIRECTORY EMPATHY_PREFS_PATH "/ui/avatar_directory" +#define EMPATHY_PREFS_UI_SHOW_AVATARS EMPATHY_PREFS_PATH "/ui/show_avatars" +#define EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST EMPATHY_PREFS_PATH "/ui/compact_contact_list" +#define EMPATHY_PREFS_CONTACTS_SHOW_OFFLINE EMPATHY_PREFS_PATH "/contacts/show_offline" +#define EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM EMPATHY_PREFS_PATH "/contacts/sort_criterium" +#define EMPATHY_PREFS_HINTS_CLOSE_MAIN_WINDOW EMPATHY_PREFS_PATH "/hints/close_main_window" +#define EMPATHY_PREFS_SALUT_ACCOUNT_CREATED EMPATHY_PREFS_PATH "/accounts/salut_created" +#define EMPATHY_PREFS_USE_NM EMPATHY_PREFS_PATH "/use_nm" +#define EMPATHY_PREFS_AUTOCONNECT EMPATHY_PREFS_PATH "/autoconnect" + +GtkWidget * empathy_preferences_show (GtkWindow *parent); + +G_END_DECLS + +#endif /* __EMPATHY_PREFERENCES_H__ */ + + diff --git a/gnome-2-22/libempathy-gtk/empathy-presence-chooser.c b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.c new file mode 100644 index 000000000..dfed7bfc7 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.c @@ -0,0 +1,974 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <telepathy-glib/util.h> +#include <libmissioncontrol/mc-enum-types.h> + +#include <libempathy/empathy-idle.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-status-presets.h> + +#include "empathy-ui-utils.h" +#include "empathy-images.h" +#include "empathy-presence-chooser.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_PRESENCE_CHOOSER, EmpathyPresenceChooserPriv)) + +#define DEBUG_DOMAIN "PresenceChooser" + +/* Flashing delay for icons (milliseconds). */ +#define FLASH_TIMEOUT 500 + +typedef struct { + EmpathyIdle *idle; + + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *menu; + + McPresence last_state; + + McPresence flash_state_1; + McPresence flash_state_2; + guint flash_timeout_id; + + /* The handle the kind of unnessecary scroll support. */ + guint scroll_timeout_id; + McPresence scroll_state; + gchar *scroll_status; +} EmpathyPresenceChooserPriv; + +typedef struct { + GtkWidget *dialog; + GtkWidget *checkbutton_save; + GtkWidget *comboboxentry_message; + GtkWidget *entry_message; + GtkWidget *combobox_status; + GtkTreeModel *model_status; +} CustomMessageDialog; + +enum { + COL_ICON, + COL_LABEL, + COL_PRESENCE, + COL_COUNT +}; + +typedef struct { + McPresence state; + const gchar *status; +} StateAndStatus; + +static CustomMessageDialog *message_dialog = NULL; +/* States to be listed in the menu. + * Each state has a boolean telling if it can have custom message */ +static guint states[] = {MC_PRESENCE_AVAILABLE, TRUE, + MC_PRESENCE_DO_NOT_DISTURB, TRUE, + MC_PRESENCE_AWAY, TRUE, + MC_PRESENCE_HIDDEN, FALSE, + MC_PRESENCE_OFFLINE, FALSE}; + +static void empathy_presence_chooser_class_init (EmpathyPresenceChooserClass *klass); +static void empathy_presence_chooser_init (EmpathyPresenceChooser *chooser); +static void presence_chooser_finalize (GObject *object); +static void presence_chooser_presence_changed_cb (EmpathyPresenceChooser *chooser); +static void presence_chooser_reset_scroll_timeout (EmpathyPresenceChooser *chooser); +static gboolean presence_chooser_scroll_timeout_cb (EmpathyPresenceChooser *chooser); +static gboolean presence_chooser_scroll_event_cb (EmpathyPresenceChooser *chooser, + GdkEventScroll *event, + gpointer user_data); +static GList * presence_chooser_get_presets (EmpathyPresenceChooser *chooser); +static StateAndStatus *presence_chooser_state_and_status_new (McPresence state, + const gchar *status); +static gboolean presence_chooser_flash_timeout_cb (EmpathyPresenceChooser *chooser); +static void presence_chooser_flash_start (EmpathyPresenceChooser *chooser, + McPresence state_1, + McPresence state_2); +static void presence_chooser_flash_stop (EmpathyPresenceChooser *chooser, + McPresence state); +static gboolean presence_chooser_button_press_event_cb (GtkWidget *chooser, + GdkEventButton *event, + gpointer user_data); +static void presence_chooser_toggled_cb (GtkWidget *chooser, + gpointer user_data); +static void presence_chooser_menu_popup (EmpathyPresenceChooser *chooser); +static void presence_chooser_menu_popdown (EmpathyPresenceChooser *chooser); +static void presence_chooser_menu_selection_done_cb (GtkMenuShell *menushell, + EmpathyPresenceChooser *chooser); +static void presence_chooser_menu_destroy_cb (GtkWidget *menu, + EmpathyPresenceChooser *chooser); +static void presence_chooser_menu_detach (GtkWidget *attach_widget, + GtkMenu *menu); +static void presence_chooser_menu_align_func (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + GtkWidget *widget); +static void presence_chooser_menu_add_item (GtkWidget *menu, + const gchar *str, + McPresence state); +static void presence_chooser_noncustom_activate_cb (GtkWidget *item, + gpointer user_data); +static void presence_chooser_set_state (McPresence state, + const gchar *status); +static void presence_chooser_custom_activate_cb (GtkWidget *item, + gpointer user_data); +static void presence_chooser_dialog_show (void); + +G_DEFINE_TYPE (EmpathyPresenceChooser, empathy_presence_chooser, GTK_TYPE_TOGGLE_BUTTON); + +static void +empathy_presence_chooser_class_init (EmpathyPresenceChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = presence_chooser_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyPresenceChooserPriv)); +} + +static void +empathy_presence_chooser_init (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + GtkWidget *arrow; + GtkWidget *alignment; + + priv = GET_PRIV (chooser); + + gtk_button_set_relief (GTK_BUTTON (chooser), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click (GTK_BUTTON (chooser), FALSE); + + alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment); + gtk_container_add (GTK_CONTAINER (chooser), alignment); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 1, 0); + + priv->hbox = gtk_hbox_new (FALSE, 1); + gtk_widget_show (priv->hbox); + gtk_container_add (GTK_CONTAINER (alignment), priv->hbox); + + priv->image = gtk_image_new (); + gtk_widget_show (priv->image); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->image, FALSE, TRUE, 0); + + priv->label = gtk_label_new (NULL); + gtk_widget_show (priv->label); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->label, TRUE, TRUE, 0); + gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (priv->label), 0, 0.5); + gtk_misc_set_padding (GTK_MISC (priv->label), 4, 1); + + alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment); + gtk_box_pack_start (GTK_BOX (priv->hbox), alignment, FALSE, FALSE, 0); + + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_widget_show (arrow); + gtk_container_add (GTK_CONTAINER (alignment), arrow); + + g_signal_connect (chooser, "toggled", + G_CALLBACK (presence_chooser_toggled_cb), + NULL); + g_signal_connect (chooser, "button-press-event", + G_CALLBACK (presence_chooser_button_press_event_cb), + NULL); + g_signal_connect (chooser, "scroll-event", + G_CALLBACK (presence_chooser_scroll_event_cb), + NULL); + + priv->idle = empathy_idle_new (); + presence_chooser_presence_changed_cb (chooser); + g_signal_connect_swapped (priv->idle, "notify", + G_CALLBACK (presence_chooser_presence_changed_cb), + chooser); +} + +static void +presence_chooser_finalize (GObject *object) +{ + EmpathyPresenceChooserPriv *priv; + + priv = GET_PRIV (object); + + if (priv->flash_timeout_id) { + g_source_remove (priv->flash_timeout_id); + } + + if (priv->scroll_timeout_id) { + g_source_remove (priv->scroll_timeout_id); + } + + g_signal_handlers_disconnect_by_func (priv->idle, + presence_chooser_presence_changed_cb, + object); + g_object_unref (priv->idle); + + G_OBJECT_CLASS (empathy_presence_chooser_parent_class)->finalize (object); +} + +GtkWidget * +empathy_presence_chooser_new (void) +{ + GtkWidget *chooser; + + chooser = g_object_new (EMPATHY_TYPE_PRESENCE_CHOOSER, NULL); + + return chooser; +} + +static void +presence_chooser_presence_changed_cb (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + McPresence state; + McPresence flash_state; + const gchar *status; + + priv = GET_PRIV (chooser); + + state = empathy_idle_get_state (priv->idle); + status = empathy_idle_get_status (priv->idle); + flash_state = empathy_idle_get_flash_state (priv->idle); + + presence_chooser_reset_scroll_timeout (chooser); + gtk_label_set_text (GTK_LABEL (priv->label), status); + + if (flash_state != MC_PRESENCE_UNSET) { + presence_chooser_flash_start (chooser, state, flash_state); + } else { + presence_chooser_flash_stop (chooser, state); + } +} + +static void +presence_chooser_reset_scroll_timeout (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + + priv = GET_PRIV (chooser); + + if (priv->scroll_timeout_id) { + g_source_remove (priv->scroll_timeout_id); + priv->scroll_timeout_id = 0; + } + + g_free (priv->scroll_status); + priv->scroll_status = NULL; +} + +static gboolean +presence_chooser_scroll_timeout_cb (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + + priv = GET_PRIV (chooser); + + priv->scroll_timeout_id = 0; + + empathy_idle_set_presence (priv->idle, + priv->scroll_state, + priv->scroll_status); + + g_free (priv->scroll_status); + priv->scroll_status = NULL; + + return FALSE; +} + +static gboolean +presence_chooser_scroll_event_cb (EmpathyPresenceChooser *chooser, + GdkEventScroll *event, + gpointer user_data) +{ + EmpathyPresenceChooserPriv *priv; + GList *list, *l; + const gchar *current_status; + StateAndStatus *sas; + gboolean match; + + priv = GET_PRIV (chooser); + + switch (event->direction) { + case GDK_SCROLL_UP: + break; + case GDK_SCROLL_DOWN: + break; + default: + return FALSE; + } + + current_status = gtk_label_get_text (GTK_LABEL (priv->label)); + + /* Get the list of presets, which in this context means all the items + * without a trailing "...". + */ + list = presence_chooser_get_presets (chooser); + sas = NULL; + match = FALSE; + for (l = list; l; l = l->next) { + sas = l->data; + + if (sas->state == priv->last_state && + strcmp (sas->status, current_status) == 0) { + sas = NULL; + match = TRUE; + if (event->direction == GDK_SCROLL_UP) { + if (l->prev) { + sas = l->prev->data; + } + } + else if (event->direction == GDK_SCROLL_DOWN) { + if (l->next) { + sas = l->next->data; + } + } + break; + } + + sas = NULL; + } + + if (sas) { + presence_chooser_reset_scroll_timeout (chooser); + + priv->scroll_status = g_strdup (sas->status); + priv->scroll_state = sas->state; + + priv->scroll_timeout_id = + g_timeout_add_seconds (1, + (GSourceFunc) presence_chooser_scroll_timeout_cb, + chooser); + + presence_chooser_flash_stop (chooser, sas->state); + gtk_label_set_text (GTK_LABEL (priv->label), sas->status); + } + else if (!match) { + const gchar *status; + /* If we didn't get any match at all, it means the last state + * was a custom one. Just switch to the first one. + */ + status = empathy_presence_get_default_message (states[0]); + + presence_chooser_reset_scroll_timeout (chooser); + empathy_idle_set_presence (priv->idle, states[0], status); + } + + g_list_foreach (list, (GFunc) g_free, NULL); + g_list_free (list); + + return TRUE; +} + +static GList * +presence_chooser_get_presets (EmpathyPresenceChooser *chooser) +{ + GList *list = NULL; + guint i; + + for (i = 0; i < G_N_ELEMENTS (states); i += 2) { + GList *presets, *p; + StateAndStatus *sas; + const gchar *status; + + status = empathy_presence_get_default_message (states[i]); + sas = presence_chooser_state_and_status_new (states[i], status); + list = g_list_prepend (list, sas); + + /* Go to next state if we don't want messages for that state */ + if (!states[i+1]) { + continue; + } + + presets = empathy_status_presets_get (states[i], 5); + for (p = presets; p; p = p->next) { + sas = presence_chooser_state_and_status_new (states[i], p->data); + list = g_list_prepend (list, sas); + } + g_list_free (presets); + } + list = g_list_reverse (list); + + return list; +} + +static StateAndStatus * +presence_chooser_state_and_status_new (McPresence state, + const gchar *status) +{ + StateAndStatus *sas; + + sas = g_new0 (StateAndStatus, 1); + + sas->state = state; + sas->status = status; + + return sas; +} + +static gboolean +presence_chooser_flash_timeout_cb (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + McPresence state; + static gboolean on = FALSE; + + priv = GET_PRIV (chooser); + + if (on) { + state = priv->flash_state_1; + } else { + state = priv->flash_state_2; + } + + gtk_image_set_from_icon_name (GTK_IMAGE (priv->image), + empathy_icon_name_for_presence (state), + GTK_ICON_SIZE_MENU); + + on = !on; + + return TRUE; +} + +static void +presence_chooser_flash_start (EmpathyPresenceChooser *chooser, + McPresence state_1, + McPresence state_2) +{ + EmpathyPresenceChooserPriv *priv; + + g_return_if_fail (EMPATHY_IS_PRESENCE_CHOOSER (chooser)); + + priv = GET_PRIV (chooser); + + priv->flash_state_1 = state_1; + priv->flash_state_2 = state_2; + + if (!priv->flash_timeout_id) { + priv->flash_timeout_id = g_timeout_add (FLASH_TIMEOUT, + (GSourceFunc) presence_chooser_flash_timeout_cb, + chooser); + } +} + +static void +presence_chooser_flash_stop (EmpathyPresenceChooser *chooser, + McPresence state) +{ + EmpathyPresenceChooserPriv *priv; + + g_return_if_fail (EMPATHY_IS_PRESENCE_CHOOSER (chooser)); + + priv = GET_PRIV (chooser); + + if (priv->flash_timeout_id) { + g_source_remove (priv->flash_timeout_id); + priv->flash_timeout_id = 0; + } + + gtk_image_set_from_icon_name (GTK_IMAGE (priv->image), + empathy_icon_name_for_presence (state), + GTK_ICON_SIZE_MENU); + + priv->last_state = state; +} + +static gboolean +presence_chooser_button_press_event_cb (GtkWidget *chooser, + GdkEventButton *event, + gpointer user_data) +{ + if (event->button != 1 || event->type != GDK_BUTTON_PRESS) { + return FALSE; + } + + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chooser))) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser), TRUE); + return TRUE; + } + + return FALSE; +} + +static void +presence_chooser_toggled_cb (GtkWidget *chooser, + gpointer user_data) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chooser))) { + presence_chooser_menu_popup (EMPATHY_PRESENCE_CHOOSER (chooser)); + } else { + presence_chooser_menu_popdown (EMPATHY_PRESENCE_CHOOSER (chooser)); + } +} + +static void +presence_chooser_menu_popup (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + GtkWidget *menu; + + priv = GET_PRIV (chooser); + + if (priv->menu) { + return; + } + + menu = empathy_presence_chooser_create_menu (); + + g_signal_connect_after (menu, "selection-done", + G_CALLBACK (presence_chooser_menu_selection_done_cb), + chooser); + + g_signal_connect (menu, "destroy", + G_CALLBACK (presence_chooser_menu_destroy_cb), + chooser); + + gtk_menu_attach_to_widget (GTK_MENU (menu), + GTK_WIDGET (chooser), + presence_chooser_menu_detach); + + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, + (GtkMenuPositionFunc) presence_chooser_menu_align_func, + chooser, + 1, + gtk_get_current_event_time ()); + + priv->menu = menu; +} + +static void +presence_chooser_menu_popdown (EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + + priv = GET_PRIV (chooser); + + if (priv->menu) { + gtk_widget_destroy (priv->menu); + } +} + +static void +presence_chooser_menu_selection_done_cb (GtkMenuShell *menushell, + EmpathyPresenceChooser *chooser) +{ + gtk_widget_destroy (GTK_WIDGET (menushell)); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chooser), FALSE); +} + +static void +presence_chooser_menu_destroy_cb (GtkWidget *menu, + EmpathyPresenceChooser *chooser) +{ + EmpathyPresenceChooserPriv *priv; + + priv = GET_PRIV (chooser); + + priv->menu = NULL; +} + +static void +presence_chooser_menu_detach (GtkWidget *attach_widget, + GtkMenu *menu) +{ + /* We don't need to do anything, but attaching the menu means + * we don't own the ref count and it is cleaned up properly. + */ +} + +static void +presence_chooser_menu_align_func (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + GtkWidget *widget) +{ + GtkRequisition req; + GdkScreen *screen; + gint screen_height; + + gtk_widget_size_request (GTK_WIDGET (menu), &req); + + gdk_window_get_origin (widget->window, x, y); + + *x += widget->allocation.x + 1; + *y += widget->allocation.y; + + screen = gtk_widget_get_screen (GTK_WIDGET (menu)); + screen_height = gdk_screen_get_height (screen); + + if (req.height > screen_height) { + /* Too big for screen height anyway. */ + *y = 0; + return; + } + + if ((*y + req.height + widget->allocation.height) > screen_height) { + /* Can't put it below the button. */ + *y -= req.height; + *y += 1; + } else { + /* Put menu below button. */ + *y += widget->allocation.height; + *y -= 1; + } + + *push_in = FALSE; +} + +GtkWidget * +empathy_presence_chooser_create_menu (void) +{ + const gchar *status; + GtkWidget *menu; + GtkWidget *item; + GtkWidget *image; + guint i; + + menu = gtk_menu_new (); + + for (i = 0; i < G_N_ELEMENTS (states); i += 2) { + GList *list, *l; + + status = empathy_presence_get_default_message (states[i]); + presence_chooser_menu_add_item (menu, + status, + states[i]); + + if (states[i+1]) { + /* Set custom messages if wanted */ + list = empathy_status_presets_get (states[i], 5); + for (l = list; l; l = l->next) { + presence_chooser_menu_add_item (menu, + l->data, + states[i]); + } + g_list_free (list); + } + + } + + /* Separator. */ + item = gtk_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + /* Custom messages */ + item = gtk_image_menu_item_new_with_label (_("Custom messages...")); + image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (image); + gtk_widget_show (item); + + g_signal_connect (item, + "activate", + G_CALLBACK (presence_chooser_custom_activate_cb), + NULL); + + return menu; +} + +static void +presence_chooser_menu_add_item (GtkWidget *menu, + const gchar *str, + McPresence state) +{ + GtkWidget *item; + GtkWidget *image; + const gchar *icon_name; + + item = gtk_image_menu_item_new_with_label (str); + icon_name = empathy_icon_name_for_presence (state); + + g_signal_connect (item, "activate", + G_CALLBACK (presence_chooser_noncustom_activate_cb), + NULL); + + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + gtk_widget_show (image); + + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + gtk_widget_show (item); + + g_object_set_data_full (G_OBJECT (item), + "status", g_strdup (str), + (GDestroyNotify) g_free); + + g_object_set_data (G_OBJECT (item), "state", GINT_TO_POINTER (state)); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +} + +static void +presence_chooser_noncustom_activate_cb (GtkWidget *item, + gpointer user_data) +{ + McPresence state; + const gchar *status; + + status = g_object_get_data (G_OBJECT (item), "status"); + state = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "state")); + + presence_chooser_set_state (state, status); +} + +static void +presence_chooser_set_state (McPresence state, + const gchar *status) +{ + EmpathyIdle *idle; + + idle = empathy_idle_new (); + empathy_idle_set_presence (idle, state, status); + g_object_unref (idle); +} + +static void +presence_chooser_custom_activate_cb (GtkWidget *item, + gpointer user_data) +{ + presence_chooser_dialog_show (); +} + +static McPresence +presence_chooser_dialog_get_selected (CustomMessageDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeIter iter; + McPresence presence = LAST_MC_PRESENCE; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (dialog->combobox_status)); + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (dialog->combobox_status), &iter)) { + gtk_tree_model_get (model, &iter, + COL_PRESENCE, &presence, + -1); + } + + return presence; +} + +static void +presence_chooser_dialog_status_changed_cb (GtkWidget *widget, + CustomMessageDialog *dialog) +{ + GtkListStore *store; + GtkTreeIter iter; + McPresence presence = LAST_MC_PRESENCE; + GList *messages, *l; + + presence = presence_chooser_dialog_get_selected (dialog); + + store = gtk_list_store_new (1, G_TYPE_STRING); + messages = empathy_status_presets_get (presence, -1); + for (l = messages; l; l = l->next) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, l->data, -1); + } + + gtk_entry_set_text (GTK_ENTRY (dialog->entry_message), + messages ? messages->data : ""); + + g_list_free (messages); + + gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->comboboxentry_message), + GTK_TREE_MODEL (store)); + + g_object_unref (store); +} + +static void +presence_chooser_dialog_message_changed_cb (GtkWidget *widget, + CustomMessageDialog *dialog) +{ + McPresence presence; + GList *messages, *l; + const gchar *text; + gboolean found = FALSE; + + presence = presence_chooser_dialog_get_selected (dialog); + text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message)); + + messages = empathy_status_presets_get (presence, -1); + for (l = messages; l; l = l->next) { + if (!tp_strdiff (text, l->data)) { + found = TRUE; + break; + } + } + g_list_free (messages); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->checkbutton_save), + found); +} + +static void +presence_chooser_dialog_save_toggled_cb (GtkWidget *widget, + CustomMessageDialog *dialog) +{ + gboolean active; + McPresence state; + const gchar *text; + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->checkbutton_save)); + state = presence_chooser_dialog_get_selected (dialog); + text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message)); + + if (active) { + empathy_status_presets_set_last (state, text); + } else { + empathy_status_presets_remove (state, text); + } +} + +static void +presence_chooser_dialog_setup (CustomMessageDialog *dialog) +{ + GtkListStore *store; + GtkCellRenderer *renderer; + GtkTreeIter iter; + guint i; + + store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* Icon name */ + G_TYPE_STRING, /* Label */ + MC_TYPE_PRESENCE); /* Presence */ + gtk_combo_box_set_model (GTK_COMBO_BOX (dialog->combobox_status), + GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, + "icon-name", COL_ICON, + NULL); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (dialog->combobox_status), renderer, + "text", COL_LABEL, + NULL); + + for (i = 0; i < G_N_ELEMENTS (states); i += 2) { + if (!states[i+1]) { + continue; + } + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_ICON, empathy_icon_name_for_presence (states[i]), + COL_LABEL, empathy_presence_get_default_message (states[i]), + COL_PRESENCE, states[i], + -1); + } + + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combobox_status), 0); +} + +static void +presence_chooser_dialog_response_cb (GtkWidget *widget, + gint response, + CustomMessageDialog *dialog) +{ + if (response == GTK_RESPONSE_APPLY) { + McPresence state; + const gchar *text; + + state = presence_chooser_dialog_get_selected (dialog); + text = gtk_entry_get_text (GTK_ENTRY (dialog->entry_message)); + + presence_chooser_set_state (state, text); + } + + gtk_widget_destroy (widget); +} + +static void +presence_chooser_dialog_destroy_cb (GtkWidget *widget, + CustomMessageDialog *dialog) +{ + + g_free (dialog); + message_dialog = NULL; +} + +static void +presence_chooser_dialog_show (void) +{ + GladeXML *glade; + + if (message_dialog) { + gtk_window_present (GTK_WINDOW (message_dialog->dialog)); + return; + } + + message_dialog = g_new0 (CustomMessageDialog, 1); + glade = empathy_glade_get_file ("empathy-presence-chooser.glade", + "custom_message_dialog", + NULL, + "custom_message_dialog", &message_dialog->dialog, + "checkbutton_save", &message_dialog->checkbutton_save, + "comboboxentry_message", &message_dialog->comboboxentry_message, + "combobox_status", &message_dialog->combobox_status, + NULL); + empathy_glade_connect (glade, + message_dialog, + "custom_message_dialog", "destroy", presence_chooser_dialog_destroy_cb, + "custom_message_dialog", "response", presence_chooser_dialog_response_cb, + "combobox_status", "changed", presence_chooser_dialog_status_changed_cb, + "checkbutton_save", "toggled", presence_chooser_dialog_save_toggled_cb, + NULL); + + g_object_unref (glade); + + /* Setup the message combobox */ + message_dialog->entry_message = GTK_BIN (message_dialog->comboboxentry_message)->child; + gtk_entry_set_activates_default (GTK_ENTRY (message_dialog->entry_message), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (message_dialog->entry_message), 25); + g_signal_connect (message_dialog->entry_message, "changed", + G_CALLBACK (presence_chooser_dialog_message_changed_cb), + message_dialog); + + presence_chooser_dialog_setup (message_dialog); + + gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (message_dialog->comboboxentry_message), 0); + + /* FIXME: Set transian for a window ? */ + + gtk_widget_show_all (message_dialog->dialog); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-presence-chooser.glade b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.glade new file mode 100644 index 000000000..a3a26a156 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.glade @@ -0,0 +1,200 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> +<requires lib="gnome"/> + +<widget class="GtkDialog" id="custom_message_dialog"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Custom message</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area6"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="closebutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-apply</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-10</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table1"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkLabel" id="label471"> + <property name="visible">True</property> + <property name="label" translatable="yes">Status:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label472"> + <property name="visible">True</property> + <property name="label" translatable="yes">Message:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton_save"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Save message</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBoxEntry" id="comboboxentry_message"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="has_frame">True</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="combobox_status"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-presence-chooser.h b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.h new file mode 100644 index 000000000..a04458d83 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-presence-chooser.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_PRESENCE_CHOOSER_H__ +#define __EMPATHY_PRESENCE_CHOOSER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_PRESENCE_CHOOSER (empathy_presence_chooser_get_type ()) +#define EMPATHY_PRESENCE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_PRESENCE_CHOOSER, EmpathyPresenceChooser)) +#define EMPATHY_PRESENCE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_PRESENCE_CHOOSER, EmpathyPresenceChooserClass)) +#define EMPATHY_IS_PRESENCE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_PRESENCE_CHOOSER)) +#define EMPATHY_IS_PRESENCE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_PRESENCE_CHOOSER)) +#define EMPATHY_PRESENCE_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_PRESENCE_CHOOSER, EmpathyPresenceChooserClass)) + +typedef struct _EmpathyPresenceChooser EmpathyPresenceChooser; +typedef struct _EmpathyPresenceChooserClass EmpathyPresenceChooserClass; + +struct _EmpathyPresenceChooser { + GtkToggleButton parent; +}; + +struct _EmpathyPresenceChooserClass { + GtkToggleButtonClass parent_class; +}; + +GType empathy_presence_chooser_get_type (void) G_GNUC_CONST; +GtkWidget *empathy_presence_chooser_new (void); +GtkWidget *empathy_presence_chooser_create_menu (void); + +G_END_DECLS + +#endif /* __EMPATHY_PRESENCE_CHOOSER_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-private-chat.c b/gnome-2-22/libempathy-gtk/empathy-private-chat.c new file mode 100644 index 000000000..4ee02e467 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-private-chat.c @@ -0,0 +1,362 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-tp-chat.h> +#include <libempathy/empathy-tp-contact-list.h> +#include <libempathy/empathy-contact-factory.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-private-chat.h" +#include "empathy-chat-view.h" +#include "empathy-chat.h" +#include "empathy-preferences.h" +//#include "empathy-sound.h" +#include "empathy-images.h" +#include "empathy-ui-utils.h" + +#define DEBUG_DOMAIN "PrivateChat" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_PRIVATE_CHAT, EmpathyPrivateChatPriv)) + +struct _EmpathyPrivateChatPriv { + EmpathyContactFactory *factory; + EmpathyContact *contact; + gchar *name; + gboolean is_online; + GtkWidget *widget; + GtkWidget *text_view_sw; +}; + +static void empathy_private_chat_class_init (EmpathyPrivateChatClass *klass); +static void empathy_private_chat_init (EmpathyPrivateChat *chat); +static void private_chat_finalize (GObject *object); +static void private_chat_create_ui (EmpathyPrivateChat *chat); +static void private_chat_contact_presence_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyPrivateChat *chat); +static void private_chat_contact_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyPrivateChat *chat); +static void private_chat_widget_destroy_cb (GtkWidget *widget, + EmpathyPrivateChat *chat); +static const gchar * private_chat_get_name (EmpathyChat *chat); +static gchar * private_chat_get_tooltip (EmpathyChat *chat); +static const gchar * private_chat_get_status_icon_name (EmpathyChat *chat); +static GtkWidget * private_chat_get_widget (EmpathyChat *chat); + +G_DEFINE_TYPE (EmpathyPrivateChat, empathy_private_chat, EMPATHY_TYPE_CHAT); + + +static GObject * +private_chat_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *chat; + EmpathyPrivateChatPriv *priv; + EmpathyTpChat *tp_chat; + TpChan *tp_chan; + McAccount *account; + + chat = G_OBJECT_CLASS (empathy_private_chat_parent_class)->constructor (type, n_props, props); + + priv = GET_PRIV (chat); + + g_object_get (chat, "tp-chat", &tp_chat, NULL); + tp_chan = empathy_tp_chat_get_channel (tp_chat); + account = empathy_tp_chat_get_account (tp_chat); + + priv->factory = empathy_contact_factory_new (); + priv->contact = empathy_contact_factory_get_from_handle (priv->factory, + account, + tp_chan->handle); + + priv->name = g_strdup (empathy_contact_get_name (priv->contact)); + + g_signal_connect (priv->contact, + "notify::name", + G_CALLBACK (private_chat_contact_updated_cb), + chat); + g_signal_connect (priv->contact, + "notify::presence", + G_CALLBACK (private_chat_contact_presence_updated_cb), + chat); + + priv->is_online = empathy_contact_is_online (priv->contact); + + g_object_unref (tp_chat); + + return chat; +} + +static void +empathy_private_chat_class_init (EmpathyPrivateChatClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + EmpathyChatClass *chat_class = EMPATHY_CHAT_CLASS (klass); + + object_class->finalize = private_chat_finalize; + object_class->constructor = private_chat_constructor; + + chat_class->get_name = private_chat_get_name; + chat_class->get_tooltip = private_chat_get_tooltip; + chat_class->get_status_icon_name = private_chat_get_status_icon_name; + chat_class->get_widget = private_chat_get_widget; + chat_class->set_tp_chat = NULL; + + g_type_class_add_private (object_class, sizeof (EmpathyPrivateChatPriv)); +} + +static void +empathy_private_chat_init (EmpathyPrivateChat *chat) +{ + private_chat_create_ui (chat); +} + +static void +private_chat_finalize (GObject *object) +{ + EmpathyPrivateChat *chat; + EmpathyPrivateChatPriv *priv; + + chat = EMPATHY_PRIVATE_CHAT (object); + priv = GET_PRIV (chat); + + g_signal_handlers_disconnect_by_func (priv->contact, + private_chat_contact_updated_cb, + chat); + g_signal_handlers_disconnect_by_func (priv->contact, + private_chat_contact_presence_updated_cb, + chat); + + if (priv->contact) { + g_object_unref (priv->contact); + } + if (priv->factory) { + g_object_unref (priv->factory); + } + g_free (priv->name); + + G_OBJECT_CLASS (empathy_private_chat_parent_class)->finalize (object); +} + +static void +private_chat_create_ui (EmpathyPrivateChat *chat) +{ + GladeXML *glade; + EmpathyPrivateChatPriv *priv; + GtkWidget *input_text_view_sw; + + priv = GET_PRIV (chat); + + glade = empathy_glade_get_file ("empathy-chat.glade", + "chat_widget", + NULL, + "chat_widget", &priv->widget, + "chat_view_sw", &priv->text_view_sw, + "input_text_view_sw", &input_text_view_sw, + NULL); + + empathy_glade_connect (glade, + chat, + "chat_widget", "destroy", private_chat_widget_destroy_cb, + NULL); + + g_object_unref (glade); + + g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat)); + + gtk_container_add (GTK_CONTAINER (priv->text_view_sw), + GTK_WIDGET (EMPATHY_CHAT (chat)->view)); + gtk_widget_show (GTK_WIDGET (EMPATHY_CHAT (chat)->view)); + + gtk_container_add (GTK_CONTAINER (input_text_view_sw), + EMPATHY_CHAT (chat)->input_text_view); + gtk_widget_show (EMPATHY_CHAT (chat)->input_text_view); +} + +static void +private_chat_contact_presence_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyPrivateChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + priv = GET_PRIV (chat); + + empathy_debug (DEBUG_DOMAIN, "Presence update for contact: %s", + empathy_contact_get_id (contact)); + + if (!empathy_contact_is_online (contact)) { + if (priv->is_online && !EMPATHY_CHAT (chat)->block_events) { + gchar *msg; + + msg = g_strdup_printf (_("%s went offline"), + empathy_contact_get_name (priv->contact)); + empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, msg); + g_free (msg); + } + + priv->is_online = FALSE; + + g_signal_emit_by_name (chat, "composing", FALSE); + + } else { + if (!priv->is_online && !EMPATHY_CHAT (chat)->block_events) { + gchar *msg; + + msg = g_strdup_printf (_("%s has come online"), + empathy_contact_get_name (priv->contact)); + empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, msg); + g_free (msg); + } + + priv->is_online = TRUE; + + /* If offline message is not supported by CM we need to + * request a new Text Channel. */ + if (!empathy_chat_is_connected (EMPATHY_CHAT (chat))) { + empathy_chat_with_contact (contact); + } + } + + g_signal_emit_by_name (chat, "status-changed"); +} + +static void +private_chat_contact_updated_cb (EmpathyContact *contact, + GParamSpec *param, + EmpathyPrivateChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + priv = GET_PRIV (chat); + + if (strcmp (priv->name, empathy_contact_get_name (contact)) != 0) { + g_free (priv->name); + priv->name = g_strdup (empathy_contact_get_name (contact)); + g_signal_emit_by_name (chat, "name-changed", priv->name); + } +} + +static void +private_chat_widget_destroy_cb (GtkWidget *widget, + EmpathyPrivateChat *chat) +{ + empathy_debug (DEBUG_DOMAIN, "Destroyed"); + + g_object_unref (chat); +} + +static const gchar * +private_chat_get_name (EmpathyChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_PRIVATE_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + return priv->name; +} + +static gchar * +private_chat_get_tooltip (EmpathyChat *chat) +{ + EmpathyPrivateChatPriv *priv; + const gchar *status; + + g_return_val_if_fail (EMPATHY_IS_PRIVATE_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + status = empathy_contact_get_status (priv->contact); + + return g_strdup_printf ("%s\n%s", + empathy_contact_get_id (priv->contact), + status); +} + +static const gchar * +private_chat_get_status_icon_name (EmpathyChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_PRIVATE_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + return empathy_icon_name_for_contact (priv->contact); +} + +EmpathyContact * +empathy_private_chat_get_contact (EmpathyPrivateChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_PRIVATE_CHAT (chat), NULL); + + priv = GET_PRIV (chat); + + return priv->contact; +} + +static GtkWidget * +private_chat_get_widget (EmpathyChat *chat) +{ + EmpathyPrivateChatPriv *priv; + + priv = GET_PRIV (chat); + + return priv->widget; +} + +EmpathyPrivateChat * +empathy_private_chat_new (EmpathyTpChat *tp_chat) +{ + EmpathyPrivateChat *chat; + + g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL); + + chat = g_object_new (EMPATHY_TYPE_PRIVATE_CHAT, + "tp-chat", tp_chat, + NULL); + + return chat; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-private-chat.h b/gnome-2-22/libempathy-gtk/empathy-private-chat.h new file mode 100644 index 000000000..169809bb0 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-private-chat.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Geert-Jan Van den Bogaerde <geertjan@gnome.org> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_PRIVATE_CHAT_H__ +#define __EMPATHY_PRIVATE_CHAT_H__ + +#include <libempathy/empathy-tp-chat.h> +#include <libempathy/empathy-contact.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_PRIVATE_CHAT (empathy_private_chat_get_type ()) +#define EMPATHY_PRIVATE_CHAT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_PRIVATE_CHAT, EmpathyPrivateChat)) +#define EMPATHY_PRIVATE_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_PRIVATE_CHAT, EmpathyPrivateChatClass)) +#define EMPATHY_IS_PRIVATE_CHAT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_PRIVATE_CHAT)) +#define EMPATHY_IS_PRIVATE_CHAT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_PRIVATE_CHAT)) +#define EMPATHY_PRIVATE_CHAT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_PRIVATE_CHAT, EmpathyPrivateChatClass)) + +typedef struct _EmpathyPrivateChat EmpathyPrivateChat; +typedef struct _EmpathyPrivateChatClass EmpathyPrivateChatClass; +typedef struct _EmpathyPrivateChatPriv EmpathyPrivateChatPriv; + +#include "empathy-chat.h" + +struct _EmpathyPrivateChat { + EmpathyChat parent; +}; + +struct _EmpathyPrivateChatClass { + EmpathyChatClass parent; +}; + +GType empathy_private_chat_get_type (void); +EmpathyPrivateChat * empathy_private_chat_new (EmpathyTpChat *tp_chat); +EmpathyContact * empathy_private_chat_get_contact (EmpathyPrivateChat *chat); + +G_END_DECLS + +#endif /* __EMPATHY_PRIVATE_CHAT_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-profile-chooser.c b/gnome-2-22/libempathy-gtk/empathy-profile-chooser.c new file mode 100644 index 000000000..6c46b6cc5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-profile-chooser.c @@ -0,0 +1,190 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> + +#include <gtk/gtk.h> +#include <libmissioncontrol/mc-profile.h> +#include <libmissioncontrol/mc-protocol.h> + +#include "empathy-profile-chooser.h" +#include "empathy-ui-utils.h" + +enum { + COL_ICON, + COL_LABEL, + COL_PROFILE, + COL_COUNT +}; + +McProfile* +empathy_profile_chooser_get_selected (GtkWidget *widget) +{ + GtkTreeModel *model; + GtkTreeIter iter; + McProfile *profile = NULL; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) { + gtk_tree_model_get (model, &iter, + COL_PROFILE, &profile, + -1); + } + + return profile; +} + +gint +empathy_profile_chooser_n_profiles (GtkWidget *widget) +{ + GtkTreeModel *model; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); + + return gtk_tree_model_iter_n_children (model, NULL); +} + +static gint +profile_chooser_sort_profile_value (McProfile *profile) +{ + guint i; + const gchar *profile_name; + const gchar *names[] = {"jabber", + "salut", + "gtalk", + NULL}; + + profile_name = mc_profile_get_unique_name (profile); + + for (i = 0 ; names[i]; i++) { + if (strcmp (profile_name, names[i]) == 0) { + return i; + } + } + + return i; +} + +static gint +profile_chooser_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + McProfile *profile_a; + McProfile *profile_b; + gint cmp; + + gtk_tree_model_get (model, iter_a, + COL_PROFILE, &profile_a, + -1); + gtk_tree_model_get (model, iter_b, + COL_PROFILE, &profile_b, + -1); + + cmp = profile_chooser_sort_profile_value (profile_a); + cmp -= profile_chooser_sort_profile_value (profile_b); + if (cmp == 0) { + cmp = strcmp (mc_profile_get_display_name (profile_a), + mc_profile_get_display_name (profile_b)); + } + + g_object_unref (profile_a); + g_object_unref (profile_b); + + return cmp; +} + +GtkWidget * +empathy_profile_chooser_new (void) +{ + GList *profiles, *l; + GtkListStore *store; + GtkCellRenderer *renderer; + GtkWidget *combo_box; + GtkTreeIter iter; + gboolean iter_set = FALSE; + + /* set up combo box with new store */ + store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* Icon name */ + G_TYPE_STRING, /* Label */ + MC_TYPE_PROFILE); /* Profile */ + combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, + "icon-name", COL_ICON, + NULL); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, + "text", COL_LABEL, + NULL); + + profiles = mc_profiles_list (); + for (l = profiles; l; l = l->next) { + McProfile *profile; + McProtocol *protocol; + + profile = l->data; + + /* Check if the CM is installed, otherwise skip that profile. + * Workaround SF bug #1688779 */ + protocol = mc_profile_get_protocol (profile); + if (!protocol) { + continue; + } + g_object_unref (protocol); + + gtk_list_store_insert_with_values (store, &iter, 0, + COL_ICON, mc_profile_get_icon_name (profile), + COL_LABEL, mc_profile_get_display_name (profile), + COL_PROFILE, profile, + -1); + iter_set = TRUE; + } + + /* Set the profile sort function */ + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + COL_PROFILE, + profile_chooser_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_PROFILE, + GTK_SORT_ASCENDING); + + if (iter_set) { + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter); + } + + mc_profiles_free_list (profiles); + g_object_unref (store); + + return combo_box; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-profile-chooser.h b/gnome-2-22/libempathy-gtk/empathy-profile-chooser.h new file mode 100644 index 000000000..2cd74e351 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-profile-chooser.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_PROTOCOL_CHOOSER_H__ +#define __EMPATHY_PROTOCOL_CHOOSER_H__ + +#include <libmissioncontrol/mc-profile.h> + +G_BEGIN_DECLS + +GtkWidget * empathy_profile_chooser_new (void); +McProfile * empathy_profile_chooser_get_selected (GtkWidget *widget); +gint empathy_profile_chooser_n_profiles (GtkWidget *widget); + +G_END_DECLS +#endif /* __EMPATHY_PROTOCOL_CHOOSER_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-smiley-manager.c b/gnome-2-22/libempathy-gtk/empathy-smiley-manager.c new file mode 100644 index 000000000..d32570bb3 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-smiley-manager.c @@ -0,0 +1,353 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Dafydd Harrie <dafydd.harries@collabora.co.uk> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-smiley-manager.h" +#include "empathy-ui-utils.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_SMILEY_MANAGER, EmpathySmileyManagerPriv)) + +#define DEBUG_DOMAIN "SmileyManager" + +typedef struct { + gunichar c; + GdkPixbuf *pixbuf; + GSList *childrens; +} SmileyManagerTree; + +struct _EmpathySmileyManagerPriv { + SmileyManagerTree *tree; + GSList *smileys; +}; + +static void empathy_smiley_manager_class_init (EmpathySmileyManagerClass *klass); +static void empathy_smiley_manager_init (EmpathySmileyManager *manager); + +G_DEFINE_TYPE (EmpathySmileyManager, empathy_smiley_manager, G_TYPE_OBJECT); + +static SmileyManagerTree * +smiley_manager_tree_new (gunichar c) +{ + SmileyManagerTree *tree; + + tree = g_slice_new0 (SmileyManagerTree); + tree->c = c; + tree->pixbuf = NULL; + tree->childrens = NULL; + + return tree; +} + +static void +smiley_manager_tree_free (SmileyManagerTree *tree) +{ + GSList *l; + + if (!tree) { + return; + } + + for (l = tree->childrens; l; l = l->next) { + smiley_manager_tree_free (l->data); + } + + if (tree->pixbuf) { + g_object_unref (tree->pixbuf); + } + g_slist_free (tree->childrens); + g_slice_free (SmileyManagerTree, tree); +} + +static EmpathySmiley * +smiley_new (GdkPixbuf *pixbuf, const gchar *str) +{ + EmpathySmiley *smiley; + + smiley = g_slice_new0 (EmpathySmiley); + if (pixbuf) { + smiley->pixbuf = g_object_ref (pixbuf); + } + smiley->str = g_strdup (str); + + return smiley; +} + +void +empathy_smiley_free (EmpathySmiley *smiley) +{ + if (!smiley) { + return; + } + + if (smiley->pixbuf) { + g_object_unref (smiley->pixbuf); + } + g_free (smiley->str); + g_slice_free (EmpathySmiley, smiley); +} + +static void +smiley_manager_finalize (GObject *object) +{ + EmpathySmileyManagerPriv *priv = GET_PRIV (object); + + smiley_manager_tree_free (priv->tree); + g_slist_foreach (priv->smileys, (GFunc) empathy_smiley_free, NULL); + g_slist_free (priv->smileys); +} + +static void +empathy_smiley_manager_class_init (EmpathySmileyManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = smiley_manager_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathySmileyManagerPriv)); +} + +static void +empathy_smiley_manager_init (EmpathySmileyManager *manager) +{ + EmpathySmileyManagerPriv *priv = GET_PRIV (manager); + + priv->tree = smiley_manager_tree_new ('\0'); + priv->smileys = NULL; +} + +EmpathySmileyManager * +empathy_smiley_manager_new (void) +{ + static EmpathySmileyManager *manager = NULL; + + if (!manager) { + manager = g_object_new (EMPATHY_TYPE_SMILEY_MANAGER, NULL); + g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager); + empathy_smiley_manager_load (manager); + } else { + g_object_ref (manager); + } + + return manager; +} + +static SmileyManagerTree * +smiley_manager_tree_find_child (SmileyManagerTree *tree, gunichar c) +{ + GSList *l; + + for (l = tree->childrens; l; l = l->next) { + SmileyManagerTree *child = l->data; + + if (child->c == c) { + return child; + } + } + + return NULL; +} + +static SmileyManagerTree * +smiley_manager_tree_find_or_insert_child (SmileyManagerTree *tree, gunichar c) +{ + SmileyManagerTree *child; + + child = smiley_manager_tree_find_child (tree, c); + + if (!child) { + child = smiley_manager_tree_new (c); + tree->childrens = g_slist_prepend (tree->childrens, child); + } + + return child; +} + +static void +smiley_manager_tree_insert (SmileyManagerTree *tree, + GdkPixbuf *smiley, + const gchar *str) +{ + SmileyManagerTree *child; + + child = smiley_manager_tree_find_or_insert_child (tree, g_utf8_get_char (str)); + + str = g_utf8_next_char (str); + if (*str) { + smiley_manager_tree_insert (child, smiley, str); + return; + } + + child->pixbuf = g_object_ref (smiley); +} + +static void +smiley_manager_add_valist (EmpathySmileyManager *manager, + GdkPixbuf *smiley, + const gchar *first_str, + va_list var_args) +{ + EmpathySmileyManagerPriv *priv = GET_PRIV (manager); + const gchar *str; + + for (str = first_str; str; str = va_arg (var_args, gchar*)) { + smiley_manager_tree_insert (priv->tree, smiley, str); + } + + priv->smileys = g_slist_prepend (priv->smileys, smiley_new (smiley, first_str)); +} + +void +empathy_smiley_manager_add (EmpathySmileyManager *manager, + const gchar *icon_name, + const gchar *first_str, + ...) +{ + GdkPixbuf *smiley; + va_list var_args; + + g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); + g_return_if_fail (!G_STR_EMPTY (icon_name)); + g_return_if_fail (!G_STR_EMPTY (first_str)); + + smiley = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + if (smiley) { + va_start (var_args, first_str); + smiley_manager_add_valist (manager, smiley, first_str, var_args); + va_end (var_args); + g_object_unref (smiley); + } +} + +void +empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager, + GdkPixbuf *smiley, + const gchar *first_str, + ...) +{ + va_list var_args; + + g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); + g_return_if_fail (GDK_IS_PIXBUF (smiley)); + g_return_if_fail (!G_STR_EMPTY (first_str)); + + va_start (var_args, first_str); + smiley_manager_add_valist (manager, smiley, first_str, var_args); + va_end (var_args); +} + +void +empathy_smiley_manager_load (EmpathySmileyManager *manager) +{ + g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); + + /* From fd.o icon-naming spec */ + empathy_smiley_manager_add (manager, "face-angel", "O:-)", "O:)", NULL); + empathy_smiley_manager_add (manager, "face-cool", "B-)", "B)", NULL); + empathy_smiley_manager_add (manager, "face-crying", ":'(", NULL); + empathy_smiley_manager_add (manager, "face-devilish", ">:-)", ">:)", NULL); + empathy_smiley_manager_add (manager, "face-embarrassed",":-[", ":[", ":-$", ":$", NULL); + empathy_smiley_manager_add (manager, "face-kiss", ":-*", ":*", NULL); + empathy_smiley_manager_add (manager, "face-monkey", ":-(|)", ":(|)", NULL); + empathy_smiley_manager_add (manager, "face-plain", ":-|", ":|", NULL); + empathy_smiley_manager_add (manager, "face-raspberry", ":-P", ":P", ":-p", ":p", NULL); + empathy_smiley_manager_add (manager, "face-sad", ":-(", ":(", NULL); + empathy_smiley_manager_add (manager, "face-smile", ":-)", ":)", NULL); + empathy_smiley_manager_add (manager, "face-smile-big", ":-D", ":D", ":-d", ":d", NULL); + empathy_smiley_manager_add (manager, "face-smirk", ":-!", ":!", NULL); + empathy_smiley_manager_add (manager, "face-surprise", ":-O", ":O", NULL); + empathy_smiley_manager_add (manager, "face-wink", ";-)", ";)", NULL); +} + +GSList * +empathy_smiley_manager_parse (EmpathySmileyManager *manager, + const gchar *text) +{ + EmpathySmileyManagerPriv *priv = GET_PRIV (manager); + EmpathySmiley *smiley; + SmileyManagerTree *cur_tree = priv->tree; + const gchar *t; + const gchar *cur_str = text; + GSList *smileys = NULL; + + g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL); + g_return_val_if_fail (text != NULL, NULL); + + for (t = text; *t; t = g_utf8_next_char (t)) { + SmileyManagerTree *child; + gunichar c; + + c = g_utf8_get_char (t); + child = smiley_manager_tree_find_child (cur_tree, c); + + if (cur_tree == priv->tree) { + if (child) { + if (t > cur_str) { + smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str)); + smileys = g_slist_prepend (smileys, smiley); + } + cur_str = t; + cur_tree = child; + } + + continue; + } + + if (child) { + cur_tree = child; + continue; + } + + smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); + smileys = g_slist_prepend (smileys, smiley); + if (cur_tree->pixbuf) { + cur_str = t; + cur_tree = smiley_manager_tree_find_child (priv->tree, c); + + if (!cur_tree) { + cur_tree = priv->tree; + } + } else { + cur_str = t; + cur_tree = priv->tree; + } + } + + smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); + smileys = g_slist_prepend (smileys, smiley); + + return g_slist_reverse (smileys); +} + +GSList * +empathy_smiley_manager_get_all (EmpathySmileyManager *manager) +{ + EmpathySmileyManagerPriv *priv = GET_PRIV (manager); + + return priv->smileys; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-smiley-manager.h b/gnome-2-22/libempathy-gtk/empathy-smiley-manager.h new file mode 100644 index 000000000..19604f108 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-smiley-manager.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Dafydd Harrie <dafydd.harries@collabora.co.uk> + * Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_SMILEY_MANAGER__H__ +#define __EMPATHY_SMILEY_MANAGER_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_SMILEY_MANAGER (empathy_smiley_manager_get_type ()) +#define EMPATHY_SMILEY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_SMILEY_MANAGER, EmpathySmileyManager)) +#define EMPATHY_SMILEY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_SMILEY_MANAGER, EmpathySmileyManagerClass)) +#define EMPATHY_IS_SMILEY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_SMILEY_MANAGER)) +#define EMPATHY_IS_SMILEY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_SMILEY_MANAGER)) +#define EMPATHY_SMILEY_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_SMILEY_MANAGER, EmpathySmileyManagerClass)) + +typedef struct _EmpathySmileyManager EmpathySmileyManager; +typedef struct _EmpathySmileyManagerClass EmpathySmileyManagerClass; +typedef struct _EmpathySmileyManagerPriv EmpathySmileyManagerPriv; + +struct _EmpathySmileyManager { + GObject parent; +}; + +struct _EmpathySmileyManagerClass { + GObjectClass parent_class; +}; + +typedef struct { + GdkPixbuf *pixbuf; + gchar *str; +} EmpathySmiley; + +GType empathy_smiley_manager_get_type (void) G_GNUC_CONST; +EmpathySmileyManager *empathy_smiley_manager_new (void); +void empathy_smiley_manager_load (EmpathySmileyManager *manager); +void empathy_smiley_manager_add (EmpathySmileyManager *manager, + const gchar *icon_name, + const gchar *first_str, + ...); +void empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager, + GdkPixbuf *smiley, + const gchar *first_str, + ...); +GSList * empathy_smiley_manager_get_all (EmpathySmileyManager *manager); +GSList * empathy_smiley_manager_parse (EmpathySmileyManager *manager, + const gchar *text); +void empathy_smiley_free (EmpathySmiley *smiley); + +G_END_DECLS + +#endif /* __EMPATHY_SMILEY_MANAGER_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-spell-dialog.c b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.c new file mode 100644 index 000000000..7f4d75a13 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.c @@ -0,0 +1,267 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkliststore.h> +#include <gtk/gtktreeview.h> +#include <gtk/gtktreeselection.h> +#include <gtk/gtksizegroup.h> +#include <glade/glade.h> + +#include "empathy-chat.h" +#include "empathy-spell-dialog.h" +#include "empathy-ui-utils.h" + +typedef struct { + GtkWidget *window; + GtkWidget *button_replace; + GtkWidget *label_word; + GtkWidget *treeview_words; + + EmpathyChat *chat; + + gchar *word; + GtkTextIter start; + GtkTextIter end; +} EmpathySpellDialog; + +enum { + COL_SPELL_WORD, + COL_SPELL_COUNT +}; + +static void spell_dialog_model_populate_columns (EmpathySpellDialog *dialog); +static void spell_dialog_model_populate_suggestions (EmpathySpellDialog *dialog); +static void spell_dialog_model_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathySpellDialog *dialog); +static void spell_dialog_model_selection_changed_cb (GtkTreeSelection *treeselection, + EmpathySpellDialog *dialog); +static void spell_dialog_model_setup (EmpathySpellDialog *dialog); +static void spell_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathySpellDialog *dialog); +static void spell_dialog_destroy_cb (GtkWidget *widget, + EmpathySpellDialog *dialog); + +static void +spell_dialog_model_populate_columns (EmpathySpellDialog *dialog) +{ + GtkTreeModel *model; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + guint col_offset; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview_words)); + + renderer = gtk_cell_renderer_text_new (); + col_offset = gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (dialog->treeview_words), + -1, _("Word"), + renderer, + "text", COL_SPELL_WORD, + NULL); + + g_object_set_data (G_OBJECT (renderer), + "column", GINT_TO_POINTER (COL_SPELL_WORD)); + + column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->treeview_words), col_offset - 1); + gtk_tree_view_column_set_sort_column_id (column, COL_SPELL_WORD); + gtk_tree_view_column_set_resizable (column, FALSE); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); +} + +static void +spell_dialog_model_populate_suggestions (EmpathySpellDialog *dialog) +{ + GtkTreeModel *model; + GtkListStore *store; + GList *suggestions, *l; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->treeview_words)); + store = GTK_LIST_STORE (model); + + suggestions = empathy_spell_get_suggestions (dialog->word); + for (l = suggestions; l; l=l->next) { + GtkTreeIter iter; + gchar *word; + + word = l->data; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_SPELL_WORD, word, + -1); + } + + empathy_spell_free_suggestions (suggestions); +} + +static void +spell_dialog_model_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + EmpathySpellDialog *dialog) +{ + spell_dialog_response_cb (dialog->window, GTK_RESPONSE_OK, dialog); +} + +static void +spell_dialog_model_selection_changed_cb (GtkTreeSelection *treeselection, + EmpathySpellDialog *dialog) +{ + gint count; + + count = gtk_tree_selection_count_selected_rows (treeselection); + gtk_widget_set_sensitive (dialog->button_replace, (count == 1)); +} + +static void +spell_dialog_model_setup (EmpathySpellDialog *dialog) +{ + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + + view = GTK_TREE_VIEW (dialog->treeview_words); + + g_signal_connect (view, "row-activated", + G_CALLBACK (spell_dialog_model_row_activated_cb), + dialog); + + store = gtk_list_store_new (COL_SPELL_COUNT, + G_TYPE_STRING); /* word */ + + gtk_tree_view_set_model (view, GTK_TREE_MODEL (store)); + + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + g_signal_connect (selection, "changed", + G_CALLBACK (spell_dialog_model_selection_changed_cb), + dialog); + + spell_dialog_model_populate_columns (dialog); + spell_dialog_model_populate_suggestions (dialog); + + g_object_unref (store); +} + +static void +spell_dialog_destroy_cb (GtkWidget *widget, + EmpathySpellDialog *dialog) +{ + g_object_unref (dialog->chat); + g_free (dialog->word); + + g_free (dialog); +} + +static void +spell_dialog_response_cb (GtkWidget *widget, + gint response, + EmpathySpellDialog *dialog) +{ + if (response == GTK_RESPONSE_OK) { + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + gchar *new_word; + + view = GTK_TREE_VIEW (dialog->treeview_words); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { + return; + } + + gtk_tree_model_get (model, &iter, COL_SPELL_WORD, &new_word, -1); + + empathy_chat_correct_word (dialog->chat, + dialog->start, + dialog->end, + new_word); + + g_free (new_word); + } + + gtk_widget_destroy (dialog->window); +} + +void +empathy_spell_dialog_show (EmpathyChat *chat, + GtkTextIter start, + GtkTextIter end, + const gchar *word) +{ + EmpathySpellDialog *dialog; + GladeXML *gui; + gchar *str; + + g_return_if_fail (chat != NULL); + g_return_if_fail (word != NULL); + + dialog = g_new0 (EmpathySpellDialog, 1); + + dialog->chat = g_object_ref (chat); + + dialog->word = g_strdup (word); + + dialog->start = start; + dialog->end = end; + + gui = empathy_glade_get_file ("empathy-spell-dialog.glade", + "spell_dialog", + NULL, + "spell_dialog", &dialog->window, + "button_replace", &dialog->button_replace, + "label_word", &dialog->label_word, + "treeview_words", &dialog->treeview_words, + NULL); + + empathy_glade_connect (gui, + dialog, + "spell_dialog", "response", spell_dialog_response_cb, + "spell_dialog", "destroy", spell_dialog_destroy_cb, + NULL); + + g_object_unref (gui); + + str = g_strdup_printf ("%s:\n<b>%s</b>", + _("Suggestions for the word"), + word); + + gtk_label_set_markup (GTK_LABEL (dialog->label_word), str); + g_free (str); + + spell_dialog_model_setup (dialog); + + gtk_widget_show (dialog->window); +} diff --git a/gnome-2-22/libempathy-gtk/empathy-spell-dialog.glade b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.glade new file mode 100644 index 000000000..502fb0d13 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.glade @@ -0,0 +1,205 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="spell_dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Spell Checker</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="modal">True</property> + <property name="default_width">275</property> + <property name="default_height">225</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox7"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area7"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button_replace"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + + <child> + <widget class="GtkAlignment" id="alignment6"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox135"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image245"> + <property name="visible">True</property> + <property name="stock">gtk-convert</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label594"> + <property name="visible">True</property> + <property name="label">_Replace</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox128"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label_word"> + <property name="visible">True</property> + <property name="label" translatable="yes">Suggestions for the word:</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow9"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview_words"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-spell-dialog.h b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.h new file mode 100644 index 000000000..e6d2e4c7a --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-spell-dialog.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Richard Hult <richard@imendio.com> + */ + +#ifndef __EMPATHY_SPELL_DIALOG_H__ +#define __EMPATHY_SPELL_DIALOG_H__ + +#include <gtk/gtktextiter.h> +#include "empathy-chat.h" + +G_BEGIN_DECLS + +void empathy_spell_dialog_show (EmpathyChat *chat, + GtkTextIter start, + GtkTextIter end, + const gchar *word); + +G_END_DECLS + +#endif /* __EMPATHY_SPELL_DIALOG_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-spell.c b/gnome-2-22/libempathy-gtk/empathy-spell.c new file mode 100644 index 000000000..b6620118a --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-spell.c @@ -0,0 +1,452 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Richard Hult <richard@imendio.com> + */ + +#include "config.h" + +#include <string.h> +#include <stdlib.h> + +#include <glib/gi18n.h> + +#ifdef HAVE_ASPELL +#include <aspell.h> +#endif + +#include <libempathy/empathy-debug.h> + +#include "empathy-spell.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" + +#define DEBUG_DOMAIN "Spell" + +#ifdef HAVE_ASPELL + +/* Note: We could use aspell_reset_cache (NULL); periodically if we wanted + * to... + */ + +typedef struct { + AspellConfig *spell_config; + AspellCanHaveError *spell_possible_err; + AspellSpeller *spell_checker; +} SpellLanguage; + +#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes" +#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale" + +static GHashTable *iso_code_names = NULL; +static GList *languages = NULL; +static gboolean empathy_conf_notify_inited = FALSE; + +static void +spell_iso_codes_parse_start_tag (GMarkupParseContext *ctx, + const gchar *element_name, + const gchar **attr_names, + const gchar **attr_values, + gpointer data, + GError **error) +{ + const gchar *ccode_longB, *ccode_longT, *ccode; + const gchar *lang_name; + + if (!g_str_equal (element_name, "iso_639_entry") || + attr_names == NULL || attr_values == NULL) { + return; + } + + ccode = NULL; + ccode_longB = NULL; + ccode_longT = NULL; + lang_name = NULL; + + while (*attr_names && *attr_values) { + if (g_str_equal (*attr_names, "iso_639_1_code")) { + if (**attr_values) { + ccode = *attr_values; + } + } + else if (g_str_equal (*attr_names, "iso_639_2B_code")) { + if (**attr_values) { + ccode_longB = *attr_values; + } + } + else if (g_str_equal (*attr_names, "iso_639_2T_code")) { + if (**attr_values) { + ccode_longT = *attr_values; + } + } + else if (g_str_equal (*attr_names, "name")) { + lang_name = *attr_values; + } + + attr_names++; + attr_values++; + } + + if (!lang_name) { + return; + } + + if (ccode) { + g_hash_table_insert (iso_code_names, + g_strdup (ccode), + g_strdup (lang_name)); + } + + if (ccode_longB) { + g_hash_table_insert (iso_code_names, + g_strdup (ccode_longB), + g_strdup (lang_name)); + } + + if (ccode_longT) { + g_hash_table_insert (iso_code_names, + g_strdup (ccode_longT), + g_strdup (lang_name)); + } +} + +static void +spell_iso_code_names_init (void) +{ + GError *err = NULL; + gchar *buf; + gsize buf_len; + + iso_code_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR); + bind_textdomain_codeset ("iso_639", "UTF-8"); + + /* FIXME: We should read this in chunks and pass to the parser. */ + if (g_file_get_contents (ISO_CODES_DATADIR "/iso_639.xml", &buf, &buf_len, &err)) { + GMarkupParseContext *ctx; + GMarkupParser parser = { + spell_iso_codes_parse_start_tag, + NULL, NULL, NULL, NULL + }; + + ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL); + if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) { + g_warning ("Failed to parse '%s': %s", + ISO_CODES_DATADIR"/iso_639.xml", + err->message); + g_error_free (err); + } + + g_markup_parse_context_free (ctx); + g_free (buf); + } else { + g_warning ("Failed to load '%s': %s", + ISO_CODES_DATADIR"/iso_639.xml", err->message); + g_error_free (err); + } +} + +static void +spell_notify_languages_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + GList *l; + + empathy_debug (DEBUG_DOMAIN, "Resetting languages due to config change"); + + /* We just reset the languages list. */ + for (l = languages; l; l = l->next) { + SpellLanguage *lang; + + lang = l->data; + + delete_aspell_config (lang->spell_config); + delete_aspell_speller (lang->spell_checker); + + g_slice_free (SpellLanguage, lang); + } + + g_list_free (languages); + languages = NULL; +} + +static void +spell_setup_languages (void) +{ + gchar *str; + + if (!empathy_conf_notify_inited) { + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES, + spell_notify_languages_cb, NULL); + + empathy_conf_notify_inited = TRUE; + } + + if (languages) { + return; + } + + if (empathy_conf_get_string (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES, + &str) && str) { + gchar **strv; + gint i; + + strv = g_strsplit (str, ",", -1); + + i = 0; + while (strv && strv[i]) { + SpellLanguage *lang; + + empathy_debug (DEBUG_DOMAIN, "Setting up language:'%s'", strv[i]); + + lang = g_slice_new0 (SpellLanguage); + + lang->spell_config = new_aspell_config(); + + aspell_config_replace (lang->spell_config, "encoding", "utf-8"); + aspell_config_replace (lang->spell_config, "lang", strv[i++]); + + lang->spell_possible_err = new_aspell_speller (lang->spell_config); + + if (aspell_error_number (lang->spell_possible_err) == 0) { + lang->spell_checker = to_aspell_speller (lang->spell_possible_err); + languages = g_list_append (languages, lang); + } else { + delete_aspell_config (lang->spell_config); + g_slice_free (SpellLanguage, lang); + } + } + + if (strv) { + g_strfreev (strv); + } + + g_free (str); + } +} + +const char * +empathy_spell_get_language_name (const char *code) +{ + const gchar *name; + + g_return_val_if_fail (code != NULL, NULL); + + if (!iso_code_names) { + spell_iso_code_names_init (); + } + + name = g_hash_table_lookup (iso_code_names, code); + if (!name) { + return NULL; + } + + return dgettext ("iso_639", name); +} + +GList * +empathy_spell_get_language_codes (void) +{ + AspellConfig *config; + AspellDictInfoList *dlist; + AspellDictInfoEnumeration *dels; + const AspellDictInfo *entry; + GList *codes = NULL; + + config = new_aspell_config (); + dlist = get_aspell_dict_info_list (config); + dels = aspell_dict_info_list_elements (dlist); + + while ((entry = aspell_dict_info_enumeration_next (dels)) != 0) { + if (g_list_find_custom (codes, entry->code, (GCompareFunc) strcmp)) { + continue; + } + + codes = g_list_append (codes, g_strdup (entry->code)); + } + + delete_aspell_dict_info_enumeration (dels); + delete_aspell_config (config); + + return codes; +} + +void +empathy_spell_free_language_codes (GList *codes) +{ + g_list_foreach (codes, (GFunc) g_free, NULL); + g_list_free (codes); +} + +gboolean +empathy_spell_check (const gchar *word) +{ + GList *l; + gint n_langs; + gboolean correct = FALSE; + gint len; + const gchar *p; + gunichar c; + gboolean digit; + + g_return_val_if_fail (word != NULL, FALSE); + + spell_setup_languages (); + + if (!languages) { + empathy_debug (DEBUG_DOMAIN, "No languages to check against"); + return TRUE; + } + + /* Ignore certain cases like numbers, etc. */ + for (p = word, digit = TRUE; *p && digit; p = g_utf8_next_char (p)) { + c = g_utf8_get_char (p); + digit = g_unichar_isdigit (c); + } + + if (digit) { + /* We don't spell check digits. */ + empathy_debug (DEBUG_DOMAIN, "Not spell checking word:'%s', it is all digits", word); + return TRUE; + } + + len = strlen (word); + n_langs = g_list_length (languages); + for (l = languages; l; l = l->next) { + SpellLanguage *lang; + + lang = l->data; + + correct = aspell_speller_check (lang->spell_checker, word, len); + if (n_langs > 1 && correct) { + break; + } + } + + return correct; +} + +GList * +empathy_spell_get_suggestions (const gchar *word) +{ + GList *l1; + GList *l2 = NULL; + const AspellWordList *suggestions; + AspellStringEnumeration *elements; + const char *next; + gint len; + + g_return_val_if_fail (word != NULL, NULL); + + spell_setup_languages (); + + len = strlen (word); + + for (l1 = languages; l1; l1 = l1->next) { + SpellLanguage *lang; + + lang = l1->data; + + suggestions = aspell_speller_suggest (lang->spell_checker, + word, len); + + elements = aspell_word_list_elements (suggestions); + + while ((next = aspell_string_enumeration_next (elements))) { + l2 = g_list_append (l2, g_strdup (next)); + } + + delete_aspell_string_enumeration (elements); + } + + return l2; +} + +gboolean +empathy_spell_supported (void) +{ + if (g_getenv ("EMPATHY_SPELL_DISABLED")) { + empathy_debug (DEBUG_DOMAIN, "EMPATHY_SPELL_DISABLE env variable defined"); + return FALSE; + } + + return TRUE; +} + +#else /* not HAVE_ASPELL */ + +gboolean +empathy_spell_supported (void) +{ + return FALSE; +} + +GList * +empathy_spell_get_suggestions (const gchar *word) +{ + empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get suggestions"); + + return NULL; +} + +gboolean +empathy_spell_check (const gchar *word) +{ + empathy_debug (DEBUG_DOMAIN, "Support disabled, could not check spelling"); + + return TRUE; +} + +const char * +empathy_spell_get_language_name (const char *lang) +{ + empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get language name"); + + return NULL; +} + +GList * +empathy_spell_get_language_codes (void) +{ + empathy_debug (DEBUG_DOMAIN, "Support disabled, could not get language codes"); + + return NULL; +} + +void +empathy_spell_free_language_codes (GList *codes) +{ +} + +#endif /* HAVE_ASPELL */ + + +void +empathy_spell_free_suggestions (GList *suggestions) +{ + g_list_foreach (suggestions, (GFunc) g_free, NULL); + g_list_free (suggestions); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-spell.h b/gnome-2-22/libempathy-gtk/empathy-spell.h new file mode 100644 index 000000000..4f483cf35 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-spell.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2004-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Martyn Russell <martyn@imendio.com> + * Richard Hult <richard@imendio.com> + */ + +#ifndef __EMPATHY_SPELL_H__ +#define __EMPATHY_SPELL_H__ + +G_BEGIN_DECLS + +gboolean empathy_spell_supported (void); +const gchar *empathy_spell_get_language_name (const gchar *code); +GList *empathy_spell_get_language_codes (void); +void empathy_spell_free_language_codes (GList *codes); +gboolean empathy_spell_check (const gchar *word); +GList * empathy_spell_get_suggestions (const gchar *word); +void empathy_spell_free_suggestions (GList *suggestions); + +G_END_DECLS + +#endif /* __EMPATHY_SPELL_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-status-icon.c b/gnome-2-22/libempathy-gtk/empathy-status-icon.c new file mode 100644 index 000000000..9f10bd37e --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-status-icon.c @@ -0,0 +1,861 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007-2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#include <config.h> + +#include <string.h> + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <glib/gi18n.h> + +#include <libmissioncontrol/mission-control.h> + +#include <libempathy/empathy-contact-list.h> +#include <libempathy/empathy-contact-manager.h> +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-tp-chat.h> +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> +#include <libempathy/empathy-idle.h> +#include <libempathy/empathy-filter.h> + +#include "empathy-status-icon.h" +#include "empathy-contact-dialogs.h" +#include "empathy-presence-chooser.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-ui-utils.h" +#include "empathy-accounts-dialog.h" +#include "empathy-images.h" +#include "empathy-new-message-dialog.h" + + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconPriv)) + +#define DEBUG_DOMAIN "StatusIcon" + +/* Number of ms to wait when blinking */ +#define BLINK_TIMEOUT 500 + +typedef struct _StatusIconEvent StatusIconEvent; + +struct _EmpathyStatusIconPriv { + GtkStatusIcon *icon; + EmpathyContactManager *manager; + EmpathyFilter *text_filter; + EmpathyFilter *call_filter; + EmpathyIdle *idle; + MissionControl *mc; + GList *events; + gboolean showing_event_icon; + StatusIconEvent *flash_state_event; + guint blink_timeout; + + GtkWindow *window; + GtkWidget *popup_menu; + GtkWidget *show_window_item; + GtkWidget *message_item; + GtkWidget *status_item; +}; + +typedef void (*EventActivatedFunc) (StatusIconEvent *event); + +struct _StatusIconEvent { + gchar *icon_name; + gchar *message; + EventActivatedFunc func; + gpointer user_data; +}; + + +static void empathy_status_icon_class_init (EmpathyStatusIconClass *klass); +static void empathy_status_icon_init (EmpathyStatusIcon *icon); +static void status_icon_finalize (GObject *object); +static void status_icon_text_filter_new_channel (EmpathyFilter *filter, + TpConn *tp_conn, + TpChan *tp_chan, + EmpathyStatusIcon *icon); +static void status_icon_call_filter_new_channel (EmpathyFilter *filter, + TpConn *tp_conn, + TpChan *tp_chan, + EmpathyStatusIcon *icon); +static void status_icon_message_received_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + EmpathyStatusIcon *icon); +static void status_icon_idle_notify_cb (EmpathyStatusIcon *icon); +static void status_icon_update_tooltip (EmpathyStatusIcon *icon); +static void status_icon_set_from_state (EmpathyStatusIcon *icon); +static void status_icon_set_visibility (EmpathyStatusIcon *icon, + gboolean visible, + gboolean store); +static void status_icon_toggle_visibility (EmpathyStatusIcon *icon); +static void status_icon_activate_cb (GtkStatusIcon *status_icon, + EmpathyStatusIcon *icon); +static gboolean status_icon_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyStatusIcon *icon); +static void status_icon_popup_menu_cb (GtkStatusIcon *status_icon, + guint button, + guint activate_time, + EmpathyStatusIcon *icon); +static void status_icon_create_menu (EmpathyStatusIcon *icon); +static void status_icon_new_message_cb (GtkWidget *widget, + EmpathyStatusIcon *icon); +static void status_icon_quit_cb (GtkWidget *window, + EmpathyStatusIcon *icon); +static void status_icon_show_hide_window_cb (GtkWidget *widget, + EmpathyStatusIcon *icon); +static void status_icon_pendings_changed_cb (EmpathyContactManager *manager, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_pending, + EmpathyStatusIcon *icon); +static void status_icon_event_subscribe_cb (StatusIconEvent *event); +static void status_icon_event_flash_state_cb (StatusIconEvent *event); +static void status_icon_event_msg_cb (StatusIconEvent *event); +static StatusIconEvent * status_icon_event_new (EmpathyStatusIcon *icon, + const gchar *icon_name, + const gchar *message); +static void status_icon_event_remove (EmpathyStatusIcon *icon, + StatusIconEvent *event); +static gboolean status_icon_event_timeout_cb (EmpathyStatusIcon *icon); +static void status_icon_event_free (StatusIconEvent *event); + +G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT); + +static void +status_icon_notify_use_nm_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyStatusIconPriv *priv = GET_PRIV (user_data); + gboolean use_nm; + + if (empathy_conf_get_bool (conf, key, &use_nm)) { + empathy_idle_set_use_nm (priv->idle, use_nm); + } +} + +static void +status_icon_notify_visibility_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyStatusIcon *icon = user_data; + gboolean hidden = FALSE; + + if (empathy_conf_get_bool (conf, key, &hidden)) { + status_icon_set_visibility (icon, !hidden, FALSE); + } +} + +static void +empathy_status_icon_class_init (EmpathyStatusIconClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = status_icon_finalize; + + g_type_class_add_private (object_class, sizeof (EmpathyStatusIconPriv)); +} + +static void +empathy_status_icon_init (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + GList *pendings, *l; + gboolean use_nm; + + priv = GET_PRIV (icon); + + priv->icon = gtk_status_icon_new (); + priv->manager = empathy_contact_manager_new (); + priv->mc = empathy_mission_control_new (); + priv->text_filter = empathy_filter_new ("org.gnome.Empathy.ChatFilter", + "/org/gnome/Empathy/ChatFilter", + TP_IFACE_CHANNEL_TYPE_TEXT, + MC_FILTER_PRIORITY_DIALOG, + MC_FILTER_FLAG_INCOMING); + priv->call_filter = empathy_filter_new ("org.gnome.Empathy.CallFilter", + "/org/gnome/Empathy/CallFilter", + TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, + MC_FILTER_PRIORITY_DIALOG, + MC_FILTER_FLAG_INCOMING); + + /* Setup EmpathyIdle */ + priv->idle = empathy_idle_new (); + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_USE_NM, + &use_nm); + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_USE_NM, + status_icon_notify_use_nm_cb, + icon); + empathy_idle_set_auto_away (priv->idle, TRUE); + empathy_idle_set_use_nm (priv->idle, use_nm); + + /* make icon listen and respond to MAIN_WINDOW_HIDDEN changes */ + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, + status_icon_notify_visibility_cb, + icon); + + status_icon_create_menu (icon); + status_icon_idle_notify_cb (icon); + + g_signal_connect (priv->text_filter, "new-channel", + G_CALLBACK (status_icon_text_filter_new_channel), + icon); + g_signal_connect (priv->call_filter, "new-channel", + G_CALLBACK (status_icon_call_filter_new_channel), + icon); + g_signal_connect_swapped (priv->idle, "notify", + G_CALLBACK (status_icon_idle_notify_cb), + icon); + g_signal_connect (priv->icon, "activate", + G_CALLBACK (status_icon_activate_cb), + icon); + g_signal_connect (priv->icon, "popup-menu", + G_CALLBACK (status_icon_popup_menu_cb), + icon); + g_signal_connect (priv->manager, "pendings-changed", + G_CALLBACK (status_icon_pendings_changed_cb), + icon); + + pendings = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (priv->manager)); + for (l = pendings; l; l = l->next) { + EmpathyPendingInfo *info; + + info = l->data; + status_icon_pendings_changed_cb (priv->manager, + info->member, + info->actor, + 0, + info->message, + TRUE, + icon); + empathy_pending_info_free (info); + } + g_list_free (pendings); +} + +static void +status_icon_finalize (GObject *object) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (object); + + g_list_foreach (priv->events, (GFunc) status_icon_event_free, NULL); + g_list_free (priv->events); + + if (priv->blink_timeout) { + g_source_remove (priv->blink_timeout); + } + + g_object_unref (priv->icon); + g_object_unref (priv->window); + g_object_unref (priv->idle); + g_object_unref (priv->manager); + g_object_unref (priv->mc); + g_object_unref (priv->text_filter); + g_object_unref (priv->call_filter); +} + +EmpathyStatusIcon * +empathy_status_icon_new (GtkWindow *window) +{ + EmpathyStatusIconPriv *priv; + EmpathyStatusIcon *icon; + gboolean should_hide; + + g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); + + icon = g_object_new (EMPATHY_TYPE_STATUS_ICON, NULL); + priv = GET_PRIV (icon); + + priv->window = g_object_ref (window); + + g_signal_connect (priv->window, "delete-event", + G_CALLBACK (status_icon_delete_event_cb), + icon); + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, + &should_hide); + + if (gtk_window_is_active (priv->window) == should_hide) { + status_icon_set_visibility (icon, !should_hide, FALSE); + } + + return icon; +} + +static void +status_icon_text_filter_new_channel (EmpathyFilter *filter, + TpConn *tp_conn, + TpChan *tp_chan, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + McAccount *account; + EmpathyTpChat *tp_chat; + + priv = GET_PRIV (icon); + + account = mission_control_get_account_for_connection (priv->mc, tp_conn, NULL); + + empathy_debug (DEBUG_DOMAIN, "New text channel to be filtered for contact %s", + empathy_inspect_channel (account, tp_chan)); + + tp_chat = empathy_tp_chat_new (account, tp_chan); + g_object_set_data (G_OBJECT (tp_chat), "filter", filter); + g_object_unref (account); + + g_signal_connect (tp_chat, "message-received", + G_CALLBACK (status_icon_message_received_cb), + icon); +} + +static void +status_icon_message_received_cb (EmpathyTpChat *tp_chat, + EmpathyMessage *message, + EmpathyStatusIcon *icon) +{ + EmpathyContact *sender; + gchar *msg; + StatusIconEvent *event; + + empathy_debug (DEBUG_DOMAIN, "Message received, add event"); + + g_signal_handlers_disconnect_by_func (tp_chat, + status_icon_message_received_cb, + icon); + + sender = empathy_message_get_sender (message); + msg = g_strdup_printf (_("New message from %s:\n%s"), + empathy_contact_get_name (sender), + empathy_message_get_body (message)); + + event = status_icon_event_new (icon, EMPATHY_IMAGE_NEW_MESSAGE, msg); + event->func = status_icon_event_msg_cb; + event->user_data = tp_chat; + g_free (msg); +} + +static void +status_icon_call_member_added_cb (EmpathyTpGroup *group, + EmpathyContact *member, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyStatusIcon *icon) +{ + EmpathyFilter *filter; + + if (empathy_contact_is_user (member)) { + /* We are member, it's an outgoing call, we can dispatch + * the channel without asking the user */ + empathy_debug (DEBUG_DOMAIN, "Process OUTGOING call channel"); + filter = g_object_get_data (G_OBJECT (group), "filter"); + empathy_filter_process (filter, + empathy_tp_group_get_channel (group), + TRUE); + g_object_unref (group); + } +} + +static void +status_icon_event_call_cb (StatusIconEvent *event) +{ + EmpathyFilter *filter; + EmpathyTpGroup *group; + + empathy_debug (DEBUG_DOMAIN, "Dispatching call channel"); + + group = event->user_data; + filter = g_object_get_data (G_OBJECT (group), "filter"); + empathy_filter_process (filter, + empathy_tp_group_get_channel (group), + TRUE); + g_object_unref (group); +} + +static void +status_icon_call_local_pending_cb (EmpathyTpGroup *group, + EmpathyContact *member, + EmpathyContact *actor, + guint reason, + const gchar *message, + EmpathyStatusIcon *icon) +{ + StatusIconEvent *event; + + if (empathy_contact_is_user (member)) { + gchar *msg; + + /* We are local pending, it's an incoming call, we need to ask + * the user if he wants to accept the call. */ + empathy_debug (DEBUG_DOMAIN, "INCOMING call, add event"); + + msg = g_strdup_printf (_("Incoming call from %s:\n%s"), + empathy_contact_get_name (member), + message); + + event = status_icon_event_new (icon, EMPATHY_IMAGE_VOIP, msg); + event->func = status_icon_event_call_cb; + event->user_data = group; + g_free (msg); + } +} + +static void +status_icon_call_filter_new_channel (EmpathyFilter *filter, + TpConn *tp_conn, + TpChan *tp_chan, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + McAccount *account; + EmpathyTpGroup *group; + + priv = GET_PRIV (icon); + + account = mission_control_get_account_for_connection (priv->mc, tp_conn, NULL); + + empathy_debug (DEBUG_DOMAIN, "New media channel to be filtered"); + + /* FIXME: We have to check if the user is member or local-pending to + * know if it's an incoming or outgoing call because of the way we + * request media channels MC can't know if it's incoming or outgoing */ + group = empathy_tp_group_new (account, tp_chan); + g_object_set_data (G_OBJECT (group), "filter", filter); + g_object_unref (account); + + g_signal_connect (group, "member-added", + G_CALLBACK (status_icon_call_member_added_cb), + icon); + g_signal_connect (group, "local-pending", + G_CALLBACK (status_icon_call_local_pending_cb), + icon); +} + +static void +status_icon_idle_notify_cb (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + McPresence flash_state; + + priv = GET_PRIV (icon); + + flash_state = empathy_idle_get_flash_state (priv->idle); + if (flash_state != MC_PRESENCE_UNSET) { + const gchar *icon_name; + + icon_name = empathy_icon_name_for_presence (flash_state); + if (!priv->flash_state_event) { + /* We are now flashing */ + priv->flash_state_event = status_icon_event_new (icon, icon_name, NULL); + priv->flash_state_event->user_data = icon; + priv->flash_state_event->func = status_icon_event_flash_state_cb; + } else { + /* We are still flashing but with another state */ + g_free (priv->flash_state_event->icon_name); + priv->flash_state_event->icon_name = g_strdup (icon_name); + } + } + else if (priv->flash_state_event) { + /* We are no more flashing */ + status_icon_event_remove (icon, priv->flash_state_event); + priv->flash_state_event = NULL; + } + + if (!priv->showing_event_icon) { + status_icon_set_from_state (icon); + } + + status_icon_update_tooltip (icon); +} + +static void +status_icon_update_tooltip (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + const gchar *tooltip = NULL; + + priv = GET_PRIV (icon); + + if (priv->events) { + StatusIconEvent *event; + + event = priv->events->data; + tooltip = event->message; + } + + if (!tooltip) { + tooltip = empathy_idle_get_status (priv->idle); + } + + gtk_status_icon_set_tooltip (priv->icon, tooltip); +} + +static void +status_icon_set_from_state (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + McPresence state; + const gchar *icon_name; + + priv = GET_PRIV (icon); + + state = empathy_idle_get_state (priv->idle); + icon_name = empathy_icon_name_for_presence (state); + gtk_status_icon_set_from_icon_name (priv->icon, icon_name); +} + +static void +status_icon_set_visibility (EmpathyStatusIcon *icon, + gboolean visible, + gboolean store) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (icon); + + if (store) { + empathy_conf_set_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_MAIN_WINDOW_HIDDEN, !visible); + } + + if (!visible) { + empathy_window_iconify (priv->window, priv->icon); + } else { + GList *accounts; + + empathy_window_present (GTK_WINDOW (priv->window), TRUE); + + /* Show the accounts dialog if there is no enabled accounts */ + accounts = mc_accounts_list_by_enabled (TRUE); + if (accounts) { + mc_accounts_list_free (accounts); + } else { + empathy_debug (DEBUG_DOMAIN, + "No enabled account, Showing account dialog"); + empathy_accounts_dialog_show (GTK_WINDOW (priv->window)); + } + } +} + +static void +status_icon_toggle_visibility (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv = GET_PRIV (icon); + gboolean visible; + + visible = gtk_window_is_active (priv->window); + status_icon_set_visibility (icon, !visible, TRUE); +} + +static void +status_icon_activate_cb (GtkStatusIcon *status_icon, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (icon); + + empathy_debug (DEBUG_DOMAIN, "Activated: %s", + priv->events ? "event" : "toggle"); + + if (priv->events) { + status_icon_event_remove (icon, priv->events->data); + } else { + status_icon_toggle_visibility (icon); + } +} + +static gboolean +status_icon_delete_event_cb (GtkWidget *widget, + GdkEvent *event, + EmpathyStatusIcon *icon) +{ + status_icon_set_visibility (icon, FALSE, TRUE); + + return TRUE; +} + +static void +status_icon_popup_menu_cb (GtkStatusIcon *status_icon, + guint button, + guint activate_time, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + GtkWidget *submenu; + gboolean show; + + priv = GET_PRIV (icon); + + show = empathy_window_get_is_visible (GTK_WINDOW (priv->window)); + + g_signal_handlers_block_by_func (priv->show_window_item, + status_icon_show_hide_window_cb, + icon); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->show_window_item), + show); + g_signal_handlers_unblock_by_func (priv->show_window_item, + status_icon_show_hide_window_cb, + icon); + + submenu = empathy_presence_chooser_create_menu (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->status_item), + submenu); + + gtk_menu_popup (GTK_MENU (priv->popup_menu), + NULL, NULL, + gtk_status_icon_position_menu, + priv->icon, + button, + activate_time); +} + +static void +status_icon_create_menu (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + GladeXML *glade; + + priv = GET_PRIV (icon); + + glade = empathy_glade_get_file ("empathy-status-icon.glade", + "tray_menu", + NULL, + "tray_menu", &priv->popup_menu, + "tray_show_list", &priv->show_window_item, + "tray_new_message", &priv->message_item, + "tray_status", &priv->status_item, + NULL); + + empathy_glade_connect (glade, + icon, + "tray_show_list", "toggled", status_icon_show_hide_window_cb, + "tray_new_message", "activate", status_icon_new_message_cb, + "tray_quit", "activate", status_icon_quit_cb, + NULL); + + g_object_unref (glade); +} + +static void +status_icon_new_message_cb (GtkWidget *widget, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (icon); + + empathy_new_message_dialog_show (NULL); +} + +static void +status_icon_quit_cb (GtkWidget *window, + EmpathyStatusIcon *icon) +{ + gtk_main_quit (); +} + +static void +status_icon_show_hide_window_cb (GtkWidget *widget, + EmpathyStatusIcon *icon) +{ + gboolean visible; + + visible = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)); + status_icon_set_visibility (icon, visible, TRUE); +} + +static void +status_icon_pendings_changed_cb (EmpathyContactManager *manager, + EmpathyContact *contact, + EmpathyContact *actor, + guint reason, + gchar *message, + gboolean is_pending, + EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + StatusIconEvent *event; + GString *str; + GList *l; + + priv = GET_PRIV (icon); + + if (!is_pending) { + /* FIXME: We should remove the event */ + return; + } + + for (l = priv->events; l; l = l->next) { + if (empathy_contact_equal (contact, ((StatusIconEvent*)l->data)->user_data)) { + return; + } + } + + str = g_string_new (NULL); + g_string_printf (str, _("Subscription requested by %s"), + empathy_contact_get_name (contact)); + if (!G_STR_EMPTY (message)) { + g_string_append_printf (str, _("\nMessage: %s"), message); + } + + event = status_icon_event_new (icon, GTK_STOCK_DIALOG_QUESTION, str->str); + event->user_data = g_object_ref (contact); + event->func = status_icon_event_subscribe_cb; + + g_string_free (str, TRUE); +} + +static void +status_icon_event_subscribe_cb (StatusIconEvent *event) +{ + EmpathyContact *contact; + + contact = EMPATHY_CONTACT (event->user_data); + + empathy_subscription_dialog_show (contact, NULL); + + g_object_unref (contact); +} + +static void +status_icon_event_flash_state_cb (StatusIconEvent *event) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (event->user_data); + + empathy_idle_set_flash_state (priv->idle, MC_PRESENCE_UNSET); +} + +static void +status_icon_event_msg_cb (StatusIconEvent *event) +{ + EmpathyFilter *filter; + EmpathyTpChat *tp_chat; + + empathy_debug (DEBUG_DOMAIN, "Dispatching text channel"); + + tp_chat = event->user_data; + filter = g_object_get_data (G_OBJECT (tp_chat), "filter"); + empathy_filter_process (filter, + empathy_tp_chat_get_channel (tp_chat), + TRUE); + + g_object_unref (tp_chat); +} + +static StatusIconEvent * +status_icon_event_new (EmpathyStatusIcon *icon, + const gchar *icon_name, + const gchar *message) +{ + EmpathyStatusIconPriv *priv; + StatusIconEvent *event; + + priv = GET_PRIV (icon); + + event = g_slice_new0 (StatusIconEvent); + event->icon_name = g_strdup (icon_name); + event->message = g_strdup (message); + + priv->events = g_list_append (priv->events, event); + if (!priv->blink_timeout) { + priv->showing_event_icon = FALSE; + priv->blink_timeout = g_timeout_add (BLINK_TIMEOUT, + (GSourceFunc) status_icon_event_timeout_cb, + icon); + status_icon_event_timeout_cb (icon); + status_icon_update_tooltip (icon); + } + + return event; +} + +static void +status_icon_event_remove (EmpathyStatusIcon *icon, + StatusIconEvent *event) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (icon); + + if (event->func) { + event->func (event); + } + priv->events = g_list_remove (priv->events, event); + status_icon_event_free (event); + priv->showing_event_icon = FALSE; + status_icon_update_tooltip (icon); + status_icon_set_from_state (icon); + + if (priv->events) { + return; + } + + if (priv->blink_timeout) { + g_source_remove (priv->blink_timeout); + priv->blink_timeout = 0; + } +} + +static gboolean +status_icon_event_timeout_cb (EmpathyStatusIcon *icon) +{ + EmpathyStatusIconPriv *priv; + + priv = GET_PRIV (icon); + + priv->showing_event_icon = !priv->showing_event_icon; + + if (!priv->showing_event_icon) { + status_icon_set_from_state (icon); + } else { + StatusIconEvent *event; + + event = priv->events->data; + gtk_status_icon_set_from_icon_name (priv->icon, event->icon_name); + } + + return TRUE; +} + +static void +status_icon_event_free (StatusIconEvent *event) +{ + g_free (event->icon_name); + g_free (event->message); + g_slice_free (StatusIconEvent, event); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-status-icon.glade b/gnome-2-22/libempathy-gtk/empathy-status-icon.glade new file mode 100644 index 000000000..ab0f09f84 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-status-icon.glade @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkMenu" id="tray_menu"> + <child> + <widget class="GtkCheckMenuItem" id="tray_show_list"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Show Contact List</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="avskiljare5"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="tray_new_message"> + <property name="visible">True</property> + <property name="label" translatable="yes">_New Conversation...</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image599"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">im-message-new</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="tray_status"> + <property name="visible">True</property> + <property name="label" translatable="yes">Status</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="avskiljare6"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="tray_quit"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Quit</property> + <property name="use_underline">True</property> + <child internal-child="image"> + <widget class="GtkImage" id="image600"> + <property name="visible">True</property> + <property name="stock">gtk-quit</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gnome-2-22/libempathy-gtk/empathy-status-icon.h b/gnome-2-22/libempathy-gtk/empathy-status-icon.h new file mode 100644 index 000000000..a7ca3c395 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-status-icon.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_STATUS_ICON_H__ +#define __EMPATHY_STATUS_ICON_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_STATUS_ICON (empathy_status_icon_get_type ()) +#define EMPATHY_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIcon)) +#define EMPATHY_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconClass)) +#define EMPATHY_IS_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_STATUS_ICON)) +#define EMPATHY_IS_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_STATUS_ICON)) +#define EMPATHY_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_STATUS_ICON, EmpathyStatusIconClass)) + +typedef struct _EmpathyStatusIcon EmpathyStatusIcon; +typedef struct _EmpathyStatusIconClass EmpathyStatusIconClass; +typedef struct _EmpathyStatusIconPriv EmpathyStatusIconPriv; + +struct _EmpathyStatusIcon { + GObject parent; +}; + +struct _EmpathyStatusIconClass { + GObjectClass parent_class; +}; + +GType empathy_status_icon_get_type (void) G_GNUC_CONST; +EmpathyStatusIcon *empathy_status_icon_new (GtkWindow *window); + +G_END_DECLS + +#endif /* __EMPATHY_STATUS_ICON_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-boxes.c b/gnome-2-22/libempathy-gtk/empathy-theme-boxes.c new file mode 100644 index 000000000..7285a9e9e --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-boxes.c @@ -0,0 +1,837 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-ui-utils.h" +#include "empathy-main-window.h" +#include "empathy-theme-boxes.h" + +#define DEBUG_DOMAIN "FancyTheme" + +#define MARGIN 4 +#define HEADER_PADDING 2 + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesPriv)) + +typedef struct _EmpathyThemeBoxesPriv EmpathyThemeBoxesPriv; + +struct _EmpathyThemeBoxesPriv { + gchar *header_foreground; + gchar *header_background; + gchar *header_line_background; + gchar *text_foreground; + gchar *text_background; + gchar *action_foreground; + gchar *highlight_foreground; + gchar *time_foreground; + gchar *event_foreground; + gchar *invite_foreground; + gchar *link_foreground; +}; + +static void theme_boxes_finalize (GObject *object); +static void theme_boxes_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void theme_boxes_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void theme_boxes_define_theme_tags (EmpathyTheme *theme, + EmpathyChatView *view); +static void theme_boxes_update_view (EmpathyTheme *theme, + EmpathyChatView *view); +static void theme_boxes_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message); +static void theme_boxes_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str); +static void theme_boxes_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time); +static void theme_boxes_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view); + +enum { + PROP_0, + PROP_HEADER_FOREGROUND, + PROP_HEADER_BACKGROUND, + PROP_HEADER_LINE_BACKGROUND, + PROP_TEXT_FOREGROUND, + PROP_TEXT_BACKGROUND, + PROP_ACTION_FOREGROUND, + PROP_HIGHLIGHT_FOREGROUND, + PROP_TIME_FOREGROUND, + PROP_EVENT_FOREGROUND, + PROP_INVITE_FOREGROUND, + PROP_LINK_FOREGROUND +}; + +enum { + PROP_FLOP, + PROP_MY_PROP +}; + +G_DEFINE_TYPE (EmpathyThemeBoxes, empathy_theme_boxes, EMPATHY_TYPE_THEME); + +static void +empathy_theme_boxes_class_init (EmpathyThemeBoxesClass *class) +{ + GObjectClass *object_class; + EmpathyThemeClass *theme_class; + + object_class = G_OBJECT_CLASS (class); + theme_class = EMPATHY_THEME_CLASS (class); + + object_class->finalize = theme_boxes_finalize; + object_class->get_property = theme_boxes_get_property; + object_class->set_property = theme_boxes_set_property; + + theme_class->update_view = theme_boxes_update_view; + theme_class->append_message = theme_boxes_append_message; + theme_class->append_event = theme_boxes_append_event; + theme_class->append_timestamp = theme_boxes_append_timestamp; + theme_class->append_spacing = theme_boxes_append_spacing; + + g_object_class_install_property (object_class, + PROP_HEADER_FOREGROUND, + g_param_spec_string ("header-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HEADER_BACKGROUND, + g_param_spec_string ("header-background", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HEADER_LINE_BACKGROUND, + g_param_spec_string ("header-line-background", + "", + "", + NULL, + G_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, + PROP_TEXT_FOREGROUND, + g_param_spec_string ("text-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_TEXT_BACKGROUND, + g_param_spec_string ("text-background", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ACTION_FOREGROUND, + g_param_spec_string ("action-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_HIGHLIGHT_FOREGROUND, + g_param_spec_string ("highlight-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_TIME_FOREGROUND, + g_param_spec_string ("time-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_EVENT_FOREGROUND, + g_param_spec_string ("event-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_INVITE_FOREGROUND, + g_param_spec_string ("invite-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_LINK_FOREGROUND, + g_param_spec_string ("link-foreground", + "", + "", + NULL, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyThemeBoxesPriv)); +} + +static void +empathy_theme_boxes_init (EmpathyThemeBoxes *theme) +{ + EmpathyThemeBoxesPriv *priv; + + priv = GET_PRIV (theme); +} + +static void +theme_boxes_finalize (GObject *object) +{ + EmpathyThemeBoxesPriv *priv; + + priv = GET_PRIV (object); + + g_free (priv->header_foreground); + g_free (priv->header_background); + g_free (priv->header_line_background); + g_free (priv->text_foreground); + g_free (priv->text_background); + g_free (priv->action_foreground); + g_free (priv->highlight_foreground); + g_free (priv->time_foreground); + g_free (priv->event_foreground); + g_free (priv->invite_foreground); + g_free (priv->link_foreground); + + (G_OBJECT_CLASS (empathy_theme_boxes_parent_class)->finalize) (object); +} + +static void +theme_boxes_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyThemeBoxesPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_HEADER_FOREGROUND: + g_value_set_string (value, priv->header_foreground); + break; + case PROP_HEADER_BACKGROUND: + g_value_set_string (value, priv->header_background); + break; + case PROP_HEADER_LINE_BACKGROUND: + g_value_set_string (value, priv->header_line_background); + break; + case PROP_TEXT_FOREGROUND: + g_value_set_string (value, priv->text_foreground); + break; + case PROP_TEXT_BACKGROUND: + g_value_set_string (value, priv->text_background); + break; + case PROP_ACTION_FOREGROUND: + g_value_set_string (value, priv->action_foreground); + break; + case PROP_HIGHLIGHT_FOREGROUND: + g_value_set_string (value, priv->highlight_foreground); + break; + case PROP_TIME_FOREGROUND: + g_value_set_string (value, priv->time_foreground); + break; + case PROP_EVENT_FOREGROUND: + g_value_set_string (value, priv->event_foreground); + break; + case PROP_INVITE_FOREGROUND: + g_value_set_string (value, priv->invite_foreground); + break; + case PROP_LINK_FOREGROUND: + g_value_set_string (value, priv->link_foreground); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} +static void +theme_boxes_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyThemeBoxesPriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_HEADER_FOREGROUND: + g_free (priv->header_foreground); + priv->header_foreground = g_value_dup_string (value); + g_object_notify (object, "header-foreground"); + break; + case PROP_HEADER_BACKGROUND: + g_free (priv->header_background); + priv->header_background = g_value_dup_string (value); + g_object_notify (object, "header-background"); + break; + case PROP_HEADER_LINE_BACKGROUND: + g_free (priv->header_line_background); + priv->header_line_background = g_value_dup_string (value); + g_object_notify (object, "header-line_background"); + break; + case PROP_TEXT_FOREGROUND: + g_free (priv->text_foreground); + priv->text_foreground = g_value_dup_string (value); + g_object_notify (object, "text-foreground"); + break; + case PROP_TEXT_BACKGROUND: + g_free (priv->text_background); + priv->text_background = g_value_dup_string (value); + g_object_notify (object, "text-background"); + break; + case PROP_ACTION_FOREGROUND: + g_free (priv->action_foreground); + priv->action_foreground = g_value_dup_string (value); + g_object_notify (object, "action-foreground"); + break; + case PROP_HIGHLIGHT_FOREGROUND: + g_free (priv->highlight_foreground); + priv->highlight_foreground = g_value_dup_string (value); + g_object_notify (object, "highlight-foreground"); + break; + case PROP_TIME_FOREGROUND: + g_free (priv->time_foreground); + priv->time_foreground = g_value_dup_string (value); + g_object_notify (object, "time-foreground"); + break; + case PROP_EVENT_FOREGROUND: + g_free (priv->event_foreground); + priv->event_foreground = g_value_dup_string (value); + g_object_notify (object, "event-foreground"); + break; + case PROP_INVITE_FOREGROUND: + g_free (priv->invite_foreground); + priv->invite_foreground = g_value_dup_string (value); + g_object_notify (object, "invite-foreground"); + break; + case PROP_LINK_FOREGROUND: + g_free (priv->link_foreground); + priv->link_foreground = g_value_dup_string (value); + g_object_notify (object, "link-foreground"); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +theme_boxes_define_theme_tags (EmpathyTheme *theme, EmpathyChatView *view) +{ + EmpathyThemeBoxesPriv *priv; + GtkTextBuffer *buffer; + GtkTextTag *tag; + + priv = GET_PRIV (theme); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + empathy_text_buffer_tag_set (buffer, "fancy-spacing", + "size", 3000, + "pixels-above-lines", 8, + NULL); + + tag = empathy_text_buffer_tag_set (buffer, "fancy-header", + "weight", PANGO_WEIGHT_BOLD, + "pixels-above-lines", HEADER_PADDING, + "pixels-below-lines", HEADER_PADDING, + NULL); + if (priv->header_foreground) { + g_object_set (tag, + "foreground", priv->header_foreground, + "paragraph-background", priv->header_background, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-header-line", + "size", 1, + NULL); + if (priv->header_line_background) { + g_object_set (tag, + "paragraph-background", priv->header_line_background, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-body", + "pixels-above-lines", 4, + NULL); + if (priv->text_background) { + g_object_set (tag, + "paragraph-background", priv->text_background, + NULL); + } + + if (priv->text_foreground) { + g_object_set (tag, + "foreground", priv->text_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-action", + "style", PANGO_STYLE_ITALIC, + "pixels-above-lines", 4, + NULL); + + if (priv->text_background) { + g_object_set (tag, + "paragraph-background", priv->text_background, + NULL); + } + + if (priv->action_foreground) { + g_object_set (tag, + "foreground", priv->action_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-highlight", + "weight", PANGO_WEIGHT_BOLD, + "pixels-above-lines", 4, + NULL); + if (priv->text_background) { + g_object_set (tag, + "paragraph-background", priv->text_background, + NULL); + } + + + if (priv->highlight_foreground) { + g_object_set (tag, + "foreground", priv->highlight_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-time", + "justification", GTK_JUSTIFY_CENTER, + NULL); + if (priv->time_foreground) { + g_object_set (tag, + "foreground", priv->time_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-event", + "justification", GTK_JUSTIFY_LEFT, + NULL); + if (priv->event_foreground) { + g_object_set (tag, + "foreground", priv->event_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "invite", NULL); + if (priv->invite_foreground) { + g_object_set (tag, + "foreground", priv->invite_foreground, + NULL); + } + + tag = empathy_text_buffer_tag_set (buffer, "fancy-link", + "underline", PANGO_UNDERLINE_SINGLE, + NULL); + if (priv->link_foreground) { + g_object_set (tag, + "foreground", priv->link_foreground, + NULL); + } +} + +static void +theme_boxes_update_view (EmpathyTheme *theme, EmpathyChatView *view) +{ + EmpathyThemeBoxesPriv *priv; + + g_return_if_fail (EMPATHY_IS_THEME_BOXES (theme)); + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + priv = GET_PRIV (theme); + + theme_boxes_define_theme_tags (theme, view); + + empathy_chat_view_set_margin (view, MARGIN); +} + +static void +table_size_allocate_cb (GtkWidget *view, + GtkAllocation *allocation, + GtkWidget *box) +{ + gint width, height; + + gtk_widget_get_size_request (box, NULL, &height); + + width = allocation->width; + + width -= \ + gtk_text_view_get_right_margin (GTK_TEXT_VIEW (view)) - \ + gtk_text_view_get_left_margin (GTK_TEXT_VIEW (view)); + width -= 2 * MARGIN; + width -= 2 * HEADER_PADDING; + + gtk_widget_set_size_request (box, width, height); +} + +static void +theme_boxes_maybe_append_header (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *msg) +{ + EmpathyThemeBoxesPriv *priv; + EmpathyContact *contact; + GdkPixbuf *avatar = NULL; + GtkTextBuffer *buffer; + const gchar *name; + gboolean header; + GtkTextIter iter; + GtkWidget *label1, *label2; + GtkTextChildAnchor *anchor; + GtkWidget *box; + gchar *str; + time_t time; + gchar *tmp; + GtkTextIter start; + GdkColor color; + gboolean parse_success; + gboolean from_self; + + priv = GET_PRIV (theme); + + contact = empathy_message_get_sender (msg); + from_self = empathy_contact_is_user (contact); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + empathy_debug (DEBUG_DOMAIN, "Maybe add fancy header"); + + name = empathy_contact_get_name (contact); + + header = FALSE; + + /* Only insert a header if the previously inserted block is not the same + * as this one. This catches all the different cases: + */ + if (empathy_chat_view_get_last_block_type (view) != EMPATHY_CHAT_VIEW_BLOCK_SELF && + empathy_chat_view_get_last_block_type (view) != EMPATHY_CHAT_VIEW_BLOCK_OTHER) { + header = TRUE; + } + else if (from_self && + empathy_chat_view_get_last_block_type (view) == EMPATHY_CHAT_VIEW_BLOCK_OTHER) { + header = TRUE; + } + else if (!from_self && + empathy_chat_view_get_last_block_type (view) == EMPATHY_CHAT_VIEW_BLOCK_SELF) { + header = TRUE; + } + else if (!from_self && + (!empathy_chat_view_get_last_contact (view) || + !empathy_contact_equal (contact, empathy_chat_view_get_last_contact (view)))) { + header = TRUE; + } + + if (!header) { + return; + } + + empathy_theme_append_spacing (theme, view); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + "\n", + -1, + "fancy-header-line", + NULL); + + gtk_text_buffer_get_end_iter (buffer, &iter); + anchor = gtk_text_buffer_create_child_anchor (buffer, &iter); + + box = gtk_hbox_new (FALSE, 0); + + + if (empathy_theme_get_show_avatars (theme)) { + avatar = empathy_chat_view_get_avatar_pixbuf_with_cache (contact); + if (avatar) { + GtkWidget *image; + + image = gtk_image_new_from_pixbuf (avatar); + + gtk_box_pack_start (GTK_BOX (box), image, + FALSE, TRUE, 2); + } + } + + g_signal_connect_object (view, "size-allocate", + G_CALLBACK (table_size_allocate_cb), + box, 0); + + str = g_strdup_printf ("<b>%s</b>", name); + + label1 = g_object_new (GTK_TYPE_LABEL, + "label", str, + "use-markup", TRUE, + "xalign", 0.0, + NULL); + + parse_success = priv->header_foreground && + gdk_color_parse (priv->header_foreground, &color); + + if (parse_success) { + gtk_widget_modify_fg (label1, GTK_STATE_NORMAL, &color); + } + + g_free (str); + + time = empathy_message_get_timestamp (msg); + + tmp = empathy_time_to_string_local (time, + EMPATHY_TIME_FORMAT_DISPLAY_SHORT); + str = g_strdup_printf ("<i>%s</i>", tmp); + g_free (tmp); + + label2 = g_object_new (GTK_TYPE_LABEL, + "label", str, + "use-markup", TRUE, + "xalign", 1.0, + NULL); + + if (parse_success) { + gtk_widget_modify_fg (label2, GTK_STATE_NORMAL, &color); + } + + g_free (str); + + gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.5); + gtk_misc_set_alignment (GTK_MISC (label2), 1.0, 0.5); + + gtk_box_pack_start (GTK_BOX (box), label1, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), label2, TRUE, TRUE, 0); + + gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view), + box, + anchor); + + gtk_widget_show_all (box); + + gtk_text_buffer_get_end_iter (buffer, &iter); + start = iter; + gtk_text_iter_backward_char (&start); + gtk_text_buffer_apply_tag_by_name (buffer, + "fancy-header", + &start, &iter); + + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + "\n", + -1, + "fancy-header", + NULL); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + "\n", + -1, + "fancy-header-line", + NULL); +} + +static void +theme_boxes_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message) +{ + EmpathyContact *sender; + + empathy_theme_maybe_append_date_and_time (theme, view, message); + theme_boxes_maybe_append_header (theme, view, message); + + sender = empathy_message_get_sender (message); + + if (empathy_message_get_type (message) == EMPATHY_MESSAGE_TYPE_ACTION) { + gchar *body; + + body = g_strdup_printf (" * %s %s", + empathy_contact_get_name (sender), + empathy_message_get_body (message)); + empathy_theme_append_text (theme, view, body, + "fancy-action", "fancy-link"); + } else { + empathy_theme_append_text (theme, view, + empathy_message_get_body (message), + "fancy-body", "fancy-link"); + } + + if (empathy_contact_is_user (sender)) { + empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_SELF); + empathy_chat_view_set_last_contact (view, NULL); + } else { + empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_OTHER); + empathy_chat_view_set_last_contact (view, sender); + } +} + +static void +theme_boxes_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *msg; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + empathy_theme_maybe_append_date_and_time (theme, view, NULL); + + gtk_text_buffer_get_end_iter (buffer, &iter); + + msg = g_strdup_printf (" - %s\n", str); + + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, + msg, -1, + "fancy-event", + NULL); + g_free (msg); + + empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_EVENT); +} + +static void +theme_boxes_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time) +{ + GtkTextBuffer *buffer; + time_t timestamp; + GDate *date; + GtkTextIter iter; + GString *str; + + if (!show_date) { + return; + } + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + date = empathy_message_get_date_and_time (message, ×tamp); + + str = g_string_new (NULL); + + if (show_time || show_date) { + empathy_theme_append_spacing (theme, view); + + g_string_append (str, "- "); + } + + if (show_date) { + gchar buf[256]; + + g_date_strftime (buf, 256, _("%A %d %B %Y"), date); + g_string_append (str, buf); + + if (show_time) { + g_string_append (str, ", "); + } + } + + g_date_free (date); + + if (show_time) { + gchar *tmp; + + tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT); + g_string_append (str, tmp); + g_free (tmp); + } + + if (show_time || show_date) { + g_string_append (str, " -\n"); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + str->str, -1, + "fancy-time", + NULL); + + empathy_chat_view_set_last_block_type (view, EMPATHY_CHAT_VIEW_BLOCK_TIME); + empathy_chat_view_set_last_timestamp (view, timestamp); + } + + g_string_free (str, TRUE); + +} + +static void +theme_boxes_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + + g_return_if_fail (EMPATHY_IS_THEME (theme)); + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + "\n", + -1, + "cut", + "fancy-spacing", + NULL); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-boxes.h b/gnome-2-22/libempathy-gtk/empathy-theme-boxes.h new file mode 100644 index 000000000..b1f0033f3 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-boxes.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_THEME_BOXES_H__ +#define __EMPATHY_THEME_BOXES_H__ + +#include <glib-object.h> + +#include "empathy-theme.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_THEME_BOXES (empathy_theme_boxes_get_type ()) +#define EMPATHY_THEME_BOXES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxes)) +#define EMPATHY_THEME_BOXES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesClass)) +#define EMPATHY_IS_THEME_BOXES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_BOXES)) +#define EMPATHY_IS_THEME_BOXES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_BOXES)) +#define EMPATHY_THEME_BOXES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_BOXES, EmpathyThemeBoxesClass)) + +typedef struct _EmpathyThemeBoxes EmpathyThemeBoxes; +typedef struct _EmpathyThemeBoxesClass EmpathyThemeBoxesClass; + +struct _EmpathyThemeBoxes { + EmpathyTheme parent; +}; + +struct _EmpathyThemeBoxesClass { + EmpathyThemeClass parent_class; +}; + +GType empathy_theme_boxes_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __EMPATHY_THEME_BOXES_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-irc.c b/gnome-2-22/libempathy-gtk/empathy-theme-irc.c new file mode 100644 index 000000000..7cd9588bb --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-irc.c @@ -0,0 +1,352 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <libempathy/empathy-debug.h> + +#include "empathy-chat.h" +#include "empathy-ui-utils.h" +#include "empathy-theme-irc.h" + +#define DEBUG_DOMAIN "Theme" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrcPriv)) + +typedef struct _EmpathyThemeIrcPriv EmpathyThemeIrcPriv; + +struct _EmpathyThemeIrcPriv { + gint my_prop; +}; + +static void theme_irc_finalize (GObject *object); +static void theme_irc_update_view (EmpathyTheme *theme, + EmpathyChatView *view); +static void theme_irc_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message); +static void theme_irc_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str); +static void theme_irc_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time); +static void theme_irc_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view); + + +enum { + PROP_0, + PROP_MY_PROP +}; + +G_DEFINE_TYPE (EmpathyThemeIrc, empathy_theme_irc, EMPATHY_TYPE_THEME); + +static void +empathy_theme_irc_class_init (EmpathyThemeIrcClass *class) +{ + GObjectClass *object_class; + EmpathyThemeClass *theme_class; + + object_class = G_OBJECT_CLASS (class); + theme_class = EMPATHY_THEME_CLASS (class); + + object_class->finalize = theme_irc_finalize; + + theme_class->update_view = theme_irc_update_view; + theme_class->append_message = theme_irc_append_message; + theme_class->append_event = theme_irc_append_event; + theme_class->append_timestamp = theme_irc_append_timestamp; + theme_class->append_spacing = theme_irc_append_spacing; + + g_type_class_add_private (object_class, sizeof (EmpathyThemeIrcPriv)); +} + +static void +empathy_theme_irc_init (EmpathyThemeIrc *presence) +{ + EmpathyThemeIrcPriv *priv; + + priv = GET_PRIV (presence); +} + +static void +theme_irc_finalize (GObject *object) +{ + EmpathyThemeIrcPriv *priv; + + priv = GET_PRIV (object); + + (G_OBJECT_CLASS (empathy_theme_irc_parent_class)->finalize) (object); +} + +static void +theme_irc_apply_theme_classic (EmpathyTheme *theme, EmpathyChatView *view) +{ + EmpathyThemeIrcPriv *priv; + GtkTextBuffer *buffer; + + priv = GET_PRIV (theme); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + empathy_text_buffer_tag_set (buffer, "irc-spacing", + "size", 2000, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-nick-self", + "foreground", "sea green", + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-body-self", + /* To get the default theme color: */ + "foreground-set", FALSE, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-action-self", + "foreground", "brown4", + "style", PANGO_STYLE_ITALIC, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-nick-highlight", + "foreground", "indian red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-nick-other", + "foreground", "skyblue4", + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-body-other", + /* To get the default theme color: */ + "foreground-set", FALSE, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-action-other", + "foreground", "brown4", + "style", PANGO_STYLE_ITALIC, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-time", + "foreground", "darkgrey", + "justification", GTK_JUSTIFY_CENTER, + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-event", + "foreground", "PeachPuff4", + "justification", GTK_JUSTIFY_LEFT, + NULL); + + empathy_text_buffer_tag_set (buffer, "invite", + "foreground", "sienna", + NULL); + + empathy_text_buffer_tag_set (buffer, "irc-link", + "foreground", "steelblue", + "underline", PANGO_UNDERLINE_SINGLE, + NULL); +} + + +static void +theme_irc_update_view (EmpathyTheme *theme, EmpathyChatView *view) +{ + theme_irc_apply_theme_classic (theme, view); + empathy_chat_view_set_margin (view, 3); +} + +static void +theme_irc_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message) +{ + GtkTextBuffer *buffer; + const gchar *name; + const gchar *nick_tag; + const gchar *body_tag; + GtkTextIter iter; + gchar *tmp; + EmpathyContact *contact; + + empathy_theme_maybe_append_date_and_time (theme, view, message); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + contact = empathy_message_get_sender (message); + name = empathy_contact_get_name (contact); + + if (empathy_message_get_type (message) == EMPATHY_MESSAGE_TYPE_ACTION) { + if (empathy_contact_is_user (contact)) { + body_tag = "irc-action-self"; + } else { + body_tag = "irc-action-other"; + } + + tmp = g_strdup_printf (" * %s %s", + empathy_contact_get_name (contact), + empathy_message_get_body (message)); + empathy_theme_append_text (theme, view, tmp, + body_tag, "irc-link"); + g_free (tmp); + return; + } + + if (empathy_contact_is_user (contact)) { + nick_tag = "irc-nick-self"; + body_tag = "irc-body-self"; + } else { + if (empathy_chat_should_highlight_nick (message)) { + nick_tag = "irc-nick-highlight"; + } else { + nick_tag = "irc-nick-other"; + } + + body_tag = "irc-body-other"; + } + + gtk_text_buffer_get_end_iter (buffer, &iter); + + /* The nickname. */ + tmp = g_strdup_printf ("%s: ", name); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + tmp, + -1, + "cut", + nick_tag, + NULL); + g_free (tmp); + + /* The text body. */ + empathy_theme_append_text (theme, view, + empathy_message_get_body (message), + body_tag, "irc-link"); +} + +static void +theme_irc_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *msg; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + empathy_theme_maybe_append_date_and_time (theme, view, NULL); + + gtk_text_buffer_get_end_iter (buffer, &iter); + + msg = g_strdup_printf (" - %s\n", str); + gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, + msg, -1, + "irc-event", + NULL); + g_free (msg); +} + +static void +theme_irc_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time) +{ + GtkTextBuffer *buffer; + time_t timestamp; + GDate *date; + GtkTextIter iter; + GString *str; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + date = empathy_message_get_date_and_time (message, ×tamp); + + str = g_string_new (NULL); + + if (show_time || show_date) { + empathy_theme_append_spacing (theme, view); + + g_string_append (str, "- "); + } + + if (show_date) { + gchar buf[256]; + + g_date_strftime (buf, 256, _("%A %d %B %Y"), date); + g_string_append (str, buf); + + if (show_time) { + g_string_append (str, ", "); + } + } + + g_date_free (date); + + if (show_time) { + gchar *tmp; + + tmp = empathy_time_to_string_local (timestamp, EMPATHY_TIME_FORMAT_DISPLAY_SHORT); + g_string_append (str, tmp); + g_free (tmp); + } + + if (show_time || show_date) { + g_string_append (str, " -\n"); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + str->str, -1, + "irc-time", + NULL); + + empathy_chat_view_set_last_timestamp (view, timestamp); + } + + g_string_free (str, TRUE); +} + +static void +theme_irc_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + + g_return_if_fail (EMPATHY_IS_THEME (theme)); + g_return_if_fail (EMPATHY_IS_CHAT_VIEW (view)); + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + "\n", + -1, + "cut", + "irc-spacing", + NULL); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-irc.h b/gnome-2-22/libempathy-gtk/empathy-theme-irc.h new file mode 100644 index 000000000..dc52a56c5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-irc.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_THEME_IRC_H__ +#define __EMPATHY_THEME_IRC_H__ + +#include <glib-object.h> + +#include "empathy-theme.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_THEME_IRC (empathy_theme_irc_get_type ()) +#define EMPATHY_THEME_IRC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrc)) +#define EMPATHY_THEME_IRC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrcClass)) +#define EMPATHY_IS_THEME_IRC(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_IRC)) +#define EMPATHY_IS_THEME_IRC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_IRC)) +#define EMPATHY_THEME_IRC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_IRC, EmpathyThemeIrcClass)) + +typedef struct _EmpathyThemeIrc EmpathyThemeIrc; +typedef struct _EmpathyThemeIrcClass EmpathyThemeIrcClass; + +struct _EmpathyThemeIrc { + EmpathyTheme parent; +}; + +struct _EmpathyThemeIrcClass { + EmpathyThemeClass parent_class; +}; + +GType empathy_theme_irc_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __EMPATHY_THEME_IRC_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-manager.c b/gnome-2-22/libempathy-gtk/empathy-theme-manager.c new file mode 100644 index 000000000..94276c964 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-manager.c @@ -0,0 +1,449 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <libempathy/empathy-utils.h> + +#include "empathy-chat-view.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-theme.h" +#include "empathy-theme-boxes.h" +#include "empathy-theme-irc.h" +#include "empathy-theme-manager.h" + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerPriv)) + +typedef struct { + gchar *name; + guint name_notify_id; + guint room_notify_id; + + gboolean show_avatars; + guint show_avatars_notify_id; + + EmpathyTheme *clean_theme; + EmpathyTheme *simple_theme; + EmpathyTheme *blue_theme; + EmpathyTheme *classic_theme; + + GtkSettings *settings; +} EmpathyThemeManagerPriv; + +static void theme_manager_finalize (GObject *object); +static void theme_manager_notify_name_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void theme_manager_notify_room_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void theme_manager_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data); +static void theme_manager_apply_theme (EmpathyThemeManager *manager, + EmpathyChatView *view, + const gchar *name); + +enum { + THEME_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static const gchar *themes[] = { + "classic", N_("Classic"), + "simple", N_("Simple"), + "clean", N_("Clean"), + "blue", N_("Blue"), + NULL +}; + +G_DEFINE_TYPE (EmpathyThemeManager, empathy_theme_manager, G_TYPE_OBJECT); + +static void +theme_manager_gdk_color_to_hex (GdkColor *gdk_color, gchar *str_color) +{ + g_snprintf (str_color, 10, + "#%02x%02x%02x", + gdk_color->red >> 8, + gdk_color->green >> 8, + gdk_color->blue >> 8); +} + + static void +theme_manager_color_hash_notify_cb (EmpathyThemeManager *manager) +{ + EmpathyThemeManagerPriv *priv; + GtkStyle *style; + gchar color[10]; + + priv = GET_PRIV (manager); + + style = gtk_widget_get_default_style (); + + g_object_freeze_notify (G_OBJECT (priv->simple_theme)); + + theme_manager_gdk_color_to_hex (&style->base[GTK_STATE_SELECTED], color); + g_object_set (priv->simple_theme, + "action-foreground", color, + "link-foreground", color, + NULL); + + theme_manager_gdk_color_to_hex (&style->bg[GTK_STATE_SELECTED], color); + g_object_set (priv->simple_theme, + "header-background", color, + NULL); + + theme_manager_gdk_color_to_hex (&style->dark[GTK_STATE_SELECTED], color); + g_object_set (priv->simple_theme, + "header-line-background", color, + NULL); + + theme_manager_gdk_color_to_hex (&style->fg[GTK_STATE_SELECTED], color); + g_object_set (priv->simple_theme, + "header-foreground", color, + NULL); + + g_object_thaw_notify (G_OBJECT (priv->simple_theme)); + +#if 0 + +FIXME: Make that work, it should update color when theme changes but it + doesnt seems to work with all themes. + + g_object_get (priv->settings, + "color-hash", &color_hash, + NULL); + + /* + * base_color: #ffffffffffff + * fg_color: #000000000000 + * bg_color: #e6e6e7e7e8e8 + * text_color: #000000000000 + * selected_bg_color: #58589a9adbdb + * selected_fg_color: #ffffffffffff + */ + + color = g_hash_table_lookup (color_hash, "base_color"); + if (color) { + theme_manager_gdk_color_to_hex (color, color_str); + g_object_set (priv->simple_theme, + "action-foreground", color_str, + "link-foreground", color_str, + NULL); + } + + color = g_hash_table_lookup (color_hash, "selected_bg_color"); + if (color) { + theme_manager_gdk_color_to_hex (color, color_str); + g_object_set (priv->simple_theme, + "header-background", color_str, + NULL); + } + + color = g_hash_table_lookup (color_hash, "bg_color"); + if (color) { + GdkColor tmp; + + tmp = *color; + tmp.red /= 2; + tmp.green /= 2; + tmp.blue /= 2; + theme_manager_gdk_color_to_hex (&tmp, color_str); + g_object_set (priv->simple_theme, + "header-line-background", color_str, + NULL); + } + + color = g_hash_table_lookup (color_hash, "selected_fg_color"); + if (color) { + theme_manager_gdk_color_to_hex (color, color_str); + g_object_set (priv->simple_theme, + "header-foreground", color_str, + NULL); + } + + g_hash_table_unref (color_hash); + +#endif +} + +static void +empathy_theme_manager_class_init (EmpathyThemeManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + signals[THEME_CHANGED] = + g_signal_new ("theme-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_type_class_add_private (object_class, sizeof (EmpathyThemeManagerPriv)); + + object_class->finalize = theme_manager_finalize; +} + +static void +empathy_theme_manager_init (EmpathyThemeManager *manager) +{ + EmpathyThemeManagerPriv *priv; + + priv = GET_PRIV (manager); + + priv->name_notify_id = + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_CHAT_THEME, + theme_manager_notify_name_cb, + manager); + + priv->room_notify_id = + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_CHAT_THEME_CHAT_ROOM, + theme_manager_notify_room_cb, + manager); + + empathy_conf_get_string (empathy_conf_get (), + EMPATHY_PREFS_CHAT_THEME, + &priv->name); + + /* Unused right now, but will be used soon. */ + priv->show_avatars_notify_id = + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_UI_SHOW_AVATARS, + theme_manager_notify_show_avatars_cb, + manager); + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_UI_SHOW_AVATARS, + &priv->show_avatars); + + priv->settings = gtk_settings_get_default (); + g_signal_connect_swapped (priv->settings, "notify::color-hash", + G_CALLBACK (theme_manager_color_hash_notify_cb), + manager); + + priv->simple_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES, NULL); + theme_manager_color_hash_notify_cb (manager); + + priv->clean_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES, + "header-foreground", "black", + "header-background", "#efefdf", + "header_line_background", "#e3e3d3", + "action_foreground", "brown4", + "time_foreground", "darkgrey", + "event_foreground", "darkgrey", + "invite_foreground", "sienna", + "link_foreground","#49789e", + NULL); + + priv->blue_theme = g_object_new (EMPATHY_TYPE_THEME_BOXES, + "header_foreground", "black", + "header_background", "#88a2b4", + "header_line_background", "#7f96a4", + "text_foreground", "black", + "text_background", "#adbdc8", + "highlight_foreground", "black", + "action_foreground", "brown4", + "time_foreground", "darkgrey", + "event_foreground", "#7f96a4", + "invite_foreground", "sienna", + "link_foreground", "#49789e", + NULL); + + priv->classic_theme = g_object_new (EMPATHY_TYPE_THEME_IRC, NULL); +} + +static void +theme_manager_finalize (GObject *object) +{ + EmpathyThemeManagerPriv *priv; + + priv = GET_PRIV (object); + + empathy_conf_notify_remove (empathy_conf_get (), priv->name_notify_id); + empathy_conf_notify_remove (empathy_conf_get (), priv->room_notify_id); + empathy_conf_notify_remove (empathy_conf_get (), priv->show_avatars_notify_id); + + g_free (priv->name); + + g_object_unref (priv->clean_theme); + g_object_unref (priv->simple_theme); + g_object_unref (priv->blue_theme); + g_object_unref (priv->classic_theme); + + G_OBJECT_CLASS (empathy_theme_manager_parent_class)->finalize (object); +} + +static void +theme_manager_notify_name_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyThemeManager *manager; + EmpathyThemeManagerPriv *priv; + gchar *name; + + manager = user_data; + priv = GET_PRIV (manager); + + g_free (priv->name); + + name = NULL; + if (!empathy_conf_get_string (conf, key, &name) || + name == NULL || name[0] == 0) { + priv->name = g_strdup ("classic"); + g_free (name); + } else { + priv->name = name; + } + + g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL); +} + +static void +theme_manager_notify_room_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + g_signal_emit (user_data, signals[THEME_CHANGED], 0, NULL); +} + +static void +theme_manager_notify_show_avatars_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyThemeManager *manager; + EmpathyThemeManagerPriv *priv; + gboolean value; + + manager = user_data; + priv = GET_PRIV (manager); + + if (!empathy_conf_get_bool (conf, key, &value)) { + priv->show_avatars = FALSE; + } else { + priv->show_avatars = value; + } +} + +static gboolean +theme_manager_ensure_theme_exists (const gchar *name) +{ + gint i; + + if (G_STR_EMPTY (name)) { + return FALSE; + } + + for (i = 0; themes[i]; i += 2) { + if (strcmp (themes[i], name) == 0) { + return TRUE; + } + } + + return FALSE; +} + +static void +theme_manager_apply_theme (EmpathyThemeManager *manager, + EmpathyChatView *view, + const gchar *name) +{ + EmpathyThemeManagerPriv *priv; + EmpathyTheme *theme; + + priv = GET_PRIV (manager); + + /* Make sure all tags are present. Note: not useful now but when we have + * user defined theme it will be. + */ + if (theme_manager_ensure_theme_exists (name)) { + if (strcmp (name, "clean") == 0) { + theme = priv->clean_theme; + } + else if (strcmp (name, "simple") == 0) { + theme = priv->simple_theme; + } + else if (strcmp (name, "blue") == 0) { + theme = priv->blue_theme; + } else { + theme = priv->classic_theme; + } + } else { + theme = priv->classic_theme; + } + + empathy_chat_view_set_theme (view, theme); +} + +EmpathyThemeManager * +empathy_theme_manager_get (void) +{ + static EmpathyThemeManager *manager = NULL; + + if (!manager) { + manager = g_object_new (EMPATHY_TYPE_THEME_MANAGER, NULL); + } + + return manager; +} + +const gchar ** +empathy_theme_manager_get_themes (void) +{ + return themes; +} + +void +empathy_theme_manager_apply (EmpathyThemeManager *manager, + EmpathyChatView *view, + const gchar *name) +{ + EmpathyThemeManagerPriv *priv; + + priv = GET_PRIV (manager); + + theme_manager_apply_theme (manager, view, name); +} + +void +empathy_theme_manager_apply_saved (EmpathyThemeManager *manager, + EmpathyChatView *view) +{ + EmpathyThemeManagerPriv *priv; + + priv = GET_PRIV (manager); + + theme_manager_apply_theme (manager, view, priv->name); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme-manager.h b/gnome-2-22/libempathy-gtk/empathy-theme-manager.h new file mode 100644 index 000000000..6bd2d41f5 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme-manager.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2005-2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_THEME_MANAGER_H__ +#define __EMPATHY_THEME_MANAGER_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_THEME_MANAGER (empathy_theme_manager_get_type ()) +#define EMPATHY_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManager)) +#define EMPATHY_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerClass)) +#define EMPATHY_IS_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_MANAGER)) +#define EMPATHY_IS_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_MANAGER)) +#define EMPATHY_THEME_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_MANAGER, EmpathyThemeManagerClass)) + +typedef struct _EmpathyThemeManager EmpathyThemeManager; +typedef struct _EmpathyThemeManagerClass EmpathyThemeManagerClass; + +struct _EmpathyThemeManager { + GObject parent; +}; + +struct _EmpathyThemeManagerClass { + GObjectClass parent_class; +}; + +GType empathy_theme_manager_get_type (void) G_GNUC_CONST; +EmpathyThemeManager *empathy_theme_manager_get (void); +const gchar ** empathy_theme_manager_get_themes (void); +void empathy_theme_manager_apply (EmpathyThemeManager *manager, + EmpathyChatView *view, + const gchar *theme); +void empathy_theme_manager_apply_saved (EmpathyThemeManager *manager, + EmpathyChatView *view); + +G_END_DECLS + +#endif /* __EMPATHY_THEME_MANAGER_H__ */ diff --git a/gnome-2-22/libempathy-gtk/empathy-theme.c b/gnome-2-22/libempathy-gtk/empathy-theme.c new file mode 100644 index 000000000..dae690b5c --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme.c @@ -0,0 +1,417 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <string.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <libempathy/empathy-debug.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-chat.h" +#include "empathy-conf.h" +#include "empathy-preferences.h" +#include "empathy-theme.h" +#include "empathy-smiley-manager.h" + +#define DEBUG_DOMAIN "Theme" + +/* Number of seconds between timestamps when using normal mode, 5 minutes. */ +#define TIMESTAMP_INTERVAL 300 + +#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_THEME, EmpathyThemePriv)) + +typedef struct _EmpathyThemePriv EmpathyThemePriv; + +struct _EmpathyThemePriv { + EmpathySmileyManager *smiley_manager; + gboolean show_avatars; +}; + +static void theme_finalize (GObject *object); +static void theme_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void theme_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (EmpathyTheme, empathy_theme, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_SHOW_AVATARS +}; + +static void +empathy_theme_class_init (EmpathyThemeClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + object_class->finalize = theme_finalize; + object_class->get_property = theme_get_property; + object_class->set_property = theme_set_property; + + class->update_view = NULL; + class->append_message = NULL; + class->append_event = NULL; + class->append_timestamp = NULL; + class->append_spacing = NULL; + + g_object_class_install_property (object_class, + PROP_SHOW_AVATARS, + g_param_spec_boolean ("show-avatars", + "", "", + TRUE, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (EmpathyThemePriv)); +} + +static void +empathy_theme_init (EmpathyTheme *presence) +{ + EmpathyThemePriv *priv; + + priv = GET_PRIV (presence); + + priv->smiley_manager = empathy_smiley_manager_new (); +} + +static void +theme_finalize (GObject *object) +{ + EmpathyThemePriv *priv; + + priv = GET_PRIV (object); + + if (priv->smiley_manager) { + g_object_unref (priv->smiley_manager); + } + + (G_OBJECT_CLASS (empathy_theme_parent_class)->finalize) (object); +} + +static void +theme_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyThemePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_SHOW_AVATARS: + g_value_set_boolean (value, priv->show_avatars); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +theme_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyThemePriv *priv; + + priv = GET_PRIV (object); + + switch (param_id) { + case PROP_SHOW_AVATARS: + empathy_theme_set_show_avatars (EMPATHY_THEME (object), + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +void +empathy_theme_maybe_append_date_and_time (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message) +{ + time_t timestamp; + GDate *date, *last_date; + gboolean append_date, append_time; + + date = empathy_message_get_date_and_time (message, ×tamp); + + last_date = g_date_new (); + g_date_set_time (last_date, empathy_chat_view_get_last_timestamp (view)); + + append_date = FALSE; + append_time = FALSE; + + if (g_date_compare (date, last_date) > 0) { + append_date = TRUE; + append_time = TRUE; + } + + g_date_free (last_date); + g_date_free (date); + + if (empathy_chat_view_get_last_timestamp (view) + TIMESTAMP_INTERVAL < timestamp) { + append_time = TRUE; + } + + if (append_time || append_date) { + empathy_theme_append_timestamp (theme, view, message, + append_date, append_time); + } +} + +void +empathy_theme_update_view (EmpathyTheme *theme, + EmpathyChatView *view) +{ + if (!EMPATHY_THEME_GET_CLASS(theme)->update_view) { + g_error ("Theme must override update_view"); + } + + return EMPATHY_THEME_GET_CLASS(theme)->update_view (theme, view); +} + +void +empathy_theme_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message) +{ + if (!EMPATHY_THEME_GET_CLASS(theme)->append_message) { + g_warning ("Theme should override append_message"); + return; + } + + EMPATHY_THEME_GET_CLASS(theme)->append_message (theme, view, message); +} + +static void +theme_insert_text_with_emoticons (GtkTextBuffer *buf, + GtkTextIter *iter, + const gchar *str, + EmpathySmileyManager *smiley_manager) +{ + gboolean use_smileys = FALSE; + GSList *smileys, *l; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SHOW_SMILEYS, + &use_smileys); + + if (!use_smileys) { + gtk_text_buffer_insert (buf, iter, str, -1); + return; + } + + smileys = empathy_smiley_manager_parse (smiley_manager, str); + for (l = smileys; l; l = l->next) { + EmpathySmiley *smiley; + + smiley = l->data; + if (smiley->pixbuf) { + gtk_text_buffer_insert_pixbuf (buf, iter, smiley->pixbuf); + } else { + gtk_text_buffer_insert (buf, iter, smiley->str, -1); + } + empathy_smiley_free (smiley); + } + g_slist_free (smileys); +} + +void +empathy_theme_append_text (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *body, + const gchar *tag, + const gchar *link_tag) +{ + EmpathyThemePriv *priv; + GtkTextBuffer *buffer; + GtkTextIter start_iter, end_iter; + GtkTextMark *mark; + GtkTextIter iter; + gint num_matches, i; + GArray *start, *end; + + priv = GET_PRIV (theme); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + + gtk_text_buffer_get_end_iter (buffer, &start_iter); + mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, TRUE); + + start = g_array_new (FALSE, FALSE, sizeof (gint)); + end = g_array_new (FALSE, FALSE, sizeof (gint)); + + num_matches = empathy_regex_match (EMPATHY_REGEX_ALL, body, start, end); + + if (num_matches == 0) { + gtk_text_buffer_get_end_iter (buffer, &iter); + theme_insert_text_with_emoticons (buffer, &iter, body, priv->smiley_manager); + } else { + gint last = 0; + gint s = 0, e = 0; + gchar *tmp; + + for (i = 0; i < num_matches; i++) { + s = g_array_index (start, gint, i); + e = g_array_index (end, gint, i); + + if (s > last) { + tmp = empathy_substring (body, last, s); + + gtk_text_buffer_get_end_iter (buffer, &iter); + theme_insert_text_with_emoticons (buffer, + &iter, + tmp, + priv->smiley_manager); + g_free (tmp); + } + + tmp = empathy_substring (body, s, e); + + gtk_text_buffer_get_end_iter (buffer, &iter); + if (!link_tag) { + gtk_text_buffer_insert (buffer, &iter, + tmp, -1); + } { + gtk_text_buffer_insert_with_tags_by_name (buffer, + &iter, + tmp, + -1, + link_tag, + "link", + NULL); + } + + g_free (tmp); + + last = e; + } + + if (e < strlen (body)) { + tmp = empathy_substring (body, e, strlen (body)); + + gtk_text_buffer_get_end_iter (buffer, &iter); + theme_insert_text_with_emoticons (buffer, + &iter, + tmp, + priv->smiley_manager); + g_free (tmp); + } + } + + g_array_free (start, TRUE); + g_array_free (end, TRUE); + + gtk_text_buffer_get_end_iter (buffer, &iter); + gtk_text_buffer_insert (buffer, &iter, "\n", 1); + + /* Apply the style to the inserted text. */ + gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, mark); + gtk_text_buffer_get_end_iter (buffer, &end_iter); + + gtk_text_buffer_apply_tag_by_name (buffer, + tag, + &start_iter, + &end_iter); + + gtk_text_buffer_delete_mark (buffer, mark); +} + +void +empathy_theme_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str) +{ + if (!EMPATHY_THEME_GET_CLASS(theme)->append_event) { + return; + } + + EMPATHY_THEME_GET_CLASS(theme)->append_event (theme, view, str); +} + +void +empathy_theme_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view) +{ + if (!EMPATHY_THEME_GET_CLASS(theme)->append_spacing) { + return; + } + + EMPATHY_THEME_GET_CLASS(theme)->append_spacing (theme, view); +} + + +void +empathy_theme_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time) +{ + if (!EMPATHY_THEME_GET_CLASS(theme)->append_timestamp) { + return; + } + + EMPATHY_THEME_GET_CLASS(theme)->append_timestamp (theme, view, + message, show_date, + show_time); +} + +gboolean +empathy_theme_get_show_avatars (EmpathyTheme *theme) +{ + EmpathyThemePriv *priv; + + g_return_val_if_fail (EMPATHY_IS_THEME (theme), FALSE); + + priv = GET_PRIV (theme); + + return priv->show_avatars; +} + +void +empathy_theme_set_show_avatars (EmpathyTheme *theme, gboolean show) +{ + EmpathyThemePriv *priv; + + g_return_if_fail (EMPATHY_IS_THEME (theme)); + + priv = GET_PRIV (theme); + + priv->show_avatars = show; + + g_object_notify (G_OBJECT (theme), "show-avatars"); +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-theme.h b/gnome-2-22/libempathy-gtk/empathy-theme.h new file mode 100644 index 000000000..34875bcb4 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-theme.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2007 Imendio AB + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EMPATHY_THEME_H__ +#define __EMPATHY_THEME_H__ + +#include <glib-object.h> +#include <gtk/gtktextbuffer.h> + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_THEME (empathy_theme_get_type ()) +#define EMPATHY_THEME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME, EmpathyTheme)) +#define EMPATHY_THEME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EMPATHY_TYPE_THEME, EmpathyThemeClass)) +#define EMPATHY_IS_THEME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME)) +#define EMPATHY_IS_THEME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME)) +#define EMPATHY_THEME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME, EmpathyThemeClass)) + +typedef struct _EmpathyTheme EmpathyTheme; +typedef struct _EmpathyThemeClass EmpathyThemeClass; + +#include "empathy-chat-view.h" + +struct _EmpathyTheme { + GObject parent; +}; + +struct _EmpathyThemeClass { + GObjectClass parent_class; + + /* <vtable> */ + void (*update_view) (EmpathyTheme *theme, + EmpathyChatView *view); + void (*append_message) (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message); + void (*append_event) (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str); + void (*append_timestamp) (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time); + void (*append_spacing) (EmpathyTheme *theme, + EmpathyChatView *view); +}; + +GType empathy_theme_get_type (void) G_GNUC_CONST; +void empathy_theme_update_view (EmpathyTheme *theme, + EmpathyChatView *view); +void empathy_theme_append_message (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *msg); +void empathy_theme_append_text (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *body, + const gchar *tag, + const gchar *link_tag); +void empathy_theme_append_spacing (EmpathyTheme *theme, + EmpathyChatView *view); +void empathy_theme_append_event (EmpathyTheme *theme, + EmpathyChatView *view, + const gchar *str); +void empathy_theme_append_timestamp (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message, + gboolean show_date, + gboolean show_time); +void empathy_theme_maybe_append_date_and_time (EmpathyTheme *theme, + EmpathyChatView *view, + EmpathyMessage *message); +gboolean empathy_theme_get_show_avatars (EmpathyTheme *theme); +void empathy_theme_set_show_avatars (EmpathyTheme *theme, + gboolean show); + +G_END_DECLS + +#endif /* __EMPATHY_THEME_H__ */ + diff --git a/gnome-2-22/libempathy-gtk/empathy-ui-utils.c b/gnome-2-22/libempathy-gtk/empathy-ui-utils.c new file mode 100644 index 000000000..80dcd4227 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-ui-utils.c @@ -0,0 +1,1418 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007-2008 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + * + * Part of this file is copied from GtkSourceView (gtksourceiter.c): + * Paolo Maggi + * Jeroen Zwartepoorte + */ + +#include <config.h> + +#include <string.h> +#include <X11/Xatom.h> +#include <gdk/gdkx.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <libgnomevfs/gnome-vfs-utils.h> + +#include <libmissioncontrol/mc-profile.h> + +#include <libempathy/empathy-debug.h> + +#include "empathy-ui-utils.h" +#include "empathy-images.h" + +#define DEBUG_DOMAIN "UiUtils" + +struct SizeData { + gint width; + gint height; + gboolean preserve_aspect_ratio; +}; + +static GladeXML * +get_glade_file (const gchar *filename, + const gchar *root, + const gchar *domain, + const gchar *first_required_widget, + va_list args) +{ + GladeXML *gui; + gchar *path; + const char *name; + GtkWidget **widget_ptr; + + path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "libempathy-gtk", + filename, NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + path = g_build_filename (DATADIR, "empathy", filename, NULL); + } + empathy_debug (DEBUG_DOMAIN, "Loading glade file %s", path); + + gui = glade_xml_new (path, root, domain); + g_free (path); + + if (!gui) { + g_warning ("Couldn't find necessary glade file '%s'", filename); + } + + for (name = first_required_widget; name; name = va_arg (args, char *)) { + widget_ptr = va_arg (args, void *); + + *widget_ptr = glade_xml_get_widget (gui, name); + + if (!*widget_ptr) { + g_warning ("Glade file '%s' is missing widget '%s'.", + filename, name); + continue; + } + } + + return gui; +} + +void +empathy_glade_get_file_simple (const gchar *filename, + const gchar *root, + const gchar *domain, + const gchar *first_required_widget, ...) +{ + va_list args; + GladeXML *gui; + + va_start (args, first_required_widget); + + gui = get_glade_file (filename, + root, + domain, + first_required_widget, + args); + + va_end (args); + + if (gui) { + g_object_unref (gui); + } +} + +GladeXML * +empathy_glade_get_file (const gchar *filename, + const gchar *root, + const gchar *domain, + const gchar *first_required_widget, ...) +{ + va_list args; + GladeXML *gui; + + va_start (args, first_required_widget); + + gui = get_glade_file (filename, + root, + domain, + first_required_widget, + args); + + va_end (args); + + if (!gui) { + return NULL; + } + + return gui; +} + +void +empathy_glade_connect (GladeXML *gui, + gpointer user_data, + gchar *first_widget, ...) +{ + va_list args; + const gchar *name; + const gchar *signal; + GtkWidget *widget; + gpointer *callback; + + va_start (args, first_widget); + + for (name = first_widget; name; name = va_arg (args, char *)) { + signal = va_arg (args, void *); + callback = va_arg (args, void *); + + widget = glade_xml_get_widget (gui, name); + if (!widget) { + g_warning ("Glade file is missing widget '%s', aborting", + name); + continue; + } + + g_signal_connect (widget, + signal, + G_CALLBACK (callback), + user_data); + } + + va_end (args); +} + +void +empathy_glade_setup_size_group (GladeXML *gui, + GtkSizeGroupMode mode, + gchar *first_widget, ...) +{ + va_list args; + GtkWidget *widget; + GtkSizeGroup *size_group; + const gchar *name; + + va_start (args, first_widget); + + size_group = gtk_size_group_new (mode); + + for (name = first_widget; name; name = va_arg (args, char *)) { + widget = glade_xml_get_widget (gui, name); + if (!widget) { + g_warning ("Glade file is missing widget '%s'", name); + continue; + } + + gtk_size_group_add_widget (size_group, widget); + } + + g_object_unref (size_group); + + va_end (args); +} + +const gchar * +empathy_icon_name_from_account (McAccount *account) +{ + McProfile *profile; + + profile = mc_account_get_profile (account); + + return mc_profile_get_icon_name (profile); +} + +const gchar * +empathy_icon_name_for_presence (McPresence presence) +{ + switch (presence) { + case MC_PRESENCE_AVAILABLE: + return EMPATHY_IMAGE_AVAILABLE; + case MC_PRESENCE_DO_NOT_DISTURB: + return EMPATHY_IMAGE_BUSY; + case MC_PRESENCE_AWAY: + return EMPATHY_IMAGE_AWAY; + case MC_PRESENCE_EXTENDED_AWAY: + return EMPATHY_IMAGE_EXT_AWAY; + case MC_PRESENCE_HIDDEN: + return EMPATHY_IMAGE_HIDDEN; + case MC_PRESENCE_OFFLINE: + case MC_PRESENCE_UNSET: + return EMPATHY_IMAGE_OFFLINE; + default: + g_assert_not_reached (); + } + + return NULL; +} + +const gchar * +empathy_icon_name_for_contact (EmpathyContact *contact) +{ + McPresence presence; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), + EMPATHY_IMAGE_OFFLINE); + + presence = empathy_contact_get_presence (contact); + return empathy_icon_name_for_presence (presence); +} + +GdkPixbuf * +empathy_pixbuf_from_data (gchar *data, + gsize data_size) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + + if (!data) { + return NULL; + } + + loader = gdk_pixbuf_loader_new (); + if (!gdk_pixbuf_loader_write (loader, data, data_size, &error)) { + empathy_debug (DEBUG_DOMAIN, "Failed to write to pixbuf loader: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_object_unref (loader); + return NULL; + } + if (!gdk_pixbuf_loader_close (loader, &error)) { + empathy_debug (DEBUG_DOMAIN, "Failed to close pixbuf loader: %s", + error ? error->message : "No error given"); + g_clear_error (&error); + g_object_unref (loader); + return NULL; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf) { + g_object_ref (pixbuf); + } + + g_object_unref (loader); + + return pixbuf; +} + +static void +pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader, + int width, + int height, + struct SizeData *data) +{ + g_return_if_fail (width > 0 && height > 0); + + if (data->preserve_aspect_ratio && (data->width > 0 || data->height > 0)) { + if (width < data->width && height < data->height) { + width = width; + height = height; + } + + if (data->width < 0) { + width = width * (double) data->height / (gdouble) height; + height = data->height; + } else if (data->height < 0) { + height = height * (double) data->width / (double) width; + width = data->width; + } else if ((double) height * (double) data->width > + (double) width * (double) data->height) { + width = 0.5 + (double) width * (double) data->height / (double) height; + height = data->height; + } else { + height = 0.5 + (double) height * (double) data->width / (double) width; + width = data->width; + } + } else { + if (data->width > 0) { + width = data->width; + } + + if (data->height > 0) { + height = data->height; + } + } + + gdk_pixbuf_loader_set_size (loader, width, height); +} + +static void +empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf) +{ + gint width, height, rowstride; + guchar *pixels; + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + if (width < 6 || height < 6) { + return; + } + + /* Top left */ + pixels[3] = 0; + pixels[7] = 0x80; + pixels[11] = 0xC0; + pixels[rowstride + 3] = 0x80; + pixels[rowstride * 2 + 3] = 0xC0; + + /* Top right */ + pixels[width * 4 - 1] = 0; + pixels[width * 4 - 5] = 0x80; + pixels[width * 4 - 9] = 0xC0; + pixels[rowstride + (width * 4) - 1] = 0x80; + pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0; + + /* Bottom left */ + pixels[(height - 1) * rowstride + 3] = 0; + pixels[(height - 1) * rowstride + 7] = 0x80; + pixels[(height - 1) * rowstride + 11] = 0xC0; + pixels[(height - 2) * rowstride + 3] = 0x80; + pixels[(height - 3) * rowstride + 3] = 0xC0; + + /* Bottom right */ + pixels[height * rowstride - 1] = 0; + pixels[(height - 1) * rowstride - 1] = 0x80; + pixels[(height - 2) * rowstride - 1] = 0xC0; + pixels[height * rowstride - 5] = 0x80; + pixels[height * rowstride - 9] = 0xC0; +} + +static gboolean +empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf) +{ + gint width, height, rowstride, i; + guchar *pixels; + guchar *row; + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + + row = pixels; + for (i = 3; i < rowstride; i+=4) { + if (row[i] < 0xfe) { + return FALSE; + } + } + + for (i = 1; i < height - 1; i++) { + row = pixels + (i*rowstride); + if (row[3] < 0xfe || row[rowstride-1] < 0xfe) { + return FALSE; + } + } + + row = pixels + ((height-1) * rowstride); + for (i = 3; i < rowstride; i+=4) { + if (row[i] < 0xfe) { + return FALSE; + } + } + + return TRUE; +} + +GdkPixbuf * +empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar, + gint width, + gint height) +{ + GdkPixbuf *pixbuf; + GdkPixbufLoader *loader; + struct SizeData data; + GError *error = NULL; + + if (!avatar) { + return NULL; + } + + data.width = width; + data.height = height; + data.preserve_aspect_ratio = TRUE; + + loader = gdk_pixbuf_loader_new (); + + g_signal_connect (loader, "size-prepared", + G_CALLBACK (pixbuf_from_avatar_size_prepared_cb), + &data); + + if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) { + g_warning ("Couldn't write avatar image:%p with " + "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s", + avatar->data, avatar->len, error->message); + g_error_free (error); + return NULL; + } + + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (!gdk_pixbuf_get_has_alpha (pixbuf)) { + GdkPixbuf *rounded_pixbuf; + + rounded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf)); + gdk_pixbuf_copy_area (pixbuf, 0, 0, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + rounded_pixbuf, + 0, 0); + pixbuf = rounded_pixbuf; + } else { + g_object_ref (pixbuf); + } + + if (empathy_gdk_pixbuf_is_opaque (pixbuf)) { + empathy_avatar_pixbuf_roundify (pixbuf); + } + + g_object_unref (loader); + + return pixbuf; +} + +GdkPixbuf * +empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact, + gint width, + gint height) +{ + EmpathyAvatar *avatar; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + avatar = empathy_contact_get_avatar (contact); + + return empathy_pixbuf_from_avatar_scaled (avatar, width, height); +} + +GdkPixbuf * +empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size) +{ + gint width, height; + gdouble factor; + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + if (width > max_size || height > max_size) { + factor = (gdouble) max_size / MAX (width, height); + + width = width * factor; + height = height * factor; + + return gdk_pixbuf_scale_simple (pixbuf, + width, height, + GDK_INTERP_HYPER); + } + + return g_object_ref (pixbuf); +} + +GdkPixbuf * +empathy_pixbuf_from_icon_name (const gchar *icon_name, + GtkIconSize icon_size) +{ + GtkIconTheme *theme; + GdkPixbuf *pixbuf = NULL; + GError *error = NULL; + gint w, h; + gint size = 48; + + if (!icon_name) { + return NULL; + } + + theme = gtk_icon_theme_get_default (); + + if (gtk_icon_size_lookup (icon_size, &w, &h)) { + size = (w + h) / 2; + } + + pixbuf = gtk_icon_theme_load_icon (theme, + icon_name, + size, + 0, + &error); + if (error) { + empathy_debug (DEBUG_DOMAIN, "Error loading icon: %s", error->message); + g_clear_error (&error); + } + + return pixbuf; +} + +/* Stolen from GtkSourceView, hence the weird intendation. Please keep it like + * that to make it easier to apply changes from the original code. + */ +#define GTK_TEXT_UNKNOWN_CHAR 0xFFFC + +/* this function acts like g_utf8_offset_to_pointer() except that if it finds a + * decomposable character it consumes the decomposition length from the given + * offset. So it's useful when the offset was calculated for the normalized + * version of str, but we need a pointer to str itself. */ +static const gchar * +pointer_from_offset_skipping_decomp (const gchar *str, gint offset) +{ + gchar *casefold, *normal; + const gchar *p, *q; + + p = str; + while (offset > 0) + { + q = g_utf8_next_char (p); + casefold = g_utf8_casefold (p, q - p); + normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + offset -= g_utf8_strlen (normal, -1); + g_free (casefold); + g_free (normal); + p = q; + } + return p; +} + +static const gchar * +g_utf8_strcasestr (const gchar *haystack, const gchar *needle) +{ + gsize needle_len; + gsize haystack_len; + const gchar *ret = NULL; + gchar *p; + gchar *casefold; + gchar *caseless_haystack; + gint i; + + g_return_val_if_fail (haystack != NULL, NULL); + g_return_val_if_fail (needle != NULL, NULL); + + casefold = g_utf8_casefold (haystack, -1); + caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + + needle_len = g_utf8_strlen (needle, -1); + haystack_len = g_utf8_strlen (caseless_haystack, -1); + + if (needle_len == 0) + { + ret = (gchar *)haystack; + goto finally_1; + } + + if (haystack_len < needle_len) + { + ret = NULL; + goto finally_1; + } + + p = (gchar*)caseless_haystack; + needle_len = strlen (needle); + i = 0; + + while (*p) + { + if ((strncmp (p, needle, needle_len) == 0)) + { + ret = pointer_from_offset_skipping_decomp (haystack, i); + goto finally_1; + } + + p = g_utf8_next_char (p); + i++; + } + +finally_1: + g_free (caseless_haystack); + + return ret; +} + +static gboolean +g_utf8_caselessnmatch (const char *s1, const char *s2, + gssize n1, gssize n2) +{ + gchar *casefold; + gchar *normalized_s1; + gchar *normalized_s2; + gint len_s1; + gint len_s2; + gboolean ret = FALSE; + + g_return_val_if_fail (s1 != NULL, FALSE); + g_return_val_if_fail (s2 != NULL, FALSE); + g_return_val_if_fail (n1 > 0, FALSE); + g_return_val_if_fail (n2 > 0, FALSE); + + casefold = g_utf8_casefold (s1, n1); + normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + + casefold = g_utf8_casefold (s2, n2); + normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + + len_s1 = strlen (normalized_s1); + len_s2 = strlen (normalized_s2); + + if (len_s1 < len_s2) + goto finally_2; + + ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0); + +finally_2: + g_free (normalized_s1); + g_free (normalized_s2); + + return ret; +} + +static void +forward_chars_with_skipping (GtkTextIter *iter, + gint count, + gboolean skip_invisible, + gboolean skip_nontext, + gboolean skip_decomp) +{ + gint i; + + g_return_if_fail (count >= 0); + + i = count; + + while (i > 0) + { + gboolean ignored = FALSE; + + /* minimal workaround to avoid the infinite loop of bug #168247. + * It doesn't fix the problemjust the symptom... + */ + if (gtk_text_iter_is_end (iter)) + return; + + if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR) + ignored = TRUE; + + if (!ignored && skip_invisible && + /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE) + ignored = TRUE; + + if (!ignored && skip_decomp) + { + /* being UTF8 correct sucks; this accounts for extra + offsets coming from canonical decompositions of + UTF8 characters (e.g. accented characters) which + g_utf8_normalize() performs */ + gchar *normal; + gchar buffer[6]; + gint buffer_len; + + buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer); + normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD); + i -= (g_utf8_strlen (normal, -1) - 1); + g_free (normal); + } + + gtk_text_iter_forward_char (iter); + + if (!ignored) + --i; + } +} + +static gboolean +lines_match (const GtkTextIter *start, + const gchar **lines, + gboolean visible_only, + gboolean slice, + GtkTextIter *match_start, + GtkTextIter *match_end) +{ + GtkTextIter next; + gchar *line_text; + const gchar *found; + gint offset; + + if (*lines == NULL || **lines == '\0') + { + if (match_start) + *match_start = *start; + if (match_end) + *match_end = *start; + return TRUE; + } + + next = *start; + gtk_text_iter_forward_line (&next); + + /* No more text in buffer, but *lines is nonempty */ + if (gtk_text_iter_equal (start, &next)) + return FALSE; + + if (slice) + { + if (visible_only) + line_text = gtk_text_iter_get_visible_slice (start, &next); + else + line_text = gtk_text_iter_get_slice (start, &next); + } + else + { + if (visible_only) + line_text = gtk_text_iter_get_visible_text (start, &next); + else + line_text = gtk_text_iter_get_text (start, &next); + } + + if (match_start) /* if this is the first line we're matching */ + { + found = g_utf8_strcasestr (line_text, *lines); + } + else + { + /* If it's not the first line, we have to match from the + * start of the line. + */ + if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), + strlen (*lines))) + found = line_text; + else + found = NULL; + } + + if (found == NULL) + { + g_free (line_text); + return FALSE; + } + + /* Get offset to start of search string */ + offset = g_utf8_strlen (line_text, found - line_text); + + next = *start; + + /* If match start needs to be returned, set it to the + * start of the search string. + */ + forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); + if (match_start) + { + *match_start = next; + } + + /* Go to end of search string */ + forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); + + g_free (line_text); + + ++lines; + + if (match_end) + *match_end = next; + + /* pass NULL for match_start, since we don't need to find the + * start again. + */ + return lines_match (&next, lines, visible_only, slice, NULL, match_end); +} + +/* strsplit () that retains the delimiter as part of the string. */ +static gchar ** +strbreakup (const char *string, + const char *delimiter, + gint max_tokens) +{ + GSList *string_list = NULL, *slist; + gchar **str_array, *s, *casefold, *new_string; + guint i, n = 1; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != NULL, NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + s = strstr (string, delimiter); + if (s) + { + guint delimiter_len = strlen (delimiter); + + do + { + guint len; + + len = s - string + delimiter_len; + new_string = g_new (gchar, len + 1); + strncpy (new_string, string, len); + new_string[len] = 0; + casefold = g_utf8_casefold (new_string, -1); + g_free (new_string); + new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + string_list = g_slist_prepend (string_list, new_string); + n++; + string = s + delimiter_len; + s = strstr (string, delimiter); + } while (--max_tokens && s); + } + + if (*string) + { + n++; + casefold = g_utf8_casefold (string, -1); + new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + string_list = g_slist_prepend (string_list, new_string); + } + + str_array = g_new (gchar*, n); + + i = n - 1; + + str_array[i--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[i--] = slist->data; + + g_slist_free (string_list); + + return str_array; +} + +gboolean +empathy_text_iter_forward_search (const GtkTextIter *iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *limit) +{ + gchar **lines = NULL; + GtkTextIter match; + gboolean retval = FALSE; + GtkTextIter search; + gboolean visible_only; + gboolean slice; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + if (limit && gtk_text_iter_compare (iter, limit) >= 0) + return FALSE; + + if (*str == '\0') { + /* If we can move one char, return the empty string there */ + match = *iter; + + if (gtk_text_iter_forward_char (&match)) { + if (limit && gtk_text_iter_equal (&match, limit)) { + return FALSE; + } + + if (match_start) { + *match_start = match; + } + if (match_end) { + *match_end = match; + } + return TRUE; + } else { + return FALSE; + } + } + + visible_only = TRUE; + slice = FALSE; + + /* locate all lines */ + lines = strbreakup (str, "\n", -1); + + search = *iter; + + do { + /* This loop has an inefficient worst-case, where + * gtk_text_iter_get_text () is called repeatedly on + * a single line. + */ + GtkTextIter end; + + if (limit && gtk_text_iter_compare (&search, limit) >= 0) { + break; + } + + if (lines_match (&search, (const gchar**)lines, + visible_only, slice, &match, &end)) { + if (limit == NULL || + (limit && gtk_text_iter_compare (&end, limit) <= 0)) { + retval = TRUE; + + if (match_start) { + *match_start = match; + } + if (match_end) { + *match_end = end; + } + } + break; + } + } while (gtk_text_iter_forward_line (&search)); + + g_strfreev ((gchar**)lines); + + return retval; +} + +static const gchar * +g_utf8_strrcasestr (const gchar *haystack, const gchar *needle) +{ + gsize needle_len; + gsize haystack_len; + const gchar *ret = NULL; + gchar *p; + gchar *casefold; + gchar *caseless_haystack; + gint i; + + g_return_val_if_fail (haystack != NULL, NULL); + g_return_val_if_fail (needle != NULL, NULL); + + casefold = g_utf8_casefold (haystack, -1); + caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD); + g_free (casefold); + + needle_len = g_utf8_strlen (needle, -1); + haystack_len = g_utf8_strlen (caseless_haystack, -1); + + if (needle_len == 0) + { + ret = (gchar *)haystack; + goto finally_1; + } + + if (haystack_len < needle_len) + { + ret = NULL; + goto finally_1; + } + + i = haystack_len - needle_len; + p = g_utf8_offset_to_pointer (caseless_haystack, i); + needle_len = strlen (needle); + + while (p >= caseless_haystack) + { + if (strncmp (p, needle, needle_len) == 0) + { + ret = pointer_from_offset_skipping_decomp (haystack, i); + goto finally_1; + } + + p = g_utf8_prev_char (p); + i--; + } + +finally_1: + g_free (caseless_haystack); + + return ret; +} + +static gboolean +backward_lines_match (const GtkTextIter *start, + const gchar **lines, + gboolean visible_only, + gboolean slice, + GtkTextIter *match_start, + GtkTextIter *match_end) +{ + GtkTextIter line, next; + gchar *line_text; + const gchar *found; + gint offset; + + if (*lines == NULL || **lines == '\0') + { + if (match_start) + *match_start = *start; + if (match_end) + *match_end = *start; + return TRUE; + } + + line = next = *start; + if (gtk_text_iter_get_line_offset (&next) == 0) + { + if (!gtk_text_iter_backward_line (&next)) + return FALSE; + } + else + gtk_text_iter_set_line_offset (&next, 0); + + if (slice) + { + if (visible_only) + line_text = gtk_text_iter_get_visible_slice (&next, &line); + else + line_text = gtk_text_iter_get_slice (&next, &line); + } + else + { + if (visible_only) + line_text = gtk_text_iter_get_visible_text (&next, &line); + else + line_text = gtk_text_iter_get_text (&next, &line); + } + + if (match_start) /* if this is the first line we're matching */ + { + found = g_utf8_strrcasestr (line_text, *lines); + } + else + { + /* If it's not the first line, we have to match from the + * start of the line. + */ + if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text), + strlen (*lines))) + found = line_text; + else + found = NULL; + } + + if (found == NULL) + { + g_free (line_text); + return FALSE; + } + + /* Get offset to start of search string */ + offset = g_utf8_strlen (line_text, found - line_text); + + forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE); + + /* If match start needs to be returned, set it to the + * start of the search string. + */ + if (match_start) + { + *match_start = next; + } + + /* Go to end of search string */ + forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE); + + g_free (line_text); + + ++lines; + + if (match_end) + *match_end = next; + + /* try to match the rest of the lines forward, passing NULL + * for match_start so lines_match will try to match the entire + * line */ + return lines_match (&next, lines, visible_only, + slice, NULL, match_end); +} + +gboolean +empathy_text_iter_backward_search (const GtkTextIter *iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter *limit) +{ + gchar **lines = NULL; + GtkTextIter match; + gboolean retval = FALSE; + GtkTextIter search; + gboolean visible_only; + gboolean slice; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + if (limit && gtk_text_iter_compare (iter, limit) <= 0) + return FALSE; + + if (*str == '\0') + { + /* If we can move one char, return the empty string there */ + match = *iter; + + if (gtk_text_iter_backward_char (&match)) + { + if (limit && gtk_text_iter_equal (&match, limit)) + return FALSE; + + if (match_start) + *match_start = match; + if (match_end) + *match_end = match; + return TRUE; + } + else + { + return FALSE; + } + } + + visible_only = TRUE; + slice = TRUE; + + /* locate all lines */ + lines = strbreakup (str, "\n", -1); + + search = *iter; + + while (TRUE) + { + /* This loop has an inefficient worst-case, where + * gtk_text_iter_get_text () is called repeatedly on + * a single line. + */ + GtkTextIter end; + + if (limit && gtk_text_iter_compare (&search, limit) <= 0) + break; + + if (backward_lines_match (&search, (const gchar**)lines, + visible_only, slice, &match, &end)) + { + if (limit == NULL || (limit && + gtk_text_iter_compare (&end, limit) > 0)) + { + retval = TRUE; + + if (match_start) + *match_start = match; + if (match_end) + *match_end = end; + } + break; + } + + if (gtk_text_iter_get_line_offset (&search) == 0) + { + if (!gtk_text_iter_backward_line (&search)) + break; + } + else + { + gtk_text_iter_set_line_offset (&search, 0); + } + } + + g_strfreev ((gchar**)lines); + + return retval; +} + +gboolean +empathy_window_get_is_visible (GtkWindow *window) +{ + GdkWindowState state; + GdkWindow *gdk_window; + + g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); + + gdk_window = GTK_WIDGET (window)->window; + if (!gdk_window) { + return FALSE; + } + + state = gdk_window_get_state (gdk_window); + if (state & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED)) { + return FALSE; + } + + return TRUE; +} + +void +empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon) +{ + GdkRectangle icon_location; + gulong data[4]; + Display *dpy; + GdkWindow *gdk_window; + + gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL); + gdk_window = GTK_WIDGET (window)->window; + dpy = gdk_x11_drawable_get_xdisplay (gdk_window); + + data[0] = icon_location.x; + data[1] = icon_location.y; + data[2] = icon_location.width; + data[3] = icon_location.height; + + XChangeProperty (dpy, + GDK_WINDOW_XID (gdk_window), + gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window), + "_NET_WM_ICON_GEOMETRY"), + XA_CARDINAL, 32, PropModeReplace, + (guchar *)&data, 4); + + gtk_window_set_skip_taskbar_hint (window, TRUE); + gtk_window_iconify (window); +} + +/* Takes care of moving the window to the current workspace. */ +void +empathy_window_present (GtkWindow *window, + gboolean steal_focus) +{ + guint32 timestamp; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + /* There are three cases: hidden, visible, visible on another + * workspace. + */ + + if (!empathy_window_get_is_visible (window)) { + /* Hide it so present brings it to the current workspace. */ + gtk_widget_hide (GTK_WIDGET (window)); + } + + timestamp = gtk_get_current_event_time (); + gtk_window_set_skip_taskbar_hint (window, FALSE); + gtk_window_present_with_time (window, timestamp); + /* FIXME: This shouldn't be required as gtk_window_present's doc says + * it deiconify automatically. */ + gtk_window_deiconify (window); +} + +GtkWindow * +empathy_get_toplevel_window (GtkWidget *widget) +{ + GtkWidget *toplevel; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + toplevel = gtk_widget_get_toplevel (widget); + if (GTK_IS_WINDOW (toplevel) && + GTK_WIDGET_TOPLEVEL (toplevel)) { + return GTK_WINDOW (toplevel); + } + + return NULL; +} + +/* The URL opening code can't handle schemeless strings, so we try to be + * smart and add http if there is no scheme or doesn't look like a mail + * address. This should work in most cases, and let us click on strings + * like "www.gnome.org". + */ +static gchar * +fixup_url (const gchar *url) +{ + gchar *real_url; + + if (!g_str_has_prefix (url, "http://") && + !strstr (url, ":/") && + !strstr (url, "@")) { + real_url = g_strdup_printf ("http://%s", url); + } else { + real_url = g_strdup (url); + } + + return real_url; +} + +void +empathy_url_show (const char *url) +{ + gchar *real_url; + GnomeVFSResult res; + + real_url = fixup_url (url); + res = gnome_vfs_url_show (real_url); + if (res != GNOME_VFS_OK) { + empathy_debug (DEBUG_DOMAIN, "Couldn't show URL %s: %s", + real_url, + gnome_vfs_result_to_string (res)); + } + + g_free (real_url); +} + +static void +link_button_hook (GtkLinkButton *button, + const gchar *link, + gpointer user_data) +{ + empathy_url_show (link); +} + +GtkWidget * +empathy_link_button_new (const gchar *url, + const gchar *title) +{ + static gboolean hook = FALSE; + + if (!hook) { + hook = TRUE; + gtk_link_button_set_uri_hook (link_button_hook, NULL, NULL); + } + + return gtk_link_button_new_with_label (url, title); +} + +void +empathy_toggle_button_set_state_quietly (GtkWidget *widget, + GCallback callback, + gpointer user_data, + gboolean active) +{ + g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget)); + + g_signal_handlers_block_by_func (widget, callback, user_data); + g_object_set (widget, "active", active, NULL); + g_signal_handlers_unblock_by_func (widget, callback, user_data); +} + +GtkTextTag * +empathy_text_buffer_tag_set (GtkTextBuffer *buffer, + const gchar *tag_name, + const gchar *first_property_name, + ...) +{ + GtkTextTagTable *table; + GtkTextTag *tag; + + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL); + g_return_val_if_fail (tag_name != NULL, NULL); + + table = gtk_text_buffer_get_tag_table (buffer); + tag = gtk_text_tag_table_lookup (table, tag_name); + + if (!tag) { + tag = gtk_text_tag_new (tag_name); + gtk_text_tag_table_add (table, tag); + g_object_unref (tag); + } else { + /* Clear the old values so that we don't affect the new theme. */ + g_object_set (tag, + "background-set", FALSE, + "foreground-set", FALSE, + "invisible-set", FALSE, + "justification-set", FALSE, + "paragraph-background-set", FALSE, + "pixels-above-lines-set", FALSE, + "pixels-below-lines-set", FALSE, + "rise-set", FALSE, + "scale-set", FALSE, + "size-set", FALSE, + "style-set", FALSE, + "weight-set", FALSE, + NULL); + } + + if (first_property_name) { + va_list list; + + va_start (list, first_property_name); + g_object_set_valist (G_OBJECT (tag), first_property_name, list); + va_end (list); + } + + return tag; +} + diff --git a/gnome-2-22/libempathy-gtk/empathy-ui-utils.h b/gnome-2-22/libempathy-gtk/empathy-ui-utils.h new file mode 100644 index 000000000..1fb29a97d --- /dev/null +++ b/gnome-2-22/libempathy-gtk/empathy-ui-utils.h @@ -0,0 +1,116 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002-2007 Imendio AB + * Copyright (C) 2007 Collabora Ltd. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Mikael Hallendal <micke@imendio.com> + * Richard Hult <richard@imendio.com> + * Martyn Russell <martyn@imendio.com> + * Xavier Claessens <xclaesse@gmail.com> + * + * Part of this file is copied from GtkSourceView (gtksourceiter.c): + * Paolo Maggi + * Jeroen Zwartepoorte + */ + +#ifndef __EMPATHY_UI_UTILS_H__ +#define __EMPATHY_UI_UTILS_H__ + +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include <libmissioncontrol/mc-account.h> +#include <libmissioncontrol/mc-profile.h> + +#include <libempathy/empathy-contact.h> +#include <libempathy/empathy-avatar.h> + +#include "empathy-chat-view.h" + +G_BEGIN_DECLS + +#define G_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0') + +/* Glade */ +void empathy_glade_get_file_simple (const gchar *filename, + const gchar *root, + const gchar *domain, + const gchar *first_required_widget, + ...); +GladeXML * empathy_glade_get_file (const gchar *filename, + const gchar *root, + const gchar *domain, + const gchar *first_required_widget, + ...); +void empathy_glade_connect (GladeXML *gui, + gpointer user_data, + gchar *first_widget, + ...); +void empathy_glade_setup_size_group (GladeXML *gui, + GtkSizeGroupMode mode, + gchar *first_widget, + ...); +/* Pixbufs */ +const gchar * empathy_icon_name_from_account (McAccount *account); +const gchar * empathy_icon_name_for_presence (McPresence presence); +const gchar * empathy_icon_name_for_contact (EmpathyContact *contact); +GdkPixbuf * empathy_pixbuf_from_data (gchar *data, + gsize data_size); +GdkPixbuf * empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar, + gint width, + gint height); +GdkPixbuf * empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact, + gint width, + gint height); +GdkPixbuf * empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, + gint max_size); +GdkPixbuf * empathy_pixbuf_from_icon_name (const gchar *icon_name, + GtkIconSize icon_size); +/* Text view */ +gboolean empathy_text_iter_forward_search (const GtkTextIter*iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter*limit); +gboolean empathy_text_iter_backward_search (const GtkTextIter*iter, + const gchar *str, + GtkTextIter *match_start, + GtkTextIter *match_end, + const GtkTextIter*limit); +/* Windows */ +gboolean empathy_window_get_is_visible (GtkWindow *window); +void empathy_window_present (GtkWindow *window, + gboolean steal_focus); +void empathy_window_iconify (GtkWindow *window, + GtkStatusIcon *status_icon); +GtkWindow * empathy_get_toplevel_window (GtkWidget *widget); +void empathy_url_show (const char *url); +void empathy_toggle_button_set_state_quietly (GtkWidget *widget, + GCallback callback, + gpointer user_data, + gboolean active); +GtkWidget * empathy_link_button_new (const gchar *url, + const gchar *title); +GtkTextTag *empathy_text_buffer_tag_set (GtkTextBuffer *buffer, + const gchar *tag_name, + const gchar *first_property_name, + ...); + +G_END_DECLS + +#endif /* __EMPATHY_UI_UTILS_H__ */ diff --git a/gnome-2-22/libempathy-gtk/ephy-spinner.c b/gnome-2-22/libempathy-gtk/ephy-spinner.c new file mode 100644 index 000000000..a8f371df3 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/ephy-spinner.c @@ -0,0 +1,977 @@ +/* + * Copyright © 2000 Eazel, Inc. + * Copyright © 2002-2004 Marco Pesenti Gritti + * Copyright © 2004, 2006 Christian Persch + * + * Nautilus 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. + * + * Nautilus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Andy Hertzfeld <andy@eazel.com> + * + * Ephy port by Marco Pesenti Gritti <marco@it.gnome.org> + * + * $Id: ephy-spinner.c 2114 2006-12-25 12:15:00Z mr $ + */ + +#include "config.h" + +#include "ephy-spinner.h" + +/* #include "ephy-debug.h" */ +#define LOG(msg, args...) +#define START_PROFILER(name) +#define STOP_PROFILER(name) + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtkicontheme.h> +#include <gtk/gtkiconfactory.h> +#include <gtk/gtksettings.h> + +/* Spinner cache implementation */ + +#define EPHY_TYPE_SPINNER_CACHE (ephy_spinner_cache_get_type()) +#define EPHY_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCache)) +#define EPHY_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass)) +#define EPHY_IS_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EPHY_TYPE_SPINNER_CACHE)) +#define EPHY_IS_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EPHY_TYPE_SPINNER_CACHE)) +#define EPHY_SPINNER_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass)) + +typedef struct _EphySpinnerCache EphySpinnerCache; +typedef struct _EphySpinnerCacheClass EphySpinnerCacheClass; +typedef struct _EphySpinnerCachePrivate EphySpinnerCachePrivate; + +struct _EphySpinnerCacheClass +{ + GObjectClass parent_class; +}; + +struct _EphySpinnerCache +{ + GObject parent_object; + + /*< private >*/ + EphySpinnerCachePrivate *priv; +}; + +#define EPHY_SPINNER_CACHE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCachePrivate)) + +struct _EphySpinnerCachePrivate +{ + /* Hash table of GdkScreen -> EphySpinnerCacheData */ + GHashTable *hash; +}; + +typedef struct +{ + guint ref_count; + GtkIconSize size; + int width; + int height; + GdkPixbuf **animation_pixbufs; + guint n_animation_pixbufs; +} EphySpinnerImages; + +#define LAST_ICON_SIZE GTK_ICON_SIZE_DIALOG + 1 +#define SPINNER_ICON_NAME "process-working" +#define SPINNER_FALLBACK_ICON_NAME "gnome-spinner" +#define EPHY_SPINNER_IMAGES_INVALID ((EphySpinnerImages *) 0x1) + +typedef struct +{ + GdkScreen *screen; + GtkIconTheme *icon_theme; + EphySpinnerImages *images[LAST_ICON_SIZE]; +} EphySpinnerCacheData; + +static void ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass); +static void ephy_spinner_cache_init (EphySpinnerCache *cache); + +static GObjectClass *ephy_spinner_cache_parent_class; + +static GType +ephy_spinner_cache_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + const GTypeInfo our_info = + { + sizeof (EphySpinnerCacheClass), + NULL, + NULL, + (GClassInitFunc) ephy_spinner_cache_class_init, + NULL, + NULL, + sizeof (EphySpinnerCache), + 0, + (GInstanceInitFunc) ephy_spinner_cache_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "EphySpinnerCache", + &our_info, 0); + } + + return type; +} + +static EphySpinnerImages * +ephy_spinner_images_ref (EphySpinnerImages *images) +{ + g_return_val_if_fail (images != NULL, NULL); + + images->ref_count++; + + return images; +} + +static void +ephy_spinner_images_unref (EphySpinnerImages *images) +{ + g_return_if_fail (images != NULL); + + images->ref_count--; + if (images->ref_count == 0) + { + guint i; + + LOG ("Freeing spinner images %p for size %d", images, images->size); + + for (i = 0; i < images->n_animation_pixbufs; ++i) + { + g_object_unref (images->animation_pixbufs[i]); + } + g_free (images->animation_pixbufs); + + g_free (images); + } +} + +static void +ephy_spinner_cache_data_unload (EphySpinnerCacheData *data) +{ + GtkIconSize size; + EphySpinnerImages *images; + + g_return_if_fail (data != NULL); + + LOG ("EphySpinnerDataCache unload for screen %p", data->screen); + + for (size = GTK_ICON_SIZE_INVALID; size < LAST_ICON_SIZE; ++size) + { + images = data->images[size]; + data->images[size] = NULL; + + if (images != NULL && images != EPHY_SPINNER_IMAGES_INVALID) + { + ephy_spinner_images_unref (images); + } + } +} + +static GdkPixbuf * +extract_frame (GdkPixbuf *grid_pixbuf, + int x, + int y, + int size) +{ + GdkPixbuf *pixbuf; + + if (x + size > gdk_pixbuf_get_width (grid_pixbuf) || + y + size > gdk_pixbuf_get_height (grid_pixbuf)) + { + return NULL; + } + + pixbuf = gdk_pixbuf_new_subpixbuf (grid_pixbuf, + x, y, + size, size); + g_return_val_if_fail (pixbuf != NULL, NULL); + + return pixbuf; +} + +static GdkPixbuf * +scale_to_size (GdkPixbuf *pixbuf, + int dw, + int dh) +{ + GdkPixbuf *result; + int pw, ph; + + g_return_val_if_fail (pixbuf != NULL, NULL); + + pw = gdk_pixbuf_get_width (pixbuf); + ph = gdk_pixbuf_get_height (pixbuf); + + if (pw != dw || ph != dh) + { + result = gdk_pixbuf_scale_simple (pixbuf, dw, dh, + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + return result; + } + + return pixbuf; +} + +static EphySpinnerImages * +ephy_spinner_images_load (GdkScreen *screen, + GtkIconTheme *icon_theme, + GtkIconSize icon_size) +{ + EphySpinnerImages *images; + GdkPixbuf *icon_pixbuf, *pixbuf; + GtkIconInfo *icon_info = NULL; + int grid_width, grid_height, x, y, requested_size, size, isw, ish, n; + const char *icon; + GSList *list = NULL, *l; + + LOG ("EphySpinnerCacheData loading for screen %p at size %d", screen, icon_size); + + START_PROFILER ("loading spinner animation") + + if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_for_screen (screen), + icon_size, &isw, &ish)) goto loser; + + requested_size = MAX (ish, isw); + + /* Load the animation. The 'rest icon' is the 0th frame */ + icon_info = gtk_icon_theme_lookup_icon (icon_theme, + SPINNER_ICON_NAME, + requested_size, 0); + if (icon_info == NULL) + { + g_warning ("Throbber animation not found"); + + /* If the icon naming spec compliant name wasn't found, try the old name */ + icon_info = gtk_icon_theme_lookup_icon (icon_theme, + SPINNER_FALLBACK_ICON_NAME, + requested_size, 0); + if (icon_info == NULL) + { + g_warning ("Throbber fallback animation not found either"); + goto loser; + } + } + g_assert (icon_info != NULL); + + size = gtk_icon_info_get_base_size (icon_info); + icon = gtk_icon_info_get_filename (icon_info); + if (icon == NULL) goto loser; + + icon_pixbuf = gdk_pixbuf_new_from_file (icon, NULL); + gtk_icon_info_free (icon_info); + icon_info = NULL; + + if (icon_pixbuf == NULL) + { + g_warning ("Could not load the spinner file"); + goto loser; + } + + grid_width = gdk_pixbuf_get_width (icon_pixbuf); + grid_height = gdk_pixbuf_get_height (icon_pixbuf); + + n = 0; + for (y = 0; y < grid_height; y += size) + { + for (x = 0; x < grid_width ; x += size) + { + pixbuf = extract_frame (icon_pixbuf, x, y, size); + + if (pixbuf) + { + list = g_slist_prepend (list, pixbuf); + ++n; + } + else + { + g_warning ("Cannot extract frame (%d, %d) from the grid\n", x, y); + } + } + } + + g_object_unref (icon_pixbuf); + + if (list == NULL) goto loser; + g_assert (n > 0); + + if (size > requested_size) + { + for (l = list; l != NULL; l = l->next) + { + l->data = scale_to_size (l->data, isw, ish); + } + } + + /* Now we've successfully got all the data */ + images = g_new (EphySpinnerImages, 1); + images->ref_count = 1; + + images->size = icon_size; + images->width = images->height = requested_size; + + images->n_animation_pixbufs = n; + images->animation_pixbufs = g_new (GdkPixbuf *, n); + + for (l = list; l != NULL; l = l->next) + { + g_assert (l->data != NULL); + images->animation_pixbufs[--n] = l->data; + } + g_assert (n == 0); + + g_slist_free (list); + + STOP_PROFILER ("loading spinner animation") + + return images; + +loser: + if (icon_info) + { + gtk_icon_info_free (icon_info); + } + g_slist_foreach (list, (GFunc) g_object_unref, NULL); + + STOP_PROFILER ("loading spinner animation") + + return NULL; +} + +static EphySpinnerCacheData * +ephy_spinner_cache_data_new (GdkScreen *screen) +{ + EphySpinnerCacheData *data; + + data = g_new0 (EphySpinnerCacheData, 1); + + data->screen = screen; + data->icon_theme = gtk_icon_theme_get_for_screen (screen); + g_signal_connect_swapped (data->icon_theme, "changed", + G_CALLBACK (ephy_spinner_cache_data_unload), + data); + + return data; +} + +static void +ephy_spinner_cache_data_free (EphySpinnerCacheData *data) +{ + g_return_if_fail (data != NULL); + g_return_if_fail (data->icon_theme != NULL); + + g_signal_handlers_disconnect_by_func + (data->icon_theme, + G_CALLBACK (ephy_spinner_cache_data_unload), data); + + ephy_spinner_cache_data_unload (data); + + g_free (data); +} + +static EphySpinnerImages * +ephy_spinner_cache_get_images (EphySpinnerCache *cache, + GdkScreen *screen, + GtkIconSize icon_size) +{ + EphySpinnerCachePrivate *priv = cache->priv; + EphySpinnerCacheData *data; + EphySpinnerImages *images; + + LOG ("Getting animation images for screen %p at size %d", screen, icon_size); + + g_return_val_if_fail (icon_size >= 0 && icon_size < LAST_ICON_SIZE, NULL); + + /* Backward compat: "invalid" meant "native" size which doesn't exist anymore */ + if (icon_size == GTK_ICON_SIZE_INVALID) + { + icon_size = GTK_ICON_SIZE_DIALOG; + } + + data = g_hash_table_lookup (priv->hash, screen); + if (data == NULL) + { + data = ephy_spinner_cache_data_new (screen); + /* FIXME: think about what happens when the screen's display is closed later on */ + g_hash_table_insert (priv->hash, screen, data); + } + + images = data->images[icon_size]; + if (images == EPHY_SPINNER_IMAGES_INVALID) + { + /* Load failed, but don't try endlessly again! */ + return NULL; + } + + if (images != NULL) + { + /* Return cached data */ + return ephy_spinner_images_ref (images); + } + + images = ephy_spinner_images_load (screen, data->icon_theme, icon_size); + + if (images == NULL) + { + /* Mark as failed-to-load */ + data->images[icon_size] = EPHY_SPINNER_IMAGES_INVALID; + + return NULL; + } + + data->images[icon_size] = images; + + return ephy_spinner_images_ref (images); +} + +static void +ephy_spinner_cache_init (EphySpinnerCache *cache) +{ + EphySpinnerCachePrivate *priv; + + priv = cache->priv = EPHY_SPINNER_CACHE_GET_PRIVATE (cache); + + LOG ("EphySpinnerCache initialising"); + + priv->hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify) ephy_spinner_cache_data_free); +} + +static void +ephy_spinner_cache_finalize (GObject *object) +{ + EphySpinnerCache *cache = EPHY_SPINNER_CACHE (object); + EphySpinnerCachePrivate *priv = cache->priv; + + g_hash_table_destroy (priv->hash); + + LOG ("EphySpinnerCache finalised"); + + G_OBJECT_CLASS (ephy_spinner_cache_parent_class)->finalize (object); +} + +static void +ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + ephy_spinner_cache_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = ephy_spinner_cache_finalize; + + g_type_class_add_private (object_class, sizeof (EphySpinnerCachePrivate)); +} + +static EphySpinnerCache *spinner_cache = NULL; + +static EphySpinnerCache * +ephy_spinner_cache_ref (void) +{ + if (spinner_cache == NULL) + { + EphySpinnerCache **cache_ptr; + + spinner_cache = g_object_new (EPHY_TYPE_SPINNER_CACHE, NULL); + cache_ptr = &spinner_cache; + g_object_add_weak_pointer (G_OBJECT (spinner_cache), + (gpointer *) cache_ptr); + + return spinner_cache; + } + + return g_object_ref (spinner_cache); +} + +/* Spinner implementation */ + +#define SPINNER_TIMEOUT 125 /* ms */ + +#define EPHY_SPINNER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER, EphySpinnerDetails)) + +struct _EphySpinnerDetails +{ + GtkIconTheme *icon_theme; + EphySpinnerCache *cache; + GtkIconSize size; + EphySpinnerImages *images; + guint current_image; + guint timeout; + guint timer_task; + guint spinning : 1; + guint need_load : 1; +}; + +static void ephy_spinner_class_init (EphySpinnerClass *class); +static void ephy_spinner_init (EphySpinner *spinner); + +static GObjectClass *parent_class; + +GType +ephy_spinner_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + const GTypeInfo our_info = + { + sizeof (EphySpinnerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) ephy_spinner_class_init, + NULL, + NULL, /* class_data */ + sizeof (EphySpinner), + 0, /* n_preallocs */ + (GInstanceInitFunc) ephy_spinner_init + }; + + type = g_type_register_static (GTK_TYPE_WIDGET, + "EphySpinner", + &our_info, 0); + } + + return type; +} + +static gboolean +ephy_spinner_load_images (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + if (details->need_load) + { + START_PROFILER ("ephy_spinner_load_images") + + details->images = + ephy_spinner_cache_get_images + (details->cache, + gtk_widget_get_screen (GTK_WIDGET (spinner)), + details->size); + + STOP_PROFILER ("ephy_spinner_load_images") + + details->current_image = 0; /* 'rest' icon */ + details->need_load = FALSE; + } + + return details->images != NULL; +} + +static void +ephy_spinner_unload_images (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + if (details->images != NULL) + { + ephy_spinner_images_unref (details->images); + details->images = NULL; + } + + details->current_image = 0; + details->need_load = TRUE; +} + +static void +icon_theme_changed_cb (GtkIconTheme *icon_theme, + EphySpinner *spinner) +{ + ephy_spinner_unload_images (spinner); + gtk_widget_queue_resize (GTK_WIDGET (spinner)); +} + +static void +ephy_spinner_init (EphySpinner *spinner) +{ + EphySpinnerDetails *details; + + details = spinner->details = EPHY_SPINNER_GET_PRIVATE (spinner); + + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW); + + details->cache = ephy_spinner_cache_ref (); + details->size = GTK_ICON_SIZE_DIALOG; + details->spinning = FALSE; + details->timeout = SPINNER_TIMEOUT; + details->need_load = TRUE; +} + +static int +ephy_spinner_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + EphySpinner *spinner = EPHY_SPINNER (widget); + EphySpinnerDetails *details = spinner->details; + EphySpinnerImages *images; + GdkPixbuf *pixbuf; + GdkGC *gc; + int x_offset, y_offset, width, height; + GdkRectangle pix_area, dest; + + if (!GTK_WIDGET_DRAWABLE (spinner)) + { + return FALSE; + } + + if (details->need_load && + !ephy_spinner_load_images (spinner)) + { + return FALSE; + } + + images = details->images; + if (images == NULL) + { + return FALSE; + } + + /* Otherwise |images| will be NULL anyway */ + g_assert (images->n_animation_pixbufs > 0); + + g_assert (details->current_image >= 0 && + details->current_image < images->n_animation_pixbufs); + + pixbuf = images->animation_pixbufs[details->current_image]; + + g_assert (pixbuf != NULL); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + /* Compute the offsets for the image centered on our allocation */ + x_offset = (widget->allocation.width - width) / 2; + y_offset = (widget->allocation.height - height) / 2; + + pix_area.x = x_offset + widget->allocation.x; + pix_area.y = y_offset + widget->allocation.y; + pix_area.width = width; + pix_area.height = height; + + if (!gdk_rectangle_intersect (&event->area, &pix_area, &dest)) + { + return FALSE; + } + + gc = gdk_gc_new (widget->window); + gdk_draw_pixbuf (widget->window, gc, pixbuf, + dest.x - x_offset - widget->allocation.x, + dest.y - y_offset - widget->allocation.y, + dest.x, dest.y, + dest.width, dest.height, + GDK_RGB_DITHER_MAX, 0, 0); + g_object_unref (gc); + + return FALSE; +} + +static gboolean +bump_spinner_frame_cb (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + /* This can happen when we've unloaded the images on a theme + * change, but haven't been in the queued size request yet. + * Just skip this update. + */ + if (details->images == NULL) return TRUE; + + details->current_image++; + if (details->current_image >= details->images->n_animation_pixbufs) + { + /* the 0th frame is the 'rest' icon */ + details->current_image = MIN (1, details->images->n_animation_pixbufs); + } + + gtk_widget_queue_draw (GTK_WIDGET (spinner)); + + /* run again */ + return TRUE; +} + +/** + * ephy_spinner_start: + * @spinner: a #EphySpinner + * + * Start the spinner animation. + **/ +void +ephy_spinner_start (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + details->spinning = TRUE; + + if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)) && + details->timer_task == 0 && + ephy_spinner_load_images (spinner)) + { + /* the 0th frame is the 'rest' icon */ + details->current_image = MIN (1, details->images->n_animation_pixbufs); + + details->timer_task = + g_timeout_add_full (G_PRIORITY_LOW, + details->timeout, + (GSourceFunc) bump_spinner_frame_cb, + spinner, + NULL); + } +} + +static void +ephy_spinner_remove_update_callback (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + if (details->timer_task != 0) + { + g_source_remove (details->timer_task); + details->timer_task = 0; + } +} + +/** + * ephy_spinner_stop: + * @spinner: a #EphySpinner + * + * Stop the spinner animation. + **/ +void +ephy_spinner_stop (EphySpinner *spinner) +{ + EphySpinnerDetails *details = spinner->details; + + details->spinning = FALSE; + details->current_image = 0; + + if (details->timer_task != 0) + { + ephy_spinner_remove_update_callback (spinner); + + if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner))) + { + gtk_widget_queue_draw (GTK_WIDGET (spinner)); + } + } +} + +/* + * ephy_spinner_set_size: + * @spinner: a #EphySpinner + * @size: the size of type %GtkIconSize + * + * Set the size of the spinner. + **/ +void +ephy_spinner_set_size (EphySpinner *spinner, + GtkIconSize size) +{ + if (size == GTK_ICON_SIZE_INVALID) + { + size = GTK_ICON_SIZE_DIALOG; + } + + if (size != spinner->details->size) + { + ephy_spinner_unload_images (spinner); + + spinner->details->size = size; + + gtk_widget_queue_resize (GTK_WIDGET (spinner)); + } +} + +#if 0 +/* + * ephy_spinner_set_timeout: + * @spinner: a #EphySpinner + * @timeout: time delay between updates to the spinner. + * + * Sets the timeout delay for spinner updates. + **/ +void +ephy_spinner_set_timeout (EphySpinner *spinner, + guint timeout) +{ + EphySpinnerDetails *details = spinner->details; + + if (timeout != details->timeout) + { + ephy_spinner_stop (spinner); + + details->timeout = timeout; + + if (details->spinning) + { + ephy_spinner_start (spinner); + } + } +} +#endif + +static void +ephy_spinner_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + EphySpinner *spinner = EPHY_SPINNER (widget); + EphySpinnerDetails *details = spinner->details; + + if ((details->need_load && + !ephy_spinner_load_images (spinner)) || + details->images == NULL) + { + requisition->width = requisition->height = 0; + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget), + details->size, + &requisition->width, + &requisition->height); + return; + } + + requisition->width = details->images->width; + requisition->height = details->images->height; + + /* FIXME fix this hack */ + /* allocate some extra margin so we don't butt up against toolbar edges */ + if (details->size != GTK_ICON_SIZE_MENU) + { + requisition->width += 2; + requisition->height += 2; + } +} + +static void +ephy_spinner_map (GtkWidget *widget) +{ + EphySpinner *spinner = EPHY_SPINNER (widget); + EphySpinnerDetails *details = spinner->details; + + GTK_WIDGET_CLASS (parent_class)->map (widget); + + if (details->spinning) + { + ephy_spinner_start (spinner); + } +} + +static void +ephy_spinner_unmap (GtkWidget *widget) +{ + EphySpinner *spinner = EPHY_SPINNER (widget); + + ephy_spinner_remove_update_callback (spinner); + + GTK_WIDGET_CLASS (parent_class)->unmap (widget); +} + +static void +ephy_spinner_dispose (GObject *object) +{ + EphySpinner *spinner = EPHY_SPINNER (object); + + g_signal_handlers_disconnect_by_func + (spinner->details->icon_theme, + G_CALLBACK (icon_theme_changed_cb), spinner); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +ephy_spinner_finalize (GObject *object) +{ + EphySpinner *spinner = EPHY_SPINNER (object); + + ephy_spinner_remove_update_callback (spinner); + ephy_spinner_unload_images (spinner); + + g_object_unref (spinner->details->cache); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +ephy_spinner_screen_changed (GtkWidget *widget, + GdkScreen *old_screen) +{ + EphySpinner *spinner = EPHY_SPINNER (widget); + EphySpinnerDetails *details = spinner->details; + GdkScreen *screen; + + if (GTK_WIDGET_CLASS (parent_class)->screen_changed) + { + GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, old_screen); + } + + screen = gtk_widget_get_screen (widget); + + /* FIXME: this seems to be happening when then spinner is destroyed!? */ + if (old_screen == screen) return; + + /* We'll get mapped again on the new screen, but not unmapped from + * the old screen, so remove timeout here. + */ + ephy_spinner_remove_update_callback (spinner); + + ephy_spinner_unload_images (spinner); + + if (old_screen != NULL) + { + g_signal_handlers_disconnect_by_func + (gtk_icon_theme_get_for_screen (old_screen), + G_CALLBACK (icon_theme_changed_cb), spinner); + } + + details->icon_theme = gtk_icon_theme_get_for_screen (screen); + g_signal_connect (details->icon_theme, "changed", + G_CALLBACK (icon_theme_changed_cb), spinner); +} + +static void +ephy_spinner_class_init (EphySpinnerClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + object_class->dispose = ephy_spinner_dispose; + object_class->finalize = ephy_spinner_finalize; + + widget_class->expose_event = ephy_spinner_expose; + widget_class->size_request = ephy_spinner_size_request; + widget_class->map = ephy_spinner_map; + widget_class->unmap = ephy_spinner_unmap; + widget_class->screen_changed = ephy_spinner_screen_changed; + + g_type_class_add_private (object_class, sizeof (EphySpinnerDetails)); +} + +/* + * ephy_spinner_new: + * + * Create a new #EphySpinner. The spinner is a widget + * that gives the user feedback about network status with + * an animated image. + * + * Return Value: the spinner #GtkWidget + **/ +GtkWidget * +ephy_spinner_new (void) +{ + return GTK_WIDGET (g_object_new (EPHY_TYPE_SPINNER, NULL)); +} diff --git a/gnome-2-22/libempathy-gtk/ephy-spinner.h b/gnome-2-22/libempathy-gtk/ephy-spinner.h new file mode 100644 index 000000000..4435fe371 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/ephy-spinner.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Copyright © 2000 Eazel, Inc. + * Copyright © 2004, 2006 Christian Persch + * + * Nautilus 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. + * + * Nautilus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Andy Hertzfeld <andy@eazel.com> + * + * $Id: ephy-spinner.h 2114 2006-12-25 12:15:00Z mr $ + */ + +#ifndef EPHY_SPINNER_H +#define EPHY_SPINNER_H + +#include <gtk/gtkwidget.h> +#include <gtk/gtkenums.h> + +G_BEGIN_DECLS + +#define EPHY_TYPE_SPINNER (ephy_spinner_get_type ()) +#define EPHY_SPINNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_SPINNER, EphySpinner)) +#define EPHY_SPINNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_SPINNER, EphySpinnerClass)) +#define EPHY_IS_SPINNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_SPINNER)) +#define EPHY_IS_SPINNER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_SPINNER)) +#define EPHY_SPINNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_SPINNER, EphySpinnerClass)) + +typedef struct _EphySpinner EphySpinner; +typedef struct _EphySpinnerClass EphySpinnerClass; +typedef struct _EphySpinnerDetails EphySpinnerDetails; + +struct _EphySpinner +{ + GtkWidget parent; + + /*< private >*/ + EphySpinnerDetails *details; +}; + +struct _EphySpinnerClass +{ + GtkWidgetClass parent_class; +}; + +GType ephy_spinner_get_type (void); + +GtkWidget *ephy_spinner_new (void); + +void ephy_spinner_start (EphySpinner *throbber); + +void ephy_spinner_stop (EphySpinner *throbber); + +void ephy_spinner_set_size (EphySpinner *spinner, + GtkIconSize size); + +G_END_DECLS + +#endif /* EPHY_SPINNER_H */ diff --git a/gnome-2-22/libempathy-gtk/libempathy-gtk.pc.in b/gnome-2-22/libempathy-gtk/libempathy-gtk.pc.in new file mode 100644 index 000000000..4248387e8 --- /dev/null +++ b/gnome-2-22/libempathy-gtk/libempathy-gtk.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libempathy-gtk +Description: Empathy interface library +Requires: glib-2.0, gobject-2.0, gtk+-2.0, libglade-2.0, libmissioncontrol, libtelepathy, libempathy +Version: @VERSION@ +Libs: -L${libdir} -lempathy-gtk +Cflags: -I${includedir} diff --git a/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.c b/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.c new file mode 100644 index 000000000..e0eef082c --- /dev/null +++ b/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2001-2006 Bastien Nocera <hadess@hadess.net> + * + * encoding list copied from gnome-terminal/encoding.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Totem project hereby grant permission for non-gpl compatible GStreamer + * plugins to be used and distributed together with GStreamer and Totem. This + * permission are above and beyond the permissions granted by the GPL license + * Totem is covered by. + * + * Monday 7th February 2005: Christian Schaller: Add exception clause. + * See license_change file for details. + * + */ + +#include "config.h" +#include <glib/gi18n.h> +#include "totem-subtitle-encoding.h" +#include <string.h> + +typedef enum +{ + SUBTITLE_ENCODING_CURRENT_LOCALE, + + SUBTITLE_ENCODING_ISO_8859_6, + SUBTITLE_ENCODING_IBM_864, + SUBTITLE_ENCODING_MAC_ARABIC, + SUBTITLE_ENCODING_WINDOWS_1256, + + SUBTITLE_ENCODING_ARMSCII_8, + + SUBTITLE_ENCODING_ISO_8859_4, + SUBTITLE_ENCODING_ISO_8859_13, + SUBTITLE_ENCODING_WINDOWS_1257, + + SUBTITLE_ENCODING_ISO_8859_14, + + SUBTITLE_ENCODING_ISO_8859_2, + SUBTITLE_ENCODING_IBM_852, + SUBTITLE_ENCODING_MAC_CE, + SUBTITLE_ENCODING_WINDOWS_1250, + + SUBTITLE_ENCODING_GB18030, + SUBTITLE_ENCODING_GB2312, + SUBTITLE_ENCODING_GBK, + SUBTITLE_ENCODING_HZ, + + SUBTITLE_ENCODING_BIG5, + SUBTITLE_ENCODING_BIG5_HKSCS, + SUBTITLE_ENCODING_EUC_TW, + + SUBTITLE_ENCODING_MAC_CROATIAN, + + SUBTITLE_ENCODING_ISO_8859_5, + SUBTITLE_ENCODING_IBM_855, + SUBTITLE_ENCODING_ISO_IR_111, + SUBTITLE_ENCODING_KOI8_R, + SUBTITLE_ENCODING_MAC_CYRILLIC, + SUBTITLE_ENCODING_WINDOWS_1251, + + SUBTITLE_ENCODING_CP_866, + + SUBTITLE_ENCODING_MAC_UKRAINIAN, + SUBTITLE_ENCODING_KOI8_U, + + SUBTITLE_ENCODING_GEOSTD8, + + SUBTITLE_ENCODING_ISO_8859_7, + SUBTITLE_ENCODING_MAC_GREEK, + SUBTITLE_ENCODING_WINDOWS_1253, + + SUBTITLE_ENCODING_MAC_GUJARATI, + + SUBTITLE_ENCODING_MAC_GURMUKHI, + + SUBTITLE_ENCODING_ISO_8859_8_I, + SUBTITLE_ENCODING_IBM_862, + SUBTITLE_ENCODING_MAC_HEBREW, + SUBTITLE_ENCODING_WINDOWS_1255, + + SUBTITLE_ENCODING_ISO_8859_8, + + SUBTITLE_ENCODING_MAC_DEVANAGARI, + + SUBTITLE_ENCODING_MAC_ICELANDIC, + + SUBTITLE_ENCODING_EUC_JP, + SUBTITLE_ENCODING_ISO_2022_JP, + SUBTITLE_ENCODING_SHIFT_JIS, + + SUBTITLE_ENCODING_EUC_KR, + SUBTITLE_ENCODING_ISO_2022_KR, + SUBTITLE_ENCODING_JOHAB, + SUBTITLE_ENCODING_UHC, + + SUBTITLE_ENCODING_ISO_8859_10, + + SUBTITLE_ENCODING_MAC_FARSI, + + SUBTITLE_ENCODING_ISO_8859_16, + SUBTITLE_ENCODING_MAC_ROMANIAN, + + SUBTITLE_ENCODING_ISO_8859_3, + + SUBTITLE_ENCODING_TIS_620, + + SUBTITLE_ENCODING_ISO_8859_9, + SUBTITLE_ENCODING_IBM_857, + SUBTITLE_ENCODING_MAC_TURKISH, + SUBTITLE_ENCODING_WINDOWS_1254, + + SUBTITLE_ENCODING_UTF_7, + SUBTITLE_ENCODING_UTF_8, + SUBTITLE_ENCODING_UTF_16, + SUBTITLE_ENCODING_UCS_2, + SUBTITLE_ENCODING_UCS_4, + + SUBTITLE_ENCODING_ISO_8859_1, + SUBTITLE_ENCODING_ISO_8859_15, + SUBTITLE_ENCODING_IBM_850, + SUBTITLE_ENCODING_MAC_ROMAN, + SUBTITLE_ENCODING_WINDOWS_1252, + + SUBTITLE_ENCODING_TCVN, + SUBTITLE_ENCODING_VISCII, + SUBTITLE_ENCODING_WINDOWS_1258, + + SUBTITLE_ENCODING_LAST +} SubtitleEncodingIndex; + + +typedef struct +{ + int index; + gboolean valid; + char *charset; + char *name; +} SubtitleEncoding; + + +static SubtitleEncoding encodings[] = { + + {SUBTITLE_ENCODING_CURRENT_LOCALE, TRUE, + NULL, N_("Current Locale")}, + + {SUBTITLE_ENCODING_ISO_8859_6, FALSE, + "ISO-8859-6", N_("Arabic")}, + {SUBTITLE_ENCODING_IBM_864, FALSE, + "IBM864", N_("Arabic")}, + {SUBTITLE_ENCODING_MAC_ARABIC, FALSE, + "MAC_ARABIC", N_("Arabic")}, + {SUBTITLE_ENCODING_WINDOWS_1256, FALSE, + "WINDOWS-1256", N_("Arabic")}, + + {SUBTITLE_ENCODING_ARMSCII_8, FALSE, + "ARMSCII-8", N_("Armenian")}, + + {SUBTITLE_ENCODING_ISO_8859_4, FALSE, + "ISO-8859-4", N_("Baltic")}, + {SUBTITLE_ENCODING_ISO_8859_13, FALSE, + "ISO-8859-13", N_("Baltic")}, + {SUBTITLE_ENCODING_WINDOWS_1257, FALSE, + "WINDOWS-1257", N_("Baltic")}, + + {SUBTITLE_ENCODING_ISO_8859_14, FALSE, + "ISO-8859-14", N_("Celtic")}, + + {SUBTITLE_ENCODING_ISO_8859_2, FALSE, + "ISO-8859-2", N_("Central European")}, + {SUBTITLE_ENCODING_IBM_852, FALSE, + "IBM852", N_("Central European")}, + {SUBTITLE_ENCODING_MAC_CE, FALSE, + "MAC_CE", N_("Central European")}, + {SUBTITLE_ENCODING_WINDOWS_1250, FALSE, + "WINDOWS-1250", N_("Central European")}, + + {SUBTITLE_ENCODING_GB18030, FALSE, + "GB18030", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_GB2312, FALSE, + "GB2312", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_GBK, FALSE, + "GBK", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_HZ, FALSE, + "HZ", N_("Chinese Simplified")}, + + {SUBTITLE_ENCODING_BIG5, FALSE, + "BIG5", N_("Chinese Traditional")}, + {SUBTITLE_ENCODING_BIG5_HKSCS, FALSE, + "BIG5-HKSCS", N_("Chinese Traditional")}, + {SUBTITLE_ENCODING_EUC_TW, FALSE, + "EUC-TW", N_("Chinese Traditional")}, + + {SUBTITLE_ENCODING_MAC_CROATIAN, FALSE, + "MAC_CROATIAN", N_("Croatian")}, + + {SUBTITLE_ENCODING_ISO_8859_5, FALSE, + "ISO-8859-5", N_("Cyrillic")}, + {SUBTITLE_ENCODING_IBM_855, FALSE, + "IBM855", N_("Cyrillic")}, + {SUBTITLE_ENCODING_ISO_IR_111, FALSE, + "ISO-IR-111", N_("Cyrillic")}, + {SUBTITLE_ENCODING_KOI8_R, FALSE, + "KOI8-R", N_("Cyrillic")}, + {SUBTITLE_ENCODING_MAC_CYRILLIC, FALSE, + "MAC-CYRILLIC", N_("Cyrillic")}, + {SUBTITLE_ENCODING_WINDOWS_1251, FALSE, + "WINDOWS-1251", N_("Cyrillic")}, + + {SUBTITLE_ENCODING_CP_866, FALSE, + "CP866", N_("Cyrillic/Russian")}, + + {SUBTITLE_ENCODING_MAC_UKRAINIAN, FALSE, + "MAC_UKRAINIAN", N_("Cyrillic/Ukrainian")}, + {SUBTITLE_ENCODING_KOI8_U, FALSE, + "KOI8-U", N_("Cyrillic/Ukrainian")}, + + {SUBTITLE_ENCODING_GEOSTD8, FALSE, + "GEORGIAN-PS", N_("Georgian")}, + + {SUBTITLE_ENCODING_ISO_8859_7, FALSE, + "ISO-8859-7", N_("Greek")}, + {SUBTITLE_ENCODING_MAC_GREEK, FALSE, + "MAC_GREEK", N_("Greek")}, + {SUBTITLE_ENCODING_WINDOWS_1253, FALSE, + "WINDOWS-1253", N_("Greek")}, + + {SUBTITLE_ENCODING_MAC_GUJARATI, FALSE, + "MAC_GUJARATI", N_("Gujarati")}, + + {SUBTITLE_ENCODING_MAC_GURMUKHI, FALSE, + "MAC_GURMUKHI", N_("Gurmukhi")}, + + {SUBTITLE_ENCODING_ISO_8859_8_I, FALSE, + "ISO-8859-8-I", N_("Hebrew")}, + {SUBTITLE_ENCODING_IBM_862, FALSE, + "IBM862", N_("Hebrew")}, + {SUBTITLE_ENCODING_MAC_HEBREW, FALSE, + "MAC_HEBREW", N_("Hebrew")}, + {SUBTITLE_ENCODING_WINDOWS_1255, FALSE, + "WINDOWS-1255", N_("Hebrew")}, + + {SUBTITLE_ENCODING_ISO_8859_8, FALSE, + "ISO-8859-8", N_("Hebrew Visual")}, + + {SUBTITLE_ENCODING_MAC_DEVANAGARI, FALSE, + "MAC_DEVANAGARI", N_("Hindi")}, + + {SUBTITLE_ENCODING_MAC_ICELANDIC, FALSE, + "MAC_ICELANDIC", N_("Icelandic")}, + + {SUBTITLE_ENCODING_EUC_JP, FALSE, + "EUC-JP", N_("Japanese")}, + {SUBTITLE_ENCODING_ISO_2022_JP, FALSE, + "ISO2022JP", N_("Japanese")}, + {SUBTITLE_ENCODING_SHIFT_JIS, FALSE, + "SHIFT-JIS", N_("Japanese")}, + + {SUBTITLE_ENCODING_EUC_KR, FALSE, + "EUC-KR", N_("Korean")}, + {SUBTITLE_ENCODING_ISO_2022_KR, FALSE, + "ISO2022KR", N_("Korean")}, + {SUBTITLE_ENCODING_JOHAB, FALSE, + "JOHAB", N_("Korean")}, + {SUBTITLE_ENCODING_UHC, FALSE, + "UHC", N_("Korean")}, + + {SUBTITLE_ENCODING_ISO_8859_10, FALSE, + "ISO-8859-10", N_("Nordic")}, + + {SUBTITLE_ENCODING_MAC_FARSI, FALSE, + "MAC_FARSI", N_("Persian")}, + + {SUBTITLE_ENCODING_ISO_8859_16, FALSE, + "ISO-8859-16", N_("Romanian")}, + {SUBTITLE_ENCODING_MAC_ROMANIAN, FALSE, + "MAC_ROMANIAN", N_("Romanian")}, + + {SUBTITLE_ENCODING_ISO_8859_3, FALSE, + "ISO-8859-3", N_("South European")}, + + {SUBTITLE_ENCODING_TIS_620, FALSE, + "TIS-620", N_("Thai")}, + + {SUBTITLE_ENCODING_ISO_8859_9, FALSE, + "ISO-8859-9", N_("Turkish")}, + {SUBTITLE_ENCODING_IBM_857, FALSE, + "IBM857", N_("Turkish")}, + {SUBTITLE_ENCODING_MAC_TURKISH, FALSE, + "MAC_TURKISH", N_("Turkish")}, + {SUBTITLE_ENCODING_WINDOWS_1254, FALSE, + "WINDOWS-1254", N_("Turkish")}, + + {SUBTITLE_ENCODING_UTF_7, FALSE, + "UTF-7", N_("Unicode")}, + {SUBTITLE_ENCODING_UTF_8, FALSE, + "UTF-8", N_("Unicode")}, + {SUBTITLE_ENCODING_UTF_16, FALSE, + "UTF-16", N_("Unicode")}, + {SUBTITLE_ENCODING_UCS_2, FALSE, + "UCS-2", N_("Unicode")}, + {SUBTITLE_ENCODING_UCS_4, FALSE, + "UCS-4", N_("Unicode")}, + + {SUBTITLE_ENCODING_ISO_8859_1, FALSE, + "ISO-8859-1", N_("Western")}, + {SUBTITLE_ENCODING_ISO_8859_15, FALSE, + "ISO-8859-15", N_("Western")}, + {SUBTITLE_ENCODING_IBM_850, FALSE, + "IBM850", N_("Western")}, + {SUBTITLE_ENCODING_MAC_ROMAN, FALSE, + "MAC_ROMAN", N_("Western")}, + {SUBTITLE_ENCODING_WINDOWS_1252, FALSE, + "WINDOWS-1252", N_("Western")}, + + {SUBTITLE_ENCODING_TCVN, FALSE, + "TCVN", N_("Vietnamese")}, + {SUBTITLE_ENCODING_VISCII, FALSE, + "VISCII", N_("Vietnamese")}, + {SUBTITLE_ENCODING_WINDOWS_1258, FALSE, + "WINDOWS-1258", N_("Vietnamese")} +}; + +static const SubtitleEncoding * +find_encoding_by_charset (const char *charset) +{ + int i; + + i = 1; /* skip current locale */ + while (i < SUBTITLE_ENCODING_LAST) { + if (strcasecmp (charset, encodings[i].charset) == 0) + return &encodings[i]; + + ++i; + } + + if (strcasecmp (charset, + encodings[SUBTITLE_ENCODING_CURRENT_LOCALE].charset) == 0) + return &encodings[SUBTITLE_ENCODING_CURRENT_LOCALE]; + + return NULL; +} + +static void +subtitle_encoding_init (void) +{ + int i; + gsize bytes_read, bytes_written; + gchar *converted; + gchar ascii_sample[96]; + + g_get_charset ((const char **) + &encodings[SUBTITLE_ENCODING_CURRENT_LOCALE].charset); + + g_assert (G_N_ELEMENTS (encodings) == SUBTITLE_ENCODING_LAST); + + /* Initialize the sample text with all of the printing ASCII characters + * from space (32) to the tilde (126), 95 in all. */ + for (i = 0; i < (int) sizeof (ascii_sample); i++) + ascii_sample[i] = i + 32; + + ascii_sample[sizeof (ascii_sample) - 1] = '\0'; + + i = 0; + while (i < SUBTITLE_ENCODING_LAST) { + bytes_read = 0; + bytes_written = 0; + + g_assert (encodings[i].index == i); + + /* Translate the names */ + encodings[i].name = _(encodings[i].name); + + /* Test that the encoding is a proper superset of ASCII (which naive + * apps are going to use anyway) by attempting to validate the text + * using the current encoding. This also flushes out any encodings + * which the underlying GIConv implementation can't support. + */ + converted = g_convert (ascii_sample, sizeof (ascii_sample) - 1, + encodings[i].charset, encodings[i].charset, + &bytes_read, &bytes_written, NULL); + + /* The encoding is only valid if ASCII passes through cleanly. */ + if (i == SUBTITLE_ENCODING_CURRENT_LOCALE) + encodings[i].valid = TRUE; + else + encodings[i].valid = + (bytes_read == (sizeof (ascii_sample) - 1)) && + (converted != NULL) && (strcmp (converted, ascii_sample) == 0); + +#ifdef DEBUG_ENCODINGS + if (!encodings[i].valid) { + g_print ("Rejecting encoding %s as invalid:\n", encodings[i].charset); + g_print (" input \"%s\"\n", ascii_sample); + g_print (" output \"%s\"\n\n", converted ? converted : "(null)"); + } +#endif + + /* Discard the converted string. */ + g_free (converted); + + ++i; + } +} + +static int +subtitle_encoding_get_index (const char *charset) +{ + const SubtitleEncoding *e; + + e = find_encoding_by_charset (charset); + if (e != NULL) + return e->index; + else + return SUBTITLE_ENCODING_CURRENT_LOCALE; +} + +static const char * +subtitle_encoding_get_charset (int index) +{ + const SubtitleEncoding *e; + + if (index >= SUBTITLE_ENCODING_LAST) + e = &encodings[SUBTITLE_ENCODING_CURRENT_LOCALE]; + else if (index < SUBTITLE_ENCODING_CURRENT_LOCALE) + e = &encodings[SUBTITLE_ENCODING_CURRENT_LOCALE]; + else if (!encodings[index].valid) + e = &encodings[SUBTITLE_ENCODING_CURRENT_LOCALE]; + else + e = &encodings[index]; + return e->charset; +} + +enum +{ + INDEX_COL, + NAME_COL +}; + +static gint +compare (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer data) +{ + gchar *str_a, *str_b; + gint result; + + gtk_tree_model_get (model, a, NAME_COL, &str_a, -1); + gtk_tree_model_get (model, b, NAME_COL, &str_b, -1); + + result = strcmp (str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return result; +} + +static void +is_encoding_sensitive (GtkCellLayout * cell_layout, + GtkCellRenderer * cell, + GtkTreeModel * tree_model, GtkTreeIter * iter, gpointer data) +{ + + gboolean sensitive; + + sensitive = !gtk_tree_model_iter_has_child (tree_model, iter); + g_object_set (cell, "sensitive", sensitive, NULL); +} + +static GtkTreeModel * +subtitle_encoding_create_store (void) +{ + gchar *label; + gchar *lastlang = ""; + GtkTreeIter iter, iter2; + GtkTreeStore *store; + int i; + + store = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_STRING); + + for (i = 0; i < SUBTITLE_ENCODING_LAST; i++) { + if (encodings[i].valid) { + if (strcmp (lastlang, encodings[i].name)) { + lastlang = encodings[i].name; + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, INDEX_COL, + -1, NAME_COL, lastlang, -1); + } + label = g_strdup_printf("%s (%s)", lastlang, encodings[i].charset); + gtk_tree_store_append (store, &iter2, &iter); + gtk_tree_store_set (store, &iter2, INDEX_COL, + encodings[i].index, NAME_COL, label, -1); + g_free(label); + } + } + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store), + compare, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + NAME_COL, GTK_SORT_ASCENDING); + return GTK_TREE_MODEL (store); +} + +static void +subtitle_encoding_combo_render (GtkComboBox * combo) +{ + GtkCellRenderer *renderer; + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, + "text", NAME_COL, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), + renderer, is_encoding_sensitive, NULL, NULL); +} + +const char * +totem_subtitle_encoding_get_selected (GtkComboBox * combo) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gint index = -1; + + model = gtk_combo_box_get_model (combo); + if (gtk_combo_box_get_active_iter (combo, &iter)) { + gtk_tree_model_get (model, &iter, INDEX_COL, &index, -1); + } + if (index == -1) + return NULL; + return subtitle_encoding_get_charset (index); +} + +void +totem_subtitle_encoding_set (GtkComboBox * combo, const char *encoding) +{ + GtkTreeModel *model; + GtkTreeIter iter, iter2; + gint index, i; + + g_return_if_fail (encoding != NULL); + + model = gtk_combo_box_get_model (combo); + index = subtitle_encoding_get_index (encoding); + gtk_tree_model_get_iter_first (model, &iter); + do { + if (!gtk_tree_model_iter_has_child (model, &iter)) + continue; + if (!gtk_tree_model_iter_children (model, &iter2, &iter)) + continue; + do { + gtk_tree_model_get (model, &iter2, INDEX_COL, &i, -1); + if (i == index) + break; + } while (gtk_tree_model_iter_next (model, &iter2)); + if (i == index) + break; + } while (gtk_tree_model_iter_next (model, &iter)); + gtk_combo_box_set_active_iter (combo, &iter2); +} + +void +totem_subtitle_encoding_init (GtkComboBox *combo) +{ + GtkTreeModel *model; + subtitle_encoding_init (); + model = subtitle_encoding_create_store (); + gtk_combo_box_set_model (combo, model); + g_object_unref (model); + subtitle_encoding_combo_render (combo); +} + +/* + * vim: sw=2 ts=8 cindent noai bs=2 + */ diff --git a/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.h b/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.h new file mode 100644 index 000000000..7283f003a --- /dev/null +++ b/gnome-2-22/libempathy-gtk/totem-subtitle-encoding.h @@ -0,0 +1,12 @@ +/* Encoding stuff */ + +#ifndef TOTEM_SUBTITLE_ENCODING_H +#define TOTEM_SUBTITLE_ENCODING_H + +#include <gtk/gtk.h> + +void totem_subtitle_encoding_init (GtkComboBox *combo); +void totem_subtitle_encoding_set (GtkComboBox *combo, const char *encoding); +const char * totem_subtitle_encoding_get_selected (GtkComboBox *combo); + +#endif /* SUBTITLE_ENCODING_H */ |