+2007-06-22 Xavier Claessens <xclaesse@gmail.com>
+
+ * libempathy/*.[ch]:
+ * libempathy-gtk/*.[ch]:
+ * src/*.[ch]: Rename all filenames starting with "gossip" by "empathy",
+ change namespace of all gossip_*() functions to empathy_*().
+ Fixes bug #444490 (Mario Danic, Xavier Claessens).
+
2007-06-21 Xavier Claessens <xclaesse@gmail.com>
* configure.ac: Bump version to 0.9
Things you can do if you want to help:
- - Rename all files and functions name to use the empathy namespace. Bug #444490.
- Porting gossip-account-widget-*.{c,h} from gossip project (Guillaume is already working on IRC widget).
- UI for inviting a contact in a chatroom.
- UI for accept/refuse invitation to join a chatroom.
- UI to send a message directly to a contact.
- GtkWidget-ify libempathy-gtk. Actually most window/dialog do not inherit from GtkWindow/GtkDialog. Need to create a glade catalog.
- - Filter channels before dispatching them. For example we need a GtkStatusIcon that blink when an event arrives (text/voip/ft channel) and tells the MC to dispatch the channel only when the user clicked the icon. Like in gossip. For that we need a filter DBus API in MC, not yet written, a draft spec is proposed on the telepathy ML.
- - Make use of NetworkManager to set the presence
- Remove Quit option everywhere, empathy is a session service and shouldn't be leaved.
- Add sound events
- Add register capability in GossipAccountsDialog if the profile says it's supported.
- Write a manual based on gossip's.
- Testing and Bugfixing.
- Update translations.
+ - Fix indentation and alignment broken since namespace changed from gossip_ to empathy_
SoC projects:
- Adding VoIP support based on the patch proposed for gossip.
empathy-status-icon.c empathy-status-icon.h \
empathy-contact-widget.c empathy-contact-widget.h \
empathy-contact-dialogs.c empathy-contact-dialogs.h \
- gossip-accounts-dialog.c gossip-accounts-dialog.h \
- gossip-account-widget-generic.c gossip-account-widget-generic.h \
- gossip-account-widget-jabber.c gossip-account-widget-jabber.h \
- gossip-profile-chooser.c gossip-profile-chooser.h \
- gossip-cell-renderer-expander.c gossip-cell-renderer-expander.h \
- gossip-cell-renderer-text.c gossip-cell-renderer-text.h \
- gossip-spell.c gossip-spell.h \
- gossip-spell-dialog.c gossip-spell-dialog.h \
- gossip-contact-groups.c gossip-contact-groups.h \
- gossip-contact-list-store.c gossip-contact-list-store.h \
- gossip-contact-list-view.c gossip-contact-list-view.h \
- gossip-preferences.c gossip-preferences.h \
- gossip-theme-manager.c gossip-theme-manager.h \
- gossip-chat-window.c gossip-chat-window.h \
- gossip-chat.c gossip-chat.h \
- gossip-chat-view.c gossip-chat-view.h \
- gossip-private-chat.c gossip-private-chat.h \
- gossip-group-chat.c gossip-group-chat.h \
- gossip-geometry.c gossip-geometry.h \
- gossip-status-presets.c gossip-status-presets.h \
- gossip-presence-chooser.c gossip-presence-chooser.h \
- gossip-about-dialog.c gossip-about-dialog.h \
- gossip-account-chooser.c gossip-account-chooser.h \
- gossip-new-chatroom-dialog.c gossip-new-chatroom-dialog.h \
- gossip-chatrooms-window.c gossip-chatrooms-window.h \
- gossip-log-window.c gossip-log-window.h \
- gossip-ui-utils.c gossip-ui-utils.h
+ empathy-accounts-dialog.c empathy-accounts-dialog.h \
+ empathy-account-widget-generic.c empathy-account-widget-generic.h \
+ empathy-account-widget-jabber.c empathy-account-widget-jabber.h \
+ empathy-profile-chooser.c empathy-profile-chooser.h \
+ empathy-cell-renderer-expander.c empathy-cell-renderer-expander.h \
+ empathy-cell-renderer-text.c empathy-cell-renderer-text.h \
+ empathy-spell.c empathy-spell.h \
+ empathy-spell-dialog.c empathy-spell-dialog.h \
+ empathy-contact-groups.c empathy-contact-groups.h \
+ empathy-contact-list-store.c empathy-contact-list-store.h \
+ empathy-contact-list-view.c empathy-contact-list-view.h \
+ empathy-preferences.c empathy-preferences.h \
+ empathy-theme-manager.c empathy-theme-manager.h \
+ empathy-chat-window.c empathy-chat-window.h \
+ empathy-chat.c empathy-chat.h \
+ empathy-chat-view.c empathy-chat-view.h \
+ empathy-private-chat.c empathy-private-chat.h \
+ empathy-group-chat.c empathy-group-chat.h \
+ empathy-geometry.c empathy-geometry.h \
+ empathy-status-presets.c empathy-status-presets.h \
+ empathy-presence-chooser.c empathy-presence-chooser.h \
+ empathy-about-dialog.c empathy-about-dialog.h \
+ empathy-account-chooser.c empathy-account-chooser.h \
+ empathy-new-chatroom-dialog.c empathy-new-chatroom-dialog.h \
+ empathy-chatrooms-window.c empathy-chatrooms-window.h \
+ empathy-log-window.c empathy-log-window.h \
+ empathy-ui-utils.c empathy-ui-utils.h
libempathy_gtk_la_LIBADD = \
$(EMPATHY_LIBS) \
empathy-status-icon.glade \
empathy-contact-widget.glade \
empathy-contact-dialogs.glade \
- gossip-preferences.glade \
- gossip-presence-chooser.glade \
- gossip-accounts-dialog.glade \
- gossip-account-widget-jabber.glade \
- gossip-new-chatroom-dialog.glade \
- gossip-group-chat.glade \
- gossip-chatrooms-window.glade \
- gossip-spell-dialog.glade \
- gossip-log-window.glade \
- gossip-chat.glade
+ empathy-preferences.glade \
+ empathy-presence-chooser.glade \
+ empathy-accounts-dialog.glade \
+ empathy-account-widget-jabber.glade \
+ empathy-new-chatroom-dialog.glade \
+ empathy-group-chat.glade \
+ empathy-chatrooms-window.glade \
+ empathy-spell-dialog.glade \
+ empathy-log-window.glade \
+ empathy-chat.glade
dtddir = $(datadir)/empathy
dtd_DATA = \
- gossip-status-presets.dtd \
- gossip-contact-groups.dtd
+ empathy-status-presets.dtd \
+ empathy-contact-groups.dtd
EXTRA_DIST = \
--- /dev/null
+/* -*- 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>
+ */
+
+#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);
+}
+
+
--- /dev/null
+/* -*- 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: Martyn Russell <martyn@imendio.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__ */
--- /dev/null
+/* -*- 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: Martyn Russell <martyn@imendio.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 can_select_all;
+ gboolean has_all_option;
+} 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,
+ McAccount *account);
+static void account_chooser_status_changed_cb (MissionControl *mc,
+ TelepathyConnectionStatus status,
+ McPresence presence,
+ TelepathyConnectionStatusReason 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);
+static gboolean account_chooser_set_enabled_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ EmpathyAccountChooser *chooser);
+
+enum {
+ PROP_0,
+ PROP_CAN_SELECT_ALL,
+ 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_CAN_SELECT_ALL,
+ g_param_spec_boolean ("can-select-all",
+ "Can Select All",
+ "Should the user be able to select offline accounts",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ 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)
+{
+}
+
+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_CAN_SELECT_ALL:
+ g_value_set_boolean (value, priv->can_select_all);
+ break;
+ 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_CAN_SELECT_ALL:
+ empathy_account_chooser_set_can_select_all (EMPATHY_ACCOUNT_CHOOSER (object),
+ g_value_get_boolean (value));
+ break;
+ 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_can_select_all (EmpathyAccountChooser *chooser)
+{
+ EmpathyAccountChooserPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
+
+ priv = GET_PRIV (chooser);
+
+ return priv->can_select_all;
+}
+
+void
+empathy_account_chooser_set_can_select_all (EmpathyAccountChooser *chooser,
+ gboolean can_select_all)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkComboBox *combobox;
+ GtkTreeModel *model;
+
+ g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
+
+ priv = GET_PRIV (chooser);
+
+ if (priv->can_select_all == can_select_all) {
+ return;
+ }
+
+ combobox = GTK_COMBO_BOX (chooser);
+ model = gtk_combo_box_get_model (combobox);
+
+ priv->can_select_all = can_select_all;
+
+ gtk_tree_model_foreach (model,
+ (GtkTreeModelForeachFunc) account_chooser_set_enabled_foreach,
+ chooser);
+
+ g_object_notify (G_OBJECT (chooser), "can-select-all");
+}
+
+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,
+ 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)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkListStore *store;
+ GtkComboBox *combobox;
+ GtkTreeIter iter;
+
+ priv = GET_PRIV (chooser);
+
+ combobox = GTK_COMBO_BOX (chooser);
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
+
+ gtk_list_store_append (store, &iter);
+ account_chooser_update_iter (chooser, &iter, account);
+}
+
+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);
+}
+
+static void
+account_chooser_account_remove_foreach (McAccount *account,
+ EmpathyAccountChooser *chooser)
+{
+ /* Fixme: TODO */
+}
+
+static void
+account_chooser_update_iter (EmpathyAccountChooser *chooser,
+ GtkTreeIter *iter,
+ McAccount *account)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkListStore *store;
+ GtkComboBox *combobox;
+ TpConn *tp_conn;
+ const gchar *icon_name;
+ gboolean is_enabled;
+
+ priv = GET_PRIV (chooser);
+
+ combobox = GTK_COMBO_BOX (chooser);
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
+
+ icon_name = empathy_icon_name_from_account (account);
+ tp_conn = mission_control_get_connection (priv->mc, account, NULL);
+ is_enabled = (tp_conn != NULL || priv->can_select_all);
+
+ if (tp_conn) {
+ g_object_unref (tp_conn);
+ }
+
+ 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,
+ COL_ACCOUNT_POINTER, account,
+ -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);
+ }
+}
+
+static void
+account_chooser_status_changed_cb (MissionControl *mc,
+ TelepathyConnectionStatus status,
+ McPresence presence,
+ TelepathyConnectionStatusReason reason,
+ const gchar *unique_name,
+ EmpathyAccountChooser *chooser)
+{
+ /* FIXME: implement */
+}
+
+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_set_enabled_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ EmpathyAccountChooser *chooser)
+{
+ EmpathyAccountChooserPriv *priv;
+ McAccount *account;
+
+ priv = GET_PRIV (chooser);
+
+ gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
+ if (!account) {
+ return FALSE;
+ }
+
+ account_chooser_update_iter (chooser, iter, account);
+ g_object_unref (account);
+
+ return FALSE;
+}
+
--- /dev/null
+/* -*- 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: Martyn Russell <martyn@imendio.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 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_can_select_all (EmpathyAccountChooser *chooser);
+
+void empathy_account_chooser_set_can_select_all (EmpathyAccountChooser *chooser,
+ gboolean can_select_all);
+gboolean empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser);
+void empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
+ gboolean has_all_option);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_ACCOUNT_CHOOSER_H__ */
+
--- /dev/null
+/* -*- 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>
+ */
+
+#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-gtk/empathy-ui-utils.h>
+
+#include "empathy-account-widget-generic.h"
+
+typedef struct {
+ McAccount *account;
+
+ GtkWidget *sw;
+ GtkWidget *table_settings;
+ GtkSizeGroup *size_group;
+
+ guint n_rows;
+} EmpathyAccountWidgetGeneric;
+
+static gboolean account_widget_generic_entry_focus_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ EmpathyAccountWidgetGeneric *settings);
+static void account_widget_generic_int_changed_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings);
+static void account_widget_generic_checkbutton_toggled_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings);
+static gchar * account_widget_generic_format_param_name (const gchar *param_name);
+static void account_widget_generic_setup_foreach (McProtocolParam *param,
+ EmpathyAccountWidgetGeneric *settings);
+static void account_widget_generic_destroy_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings);
+
+static gboolean
+account_widget_generic_entry_focus_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ EmpathyAccountWidgetGeneric *settings)
+{
+ 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");
+
+ mc_account_set_param_string (settings->account, param_name, str);
+
+ return FALSE;
+}
+
+static void
+account_widget_generic_int_changed_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings)
+{
+ const gchar *param_name;
+ gint value;
+
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+ param_name = g_object_get_data (G_OBJECT (widget), "param_name");
+
+ mc_account_set_param_int (settings->account, param_name, value);
+}
+
+static void
+account_widget_generic_checkbutton_toggled_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings)
+{
+ gboolean active;
+ const gchar *param_name;
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ param_name = g_object_get_data (G_OBJECT (widget), "param_name");
+
+ mc_account_set_param_boolean (settings->account, param_name, active);
+}
+
+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
+account_widget_generic_setup_foreach (McProtocolParam *param,
+ EmpathyAccountWidgetGeneric *settings)
+{
+ GtkWidget *widget;
+ gchar *param_name_formatted;
+
+ param_name_formatted = account_widget_generic_format_param_name (param->name);
+
+ gtk_table_resize (GTK_TABLE (settings->table_settings),
+ ++settings->n_rows,
+ 2);
+
+ if (param->signature[0] == 's') {
+ gchar *str = NULL;
+
+ str = g_strdup_printf (_("%s:"), param_name_formatted);
+ widget = gtk_label_new (str);
+ g_free (str);
+
+ gtk_size_group_add_widget (settings->size_group, widget);
+ gtk_table_attach (GTK_TABLE (settings->table_settings),
+ widget,
+ 0, 1,
+ settings->n_rows - 1, settings->n_rows,
+ GTK_FILL, 0,
+ 0, 0);
+
+ str = NULL;
+ widget = gtk_entry_new ();
+ mc_account_get_param_string (settings->account,
+ param->name,
+ &str);
+ if (str) {
+ gtk_entry_set_text (GTK_ENTRY (widget), 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_generic_entry_focus_cb),
+ settings);
+
+ gtk_table_attach (GTK_TABLE (settings->table_settings),
+ widget,
+ 1, 2,
+ settings->n_rows - 1, settings->n_rows,
+ GTK_FILL | GTK_EXPAND, 0,
+ 0, 0);
+ }
+ else if (param->signature[0] == 'q' ||
+ param->signature[0] == 'n') {
+ gchar *str = NULL;
+ gint value = 0;
+
+ str = g_strdup_printf (_("%s:"), param_name_formatted);
+ widget = gtk_label_new (str);
+ g_free (str);
+
+ gtk_size_group_add_widget (settings->size_group, widget);
+ gtk_table_attach (GTK_TABLE (settings->table_settings),
+ widget,
+ 0, 1,
+ settings->n_rows - 1, settings->n_rows,
+ GTK_FILL, 0,
+ 0, 0);
+
+ widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+ mc_account_get_param_int (settings->account,
+ param->name,
+ &value);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
+
+ g_signal_connect (widget, "value-changed",
+ G_CALLBACK (account_widget_generic_int_changed_cb),
+ settings);
+
+ gtk_table_attach (GTK_TABLE (settings->table_settings),
+ widget,
+ 1, 2,
+ settings->n_rows - 1, settings->n_rows,
+ GTK_FILL | GTK_EXPAND, 0,
+ 0, 0);
+ }
+ else if (param->signature[0] == 'b') {
+ gboolean value = FALSE;
+
+ mc_account_get_param_boolean (settings->account,
+ param->name,
+ &value);
+
+ widget = gtk_check_button_new_with_label (param_name_formatted);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+ g_signal_connect (widget, "toggled",
+ G_CALLBACK (account_widget_generic_checkbutton_toggled_cb),
+ settings);
+
+ gtk_table_attach (GTK_TABLE (settings->table_settings),
+ widget,
+ 0, 2,
+ settings->n_rows - 1, settings->n_rows,
+ GTK_FILL | GTK_EXPAND, 0,
+ 0, 0);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ g_free (param_name_formatted);
+
+ g_object_set_data_full (G_OBJECT (widget), "param_name",
+ g_strdup (param->name), g_free);
+}
+
+static void
+accounts_widget_generic_setup (EmpathyAccountWidgetGeneric *settings)
+{
+ McProtocol *protocol;
+ McProfile *profile;
+ GSList *params;
+
+ profile = mc_account_get_profile (settings->account);
+ protocol = mc_profile_get_protocol (profile);
+ params = mc_protocol_get_params (protocol);
+
+ g_slist_foreach (params,
+ (GFunc) account_widget_generic_setup_foreach,
+ settings);
+
+ g_slist_free (params);
+}
+
+static void
+account_widget_generic_destroy_cb (GtkWidget *widget,
+ EmpathyAccountWidgetGeneric *settings)
+{
+ g_object_unref (settings->account);
+ g_object_unref (settings->size_group);
+
+ g_free (settings);
+}
+
+GtkWidget *
+empathy_account_widget_generic_new (McAccount *account,
+ GtkWidget *label_name)
+{
+ EmpathyAccountWidgetGeneric *settings;
+
+ g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (label_name), NULL);
+
+ settings = g_new0 (EmpathyAccountWidgetGeneric, 1);
+
+ settings->account = g_object_ref (account);
+
+ settings->table_settings = gtk_table_new (0, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (settings->table_settings), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (settings->table_settings), 6);
+ settings->sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (settings->sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (settings->sw),
+ settings->table_settings);
+
+ settings->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ if (label_name) {
+ gtk_size_group_add_widget (settings->size_group, label_name);
+ }
+
+ accounts_widget_generic_setup (settings);
+
+ g_signal_connect (settings->sw, "destroy",
+ G_CALLBACK (account_widget_generic_destroy_cb),
+ settings);
+
+ gtk_widget_show_all (settings->sw);
+
+ return settings->sw;
+}
--- /dev/null
+/* -*- 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 <libmissioncontrol/mc-account.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *empathy_account_widget_generic_new (McAccount *account,
+ GtkWidget *label_name);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_ACCOUNT_WIDGET_GENERIC_H__ */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * 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>
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include <libmissioncontrol/mc-profile.h>
+
+#include <libempathy/empathy-utils.h>
+
+#include "empathy-account-widget-jabber.h"
+#include "empathy-ui-utils.h"
+
+#define PORT_WITHOUT_SSL 5222
+#define PORT_WITH_SSL 5223
+
+typedef struct {
+ McAccount *account;
+
+ GtkWidget *vbox_settings;
+ GtkWidget *button_forget;
+ GtkWidget *entry_id;
+ GtkWidget *entry_password;
+ GtkWidget *entry_resource;
+ GtkWidget *entry_server;
+ GtkWidget *spinbutton_port;
+ GtkWidget *checkbutton_ssl;
+} EmpathyAccountWidgetJabber;
+
+static gboolean account_widget_jabber_entry_focus_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_entry_changed_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_checkbutton_toggled_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_value_changed_cb (GtkWidget *spinbutton,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_button_forget_clicked_cb (GtkWidget *button,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_destroy_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings);
+static void account_widget_jabber_setup (EmpathyAccountWidgetJabber *settings);
+
+static gboolean
+account_widget_jabber_entry_focus_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ EmpathyAccountWidgetJabber *settings)
+{
+ const gchar *param;
+ const gchar *str;
+
+ if (widget == settings->entry_password) {
+ param = "password";
+ }
+ else if (widget == settings->entry_resource) {
+ param = "resource";
+ }
+ else if (widget == settings->entry_server) {
+ param = "server";
+ }
+ else if (widget == settings->entry_id) {
+ param = "account";
+ } else {
+ return FALSE;
+ }
+
+ str = gtk_entry_get_text (GTK_ENTRY (widget));
+ if (G_STR_EMPTY (str)) {
+ gchar *value = NULL;
+
+ mc_account_get_param_string (settings->account, param, &value);
+ gtk_entry_set_text (GTK_ENTRY (widget), value ? value : "");
+ g_free (value);
+ } else {
+ mc_account_set_param_string (settings->account, param, str);
+ }
+
+ return FALSE;
+}
+
+static void
+account_widget_jabber_entry_changed_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings)
+{
+ if (widget == settings->entry_password) {
+ const gchar *str;
+
+ str = gtk_entry_get_text (GTK_ENTRY (widget));
+ gtk_widget_set_sensitive (settings->button_forget, !G_STR_EMPTY (str));
+ }
+}
+
+static void
+account_widget_jabber_checkbutton_toggled_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings)
+{
+ if (widget == settings->checkbutton_ssl) {
+ gint port = 0;
+ gboolean old_ssl;
+
+ mc_account_get_param_int (settings->account, "port", &port);
+ old_ssl = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if (old_ssl) {
+ if (port == PORT_WITHOUT_SSL) {
+ port = PORT_WITH_SSL;
+ }
+ } else {
+ if (port == PORT_WITH_SSL) {
+ port = PORT_WITHOUT_SSL;
+ }
+ }
+
+ mc_account_set_param_int (settings->account, "port", port);
+ mc_account_set_param_boolean (settings->account, "old-ssl", old_ssl);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (settings->spinbutton_port), port);
+ }
+}
+
+static void
+account_widget_jabber_value_changed_cb (GtkWidget *spinbutton,
+ EmpathyAccountWidgetJabber *settings)
+{
+ if (spinbutton == settings->spinbutton_port) {
+ gdouble value;
+
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spinbutton));
+ mc_account_set_param_int (settings->account, "port", (gint) value);
+ }
+}
+
+static void
+account_widget_jabber_button_forget_clicked_cb (GtkWidget *button,
+ EmpathyAccountWidgetJabber *settings)
+{
+ mc_account_set_param_string (settings->account, "password", "");
+ gtk_entry_set_text (GTK_ENTRY (settings->entry_password), "");
+}
+
+static void
+account_widget_jabber_destroy_cb (GtkWidget *widget,
+ EmpathyAccountWidgetJabber *settings)
+{
+ g_object_unref (settings->account);
+ g_free (settings);
+}
+
+static void
+account_widget_jabber_setup (EmpathyAccountWidgetJabber *settings)
+{
+ gint port = 0;
+ gchar *id = NULL;
+ gchar *resource = NULL;
+ gchar *server = NULL;
+ gchar *password = NULL;
+ gboolean old_ssl = FALSE;
+
+ mc_account_get_param_int (settings->account, "port", &port);
+ mc_account_get_param_string (settings->account, "account", &id);
+ mc_account_get_param_string (settings->account, "resource", &resource);
+ mc_account_get_param_string (settings->account, "server", &server);
+ mc_account_get_param_string (settings->account, "password", &password);
+ mc_account_get_param_boolean (settings->account, "old-ssl", &old_ssl);
+
+ if (!id) {
+ McProfile *profile;
+ const gchar *server;
+
+ profile = mc_account_get_profile (settings->account);
+ server = mc_profile_get_default_account_domain (profile);
+ if (server) {
+ id = g_strconcat ("user@", server, NULL);
+ }
+ g_object_unref (profile);
+ }
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (settings->checkbutton_ssl), old_ssl);
+ gtk_entry_set_text (GTK_ENTRY (settings->entry_id), id ? id : "");
+ gtk_entry_set_text (GTK_ENTRY (settings->entry_password), password ? password : "");
+ gtk_entry_set_text (GTK_ENTRY (settings->entry_resource), resource ? resource : "");
+ gtk_entry_set_text (GTK_ENTRY (settings->entry_server), server ? server : "");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (settings->spinbutton_port), port);
+
+ gtk_widget_set_sensitive (settings->button_forget, !G_STR_EMPTY (password));
+
+ g_free (id);
+ g_free (resource);
+ g_free (server);
+ g_free (password);
+}
+
+GtkWidget *
+empathy_account_widget_jabber_new (McAccount *account)
+{
+ EmpathyAccountWidgetJabber *settings;
+ GladeXML *glade;
+ GtkSizeGroup *size_group;
+ GtkWidget *label_id, *label_password;
+ GtkWidget *label_server, *label_resource, *label_port;
+
+ settings = g_new0 (EmpathyAccountWidgetJabber, 1);
+ settings->account = g_object_ref (account);
+
+ glade = empathy_glade_get_file ("empathy-account-widget-jabber.glade",
+ "vbox_jabber_settings",
+ NULL,
+ "vbox_jabber_settings", &settings->vbox_settings,
+ "button_forget", &settings->button_forget,
+ "label_id", &label_id,
+ "label_password", &label_password,
+ "label_resource", &label_resource,
+ "label_server", &label_server,
+ "label_port", &label_port,
+ "entry_id", &settings->entry_id,
+ "entry_password", &settings->entry_password,
+ "entry_resource", &settings->entry_resource,
+ "entry_server", &settings->entry_server,
+ "spinbutton_port", &settings->spinbutton_port,
+ "checkbutton_ssl", &settings->checkbutton_ssl,
+ NULL);
+
+ account_widget_jabber_setup (settings);
+
+ empathy_glade_connect (glade,
+ settings,
+ "vbox_jabber_settings", "destroy", account_widget_jabber_destroy_cb,
+ "button_forget", "clicked", account_widget_jabber_button_forget_clicked_cb,
+ "entry_password", "changed", account_widget_jabber_entry_changed_cb,
+ "spinbutton_port", "value-changed", account_widget_jabber_value_changed_cb,
+ "entry_id", "focus-out-event", account_widget_jabber_entry_focus_cb,
+ "entry_password", "focus-out-event", account_widget_jabber_entry_focus_cb,
+ "entry_resource", "focus-out-event", account_widget_jabber_entry_focus_cb,
+ "entry_server", "focus-out-event", account_widget_jabber_entry_focus_cb,
+ "checkbutton_ssl", "toggled", account_widget_jabber_checkbutton_toggled_cb,
+ NULL);
+
+ g_object_unref (glade);
+
+ /* Set up remaining widgets */
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ gtk_size_group_add_widget (size_group, label_id);
+ gtk_size_group_add_widget (size_group, label_password);
+ gtk_size_group_add_widget (size_group, label_resource);
+ gtk_size_group_add_widget (size_group, label_server);
+ gtk_size_group_add_widget (size_group, label_port);
+
+ g_object_unref (size_group);
+
+ gtk_widget_show (settings->vbox_settings);
+
+ return settings->vbox_settings;
+}
+
--- /dev/null
+<?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="GtkTable" id="vbox_jabber_settings">
+ <property name="n_rows">6</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_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="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="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_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_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>
+
+ <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">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</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 encryption (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">3</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>
+
+ <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">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</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="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_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>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * 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_ACCOUNT_WIDGET_JABBER_H__
+#define __EMPATHY_ACCOUNT_WIDGET_JABBER_H__
+
+#include <libmissioncontrol/mc-account.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *empathy_account_widget_jabber_new (McAccount *account);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_ACCOUNT_WIDGET_JABBER_H__ */
--- /dev/null
+/* -*- 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>
+ */
+
+#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 <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-generic.h"
+#include "empathy-account-widget-jabber.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_remove;
+ GtkWidget *button_connect;
+
+ GtkWidget *frame_new_account;
+ GtkWidget *combobox_profile;
+ GtkWidget *entry_name;
+ GtkWidget *table_new_account;
+ 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_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,
+ TelepathyConnectionStatus status,
+ McPresence presence,
+ TelepathyConnectionStatusReason reason,
+ const gchar *unique_name,
+ EmpathyAccountsDialog *dialog);
+static void accounts_dialog_entry_name_changed_cb (GtkWidget *widget,
+ 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_connect_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_treeview_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ 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;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GList *accounts, *l;
+
+ view = GTK_TREE_VIEW (dialog->treeview);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
+ selection = gtk_tree_view_get_selection (view);
+
+ accounts = mc_accounts_list ();
+
+ for (l = accounts; l; l = l->next) {
+ McAccount *account;
+ const gchar *name;
+ TelepathyConnectionStatus status;
+
+ account = l->data;
+
+ name = mc_account_get_display_name (account);
+ if (!name) {
+ continue;
+ }
+
+ status = mission_control_get_connection_status (dialog->mc, account, NULL);
+
+ gtk_list_store_insert_with_values (store, &iter,
+ -1,
+ COL_NAME, name,
+ COL_STATUS, status,
+ COL_ACCOUNT_POINTER, account,
+ -1);
+
+ accounts_dialog_status_changed_cb (dialog->mc,
+ status,
+ MC_PRESENCE_UNSET,
+ TP_CONN_STATUS_REASON_NONE_SPECIFIED,
+ mc_account_get_unique_name (account),
+ dialog);
+
+ g_object_unref (account);
+ }
+
+ g_list_free (accounts);
+}
+
+static void
+accounts_dialog_update_connect_button (EmpathyAccountsDialog *dialog)
+{
+ McAccount *account;
+ GtkWidget *image;
+ const gchar *stock_id;
+ const gchar *label;
+
+ account = accounts_dialog_model_get_selected (dialog);
+
+ if (!account) {
+ gtk_widget_set_sensitive (dialog->button_connect, FALSE);
+ return;
+ }
+
+ if (mc_account_is_enabled (account)) {
+ label = _("Disable");
+ stock_id = GTK_STOCK_DISCONNECT;
+ } else {
+ label = _("Enable");
+ stock_id = GTK_STOCK_CONNECT;
+ }
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+
+ gtk_button_set_label (GTK_BUTTON (dialog->button_connect), label);
+ gtk_button_set_image (GTK_BUTTON (dialog->button_connect), image);
+}
+
+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;
+
+ gtk_widget_show (dialog->frame_no_account);
+ gtk_widget_hide (dialog->vbox_details);
+
+ gtk_widget_set_sensitive (dialog->button_connect, FALSE);
+ gtk_widget_set_sensitive (dialog->button_remove, FALSE);
+
+ view = GTK_TREE_VIEW (dialog->treeview);
+ model = gtk_tree_view_get_model (view);
+
+ 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>"));
+ gtk_label_set_markup (GTK_LABEL (dialog->label_no_account_blurb),
+ _("To add a new account, you can click on the "
+ "'Add' button and a new entry will be created "
+ "for you to start configuring.\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>"));
+ gtk_label_set_markup (GTK_LABEL (dialog->label_no_account_blurb),
+ _("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 {
+ 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);
+
+ if (strcmp (config_ui, "jabber") == 0) {
+ dialog->settings_widget =
+ empathy_account_widget_jabber_new (account);
+ } else {
+ dialog->settings_widget =
+ empathy_account_widget_generic_new (account,
+ dialog->label_name);
+ }
+
+ gtk_widget_grab_focus (dialog->settings_widget);
+ }
+
+ if (dialog->settings_widget) {
+ gtk_container_add (GTK_CONTAINER (dialog->alignment_settings),
+ dialog->settings_widget);
+ }
+
+ if (account) {
+ McProfile *profile;
+
+ 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_label_set_text (GTK_LABEL (dialog->label_type),
+ mc_profile_get_display_name (profile));
+ gtk_label_set_text (GTK_LABEL (dialog->label_name),
+ mc_account_get_display_name (account));
+ }
+}
+
+static void
+accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
+{
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ store = gtk_list_store_new (COL_COUNT,
+ 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_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);
+
+ /* account name/status */
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Accounts"));
+
+ 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);
+
+ 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_NAME);
+
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (view, column);
+}
+
+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;
+ TelepathyConnectionStatus 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_CONN_STATUS_DISCONNECTED ||
+ (status == TP_CONN_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 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)
+{
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean ok;
+
+ view = GTK_TREE_VIEW (dialog->treeview);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (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) {
+ gtk_tree_selection_select_iter (selection, &iter);
+ break;
+ }
+ }
+}
+
+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_remove, is_selection);
+ gtk_widget_set_sensitive (dialog->button_connect, is_selection);
+
+ accounts_dialog_update_connect_button (dialog);
+
+ account = accounts_dialog_model_get_selected (dialog);
+ accounts_dialog_update_account (dialog, account);
+
+ if (account) {
+ g_object_unref (account);
+ }
+}
+
+static void
+accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
+ McAccount *account)
+{
+ TelepathyConnectionStatus status;
+ const gchar *name;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gboolean ok;
+
+ view = GTK_TREE_VIEW (dialog->treeview);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+
+ 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;
+ }
+ }
+
+ status = mission_control_get_connection_status (dialog->mc, account, NULL);
+ name = mc_account_get_display_name (account);
+
+ g_return_if_fail (name != NULL);
+
+ empathy_debug (DEBUG_DOMAIN, "Adding new account: %s", name);
+
+ gtk_list_store_insert_with_values (store, &iter,
+ -1,
+ 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,
+ TelepathyConnectionStatus status,
+ McPresence presence,
+ TelepathyConnectionStatusReason reason,
+ const gchar *unique_name,
+ EmpathyAccountsDialog *dialog)
+{
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean ok;
+ McAccount *account;
+ GList *accounts, *l;
+ gboolean found = FALSE;
+
+ /* 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);
+ account = mc_account_lookup (unique_name);
+
+ empathy_debug (DEBUG_DOMAIN, "Status changed for account %s: "
+ "status=%d presence=%d",
+ unique_name, status, presence);
+
+ 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) {
+ 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);
+
+ break;
+ }
+ }
+
+ g_object_unref (account);
+
+ /* Start to flash account if status is connecting */
+ if (status == TP_CONN_STATUS_CONNECTING) {
+ if (!dialog->connecting_id) {
+ dialog->connecting_id = g_timeout_add (FLASH_TIMEOUT,
+ (GSourceFunc) accounts_dialog_flash_connecting_cb,
+ dialog);
+ }
+
+ return;
+ }
+
+ /* Stop to flash if no account is connecting */
+ accounts = mc_accounts_list ();
+ for (l = accounts; l; l = l->next) {
+ McAccount *this_account;
+
+ this_account = l->data;
+
+ if (mission_control_get_connection_status (mc, this_account, NULL) == TP_CONN_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;
+ }
+
+ gtk_widget_show (dialog->window);
+}
+
+static void
+accounts_dialog_entry_name_changed_cb (GtkWidget *widget,
+ EmpathyAccountsDialog *dialog)
+{
+ const gchar *str;
+
+ str = gtk_entry_get_text (GTK_ENTRY (widget));
+ gtk_widget_set_sensitive (dialog->button_create, !G_STR_EMPTY (str));
+}
+
+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 = gtk_entry_get_text (GTK_ENTRY (dialog->entry_name));
+ 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);
+
+ account = accounts_dialog_model_get_selected (dialog);
+ accounts_dialog_update_account (dialog, account);
+}
+
+static void
+accounts_dialog_button_connect_clicked_cb (GtkWidget *button,
+ EmpathyAccountsDialog *dialog)
+{
+ McAccount *account;
+ gboolean enable;
+
+ account = accounts_dialog_model_get_selected (dialog);
+ enable = (!mc_account_is_enabled (account));
+ mc_account_set_enabled (account, enable);
+ accounts_dialog_update_connect_button (dialog);
+
+ g_object_unref (account);
+}
+
+static void
+accounts_dialog_button_add_clicked_cb (GtkWidget *button,
+ EmpathyAccountsDialog *dialog)
+{
+ 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_entry_set_text (GTK_ENTRY (dialog->entry_name), "");
+ gtk_widget_grab_focus (dialog->entry_name);
+}
+
+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_treeview_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ EmpathyAccountsDialog *dialog)
+{
+
+ accounts_dialog_button_connect_clicked_cb (dialog->button_connect,
+ 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);
+ 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,
+ "entry_name", &dialog->entry_name,
+ "table_new_account", &dialog->table_new_account,
+ "button_create", &dialog->button_create,
+ "button_back", &dialog->button_back,
+ "image_type", &dialog->image_type,
+ "label_type", &dialog->label_type,
+ "label_name", &dialog->label_name,
+ "button_remove", &dialog->button_remove,
+ "button_connect", &dialog->button_connect,
+ "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,
+ "entry_name", "changed", accounts_dialog_entry_name_changed_cb,
+ "treeview", "row-activated", accounts_dialog_treeview_row_activated_cb,
+ "button_connect", "clicked", accounts_dialog_button_connect_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_table_attach_defaults (GTK_TABLE (dialog->table_new_account),
+ dialog->combobox_profile,
+ 1, 2,
+ 0, 1);
+ gtk_widget_show (dialog->combobox_profile);
+
+ /* Set up signalling */
+ dialog->mc = empathy_mission_control_new ();
+ dialog->monitor = mc_account_monitor_new ();
+
+ /* FIXME: connect account-enabled/disabled too */
+ 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);
+ 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;
+}
+
--- /dev/null
+<?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="accounts_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Accounts</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_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_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_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="GtkHBox" id="hbox146">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">18</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox195">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</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>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="treeview">
+ <property name="height_request">200</property>
+ <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="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox196">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="button_connect">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-connect</property>
+ <property name="use_stock">False</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>
+
+ <child>
+ <widget class="GtkHBox" id="hbox148">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">6</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="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">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_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="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">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="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>
+ <packing>
+ <property name="padding">0</property>
+ <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="homogeneous">False</property>
+ <property name="spacing">18</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox_details">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">18</property>
+
+ <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="alignment7">
+ <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">20</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox213">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</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="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+
+ <child>
+ <widget class="GtkImage" id="image_type">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cut</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="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</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_name">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Imendio </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">0</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="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label_type">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Jabber</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="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>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label598">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Account</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="frame2">
+ <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="alignment_settings">
+ <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">20</property>
+ <property name="right_padding">0</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_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="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame_new_account">
+ <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="alignment29">
+ <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">20</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox216">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkTable" id="table_new_account">
+ <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="GtkLabel" id="label640">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Type:</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">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label638">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_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_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"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="entry_name">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">A unique name for this account to identify it personally to you.</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>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox181">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkButton" id="button_create">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</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">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="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</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="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Cr_eate</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>
+ <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_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="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>
+ <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>
+ <property name="pack_type">GTK_PACK_END</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_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">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame_no_account">
+ <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="alignment21">
+ <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="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_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.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>
+ </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_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="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>
--- /dev/null
+/* -*- 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>
+ */
+
+#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__ */
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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__ */
--- /dev/null
+/* -*- 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);
+}
--- /dev/null
+/* -*- 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__ */
--- /dev/null
+/* -*- 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 <libmissioncontrol/mc-account.h>
+
+#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-debug.h>
+#include <libempathy/empathy-conf.h>
+
+#include "empathy-chat-view.h"
+#include "empathy-chat.h"
+#include "empathy-preferences.h"
+#include "empathy-theme-manager.h"
+#include "empathy-ui-utils.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))
+
+typedef enum {
+ BLOCK_TYPE_NONE,
+ BLOCK_TYPE_SELF,
+ BLOCK_TYPE_OTHER,
+ BLOCK_TYPE_EVENT,
+ BLOCK_TYPE_TIME,
+ BLOCK_TYPE_INVITE
+} BlockType;
+
+struct _EmpathyChatViewPriv {
+ GtkTextBuffer *buffer;
+
+ gboolean irc_style;
+ time_t last_timestamp;
+ BlockType 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;
+};
+
+typedef struct {
+ EmpathySmiley smiley;
+ const gchar *pattern;
+} EmpathySmileyPattern;
+
+static const EmpathySmileyPattern smileys[] = {
+ /* Forward smileys. */
+ { EMPATHY_SMILEY_NORMAL, ":)" },
+ { EMPATHY_SMILEY_WINK, ";)" },
+ { EMPATHY_SMILEY_WINK, ";-)" },
+ { EMPATHY_SMILEY_BIGEYE, "=)" },
+ { EMPATHY_SMILEY_NOSE, ":-)" },
+ { EMPATHY_SMILEY_CRY, ":'(" },
+ { EMPATHY_SMILEY_SAD, ":(" },
+ { EMPATHY_SMILEY_SAD, ":-(" },
+ { EMPATHY_SMILEY_SCEPTICAL, ":/" },
+ { EMPATHY_SMILEY_SCEPTICAL, ":\\" },
+ { EMPATHY_SMILEY_BIGSMILE, ":D" },
+ { EMPATHY_SMILEY_BIGSMILE, ":-D" },
+ { EMPATHY_SMILEY_INDIFFERENT, ":|" },
+ { EMPATHY_SMILEY_TOUNGE, ":p" },
+ { EMPATHY_SMILEY_TOUNGE, ":-p" },
+ { EMPATHY_SMILEY_TOUNGE, ":P" },
+ { EMPATHY_SMILEY_TOUNGE, ":-P" },
+ { EMPATHY_SMILEY_TOUNGE, ";p" },
+ { EMPATHY_SMILEY_TOUNGE, ";-p" },
+ { EMPATHY_SMILEY_TOUNGE, ";P" },
+ { EMPATHY_SMILEY_TOUNGE, ";-P" },
+ { EMPATHY_SMILEY_SHOCKED, ":o" },
+ { EMPATHY_SMILEY_SHOCKED, ":-o" },
+ { EMPATHY_SMILEY_SHOCKED, ":O" },
+ { EMPATHY_SMILEY_SHOCKED, ":-O" },
+ { EMPATHY_SMILEY_COOL, "8)" },
+ { EMPATHY_SMILEY_COOL, "B)" },
+ { EMPATHY_SMILEY_SORRY, "*|" },
+ { EMPATHY_SMILEY_KISS, ":*" },
+ { EMPATHY_SMILEY_SHUTUP, ":#" },
+ { EMPATHY_SMILEY_SHUTUP, ":-#" },
+ { EMPATHY_SMILEY_YAWN, "|O" },
+ { EMPATHY_SMILEY_CONFUSED, ":S" },
+ { EMPATHY_SMILEY_CONFUSED, ":s" },
+ { EMPATHY_SMILEY_ANGEL, "<)" },
+ { EMPATHY_SMILEY_OOOH, ":x" },
+ { EMPATHY_SMILEY_LOOKAWAY, "*)" },
+ { EMPATHY_SMILEY_LOOKAWAY, "*-)" },
+ { EMPATHY_SMILEY_BLUSH, "*S" },
+ { EMPATHY_SMILEY_BLUSH, "*s" },
+ { EMPATHY_SMILEY_BLUSH, "*$" },
+ { EMPATHY_SMILEY_COOLBIGSMILE, "8D" },
+ { EMPATHY_SMILEY_ANGRY, ":@" },
+ { EMPATHY_SMILEY_BOSS, "@)" },
+ { EMPATHY_SMILEY_MONKEY, "#)" },
+ { EMPATHY_SMILEY_SILLY, "O)" },
+ { EMPATHY_SMILEY_SICK, "+o(" },
+
+ /* Backward smileys. */
+ { EMPATHY_SMILEY_NORMAL, "(:" },
+ { EMPATHY_SMILEY_WINK, "(;" },
+ { EMPATHY_SMILEY_WINK, "(-;" },
+ { EMPATHY_SMILEY_BIGEYE, "(=" },
+ { EMPATHY_SMILEY_NOSE, "(-:" },
+ { EMPATHY_SMILEY_CRY, ")':" },
+ { EMPATHY_SMILEY_SAD, "):" },
+ { EMPATHY_SMILEY_SAD, ")-:" },
+ { EMPATHY_SMILEY_SCEPTICAL, "/:" },
+ { EMPATHY_SMILEY_SCEPTICAL, "//:" },
+ { EMPATHY_SMILEY_INDIFFERENT, "|:" },
+ { EMPATHY_SMILEY_TOUNGE, "d:" },
+ { EMPATHY_SMILEY_TOUNGE, "d-:" },
+ { EMPATHY_SMILEY_TOUNGE, "d;" },
+ { EMPATHY_SMILEY_TOUNGE, "d-;" },
+ { EMPATHY_SMILEY_SHOCKED, "o:" },
+ { EMPATHY_SMILEY_SHOCKED, "O:" },
+ { EMPATHY_SMILEY_COOL, "(8" },
+ { EMPATHY_SMILEY_COOL, "(B" },
+ { EMPATHY_SMILEY_SORRY, "|*" },
+ { EMPATHY_SMILEY_KISS, "*:" },
+ { EMPATHY_SMILEY_SHUTUP, "#:" },
+ { EMPATHY_SMILEY_SHUTUP, "#-:" },
+ { EMPATHY_SMILEY_YAWN, "O|" },
+ { EMPATHY_SMILEY_CONFUSED, "S:" },
+ { EMPATHY_SMILEY_CONFUSED, "s:" },
+ { EMPATHY_SMILEY_ANGEL, "(>" },
+ { EMPATHY_SMILEY_OOOH, "x:" },
+ { EMPATHY_SMILEY_LOOKAWAY, "(*" },
+ { EMPATHY_SMILEY_LOOKAWAY, "(-*" },
+ { EMPATHY_SMILEY_BLUSH, "S*" },
+ { EMPATHY_SMILEY_BLUSH, "s*" },
+ { EMPATHY_SMILEY_BLUSH, "$*" },
+ { EMPATHY_SMILEY_ANGRY, "@:" },
+ { EMPATHY_SMILEY_BOSS, "(@" },
+ { EMPATHY_SMILEY_MONKEY, "#)" },
+ { EMPATHY_SMILEY_SILLY, "(O" },
+ { EMPATHY_SMILEY_SICK, ")o+" }
+};
+
+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 void chat_view_insert_text_with_emoticons (GtkTextBuffer *buf,
+ GtkTextIter *iter,
+ const gchar *str);
+static gboolean chat_view_is_scrolled_down (EmpathyChatView *view);
+static void chat_view_theme_changed_cb (EmpathyThemeManager *manager,
+ EmpathyChatView *view);
+static void chat_view_maybe_append_date_and_time (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static void chat_view_append_spacing (EmpathyChatView *view);
+static void chat_view_append_text (EmpathyChatView *view,
+ const gchar *body,
+ const gchar *tag);
+static void chat_view_maybe_append_fancy_header (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static void chat_view_append_irc_action (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static void chat_view_append_fancy_action (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static void chat_view_append_irc_message (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static void chat_view_append_fancy_message (EmpathyChatView *view,
+ EmpathyMessage *msg);
+static GdkPixbuf *chat_view_pad_to_size (GdkPixbuf *pixbuf,
+ gint width,
+ gint height,
+ gint extra_padding_right);
+
+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 = BLOCK_TYPE_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_manager_update_show_avatars (empathy_theme_manager_get (),
+ view, 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);
+ }
+
+ 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;
+ gboolean show_avatars = FALSE;
+
+ view = user_data;
+
+ 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_manager_update_show_avatars (empathy_theme_manager_get (),
+ view, 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_manager_update_show_avatars (empathy_theme_manager_get (),
+ view, 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 void
+chat_view_insert_text_with_emoticons (GtkTextBuffer *buf,
+ GtkTextIter *iter,
+ const gchar *str)
+{
+ const gchar *p;
+ gunichar c, prev_c;
+ gint i;
+ gint match;
+ gint submatch;
+ gboolean use_smileys = FALSE;
+
+ 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;
+ }
+
+ while (*str) {
+ gint smileys_index[G_N_ELEMENTS (smileys)];
+ GdkPixbuf *pixbuf;
+ gint len;
+ const gchar *start;
+
+ memset (smileys_index, 0, sizeof (smileys_index));
+
+ match = -1;
+ submatch = -1;
+ p = str;
+ prev_c = 0;
+
+ while (*p) {
+ c = g_utf8_get_char (p);
+
+ if (match != -1 && g_unichar_isspace (c)) {
+ break;
+ } else {
+ match = -1;
+ }
+
+ if (submatch != -1 || prev_c == 0 || g_unichar_isspace (prev_c)) {
+ submatch = -1;
+
+ for (i = 0; i < G_N_ELEMENTS (smileys); i++) {
+ /* Only try to match if we already have
+ * a beginning match for the pattern, or
+ * if it's the first character in the
+ * pattern, if it's not in the middle of
+ * a word.
+ */
+ if (((smileys_index[i] == 0 && (prev_c == 0 || g_unichar_isspace (prev_c))) ||
+ smileys_index[i] > 0) &&
+ smileys[i].pattern[smileys_index[i]] == c) {
+ submatch = i;
+
+ smileys_index[i]++;
+ if (!smileys[i].pattern[smileys_index[i]]) {
+ match = i;
+ }
+ } else {
+ smileys_index[i] = 0;
+ }
+ }
+ }
+
+ prev_c = c;
+ p = g_utf8_next_char (p);
+ }
+
+ if (match == -1) {
+ gtk_text_buffer_insert (buf, iter, str, -1);
+ return;
+ }
+
+ start = p - strlen (smileys[match].pattern);
+
+ if (start > str) {
+ len = start - str;
+ gtk_text_buffer_insert (buf, iter, str, len);
+ }
+
+ pixbuf = empathy_chat_view_get_smiley_image (smileys[match].smiley);
+ gtk_text_buffer_insert_pixbuf (buf, iter, pixbuf);
+
+ gtk_text_buffer_insert (buf, iter, " ", 1);
+
+ str = g_utf8_find_next_char (p, NULL);
+ }
+}
+
+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_maybe_append_date_and_time (EmpathyChatView *view,
+ EmpathyMessage *msg)
+{
+ EmpathyChatViewPriv *priv;
+ const gchar *tag;
+ time_t timestamp;
+ GDate *date, *last_date;
+ GtkTextIter iter;
+ gboolean append_date, append_time;
+ GString *str;
+
+ priv = GET_PRIV (view);
+
+ if (priv->irc_style) {
+ tag = "irc-time";
+ } else {
+ tag = "fancy-time";
+ }
+
+ if (priv->last_block_type == BLOCK_TYPE_TIME) {
+ return;
+ }
+
+ str = g_string_new (NULL);
+
+ timestamp = 0;
+ if (msg) {
+ timestamp = empathy_message_get_timestamp (msg);
+ }
+
+ if (timestamp <= 0) {
+ timestamp = empathy_time_get_current ();
+ }
+
+ date = g_date_new ();
+ g_date_set_time (date, timestamp);
+
+ last_date = g_date_new ();
+ g_date_set_time (last_date, priv->last_timesta