aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac20
-rw-r--r--data/.gitignore1
-rw-r--r--data/Makefile.am11
-rw-r--r--data/canonical-logo.pngbin0 -> 4272 bytes
-rw-r--r--data/icons/Makefile.am6
-rw-r--r--data/icons/hicolor_apps_16x16_im-skype.pngbin0 -> 364 bytes
-rw-r--r--data/icons/hicolor_apps_22x22_im-skype.pngbin0 -> 438 bytes
-rw-r--r--data/icons/hicolor_apps_24x24_im-skype.pngbin0 -> 403 bytes
-rw-r--r--data/icons/hicolor_apps_32x32_im-skype.pngbin0 -> 557 bytes
-rw-r--r--data/icons/hicolor_apps_48x48_im-skype.pngbin0 -> 698 bytes
-rw-r--r--data/icons/hicolor_apps_scalable_im-skype.svg76
-rw-r--r--data/org.gnome.Empathy.gschema.xml.in5
-rw-r--r--data/plugged-into-skype.pngbin0 -> 4045 bytes
-rw-r--r--data/skype-eula.txt1
-rw-r--r--extensions/Account_Interface_External_Password_Storage.xml52
-rw-r--r--extensions/Connection_Interface_Privacy_Settings.xml123
-rw-r--r--extensions/Makefile.am2
-rw-r--r--extensions/misc.xml2
-rw-r--r--libempathy-gtk/Makefile.am3
-rw-r--r--libempathy-gtk/empathy-account-chooser.c41
-rw-r--r--libempathy-gtk/empathy-account-chooser.h4
-rw-r--r--libempathy-gtk/empathy-account-widget-private.h46
-rw-r--r--libempathy-gtk/empathy-account-widget-skype.c1064
-rw-r--r--libempathy-gtk/empathy-account-widget-skype.h38
-rw-r--r--libempathy-gtk/empathy-account-widget-skype.ui588
-rw-r--r--libempathy-gtk/empathy-account-widget.c74
-rw-r--r--libempathy-gtk/empathy-call-utils.c2
-rw-r--r--libempathy-gtk/empathy-chat-text-view.c41
-rw-r--r--libempathy-gtk/empathy-chat-text-view.h2
-rw-r--r--libempathy-gtk/empathy-chat.c211
-rw-r--r--libempathy-gtk/empathy-chat.h5
-rw-r--r--libempathy-gtk/empathy-contact-blocking-dialog.ui10
-rw-r--r--libempathy-gtk/empathy-contact-selector-dialog.c25
-rw-r--r--libempathy-gtk/empathy-contact-widget.c6
-rw-r--r--libempathy-gtk/empathy-images.h5
-rw-r--r--libempathy-gtk/empathy-individual-menu.c65
-rw-r--r--libempathy-gtk/empathy-individual-menu.h5
-rw-r--r--libempathy-gtk/empathy-individual-view.c18
-rw-r--r--libempathy-gtk/empathy-individual-widget.c6
-rw-r--r--libempathy-gtk/empathy-location-manager.c12
-rw-r--r--libempathy-gtk/empathy-log-window.c3819
-rw-r--r--libempathy-gtk/empathy-log-window.h12
-rw-r--r--libempathy-gtk/empathy-log-window.ui449
-rw-r--r--libempathy-gtk/empathy-new-message-dialog.c104
-rw-r--r--libempathy-gtk/empathy-theme-adium.c6
-rw-r--r--libempathy-gtk/empathy-theme-boxes.c2
-rw-r--r--libempathy/Makefile.am2
-rw-r--r--libempathy/action-chain-internal.h51
-rw-r--r--libempathy/action-chain.c192
-rw-r--r--libempathy/empathy-contact.c43
-rw-r--r--libempathy/empathy-contact.h3
-rw-r--r--libempathy/empathy-dispatcher.c51
-rw-r--r--libempathy/empathy-dispatcher.h5
-rw-r--r--libempathy/empathy-ft-handler.c4
-rw-r--r--libempathy/empathy-message.c247
-rw-r--r--libempathy/empathy-message.h21
-rw-r--r--libempathy/empathy-time.c122
-rw-r--r--libempathy/empathy-time.h10
-rw-r--r--libempathy/empathy-tp-chat.c541
-rw-r--r--libempathy/empathy-tp-chat.h12
-rw-r--r--libempathy/empathy-tp-file.c2
-rw-r--r--libempathy/empathy-utils.c169
-rw-r--r--libempathy/empathy-utils.h33
-rw-r--r--po/POTFILES.in3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/empathy-account-assistant.c15
-rw-r--r--src/empathy-accounts-dialog.c36
-rw-r--r--src/empathy-call-handler.c13
-rw-r--r--src/empathy-call-window.c113
-rw-r--r--src/empathy-chat-manager.c20
-rw-r--r--src/empathy-chat-window.c123
-rw-r--r--src/empathy-chat-window.h3
-rw-r--r--src/empathy-event-manager.c2
-rw-r--r--src/empathy-main-window.c346
-rw-r--r--src/empathy-main-window.ui29
-rw-r--r--src/empathy-map-view.c16
m---------telepathy-yell0
77 files changed, 7021 insertions, 2170 deletions
diff --git a/configure.ac b/configure.ac
index 6986cd6a7..c151b3a3f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,7 @@ LIBCANBERRA_GTK_REQUIRED=0.4
LIBNOTIFY_REQUIRED=0.7.0
TELEPATHY_FARSIGHT_REQUIRED=0.0.14
TELEPATHY_GLIB_REQUIRED=0.14.1
-TELEPATHY_LOGGER=0.2.0
+TELEPATHY_LOGGER=0.2.8
UNIQUE_REQUIRED=1.1.2
# Optionnal deps
@@ -59,8 +59,11 @@ GNOME_CONTROL_CENTER_GTK3_REQUIRED=2.31.4
# telepathy-yell
prev_top_build_prefix=$ac_top_build_prefix
+prev_ac_configure_args=$ac_configure_args
+ac_configure_args="$ac_configure_args --disable-shared-library"
AX_CONFIG_DIR([telepathy-yell])
ac_top_build_prefix=$prev_top_build_prefix
+ac_configure_args=$prev_ac_configure_args
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:"$ac_top_build_prefix"telepathy-yell/telepathy-yell
AC_CONFIG_MACRO_DIR([m4])
@@ -197,6 +200,21 @@ if test "x$with_call" = "xyes" -a "x$have_farstream" != "xyes"; then
fi
AM_CONDITIONAL(HAVE_CALL, test "x$have_farstream" = "xyes")
+
+# -----------------------------------------------------------
+# Call support in tp-logger
+# -----------------------------------------------------------
+SAVE_CFLAGS=$CFLAGS
+SAVE_CPPFLAGS=$CPPFLAGS
+CFLAGS="$CFLAGS $EMPATHY_CFLAGS"
+CPPFLAGS="$CPPFLAGS $EMPATHY_CFLAGS"
+
+AC_CHECK_HEADER(telepathy-logger/call-event.h,,
+ AC_MSG_ERROR([tp-logger must be compiled with --enable-call]))
+
+CFLAGS=$SAVE_CFLAGS
+CPPFLAGS=$SAVE_CPPFLAGS
+
# -----------------------------------------------------------
# evolution-data-server (about-me)
# -----------------------------------------------------------
diff --git a/data/.gitignore b/data/.gitignore
index 7ec3d4d81..902e80747 100644
--- a/data/.gitignore
+++ b/data/.gitignore
@@ -7,3 +7,4 @@ org.freedesktop.Telepathy.Client.Empathy.AudioVideo.service
empathy-accounts.desktop
empathy-accounts.desktop.in
org.freedesktop.Telepathy.Client.Empathy.Auth.service
+org.freedesktop.Telepathy.Client.Empathy.Call.service
diff --git a/data/Makefile.am b/data/Makefile.am
index a0c715550..b7fb23e9d 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -55,6 +55,15 @@ clientfile_DATA = \
htmldir = $(datadir)/empathy
html_DATA = Template.html
+pixmapsdir = $(datadir)/empathy
+pixmaps_DATA = \
+ canonical-logo.png \
+ plugged-into-skype.png \
+ $(NULL)
+
+euladir = $(datadir)/empathy
+eula_DATA = skype-eula.txt
+
EXTRA_DIST = \
$(convert_DATA) \
$(desktop_in_files) \
@@ -65,6 +74,8 @@ EXTRA_DIST = \
$(clientfile_DATA) \
$(servicefile_in_files) \
$(gsettings_files) \
+ $(pixmaps_DATA) \
+ $(eula_DATA) \
$(NULL)
DISTCLEANFILES = \
diff --git a/data/canonical-logo.png b/data/canonical-logo.png
new file mode 100644
index 000000000..3a1804128
--- /dev/null
+++ b/data/canonical-logo.png
Binary files differ
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
index 2e38cc542..d74a9c900 100644
--- a/data/icons/Makefile.am
+++ b/data/icons/Makefile.am
@@ -44,6 +44,7 @@ private_icons = \
hicolor_apps_16x16_im-mxit.png \
hicolor_apps_16x16_im-myspace.png \
hicolor_apps_16x16_im-sametime.png \
+ hicolor_apps_16x16_im-skype.png \
hicolor_apps_16x16_im-yahoo.png \
hicolor_apps_16x16_im-zephyr.png \
hicolor_apps_22x22_im-aim.png \
@@ -65,6 +66,7 @@ private_icons = \
hicolor_apps_22x22_im-mxit.png \
hicolor_apps_22x22_im-myspace.png \
hicolor_apps_22x22_im-sametime.png \
+ hicolor_apps_22x22_im-skype.png \
hicolor_apps_22x22_im-yahoo.png \
hicolor_apps_22x22_im-zephyr.png \
hicolor_apps_24x24_im-aim.png \
@@ -85,6 +87,7 @@ private_icons = \
hicolor_apps_24x24_im-mxit.png \
hicolor_apps_24x24_im-myspace.png \
hicolor_apps_24x24_im-sametime.png \
+ hicolor_apps_24x24_im-skype.png \
hicolor_apps_24x24_im-yahoo.png \
hicolor_apps_24x24_im-zephyr.png \
hicolor_apps_32x32_im-aim.png \
@@ -105,6 +108,7 @@ private_icons = \
hicolor_apps_32x32_im-mxit.png \
hicolor_apps_32x32_im-myspace.png \
hicolor_apps_32x32_im-sametime.png \
+ hicolor_apps_32x32_im-skype.png \
hicolor_apps_32x32_im-yahoo.png \
hicolor_apps_32x32_im-zephyr.png \
hicolor_apps_48x48_im-aim.png \
@@ -126,6 +130,7 @@ private_icons = \
hicolor_apps_48x48_im-mxit.png \
hicolor_apps_48x48_im-myspace.png \
hicolor_apps_48x48_im-sametime.png \
+ hicolor_apps_48x48_im-skype.png \
hicolor_apps_48x48_im-yahoo.png \
hicolor_apps_48x48_im-zephyr.png \
hicolor_apps_scalable_im-aim.svg \
@@ -146,6 +151,7 @@ private_icons = \
hicolor_apps_scalable_im-mxit.svg \
hicolor_apps_scalable_im-myspace.svg \
hicolor_apps_scalable_im-sametime.svg \
+ hicolor_apps_scalable_im-skype.svg \
hicolor_apps_scalable_im-yahoo.svg \
hicolor_apps_scalable_im-zephyr.svg \
hicolor_status_16x16_user-available.png \
diff --git a/data/icons/hicolor_apps_16x16_im-skype.png b/data/icons/hicolor_apps_16x16_im-skype.png
new file mode 100644
index 000000000..186d8c2f6
--- /dev/null
+++ b/data/icons/hicolor_apps_16x16_im-skype.png
Binary files differ
diff --git a/data/icons/hicolor_apps_22x22_im-skype.png b/data/icons/hicolor_apps_22x22_im-skype.png
new file mode 100644
index 000000000..239f697cd
--- /dev/null
+++ b/data/icons/hicolor_apps_22x22_im-skype.png
Binary files differ
diff --git a/data/icons/hicolor_apps_24x24_im-skype.png b/data/icons/hicolor_apps_24x24_im-skype.png
new file mode 100644
index 000000000..d4e78cafb
--- /dev/null
+++ b/data/icons/hicolor_apps_24x24_im-skype.png
Binary files differ
diff --git a/data/icons/hicolor_apps_32x32_im-skype.png b/data/icons/hicolor_apps_32x32_im-skype.png
new file mode 100644
index 000000000..80721f871
--- /dev/null
+++ b/data/icons/hicolor_apps_32x32_im-skype.png
Binary files differ
diff --git a/data/icons/hicolor_apps_48x48_im-skype.png b/data/icons/hicolor_apps_48x48_im-skype.png
new file mode 100644
index 000000000..a97b6c13d
--- /dev/null
+++ b/data/icons/hicolor_apps_48x48_im-skype.png
Binary files differ
diff --git a/data/icons/hicolor_apps_scalable_im-skype.svg b/data/icons/hicolor_apps_scalable_im-skype.svg
new file mode 100644
index 000000000..3b07a1cf2
--- /dev/null
+++ b/data/icons/hicolor_apps_scalable_im-skype.svg
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2991"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="hicolor_apps_scalable_im-skype.svg">
+ <defs
+ id="defs2993" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="-30.428571"
+ inkscape:cy="35.428571"
+ inkscape:current-layer="svg2991"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1143"
+ inkscape:window-x="1280"
+ inkscape:window-y="26"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2996">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Emilio Pozuelo Monfort</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>Collabora Ltd.</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#00aeef;fill-opacity:1"
+ id="path2999"
+ sodipodi:cx="23.142857"
+ sodipodi:cy="26.285715"
+ sodipodi:rx="13.857142"
+ sodipodi:ry="13.857142"
+ d="m 36.999999,26.285715 a 13.857142,13.857142 0 1 1 -27.7142849,0 13.857142,13.857142 0 1 1 27.7142849,0 z"
+ transform="translate(0.71428571,-1.2857143)" />
+ </g>
+</svg>
diff --git a/data/org.gnome.Empathy.gschema.xml.in b/data/org.gnome.Empathy.gschema.xml.in
index 27a8cb375..10001507d 100644
--- a/data/org.gnome.Empathy.gschema.xml.in
+++ b/data/org.gnome.Empathy.gschema.xml.in
@@ -55,6 +55,11 @@
<_summary>Show protocols</_summary>
<_description>Whether to show protocols for contacts in the contact list.</_description>
</key>
+ <key name="show-balance-in-roster" type="b">
+ <default>false</default>
+ <_summary>Show Balance in roster</_summary>
+ <_description>Whether to show account balances for in the contact roster.</_description>
+ </key>
<key name="compact-contact-list" type="b">
<default>false</default>
<_summary>Compact contact list</_summary>
diff --git a/data/plugged-into-skype.png b/data/plugged-into-skype.png
new file mode 100644
index 000000000..670eda7ba
--- /dev/null
+++ b/data/plugged-into-skype.png
Binary files differ
diff --git a/data/skype-eula.txt b/data/skype-eula.txt
new file mode 100644
index 000000000..2b70dd2ca
--- /dev/null
+++ b/data/skype-eula.txt
@@ -0,0 +1 @@
+END USER LICENSE AGREEMENT
diff --git a/extensions/Account_Interface_External_Password_Storage.xml b/extensions/Account_Interface_External_Password_Storage.xml
new file mode 100644
index 000000000..71a542428
--- /dev/null
+++ b/extensions/Account_Interface_External_Password_Storage.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" ?>
+<node name="/Account_Interface_External_Password_Storage"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+
+ <tp:copyright>Copyright © 2011 Collabora Ltd.</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+ <p>This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.</p>
+
+ <p>This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.</p>
+
+ <p>You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.</p>
+ </tp:license>
+
+ <interface name="org.freedesktop.Telepathy.Account.Interface.ExternalPasswordStorage.DRAFT"
+ tp:causes-havoc="experimental">
+ <tp:added version="0.21.10">(draft 1)</tp:added>
+ <tp:requires interface="org.freedesktop.Telepathy.Account"/>
+
+ <tp:docstring xmlns="http://www.w3.org/1999/xhtml">
+ <p>An interface for Accounts whose passwords are stored externally and
+ SHOULD NOT be stored by either the <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy">AccountManager</tp:dbus-ref> nor
+ any <tp:dbus-ref
+ namespace="org.freedesktop.Telepathy.Channel.Type">ServerAuthentication</tp:dbus-ref>
+ handler.</p>
+ </tp:docstring>
+
+ <method name="ForgetPassword" tp:name-for-bindings="Forget_Password">
+ <tp:docstring>
+ <p>Clears any saved password associated with this account.</p>
+ </tp:docstring>
+ </method>
+
+ <property name="PasswordSaved"
+ tp:name-for-bindings="Password_Saved"
+ type="b" access="read">
+ <tp:docstring>
+ <p>Indicates whether the account has a saved password or not.</p>
+ </tp:docstring>
+ </property>
+
+ </interface>
+</node>
diff --git a/extensions/Connection_Interface_Privacy_Settings.xml b/extensions/Connection_Interface_Privacy_Settings.xml
new file mode 100644
index 000000000..47bb03270
--- /dev/null
+++ b/extensions/Connection_Interface_Privacy_Settings.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" ?>
+<node name="/Connection_Interface_Privacy_Settings"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:copyright>Copyright &#x00a9; 2010 Collabora Ltd.</tp:copyright>
+ <tp:license xmlns="http://www.w3.org/1999/xhtml">
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ </tp:license>
+
+ <interface
+ name="uk.co.Collabora.Telepathy.Psyke.Connection.Interface.PrivacySettings">
+
+ <tp:docstring>
+ Privacy Setting Connection properties for telepathy-psyke.
+ </tp:docstring>
+
+ <tp:enum name="Privacy_Setting" type="u"
+ value_prefix="Privacy_Setting">
+ <tp:docstring>
+ Ordering of these values in SkypeKit is inconsistent. Always switch
+ for the values you want.
+ </tp:docstring>
+
+ <tp:enumvalue suffix="Nobody" value="0">
+ <tp:docstring>
+ No users (not available for all properties).
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Contacts" value="1">
+ <tp:docstring>
+ Users on the contact list.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Anyone" value="2">
+ <tp:docstring>
+ All users.
+ </tp:docstring>
+ </tp:enumvalue>
+ <tp:enumvalue suffix="Known_Numbers" value="3">
+ <tp:docstring>
+ Known numbers (not available for all properties).
+ </tp:docstring>
+ </tp:enumvalue>
+ </tp:enum>
+
+ <property name="AllowTextChannelsFrom"
+ tp:name-for-bindings="Allow_Text_Channels_From"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ From whom to allow incoming Text channels.
+ </tp:docstring>
+ </property>
+
+ <property name="AllowCallChannelsFrom"
+ tp:name-for-bindings="Allow_Call_Channels_From"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ From whom to allow incoming Call channels.
+ </tp:docstring>
+ </property>
+
+ <!-- FIXME: this property is difficult to implement, disable for now
+ <property name="AllowVideoFrom"
+ tp:name-for-bindings="Allow_Video_From"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ From whom to automatically enable video.
+ </tp:docstring>
+ </property>
+ -->
+
+ <property name="AllowOutsideCallsFrom"
+ tp:name-for-bindings="Allow_Outside_Calls_From"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ From whom to receive outside calls.
+ </tp:docstring>
+ </property>
+
+ <property name="ShowMyAvatarTo"
+ tp:name-for-bindings="Show_My_Avatar_To"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ To whom to show my avatar.
+ </tp:docstring>
+ </property>
+
+ <property name="ShowMyWebStatus"
+ tp:name-for-bindings="Show_My_Web_Status"
+ type="b" access="readwrite">
+ <tp:docstring>
+ Whether or not to show my status on the web.
+ </tp:docstring>
+ </property>
+
+ <property name="ShowIHaveVideoTo"
+ tp:name-for-bindings="Show_I_Have_Video_To"
+ type="u" tp:type="Privacy_Setting"
+ access="readwrite">
+ <tp:docstring>
+ To whom I should advertise video (Nobody, Contacts, Anyone).
+ </tp:docstring>
+ </property>
+
+ </interface>
+
+</node>
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index 30adce0da..87fbca4b4 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -14,9 +14,11 @@ EXTRA_DIST = \
misc.xml \
Debug.xml \
Logger.xml \
+ Account_Interface_External_Password_Storage.xml \
Authentication_TLS_Certificate.xml \
Channel_Interface_Credentials_Storage.xml \
Channel_Type_Server_TLS_Connection.xml \
+ Connection_Interface_Privacy_Settings.xml \
$(NULL)
noinst_LTLIBRARIES = libemp-extensions.la
diff --git a/extensions/misc.xml b/extensions/misc.xml
index 6a137ef06..0686d564c 100644
--- a/extensions/misc.xml
+++ b/extensions/misc.xml
@@ -6,8 +6,10 @@
<xi:include href="Debug.xml" />
<xi:include href="Logger.xml" />
+<xi:include href="Account_Interface_External_Password_Storage.xml" />
<xi:include href="Authentication_TLS_Certificate.xml" />
<xi:include href="Channel_Interface_Credentials_Storage.xml" />
<xi:include href="Channel_Type_Server_TLS_Connection.xml" />
+<xi:include href="Connection_Interface_Privacy_Settings.xml" />
</tp:spec>
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
index 50b5dc547..28bfbe07c 100644
--- a/libempathy-gtk/Makefile.am
+++ b/libempathy-gtk/Makefile.am
@@ -35,6 +35,7 @@ libempathy_gtk_handwritten_source = \
empathy-account-widget-irc.c \
empathy-account-widget-private.h \
empathy-account-widget-sip.c \
+ empathy-account-widget-skype.c \
empathy-account-widget.c \
empathy-avatar-chooser.c \
empathy-avatar-image.c \
@@ -97,6 +98,7 @@ libempathy_gtk_headers = \
empathy-account-chooser.h \
empathy-account-widget-irc.h \
empathy-account-widget-sip.h \
+ empathy-account-widget-skype.h \
empathy-account-widget.h \
empathy-avatar-chooser.h \
empathy-avatar-image.h \
@@ -202,6 +204,7 @@ ui_DATA = \
empathy-account-widget-yahoo.ui \
empathy-account-widget-groupwise.ui \
empathy-account-widget-aim.ui \
+ empathy-account-widget-skype.ui \
empathy-status-preset-dialog.ui \
empathy-log-window.ui \
empathy-chat.ui \
diff --git a/libempathy-gtk/empathy-account-chooser.c b/libempathy-gtk/empathy-account-chooser.c
index b7eebe587..b6c4d51a1 100644
--- a/libempathy-gtk/empathy-account-chooser.c
+++ b/libempathy-gtk/empathy-account-chooser.c
@@ -416,6 +416,30 @@ empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
return data.set;
}
+void
+empathy_account_chooser_set_all (EmpathyAccountChooser *chooser)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkComboBox *combobox;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
+
+ priv = GET_PRIV (chooser);
+
+ g_return_if_fail (priv->has_all_option);
+
+ combobox = GTK_COMBO_BOX (chooser);
+ model = gtk_combo_box_get_model (combobox);
+
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ /* 'All accounts' is the first row */
+ gtk_combo_box_set_active_iter (combobox, &iter);
+ priv->account_manually_set = TRUE;
+ }
+}
+
/**
* empathy_account_chooser_get_has_all_option:
* @chooser: an #EmpathyAccountChooser
@@ -700,8 +724,15 @@ account_chooser_find_account_foreach (GtkTreeModel *model,
{
FindAccountData *data = user_data;
TpAccount *account;
+ RowType type;
- gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
+ gtk_tree_model_get (model, iter,
+ COL_ACCOUNT_POINTER, &account,
+ COL_ACCOUNT_ROW_TYPE, &type,
+ -1);
+
+ if (type != ROW_ACCOUNT)
+ return FALSE;
if (account == data->account) {
data->found = TRUE;
@@ -984,3 +1015,11 @@ empathy_account_chooser_get_account (EmpathyAccountChooser *chooser)
return account;
}
+
+TpAccountManager *
+empathy_account_chooser_get_account_manager (EmpathyAccountChooser *self)
+{
+ EmpathyAccountChooserPriv *priv = GET_PRIV (self);
+
+ return priv->manager;
+}
diff --git a/libempathy-gtk/empathy-account-chooser.h b/libempathy-gtk/empathy-account-chooser.h
index 08141f497..f3658ad7d 100644
--- a/libempathy-gtk/empathy-account-chooser.h
+++ b/libempathy-gtk/empathy-account-chooser.h
@@ -27,7 +27,7 @@
#include <gtk/gtk.h>
-#include <telepathy-glib/account.h>
+#include <telepathy-glib/telepathy-glib.h>
G_BEGIN_DECLS
@@ -81,6 +81,8 @@ TpAccount * empathy_account_chooser_get_account (EmpathyAccountChooser
TpConnection * empathy_account_chooser_get_connection (EmpathyAccountChooser *chooser);
gboolean empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
TpAccount *account);
+void empathy_account_chooser_set_all (EmpathyAccountChooser *chooser);
+TpAccountManager * empathy_account_chooser_get_account_manager (EmpathyAccountChooser *self);
gboolean empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser);
void empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
gboolean has_all_option);
diff --git a/libempathy-gtk/empathy-account-widget-private.h b/libempathy-gtk/empathy-account-widget-private.h
index d15aa550d..a23a947f1 100644
--- a/libempathy-gtk/empathy-account-widget-private.h
+++ b/libempathy-gtk/empathy-account-widget-private.h
@@ -26,6 +26,8 @@
#include <glib.h>
#include <gtk/gtk.h>
+#include "empathy-account-widget-irc.h"
+
G_BEGIN_DECLS
struct _EmpathyAccountWidgetUIDetails {
@@ -43,6 +45,50 @@ struct _EmpathyAccountWidgetUIDetails {
gpointer user_data);
};
+typedef struct {
+ EmpathyAccountSettings *settings;
+
+ GtkWidget *table_common_settings;
+ GtkWidget *apply_button;
+ GtkWidget *cancel_button;
+ GtkWidget *entry_password;
+ GtkWidget *spinbutton_port;
+ GtkWidget *enabled_checkbox;
+ GtkWidget *radiobutton_reuse;
+
+ gboolean simple;
+ gboolean enabled;
+
+ gboolean contains_pending_changes;
+
+ /* An EmpathyAccountWidget can be used to either create an account or
+ * modify it. When we are creating an account, this member is set to TRUE */
+ gboolean creating_account;
+
+ /* whether there are any other real accounts. Necessary so we know whether
+ * it's safe to dismiss this widget in some cases (eg, whether the Cancel
+ * button should be sensitive) */
+ gboolean other_accounts_exist;
+
+ /* if TRUE, the GTK+ destroy signal has been fired and so the widgets
+ * embedded in this account widget can't be used any more
+ * workaround because some async callbacks can be called after the
+ * widget has been destroyed */
+ gboolean destroyed;
+
+ TpAccountManager *account_manager;
+
+ GtkWidget *param_account_widget;
+ GtkWidget *param_password_widget;
+
+ gboolean automatic_change;
+ GtkWidget *remember_password_widget;
+
+ /* Used only for IRC accounts */
+ EmpathyIrcNetworkChooser *irc_network_chooser;
+
+ gboolean dispose_run;
+} EmpathyAccountWidgetPriv;
void empathy_account_widget_handle_params (EmpathyAccountWidget *self,
const gchar *first_widget,
diff --git a/libempathy-gtk/empathy-account-widget-skype.c b/libempathy-gtk/empathy-account-widget-skype.c
new file mode 100644
index 000000000..288fc039a
--- /dev/null
+++ b/libempathy-gtk/empathy-account-widget-skype.c
@@ -0,0 +1,1064 @@
+/*
+ * Copyright (C) 2011 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
+ * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <extensions/extensions.h>
+
+#include <libempathy/empathy-utils.h>
+#include <libempathy/empathy-server-sasl-handler.h>
+
+#include "empathy-account-widget-skype.h"
+#include "empathy-account-widget-private.h"
+#include "empathy-ui-utils.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
+#include <libempathy/empathy-debug.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountWidget)
+
+typedef struct
+{
+ TpAccount *account;
+ TpChannel *channel;
+ char *password;
+ gboolean remember;
+} ObserveChannelsData;
+
+static ObserveChannelsData *
+observe_channels_data_new (TpAccount *account,
+ TpChannel *channel,
+ const char *password,
+ gboolean remember)
+{
+ ObserveChannelsData *data = g_slice_new0 (ObserveChannelsData);
+
+ data->account = g_object_ref (account);
+ data->channel = g_object_ref (channel);
+ data->password = g_strdup (password);
+ data->remember = remember;
+
+ return data;
+}
+
+static void
+observe_channels_data_free (ObserveChannelsData *data)
+{
+ g_object_unref (data->account);
+ g_object_unref (data->channel);
+ g_free (data->password);
+
+ g_slice_free (ObserveChannelsData, data);
+}
+
+static void
+auth_observer_sasl_handler_invalidated (EmpathyServerSASLHandler *sasl_handler,
+ gpointer user_data)
+{
+ DEBUG ("SASL Handler done");
+
+ g_object_unref (sasl_handler);
+}
+
+static void
+auth_observer_new_sasl_handler_cb (GObject *obj,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ObserveChannelsData *data = user_data;
+ EmpathyServerSASLHandler *sasl_handler;
+ GError *error = NULL;
+
+ sasl_handler = empathy_server_sasl_handler_new_finish (result, &error);
+ if (error != NULL)
+ {
+ DEBUG ("Failed to create SASL handler: %s", error->message);
+
+ tp_channel_close_async (data->channel, NULL, NULL);
+
+ g_error_free (error);
+ goto finally;
+ }
+
+ DEBUG ("providing password, remember = %s", data->remember ? "yes" : "no");
+
+ g_signal_connect (sasl_handler, "invalidated",
+ G_CALLBACK (auth_observer_sasl_handler_invalidated), NULL);
+ empathy_server_sasl_handler_provide_password (sasl_handler,
+ data->password, data->remember);
+
+finally:
+ observe_channels_data_free (data);
+}
+
+static void
+auth_observer_claim_cb (GObject *dispatch_operation,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ObserveChannelsData *data = user_data;
+ GError *error = NULL;
+
+ if (!tp_channel_dispatch_operation_claim_finish (
+ TP_CHANNEL_DISPATCH_OPERATION (dispatch_operation), result, &error))
+ {
+ DEBUG ("Failed to claim auth channel: %s", error->message);
+
+ g_error_free (error);
+ observe_channels_data_free (data);
+ return;
+ }
+
+ empathy_server_sasl_handler_new_async (data->account, data->channel,
+ auth_observer_new_sasl_handler_cb, data);
+}
+
+static void
+auth_observer_observe_channels (TpSimpleObserver *auth_observer,
+ TpAccount *account,
+ TpConnection *connection,
+ GList *channels,
+ TpChannelDispatchOperation *dispatch_operation,
+ GList *requests,
+ TpObserveChannelsContext *context,
+ gpointer user_data)
+{
+ TpChannel *channel;
+ GHashTable *props;
+ GStrv available_mechanisms;
+ const char *password = NULL;
+ gboolean remember;
+
+ /* we only do this for Psyke */
+ if (tp_strdiff (
+ tp_connection_get_connection_manager_name (connection),
+ "psyke"))
+ goto finally;
+
+ /* can only deal with one channel */
+ if (g_list_length (channels) != 1)
+ goto finally;
+
+ channel = channels->data;
+ props = tp_channel_borrow_immutable_properties (channel);
+ available_mechanisms = tp_asv_get_boxed (props,
+ TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
+ G_TYPE_STRV);
+
+ /* must support X-TELEPATHY-PASSWORD */
+ if (!tp_strv_contains ((const char * const *) available_mechanisms,
+ "X-TELEPATHY-PASSWORD"))
+ goto finally;
+
+ password = g_object_get_data (G_OBJECT (auth_observer), "password");
+ remember = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (auth_observer),
+ "remember-password"));
+
+ if (tp_str_empty (password))
+ goto finally;
+
+ DEBUG ("claiming auth channel");
+
+ tp_channel_dispatch_operation_claim_async (dispatch_operation,
+ auth_observer_claim_cb,
+ observe_channels_data_new (account, channel, password, remember));
+
+finally:
+ tp_observe_channels_context_accept (context);
+}
+
+static TpBaseClient *
+auth_observer_new (void)
+{
+ TpDBusDaemon *dbus;
+ TpBaseClient *auth_observer;
+ GError *error = NULL;
+
+ dbus = tp_dbus_daemon_dup (&error);
+
+ if (error != NULL)
+ {
+ g_warning ("Failed to get DBus daemon: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ auth_observer = tp_simple_observer_new (dbus, FALSE, "Empathy.PsykePreAuth",
+ FALSE, auth_observer_observe_channels, NULL, NULL);
+
+ tp_base_client_set_observer_delay_approvers (auth_observer, TRUE);
+ tp_base_client_take_observer_filter (auth_observer, tp_asv_new (
+ TP_PROP_CHANNEL_CHANNEL_TYPE,
+ G_TYPE_STRING,
+ TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
+
+ TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
+ G_TYPE_STRING,
+ TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
+
+ NULL));
+
+ if (!tp_base_client_register (auth_observer, &error))
+ {
+ DEBUG ("Failed to register Psyke pre-auth observer: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (dbus);
+
+ return auth_observer;
+}
+
+enum {
+ PS_COL_ENUM_VALUE,
+ PS_COL_DISPLAY_NAME,
+ NUM_PS_COLS
+};
+
+static void
+account_widget_skype_combo_changed_cb (GtkComboBox *combo,
+ EmpathyAccountWidget *self)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ const char *prop_name = g_object_get_data (G_OBJECT (combo), "dbus-property");
+ TpConnection *conn;
+ EmpPrivacySetting prop_value;
+ GtkTreeIter iter;
+ GValue value = { 0, };
+
+ gtk_combo_box_get_active_iter (combo, &iter);
+ gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
+ PS_COL_ENUM_VALUE, &prop_value,
+ -1);
+
+ DEBUG ("Combo changed: %s", prop_name);
+
+ g_value_init (&value, G_TYPE_UINT);
+ g_value_set_uint (&value, prop_value);
+
+ conn = tp_account_get_connection (
+ empathy_account_settings_get_account (priv->settings));
+ tp_cli_dbus_properties_call_set (conn, -1,
+ EMP_IFACE_CONNECTION_INTERFACE_PRIVACY_SETTINGS,
+ prop_name, &value, NULL, NULL, NULL, NULL);
+
+ g_value_unset (&value);
+}
+
+static void
+account_widget_skype_toggle_changed_cb (GtkToggleButton *toggle,
+ EmpathyAccountWidget *self)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ const char *prop_name = g_object_get_data (G_OBJECT (toggle),
+ "dbus-property");
+ TpConnection *conn;
+ GValue value = { 0, };
+
+ DEBUG ("Toggle changed: %s", prop_name);
+
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, gtk_toggle_button_get_active (toggle));
+
+ conn = tp_account_get_connection (
+ empathy_account_settings_get_account (priv->settings));
+ tp_cli_dbus_properties_call_set (conn, -1,
+ EMP_IFACE_CONNECTION_INTERFACE_PRIVACY_SETTINGS,
+ prop_name, &value, NULL, NULL, NULL, NULL);
+
+ g_value_unset (&value);
+}
+
+static void
+account_widget_skype_set_value (EmpathyAccountWidget *self,
+ GtkWidget *widget,
+ GValue *value)
+{
+ g_return_if_fail (value != NULL);
+
+ if (GTK_IS_COMBO_BOX (widget))
+ {
+ GtkTreeModel *model =
+ gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ guint prop_value;
+ GtkTreeIter iter;
+ gboolean valid;
+
+ g_return_if_fail (G_VALUE_HOLDS_UINT (value));
+
+ prop_value = g_value_get_uint (value);
+
+ g_signal_handlers_block_by_func (widget,
+ account_widget_skype_combo_changed_cb, self);
+
+ for (valid = gtk_tree_model_get_iter_first (model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ guint v;
+
+ gtk_tree_model_get (model, &iter,
+ PS_COL_ENUM_VALUE, &v,
+ -1);
+
+ if (v == prop_value)
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
+ break;
+ }
+ }
+
+ g_signal_handlers_unblock_by_func (widget,
+ account_widget_skype_combo_changed_cb, self);
+ }
+ else if (GTK_IS_TOGGLE_BUTTON (widget))
+ {
+ g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value));
+
+ g_signal_handlers_block_by_func (widget,
+ account_widget_skype_toggle_changed_cb, self);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ g_value_get_boolean (value));
+
+ g_signal_handlers_unblock_by_func (widget,
+ account_widget_skype_toggle_changed_cb, self);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+}
+
+static void
+account_widget_build_skype_get_password_saved_cb (TpProxy *account,
+ const GValue *value,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *password_entry)
+{
+ GtkWidget *remember_password = user_data;
+ gboolean password_saved;
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Failed to get PasswordSaved: %s", in_error->message);
+ return;
+ }
+
+ g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value));
+
+ password_saved = g_value_get_boolean (value);
+
+ DEBUG ("PasswordSaved: %s", password_saved ? "yes" : "no");
+
+ /* don't wipe the password if there's a real value in there */
+ if (g_object_get_data (password_entry, "fake-password") == NULL &&
+ !tp_str_empty (gtk_entry_get_text (GTK_ENTRY (password_entry))))
+ return;
+
+ gtk_entry_set_text (GTK_ENTRY (password_entry),
+ password_saved ? "xxxxxxxxxxxx": "");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (remember_password),
+ password_saved);
+
+ g_object_set_data (password_entry, "fake-password",
+ GUINT_TO_POINTER (password_saved));
+}
+
+static void
+account_widget_build_skype_account_properties_changed_cb (TpProxy *account,
+ const char *iface,
+ GHashTable *changed,
+ const char **invalidated,
+ gpointer user_data,
+ GObject *password_entry)
+{
+ GValue *value;
+
+ if (tp_strdiff (iface,
+ EMP_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE))
+ return;
+
+ value = g_hash_table_lookup (changed, "PasswordSaved");
+
+ if (value == NULL)
+ return;
+
+ account_widget_build_skype_get_password_saved_cb (account, value, NULL,
+ user_data, password_entry);
+}
+
+static void
+account_widget_skype_additional_apply_forget_passwd_cb (TpProxy *account,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (weak_obj);
+
+ if (in_error != NULL)
+ {
+ DEBUG ("Did not forget password: %s", in_error->message);
+ }
+ else
+ {
+ DEBUG ("Password forgot, proceed to reconnect");
+ }
+
+ /* reconnect is required */
+ g_simple_async_result_set_op_res_gboolean (simple, in_error == NULL);
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
+static void
+account_widget_skype_additional_apply_async (EmpathyAccountWidget *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ TpAccount *account = empathy_account_settings_get_account (priv->settings);
+ GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self),
+ callback, user_data, NULL);
+ GtkWidget *password_entry, *remember_password;
+ GObject *auth_observer;
+ const char *password = NULL;
+ gboolean remember;
+
+ /* sync the password with the observer */
+ auth_observer = g_object_get_data (G_OBJECT (self), "auth-observer");
+ password_entry = g_object_get_data (G_OBJECT (self), "password-entry");
+ remember_password = g_object_get_data (G_OBJECT (self), "remember-password");
+
+ if (g_object_get_data (G_OBJECT (password_entry), "fake-password") == NULL)
+ password = gtk_entry_get_text (GTK_ENTRY (password_entry));
+
+ remember = gtk_toggle_button_get_active (
+ GTK_TOGGLE_BUTTON (remember_password));
+
+ g_object_set_data_full (auth_observer, "password",
+ g_strdup (password), g_free);
+ g_object_set_data (auth_observer, "remember-password",
+ GUINT_TO_POINTER (remember));
+
+ /* we have to forget the password, else psyke won't query for the new one */
+ emp_cli_account_interface_external_password_storage_call_forget_password (
+ TP_PROXY (account), -1,
+ account_widget_skype_additional_apply_forget_passwd_cb,
+ NULL, NULL, G_OBJECT (simple));
+}
+
+static gboolean
+account_widget_build_skype_password_entry_focus (GtkWidget *password_entry,
+ GdkEventFocus *event,
+ EmpathyAccountWidget *self)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ TpAccount *account = empathy_account_settings_get_account (priv->settings);
+
+ if (g_object_get_data (G_OBJECT (password_entry), "fake-password") != NULL)
+ {
+ DEBUG ("Clearing fake password for editing");
+
+ gtk_entry_set_text (GTK_ENTRY (password_entry), "");
+ g_object_set_data (G_OBJECT (password_entry), "fake-password",
+ GUINT_TO_POINTER (FALSE));
+ }
+
+ if (account != NULL && tp_account_is_enabled (account))
+ {
+ DEBUG ("Highlighting Apply/Cancel button");
+
+ self->ui_details->additional_apply_async =
+ account_widget_skype_additional_apply_async;
+ empathy_account_widget_changed (self);
+ }
+
+ return FALSE;
+}
+
+static void
+account_widget_skype_remember_password_toggled (GtkToggleButton *button,
+ EmpathyAccountWidget *self)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ TpAccount *account = empathy_account_settings_get_account (priv->settings);
+
+ /* we only forget the password if the toggle goes inactive */
+ if (gtk_toggle_button_get_active (button))
+ return;
+
+ DEBUG ("forgetting password");
+
+ emp_cli_account_interface_external_password_storage_call_forget_password (
+ TP_PROXY (account), -1, NULL, NULL, NULL, NULL);
+}
+
+static void
+account_widget_build_skype_get_privacy_settings_cb (TpProxy *cm,
+ GHashTable *props,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (weak_obj);
+ GtkBuilder *gui = user_data;
+ guint i;
+
+ static const char *widgets[] = {
+ "allow-text-chats-from",
+ "allow-skype-calls-from",
+ "allow-video-from",
+ "show-my-avatar-to",
+ "show-my-web-status",
+ "show-i-have-video-to"
+ };
+
+ if (in_error != NULL)
+ {
+ GtkWidget *table, *infobar, *label;
+
+ DEBUG ("Failed to get properties: %s", in_error->message);
+
+ table = GTK_WIDGET (gtk_builder_get_object (gui,
+ "privacy-settings-table"));
+ gtk_widget_set_sensitive (table, FALSE);
+
+ infobar = gtk_info_bar_new ();
+ gtk_box_pack_start (
+ GTK_BOX (gtk_builder_get_object (gui, "privacy-settings-vbox")),
+ infobar, FALSE, TRUE, 0);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_ERROR);
+ label = gtk_label_new (_("Failed to retrieve privacy settings."));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_container_add (GTK_CONTAINER (
+ gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))),
+ label);
+ gtk_widget_show (label);
+
+ return;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (widgets); i++)
+ {
+ GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (gui, widgets[i]));
+ const char *prop_name = g_object_get_data (G_OBJECT (widget),
+ "dbus-property");
+ GValue *value;
+
+ DEBUG ("Widget '%s' (%s), prop = %s",
+ widgets[i], G_OBJECT_TYPE_NAME (widget), prop_name);
+
+ value = g_hash_table_lookup (props, prop_name);
+ if (value == NULL)
+ {
+ g_warning ("Couldn't get a value for %s", prop_name);
+ continue;
+ }
+
+ account_widget_skype_set_value (self, widget, value);
+ }
+}
+
+static void
+account_widget_skype_properties_changed_cb (TpProxy *conn,
+ const char *interface,
+ GHashTable *props,
+ const char **invalidated,
+ gpointer user_data,
+ GObject *weak_obj)
+{
+ EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (weak_obj);
+ GtkWidget *table = user_data;
+ GHashTableIter iter;
+ const char *prop;
+ GValue *value;
+
+ g_hash_table_iter_init (&iter, props);
+ while (g_hash_table_iter_next (&iter, (gpointer) &prop, (gpointer) &value))
+ {
+ GList *children, *ptr;
+
+ DEBUG ("Property changed: %s", prop);
+
+ /* find this value in the widget tree */
+ children = gtk_container_get_children (GTK_CONTAINER (table));
+
+ for (ptr = children; ptr != NULL; ptr = ptr->next)
+ {
+ GtkWidget *widget = ptr->data;
+ const char *prop_name = g_object_get_data (G_OBJECT (widget),
+ "dbus-property");
+
+ if (!tp_strdiff (prop_name, prop))
+ {
+ DEBUG ("Got child %p (%s)", widget, G_OBJECT_TYPE_NAME (widget));
+
+ account_widget_skype_set_value (self, widget, value);
+ break;
+ }
+ }
+
+ g_list_free (children);
+ }
+}
+
+/**
+ * account_widget_build_skype_setup_combo:
+ * @gui:
+ * @widget:
+ * @first_option: a list of options from the enum, terminated by -1
+ */
+static void
+account_widget_build_skype_setup_combo (EmpathyAccountWidget *self,
+ GtkBuilder *gui,
+ const char *widget,
+ const char *prop_name,
+ int first_option,
+ ...)
+{
+ GtkWidget *combo;
+ GtkListStore *store;
+ va_list var_args;
+ int option;
+
+ static const char *options[NUM_EMP_PRIVACY_SETTINGS] = {
+ N_("No one"),
+ N_("My contacts"),
+ N_("Anyone"),
+ N_("Known Numbers")
+ };
+
+ combo = GTK_WIDGET (gtk_builder_get_object (gui, widget));
+
+ g_return_if_fail (combo != NULL);
+
+ store = gtk_list_store_new (NUM_PS_COLS,
+ G_TYPE_UINT, /* PS_COL_ENUM_VALUE */
+ G_TYPE_STRING /* PS_COL_DISPLAY_NAME */
+ );
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
+
+ va_start (var_args, first_option);
+
+ for (option = first_option; option != -1; option = va_arg (var_args, int))
+ {
+ gtk_list_store_insert_with_values (store, NULL, -1,
+ PS_COL_ENUM_VALUE, option,
+ PS_COL_DISPLAY_NAME, gettext (options[option]),
+ -1);
+ }
+
+ va_end (var_args);
+
+ g_object_set_data (G_OBJECT (combo), "dbus-property", (gpointer) prop_name);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (account_widget_skype_combo_changed_cb), self);
+}
+
+static void
+account_widget_skype_privacy_settings (GtkWidget *button,
+ EmpathyAccountWidget *self)
+{
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ TpConnection *conn;
+ char *filename;
+ GtkBuilder *gui;
+ GtkWidget *dialog, *show_my_web_status, *table, *vbox;
+ GtkWidget *toplevel, *infobar, *label;
+
+ DEBUG ("Loading privacy settings");
+
+ filename = empathy_file_lookup ("empathy-account-widget-skype.ui",
+ "libempathy-gtk");
+ gui = empathy_builder_get_file (filename,
+ "privacy-settings-dialog", &dialog,
+ "privacy-settings-table", &table,
+ "privacy-settings-vbox", &vbox,
+ "show-my-web-status", &show_my_web_status,
+ NULL);
+
+ toplevel = gtk_widget_get_toplevel (self->ui_details->widget);
+ if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
+
+ /* make the settings insensitive when the account is disconnected */
+ tp_account_bind_connection_status_to_property (
+ empathy_account_settings_get_account (priv->settings),
+ table, "sensitive", FALSE);
+
+ /* set up an informative info bar */
+ infobar = gtk_info_bar_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), infobar, FALSE, TRUE, 0);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO);
+ label = gtk_label_new (_("Privacy settings can only be changed while the "
+ "account is connected."));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_container_add (
+ GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))),
+ label);
+ gtk_widget_show (label);
+ g_object_bind_property (table, "sensitive", infobar, "visible",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ account_widget_build_skype_setup_combo (self, gui,
+ "allow-text-chats-from", "AllowTextChannelsFrom",
+ EMP_PRIVACY_SETTING_ANYONE,
+ EMP_PRIVACY_SETTING_CONTACTS,
+ -1);
+ account_widget_build_skype_setup_combo (self, gui,
+ "allow-skype-calls-from", "AllowCallChannelsFrom",
+ EMP_PRIVACY_SETTING_ANYONE,
+ EMP_PRIVACY_SETTING_CONTACTS,
+ -1);
+ account_widget_build_skype_setup_combo (self, gui,
+ "allow-video-from", "AllowVideoFrom",
+ EMP_PRIVACY_SETTING_ANYONE,
+ EMP_PRIVACY_SETTING_CONTACTS,
+ -1);
+ account_widget_build_skype_setup_combo (self, gui,
+ "show-i-have-video-to", "ShowIHaveVideoTo",
+ EMP_PRIVACY_SETTING_ANYONE,
+ EMP_PRIVACY_SETTING_CONTACTS,
+ EMP_PRIVACY_SETTING_NOBODY,
+ -1);
+ account_widget_build_skype_setup_combo (self, gui,
+ "show-my-avatar-to", "ShowMyAvatarTo",
+ EMP_PRIVACY_SETTING_ANYONE,
+ EMP_PRIVACY_SETTING_CONTACTS,
+ -1);
+
+ g_object_set_data (G_OBJECT (show_my_web_status), "dbus-property",
+ "ShowMyWebStatus");
+ g_signal_connect (show_my_web_status, "toggled",
+ G_CALLBACK (account_widget_skype_toggle_changed_cb), self);
+
+ /* get the current parameter values from psyke */
+ conn = tp_account_get_connection (
+ empathy_account_settings_get_account (priv->settings));
+
+ tp_cli_dbus_properties_call_get_all (conn, -1,
+ EMP_IFACE_CONNECTION_INTERFACE_PRIVACY_SETTINGS,
+ account_widget_build_skype_get_privacy_settings_cb,
+ g_object_ref (gui), g_object_unref,
+ G_OBJECT (self));
+ tp_cli_dbus_properties_connect_to_properties_changed (conn,
+ account_widget_skype_properties_changed_cb,
+ table, NULL, G_OBJECT (self), NULL);
+
+ g_object_unref (gui);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+account_widget_build_skype_set_pixmap (GtkBuilder *gui,
+ const char *widget,
+ const char *file)
+{
+ GtkImage *image = GTK_IMAGE (gtk_builder_get_object (gui, widget));
+ char *filename = empathy_file_lookup (file, "data");
+
+ gtk_image_set_from_file (image, filename);
+
+ g_free (filename);
+}
+
+void
+empathy_account_widget_build_skype (EmpathyAccountWidget *self,
+ const char *filename)
+{
+ static TpBaseClient *auth_observer = NULL;
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ TpAccount *account = empathy_account_settings_get_account (priv->settings);
+ GtkWidget *password_entry, *remember_password;
+
+ if (priv->simple || priv->creating_account)
+ {
+ GtkWidget *skype_info;
+
+ /* if we don't have an account it means we're doing the initial setup */
+ self->ui_details->gui = empathy_builder_get_file (filename,
+ "table_common_skype_settings_setup", &priv->table_common_settings,
+ "vbox_skype_settings_setup", &self->ui_details->widget,
+ "skype-info-vbox", &skype_info,
+ "entry_password_setup", &password_entry,
+ "remember-password-setup", &remember_password,
+ NULL);
+
+ account_widget_build_skype_set_pixmap (self->ui_details->gui,
+ "plugged-into-skype-logo", "plugged-into-skype.png");
+ account_widget_build_skype_set_pixmap (self->ui_details->gui,
+ "canonical-logo", "canonical-logo.png");
+
+ gtk_box_pack_end (GTK_BOX (self->ui_details->widget), skype_info,
+ TRUE, TRUE, 0);
+
+ empathy_account_widget_handle_params (self,
+ "entry_id_setup", "account",
+ NULL);
+
+ self->ui_details->default_focus = g_strdup ("entry_id_setup");
+ }
+ else
+ {
+ GtkWidget *edit_privacy_settings_button, *skype_info;
+
+ self->ui_details->gui = empathy_builder_get_file (filename,
+ "table_common_skype_settings", &priv->table_common_settings,
+ "vbox_skype_settings", &self->ui_details->widget,
+ "skype-info-vbox", &skype_info,
+ "edit-privacy-settings-button", &edit_privacy_settings_button,
+ "entry_password", &password_entry,
+ "remember-password", &remember_password,
+ NULL);
+
+ empathy_builder_connect (self->ui_details->gui, self,
+ "edit-privacy-settings-button", "clicked",
+ account_widget_skype_privacy_settings,
+ "remember-password", "toggled",
+ account_widget_skype_remember_password_toggled,
+ NULL);
+
+ if (account != NULL)
+ tp_account_bind_connection_status_to_property (account,
+ edit_privacy_settings_button, "sensitive", FALSE);
+ else
+ gtk_widget_set_sensitive (edit_privacy_settings_button, FALSE);
+
+ account_widget_build_skype_set_pixmap (self->ui_details->gui,
+ "plugged-into-skype-logo", "plugged-into-skype.png");
+ account_widget_build_skype_set_pixmap (self->ui_details->gui,
+ "canonical-logo", "canonical-logo.png");
+
+ gtk_box_pack_end (GTK_BOX (self->ui_details->widget), skype_info,
+ TRUE, TRUE, 0);
+
+ empathy_account_widget_handle_params (self,
+ "entry_id", "account",
+ NULL);
+
+ self->ui_details->default_focus = g_strdup ("entry_id");
+ }
+
+ /* create the Psyke pre-authentication observer */
+ if (auth_observer == NULL)
+ {
+ /* the auth observer lives for the lifetime of the process */
+ DEBUG ("Creating Psyke authentication observer");
+ auth_observer = auth_observer_new ();
+ }
+
+ g_object_set_data (G_OBJECT (self), "auth-observer", auth_observer);
+ g_object_set_data (G_OBJECT (self), "password-entry", password_entry);
+ g_object_set_data (G_OBJECT (self), "remember-password", remember_password);
+
+ g_object_bind_property (remember_password, "active",
+ password_entry, "sensitive", G_BINDING_SYNC_CREATE);
+
+ /* find out if we know the password */
+ if (account != NULL && tp_proxy_has_interface_by_id (account,
+ EMP_IFACE_QUARK_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE))
+ {
+ tp_cli_dbus_properties_call_get (account, -1,
+ EMP_IFACE_ACCOUNT_INTERFACE_EXTERNAL_PASSWORD_STORAGE,
+ "PasswordSaved",
+ account_widget_build_skype_get_password_saved_cb,
+ remember_password, NULL, G_OBJECT (password_entry));
+ tp_cli_dbus_properties_connect_to_properties_changed (account,
+ account_widget_build_skype_account_properties_changed_cb,
+ remember_password, NULL, G_OBJECT (password_entry), NULL);
+ }
+
+ /* if the user changes the password, it's probably no longer a fake
+ * password */
+ g_signal_connect (password_entry, "focus-in-event",
+ G_CALLBACK (account_widget_build_skype_password_entry_focus), self);
+}
+
+gboolean
+empathy_account_widget_skype_show_eula (GtkWindow *parent)
+{
+ GtkWidget *dialog, *textview, *vbox, *scrolledwindow;
+ GtkTextBuffer *buffer;
+ gchar *filename, *l10n_filename;
+ const gchar * const * langs;
+ GError *error = NULL;
+ gchar *eula;
+ gint result;
+ gsize len;
+ gint i;
+
+ filename = empathy_file_lookup ("skype-eula.txt", "data");
+
+ langs = g_get_language_names ();
+
+ for (i = 0; langs[i] != NULL; i++)
+ {
+ l10n_filename = g_strconcat (filename, ".", langs[i], NULL);
+ g_file_get_contents (l10n_filename, &eula, &len, NULL);
+ g_free (l10n_filename);
+
+ if (eula != NULL)
+ break;
+ }
+
+ if (eula == NULL)
+ {
+ DEBUG ("Could not open translated Skype EULA");
+ g_file_get_contents (filename, &eula, &len, &error);
+ }
+
+ g_free (filename);
+
+ if (error != NULL)
+ {
+ g_warning ("Could not open Skype EULA: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ dialog = gtk_dialog_new_with_buttons (_("End User License Agreement"),
+ parent, GTK_DIALOG_MODAL,
+ _("Decline"), GTK_RESPONSE_CANCEL,
+ _("Accept"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ buffer = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_set_text (buffer, eula, len);
+ g_free (eula);
+ textview = gtk_text_view_new_with_buffer (buffer);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview), GTK_WRAP_WORD_CHAR);
+
+ vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_add_with_viewport (
+ GTK_SCROLLED_WINDOW (scrolledwindow), textview);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 250);
+ gtk_widget_show_all (dialog);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return (result == GTK_RESPONSE_ACCEPT);
+}
+
+static gboolean
+is_other_psyke_account (TpAccount *ours,
+ TpAccount *other)
+{
+ gboolean paths_diff;
+
+ if (ours == NULL)
+ paths_diff = TRUE;
+ else
+ paths_diff = tp_strdiff (
+ tp_proxy_get_object_path (ours),
+ tp_proxy_get_object_path (other));
+
+ return (paths_diff &&
+ tp_account_is_enabled (other) &&
+ !tp_strdiff (tp_account_get_connection_manager (other), "psyke"));
+}
+
+gboolean
+empathy_accounts_dialog_skype_disable_other_accounts (TpAccount *account,
+ GtkWindow *parent)
+{
+ TpAccountManager *am = tp_account_manager_dup ();
+ GList *accounts, *ptr;
+ gboolean other_psyke_accounts = FALSE;
+
+ /* check if any other psyke accounts are enabled */
+ accounts = tp_account_manager_get_valid_accounts (am);
+
+ for (ptr = accounts; ptr != NULL; ptr = ptr->next)
+ {
+ TpAccount *a = ptr->data;
+
+ if (is_other_psyke_account (account, a))
+ {
+ other_psyke_accounts = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (accounts);
+
+ if (other_psyke_accounts)
+ {
+#if 0 /* this dialog is a good idea, but currently lacking from the UX spec,
+ * so it's being left out for now */
+ GtkWidget *msg = gtk_message_dialog_new (parent, 0,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ "%s\n\n%s",
+ _("Multiple Skype accounts can not be enabled simultaneously. "
+ "Enabling this Skype account will disable all others."),
+ _("Continue to enable this account?"));
+ int response;
+
+ response = gtk_dialog_run (GTK_DIALOG (msg));
+ gtk_widget_destroy (msg);
+
+ if (response != GTK_RESPONSE_YES)
+ {
+ /* the user chose not to proceed */
+ g_object_unref (am);
+
+ return FALSE;
+ }
+#endif
+
+ /* the user chose to proceed, disable the other accounts */
+ accounts = tp_account_manager_get_valid_accounts (am);
+
+ for (ptr = accounts; ptr != NULL; ptr = ptr->next)
+ {
+ TpAccount *a = ptr->data;
+
+ if (is_other_psyke_account (account, a))
+ {
+ tp_account_set_enabled_async (a, FALSE, NULL, NULL);
+ }
+ }
+
+ g_list_free (accounts);
+ }
+
+ g_object_unref (am);
+
+ return TRUE;
+}
diff --git a/libempathy-gtk/empathy-account-widget-skype.h b/libempathy-gtk/empathy-account-widget-skype.h
new file mode 100644
index 000000000..5ba0c6ed5
--- /dev/null
+++ b/libempathy-gtk/empathy-account-widget-skype.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
+ * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ */
+
+#ifndef __EMPATHY_ACCOUNT_WIDGET_SKYPE_H__
+#define __EMPATHY_ACCOUNT_WIDGET_SKYPE_H__
+
+#include <gtk/gtk.h>
+#include <libempathy-gtk/empathy-account-widget.h>
+
+G_BEGIN_DECLS
+
+void empathy_account_widget_build_skype (EmpathyAccountWidget *self,
+ const char *filename);
+gboolean empathy_account_widget_skype_show_eula (GtkWindow *parent);
+gboolean empathy_accounts_dialog_skype_disable_other_accounts (
+ TpAccount *account, GtkWindow *parent);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_ACCOUNT_WIDGET_SKYPE_H__ */
diff --git a/libempathy-gtk/empathy-account-widget-skype.ui b/libempathy-gtk/empathy-account-widget-skype.ui
new file mode 100644
index 000000000..dbea877da
--- /dev/null
+++ b/libempathy-gtk/empathy-account-widget-skype.ui
@@ -0,0 +1,588 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="vbox_skype_settings_setup">
+ <property name="visible">True</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table_common_skype_settings_setup">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label_id">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Skype name:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry_id_setup</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_id_setup">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_password_setup">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Don't have a Skype Name?
+Get one at &lt;a href="http://www.skype.com/go/register"&gt;Skype.com&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;a href="https://login.skype.com/account/password-reset-request"&gt;Forgotten your password?&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="remember-password-setup">
+ <property name="label" translatable="yes">Remember pass_word:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkDialog" id="privacy-settings-dialog">
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="skip_pager_hint">True</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="privacy-settings-vbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="privacy-settings-table">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Allow _text chats from:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">allow-text-chats-from</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Allow _calls from:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">allow-skype-calls-from</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Allow video _from:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">allow-video-from</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Show my profile _picture to:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">show-my-avatar-to</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="allow-text-chats-from">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="allow-skype-calls-from">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="allow-video-from">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="show-my-avatar-to">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Show whether I have _video to:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">show-i-have-video-to</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="show-i-have-video-to">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">Get your own customized button for your website</property>
+ <property name="label" translatable="yes">&lt;a href="http://www.skype.com/intl/en-us/tell-a-friend/get-a-skype-button/"&gt;What's this?&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show-my-web-status">
+ <property name="label" translatable="yes">Show my status in _Web buttons</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;span size="x-large" weight="bold"&gt;Privacy Settings&lt;/span&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">button1</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkVBox" id="vbox_skype_settings">
+ <property name="visible">True</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table_common_skype_settings">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label_id1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Skype name:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry_id_setup</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_id">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â—Ź</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">â—Ź</property>
+ </object>
+ <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>
+ <object class="GtkLinkButton" id="linkbutton2">
+ <property name="label" translatable="yes">Change Password...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="uri">https://secure.skype.com/account/personal/change-password</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="remember-password">
+ <property name="label" translatable="yes">Remember pass_word:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="edit-privacy-settings-button">
+ <property name="label" translatable="yes">_Privacy Settings...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkVBox" id="skype-info-vbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="plugged-into-skype-logo">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="stock">gtk-missing-image</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="width_request">600</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Empathy is plugged into Skype™, allowing free calls to Skype users. Skype is a trademark of Skype Limited or its related companies. This application has not been checked, verified, certified, or otherwise approved or endorsed by Skype Communications S.a.r.l. or any of their related companies.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="canonical-logo">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="stock">gtk-missing-image</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="linkbutton1">
+ <property name="label" translatable="yes">Canonical Support...</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="uri">http://canonical.com/support?app=empathy</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Canonical provides technical support for Empathy on Ubuntu.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/libempathy-gtk/empathy-account-widget.c b/libempathy-gtk/empathy-account-widget.c
index 876f6a436..edc2f98eb 100644
--- a/libempathy-gtk/empathy-account-widget.c
+++ b/libempathy-gtk/empathy-account-widget.c
@@ -21,6 +21,7 @@
* Martyn Russell <martyn@imendio.com>
* Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
* Jonathan Tellier <jonathan.tellier@gmail.com>
+ * Danielle Madeley <danielle.madeley@collabora.co.uk>
*/
#include <config.h>
@@ -34,6 +35,7 @@
#include <mx-gtk/mx-gtk.h>
#endif /* HAVE_MEEGO */
+
#include <libempathy/empathy-utils.h>
#include <telepathy-glib/account.h>
@@ -46,6 +48,7 @@
#include "empathy-account-widget-private.h"
#include "empathy-account-widget-sip.h"
#include "empathy-account-widget-irc.h"
+#include "empathy-account-widget-skype.h"
#include "empathy-ui-utils.h"
#define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
@@ -53,51 +56,6 @@
G_DEFINE_TYPE (EmpathyAccountWidget, empathy_account_widget, G_TYPE_OBJECT)
-typedef struct {
- EmpathyAccountSettings *settings;
-
- GtkWidget *table_common_settings;
- GtkWidget *apply_button;
- GtkWidget *cancel_button;
- GtkWidget *entry_password;
- GtkWidget *spinbutton_port;
- GtkWidget *enabled_checkbox;
- GtkWidget *radiobutton_reuse;
-
- gboolean simple;
- gboolean enabled;
-
- gboolean contains_pending_changes;
-
- /* An EmpathyAccountWidget can be used to either create an account or
- * modify it. When we are creating an account, this member is set to TRUE */
- gboolean creating_account;
-
- /* whether there are any other real accounts. Necessary so we know whether
- * it's safe to dismiss this widget in some cases (eg, whether the Cancel
- * button should be sensitive) */
- gboolean other_accounts_exist;
-
- /* if TRUE, the GTK+ destroy signal has been fired and so the widgets
- * embedded in this account widget can't be used any more
- * workaround because some async callbacks can be called after the
- * widget has been destroyed */
- gboolean destroyed;
-
- TpAccountManager *account_manager;
-
- GtkWidget *param_account_widget;
- GtkWidget *param_password_widget;
-
- gboolean automatic_change;
- GtkWidget *remember_password_widget;
-
- /* Used only for IRC accounts */
- EmpathyIrcNetworkChooser *irc_network_chooser;
-
- gboolean dispose_run;
-} EmpathyAccountWidgetPriv;
-
enum {
PROP_PROTOCOL = 1,
PROP_SETTINGS,
@@ -908,7 +866,22 @@ static void
account_widget_apply_clicked_cb (GtkWidget *button,
EmpathyAccountWidget *self)
{
- account_widget_apply_and_log_in (self);
+ EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
+ GtkWidget *parent;
+
+ parent = gtk_widget_get_toplevel (button);
+ if (!GTK_IS_WINDOW (parent) || !gtk_widget_is_toplevel (parent))
+ parent = NULL;
+
+ if (priv->creating_account)
+ {
+ if (!empathy_accounts_dialog_skype_disable_other_accounts (NULL,
+ GTK_WINDOW (parent)))
+ /* the user chose not to proceed */
+ return;
+ }
+
+ account_widget_apply_and_log_in (self);
}
static void
@@ -1481,6 +1454,14 @@ account_widget_build_groupwise (EmpathyAccountWidget *self,
}
static void
+account_widget_build_skype (EmpathyAccountWidget *self,
+ const char *filename)
+{
+ empathy_account_widget_build_skype (self, filename);
+}
+
+
+static void
account_widget_destroy_cb (GtkWidget *widget,
EmpathyAccountWidget *self)
{
@@ -1884,6 +1865,7 @@ do_constructed (GObject *obj)
WIDGET (haze, groupwise),
WIDGET (idle, irc),
WIDGET (sofiasip, sip),
+ WIDGET (psyke, skype),
};
cm_name = empathy_account_settings_get_cm (priv->settings);
diff --git a/libempathy-gtk/empathy-call-utils.c b/libempathy-gtk/empathy-call-utils.c
index b362ecaa8..1fe9257cc 100644
--- a/libempathy-gtk/empathy-call-utils.c
+++ b/libempathy-gtk/empathy-call-utils.c
@@ -51,6 +51,8 @@ get_error_display_message (GError *error)
return _("The specified contact is not valid");
case TP_ERROR_EMERGENCY_CALLS_NOT_SUPPORTED:
return _("Emergency calls are not supported on this protocol");
+ case TP_ERROR_INSUFFICIENT_BALANCE:
+ return _("You don't have enough credit in order to place this call");
}
return _("There was an error starting the call");
diff --git a/libempathy-gtk/empathy-chat-text-view.c b/libempathy-gtk/empathy-chat-text-view.c
index c05b92fb9..977530971 100644
--- a/libempathy-gtk/empathy-chat-text-view.c
+++ b/libempathy-gtk/empathy-chat-text-view.c
@@ -49,7 +49,7 @@
#include <libempathy/empathy-debug.h>
/* Number of seconds between timestamps when using normal mode, 5 minutes. */
-#define TIMESTAMP_INTERVAL 300
+#define TIMESTAMP_INTERVAL (5 * G_TIME_SPAN_MINUTE)
#define MAX_LINES 800
#define MAX_SCROLL_TIME 0.4 /* seconds */
@@ -66,7 +66,7 @@ typedef struct {
gboolean find_wrapped;
gboolean find_last_direction;
EmpathyContact *last_contact;
- time_t last_timestamp;
+ gint64 last_timestamp;
gboolean allow_scrolling;
guint notify_system_fonts_id;
GConfClient *gconf_client;
@@ -402,7 +402,7 @@ chat_text_view_maybe_trim_buffer (EmpathyChatTextView *view)
static void
chat_text_view_append_timestamp (EmpathyChatTextView *view,
- time_t timestamp,
+ gint64 timestamp,
gboolean show_date)
{
EmpathyChatTextViewPriv *priv = GET_PRIV (view);
@@ -414,17 +414,12 @@ chat_text_view_append_timestamp (EmpathyChatTextView *view,
/* Append date if needed */
if (show_date) {
- GDate *date;
- gchar buf[256];
-
- date = g_date_new ();
- g_date_set_time_t (date, timestamp);
/* Translators: timestamp displayed between conversations in
* chat windows (strftime format string) */
- g_date_strftime (buf, 256, _("%A %B %d %Y"), date);
- g_string_append (str, buf);
+ tmp = empathy_time_to_string_utc (timestamp, _("%A %B %d %Y"));
+ g_string_append (str, tmp);
g_string_append (str, ", ");
- g_date_free (date);
+ g_free (tmp);
}
/* Append time */
@@ -448,32 +443,32 @@ chat_text_view_append_timestamp (EmpathyChatTextView *view,
static void
chat_text_maybe_append_date_and_time (EmpathyChatTextView *view,
- time_t timestamp)
+ gint64 timestamp)
{
EmpathyChatTextViewPriv *priv = GET_PRIV (view);
- GDate *date, *last_date;
+ GDateTime *date, *last_date;
gboolean append_date = FALSE;
gboolean append_time = FALSE;
+ GTimeSpan delta;
/* Get the date from last message */
- last_date = g_date_new ();
- g_date_set_time_t (last_date, priv->last_timestamp);
+ last_date = g_date_time_new_from_unix_utc (priv->last_timestamp);
/* Get the date of the message we are appending */
- date = g_date_new ();
- g_date_set_time_t (date, timestamp);
+ date = g_date_time_new_from_unix_utc (timestamp);
+ delta = g_date_time_difference (last_date, date);
/* If last message was from another day we append date and time */
- if (g_date_compare (date, last_date) > 0) {
+ if (delta >= G_TIME_SPAN_DAY) {
append_date = TRUE;
append_time = TRUE;
}
- g_date_free (last_date);
- g_date_free (date);
+ g_date_time_unref (last_date);
+ g_date_time_unref (date);
/* If last message is 'old' append the time */
- if (timestamp - priv->last_timestamp >= TIMESTAMP_INTERVAL) {
+ if (delta >= TIMESTAMP_INTERVAL) {
append_time = TRUE;
}
@@ -732,7 +727,7 @@ chat_text_view_append_message (EmpathyChatView *view,
EmpathyChatTextView *text_view = EMPATHY_CHAT_TEXT_VIEW (view);
EmpathyChatTextViewPriv *priv = GET_PRIV (text_view);
gboolean bottom;
- time_t timestamp;
+ gint64 timestamp;
g_return_if_fail (EMPATHY_IS_CHAT_TEXT_VIEW (view));
g_return_if_fail (EMPATHY_IS_MESSAGE (msg));
@@ -1327,7 +1322,7 @@ empathy_chat_text_view_get_last_contact (EmpathyChatTextView *view)
return priv->last_contact;
}
-time_t
+gint64
empathy_chat_text_view_get_last_timestamp (EmpathyChatTextView *view)
{
EmpathyChatTextViewPriv *priv = GET_PRIV (view);
diff --git a/libempathy-gtk/empathy-chat-text-view.h b/libempathy-gtk/empathy-chat-text-view.h
index 7ee7ba8a2..12edbcc64 100644
--- a/libempathy-gtk/empathy-chat-text-view.h
+++ b/libempathy-gtk/empathy-chat-text-view.h
@@ -70,7 +70,7 @@ struct _EmpathyChatTextViewClass {
GType empathy_chat_text_view_get_type (void) G_GNUC_CONST;
EmpathyContact * empathy_chat_text_view_get_last_contact (EmpathyChatTextView *view);
-time_t empathy_chat_text_view_get_last_timestamp (EmpathyChatTextView *view);
+gint64 empathy_chat_text_view_get_last_timestamp (EmpathyChatTextView *view);
void empathy_chat_text_view_set_only_if_date (EmpathyChatTextView *view,
gboolean only_if_date);
void empathy_chat_text_view_append_body (EmpathyChatTextView *view,
diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index e6eca0906..fd43473dd 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -144,6 +144,7 @@ struct _EmpathyChatPriv {
* notified again about the already notified pending messages when the
* messages in tab will be properly shown */
gboolean retrieving_backlogs;
+ gboolean sms_channel;
};
typedef struct {
@@ -168,6 +169,8 @@ enum {
PROP_SUBJECT,
PROP_REMOTE_CONTACT,
PROP_SHOW_CONTACTS,
+ PROP_SMS_CHANNEL,
+ PROP_N_MESSAGES_SENDING,
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -191,7 +194,7 @@ chat_get_property (GObject *object,
g_value_set_object (value, priv->account);
break;
case PROP_NAME:
- g_value_set_string (value, empathy_chat_get_name (chat));
+ g_value_take_string (value, empathy_chat_dup_name (chat));
break;
case PROP_ID:
g_value_set_string (value, priv->id);
@@ -205,6 +208,13 @@ chat_get_property (GObject *object,
case PROP_SHOW_CONTACTS:
g_value_set_boolean (value, priv->show_contacts);
break;
+ case PROP_SMS_CHANNEL:
+ g_value_set_boolean (value, priv->sms_channel);
+ break;
+ case PROP_N_MESSAGES_SENDING:
+ g_value_set_uint (value,
+ empathy_chat_get_n_messages_sending (chat));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -245,8 +255,14 @@ account_reconnected (EmpathyChat *chat,
* https://bugs.freedesktop.org/show_bug.cgi?id=13422 */
switch (priv->handle_type) {
case TP_HANDLE_TYPE_CONTACT:
- empathy_dispatcher_chat_with_contact_id (
- account, priv->id, TP_USER_ACTION_TIME_NOT_USER_ACTION);
+ if (priv->sms_channel)
+ empathy_dispatcher_sms_contact_id (
+ account, priv->id,
+ TP_USER_ACTION_TIME_NOT_USER_ACTION);
+ else
+ empathy_dispatcher_chat_with_contact_id (
+ account, priv->id,
+ TP_USER_ACTION_TIME_NOT_USER_ACTION);
break;
case TP_HANDLE_TYPE_ROOM:
empathy_dispatcher_join_muc (account, priv->id,
@@ -804,10 +820,35 @@ chat_command_me (EmpathyChat *chat,
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyMessage *message;
+ TpMessage *message;
+ TpChannel *channel;
+
+ channel = empathy_tp_chat_get_channel (priv->tp_chat);
+
+ /* Strictly speaking we don't depend yet on Messages so best to check that
+ * the channel is actually a TpTextChannel before casting it. */
+ if (TP_IS_TEXT_CHANNEL (channel) &&
+ !tp_text_channel_supports_message_type (TP_TEXT_CHANNEL (channel),
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)) {
+ /* Action message are not supported, 'simulate' the action */
+ EmpathyContact *self_contact;
+ gchar *tmp;
+
+ self_contact = empathy_tp_chat_get_self_contact (priv->tp_chat);
+ /* The TpChat can't be ready if it doesn't have the self contact */
+ g_assert (self_contact != NULL);
+
+ tmp = g_strdup_printf ("%s %s", empathy_contact_get_alias (self_contact),
+ strv[1]);
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ tmp);
+ g_free (tmp);
+ }
+ else {
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
+ strv[1]);
+ }
- message = empathy_message_new (strv[1]);
- empathy_message_set_tptype (message, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
@@ -817,9 +858,10 @@ chat_command_say (EmpathyChat *chat,
GStrv strv)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
- EmpathyMessage *message;
+ TpMessage *message;
- message = empathy_message_new (strv[1]);
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ strv[1]);
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
@@ -968,7 +1010,7 @@ chat_send (EmpathyChat *chat,
const gchar *msg)
{
EmpathyChatPriv *priv;
- EmpathyMessage *message;
+ TpMessage *message;
guint i;
if (EMP_STR_EMPTY (msg)) {
@@ -1033,7 +1075,8 @@ chat_send (EmpathyChat *chat,
}
}
- message = empathy_message_new (msg);
+ message = tp_client_message_new_text (TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
+ msg);
empathy_tp_chat_send (priv->tp_chat, message);
g_object_unref (message);
}
@@ -1160,36 +1203,51 @@ static void
chat_send_error_cb (EmpathyTpChat *tp_chat,
const gchar *message_body,
TpChannelTextSendError error_code,
+ const gchar *dbus_error,
EmpathyChat *chat)
{
- const gchar *error;
+ const gchar *error = NULL;
gchar *str;
- switch (error_code) {
- case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE:
- error = _("offline");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT:
- error = _("invalid contact");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED:
- error = _("permission denied");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG:
- error = _("too long message");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED:
- error = _("not implemented");
- break;
- case TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN:
- default:
- error = _("unknown");
- break;
+ if (!tp_strdiff (dbus_error, TP_ERROR_STR_INSUFFICIENT_BALANCE)) {
+ error = _("insufficient balance to send message");
+ } else if (!tp_strdiff (dbus_error, TP_ERROR_STR_NOT_CAPABLE)) {
+ error = _("not capable");
+ }
+
+ if (error == NULL) {
+ /* if we didn't find a dbus-error, try the old error */
+ switch (error_code) {
+ case TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE:
+ error = _("offline");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT:
+ error = _("invalid contact");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED:
+ error = _("permission denied");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG:
+ error = _("too long message");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED:
+ error = _("not implemented");
+ break;
+ case TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN:
+ default:
+ error = _("unknown");
+ break;
+ }
+ }
+
+ if (message_body != NULL) {
+ str = g_strdup_printf (_("Error sending message '%s': %s"),
+ message_body, error);
+ }
+ else {
+ str = g_strdup_printf (_("Error sending message: %s"), error);
}
- str = g_strdup_printf (_("Error sending message '%s': %s"),
- message_body,
- error);
empathy_chat_view_append_event (chat->view, str);
g_free (str);
}
@@ -2762,6 +2820,22 @@ empathy_chat_class_init (EmpathyChatClass *klass)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SMS_CHANNEL,
+ g_param_spec_boolean ("sms-channel",
+ "SMS Channel",
+ "TRUE if this channel is for sending SMSes",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_N_MESSAGES_SENDING,
+ g_param_spec_uint ("n-messages-sending",
+ "Num Messages Sending",
+ "The number of messages being sent",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
signals[COMPOSING] =
g_signal_new ("composing",
G_OBJECT_CLASS_TYPE (object_class),
@@ -3029,6 +3103,21 @@ chat_password_needed_changed_cb (EmpathyChat *self)
}
}
+static void
+chat_sms_channel_changed_cb (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ priv->sms_channel = empathy_tp_chat_is_sms_channel (priv->tp_chat);
+ g_object_notify (G_OBJECT (self), "sms-channel");
+}
+
+static void
+chat_n_messages_sending_changed_cb (EmpathyChat *self)
+{
+ g_object_notify (G_OBJECT (self), "n-messages-sending");
+}
+
void
empathy_chat_set_tp_chat (EmpathyChat *chat,
EmpathyTpChat *tp_chat)
@@ -3080,6 +3169,12 @@ empathy_chat_set_tp_chat (EmpathyChat *chat,
g_signal_connect_swapped (tp_chat, "notify::password-needed",
G_CALLBACK (chat_password_needed_changed_cb),
chat);
+ g_signal_connect_swapped (tp_chat, "notify::sms-channel",
+ G_CALLBACK (chat_sms_channel_changed_cb),
+ chat);
+ g_signal_connect_swapped (tp_chat, "notify::n-messages-sending",
+ G_CALLBACK (chat_n_messages_sending_changed_cb),
+ chat);
/* Get initial value of properties */
properties = empathy_tp_chat_get_properties (priv->tp_chat);
@@ -3100,6 +3195,7 @@ empathy_chat_set_tp_chat (EmpathyChat *chat,
}
}
+ chat_sms_channel_changed_cb (chat);
chat_remote_contact_changed_cb (chat);
if (chat->input_text_view) {
@@ -3142,8 +3238,8 @@ empathy_chat_get_id (EmpathyChat *chat)
return priv->id;
}
-const gchar *
-empathy_chat_get_name (EmpathyChat *chat)
+gchar *
+empathy_chat_dup_name (EmpathyChat *chat)
{
EmpathyChatPriv *priv = GET_PRIV (chat);
const gchar *ret;
@@ -3151,6 +3247,7 @@ empathy_chat_get_name (EmpathyChat *chat)
g_return_val_if_fail (EMPATHY_IS_CHAT (chat), NULL);
ret = priv->name;
+
if (!ret && priv->remote_contact) {
ret = empathy_contact_get_alias (priv->remote_contact);
}
@@ -3158,7 +3255,15 @@ empathy_chat_get_name (EmpathyChat *chat)
if (!ret)
ret = priv->id;
- return ret ? ret : _("Conversation");
+ if (!ret)
+ ret = _("Conversation");
+
+ if (priv->sms_channel)
+ /* Translators: this string is a something like
+ * "Escher Cat (SMS)" */
+ return g_strdup_printf (_("%s (SMS)"), ret);
+ else
+ return g_strdup (ret);
}
const gchar *
@@ -3356,3 +3461,35 @@ empathy_chat_messages_read (EmpathyChat *self)
}
priv->unread_messages = 0;
}
+
+gboolean
+empathy_chat_is_sms_channel (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv = GET_PRIV (self);
+
+ g_return_val_if_fail (EMPATHY_IS_CHAT (self), 0);
+
+ return priv->sms_channel;
+}
+
+guint
+empathy_chat_get_n_messages_sending (EmpathyChat *self)
+{
+ EmpathyChatPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_CHAT (self), 0);
+
+ priv = GET_PRIV (self);
+
+ if (priv->tp_chat == NULL) {
+ return 0;
+ } else {
+ guint n_messages;
+
+ g_object_get (priv->tp_chat,
+ "n-messages-sending", &n_messages,
+ NULL);
+
+ return n_messages;
+ }
+}
diff --git a/libempathy-gtk/empathy-chat.h b/libempathy-gtk/empathy-chat.h
index 596b83f5d..0fd779cc6 100644
--- a/libempathy-gtk/empathy-chat.h
+++ b/libempathy-gtk/empathy-chat.h
@@ -69,7 +69,7 @@ void empathy_chat_set_tp_chat (EmpathyChat *chat,
EmpathyTpChat *tp_chat);
TpAccount * empathy_chat_get_account (EmpathyChat *chat);
const gchar * empathy_chat_get_id (EmpathyChat *chat);
-const gchar * empathy_chat_get_name (EmpathyChat *chat);
+gchar * empathy_chat_dup_name (EmpathyChat *chat);
const gchar * empathy_chat_get_subject (EmpathyChat *chat);
EmpathyContact * empathy_chat_get_remote_contact (EmpathyChat *chat);
GtkWidget * empathy_chat_get_contact_menu (EmpathyChat *chat);
@@ -89,6 +89,9 @@ void empathy_chat_set_show_contacts (EmpathyChat *chat,
guint empathy_chat_get_nb_unread_messages (EmpathyChat *chat);
void empathy_chat_messages_read (EmpathyChat *self);
+gboolean empathy_chat_is_sms_channel (EmpathyChat *self);
+guint empathy_chat_get_n_messages_sending (EmpathyChat *self);
+
G_END_DECLS
#endif /* __EMPATHY_CHAT_H__ */
diff --git a/libempathy-gtk/empathy-contact-blocking-dialog.ui b/libempathy-gtk/empathy-contact-blocking-dialog.ui
index b2ea89b81..dd17126d7 100644
--- a/libempathy-gtk/empathy-contact-blocking-dialog.ui
+++ b/libempathy-gtk/empathy-contact-blocking-dialog.ui
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
@@ -13,6 +13,7 @@
<object class="GtkVBox" id="contents">
<property name="visible">True</property>
<property name="border_width">6</property>
+ <property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="account-hbox">
@@ -82,12 +83,11 @@
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="remove-button">
- <property name="label">gtk-remove</property>
+ <property name="label">Unblock</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -114,7 +114,7 @@
<object class="GtkEntry" id="add-contact-entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="invisible_char">â—Ź</property>
+ <property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="position">0</property>
@@ -142,8 +142,8 @@
</object>
<object class="GtkSizeGroup" id="sizegroup1">
<widgets>
- <widget name="add-button"/>
<widget name="remove-button"/>
+ <widget name="add-button"/>
</widgets>
</object>
</interface>
diff --git a/libempathy-gtk/empathy-contact-selector-dialog.c b/libempathy-gtk/empathy-contact-selector-dialog.c
index 3ed7f30e5..1fc62ad2f 100644
--- a/libempathy-gtk/empathy-contact-selector-dialog.c
+++ b/libempathy-gtk/empathy-contact-selector-dialog.c
@@ -67,7 +67,8 @@ struct _EmpathyContactSelectorDialogPriv {
enum {
PROP_0,
PROP_SHOW_ACCOUNT_CHOOSER,
- PROP_FILTER_ACCOUNT
+ PROP_FILTER_ACCOUNT,
+ PROP_SELECTED_ACCOUNT
};
enum {
@@ -152,6 +153,8 @@ contact_selector_dialog_account_changed_cb (GtkWidget *widget,
g_object_unref (contact);
members = g_list_delete_link (members, members);
}
+
+ g_object_notify (G_OBJECT (dialog), "selected-account");
}
static gboolean
@@ -376,6 +379,7 @@ empathy_contact_selector_dialog_get_property (GObject *self,
GParamSpec *pspec)
{
EmpathyContactSelectorDialog *dialog = EMPATHY_CONTACT_SELECTOR_DIALOG (self);
+ EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
switch (prop_id)
{
@@ -389,6 +393,11 @@ empathy_contact_selector_dialog_get_property (GObject *self,
empathy_contact_selector_dialog_get_filter_account (dialog));
break;
+ case PROP_SELECTED_ACCOUNT:
+ g_value_set_object (value, empathy_account_chooser_get_account (
+ EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser)));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
@@ -402,6 +411,7 @@ empathy_contact_selector_dialog_set_property (GObject *self,
GParamSpec *pspec)
{
EmpathyContactSelectorDialog *dialog = EMPATHY_CONTACT_SELECTOR_DIALOG (self);
+ EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
switch (prop_id)
{
@@ -415,6 +425,12 @@ empathy_contact_selector_dialog_set_property (GObject *self,
g_value_get_object (value));
break;
+ case PROP_SELECTED_ACCOUNT:
+ empathy_account_chooser_set_account (
+ EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser),
+ g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
break;
@@ -491,6 +507,13 @@ empathy_contact_selector_dialog_class_init (
"account are displayed",
TP_TYPE_ACCOUNT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_SELECTED_ACCOUNT,
+ g_param_spec_object ("selected-account",
+ "Selected Account",
+ "Current account selected in the account-chooser",
+ TP_TYPE_ACCOUNT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
const gchar *
diff --git a/libempathy-gtk/empathy-contact-widget.c b/libempathy-gtk/empathy-contact-widget.c
index 61e04560e..93ab9d510 100644
--- a/libempathy-gtk/empathy-contact-widget.c
+++ b/libempathy-gtk/empathy-contact-widget.c
@@ -724,13 +724,11 @@ contact_widget_location_update (EmpathyContactWidget *information)
gchar *user_date;
gchar *text;
gint64 stamp;
- time_t time_;
gchar *tmp;
stamp = g_value_get_int64 (value);
- time_ = stamp;
- user_date = empathy_time_to_string_relative (time_);
+ user_date = empathy_time_to_string_relative (stamp);
tmp = g_strdup_printf ("<b>%s</b>", _("Location"));
/* translators: format is "Location, $date" */
@@ -783,7 +781,7 @@ contact_widget_location_update (EmpathyContactWidget *information)
}
else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64)
{
- time_t time_;
+ gint64 time_;
time_ = g_value_get_int64 (value);
svalue = empathy_time_to_string_utc (time_, _("%B %e, %Y at %R UTC"));
diff --git a/libempathy-gtk/empathy-images.h b/libempathy-gtk/empathy-images.h
index 2c40b2ae7..e2512d495 100644
--- a/libempathy-gtk/empathy-images.h
+++ b/libempathy-gtk/empathy-images.h
@@ -40,12 +40,17 @@ G_BEGIN_DECLS
#define EMPATHY_IMAGE_TYPING "user-typing"
#define EMPATHY_IMAGE_CONTACT_INFORMATION "gtk-info"
#define EMPATHY_IMAGE_GROUP_MESSAGE "system-users"
+#define EMPATHY_IMAGE_SMS "stock_cell-phone"
#define EMPATHY_IMAGE_VOIP "audio-input-microphone"
#define EMPATHY_IMAGE_VIDEO_CALL "camera-web"
#define EMPATHY_IMAGE_LOG "document-open-recent"
#define EMPATHY_IMAGE_DOCUMENT_SEND "document-send"
#define EMPATHY_IMAGE_AVATAR_DEFAULT "avatar-default"
+#define EMPATHY_IMAGE_CALL_MISSED "call-start"
+#define EMPATHY_IMAGE_CALL_INCOMING "call-start"
+#define EMPATHY_IMAGE_CALL_OUTGOING "call-stop"
+
G_END_DECLS
#endif /* __EMPATHY_IMAGES_ICONS_H__ */
diff --git a/libempathy-gtk/empathy-individual-menu.c b/libempathy-gtk/empathy-individual-menu.c
index 4f4a7e20c..1f01d8d54 100644
--- a/libempathy-gtk/empathy-individual-menu.c
+++ b/libempathy-gtk/empathy-individual-menu.c
@@ -150,6 +150,14 @@ individual_menu_add_personas (GtkMenuShell *menu,
gtk_widget_show (action);
}
+ /* SMS */
+ if (features & EMPATHY_INDIVIDUAL_FEATURE_SMS)
+ {
+ action = empathy_individual_sms_menu_item_new (NULL, contact);
+ gtk_menu_shell_append (GTK_MENU_SHELL (contact_submenu), action);
+ gtk_widget_show (action);
+ }
+
if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
{
/* Audio Call */
@@ -239,6 +247,17 @@ constructed (GObject *object)
}
}
+ /* SMS */
+ if (features & EMPATHY_INDIVIDUAL_FEATURE_SMS)
+ {
+ item = empathy_individual_sms_menu_item_new (individual, NULL);
+ if (item != NULL)
+ {
+ gtk_menu_shell_append (shell, item);
+ gtk_widget_show (item);
+ }
+ }
+
if (features & EMPATHY_INDIVIDUAL_FEATURE_CALL)
{
/* Audio Call */
@@ -538,6 +557,52 @@ empathy_individual_chat_menu_item_new (FolksIndividual *individual,
}
static void
+empathy_individual_sms_menu_item_activated (GtkMenuItem *item,
+ EmpathyContact *contact)
+{
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+
+ empathy_dispatcher_sms_contact_id (
+ empathy_contact_get_account (contact),
+ empathy_contact_get_id (contact),
+ gtk_get_current_event_time ());
+}
+
+GtkWidget *
+empathy_individual_sms_menu_item_new (FolksIndividual *individual,
+ EmpathyContact *contact)
+{
+ GtkWidget *item;
+ GtkWidget *image;
+
+ g_return_val_if_fail ((FOLKS_IS_INDIVIDUAL (individual) &&
+ empathy_folks_individual_contains_contact (individual)) ||
+ EMPATHY_IS_CONTACT (contact),
+ NULL);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_SMS"));
+ image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_SMS,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ gtk_widget_show (image);
+
+ if (contact != NULL)
+ {
+ menu_item_set_contact (item, contact,
+ G_CALLBACK (empathy_individual_sms_menu_item_activated),
+ EMPATHY_ACTION_SMS);
+ }
+ else
+ {
+ menu_item_set_first_contact (item, individual,
+ G_CALLBACK (empathy_individual_sms_menu_item_activated),
+ EMPATHY_ACTION_SMS);
+ }
+
+ return item;
+}
+
+static void
empathy_individual_audio_call_menu_item_activated (GtkMenuItem *item,
EmpathyContact *contact)
{
diff --git a/libempathy-gtk/empathy-individual-menu.h b/libempathy-gtk/empathy-individual-menu.h
index 5b3d220df..c2841ad6d 100644
--- a/libempathy-gtk/empathy-individual-menu.h
+++ b/libempathy-gtk/empathy-individual-menu.h
@@ -36,7 +36,8 @@ typedef enum {
EMPATHY_INDIVIDUAL_FEATURE_INFO = 1 << 4,
EMPATHY_INDIVIDUAL_FEATURE_FAVOURITE = 1 << 5,
EMPATHY_INDIVIDUAL_FEATURE_LINK = 1 << 6,
- EMPATHY_INDIVIDUAL_FEATURE_ALL = (1 << 7) - 1,
+ EMPATHY_INDIVIDUAL_FEATURE_SMS = 1 << 7,
+ EMPATHY_INDIVIDUAL_FEATURE_ALL = (1 << 8) - 1,
} EmpathyIndividualFeatureFlags;
#define EMPATHY_TYPE_INDIVIDUAL_MENU (empathy_individual_menu_get_type ())
@@ -69,6 +70,8 @@ GtkWidget * empathy_individual_menu_new (FolksIndividual *individual,
EmpathyIndividualFeatureFlags features);
GtkWidget * empathy_individual_chat_menu_item_new (FolksIndividual *individual,
EmpathyContact *contact);
+GtkWidget * empathy_individual_sms_menu_item_new (FolksIndividual *individual,
+ EmpathyContact *contact);
GtkWidget * empathy_individual_audio_call_menu_item_new (
FolksIndividual *individual,
EmpathyContact *contact);
diff --git a/libempathy-gtk/empathy-individual-view.c b/libempathy-gtk/empathy-individual-view.c
index 98c7f96d1..1e0602353 100644
--- a/libempathy-gtk/empathy-individual-view.c
+++ b/libempathy-gtk/empathy-individual-view.c
@@ -2236,8 +2236,8 @@ empathy_individual_view_get_selected_group (EmpathyIndividualView *view,
enum
{
REMOVE_DIALOG_RESPONSE_CANCEL = 0,
- REMOVE_DIALOG_RESPONSE_DELETE,
- REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK,
+ REMOVE_DIALOG_RESPONSE_REMOVE,
+ REMOVE_DIALOG_RESPONSE_REMOVE_AND_BLOCK,
};
static int
@@ -2267,17 +2267,17 @@ individual_view_remove_dialog_show (GtkWindow *parent,
/* gtk_dialog_add_button() doesn't allow us to pass a string with a
* mnemonic so we have to create the button manually. */
button = gtk_button_new_with_mnemonic (
- _("Delete and _Block"));
+ _("Remove and _Block"));
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
- REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK);
+ REMOVE_DIALOG_RESPONSE_REMOVE_AND_BLOCK);
gtk_widget_show (button);
}
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, REMOVE_DIALOG_RESPONSE_CANCEL,
- GTK_STOCK_DELETE, REMOVE_DIALOG_RESPONSE_DELETE, NULL);
+ GTK_STOCK_REMOVE, REMOVE_DIALOG_RESPONSE_REMOVE, NULL);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", secondary_text);
@@ -2307,7 +2307,7 @@ individual_view_group_remove_activate_cb (GtkMenuItem *menuitem,
group);
parent = empathy_get_toplevel_window (GTK_WIDGET (view));
if (individual_view_remove_dialog_show (parent, _("Removing group"),
- text, FALSE, NULL) == REMOVE_DIALOG_RESPONSE_DELETE)
+ text, FALSE, NULL) == REMOVE_DIALOG_RESPONSE_REMOVE)
{
EmpathyIndividualManager *manager =
empathy_individual_manager_dup_singleton ();
@@ -2416,12 +2416,12 @@ got_avatar (GObject *source_object,
res = individual_view_remove_dialog_show (parent, _("Removing contact"),
text, can_block, avatar);
- if (res == REMOVE_DIALOG_RESPONSE_DELETE ||
- res == REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK)
+ if (res == REMOVE_DIALOG_RESPONSE_REMOVE ||
+ res == REMOVE_DIALOG_RESPONSE_REMOVE_AND_BLOCK)
{
gboolean abusive;
- if (res == REMOVE_DIALOG_RESPONSE_DELETE_AND_BLOCK)
+ if (res == REMOVE_DIALOG_RESPONSE_REMOVE_AND_BLOCK)
{
if (!empathy_block_individual_dialog_show (parent, individual,
avatar, &abusive))
diff --git a/libempathy-gtk/empathy-individual-widget.c b/libempathy-gtk/empathy-individual-widget.c
index ed5e3be6f..b2020fc6c 100644
--- a/libempathy-gtk/empathy-individual-widget.c
+++ b/libempathy-gtk/empathy-individual-widget.c
@@ -588,13 +588,11 @@ location_update (EmpathyIndividualWidget *self)
gchar *user_date;
gchar *text;
gint64 stamp;
- time_t time_;
gchar *tmp;
stamp = g_value_get_int64 (value);
- time_ = stamp;
- user_date = empathy_time_to_string_relative (time_);
+ user_date = empathy_time_to_string_relative (stamp);
tmp = g_strdup_printf ("<b>%s</b>", _("Location"));
/* translators: format is "Location, $date" */
@@ -644,7 +642,7 @@ location_update (EmpathyIndividualWidget *self)
}
else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64)
{
- time_t time_;
+ gint64 time_;
time_ = g_value_get_int64 (value);
svalue = empathy_time_to_string_utc (time_, _("%B %e, %Y at %R UTC"));
diff --git a/libempathy-gtk/empathy-location-manager.c b/libempathy-gtk/empathy-location-manager.c
index 1c09c358c..8f64e325f 100644
--- a/libempathy-gtk/empathy-location-manager.c
+++ b/libempathy-gtk/empathy-location-manager.c
@@ -39,6 +39,7 @@
#include "libempathy/empathy-gsettings.h"
#include "libempathy/empathy-location.h"
#include "libempathy/empathy-utils.h"
+#include "libempathy/empathy-time.h"
#define DEBUG_FLAG EMPATHY_DEBUG_LOCATION
#include "libempathy/empathy-debug.h"
@@ -322,16 +323,15 @@ static void
update_timestamp (EmpathyLocationManager *self)
{
EmpathyLocationManagerPriv *priv= GET_PRIV (self);
+ gint64 timestamp;
GValue *new_value;
- gint64 stamp64;
- time_t timestamp;
- timestamp = time (NULL);
- stamp64 = (gint64) timestamp;
- new_value = tp_g_value_slice_new_int64 (stamp64);
+ timestamp = empathy_time_get_current ();
+ new_value = tp_g_value_slice_new_int64 (timestamp);
g_hash_table_insert (priv->location, g_strdup (EMPATHY_LOCATION_TIMESTAMP),
new_value);
- DEBUG ("\t - Timestamp: %" G_GINT64_FORMAT, stamp64);
+
+ DEBUG ("\t - Timestamp: %" G_GINT64_FORMAT, timestamp);
}
static void
diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c
index f6b5edf16..e1c9856cb 100644
--- a/libempathy-gtk/empathy-log-window.c
+++ b/libempathy-gtk/empathy-log-window.c
@@ -1,7 +1,6 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2006-2007 Imendio AB
- * Copyright (C) 2007-2008 Collabora Ltd.
+ * Copyright (C) 2007-2011 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
@@ -20,6 +19,7 @@
*
* Authors: Martyn Russell <martyn@imendio.com>
* Xavier Claessens <xclaesse@gmail.com>
+ * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
*/
#include "config.h"
@@ -30,13 +30,15 @@
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
-#include <telepathy-glib/account-manager.h>
+#include <telepathy-glib/telepathy-glib.h>
#include <telepathy-glib/proxy-subclass.h>
-#include <telepathy-logger/log-manager.h>
+#include <telepathy-logger/telepathy-logger.h>
+#include <telepathy-logger/call-event.h>
#include <extensions/extensions.h>
+#include <libempathy/action-chain-internal.h>
#include <libempathy/empathy-chatroom-manager.h>
#include <libempathy/empathy-chatroom.h>
#include <libempathy/empathy-message.h>
@@ -45,1511 +47,2868 @@
#include "empathy-log-window.h"
#include "empathy-account-chooser.h"
+#include "empathy-call-utils.h"
#include "empathy-chat-view.h"
+#include "empathy-contact-dialogs.h"
+#include "empathy-images.h"
#include "empathy-theme-manager.h"
#include "empathy-ui-utils.h"
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
#include <libempathy/empathy-debug.h>
-typedef struct {
- GtkWidget *window;
-
- GtkWidget *notebook;
-
- GtkWidget *entry_find;
- GtkWidget *button_find;
- GtkWidget *treeview_find;
- GtkWidget *scrolledwindow_find;
- EmpathyChatView *chatview_find;
- GtkWidget *button_previous;
- GtkWidget *button_next;
-
- GtkWidget *hbox_chats;
- GtkWidget *account_chooser_chats;
- GtkWidget *entry_chats;
- GtkWidget *calendar_chats;
- GtkWidget *treeview_chats;
- GtkWidget *scrolledwindow_chats;
- EmpathyChatView *chatview_chats;
-
- gchar *last_find;
-
- TplLogManager *log_manager;
-
- /* Those are only used while waiting for the account chooser to be ready */
- TpAccount *selected_account;
- gchar *selected_chat_id;
- gboolean selected_is_chatroom;
+typedef struct
+{
+ GtkWidget *window;
+
+ GtkWidget *button_profile;
+ GtkWidget *button_chat;
+ GtkWidget *button_call;
+ GtkWidget *button_video;
+
+ GtkWidget *search_entry;
+
+ GtkWidget *notebook;
+ GtkWidget *spinner;
+
+ GtkWidget *treeview_who;
+ GtkWidget *treeview_what;
+ GtkWidget *treeview_when;
+ GtkWidget *treeview_events;
+
+ GtkTreeStore *store_events;
+
+ GtkWidget *account_chooser;
+
+ gchar *last_find;
+
+ TplActionChain *chain;
+ TplLogManager *log_manager;
+
+ /* Used to cancel logger calls when no longer needed */
+ guint count;
+
+ /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
+ GList *hits;
+ guint source;
+
+ /* Only used while waiting for the account chooser to be ready */
+ TpAccount *selected_account;
+ gchar *selected_chat_id;
+ gboolean selected_is_chatroom;
} EmpathyLogWindow;
-static void log_window_destroy_cb (GtkWidget *widget,
- EmpathyLogWindow *window);
-static void log_window_entry_find_changed_cb (GtkWidget *entry,
- EmpathyLogWindow *window);
-static void log_window_find_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window);
-static void log_window_find_populate (EmpathyLogWindow *window,
- const gchar *search_criteria);
-static void log_window_find_setup (EmpathyLogWindow *window);
-static void log_window_button_find_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window);
-static void log_window_entry_find_activate_cb (GtkWidget *widget,
- EmpathyLogWindow *window);
-static void log_window_button_next_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window);
-static void log_window_button_previous_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window);
-static void log_window_chats_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window);
-static void log_window_chats_populate (EmpathyLogWindow *window);
-static void log_window_chats_setup (EmpathyLogWindow *window);
-static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
- EmpathyLogWindow *window);
-static void log_window_chats_set_selected (EmpathyLogWindow *window);
-static gboolean log_window_chats_get_selected (EmpathyLogWindow *window,
- TpAccount **account,
- TplEntity **target);
-static void log_window_chats_get_messages (EmpathyLogWindow *window,
- GDate *date_to_show);
-static void log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
- EmpathyLogWindow *window);
-static void log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
- EmpathyLogWindow *window);
-static void log_window_entry_chats_changed_cb (GtkWidget *entry,
- EmpathyLogWindow *window);
-static void log_window_entry_chats_activate_cb (GtkWidget *entry,
- EmpathyLogWindow *window);
-static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
- EmpathyLogWindow *window);
+static void log_window_destroy_cb (GtkWidget *widget,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_changed_cb (GtkWidget *entry,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_activate_cb (GtkWidget *widget,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data);
+static void log_window_who_populate (EmpathyLogWindow *window);
+static void log_window_who_setup (EmpathyLogWindow *window);
+static void log_window_when_setup (EmpathyLogWindow *window);
+static void log_window_what_setup (EmpathyLogWindow *window);
+static void log_window_events_setup (EmpathyLogWindow *window);
+static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
+ EmpathyLogWindow *window);
+static void log_window_chats_set_selected (EmpathyLogWindow *window);
+static void log_window_chats_get_messages (EmpathyLogWindow *window,
+ gboolean force_get_dates);
+static void log_window_when_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window);
+static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
+ EmpathyLogWindow *window);
static void
empathy_account_chooser_filter_has_logs (TpAccount *account,
- EmpathyAccountChooserFilterResultCallback callback,
- gpointer callback_data,
- gpointer user_data);
-
-enum {
- COL_FIND_ACCOUNT_ICON,
- COL_FIND_ACCOUNT_NAME,
- COL_FIND_ACCOUNT,
- COL_FIND_CHAT_NAME,
- COL_FIND_TARGET,
- COL_FIND_DATE,
- COL_FIND_DATE_READABLE,
- COL_FIND_COUNT
+ EmpathyAccountChooserFilterResultCallback callback,
+ gpointer callback_data,
+ gpointer user_data);
+
+enum
+{
+ PAGE_EVENTS,
+ PAGE_SPINNER,
+ PAGE_EMPTY
};
-enum {
- COL_CHAT_ICON,
- COL_CHAT_NAME,
- COL_CHAT_ACCOUNT,
- COL_CHAT_TARGET,
- COL_CHAT_COUNT
+enum
+{
+ COL_TYPE_ANY,
+ COL_TYPE_SEPARATOR,
+ COL_TYPE_NORMAL
};
-static EmpathyLogWindow *log_window = NULL;
+enum
+{
+ COL_WHO_TYPE,
+ COL_WHO_ICON,
+ COL_WHO_NAME,
+ COL_WHO_ACCOUNT,
+ COL_WHO_TARGET,
+ COL_WHO_COUNT
+};
-static void
-account_manager_prepared_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
+enum
+{
+ COL_WHAT_TYPE,
+ COL_WHAT_SUBTYPE,
+ COL_WHAT_TEXT,
+ COL_WHAT_ICON,
+ COL_WHAT_EXPANDER,
+ COL_WHAT_COUNT
+};
+
+enum
+{
+ COL_WHEN_DATE,
+ COL_WHEN_TEXT,
+ COL_WHEN_ICON,
+ COL_WHEN_COUNT
+};
+
+enum
+{
+ COL_EVENTS_TYPE,
+ COL_EVENTS_TS,
+ COL_EVENTS_PRETTY_DATE,
+ COL_EVENTS_ICON,
+ COL_EVENTS_TEXT,
+ COL_EVENTS_ACCOUNT,
+ COL_EVENTS_TARGET,
+ COL_EVENTS_EVENT,
+ COL_EVENTS_COUNT
+};
+
+#define CALENDAR_ICON "stock_calendar"
+
+/* Seconds between two messages to be considered one conversation */
+#define MAX_GAP 30*60
+
+#define WHAT_TYPE_SEPARATOR -1
+
+typedef enum
{
- TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
- EmpathyLogWindow *window = user_data;
- guint account_num;
- GList *accounts;
- GError *error = NULL;
+ EVENT_CALL_INCOMING = 1 << 0,
+ EVENT_CALL_OUTGOING = 1 << 1,
+ EVENT_CALL_MISSED = 1 << 2,
+ EVENT_CALL_ALL = 1 << 3,
+} EventSubtype;
+
+static EmpathyLogWindow *log_window = NULL;
+
+static gboolean has_element;
- if (log_window == NULL)
- return;
+#ifndef _date_copy
+#define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
+#endif
- if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
- DEBUG ("Failed to prepare account manager: %s", error->message);
- g_error_free (error);
- return;
- }
+typedef struct
+{
+ EmpathyLogWindow *window;
+ TpAccount *account;
+ TplEntity *entity;
+ GDate *date;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ guint count;
+} Ctx;
+
+static Ctx *
+ctx_new (EmpathyLogWindow *window,
+ TpAccount *account,
+ TplEntity *entity,
+ GDate *date,
+ TplEventTypeMask event_mask,
+ EventSubtype subtype,
+ guint count)
+{
+ Ctx *ctx = g_slice_new0 (Ctx);
+
+ ctx->window = window;
+ if (account != NULL)
+ ctx->account = g_object_ref (account);
+ if (entity != NULL)
+ ctx->entity = g_object_ref (entity);
+ if (date != NULL)
+ ctx->date = _date_copy (date);
+ ctx->event_mask = event_mask;
+ ctx->subtype = subtype;
+ ctx->count = count;
+
+ return ctx;
+}
- accounts = tp_account_manager_get_valid_accounts (account_manager);
- account_num = g_list_length (accounts);
- g_list_free (accounts);
+static void
+ctx_free (Ctx *ctx)
+{
+ tp_clear_object (&ctx->account);
+ tp_clear_object (&ctx->entity);
+ tp_clear_pointer (&ctx->date, g_date_free);
- gtk_widget_show_all (window->hbox_chats);
+ g_slice_free (Ctx, ctx);
}
static void
account_chooser_ready_cb (EmpathyAccountChooser *chooser,
- EmpathyLogWindow *window)
+ EmpathyLogWindow *window)
{
- gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1);
-
- /* We'll display the account once the model has been populate with the chats
- * of this account. */
- empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
- window->account_chooser_chats), window->selected_account);
+ /* We'll display the account once the model has been populate with the chats
+ * of this account. */
+ empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
+ window->account_chooser), window->selected_account);
}
static void
select_account_once_ready (EmpathyLogWindow *self,
- TpAccount *account,
- const gchar *chat_id,
- gboolean is_chatroom)
+ TpAccount *account,
+ const gchar *chat_id,
+ gboolean is_chatroom)
{
- EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser_chats);
+ EmpathyAccountChooser *account_chooser;
- tp_clear_object (&self->selected_account);
- self->selected_account = g_object_ref (account);
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser);
- g_free (self->selected_chat_id);
- self->selected_chat_id = g_strdup (chat_id);
+ tp_clear_object (&self->selected_account);
+ self->selected_account = g_object_ref (account);
- self->selected_is_chatroom = is_chatroom;
+ g_free (self->selected_chat_id);
+ self->selected_chat_id = g_strdup (chat_id);
- if (empathy_account_chooser_is_ready (account_chooser))
- account_chooser_ready_cb (account_chooser, self);
- else
- /* Chat will be selected once the account chooser is ready */
- g_signal_connect (account_chooser, "ready",
- G_CALLBACK (account_chooser_ready_cb), self);
-}
+ self->selected_is_chatroom = is_chatroom;
-GtkWidget *
-empathy_log_window_show (TpAccount *account,
- const gchar *chat_id,
- gboolean is_chatroom,
- GtkWindow *parent)
-{
- EmpathyAccountChooser *account_chooser;
- TpAccountManager *account_manager;
- GtkBuilder *gui;
- gchar *filename;
- EmpathyLogWindow *window;
- EmpathyThemeManager *theme_mgr;
- GtkWidget *menu_delete;
-
- if (log_window != NULL) {
- gtk_window_present (GTK_WINDOW (log_window->window));
-
- if (account != NULL && chat_id != NULL) {
- gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook), 1);
- select_account_once_ready (log_window, account, chat_id, is_chatroom);
- }
-
- return log_window->window;
- }
-
- log_window = g_new0 (EmpathyLogWindow, 1);
- log_window->log_manager = tpl_log_manager_dup_singleton ();
-
- window = log_window;
-
- filename = empathy_file_lookup ("empathy-log-window.ui",
- "libempathy-gtk");
- gui = empathy_builder_get_file (filename,
- "log_window", &window->window,
- "menu_delete", &menu_delete,
- "notebook", &window->notebook,
- "entry_find", &window->entry_find,
- "button_find", &window->button_find,
- "treeview_find", &window->treeview_find,
- "scrolledwindow_find", &window->scrolledwindow_find,
- "button_previous", &window->button_previous,
- "button_next", &window->button_next,
- "entry_chats", &window->entry_chats,
- "calendar_chats", &window->calendar_chats,
- "hbox_chats", &window->hbox_chats,
- "treeview_chats", &window->treeview_chats,
- "scrolledwindow_chats", &window->scrolledwindow_chats,
- NULL);
- g_free (filename);
-
- empathy_builder_connect (gui, window,
- "log_window", "destroy", log_window_destroy_cb,
- "entry_find", "changed", log_window_entry_find_changed_cb,
- "entry_find", "activate", log_window_entry_find_activate_cb,
- "button_previous", "clicked", log_window_button_previous_clicked_cb,
- "button_next", "clicked", log_window_button_next_clicked_cb,
- "button_find", "clicked", log_window_button_find_clicked_cb,
- "entry_chats", "changed", log_window_entry_chats_changed_cb,
- "entry_chats", "activate", log_window_entry_chats_activate_cb,
- "menu_delete", "activate", log_window_delete_menu_clicked_cb,
- NULL);
-
- g_object_unref (gui);
-
- g_object_add_weak_pointer (G_OBJECT (window->window),
- (gpointer) &log_window);
-
- /* We set this up here so we can block it when needed. */
- g_signal_connect (window->calendar_chats, "day-selected",
- G_CALLBACK (log_window_calendar_chats_day_selected_cb),
- window);
- g_signal_connect (window->calendar_chats, "month-changed",
- G_CALLBACK (log_window_calendar_chats_month_changed_cb),
- window);
-
- /* Configure Search EmpathyChatView */
- theme_mgr = empathy_theme_manager_dup_singleton ();
- window->chatview_find = empathy_theme_manager_create_view (theme_mgr);
- gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find),
- GTK_WIDGET (window->chatview_find));
- gtk_widget_show (GTK_WIDGET (window->chatview_find));
-
- /* Configure Contacts EmpathyChatView */
- window->chatview_chats = empathy_theme_manager_create_view (theme_mgr);
- gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats),
- GTK_WIDGET (window->chatview_chats));
- gtk_widget_show (GTK_WIDGET (window->chatview_chats));
- g_object_unref (theme_mgr);
-
- /* Account chooser for chats */
- window->account_chooser_chats = empathy_account_chooser_new ();
- account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
- empathy_account_chooser_set_filter (account_chooser, empathy_account_chooser_filter_has_logs, NULL);
-
- gtk_box_pack_start (GTK_BOX (window->hbox_chats),
- window->account_chooser_chats,
- FALSE, TRUE, 0);
-
- g_signal_connect (window->account_chooser_chats, "changed",
- G_CALLBACK (log_window_chats_accounts_changed_cb),
- window);
-
- /* Populate */
- account_manager = tp_account_manager_dup ();
- tp_account_manager_prepare_async (account_manager, NULL,
- account_manager_prepared_cb, window);
- g_object_unref (account_manager);
-
- /* Search List */
- log_window_find_setup (window);
-
- /* Contacts */
- log_window_chats_setup (window);
- log_window_chats_populate (window);
-
- if (account != NULL && chat_id != NULL)
- select_account_once_ready (window, account, chat_id, is_chatroom);
-
- if (parent != NULL) {
- gtk_window_set_transient_for (GTK_WINDOW (window->window),
- GTK_WINDOW (parent));
- }
-
- gtk_widget_show (window->window);
-
- return window->window;
+ if (empathy_account_chooser_is_ready (account_chooser))
+ account_chooser_ready_cb (account_chooser, self);
+ else
+ /* Chat will be selected once the account chooser is ready */
+ g_signal_connect (account_chooser, "ready",
+ G_CALLBACK (account_chooser_ready_cb), self);
}
static void
-log_window_destroy_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
+toolbutton_profile_clicked (GtkToolButton *toolbutton,
+ EmpathyLogWindow *window)
{
- g_free (window->last_find);
- g_object_unref (window->log_manager);
- tp_clear_object (&window->selected_account);
- g_free (window->selected_chat_id);
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ TplEntity *target;
+ EmpathyContact *contact;
+ gint type;
+
+ g_return_if_fail (window != NULL);
- g_free (window);
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &target,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ contact = empathy_contact_from_tpl_contact (account, target);
+ empathy_contact_information_dialog_show (contact,
+ GTK_WINDOW (window->window));
+ g_object_unref (contact);
+
+ g_object_unref (account);
+ g_object_unref (target);
}
-/*
- * Search code.
- */
static void
-log_window_entry_find_changed_cb (GtkWidget *entry,
- EmpathyLogWindow *window)
+toolbutton_chat_clicked (GtkToolButton *toolbutton,
+ EmpathyLogWindow *window)
{
- const gchar *str;
- gboolean is_sensitive = TRUE;
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ TplEntity *target;
+ EmpathyContact *contact;
+ gint type;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+ g_return_if_fail (window != NULL);
- is_sensitive &= !EMP_STR_EMPTY (str);
- is_sensitive &=
- !window->last_find ||
- (window->last_find && tp_strdiff (window->last_find, str));
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
- gtk_widget_set_sensitive (window->button_find, is_sensitive);
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &target,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ contact = empathy_contact_from_tpl_contact (account, target);
+ empathy_dispatcher_chat_with_contact (contact,
+ gtk_get_current_event_time ());
+
+ g_object_unref (contact);
+ g_object_unref (account);
+ g_object_unref (target);
}
static void
-got_events_for_date_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
-{
- EmpathyLogWindow *window = user_data;
- GList *messages;
- GList *l;
- gboolean can_do_previous;
- gboolean can_do_next;
- GError *error = NULL;
-
- if (log_window == NULL)
- return;
-
- if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
- result, &messages, &error)) {
- DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
- error->message);
- empathy_chat_view_append_event (window->chatview_find,
- "Unable to retrieve messages for the selected date");
- g_error_free (error);
- return;
- }
-
- for (l = messages; l; l = l->next) {
- EmpathyMessage *message;
-
- g_assert (TPL_IS_EVENT (l->data));
-
- message = empathy_message_from_tpl_log_event (l->data);
- g_object_unref (l->data);
- empathy_chat_view_append_message (window->chatview_find, message);
- g_object_unref (message);
- }
- g_list_free (messages);
-
- /* Scroll to the most recent messages */
- empathy_chat_view_scroll (window->chatview_find, TRUE);
-
- /* Highlight and find messages */
- empathy_chat_view_highlight (window->chatview_find,
- window->last_find,
- FALSE);
- empathy_chat_view_find_next (window->chatview_find,
- window->last_find,
- TRUE,
- FALSE);
- empathy_chat_view_find_abilities (window->chatview_find,
- window->last_find,
- FALSE,
- &can_do_previous,
- &can_do_next);
- gtk_widget_set_sensitive (window->button_previous, can_do_previous);
- gtk_widget_set_sensitive (window->button_next, can_do_next);
- gtk_widget_set_sensitive (window->button_find, FALSE);
-}
-
-static GDate *
-gdate_from_str (const gchar *str)
-{
- guint u;
- guint day, month, year;
-
- if (sscanf (str, "%u", &u) != 1)
- return NULL;
-
- day = (u % 100);
- month = ((u / 100) % 100);
- year = (u / 10000);
-
- if (!g_date_valid_dmy (day, month, year))
- return NULL;
-
- return g_date_new_dmy (day, month, year);
+toolbutton_av_clicked (GtkToolButton *toolbutton,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ gchar *contact;
+ gint type;
+ gboolean video;
+
+ g_return_if_fail (window != NULL);
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_NAME, &contact,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ video = (GTK_WIDGET (toolbutton) == window->button_video);
+
+ empathy_call_new_with_streams (contact, account,
+ TRUE, video, gtk_get_current_event_time ());
+
+ g_free (contact);
+ g_object_unref (account);
}
-static void
-log_window_find_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window)
+GtkWidget *
+empathy_log_window_show (TpAccount *account,
+ const gchar *chat_id,
+ gboolean is_chatroom,
+ GtkWindow *parent)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeIter iter;
- TpAccount *account;
- TplEntity *target;
- gchar *date;
- GDate *gdate;
+ EmpathyAccountChooser *account_chooser;
+ GtkBuilder *gui;
+ gchar *filename;
+ EmpathyLogWindow *window;
+ GtkWidget *vbox, *accounts, *search, *label, *quit;
+
+ if (log_window != NULL)
+ {
+ gtk_window_present (GTK_WINDOW (log_window->window));
+
+ if (account != NULL && chat_id != NULL)
+ select_account_once_ready (log_window, account, chat_id, is_chatroom);
+
+ return log_window->window;
+ }
+
+ log_window = g_new0 (EmpathyLogWindow, 1);
+ log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
+
+ log_window->log_manager = tpl_log_manager_dup_singleton ();
+
+ window = log_window;
+
+ filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
+ gui = empathy_builder_get_file (filename,
+ "log_window", &window->window,
+ "toolbutton_profile", &window->button_profile,
+ "toolbutton_chat", &window->button_chat,
+ "toolbutton_call", &window->button_call,
+ "toolbutton_video", &window->button_video,
+ "toolbutton_accounts", &accounts,
+ "toolbutton_search", &search,
+ "imagemenuitem_quit", &quit,
+ "treeview_who", &window->treeview_who,
+ "treeview_what", &window->treeview_what,
+ "treeview_when", &window->treeview_when,
+ "treeview_events", &window->treeview_events,
+ "notebook", &window->notebook,
+ "spinner", &window->spinner,
+ NULL);
+ g_free (filename);
+
+ empathy_builder_connect (gui, window,
+ "log_window", "destroy", log_window_destroy_cb,
+ "toolbutton_profile", "clicked", toolbutton_profile_clicked,
+ "toolbutton_chat", "clicked", toolbutton_chat_clicked,
+ "toolbutton_call", "clicked", toolbutton_av_clicked,
+ "toolbutton_video", "clicked", toolbutton_av_clicked,
+ "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
+ NULL);
+
+ g_object_unref (gui);
+
+ g_object_add_weak_pointer (G_OBJECT (window->window),
+ (gpointer) &log_window);
+
+ g_signal_connect_swapped (quit, "activate",
+ G_CALLBACK (gtk_widget_destroy), window->window);
+
+ /* Account chooser for chats */
+ vbox = gtk_vbox_new (FALSE, 3);
+
+ window->account_chooser = empathy_account_chooser_new ();
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
+ empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
+ empathy_account_chooser_set_filter (account_chooser,
+ empathy_account_chooser_filter_has_logs, NULL);
+ empathy_account_chooser_set_all (account_chooser);
+
+ g_signal_connect (window->account_chooser, "changed",
+ G_CALLBACK (log_window_chats_accounts_changed_cb),
+ window);
+
+ label = gtk_label_new (_("Show"));
- /* Get selected information */
- view = GTK_TREE_VIEW (window->treeview_find);
- model = gtk_tree_view_get_model (view);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ window->account_chooser,
+ FALSE, FALSE, 0);
- if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
- gtk_widget_set_sensitive (window->button_previous, FALSE);
- gtk_widget_set_sensitive (window->button_next, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ label,
+ FALSE, FALSE, 0);
- empathy_chat_view_clear (window->chatview_find);
+ gtk_widget_show_all (vbox);
+ gtk_container_add (GTK_CONTAINER (accounts), vbox);
- return;
- }
+ /* Search entry */
+ vbox = gtk_vbox_new (FALSE, 3);
- gtk_widget_set_sensitive (window->button_previous, TRUE);
- gtk_widget_set_sensitive (window->button_next, TRUE);
+ window->search_entry = gtk_entry_new ();
+ gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
+ GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
+ gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
+ GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
- gtk_tree_model_get (model, &iter,
- COL_FIND_ACCOUNT, &account,
- COL_FIND_TARGET, &target,
- COL_FIND_DATE, &date,
- -1);
+ label = gtk_label_new (_("Search"));
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_find);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ window->search_entry,
+ FALSE, FALSE, 0);
- /* Turn off scrolling temporarily */
- empathy_chat_view_scroll (window->chatview_find, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ label,
+ FALSE, FALSE, 0);
- /* Get messages */
- gdate = gdate_from_str (date);
+ gtk_widget_show_all (vbox);
+ gtk_container_add (GTK_CONTAINER (search), vbox);
- if (gdate != NULL) {
- tpl_log_manager_get_events_for_date_async (window->log_manager,
- account,
- target,
- TPL_EVENT_MASK_TEXT,
- gdate,
- got_events_for_date_cb,
- window);
+ g_signal_connect (window->search_entry, "changed",
+ G_CALLBACK (log_window_search_entry_changed_cb),
+ window);
- g_date_free (gdate);
- }
+ g_signal_connect (window->search_entry, "activate",
+ G_CALLBACK (log_window_search_entry_activate_cb),
+ window);
- g_object_unref (account);
- g_object_unref (target);
- g_free (date);
-}
+ g_signal_connect (window->search_entry, "icon-press",
+ G_CALLBACK (log_window_search_entry_icon_pressed_cb),
+ window);
+ /* Contacts */
+ log_window_events_setup (window);
+ log_window_who_setup (window);
+ log_window_what_setup (window);
+ log_window_when_setup (window);
-static void
-log_manager_searched_new_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
-{
- GList *hits;
- GList *l;
- GtkTreeIter iter;
- GtkListStore *store = user_data;
- GError *error = NULL;
-
- if (log_window == NULL)
- return;
-
- if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager), result,
- &hits, &error)) {
- DEBUG ("%s. Aborting", error->message);
- g_error_free (error);
- return;
- }
-
- for (l = hits; l; l = l->next) {
- TplLogSearchHit *hit;
- const gchar *account_name;
- const gchar *account_icon;
- gchar date_readable[255];
- gchar tmp[255];
-
- hit = l->data;
-
- /* Protect against invalid data (corrupt or old log files. */
- if (hit->account == NULL || hit->target == NULL) {
- continue;
- }
-
- g_date_strftime (date_readable, sizeof (date_readable),
- EMPATHY_DATE_FORMAT_DISPLAY_SHORT, hit->date);
-
- g_date_strftime (tmp, sizeof (tmp),
- "%Y%m%d", hit->date);
-
- account_name = tp_account_get_display_name (hit->account);
- account_icon = tp_account_get_icon_name (hit->account);
-
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter,
- COL_FIND_ACCOUNT_ICON, account_icon,
- COL_FIND_ACCOUNT_NAME, account_name,
- COL_FIND_ACCOUNT, hit->account,
- COL_FIND_CHAT_NAME, tpl_entity_get_alias (hit->target),
- COL_FIND_TARGET, hit->target,
- COL_FIND_DATE, tmp,
- COL_FIND_DATE_READABLE, date_readable,
- -1);
-
- /* FIXME: Update COL_FIND_CHAT_NAME */
- if (tpl_entity_get_entity_type (hit->target) == TPL_ENTITY_ROOM) {
- } else {
- }
- }
-
- if (hits != NULL) {
- tpl_log_manager_search_free (hits);
- }
+ log_window_who_populate (window);
+
+ if (account != NULL && chat_id != NULL)
+ select_account_once_ready (window, account, chat_id, is_chatroom);
+
+ if (parent != NULL)
+ gtk_window_set_transient_for (GTK_WINDOW (window->window),
+ GTK_WINDOW (parent));
+
+ gtk_widget_show (window->window);
+
+ return window->window;
}
static void
-log_window_find_populate (EmpathyLogWindow *window,
- const gchar *search_criteria)
+log_window_destroy_cb (GtkWidget *widget,
+ EmpathyLogWindow *window)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkListStore *store;
+ if (window->source != 0)
+ g_source_remove (window->source);
- view = GTK_TREE_VIEW (window->treeview_find);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
- store = GTK_LIST_STORE (model);
+ g_free (window->last_find);
+ _tpl_action_chain_free (window->chain);
+ g_object_unref (window->log_manager);
+ tp_clear_object (&window->selected_account);
+ g_free (window->selected_chat_id);
- empathy_chat_view_clear (window->chatview_find);
+ g_free (window);
+}
- gtk_list_store_clear (store);
+static gboolean
+account_equal (TpAccount *a,
+ TpAccount *b)
+{
+ return g_str_equal (tp_proxy_get_object_path (a),
+ tp_proxy_get_object_path (b));
+}
- if (EMP_STR_EMPTY (search_criteria)) {
- /* Just clear the search. */
- return;
- }
+static gboolean
+entity_equal (TplEntity *a,
+ TplEntity *b)
+{
+ return g_str_equal (tpl_entity_get_identifier (a),
+ tpl_entity_get_identifier (b));
+}
- tpl_log_manager_search_async (window->log_manager,
- search_criteria, TPL_EVENT_MASK_TEXT,
- log_manager_searched_new_cb, (gpointer) store);
+static gboolean
+is_same_confroom (TplEvent *e1,
+ TplEvent *e2)
+{
+ TplEntity *sender1 = tpl_event_get_sender (e1);
+ TplEntity *receiver1 = tpl_event_get_receiver (e1);
+ TplEntity *sender2 = tpl_event_get_sender (e2);
+ TplEntity *receiver2 = tpl_event_get_receiver (e2);
+ TplEntity *room1, *room2;
+
+ if (tpl_entity_get_entity_type (sender1) == TPL_ENTITY_ROOM)
+ room1 = sender1;
+ else if (tpl_entity_get_entity_type (receiver1) == TPL_ENTITY_ROOM)
+ room1 = receiver1;
+ else
+ return FALSE;
+
+ if (tpl_entity_get_entity_type (sender2) == TPL_ENTITY_ROOM)
+ room2 = sender2;
+ else if (tpl_entity_get_entity_type (receiver2) == TPL_ENTITY_ROOM)
+ room2 = receiver2;
+ else
+ return FALSE;
+
+ return g_str_equal (tpl_entity_get_identifier (room1),
+ tpl_entity_get_identifier (room1));
}
-static void
-log_window_find_setup (EmpathyLogWindow *window)
-{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeSortable *sortable;
- GtkTreeViewColumn *column;
- GtkListStore *store;
- GtkCellRenderer *cell;
- gint offset;
-
- view = GTK_TREE_VIEW (window->treeview_find);
- selection = gtk_tree_view_get_selection (view);
-
- /* New store */
- store = gtk_list_store_new (COL_FIND_COUNT,
- G_TYPE_STRING, /* account icon name */
- G_TYPE_STRING, /* account name */
- TP_TYPE_ACCOUNT, /* account */
- G_TYPE_STRING, /* chat name */
- TPL_TYPE_ENTITY, /* target */
- G_TYPE_STRING, /* date */
- G_TYPE_STRING); /* date_readable */
-
- model = GTK_TREE_MODEL (store);
- sortable = GTK_TREE_SORTABLE (store);
-
- gtk_tree_view_set_model (view, model);
-
- /* New column */
- column = gtk_tree_view_column_new ();
-
- cell = gtk_cell_renderer_pixbuf_new ();
- gtk_tree_view_column_pack_start (column, cell, FALSE);
- gtk_tree_view_column_add_attribute (column, cell,
- "icon-name",
- COL_FIND_ACCOUNT_ICON);
-
- cell = gtk_cell_renderer_text_new ();
- gtk_tree_view_column_pack_start (column, cell, TRUE);
- gtk_tree_view_column_add_attribute (column, cell,
- "text",
- COL_FIND_ACCOUNT_NAME);
-
- gtk_tree_view_column_set_title (column, _("Account"));
- gtk_tree_view_append_column (view, column);
-
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_clickable (column, TRUE);
-
- cell = gtk_cell_renderer_text_new ();
- offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"),
- cell, "text", COL_FIND_CHAT_NAME,
- NULL);
-
- column = gtk_tree_view_get_column (view, offset - 1);
- gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_clickable (column, TRUE);
-
- cell = gtk_cell_renderer_text_new ();
- offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"),
- cell, "text", COL_FIND_DATE_READABLE,
- NULL);
-
- column = gtk_tree_view_get_column (view, offset - 1);
- gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE);
- gtk_tree_view_column_set_resizable (column, TRUE);
- gtk_tree_view_column_set_clickable (column, TRUE);
-
- /* Set up treeview properties */
- gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
- gtk_tree_sortable_set_sort_column_id (sortable,
- COL_FIND_DATE,
- GTK_SORT_ASCENDING);
-
- /* Set up signals */
- g_signal_connect (selection, "changed",
- G_CALLBACK (log_window_find_changed_cb),
- window);
-
- g_object_unref (store);
+static TplEntity *
+event_get_target (TplEvent *event)
+{
+ TplEntity *sender = tpl_event_get_sender (event);
+ TplEntity *receiver = tpl_event_get_receiver (event);
+
+ if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
+ return receiver;
+
+ return sender;
}
-static void
-start_find_search (EmpathyLogWindow *window)
+static gboolean
+model_is_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ TplEvent *event)
{
- const gchar *str;
+ TplEvent *stored_event;
+ TplEntity *target;
+ TpAccount *account;
+ gboolean found = FALSE;
+ GtkTreeIter parent;
+
+ if (gtk_tree_model_iter_parent (model, &parent, iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, iter,
+ COL_EVENTS_ACCOUNT, &account,
+ COL_EVENTS_TARGET, &target,
+ COL_EVENTS_EVENT, &stored_event,
+ -1);
+
+ if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
+ account_equal (account, tpl_event_get_account (event)) &&
+ (entity_equal (target, event_get_target (event)) ||
+ is_same_confroom (event, stored_event)))
+ {
+ GtkTreeIter child;
+ gint64 timestamp;
+
+ gtk_tree_model_iter_nth_child (model, &child, iter,
+ gtk_tree_model_iter_n_children (model, iter) - 1);
+
+ gtk_tree_model_get (model, &child,
+ COL_EVENTS_TS, &timestamp,
+ -1);
+
+ if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
+ {
+ /* The gap is smaller than 30 min */
+ found = TRUE;
+ }
+ }
+
+ g_object_unref (stored_event);
+ g_object_unref (account);
+ g_object_unref (target);
+
+ return found;
+}
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+static const gchar *
+get_contact_alias_for_message (EmpathyMessage *message)
+{
+ EmpathyContact *sender, *receiver;
- /* Don't find the same crap again */
- if (window->last_find && !tp_strdiff (window->last_find, str)) {
- return;
- }
+ sender = empathy_message_get_sender (message);
+ receiver = empathy_message_get_receiver (message);
- g_free (window->last_find);
- window->last_find = g_strdup (str);
+ if (empathy_contact_is_user (sender))
+ return empathy_contact_get_alias (receiver);
- log_window_find_populate (window, str);
+ return empathy_contact_get_alias (sender);
}
static void
-log_window_button_find_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
+get_parent_iter_for_message (TplEvent *event,
+ EmpathyMessage *message,
+ GtkTreeIter *parent)
{
- start_find_search (window);
+ GtkTreeStore *store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean parent_found = FALSE;
+ gboolean next;
+
+ store = log_window->store_events;
+ model = GTK_TREE_MODEL (store);
+
+ for (next = gtk_tree_model_get_iter_first (model, &iter);
+ next;
+ next = gtk_tree_model_iter_next (model, &iter))
+ {
+ if ((parent_found = model_is_parent (model, &iter, event)))
+ break;
+ }
+
+ if (parent_found)
+ {
+ *parent = iter;
+ }
+ else
+ {
+ GDateTime *date;
+ gchar *body, *pretty_date;
+
+ date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (date,
+ C_("A date with the time", "%A, %e %B %Y %X"));
+
+ body = g_strdup_printf (_("Chat with %s"),
+ get_contact_alias_for_message (message));
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ICON, "stock_text_justify",
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ *parent = iter;
+
+ g_free (body);
+ g_free (pretty_date);
+ g_date_time_unref (date);
+ }
}
-static void
-log_window_entry_find_activate_cb (GtkWidget *entry,
- EmpathyLogWindow *self)
+static const gchar *
+get_icon_for_event (TplEvent *event)
{
- start_find_search (self);
+ const gchar *icon = NULL;
+
+ if (TPL_IS_CALL_EVENT (event))
+ {
+ TplCallEvent *call = TPL_CALL_EVENT (event);
+ TplCallEndReason reason = tpl_call_event_get_end_reason (call);
+ TplEntity *sender = tpl_event_get_sender (event);
+ TplEntity *receiver = tpl_event_get_receiver (event);
+
+ if (reason == TPL_CALL_END_REASON_NO_ANSWER)
+ icon = EMPATHY_IMAGE_CALL_MISSED;
+ else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
+ icon = EMPATHY_IMAGE_CALL_OUTGOING;
+ else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
+ icon = EMPATHY_IMAGE_CALL_INCOMING;
+ }
+
+ return icon;
}
static void
-log_window_button_next_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
-{
- if (window->last_find) {
- gboolean can_do_previous;
- gboolean can_do_next;
-
- empathy_chat_view_find_next (window->chatview_find,
- window->last_find,
- FALSE,
- FALSE);
- empathy_chat_view_find_abilities (window->chatview_find,
- window->last_find,
- FALSE,
- &can_do_previous,
- &can_do_next);
- gtk_widget_set_sensitive (window->button_previous, can_do_previous);
- gtk_widget_set_sensitive (window->button_next, can_do_next);
- }
+log_window_append_chat_message (TplEvent *event,
+ EmpathyMessage *message)
+{
+ GtkTreeStore *store = log_window->store_events;
+ GtkTreeIter iter, parent;
+ gchar *pretty_date, *body;
+ GDateTime *date;
+
+ date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (date, "%X");
+
+ get_parent_iter_for_message (event, message, &parent);
+
+ if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
+ == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
+ {
+ body = g_strdup_printf ("* %s %s",
+ tpl_entity_get_alias (tpl_event_get_sender (event)),
+ empathy_message_get_body (message));
+ }
+ else
+ {
+ body = g_strdup_printf (
+ C_("First is a contact, second is what was said", "%s: %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (event)),
+ empathy_message_get_body (message));
+ }
+
+ gtk_tree_store_append (store, &iter, &parent);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ICON, get_icon_for_event (event),
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ g_free (body);
+ g_free (pretty_date);
+ g_date_time_unref (date);
}
static void
-log_window_button_previous_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
-{
- if (window->last_find) {
- gboolean can_do_previous;
- gboolean can_do_next;
-
- empathy_chat_view_find_previous (window->chatview_find,
- window->last_find,
- FALSE,
- FALSE);
- empathy_chat_view_find_abilities (window->chatview_find,
- window->last_find,
- FALSE,
- &can_do_previous,
- &can_do_next);
- gtk_widget_set_sensitive (window->button_previous, can_do_previous);
- gtk_widget_set_sensitive (window->button_next, can_do_next);
- }
+log_window_append_call (TplEvent *event,
+ EmpathyMessage *message)
+{
+ TplCallEvent *call = TPL_CALL_EVENT (event);
+ GtkTreeStore *store = log_window->store_events;
+ GtkTreeIter iter, child;
+ gchar *pretty_date, *duration, *finished;
+ GDateTime *started_date, *finished_date;
+ GTimeSpan span;
+
+ started_date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (started_date,
+ C_("A date with the time", "%A, %e %B %Y %X"));
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, empathy_message_get_body (message),
+ COL_EVENTS_ICON, get_icon_for_event (event),
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
+ {
+ gchar *body;
+
+ span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
+ if (span < 60)
+ duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
+ else
+ duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
+ span / 60);
+
+ finished_date = g_date_time_add (started_date, -span);
+ finished = g_date_time_format (finished_date, "%X");
+ g_date_time_unref (finished_date);
+
+ body = g_strdup_printf (_("Call took %s, ended at %s"),
+ duration, finished);
+
+ g_free (duration);
+ g_free (finished);
+
+ gtk_tree_store_append (store, &child, &iter);
+ gtk_tree_store_set (store, &child,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ g_free (body);
+ }
+
+ g_free (pretty_date);
+ g_date_time_unref (started_date);
}
-/*
- * Chats Code
- */
+static void
+log_window_append_message (TplEvent *event,
+ EmpathyMessage *message)
+{
+ if (TPL_IS_TEXT_EVENT (event))
+ log_window_append_chat_message (event, message);
+ else if (TPL_IS_CALL_EVENT (event))
+ log_window_append_call (event, message);
+ else
+ DEBUG ("Message type not handled");
+}
static void
-log_window_chats_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window)
+add_all_accounts_and_entities (GList **accounts,
+ GList **entities)
{
- gboolean selected;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ return;
+
+ do
+ {
+ TpAccount *account;
+ TplEntity *entity;
+ gint type;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &entity,
+ COL_WHO_TYPE, &type,
+ -1);
- /* The calendar has to be sensitive only if there is something selected */
- selected = log_window_chats_get_selected (window, NULL, NULL);
- gtk_widget_set_sensitive (window->calendar_chats, selected);
+ if (type != COL_TYPE_NORMAL)
+ continue;
- /* Use last date by default */
- gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
+ if (accounts != NULL)
+ *accounts = g_list_append (*accounts, account);
- log_window_chats_get_messages (window, NULL);
+ if (entities != NULL)
+ *entities = g_list_append (*entities, entity);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
}
-static void
-log_manager_got_entities_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
-{
- GList *entities;
- GList *l;
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkListStore *store;
- GtkTreeIter iter;
- GError *error = NULL;
- gboolean select_account = FALSE;
- TpAccount *account = user_data;
-
- if (log_window == NULL)
- goto out;
-
- if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
- result, &entities, &error)) {
- DEBUG ("%s. Aborting", error->message);
- g_error_free (error);
- goto out;
- }
-
- view = GTK_TREE_VIEW (log_window->treeview_chats);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
- store = GTK_LIST_STORE (model);
-
- for (l = entities; l; l = l->next) {
- TplEntity *entity;
-
- entity = TPL_ENTITY (l->data);
-
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter,
- COL_CHAT_ICON, "empathy-available", /* FIXME */
- COL_CHAT_NAME, tpl_entity_get_alias (entity),
- COL_CHAT_ACCOUNT, account,
- COL_CHAT_TARGET, entity,
- -1);
-
- if (log_window->selected_account != NULL &&
- !tp_strdiff (tp_proxy_get_object_path (account),
- tp_proxy_get_object_path (log_window->selected_account)))
- select_account = TRUE;
-
- /* FIXME: Update COL_CHAT_ICON/NAME */
- if (tpl_entity_get_entity_type (entity) == TPL_ENTITY_ROOM) {
- } else {
- }
- }
- g_list_free_full (entities, g_object_unref);
-
- /* Unblock signals */
- g_signal_handlers_unblock_by_func (selection,
- log_window_chats_changed_cb,
- log_window);
-
- /* We display the selected account if we populate the model with chats from
- * this account. */
- if (select_account)
- log_window_chats_set_selected (log_window);
+static gboolean
+log_window_get_selected (EmpathyLogWindow *window,
+ GList **accounts,
+ GList **entities,
+ GList **dates,
+ TplEventTypeMask *event_mask,
+ EventSubtype *subtype)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ TplEventTypeMask ev = 0;
+ EventSubtype st = 0;
+ GList *paths, *l;
+ gint type;
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ if (paths == NULL)
+ return FALSE;
+
+ if (accounts != NULL)
+ *accounts = NULL;
+ if (entities != NULL)
+ *entities = NULL;
+
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ TpAccount *account;
+ TplEntity *entity;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &entity,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ if (type == COL_TYPE_ANY)
+ {
+ if (accounts != NULL || entities != NULL)
+ add_all_accounts_and_entities (accounts, entities);
+ break;
+ }
+
+ if (accounts != NULL)
+ *accounts = g_list_append (*accounts, g_object_ref (account));
+
+ if (entities != NULL)
+ *entities = g_list_append (*entities, g_object_ref (entity));
+
+ g_object_unref (account);
+ g_object_unref (entity);
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ view = GTK_TREE_VIEW (window->treeview_what);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ TplEventTypeMask mask;
+ EventSubtype submask;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHAT_TYPE, &mask,
+ COL_WHAT_SUBTYPE, &submask,
+ -1);
+
+ ev |= mask;
+ st |= submask;
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ view = GTK_TREE_VIEW (window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ if (dates != NULL)
+ {
+ *dates = NULL;
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ GDate *date;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHEN_DATE, &date,
+ -1);
+
+ *dates = g_list_append (*dates, date);
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+ }
+
+ if (event_mask != NULL)
+ *event_mask = ev;
+
+ if (subtype != NULL)
+ *subtype = st;
+
+ return TRUE;
+}
-out:
- g_object_unref (account);
+static gboolean
+model_has_entity (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ TplLogSearchHit *hit = data;
+ TplEntity *e;
+ TpAccount *a;
+ gboolean ret = FALSE;
+
+ gtk_tree_model_get (model, iter,
+ COL_WHO_TARGET, &e,
+ COL_WHO_ACCOUNT, &a,
+ -1);
+
+ if (e != NULL && entity_equal (hit->target, e) &&
+ a != NULL && account_equal (hit->account, a))
+ {
+ ret = has_element = TRUE;
+ }
+
+ tp_clear_object (&e);
+ tp_clear_object (&a);
+
+ return ret;
+}
+
+static gboolean
+model_has_date (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GDate *date = data;
+ GDate *d;
+
+ gtk_tree_model_get (model, iter,
+ COL_WHEN_DATE, &d,
+ -1);
+
+ if (!g_date_compare (date, d))
+ {
+ has_element = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
}
static void
-log_window_chats_populate (EmpathyLogWindow *window)
+get_events_for_date (TplActionChain *chain, gpointer user_data);
+
+static void
+populate_events_from_search_hits (GList *accounts,
+ GList *targets,
+ GList *dates)
{
- EmpathyAccountChooser *account_chooser;
- TpAccount *account;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ GDate *anytime;
+ GList *l;
+ gboolean is_anytime = FALSE;
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkListStore *store;
+ if (!log_window_get_selected (log_window,
+ NULL, NULL, NULL, &event_mask, &subtype))
+ return;
- account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
- account = empathy_account_chooser_dup_account (account_chooser);
+ anytime = g_date_new_dmy (2, 1, -1);
+ if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
+ is_anytime = TRUE;
+
+ for (l = log_window->hits; l != NULL; l = l->next)
+ {
+ TplLogSearchHit *hit = l->data;
+ GList *acc, *targ;
+ gboolean found = FALSE;
+
+ /* Protect against invalid data (corrupt or old log files). */
+ if (hit->account == NULL || hit->target == NULL)
+ continue;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL && !found;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ if (account_equal (hit->account, account) &&
+ entity_equal (hit->target, target))
+ found = TRUE;
+ }
+
+ if (!found)
+ continue;
- view = GTK_TREE_VIEW (window->treeview_chats);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
- store = GTK_LIST_STORE (model);
+ if (is_anytime ||
+ g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
+ != NULL)
+ {
+ Ctx *ctx;
- if (account == NULL) {
- gtk_list_store_clear (store);
- return;
- }
+ ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
+ event_mask, subtype, log_window->count);
+ _tpl_action_chain_append (log_window->chain,
+ get_events_for_date, ctx);
+ }
+ }
- /* Block signals to stop the logs being retrieved prematurely */
- g_signal_handlers_block_by_func (selection,
- log_window_chats_changed_cb,
- window);
+ _tpl_action_chain_start (log_window->chain);
- gtk_list_store_clear (store);
+ g_date_free (anytime);
+}
- /* Pass the account reference to the callback */
- tpl_log_manager_get_entities_async (window->log_manager, account,
- log_manager_got_entities_cb, account);
+static gchar *
+format_date_for_display (GDate *date)
+{
+ gchar *text;
+ GDate *now = NULL;
+ gint days_elapsed;
+
+ /* g_date_strftime sucks */
+
+ now = g_date_new ();
+ g_date_set_time_t (now, time (NULL));
+
+ days_elapsed = g_date_days_between (date, now);
+
+ if (days_elapsed < 0)
+ {
+ text = NULL;
+ }
+ else if (days_elapsed == 0)
+ {
+ text = g_strdup (_("Today"));
+ }
+ else if (days_elapsed == 1)
+ {
+ text = g_strdup (_("Yesterday"));
+ }
+ else
+ {
+ GDateTime *dt;
+
+ dt = g_date_time_new_utc (g_date_get_year (date),
+ g_date_get_month (date), g_date_get_day (date),
+ 0, 0, 0);
+
+ if (days_elapsed <= 7)
+ text = g_date_time_format (dt, "%A");
+ else
+ text = g_date_time_format (dt,
+ C_("A date such as '23 May 2010', "
+ "%e is the day, %B the month and %Y the year",
+ "%e %B %Y"));
+
+ g_date_time_unref (dt);
+ }
+
+ g_date_free (now);
+
+ return text;
}
static void
-log_window_chats_setup (EmpathyLogWindow *window)
+populate_dates_from_search_hits (GList *accounts,
+ GList *targets)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeSortable *sortable;
- GtkTreeViewColumn *column;
- GtkListStore *store;
- GtkCellRenderer *cell;
+ GList *l;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
- view = GTK_TREE_VIEW (window->treeview_chats);
- selection = gtk_tree_view_get_selection (view);
+ if (log_window == NULL)
+ return;
- /* new store */
- store = gtk_list_store_new (COL_CHAT_COUNT,
- G_TYPE_STRING, /* icon */
- G_TYPE_STRING, /* name */
- TP_TYPE_ACCOUNT, /* account */
- TPL_TYPE_ENTITY); /* target */
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+ selection = gtk_tree_view_get_selection (view);
+
+ for (l = log_window->hits; l != NULL; l = l->next)
+ {
+ TplLogSearchHit *hit = l->data;
+ GList *acc, *targ;
+ gboolean found = FALSE;
+
+ /* Protect against invalid data (corrupt or old log files). */
+ if (hit->account == NULL || hit->target == NULL)
+ continue;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL && !found;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ if (account_equal (hit->account, account) &&
+ entity_equal (hit->target, target))
+ found = TRUE;
+ }
+
+ if (!found)
+ continue;
- model = GTK_TREE_MODEL (store);
- sortable = GTK_TREE_SORTABLE (store);
+ /* Add the date if it's not already there */
+ has_element = FALSE;
+ gtk_tree_model_foreach (model, model_has_date, hit->date);
+ if (!has_element)
+ {
+ gchar *text = format_date_for_display (hit->date);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, hit->date,
+ COL_WHEN_TEXT, text,
+ COL_WHEN_ICON, CALENDAR_ICON,
+ -1);
+ }
+ }
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
+ COL_WHEN_TEXT, "separator",
+ -1);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
+ COL_WHEN_TEXT, _("Anytime"),
+ -1);
+
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+}
- gtk_tree_view_set_model (view, model);
+static void
+populate_entities_from_search_hits (void)
+{
+ EmpathyAccountChooser *account_chooser;
+ TpAccount *account;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkListStore *store;
+ GList *l;
- /* new column */
- column = gtk_tree_view_column_new ();
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+
+ gtk_list_store_clear (store);
+
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->account_chooser);
+ account = empathy_account_chooser_get_account (account_chooser);
+
+ for (l = log_window->hits; l; l = l->next)
+ {
+ TplLogSearchHit *hit = l->data;
+
+ /* Protect against invalid data (corrupt or old log files). */
+ if (hit->account == NULL || hit->target == NULL)
+ continue;
+
+ /* Filter based on the selected account */
+ if (account != NULL && !account_equal (account, hit->account))
+ continue;
+
+ /* Add the entity if it's not already there */
+ has_element = FALSE;
+ gtk_tree_model_foreach (model, model_has_entity, hit);
+ if (!has_element)
+ {
+ TplEntityType type = tpl_entity_get_entity_type (hit->target);
+ gboolean room = type == TPL_ENTITY_ROOM;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_NORMAL,
+ COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
+ : EMPATHY_IMAGE_AVATAR_DEFAULT,
+ COL_WHO_NAME, tpl_entity_get_alias (hit->target),
+ COL_WHO_ACCOUNT, hit->account,
+ COL_WHO_TARGET, hit->target,
+ -1);
+ }
+ }
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_SEPARATOR,
+ COL_WHO_NAME, "separator",
+ -1);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_ANY,
+ COL_WHO_NAME, _("Anyone"),
+ -1);
+ }
+
+ /* FIXME: select old entity if still available */
+}
- cell = gtk_cell_renderer_pixbuf_new ();
- gtk_tree_view_column_pack_start (column, cell, FALSE);
- gtk_tree_view_column_add_attribute (column, cell,
- "icon-name",
- COL_CHAT_ICON);
+static void
+log_manager_searched_new_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GList *hits;
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GError *error = NULL;
+
+ if (log_window == NULL)
+ return;
- cell = gtk_cell_renderer_text_new ();
- g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
- gtk_tree_view_column_pack_start (column, cell, TRUE);
- gtk_tree_view_column_add_attribute (column, cell,
- "text",
- COL_CHAT_NAME);
+ if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
+ result, &hits, &error))
+ {
+ DEBUG ("%s. Aborting", error->message);
+ g_error_free (error);
+ return;
+ }
- gtk_tree_view_append_column (view, column);
+ tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free);
+ log_window->hits = hits;
- /* set up treeview properties */
- gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
- gtk_tree_sortable_set_sort_column_id (sortable,
- COL_CHAT_NAME,
- GTK_SORT_ASCENDING);
+ populate_entities_from_search_hits ();
- /* set up signals */
- g_signal_connect (selection, "changed",
- G_CALLBACK (log_window_chats_changed_cb),
- window);
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ selection = gtk_tree_view_get_selection (view);
- g_object_unref (store);
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_when_changed_cb,
+ log_window);
}
static void
-log_window_chats_accounts_changed_cb (GtkWidget *combobox,
- EmpathyLogWindow *window)
+log_window_find_populate (EmpathyLogWindow *window,
+ const gchar *search_criteria)
{
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_chats);
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
- log_window_chats_populate (window);
+ gtk_tree_store_clear (window->store_events);
- /* No chat is selected as we just changed the account */
- gtk_widget_set_sensitive (window->calendar_chats, FALSE);
-}
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
-static void
-log_window_chats_set_selected (EmpathyLogWindow *window)
-{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GtkTreePath *path;
- gboolean ok;
-
- view = GTK_TREE_VIEW (window->treeview_chats);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
-
- if (!gtk_tree_model_get_iter_first (model, &iter)) {
- return;
- }
-
- for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) {
- TpAccount *this_account;
- TplEntity *this_target;
- const gchar *this_chat_id;
- gboolean this_is_chatroom;
-
- gtk_tree_model_get (model, &iter,
- COL_CHAT_ACCOUNT, &this_account,
- COL_CHAT_TARGET, &this_target,
- -1);
-
- this_chat_id = tpl_entity_get_identifier (this_target);
- this_is_chatroom = tpl_entity_get_entity_type (this_target) == TPL_ENTITY_ROOM;
-
- if (this_account == window->selected_account &&
- !tp_strdiff (this_chat_id, window->selected_chat_id) &&
- this_is_chatroom == window->selected_is_chatroom) {
- gtk_tree_selection_select_iter (selection, &iter);
- path = gtk_tree_model_get_path (model, &iter);
- gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
- gtk_tree_path_free (path);
- g_object_unref (this_account);
- g_object_unref (this_target);
- break;
- }
-
- g_object_unref (this_account);
- g_object_unref (this_target);
- }
-
- tp_clear_object (&window->selected_account);
- tp_clear_pointer (&window->selected_chat_id, g_free);
+ gtk_list_store_clear (store);
+
+ view = GTK_TREE_VIEW (window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+ selection = gtk_tree_view_get_selection (view);
+
+ gtk_list_store_clear (store);
+
+ if (EMP_STR_EMPTY (search_criteria))
+ {
+ tp_clear_pointer (&window->hits, tpl_log_manager_search_free);
+ log_window_who_populate (window);
+ return;
+ }
+
+ g_signal_handlers_block_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ tpl_log_manager_search_async (window->log_manager,
+ search_criteria, TPL_EVENT_MASK_ANY,
+ log_manager_searched_new_cb, NULL);
}
static gboolean
-log_window_chats_get_selected (EmpathyLogWindow *window,
- TpAccount **account,
- TplEntity **target)
+start_find_search (EmpathyLogWindow *window)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- TplEntity *targ;
- TpAccount *acc = NULL;
+ const gchar *str;
- view = GTK_TREE_VIEW (window->treeview_chats);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
+ str = gtk_entry_get_text (GTK_ENTRY (window->search_entry));
- if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
- return FALSE;
- }
+ /* Don't find the same crap again */
+ if (window->last_find && !tp_strdiff (window->last_find, str))
+ return FALSE;
- gtk_tree_model_get (model, &iter,
- COL_CHAT_ACCOUNT, &acc,
- COL_CHAT_TARGET, &targ,
- -1);
+ g_free (window->last_find);
+ window->last_find = g_strdup (str);
- if (account != NULL) {
- *account = g_object_ref (acc);
- }
+ log_window_find_populate (window, str);
- if (target != NULL) {
- *target = g_object_ref (targ);
- }
+ return FALSE;
+}
- g_object_unref (acc);
- g_object_unref (targ);
+static void
+log_window_search_entry_changed_cb (GtkWidget *entry,
+ EmpathyLogWindow *window)
+{
+ if (window->source != 0)
+ g_source_remove (window->source);
+ window->source = g_timeout_add (500, (GSourceFunc) start_find_search,
+ window);
+}
- return TRUE;
+static void
+log_window_search_entry_activate_cb (GtkWidget *entry,
+ EmpathyLogWindow *self)
+{
+ start_find_search (self);
}
static void
-log_window_got_messages_for_date_cb (GObject *manager,
+log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
+ return;
+
+ gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
+ "", -1);
+}
+
+/*
+ * Chats Code
+ */
+
+static void
+log_window_who_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean someone = FALSE;
+
+ DEBUG ("log_window_who_changed_cb");
+
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ /* If 'Anyone' is selected, everything else should be deselected */
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+ }
+ else if (gtk_tree_selection_count_selected_rows (selection) == 1)
+ {
+ someone = TRUE;
+ }
+ }
+
+ gtk_widget_set_sensitive (window->button_profile, someone);
+ gtk_widget_set_sensitive (window->button_chat, someone);
+ gtk_widget_set_sensitive (window->button_call, someone);
+ gtk_widget_set_sensitive (window->button_video, someone);
+
+ /* The contact changed, so the dates need to be updated */
+ log_window_chats_get_messages (window, TRUE);
+}
+
+static void
+log_manager_got_entities_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
- EmpathyLogWindow *window = user_data;
- GList *events;
- GList *l;
- GError *error = NULL;
+ Ctx *ctx = user_data;
+ GList *entities;
+ GList *l;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GError *error = NULL;
+ gboolean select_account = FALSE;
if (log_window == NULL)
- return;
+ goto out;
- if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
- result, &events, &error)) {
- DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
- error->message);
- empathy_chat_view_append_event (window->chatview_find,
- "Unable to retrieve messages for the selected date");
+ if (log_window->count != ctx->count)
+ goto out;
+
+ if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
+ result, &entities, &error))
+ {
+ DEBUG ("%s. Aborting", error->message);
g_error_free (error);
- return;
- }
-
- for (l = events; l; l = l->next) {
- EmpathyMessage *message = empathy_message_from_tpl_log_event (l->data);
- g_object_unref (l->data);
- empathy_chat_view_append_message (window->chatview_chats,
- message);
- g_object_unref (message);
- }
- g_list_free (events);
+ goto out;
+ }
+
+ view = GTK_TREE_VIEW (ctx->window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+ store = GTK_LIST_STORE (model);
+
+ /* Block signals to stop the logs being retrieved prematurely */
+ g_signal_handlers_block_by_func (selection,
+ log_window_who_changed_cb, ctx->window);
+
+ for (l = entities; l; l = l->next)
+ {
+ TplEntity *entity = TPL_ENTITY (l->data);
+ TplEntityType type = tpl_entity_get_entity_type (entity);
+ gboolean room = type == TPL_ENTITY_ROOM;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_NORMAL,
+ COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
+ : EMPATHY_IMAGE_AVATAR_DEFAULT,
+ COL_WHO_NAME, tpl_entity_get_alias (entity),
+ COL_WHO_ACCOUNT, ctx->account,
+ COL_WHO_TARGET, entity,
+ -1);
+
+ if (ctx->window->selected_account != NULL &&
+ !tp_strdiff (tp_proxy_get_object_path (ctx->account),
+ tp_proxy_get_object_path (ctx->window->selected_account)))
+ select_account = TRUE;
+ }
+ g_list_free_full (entities, g_object_unref);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gint type;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ if (type != COL_TYPE_ANY)
+ {
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_SEPARATOR,
+ COL_WHO_NAME, "separator",
+ -1);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHO_TYPE, COL_TYPE_ANY,
+ COL_WHO_NAME, _("Anyone"),
+ -1);
+ }
+ }
+
+ /* Unblock signals */
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_who_changed_cb,
+ ctx->window);
+
+ /* We display the selected account if we populate the model with chats from
+ * this account. */
+ if (select_account)
+ log_window_chats_set_selected (ctx->window);
+
+out:
+ _tpl_action_chain_continue (log_window->chain);
+ ctx_free (ctx);
+}
- /* Turn back on scrolling */
- empathy_chat_view_scroll (window->chatview_find, TRUE);
+static void
+get_entities_for_account (TplActionChain *chain, gpointer user_data)
+{
+ Ctx *ctx = user_data;
- /* Give the search entry main focus */
- gtk_widget_grab_focus (window->entry_chats);
+ tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account,
+ log_manager_got_entities_cb, ctx);
}
+static void
+select_first_entity (TplActionChain *chain, gpointer user_data)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ _tpl_action_chain_continue (log_window->chain);
+}
static void
-log_window_get_messages_for_date (EmpathyLogWindow *window,
- GDate *date)
+log_window_who_populate (EmpathyLogWindow *window)
{
+ EmpathyAccountChooser *account_chooser;
TpAccount *account;
- TplEntity *target;
+ gboolean all_accounts;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ Ctx *ctx;
+
+ if (window->hits != NULL)
+ {
+ populate_entities_from_search_hits ();
+ return;
+ }
+
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
+ account = empathy_account_chooser_dup_account (account_chooser);
+ all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+ store = GTK_LIST_STORE (model);
- if (!log_window_chats_get_selected (window, &account, &target)) {
+ /* Block signals to stop the logs being retrieved prematurely */
+ g_signal_handlers_block_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+
+ gtk_list_store_clear (store);
+
+ /* Unblock signals */
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ if (!all_accounts && account == NULL)
+ {
return;
- }
+ }
+ else if (!all_accounts)
+ {
+ ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
+ _tpl_action_chain_append (window->chain, get_entities_for_account, ctx);
+ }
+ else
+ {
+ TpAccountManager *manager;
+ GList *accounts, *l;
+
+ manager = empathy_account_chooser_get_account_manager (account_chooser);
+ accounts = tp_account_manager_get_valid_accounts (manager);
+
+ for (l = accounts; l != NULL; l = l->next)
+ {
+ account = l->data;
+
+ ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
+ _tpl_action_chain_append (window->chain,
+ get_entities_for_account, ctx);
+ }
+
+ g_list_free (accounts);
+ }
+ _tpl_action_chain_append (window->chain, select_first_entity, NULL);
+ _tpl_action_chain_start (window->chain);
+}
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_chats);
+static gint
+sort_by_name (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gchar *name1, *name2;
+ gint type1, type2;
+ gint ret;
+
+ gtk_tree_model_get (model, a,
+ COL_WHO_TYPE, &type1,
+ COL_WHO_NAME, &name1,
+ -1);
+
+ gtk_tree_model_get (model, b,
+ COL_WHO_TYPE, &type2,
+ COL_WHO_NAME, &name2,
+ -1);
+
+ if (type1 == COL_TYPE_ANY)
+ ret = -1;
+ else if (type2 == COL_TYPE_ANY)
+ ret = 1;
+ else if (type1 == COL_TYPE_SEPARATOR)
+ ret = -1;
+ else if (type2 == COL_TYPE_SEPARATOR)
+ ret = 1;
+ else
+ ret = g_strcmp0 (name1, name2);
+
+ g_free (name1);
+ g_free (name2);
+
+ return ret;
+}
- /* Turn off scrolling temporarily */
- empathy_chat_view_scroll (window->chatview_find, FALSE);
+static gboolean
+who_row_is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint type;
- /* Get events */
- tpl_log_manager_get_events_for_date_async (window->log_manager,
- account, target, TPL_EVENT_MASK_TEXT,
- date,
- log_window_got_messages_for_date_cb,
- (gpointer) window);
+ gtk_tree_model_get (model, iter,
+ COL_WHO_TYPE, &type,
+ -1);
- g_object_unref (account);
- g_object_unref (target);
+ return (type == COL_TYPE_SEPARATOR);
}
static void
-log_manager_got_dates_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
+log_window_events_setup (EmpathyLogWindow *window)
{
- EmpathyLogWindow *window = user_data;
- GList *dates;
- GList *l;
- guint year_selected;
- guint month_selected;
- gboolean day_selected = FALSE;
- GDate *date = NULL;
- GError *error = NULL;
-
- if (log_window == NULL)
- return;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column;
+ GtkTreeStore *store;
+ GtkCellRenderer *cell;
+
+ view = GTK_TREE_VIEW (window->treeview_events);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* new store */
+ window->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
+ G_TYPE_INT, /* type */
+ G_TYPE_INT64, /* timestamp */
+ G_TYPE_STRING, /* stringified date */
+ G_TYPE_STRING, /* icon */
+ G_TYPE_STRING, /* name */
+ TP_TYPE_ACCOUNT, /* account */
+ TPL_TYPE_ENTITY, /* target */
+ TPL_TYPE_EVENT); /* event */
+
+ model = GTK_TREE_MODEL (store);
+ sortable = GTK_TREE_SORTABLE (store);
+
+ gtk_tree_view_set_model (view, model);
+
+ /* new column */
+ column = gtk_tree_view_column_new ();
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "icon-name", COL_EVENTS_ICON);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text", COL_EVENTS_TEXT);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_end (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text", COL_EVENTS_PRETTY_DATE);
+
+ gtk_tree_view_append_column (view, column);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+ gtk_tree_view_set_headers_visible (view, FALSE);
+
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ COL_EVENTS_TS,
+ GTK_SORT_ASCENDING);
+
+ g_object_unref (store);
+}
- if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
- result, &dates, &error)) {
- DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
- error->message);
- empathy_chat_view_append_event (window->chatview_find,
- "Unable to retrieve messages' dates");
- return;
- }
+static void
+log_window_who_setup (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+ GtkCellRenderer *cell;
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* new store */
+ store = gtk_list_store_new (COL_WHO_COUNT,
+ G_TYPE_INT, /* type */
+ G_TYPE_STRING, /* icon */
+ G_TYPE_STRING, /* name */
+ TP_TYPE_ACCOUNT, /* account */
+ TPL_TYPE_ENTITY); /* target */
+
+ model = GTK_TREE_MODEL (store);
+ sortable = GTK_TREE_SORTABLE (store);
+
+ gtk_tree_view_set_model (view, model);
+
+ /* new column */
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("Who"));
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "icon-name",
+ COL_WHO_ICON);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text",
+ COL_WHO_NAME);
+
+ gtk_tree_view_append_column (view, column);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
+ NULL, NULL);
+
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ COL_WHO_NAME,
+ GTK_SORT_ASCENDING);
+ gtk_tree_sortable_set_sort_func (sortable,
+ COL_WHO_NAME, sort_by_name,
+ NULL, NULL);
+
+ /* set up signals */
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (log_window_who_changed_cb), window);
+
+ g_object_unref (store);
+}
- for (l = dates; l; l = l->next) {
- GDate *d = l->data;
+static void
+log_window_chats_accounts_changed_cb (GtkWidget *combobox,
+ EmpathyLogWindow *window)
+{
+ /* Clear all current messages shown in the textview */
+ gtk_tree_store_clear (window->store_events);
- gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
- &year_selected,
- &month_selected,
- NULL);
+ log_window_who_populate (window);
+}
- month_selected++;
+static void
+log_window_chats_set_selected (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gboolean next;
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ for (next = gtk_tree_model_get_iter_first (model, &iter);
+ next;
+ next = gtk_tree_model_iter_next (model, &iter))
+ {
+ TpAccount *this_account;
+ TplEntity *this_target;
+ const gchar *this_chat_id;
+ gboolean this_is_chatroom;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &this_account,
+ COL_WHO_TARGET, &this_target,
+ -1);
+
+ this_chat_id = tpl_entity_get_identifier (this_target);
+ this_is_chatroom = tpl_entity_get_entity_type (this_target)
+ == TPL_ENTITY_ROOM;
+
+ if (this_account == window->selected_account &&
+ !tp_strdiff (this_chat_id, window->selected_chat_id) &&
+ this_is_chatroom == window->selected_is_chatroom)
+ {
+ gtk_tree_selection_select_iter (selection, &iter);
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
+ gtk_tree_path_free (path);
+ g_object_unref (this_account);
+ g_object_unref (this_target);
+ break;
+ }
+
+ g_object_unref (this_account);
+ g_object_unref (this_target);
+ }
+
+ tp_clear_object (&window->selected_account);
+ tp_clear_pointer (&window->selected_chat_id, g_free);
+}
- if (!l->next) {
- date = d;
- }
+static gint
+sort_by_date (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ GDate *date1, *date2;
- if (g_date_get_year (d) != year_selected ||
- g_date_get_month (d) != month_selected) {
- continue;
- }
+ gtk_tree_model_get (model, a,
+ COL_WHEN_DATE, &date1,
+ -1);
- DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d),
- g_date_get_month (d), g_date_get_day (d));
+ gtk_tree_model_get (model, b,
+ COL_WHEN_DATE, &date2,
+ -1);
- gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats),
- g_date_get_day (d));
+ return g_date_compare (date1, date2);
+}
- if (l->next) {
- continue;
- }
+static gboolean
+when_row_is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *when;
+ gboolean ret;
- day_selected = TRUE;
+ gtk_tree_model_get (model, iter,
+ COL_WHEN_TEXT, &when,
+ -1);
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats),
- g_date_get_day (d));
- }
+ ret = g_str_equal (when, "separator");
+ g_free (when);
+ return ret;
+}
- if (!day_selected) {
- /* Unselect the day in the calendar */
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
- }
+static void
+log_window_when_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ DEBUG ("log_window_when_changed_cb");
+
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
+
+ /* If 'Anytime' is selected, everything else should be deselected */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+ }
+ }
+
+ log_window_chats_get_messages (window, FALSE);
+}
- g_signal_handlers_unblock_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
+static void
+log_window_when_setup (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+ GtkCellRenderer *cell;
+
+ view = GTK_TREE_VIEW (window->treeview_when);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* new store */
+ store = gtk_list_store_new (COL_WHEN_COUNT,
+ G_TYPE_DATE, /* date */
+ G_TYPE_STRING, /* stringified date */
+ G_TYPE_STRING); /* icon */
+
+ model = GTK_TREE_MODEL (store);
+ sortable = GTK_TREE_SORTABLE (store);
+
+ gtk_tree_view_set_model (view, model);
+
+ /* new column */
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("When"));
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "icon-name", COL_WHEN_ICON);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text",
+ COL_WHEN_TEXT);
+
+ gtk_tree_view_append_column (view, column);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
+ NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ COL_WHEN_DATE,
+ GTK_SORT_DESCENDING);
+ gtk_tree_sortable_set_sort_func (sortable,
+ COL_WHEN_DATE, sort_by_date,
+ NULL, NULL);
+
+ /* set up signals */
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (log_window_when_changed_cb),
window);
- if (date != NULL) {
- /* Show messages of the most recent date */
- log_window_get_messages_for_date (window, date);
- }
-
- g_list_foreach (dates, (GFunc) g_free, NULL);
- g_list_free (dates);
+ g_object_unref (store);
}
+static gboolean
+what_row_is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint type;
+
+ gtk_tree_model_get (model, iter,
+ COL_WHAT_TYPE, &type,
+ -1);
+
+ return (type == WHAT_TYPE_SEPARATOR);
+}
static void
-log_window_chats_get_messages (EmpathyLogWindow *window,
- GDate *date)
+log_window_what_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ DEBUG ("log_window_what_changed_cb");
+
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
+
+ /* If 'Anything' is selected, everything else should be deselected */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_what_changed_cb,
+ window);
+
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_what_changed_cb,
+ window);
+ }
+ }
+
+ /* The dates need to be updated if we're not searching */
+ log_window_chats_get_messages (window, window->hits == NULL);
+}
+
+static gboolean
+log_window_what_collapse_row_cb (GtkTreeView *tree_view,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ /* Reject collapsing */
+ return TRUE;
+}
+
+struct event
{
- TpAccount *account;
- TplEntity *target;
- guint year_selected;
- guint month_selected;
- guint day;
+ gint type;
+ EventSubtype subtype;
+ const gchar *icon;
+ const gchar *text;
+};
+static void
+log_window_what_setup (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter, parent;
+ GtkTreeStore *store;
+ GtkCellRenderer *cell;
+ guint i;
+ struct event events [] = {
+ { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
+ { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
+ { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
+ { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") }
+ };
+ struct event call_events [] = {
+ { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, "call-start", _("Incoming calls") },
+ { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, "call-start", _("Outgoing calls") },
+ { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, "call-stop", _("Missed calls") }
+ };
+
+ view = GTK_TREE_VIEW (window->treeview_what);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* new store */
+ store = gtk_tree_store_new (COL_WHAT_COUNT,
+ G_TYPE_INT, /* history type */
+ G_TYPE_INT, /* history subtype */
+ G_TYPE_STRING, /* stringified history type */
+ G_TYPE_STRING, /* icon */
+ G_TYPE_BOOLEAN); /* expander (hidden) */
+
+ model = GTK_TREE_MODEL (store);
+ sortable = GTK_TREE_SORTABLE (store);
+
+ gtk_tree_view_set_model (view, model);
+
+ /* new column */
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("What"));
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "icon-name", COL_WHAT_ICON);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text", COL_WHAT_TEXT);
+
+ gtk_tree_view_append_column (view, column);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_show_expanders (view, FALSE);
+ gtk_tree_view_set_level_indentation (view, 12);
+ gtk_tree_view_expand_all (view);
+ gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
+ NULL, NULL);
+
+ /* populate */
+ for (i = 0; i < G_N_ELEMENTS (events); i++)
+ {
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_WHAT_TYPE, events[i].type,
+ COL_WHAT_SUBTYPE, events[i].subtype,
+ COL_WHAT_TEXT, events[i].text,
+ COL_WHAT_ICON, events[i].icon,
+ -1);
+ }
+
+ gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
+ for (i = 0; i < G_N_ELEMENTS (call_events); i++)
+ {
+ gtk_tree_store_append (store, &iter, &parent);
+ gtk_tree_store_set (store, &iter,
+ COL_WHAT_TYPE, call_events[i].type,
+ COL_WHAT_SUBTYPE, call_events[i].subtype,
+ COL_WHAT_TEXT, call_events[i].text,
+ COL_WHAT_ICON, call_events[i].icon,
+ -1);
+ }
+
+ gtk_tree_view_expand_all (view);
+
+ /* select 'Anything' */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* set up signals */
+ g_signal_connect (view, "test-collapse-row",
+ G_CALLBACK (log_window_what_collapse_row_cb),
+ NULL);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (log_window_what_changed_cb),
+ window);
- if (!log_window_chats_get_selected (window, &account, &target)) {
- return;
- }
+ g_object_unref (store);
+}
- g_signal_handlers_block_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
- window);
+static void
+start_spinner (void)
+{
+ gtk_spinner_start (GTK_SPINNER (log_window->spinner));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_EMPTY);
+}
- /* Either use the supplied date or get the last */
- if (date == NULL) {
- /* Get a list of dates and show them on the calendar */
- tpl_log_manager_get_dates_async (window->log_manager,
- account, target, TPL_EVENT_MASK_TEXT,
- log_manager_got_dates_cb, (gpointer) window);
- /* signal unblocked at the end of the CB flow */
- } else {
- day = g_date_get_day (date);
- gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
- &year_selected,
- &month_selected,
- NULL);
+static gboolean
+show_spinner (gpointer data)
+{
+ gboolean active;
- month_selected++;
+ if (log_window == NULL)
+ return FALSE;
- if (g_date_get_year (date) != year_selected &&
- g_date_get_month (date) != month_selected) {
- day = 0;
- }
+ g_object_get (log_window->spinner, "active", &active, NULL);
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
+ if (active)
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_SPINNER);
- g_signal_handlers_unblock_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
- window);
- }
+ return FALSE;
+}
- if (date != NULL) {
- /* Show messages of the selected date */
- log_window_get_messages_for_date (window, date);
- }
+static void
+show_events (TplActionChain *chain,
+ gpointer user_data)
+{
+ gtk_spinner_stop (GTK_SPINNER (log_window->spinner));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_EVENTS);
- g_object_unref (account);
- g_object_unref (target);
+ _tpl_action_chain_continue (chain);
}
static void
-log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
- EmpathyLogWindow *window)
+log_window_got_messages_for_date_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
{
- guint year;
- guint month;
- guint day;
- GDate *date;
+ Ctx *ctx = user_data;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *events;
+ GList *l;
+ GError *error = NULL;
+ gint n;
+
+ if (log_window == NULL)
+ {
+ ctx_free (ctx);
+ return;
+ }
+
+ if (log_window->count != ctx->count)
+ goto out;
- gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
- if (day == 0)
- /* No date selected */
- return;
+ if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
+ result, &events, &error))
+ {
+ DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ for (l = events; l; l = l->next)
+ {
+ TplEvent *event = l->data;
+ gboolean append = TRUE;
+
+ if (TPL_IS_CALL_EVENT (l->data)
+ && ctx->event_mask & TPL_EVENT_MASK_CALL
+ && ctx->event_mask != TPL_EVENT_MASK_ANY)
+ {
+ TplCallEvent *call = l->data;
+
+ append = FALSE;
+
+ if (ctx->subtype & EVENT_CALL_ALL)
+ {
+ append = TRUE;
+ }
+ else
+ {
+ TplCallEndReason reason = tpl_call_event_get_end_reason (call);
+ TplEntity *sender = tpl_event_get_sender (event);
+ TplEntity *receiver = tpl_event_get_receiver (event);
+
+ if (reason == TPL_CALL_END_REASON_NO_ANSWER)
+ {
+ if (ctx->subtype & EVENT_CALL_MISSED)
+ append = TRUE;
+ }
+ else if (ctx->subtype & EVENT_CALL_OUTGOING
+ && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
+ {
+ append = TRUE;
+ }
+ else if (ctx->subtype & EVENT_CALL_INCOMING
+ && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
+ {
+ append = TRUE;
+ }
+ }
+ }
+
+ if (append)
+ {
+ EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
+ log_window_append_message (event, msg);
+ g_object_unref (msg);
+ }
+
+ g_object_unref (event);
+ }
+ g_list_free (events);
- /* We need this hear because it appears that the months start from 0 */
- month++;
+ view = GTK_TREE_VIEW (log_window->treeview_events);
+ model = gtk_tree_view_get_model (view);
+ n = gtk_tree_model_iter_n_children (model, NULL) - 1;
- date = g_date_new_dmy (day, month, year);
+ if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
+ {
+ GtkTreePath *path;
- DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day);
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+ }
- log_window_chats_get_messages (window, date);
+ out:
+ ctx_free (ctx);
- g_date_free (date);
+ _tpl_action_chain_continue (log_window->chain);
}
static void
-log_window_updating_calendar_month_cb (GObject *manager,
- GAsyncResult *result, gpointer user_data)
-{
- EmpathyLogWindow *window = user_data;
- GList *dates;
- GList *l;
- guint year_selected;
- guint month_selected;
- GError *error = NULL;
-
- if (log_window == NULL)
- return;
-
- if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
- result, &dates, &error)) {
- DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
- error->message);
- empathy_chat_view_append_event (window->chatview_find,
- "Unable to retrieve messages' dates");
- g_error_free (error);
- return;
- }
-
- gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
- g_object_get (window->calendar_chats,
- "month", &month_selected,
- "year", &year_selected,
- NULL);
-
- /* We need this here because it appears that the months start from 0 */
- month_selected++;
-
- for (l = dates; l; l = l->next) {
- GDate *date = l->data;
-
- if (g_date_get_year (date) == year_selected &&
- g_date_get_month (date) == month_selected) {
- DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (date),
- g_date_get_month (date), g_date_get_day (date));
- gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), g_date_get_day (date));
- }
- }
-
- g_list_foreach (dates, (GFunc) g_free, NULL);
- g_list_free (dates);
-
- DEBUG ("Currently showing month %d and year %d", month_selected,
- year_selected);
+get_events_for_date (TplActionChain *chain, gpointer user_data)
+{
+ Ctx *ctx = user_data;
+
+ tpl_log_manager_get_events_for_date_async (ctx->window->log_manager,
+ ctx->account, ctx->entity, ctx->event_mask,
+ ctx->date,
+ log_window_got_messages_for_date_cb,
+ ctx);
}
static void
-log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
- EmpathyLogWindow *window)
+log_window_get_messages_for_dates (EmpathyLogWindow *window,
+ GList *dates)
{
- TpAccount *account;
- TplEntity *target;
+ GList *accounts, *targets, *acc, *targ, *l;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ GDate *date, *anytime, *separator;
+
+ if (!log_window_get_selected (window,
+ &accounts, &targets, NULL, &event_mask, &subtype))
+ return;
+
+ anytime = g_date_new_dmy (2, 1, -1);
+ separator = g_date_new_dmy (1, 1, -1);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ for (l = dates; l != NULL; l = l->next)
+ {
+ date = l->data;
+
+ /* Get events */
+ if (g_date_compare (date, anytime) != 0)
+ {
+ Ctx *ctx;
+
+ ctx = ctx_new (window, account, target, date, event_mask, subtype,
+ window->count);
+ _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
+ }
+ else
+ {
+ GtkTreeView *view = GTK_TREE_VIEW (window->treeview_when);
+ GtkTreeModel *model = gtk_tree_view_get_model (view);
+ GtkTreeIter iter;
+ gboolean next;
+ GDate *d;
+
+ for (next = gtk_tree_model_get_iter_first (model, &iter);
+ next;
+ next = gtk_tree_model_iter_next (model, &iter))
+ {
+ Ctx *ctx;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHEN_DATE, &d,
+ -1);
+
+ if (g_date_compare (d, anytime) != 0 &&
+ g_date_compare (d, separator) != 0)
+ {
+ ctx = ctx_new (window, account, target, d,
+ event_mask, subtype, window->count);
+ _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
+ }
+ }
+ }
+ }
+ }
+
+ start_spinner ();
+ g_timeout_add (1000, show_spinner, NULL);
+ _tpl_action_chain_append (window->chain, show_events, NULL);
+ _tpl_action_chain_start (window->chain);
+
+ g_list_free_full (accounts, g_object_unref);
+ g_list_free_full (targets, g_object_unref);
+ g_date_free (separator);
+ g_date_free (anytime);
+}
- gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
+static void
+log_manager_got_dates_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Ctx *ctx = user_data;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *dates;
+ GList *l;
+ GError *error = NULL;
- if (!log_window_chats_get_selected (window, &account, &target)) {
- DEBUG ("No chat selected to get dates for...");
- return;
- }
+ if (log_window == NULL)
+ goto out;
- /* Get the log object for this contact */
- tpl_log_manager_get_dates_async (window->log_manager, account, target,
- TPL_EVENT_MASK_TEXT,
- log_window_updating_calendar_month_cb,
- (gpointer) window);
+ if (log_window->count != ctx->count)
+ goto out;
- g_object_unref (account);
- g_object_unref (target);
+ if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
+ result, &dates, &error))
+ {
+ DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
+ error->message);
+ goto out;
+ }
+
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+ selection = gtk_tree_view_get_selection (view);
+
+ for (l = dates; l != NULL; l = l->next)
+ {
+ GDate *date = l->data;
+
+ /* Add the date if it's not already there */
+ has_element = FALSE;
+ gtk_tree_model_foreach (model, model_has_date, date);
+ if (!has_element)
+ {
+ gchar *text = format_date_for_display (date);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, date,
+ COL_WHEN_TEXT, text,
+ COL_WHEN_ICON, CALENDAR_ICON,
+ -1);
+
+ g_free (text);
+ }
+ }
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gchar *separator = NULL;
+
+ if (gtk_tree_model_iter_next (model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ COL_WHEN_TEXT, &separator,
+ -1);
+ }
+
+ if (g_strcmp0 (separator, "separator") != 0)
+ {
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
+ COL_WHEN_TEXT, "separator",
+ -1);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
+ COL_WHEN_TEXT, _("Anytime"),
+ -1);
+ }
+ }
+
+ g_list_free_full (dates, g_free);
+ out:
+ ctx_free (ctx);
+ _tpl_action_chain_continue (log_window->chain);
}
static void
-log_window_entry_chats_changed_cb (GtkWidget *entry,
- EmpathyLogWindow *window)
+select_first_date (TplActionChain *chain, gpointer user_data)
{
- const gchar *str;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
- empathy_chat_view_highlight (window->chatview_chats, str, FALSE);
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
- if (str != NULL) {
- empathy_chat_view_find_next (window->chatview_chats,
- str,
- TRUE,
- FALSE);
- }
+ /* Show messages of the most recent date */
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ _tpl_action_chain_continue (log_window->chain);
}
static void
-log_window_entry_chats_activate_cb (GtkWidget *entry,
- EmpathyLogWindow *window)
+get_dates_for_entity (TplActionChain *chain, gpointer user_data)
{
- const gchar *str;
+ Ctx *ctx = user_data;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
+ tpl_log_manager_get_dates_async (ctx->window->log_manager,
+ ctx->account, ctx->entity, ctx->event_mask,
+ log_manager_got_dates_cb, ctx);
+}
- if (str != NULL) {
- empathy_chat_view_find_next (window->chatview_chats,
- str,
- FALSE,
- FALSE);
- }
+static void
+log_window_chats_get_messages (EmpathyLogWindow *window,
+ gboolean force_get_dates)
+{
+ GList *accounts, *targets, *dates;
+ TplEventTypeMask event_mask;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ if (!log_window_get_selected (window, &accounts, &targets,
+ &dates, &event_mask, NULL))
+ return;
+
+ view = GTK_TREE_VIEW (window->treeview_when);
+ selection = gtk_tree_view_get_selection (view);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+
+ /* Clear all current messages shown in the textview */
+ gtk_tree_store_clear (window->store_events);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ /* If there's a search use the returned hits */
+ if (window->hits != NULL)
+ {
+ if (force_get_dates)
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ gtk_list_store_clear (store);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ populate_dates_from_search_hits (accounts, targets);
+ }
+ else
+ {
+ populate_events_from_search_hits (accounts, targets, dates);
+ }
+ }
+ /* Either use the supplied date or get the last */
+ else if (force_get_dates || dates == NULL)
+ {
+ GList *acc, *targ;
+
+ g_signal_handlers_block_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ gtk_list_store_clear (store);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ /* Get a list of dates and show them on the treeview */
+ for (targ = targets, acc = accounts;
+ targ != NULL && acc != NULL;
+ targ = targ->next, acc = acc->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+ Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0,
+ window->count);
+
+ _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx);
+ }
+ _tpl_action_chain_append (window->chain, select_first_date, NULL);
+ _tpl_action_chain_start (window->chain);
+ }
+ else
+ {
+ /* Show messages of the selected date */
+ log_window_get_messages_for_dates (window, dates);
+ }
+
+ g_list_free_full (accounts, g_object_unref);
+ g_list_free_full (targets, g_object_unref);
+ g_list_free_full (dates, (GFreeFunc) g_date_free);
}
typedef struct {
- EmpathyAccountChooserFilterResultCallback callback;
- gpointer user_data;
+ EmpathyAccountChooserFilterResultCallback callback;
+ gpointer user_data;
} FilterCallbackData;
static void
-got_entities (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
+got_entities (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
{
- FilterCallbackData *data = user_data;
- GList *entities;
- GError *error;
+ FilterCallbackData *data = user_data;
+ GList *entities;
+ GError *error = NULL;
- if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager), result, &entities, &error)) {
- DEBUG ("Could not get entities: %s", error->message);
- g_error_free (error);
- data->callback (FALSE, data->user_data);
- } else {
- data->callback (entities != NULL, data->user_data);
+ if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
+ result, &entities, &error))
+ {
+ DEBUG ("Could not get entities: %s", error->message);
+ g_error_free (error);
+ data->callback (FALSE, data->user_data);
+ }
+ else
+ {
+ data->callback (entities != NULL, data->user_data);
- g_list_free_full (entities, g_object_unref);
- }
+ g_list_free_full (entities, g_object_unref);
+ }
- g_slice_free (FilterCallbackData, data);
+ g_slice_free (FilterCallbackData, data);
}
static void
empathy_account_chooser_filter_has_logs (TpAccount *account,
- EmpathyAccountChooserFilterResultCallback callback,
- gpointer callback_data,
- gpointer user_data)
+ EmpathyAccountChooserFilterResultCallback callback,
+ gpointer callback_data,
+ gpointer user_data)
{
- TplLogManager *manager = tpl_log_manager_dup_singleton ();
- FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
+ TplLogManager *manager = tpl_log_manager_dup_singleton ();
+ FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
- cb_data->callback = callback;
- cb_data->user_data = callback_data;
+ cb_data->callback = callback;
+ cb_data->user_data = callback_data;
- tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
+ tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
- g_object_unref (manager);
+ g_object_unref (manager);
}
static void
log_window_logger_clear_account_cb (TpProxy *proxy,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
{
- EmpathyLogWindow *window = user_data;
+ EmpathyLogWindow *window = user_data;
- if (error != NULL)
- g_warning ("Error when clearing logs: %s", error->message);
+ if (error != NULL)
+ g_warning ("Error when clearing logs: %s", error->message);
- /* Refresh the log viewer so the logs are cleared if the account
- * has been deleted */
- empathy_chat_view_clear (window->chatview_chats);
- log_window_chats_populate (window);
+ /* Refresh the log viewer so the logs are cleared if the account
+ * has been deleted */
+ gtk_tree_store_clear (window->store_events);
+ log_window_who_populate (window);
- /* Re-filter the account chooser so the accounts without logs get greyed out */
- empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats),
- empathy_account_chooser_filter_has_logs, NULL);
+ /* Re-filter the account chooser so the accounts without logs get greyed out */
+ empathy_account_chooser_set_filter (
+ EMPATHY_ACCOUNT_CHOOSER (window->account_chooser),
+ empathy_account_chooser_filter_has_logs, NULL);
}
static void
log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
- EmpathyLogWindow *window)
+ EmpathyLogWindow *window)
{
- empathy_account_chooser_set_account (chooser,
- empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats)));
+ EmpathyAccountChooser *account_chooser;
+
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
+
+ empathy_account_chooser_set_account (chooser,
+ empathy_account_chooser_get_account (account_chooser));
}
static void
-log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
- EmpathyLogWindow *window)
-{
- GtkWidget *dialog, *content_area, *hbox, *label;
- EmpathyAccountChooser *account_chooser;
- gint response_id;
- TpDBusDaemon *bus;
- TpProxy *logger;
- GError *error = NULL;
-
- account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
- empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
- empathy_account_chooser_set_filter (account_chooser, empathy_account_chooser_filter_has_logs, NULL);
-
- /* Select the same account as in the history window */
- if (empathy_account_chooser_is_ready (account_chooser))
- log_window_clear_logs_chooser_select_account (account_chooser, window);
- else
- g_signal_connect (account_chooser, "ready",
- G_CALLBACK (log_window_clear_logs_chooser_select_account), window);
-
- dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window),
- GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
- GTK_BUTTONS_NONE,
- _("Are you sure you want to delete all logs of previous conversations?"));
-
- gtk_dialog_add_buttons (GTK_DIALOG (dialog),
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- _("Clear All"), GTK_RESPONSE_APPLY,
- NULL);
-
- content_area = gtk_message_dialog_get_message_area (
- GTK_MESSAGE_DIALOG (dialog));
-
- hbox = gtk_hbox_new (FALSE, 6);
- label = gtk_label_new (_("Delete from:"));
- gtk_box_pack_start (GTK_BOX (hbox), label,
- FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
- FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (content_area), hbox,
- FALSE, FALSE, 0);
-
- gtk_widget_show_all (hbox);
-
- response_id = gtk_dialog_run (GTK_DIALOG (dialog));
-
- if (response_id != GTK_RESPONSE_APPLY)
- goto out;
-
- bus = tp_dbus_daemon_dup (&error);
- if (error != NULL) {
- g_warning ("Could not delete logs: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- logger = g_object_new (TP_TYPE_PROXY,
- "bus-name", "org.freedesktop.Telepathy.Logger",
- "object-path", "/org/freedesktop/Telepathy/Logger",
- "dbus-daemon", bus,
- NULL);
- g_object_unref (bus);
-
- tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
-
- if (empathy_account_chooser_has_all_selected (account_chooser)) {
- DEBUG ("Deleting logs for all the accounts");
-
- emp_cli_logger_call_clear (logger, -1,
- log_window_logger_clear_account_cb,
- window, NULL, G_OBJECT (window->window));
- } else {
- TpAccount *account = empathy_account_chooser_get_account (account_chooser);
-
- DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
-
- emp_cli_logger_call_clear_account (logger, -1,
- tp_proxy_get_object_path (account),
- log_window_logger_clear_account_cb,
- window, NULL, G_OBJECT (window->window));
- }
-
- g_object_unref (logger);
+log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
+ EmpathyLogWindow *window)
+{
+ GtkWidget *dialog, *content_area, *hbox, *label;
+ EmpathyAccountChooser *account_chooser;
+ gint response_id;
+ TpDBusDaemon *bus;
+ TpProxy *logger;
+ GError *error = NULL;
+
+ account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
+ empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
+ empathy_account_chooser_set_filter (account_chooser,
+ empathy_account_chooser_filter_has_logs, NULL);
+
+ /* Select the same account as in the history window */
+ if (empathy_account_chooser_is_ready (account_chooser))
+ log_window_clear_logs_chooser_select_account (account_chooser, window);
+ else
+ g_signal_connect (account_chooser, "ready",
+ G_CALLBACK (log_window_clear_logs_chooser_select_account), window);
+
+ dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window),
+ GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ _("Are you sure you want to delete all logs of previous conversations?"));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("Clear All"), GTK_RESPONSE_APPLY,
+ NULL);
+
+ content_area = gtk_message_dialog_get_message_area (
+ GTK_MESSAGE_DIALOG (dialog));
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ label = gtk_label_new (_("Delete from:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label,
+ FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
+ FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (content_area), hbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ response_id = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response_id != GTK_RESPONSE_APPLY)
+ goto out;
+
+ bus = tp_dbus_daemon_dup (&error);
+ if (error != NULL)
+ {
+ g_warning ("Could not delete logs: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ logger = g_object_new (TP_TYPE_PROXY,
+ "bus-name", "org.freedesktop.Telepathy.Logger",
+ "object-path", "/org/freedesktop/Telepathy/Logger",
+ "dbus-daemon", bus,
+ NULL);
+ g_object_unref (bus);
+
+ tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
+
+ if (empathy_account_chooser_has_all_selected (account_chooser))
+ {
+ DEBUG ("Deleting logs for all the accounts");
+
+ emp_cli_logger_call_clear (logger, -1,
+ log_window_logger_clear_account_cb,
+ window, NULL, G_OBJECT (window->window));
+ }
+ else
+ {
+ TpAccount *account;
+
+ account = empathy_account_chooser_get_account (account_chooser);
+
+ DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
+
+ emp_cli_logger_call_clear_account (logger, -1,
+ tp_proxy_get_object_path (account),
+ log_window_logger_clear_account_cb,
+ window, NULL, G_OBJECT (window->window));
+ }
+
+ g_object_unref (logger);
out:
- gtk_widget_destroy (dialog);
+ gtk_widget_destroy (dialog);
}
diff --git a/libempathy-gtk/empathy-log-window.h b/libempathy-gtk/empathy-log-window.h
index def0d846b..373f48c36 100644
--- a/libempathy-gtk/empathy-log-window.h
+++ b/libempathy-gtk/empathy-log-window.h
@@ -1,7 +1,6 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2006-2007 Imendio AB
- * Copyright (C) 2007-2008 Collabora Ltd.
+ * Copyright (C) 2007-2011 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
@@ -20,6 +19,7 @@
*
* Authors: Martyn Russell <martyn@imendio.com>
* Xavier Claessens <xclaesse@gmail.com>
+ * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
*/
#ifndef __EMPATHY_LOG_WINDOW_H__
@@ -29,10 +29,10 @@
G_BEGIN_DECLS
-GtkWidget * empathy_log_window_show (TpAccount *account,
- const gchar *chat_id,
- gboolean chatroom,
- GtkWindow *parent);
+GtkWidget * empathy_log_window_show (TpAccount *account,
+ const gchar *chat_id,
+ gboolean chatroom,
+ GtkWindow *parent);
G_END_DECLS
diff --git a/libempathy-gtk/empathy-log-window.ui b/libempathy-gtk/empathy-log-window.ui
index 499bff999..9c2303829 100644
--- a/libempathy-gtk/empathy-log-window.ui
+++ b/libempathy-gtk/empathy-log-window.ui
@@ -1,13 +1,11 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy toplevel-contextual -->
+ <!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="log_window">
- <property name="title" translatable="yes">Previous Conversations</property>
- <property name="role">log</property>
- <property name="default_width">640</property>
- <property name="default_height">450</property>
- <property name="icon_name">document-open-recent</property>
+ <property name="title" translatable="yes">History</property>
+ <property name="default_width">800</property>
+ <property name="default_height">600</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
@@ -16,6 +14,26 @@
<object class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="imagemenuitem_quit">
+ <property name="label">gtk-quit</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
<object class="GtkMenuItem" id="menuitem2">
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
@@ -24,10 +42,10 @@
<object class="GtkMenu" id="menu2">
<property name="visible">True</property>
<child>
- <object class="GtkImageMenuItem" id="menu_delete">
- <property name="label" translatable="yes">Delete All History...</property>
+ <object class="GtkImageMenuItem" id="imagemenuitem_delete">
+ <property name="label">Delete All History...</property>
<property name="visible">True</property>
- <property name="image">image3</property>
+ <property name="image">image1</property>
<property name="use_stock">False</property>
</object>
</child>
@@ -42,322 +60,239 @@
</packing>
</child>
<child>
- <object class="GtkNotebook" id="notebook">
+ <object class="GtkToolbar" id="toolbar1">
+ <property name="visible">True</property>
+ <property name="toolbar_style">both</property>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_profile">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Profile</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-dialog-info</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_chat">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Chat</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-edit</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_call">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Call</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">audio-input-microphone</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_video">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Video</property>
+ <property name="use_underline">True</property>
+ <property name="icon_name">camera-video</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton_sep1">
+ <property name="visible">True</property>
+ <property name="draw">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolbutton_accounts">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton_sep2">
+ <property name="visible">True</property>
+ <property name="draw">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolbutton_search">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVPaned" id="vpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="border_width">2</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkVBox" id="vbox192">
+ <object class="GtkHBox" id="hbox1">
+ <property name="height_request">160</property>
<property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
<child>
- <object class="GtkHBox" id="hbox144">
+ <object class="GtkScrolledWindow" id="scrolledwindow_who">
<property name="visible">True</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label628">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="Searching *for* something">_For:</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">entry_find</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="entry_find">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="invisible_char">&#x25CF;</property>
- <property name="activates_default">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
<child>
- <object class="GtkButton" id="button_find">
- <property name="label">gtk-find</property>
+ <object class="GtkTreeView" id="treeview_who">
<property name="visible">True</property>
- <property name="sensitive">False</property>
<property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <property name="focus_on_click">False</property>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
</child>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkVPaned" id="vpaned1">
+ <object class="GtkScrolledWindow" id="scrolledwindow_what">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="orientation">vertical</property>
- <property name="position">120</property>
- <property name="position_set">True</property>
+ <property name="hscrollbar_policy">automatic</property>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow14">
+ <object class="GtkTreeView" id="treeview_what">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="treeview_find">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="enable_search">False</property>
- </object>
- </child>
</object>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">True</property>
- </packing>
</child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_when">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
<child>
- <object class="GtkVBox" id="vbox215">
+ <object class="GtkTreeView" id="treeview_when">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow_find">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox171">
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <object class="GtkButton" id="button_next">
- <property name="label" translatable="yes">Find Next</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="image">image1</property>
- <property name="focus_on_click">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button_previous">
- <property name="label" translatable="yes">Find Previous</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="image">image2</property>
- <property name="focus_on_click">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack_type">end</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
+ <property name="can_focus">True</property>
</object>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
</child>
</object>
<packing>
- <property name="position">120</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label595">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="Tab Label">Search</property>
- </object>
<packing>
- <property name="tab_fill">False</property>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
</packing>
</child>
<child>
- <object class="GtkTable" id="table7">
+ <object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
- <property name="border_width">12</property>
- <property name="n_rows">2</property>
- <property name="n_columns">2</property>
- <property name="column_spacing">6</property>
- <property name="row_spacing">6</property>
+ <property name="can_focus">True</property>
+ <property name="show_border">False</property>
+ <property name="show_tabs">False</property>
<child>
- <object class="GtkHBox" id="hbox_chats">
+ <object class="GtkScrolledWindow" id="scrolledwindow_events">
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
<child>
- <placeholder/>
+ <object class="GtkTreeView" id="treeview_events">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
</child>
</object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
<packing>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="tab_fill">False</property>
</packing>
</child>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow_chats">
+ <object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <placeholder/>
- </child>
</object>
<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="position">1</property>
</packing>
</child>
- <child>
- <object class="GtkVBox" id="vbox191">
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow13">
- <property name="width_request">150</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="treeview_chats">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCalendar" id="calendar_chats">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="year">2011</property>
- <property name="month">2</property>
- <property name="day">15</property>
- <property name="sensitive">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
+ <property name="label" translatable="yes">page 2</property>
</object>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="tab_fill">False</property>
</packing>
</child>
<child>
- <object class="GtkHBox" id="hbox143">
+ <object class="GtkScrolledWindow" id="scrolledwindow_empty">
<property name="visible">True</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image247">
- <property name="visible">True</property>
- <property name="stock">gtk-find</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
<child>
- <object class="GtkEntry" id="entry_chats">
+ <object class="GtkTreeView" id="treeview_empty">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="invisible_char">&#x25CF;</property>
- <property name="activates_default">True</property>
</object>
- <packing>
- <property name="position">1</property>
- </packing>
</child>
</object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
</child>
</object>
<packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label596">
- <property name="visible">True</property>
- <property name="label" translatable="yes" comments="Tab Label">Conversations</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
</packing>
</child>
</object>
<packing>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
@@ -365,14 +300,6 @@
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
- <property name="stock">gtk-go-forward</property>
- </object>
- <object class="GtkImage" id="image2">
- <property name="visible">True</property>
- <property name="stock">gtk-go-back</property>
- </object>
- <object class="GtkImage" id="image3">
- <property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
</object>
</interface>
diff --git a/libempathy-gtk/empathy-new-message-dialog.c b/libempathy-gtk/empathy-new-message-dialog.c
index 8249b8693..1fe935d4e 100644
--- a/libempathy-gtk/empathy-new-message-dialog.c
+++ b/libempathy-gtk/empathy-new-message-dialog.c
@@ -62,21 +62,40 @@ G_DEFINE_TYPE(EmpathyNewMessageDialog, empathy_new_message_dialog,
* to be started with any contact on any enabled account.
*/
+enum
+{
+ EMP_NEW_MESSAGE_TEXT,
+ EMP_NEW_MESSAGE_SMS,
+};
+
static void
empathy_new_message_dialog_response (GtkDialog *dialog, int response_id)
{
TpAccount *account;
const gchar *contact_id;
- if (response_id != GTK_RESPONSE_ACCEPT) goto out;
+ if (response_id < EMP_NEW_MESSAGE_TEXT) goto out;
contact_id = empathy_contact_selector_dialog_get_selected (
EMPATHY_CONTACT_SELECTOR_DIALOG (dialog), NULL, &account);
if (EMP_STR_EMPTY (contact_id) || account == NULL) goto out;
- empathy_dispatcher_chat_with_contact_id (account, contact_id,
- gtk_get_current_event_time ());
+ switch (response_id)
+ {
+ case EMP_NEW_MESSAGE_TEXT:
+ empathy_dispatcher_chat_with_contact_id (account, contact_id,
+ gtk_get_current_event_time ());
+ break;
+
+ case EMP_NEW_MESSAGE_SMS:
+ empathy_dispatcher_sms_contact_id (account, contact_id,
+ gtk_get_current_event_time ());
+ break;
+
+ default:
+ g_warn_if_reached ();
+ }
out:
gtk_widget_destroy (GTK_WIDGET (dialog));
@@ -158,6 +177,63 @@ empathy_new_message_account_filter (EmpathyContactSelectorDialog *dialog,
tp_proxy_prepare_async (connection, features, conn_prepared_cb, cb_data);
}
+static void
+empathy_new_message_dialog_update_sms_button_sensitivity (GtkWidget *widget,
+ GParamSpec *pspec,
+ GtkWidget *button)
+{
+ GtkWidget *self = gtk_widget_get_toplevel (widget);
+ EmpathyContactSelectorDialog *dialog;
+ TpConnection *conn;
+ GPtrArray *rccs;
+ gboolean sensitive = FALSE;
+ guint i;
+
+ g_return_if_fail (EMPATHY_IS_NEW_MESSAGE_DIALOG (self));
+
+ dialog = EMPATHY_CONTACT_SELECTOR_DIALOG (self);
+
+ /* if the Text widget isn't sensitive, don't bother checking the caps */
+ if (!gtk_widget_get_sensitive (dialog->button_action))
+ goto finally;
+
+ empathy_contact_selector_dialog_get_selected (dialog, &conn, NULL);
+
+ if (conn == NULL)
+ goto finally;
+
+ /* iterate the rccs to find if SMS channels are supported, this should
+ * be in tp-glib */
+ rccs = tp_capabilities_get_channel_classes (
+ tp_connection_get_capabilities (conn));
+
+ for (i = 0; i < rccs->len; i++)
+ {
+ GHashTable *fixed;
+ GStrv allowed;
+ const char *type;
+ gboolean sms_channel;
+
+ tp_value_array_unpack (g_ptr_array_index (rccs, i), 2,
+ &fixed,
+ &allowed);
+
+ /* SMS channels are type:Text and sms-channel:True */
+ type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE);
+ sms_channel = tp_asv_get_boolean (fixed,
+ TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, NULL);
+
+ sensitive = sms_channel &&
+ !tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_TEXT);
+
+ if (sensitive)
+ break;
+ }
+
+finally:
+ gtk_widget_set_sensitive (button, sensitive);
+}
+
static GObject *
empathy_new_message_dialog_constructor (GType type,
guint n_props,
@@ -188,8 +264,19 @@ empathy_new_message_dialog_init (EmpathyNewMessageDialog *dialog)
{
EmpathyContactSelectorDialog *parent = EMPATHY_CONTACT_SELECTOR_DIALOG (
dialog);
+ GtkWidget *button;
GtkWidget *image;
+ /* add an SMS button */
+ button = gtk_button_new_with_mnemonic (_("_SMS"));
+ image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_SMS,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (button), image);
+
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
+ EMP_NEW_MESSAGE_SMS);
+ gtk_widget_show (button);
+
/* add chat button */
parent->button_action = gtk_button_new_with_mnemonic (_("C_hat"));
image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_NEW_MESSAGE,
@@ -197,9 +284,18 @@ empathy_new_message_dialog_init (EmpathyNewMessageDialog *dialog)
gtk_button_set_image (GTK_BUTTON (parent->button_action), image);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), parent->button_action,
- GTK_RESPONSE_ACCEPT);
+ EMP_NEW_MESSAGE_TEXT);
gtk_widget_show (parent->button_action);
+ /* the parent class will update the sensitivity of button_action, propagate
+ * it */
+ g_signal_connect (parent->button_action, "notify::sensitive",
+ G_CALLBACK (empathy_new_message_dialog_update_sms_button_sensitivity),
+ button);
+ g_signal_connect (dialog, "notify::selected-account",
+ G_CALLBACK (empathy_new_message_dialog_update_sms_button_sensitivity),
+ button);
+
/* Tweak the dialog */
gtk_window_set_title (GTK_WINDOW (dialog), _("New Conversation"));
gtk_window_set_role (GTK_WINDOW (dialog), "new_message");
diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c
index b2f759e30..2569e7faf 100644
--- a/libempathy-gtk/empathy-theme-adium.c
+++ b/libempathy-gtk/empathy-theme-adium.c
@@ -59,7 +59,7 @@ typedef struct {
EmpathyAdiumData *data;
EmpathySmileyManager *smiley_manager;
EmpathyContact *last_contact;
- time_t last_timestamp;
+ gint64 last_timestamp;
gboolean last_is_backlog;
guint pages_loading;
GList *message_queue;
@@ -324,7 +324,7 @@ theme_adium_append_html (EmpathyThemeAdium *theme,
const gchar *contact_id,
const gchar *service_name,
const gchar *message_classes,
- time_t timestamp,
+ gint64 timestamp,
gboolean is_backlog)
{
GString *string;
@@ -442,7 +442,7 @@ theme_adium_append_message (EmpathyChatView *view,
const gchar *contact_id;
EmpathyAvatar *avatar;
const gchar *avatar_filename = NULL;
- time_t timestamp;
+ gint64 timestamp;
gchar *html = NULL;
gsize len = 0;
const gchar *func;
diff --git a/libempathy-gtk/empathy-theme-boxes.c b/libempathy-gtk/empathy-theme-boxes.c
index bf97f3f97..c0377b6ae 100644
--- a/libempathy-gtk/empathy-theme-boxes.c
+++ b/libempathy-gtk/empathy-theme-boxes.c
@@ -196,7 +196,7 @@ theme_boxes_maybe_append_header (EmpathyThemeBoxes *theme,
GtkTextChildAnchor *anchor;
GtkWidget *box;
gchar *str;
- time_t time_;
+ gint64 time_;
gchar *tmp;
GtkTextIter start;
gboolean color_set;
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 95c561261..571980dd8 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -26,6 +26,7 @@ BUILT_SOURCES = \
noinst_LTLIBRARIES = libempathy.la
libempathy_headers = \
+ action-chain-internal.h \
empathy-account-settings.h \
empathy-auth-factory.h \
empathy-channel-factory.h \
@@ -67,6 +68,7 @@ libempathy_headers = \
libempathy_la_SOURCES = \
$(libempathy_headers) \
+ action-chain.c \
empathy-account-settings.c \
empathy-auth-factory.c \
empathy-channel-factory.c \
diff --git a/libempathy/action-chain-internal.h b/libempathy/action-chain-internal.h
new file mode 100644
index 000000000..14750c938
--- /dev/null
+++ b/libempathy/action-chain-internal.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
+ */
+
+#ifndef __TPL_ACTION_CHAIN_H__
+#define __TPL_ACTION_CHAIN_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+typedef struct {
+ GQueue *chain;
+ GSimpleAsyncResult *simple;
+ gboolean running;
+} TplActionChain;
+
+TplActionChain *_tpl_action_chain_new_async (GObject *obj,
+ GAsyncReadyCallback cb,
+ gpointer user_data);
+void _tpl_action_chain_free (TplActionChain *self);
+typedef void (*TplPendingAction) (TplActionChain *ctx, gpointer user_data);
+void _tpl_action_chain_append (TplActionChain *self, TplPendingAction func,
+ gpointer user_data);
+void _tpl_action_chain_prepend (TplActionChain *self, TplPendingAction func,
+ gpointer user_data);
+void _tpl_action_chain_start (TplActionChain *self);
+void _tpl_action_chain_continue (TplActionChain *self);
+void _tpl_action_chain_terminate (TplActionChain *self, const GError *error);
+void _tpl_action_chain_clear (TplActionChain *self);
+
+gpointer _tpl_action_chain_get_object (TplActionChain *self);
+gboolean _tpl_action_chain_new_finish (GObject *source,
+ GAsyncResult *result, GError **error);
+
+#endif // __TPL_ACTION_CHAIN_H__
diff --git a/libempathy/action-chain.c b/libempathy/action-chain.c
new file mode 100644
index 000000000..b6bf25ab9
--- /dev/null
+++ b/libempathy/action-chain.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "action-chain-internal.h"
+
+typedef struct {
+ TplPendingAction action;
+ gpointer user_data;
+} TplActionLink;
+
+
+TplActionChain *
+_tpl_action_chain_new_async (GObject *obj,
+ GAsyncReadyCallback cb,
+ gpointer user_data)
+{
+ TplActionChain *ret = g_slice_new0 (TplActionChain);
+
+ ret->chain = g_queue_new ();
+ ret->simple = g_simple_async_result_new (obj, cb, user_data,
+ _tpl_action_chain_new_async);
+
+ g_object_set_data (G_OBJECT (ret->simple), "chain", ret);
+
+ return ret;
+}
+
+
+static void
+link_free (TplActionLink *l)
+{
+ g_slice_free (TplActionLink, l);
+}
+
+
+void
+_tpl_action_chain_free (TplActionChain *self)
+{
+ g_queue_foreach (self->chain, (GFunc) link_free, NULL);
+ g_queue_free (self->chain);
+ g_object_unref (self->simple);
+ g_slice_free (TplActionChain, self);
+}
+
+
+gpointer // FIXME GObject *
+_tpl_action_chain_get_object (TplActionChain *self)
+{
+ GObject *obj;
+
+ g_return_val_if_fail (self != NULL && self->simple != NULL, NULL);
+
+ obj = g_async_result_get_source_object (G_ASYNC_RESULT (self->simple));
+ g_object_unref (obj); /* don't want the extra ref */
+
+ return obj;
+}
+
+
+void
+_tpl_action_chain_prepend (TplActionChain *self,
+ TplPendingAction func,
+ gpointer user_data)
+{
+ TplActionLink *l;
+
+ l = g_slice_new0 (TplActionLink);
+ l->action = func;
+ l->user_data = user_data;
+
+ g_queue_push_head (self->chain, l);
+}
+
+
+void
+_tpl_action_chain_append (TplActionChain *self,
+ TplPendingAction func,
+ gpointer user_data)
+{
+ TplActionLink *l;
+
+ l = g_slice_new0 (TplActionLink);
+ l->action = func;
+ l->user_data = user_data;
+
+ g_queue_push_tail (self->chain, l);
+}
+
+void
+_tpl_action_chain_start (TplActionChain *self)
+{
+ g_return_if_fail (!g_queue_is_empty (self->chain));
+
+ if (self->running)
+ return;
+
+ _tpl_action_chain_continue (self);
+}
+
+void
+_tpl_action_chain_continue (TplActionChain *self)
+{
+ if (g_queue_is_empty (self->chain))
+ {
+ self->running = FALSE;
+ g_simple_async_result_complete (self->simple);
+ }
+ else
+ {
+ TplActionLink *l = g_queue_pop_head (self->chain);
+
+ self->running = TRUE;
+ l->action (self, l->user_data);
+ link_free (l);
+ if (g_queue_is_empty (self->chain))
+ self->running = FALSE;
+ }
+}
+
+
+void
+_tpl_action_chain_clear (TplActionChain *self)
+{
+ g_queue_foreach (self->chain, (GFunc) link_free, NULL);
+ g_queue_clear (self->chain);
+}
+
+void
+_tpl_action_chain_terminate (TplActionChain *self,
+ const GError *error)
+{
+ GSimpleAsyncResult *simple = self->simple;
+
+ g_assert (error != NULL);
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+}
+
+
+/**
+ * _tpl_action_chain_new_finish:
+ * @source: the #GObject pass to _tpl_action_chain_new_async()
+ * @result: the #GAsyncResult pass in callback
+ * @error: a pointer to a #GError that will be set on error, or NULL to ignore
+ *
+ * Get the result from running the action chain (%TRUE if the chain completed
+ * successfully, %FALSE with @error set if it was terminated).
+ *
+ * This function also frees the chain.
+ *
+ * Returns: %TRUE on success, %FALSE with @error set on error.
+ */
+gboolean
+_tpl_action_chain_new_finish (GObject *source,
+ GAsyncResult *result,
+ GError **error)
+{
+ TplActionChain *chain;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, source,
+ _tpl_action_chain_new_async), FALSE);
+
+ chain = g_object_get_data (G_OBJECT (result), "chain");
+
+ g_return_val_if_fail (chain != NULL, FALSE);
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+ error))
+ return FALSE;
+
+ _tpl_action_chain_free (chain);
+ return TRUE;
+}
diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c
index 3706658fa..dfe7c6fee 100644
--- a/libempathy/empathy-contact.c
+++ b/libempathy/empathy-contact.c
@@ -1075,6 +1075,18 @@ empathy_contact_get_status (EmpathyContact *contact)
}
gboolean
+empathy_contact_can_sms (EmpathyContact *contact)
+{
+ EmpathyContactPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), FALSE);
+
+ priv = GET_PRIV (contact);
+
+ return priv->capabilities & EMPATHY_CAPABILITIES_SMS;
+}
+
+gboolean
empathy_contact_can_voip (EmpathyContact *contact)
{
EmpathyContactPriv *priv;
@@ -1166,6 +1178,9 @@ empathy_contact_can_do_action (EmpathyContact *self,
case EMPATHY_ACTION_CHAT:
sensitivity = TRUE;
break;
+ case EMPATHY_ACTION_SMS:
+ sensitivity = empathy_contact_can_sms (self);
+ break;
case EMPATHY_ACTION_AUDIO_CALL:
sensitivity = empathy_contact_can_voip_audio (self);
break;
@@ -1673,19 +1688,18 @@ tp_caps_to_capabilities (TpCapabilities *caps)
TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO))
capabilities |= EMPATHY_CAPABILITIES_VIDEO;
}
- }
- else if (!tp_strdiff (chan_type,
- TPY_IFACE_CHANNEL_TYPE_CALL))
- {
- guint j;
if (tp_asv_get_boolean (fixed_prop,
- TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL))
+ TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO, NULL))
capabilities |= EMPATHY_CAPABILITIES_AUDIO;
-
if (tp_asv_get_boolean (fixed_prop,
- TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL))
+ TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO, NULL))
capabilities |= EMPATHY_CAPABILITIES_VIDEO;
+ }
+ else if (!tp_strdiff (chan_type,
+ TPY_IFACE_CHANNEL_TYPE_CALL))
+ {
+ guint j;
for (j = 0; allowed_prop[j] != NULL; j++)
{
@@ -1696,6 +1710,19 @@ tp_caps_to_capabilities (TpCapabilities *caps)
TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO))
capabilities |= EMPATHY_CAPABILITIES_VIDEO;
}
+
+ if (tp_asv_get_boolean (fixed_prop,
+ TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL))
+ capabilities |= EMPATHY_CAPABILITIES_AUDIO;
+ if (tp_asv_get_boolean (fixed_prop,
+ TPY_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL))
+ capabilities |= EMPATHY_CAPABILITIES_VIDEO;
+ }
+ else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+ {
+ if (tp_asv_get_boolean (fixed_prop,
+ TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, NULL))
+ capabilities |= EMPATHY_CAPABILITIES_SMS;
}
}
diff --git a/libempathy/empathy-contact.h b/libempathy/empathy-contact.h
index 005cf1e7a..1eb493e84 100644
--- a/libempathy/empathy-contact.h
+++ b/libempathy/empathy-contact.h
@@ -67,6 +67,7 @@ typedef enum {
EMPATHY_CAPABILITIES_VIDEO = 1 << 1,
EMPATHY_CAPABILITIES_FT = 1 << 2,
EMPATHY_CAPABILITIES_RFB_STREAM_TUBE = 1 << 3,
+ EMPATHY_CAPABILITIES_SMS = 1 << 4,
EMPATHY_CAPABILITIES_UNKNOWN = 1 << 7
} EmpathyCapabilities;
@@ -94,6 +95,7 @@ void empathy_contact_set_is_user (EmpathyContact *contact,
gboolean is_user);
gboolean empathy_contact_is_online (EmpathyContact *contact);
const gchar * empathy_contact_get_status (EmpathyContact *contact);
+gboolean empathy_contact_can_sms (EmpathyContact *contact);
gboolean empathy_contact_can_voip (EmpathyContact *contact);
gboolean empathy_contact_can_voip_audio (EmpathyContact *contact);
gboolean empathy_contact_can_voip_video (EmpathyContact *contact);
@@ -102,6 +104,7 @@ gboolean empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact);
typedef enum {
EMPATHY_ACTION_CHAT,
+ EMPATHY_ACTION_SMS,
EMPATHY_ACTION_AUDIO_CALL,
EMPATHY_ACTION_VIDEO_CALL,
EMPATHY_ACTION_VIEW_LOGS,
diff --git a/libempathy/empathy-dispatcher.c b/libempathy/empathy-dispatcher.c
index 2a009b34a..b1cc1d267 100644
--- a/libempathy/empathy-dispatcher.c
+++ b/libempathy/empathy-dispatcher.c
@@ -509,9 +509,11 @@ ensure_text_channel_cb (GObject *source,
}
}
-void
-empathy_dispatcher_chat_with_contact_id (TpAccount *account,
- const gchar *contact_id,
+static void
+empathy_dispatcher_create_text_channel (TpAccount *account,
+ TpHandleType target_handle_type,
+ const gchar *target_id,
+ gboolean sms_channel,
gint64 timestamp)
{
GHashTable *request;
@@ -520,10 +522,14 @@ empathy_dispatcher_chat_with_contact_id (TpAccount *account,
request = tp_asv_new (
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
TP_IFACE_CHANNEL_TYPE_TEXT,
- TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
- TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, contact_id,
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, target_handle_type,
+ TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, target_id,
NULL);
+ if (sms_channel)
+ tp_asv_set_boolean (request,
+ TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, TRUE);
+
req = tp_account_channel_request_new (account, request, timestamp);
tp_account_channel_request_ensure_channel_async (req, NULL, NULL,
@@ -534,27 +540,30 @@ empathy_dispatcher_chat_with_contact_id (TpAccount *account,
}
void
+empathy_dispatcher_chat_with_contact_id (TpAccount *account,
+ const gchar *contact_id,
+ gint64 timestamp)
+{
+ empathy_dispatcher_create_text_channel (account, TP_HANDLE_TYPE_CONTACT,
+ contact_id, FALSE, timestamp);
+}
+
+void
empathy_dispatcher_join_muc (TpAccount *account,
const gchar *room_name,
gint64 timestamp)
{
- GHashTable *request;
- TpAccountChannelRequest *req;
-
- request = tp_asv_new (
- TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
- TP_IFACE_CHANNEL_TYPE_TEXT,
- TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
- TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, room_name,
- NULL);
-
- req = tp_account_channel_request_new (account, request, timestamp);
-
- tp_account_channel_request_ensure_channel_async (req, NULL, NULL,
- ensure_text_channel_cb, NULL);
+ empathy_dispatcher_create_text_channel (account, TP_HANDLE_TYPE_ROOM,
+ room_name, FALSE, timestamp);
+}
- g_hash_table_unref (request);
- g_object_unref (req);
+void
+empathy_dispatcher_sms_contact_id (TpAccount *account,
+ const gchar *contact_id,
+ gint64 timestamp)
+{
+ empathy_dispatcher_create_text_channel (account, TP_HANDLE_TYPE_CONTACT,
+ contact_id, TRUE, timestamp);
}
static gboolean
diff --git a/libempathy/empathy-dispatcher.h b/libempathy/empathy-dispatcher.h
index cd54fa5d1..19da9dde5 100644
--- a/libempathy/empathy-dispatcher.h
+++ b/libempathy/empathy-dispatcher.h
@@ -70,6 +70,11 @@ void empathy_dispatcher_join_muc (TpAccount *account,
const gchar *roomname,
gint64 timestamp);
+void
+empathy_dispatcher_sms_contact_id (TpAccount *account,
+ const gchar *contact_id,
+ gint64 timestamp);
+
void empathy_dispatcher_find_requestable_channel_classes_async
(EmpathyDispatcher *dispatcher, TpConnection *connection,
const gchar *channel_type, guint handle_type,
diff --git a/libempathy/empathy-ft-handler.c b/libempathy/empathy-ft-handler.c
index 94b9cf3d4..26db91b07 100644
--- a/libempathy/empathy-ft-handler.c
+++ b/libempathy/empathy-ft-handler.c
@@ -144,7 +144,7 @@ typedef struct {
/* time and speed */
gdouble speed;
guint remaining_time;
- time_t last_update_time;
+ gint64 last_update_time;
gboolean is_completed;
} EmpathyFTHandlerPriv;
@@ -662,7 +662,7 @@ update_remaining_time_and_speed (EmpathyFTHandler *handler,
guint64 transferred_bytes)
{
EmpathyFTHandlerPriv *priv = GET_PRIV (handler);
- time_t elapsed_time, current_time;
+ gint64 elapsed_time, current_time;
guint64 transferred, last_transferred_bytes;
gdouble speed;
gint remaining_time;
diff --git a/libempathy/empathy-message.c b/libempathy/empathy-message.c
index 65ea2ae40..15999dcdd 100644
--- a/libempathy/empathy-message.c
+++ b/libempathy/empathy-message.c
@@ -26,6 +26,8 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
+
#include <telepathy-glib/util.h>
#include <telepathy-glib/account.h>
#include <telepathy-glib/account-manager.h>
@@ -33,6 +35,7 @@
#include <telepathy-logger/entity.h>
#include <telepathy-logger/event.h>
#include <telepathy-logger/text-event.h>
+#include <telepathy-logger/call-event.h>
#include "empathy-message.h"
#include "empathy-utils.h"
@@ -44,7 +47,7 @@ typedef struct {
EmpathyContact *sender;
EmpathyContact *receiver;
gchar *body;
- time_t timestamp;
+ gint64 timestamp;
gboolean is_backlog;
guint id;
gboolean incoming;
@@ -93,44 +96,46 @@ empathy_message_class_init (EmpathyMessageClass *class)
TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_SENDER,
g_param_spec_object ("sender",
"Message Sender",
"The sender of the message",
EMPATHY_TYPE_CONTACT,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_RECEIVER,
g_param_spec_object ("receiver",
"Message Receiver",
"The receiver of the message",
EMPATHY_TYPE_CONTACT,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_BODY,
g_param_spec_string ("body",
"Message Body",
"The content of the message",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_TIMESTAMP,
- g_param_spec_long ("timestamp",
+ g_param_spec_int64 ("timestamp",
"timestamp",
"timestamp",
- -1,
- G_MAXLONG,
- -1,
- G_PARAM_READWRITE));
+ G_MININT64, G_MAXINT64, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_IS_BACKLOG,
g_param_spec_boolean ("is-backlog",
"History message",
"If the message belongs to history",
FALSE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
@@ -139,7 +144,8 @@ empathy_message_class_init (EmpathyMessageClass *class)
"Incoming",
"If this is an incoming (as opposed to sent) message",
FALSE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_FLAGS,
@@ -147,7 +153,8 @@ empathy_message_class_init (EmpathyMessageClass *class)
"Flags",
"The TpChannelTextMessageFlags of this message",
0, G_MAXUINT, 0,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
@@ -205,9 +212,18 @@ message_get_property (GObject *object,
case PROP_BODY:
g_value_set_string (value, priv->body);
break;
+ case PROP_TIMESTAMP:
+ g_value_set_int64 (value, priv->timestamp);
+ break;
+ case PROP_IS_BACKLOG:
+ g_value_set_boolean (value, priv->is_backlog);
+ break;
case PROP_INCOMING:
g_value_set_boolean (value, priv->incoming);
break;
+ case PROP_FLAGS:
+ g_value_set_uint (value, priv->flags);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -226,8 +242,7 @@ message_set_property (GObject *object,
switch (param_id) {
case PROP_TYPE:
- empathy_message_set_tptype (EMPATHY_MESSAGE (object),
- g_value_get_uint (value));
+ priv->type = g_value_get_uint (value);
break;
case PROP_SENDER:
empathy_message_set_sender (EMPATHY_MESSAGE (object),
@@ -238,12 +253,23 @@ message_set_property (GObject *object,
EMPATHY_CONTACT (g_value_get_object (value)));
break;
case PROP_BODY:
- empathy_message_set_body (EMPATHY_MESSAGE (object),
- g_value_get_string (value));
+ g_assert (priv->body == NULL); /* construct only */
+ priv->body = g_value_dup_string (value);
+ break;
+ case PROP_TIMESTAMP:
+ priv->timestamp = g_value_get_int64 (value);
+ if (priv->timestamp <= 0)
+ priv->timestamp = empathy_time_get_current ();
+ break;
+ case PROP_IS_BACKLOG:
+ priv->is_backlog = g_value_get_boolean (value);
break;
case PROP_INCOMING:
priv->incoming = g_value_get_boolean (value);
break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -251,14 +277,6 @@ message_set_property (GObject *object,
}
EmpathyMessage *
-empathy_message_new (const gchar *body)
-{
- return g_object_new (EMPATHY_TYPE_MESSAGE,
- "body", body,
- NULL);
-}
-
-EmpathyMessage *
empathy_message_from_tpl_log_event (TplEvent *logevent)
{
EmpathyMessage *retval = NULL;
@@ -287,23 +305,42 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
tpl_event_get_account_path (logevent));
g_object_unref (acc_man);
- /* TODO Currently only TplTextEvent exists as subclass of TplEvent, in
- * future more TplEvent will exist and EmpathyMessage should probably
- * be enhanced to support other types of log entries (ie TplCallEvent).
- *
- * For now we just check (simply) that we are dealing with the only supported type,
- * then there will be a if/then/else or switch handling all the supported
- * cases.
- */
- if (!TPL_IS_TEXT_EVENT (logevent))
+ if (TPL_IS_TEXT_EVENT (logevent)) {
+ body = g_strdup (tpl_text_event_get_message (
+ TPL_TEXT_EVENT (logevent)));
+
+ retval = g_object_new (EMPATHY_TYPE_MESSAGE,
+ "type", tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent)),
+ "body", body,
+ "timestamp", tpl_event_get_timestamp (logevent),
+ "is-backlog", TRUE,
+ NULL);
+
+ g_free (body);
+ } else if (TPL_IS_CALL_EVENT (logevent)) {
+ TplCallEvent *call = TPL_CALL_EVENT (logevent);
+ if (tpl_call_event_get_end_reason (call) == TPL_CALL_END_REASON_NO_ANSWER)
+ body = g_strdup_printf (_("Missed call from %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (logevent)));
+ else if (tpl_entity_get_entity_type (tpl_event_get_sender (logevent)) == TPL_ENTITY_SELF)
+ body = g_strdup_printf (_("Called %s"),
+ tpl_entity_get_alias (tpl_event_get_receiver (logevent)));
+ else
+ body = g_strdup_printf (_("Call from %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (logevent)));
+
+ retval = g_object_new (EMPATHY_TYPE_MESSAGE,
+ "body", body,
+ "timestamp", tpl_event_get_timestamp (logevent),
+ NULL);
+ g_free (body);
+ } else {
return NULL;
+ }
- body = g_strdup (tpl_text_event_get_message (
- TPL_TEXT_EVENT (logevent)));
receiver = tpl_event_get_receiver (logevent);
sender = tpl_event_get_sender (logevent);
- retval = empathy_message_new (body);
if (receiver != NULL) {
contact = empathy_contact_from_tpl_contact (account, receiver);
empathy_message_set_receiver (retval, contact);
@@ -316,14 +353,6 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
g_object_unref (contact);
}
- empathy_message_set_timestamp (retval,
- tpl_event_get_timestamp (logevent));
- empathy_message_set_tptype (retval,
- tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent)));
- empathy_message_set_is_backlog (retval, TRUE);
-
- g_free (body);
-
return retval;
}
@@ -340,21 +369,6 @@ empathy_message_get_tptype (EmpathyMessage *message)
return priv->type;
}
-void
-empathy_message_set_tptype (EmpathyMessage *message,
- TpChannelTextMessageType type)
-{
- EmpathyMessagePriv *priv;
-
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
- priv = GET_PRIV (message);
-
- priv->type = type;
-
- g_object_notify (G_OBJECT (message), "type");
-}
-
EmpathyContact *
empathy_message_get_sender (EmpathyMessage *message)
{
@@ -433,26 +447,7 @@ empathy_message_get_body (EmpathyMessage *message)
return priv->body;
}
-void
-empathy_message_set_body (EmpathyMessage *message,
- const gchar *body)
-{
- EmpathyMessagePriv *priv = GET_PRIV (message);
-
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
- g_free (priv->body);
-
- if (body) {
- priv->body = g_strdup (body);
- } else {
- priv->body = NULL;
- }
-
- g_object_notify (G_OBJECT (message), "body");
-}
-
-time_t
+gint64
empathy_message_get_timestamp (EmpathyMessage *message)
{
EmpathyMessagePriv *priv;
@@ -464,26 +459,6 @@ empathy_message_get_timestamp (EmpathyMessage *message)
return priv->timestamp;
}
-void
-empathy_message_set_timestamp (EmpathyMessage *message,
- time_t timestamp)
-{
- EmpathyMessagePriv *priv;
-
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
- g_return_if_fail (timestamp >= -1);
-
- priv = GET_PRIV (message);
-
- if (timestamp <= 0) {
- priv->timestamp = empathy_time_get_current ();
- } else {
- priv->timestamp = timestamp;
- }
-
- g_object_notify (G_OBJECT (message), "timestamp");
-}
-
gboolean
empathy_message_is_backlog (EmpathyMessage *message)
{
@@ -496,21 +471,6 @@ empathy_message_is_backlog (EmpathyMessage *message)
return priv->is_backlog;
}
-void
-empathy_message_set_is_backlog (EmpathyMessage *message,
- gboolean is_backlog)
-{
- EmpathyMessagePriv *priv;
-
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
- priv = GET_PRIV (message);
-
- priv->is_backlog = is_backlog;
-
- g_object_notify (G_OBJECT (message), "is-backlog");
-}
-
#define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
gboolean
empathy_message_should_highlight (EmpathyMessage *message)
@@ -627,28 +587,6 @@ empathy_message_get_id (EmpathyMessage *message)
return priv->id;
}
-void
-empathy_message_set_id (EmpathyMessage *message, guint id)
-{
- EmpathyMessagePriv *priv = GET_PRIV (message);
-
- priv->id = id;
-}
-
-void
-empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
-{
- EmpathyMessagePriv *priv;
-
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
-
- priv = GET_PRIV (message);
-
- priv->incoming = incoming;
-
- g_object_notify (G_OBJECT (message), "incoming");
-}
-
gboolean
empathy_message_is_incoming (EmpathyMessage *message)
{
@@ -689,17 +627,38 @@ empathy_message_get_flags (EmpathyMessage *self)
return priv->flags;
}
-void
-empathy_message_set_flags (EmpathyMessage *self,
- TpChannelTextMessageFlags flags)
+EmpathyMessage *
+empathy_message_new_from_tp_message (TpMessage *tp_msg,
+ gboolean incoming)
{
+ EmpathyMessage *message;
EmpathyMessagePriv *priv;
+ gchar *body;
+ TpChannelTextMessageFlags flags;
+ guint id;
- g_return_if_fail (EMPATHY_IS_MESSAGE (self));
+ g_return_val_if_fail (TP_IS_MESSAGE (tp_msg), NULL);
- priv = GET_PRIV (self);
+ body = tp_message_to_text (tp_msg, &flags);
- priv->flags = flags;
+ message = g_object_new (EMPATHY_TYPE_MESSAGE,
+ "body", body,
+ "type", tp_message_get_message_type (tp_msg),
+ "timestamp", tp_message_get_received_timestamp (tp_msg),
+ "flags", flags,
+ "is-backlog", flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK,
+ "incoming", incoming,
+ NULL);
- g_object_notify (G_OBJECT (self), "flags");
+ priv = GET_PRIV (message);
+
+ /* FIXME: this is pretty low level, ideally we shouldn't have to use the
+ * ID directly but we don't use TpTextChannel's ack API everywhere yet. */
+ id = tp_asv_get_uint32 (tp_message_peek (tp_msg, 0),
+ "pending-message-id", NULL);
+
+ priv->id = id;
+
+ g_free (body);
+ return message;
}
diff --git a/libempathy/empathy-message.h b/libempathy/empathy-message.h
index 1db0eff60..7508cb08e 100644
--- a/libempathy/empathy-message.h
+++ b/libempathy/empathy-message.h
@@ -26,6 +26,7 @@
#define __EMPATHY_MESSAGE_H__
#include <glib-object.h>
+#include <telepathy-glib/message.h>
#include <telepathy-logger/event.h>
#include "empathy-contact.h"
@@ -53,11 +54,12 @@ struct _EmpathyMessageClass {
};
GType empathy_message_get_type (void) G_GNUC_CONST;
-EmpathyMessage * empathy_message_new (const gchar *body);
+
EmpathyMessage * empathy_message_from_tpl_log_event (TplEvent *logevent);
+EmpathyMessage * empathy_message_new_from_tp_message (TpMessage *tp_msg,
+ gboolean incoming);
+
TpChannelTextMessageType empathy_message_get_tptype (EmpathyMessage *message);
-void empathy_message_set_tptype (EmpathyMessage *message,
- TpChannelTextMessageType type);
EmpathyContact * empathy_message_get_sender (EmpathyMessage *message);
void empathy_message_set_sender (EmpathyMessage *message,
EmpathyContact *contact);
@@ -65,30 +67,19 @@ EmpathyContact * empathy_message_get_receiver (EmpathyMessage
void empathy_message_set_receiver (EmpathyMessage *message,
EmpathyContact *contact);
const gchar * empathy_message_get_body (EmpathyMessage *message);
-void empathy_message_set_body (EmpathyMessage *message,
- const gchar *body);
-time_t empathy_message_get_timestamp (EmpathyMessage *message);
-void empathy_message_set_timestamp (EmpathyMessage *message,
- time_t timestamp);
+gint64 empathy_message_get_timestamp (EmpathyMessage *message);
gboolean empathy_message_is_backlog (EmpathyMessage *message);
-void empathy_message_set_is_backlog (EmpathyMessage *message,
- gboolean is_backlog);
gboolean empathy_message_is_incoming (EmpathyMessage *message);
-void empathy_message_set_incoming (EmpathyMessage *message,
- gboolean incoming);
gboolean empathy_message_should_highlight (EmpathyMessage *message);
TpChannelTextMessageType empathy_message_type_from_str (const gchar *type_str);
const gchar * empathy_message_type_to_str (TpChannelTextMessageType type);
guint empathy_message_get_id (EmpathyMessage *message);
-void empathy_message_set_id (EmpathyMessage *message, guint id);
gboolean empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2);
TpChannelTextMessageFlags empathy_message_get_flags (EmpathyMessage *message);
-void empathy_message_set_flags (EmpathyMessage *message,
- TpChannelTextMessageFlags flags);
G_END_DECLS
diff --git a/libempathy/empathy-time.c b/libempathy/empathy-time.c
index 13e17d152..f33152d97 100644
--- a/libempathy/empathy-time.c
+++ b/libempathy/empathy-time.c
@@ -31,139 +31,107 @@
/* Note: EmpathyTime is always in UTC. */
-time_t
+gint64
empathy_time_get_current (void)
{
- return time (NULL);
-}
-
-time_t
-empathy_time_get_local_time (struct tm *tm)
-{
- const gchar *tz;
- time_t t;
-
- tz = g_getenv ("TZ");
- g_setenv ("TZ", "", TRUE);
-
- tzset ();
-
- t = mktime (tm);
+ GDateTime *now;
+ gint64 result;
- if (tz) {
- g_setenv ("TZ", tz, TRUE);
- } else {
- g_unsetenv ("TZ");
- }
-
- tzset ();
+ now = g_date_time_new_now_utc ();
+ result = g_date_time_to_unix (now);
+ g_date_time_unref (now);
- return t;
-}
-
-/* The format is: "20021209T23:51:30" and is in UTC. 0 is returned on
- * failure. The alternative format "20021209" is also accepted.
- */
-time_t
-empathy_time_parse (const gchar *str)
-{
- struct tm tm;
- gint year, month;
- gint n_parsed;
-
- memset (&tm, 0, sizeof (struct tm));
-
- n_parsed = sscanf (str, "%4d%2d%2dT%2d:%2d:%2d",
- &year, &month, &tm.tm_mday, &tm.tm_hour,
- &tm.tm_min, &tm.tm_sec);
- if (n_parsed != 3 && n_parsed != 6) {
- return 0;
- }
-
- tm.tm_year = year - 1900;
- tm.tm_mon = month - 1;
- tm.tm_isdst = -1;
-
- return empathy_time_get_local_time (&tm);
+ return result;
}
/* Converts the UTC timestamp to a string, also in UTC. Returns NULL on failure. */
gchar *
-empathy_time_to_string_utc (time_t t,
+empathy_time_to_string_utc (gint64 t,
const gchar *format)
{
- gchar stamp[128];
- struct tm *tm;
+ GDateTime *d;
+ char *result;
g_return_val_if_fail (format != NULL, NULL);
- tm = gmtime (&t);
- if (strftime (stamp, sizeof (stamp), format, tm) == 0) {
- return NULL;
- }
+ d = g_date_time_new_from_unix_utc (t);
+ result = g_date_time_format (d, format);
+ g_date_time_unref (d);
- return g_strdup (stamp);
+ return result;
}
/* Converts the UTC timestamp to a string, in local time. Returns NULL on failure. */
gchar *
-empathy_time_to_string_local (time_t t,
+empathy_time_to_string_local (gint64 t,
const gchar *format)
{
- gchar stamp[128];
- struct tm *tm;
+ GDateTime *d, *local;
+ gchar *result;
g_return_val_if_fail (format != NULL, NULL);
- tm = localtime (&t);
- if (strftime (stamp, sizeof (stamp), format, tm) == 0) {
- return NULL;
- }
+ d = g_date_time_new_from_unix_utc (t);
+ local = g_date_time_to_local (d);
+ g_date_time_unref (d);
+
+ result = g_date_time_format (local, format);
+ g_date_time_unref (local);
- return g_strdup (stamp);
+ return result;
}
gchar *
-empathy_time_to_string_relative (time_t then)
+empathy_time_to_string_relative (gint64 t)
{
- time_t now;
+ GDateTime *now, *then;
gint seconds;
+ GTimeSpan delta;
+ gchar *result;
+
+ now = g_date_time_new_now_utc ();
+ then = g_date_time_new_from_unix_utc (t);
- now = time (NULL);
- seconds = now - then;
+ delta = g_date_time_difference (now, then);
+ seconds = delta / G_TIME_SPAN_SECOND;
if (seconds > 0) {
if (seconds < 60) {
- return g_strdup_printf (ngettext ("%d second ago",
+ result = g_strdup_printf (ngettext ("%d second ago",
"%d seconds ago", seconds), seconds);
}
else if (seconds < (60 * 60)) {
seconds /= 60;
- return g_strdup_printf (ngettext ("%d minute ago",
+ result = g_strdup_printf (ngettext ("%d minute ago",
"%d minutes ago", seconds), seconds);
}
else if (seconds < (60 * 60 * 24)) {
seconds /= 60 * 60;
- return g_strdup_printf (ngettext ("%d hour ago",
+ result = g_strdup_printf (ngettext ("%d hour ago",
"%d hours ago", seconds), seconds);
}
else if (seconds < (60 * 60 * 24 * 7)) {
seconds /= 60 * 60 * 24;
- return g_strdup_printf (ngettext ("%d day ago",
+ result = g_strdup_printf (ngettext ("%d day ago",
"%d days ago", seconds), seconds);
}
else if (seconds < (60 * 60 * 24 * 30)) {
seconds /= 60 * 60 * 24 * 7;
- return g_strdup_printf (ngettext ("%d week ago",
+ result = g_strdup_printf (ngettext ("%d week ago",
"%d weeks ago", seconds), seconds);
}
else {
seconds /= 60 * 60 * 24 * 30;
- return g_strdup_printf (ngettext ("%d month ago",
+ result = g_strdup_printf (ngettext ("%d month ago",
"%d months ago", seconds), seconds);
}
}
else {
- return g_strdup (_("in the future"));
+ result = g_strdup (_("in the future"));
}
+
+ g_date_time_unref (now);
+ g_date_time_unref (then);
+
+ return result;
}
diff --git a/libempathy/empathy-time.h b/libempathy/empathy-time.h
index 65e0127c7..7fac48221 100644
--- a/libempathy/empathy-time.h
+++ b/libempathy/empathy-time.h
@@ -39,14 +39,12 @@ G_BEGIN_DECLS
#define EMPATHY_DATE_FORMAT_DISPLAY_SHORT _("%a %d %b %Y")
#define EMPATHY_TIME_DATE_FORMAT_DISPLAY_SHORT _("%a %d %b %Y, %H:%M")
-time_t empathy_time_get_current (void);
-time_t empathy_time_get_local_time (struct tm *tm);
-time_t empathy_time_parse (const gchar *str);
-gchar *empathy_time_to_string_utc (time_t t,
+gint64 empathy_time_get_current (void);
+gchar *empathy_time_to_string_utc (gint64 t,
const gchar *format);
-gchar *empathy_time_to_string_local (time_t t,
+gchar *empathy_time_to_string_local (gint64 t,
const gchar *format);
-gchar *empathy_time_to_string_relative (time_t t);
+gchar *empathy_time_to_string_relative (gint64 t);
G_END_DECLS
diff --git a/libempathy/empathy-tp-chat.c b/libempathy/empathy-tp-chat.c
index 462f8a77b..29f886416 100644
--- a/libempathy/empathy-tp-chat.c
+++ b/libempathy/empathy-tp-chat.c
@@ -31,6 +31,7 @@
#include "empathy-tp-contact-factory.h"
#include "empathy-contact-list.h"
#include "empathy-dispatcher.h"
+#include "empathy-enum-types.h"
#include "empathy-marshal.h"
#include "empathy-time.h"
#include "empathy-utils.h"
@@ -47,7 +48,6 @@ typedef struct {
EmpathyContact *remote_contact;
GList *members;
TpChannel *channel;
- gboolean listing_pending_messages;
/* Queue of messages not signalled yet */
GQueue *messages_queue;
/* Queue of messages signalled but not acked yet */
@@ -60,6 +60,10 @@ typedef struct {
gboolean got_password_flags;
gboolean ready;
gboolean can_upgrade_to_muc;
+ gboolean got_sms_channel;
+ gboolean sms_channel;
+
+ GHashTable *messages_being_sent;
} EmpathyTpChatPriv;
static void tp_chat_iface_init (EmpathyContactListIface *iface);
@@ -71,6 +75,8 @@ enum {
PROP_REMOTE_CONTACT,
PROP_PASSWORD_NEEDED,
PROP_READY,
+ PROP_SMS_CHANNEL,
+ PROP_N_MESSAGES_SENDING,
};
enum {
@@ -91,6 +97,39 @@ G_DEFINE_TYPE_WITH_CODE (EmpathyTpChat, empathy_tp_chat, G_TYPE_OBJECT,
static void acknowledge_messages (EmpathyTpChat *chat, GArray *ids);
static void
+tp_chat_set_delivery_status (EmpathyTpChat *self,
+ const gchar *token,
+ EmpathyDeliveryStatus delivery_status)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+ /* channel must support receiving failures and successes */
+ if (!tp_str_empty (token) &&
+ tp_text_channel_get_delivery_reporting_support (
+ TP_TEXT_CHANNEL (priv->channel)) &
+ (TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_FAILURES |
+ TP_DELIVERY_REPORTING_SUPPORT_FLAG_RECEIVE_SUCCESSES)) {
+
+ DEBUG ("Delivery status (%s) = %u", token, delivery_status);
+
+ switch (delivery_status) {
+ case EMPATHY_DELIVERY_STATUS_NONE:
+ g_hash_table_remove (priv->messages_being_sent,
+ token);
+ break;
+
+ default:
+ g_hash_table_insert (priv->messages_being_sent,
+ g_strdup (token),
+ GUINT_TO_POINTER (delivery_status));
+ break;
+ }
+
+ g_object_notify (G_OBJECT (self), "n-messages-sending");
+ }
+}
+
+static void
tp_chat_invalidated_cb (TpProxy *proxy,
guint domain,
gint code,
@@ -280,150 +319,204 @@ tp_chat_got_sender_cb (TpConnection *connection,
static void
tp_chat_build_message (EmpathyTpChat *chat,
- gboolean incoming,
- guint id,
- guint type,
- guint timestamp,
- guint from_handle,
- const gchar *message_body,
- TpChannelTextMessageFlags flags)
+ TpMessage *msg,
+ gboolean incoming)
{
EmpathyTpChatPriv *priv;
EmpathyMessage *message;
+ TpContact *sender;
priv = GET_PRIV (chat);
- message = empathy_message_new (message_body);
- empathy_message_set_tptype (message, type);
+ message = empathy_message_new_from_tp_message (msg, incoming);
+ /* FIXME: this is actually a lie for incoming messages. */
empathy_message_set_receiver (message, priv->user);
- empathy_message_set_timestamp (message, timestamp);
- empathy_message_set_id (message, id);
- empathy_message_set_incoming (message, incoming);
- empathy_message_set_flags (message, flags);
-
- if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK)
- empathy_message_set_is_backlog (message, TRUE);
g_queue_push_tail (priv->messages_queue, message);
- if (from_handle == 0) {
+ sender = tp_signalled_message_get_sender (msg);
+ g_assert (sender != NULL);
+
+ if (tp_contact_get_handle (sender) == 0) {
empathy_message_set_sender (message, priv->user);
tp_chat_emit_queued_messages (chat);
} else {
empathy_tp_contact_factory_get_from_handle (priv->connection,
- from_handle,
+ tp_contact_get_handle (sender),
tp_chat_got_sender_cb,
message, NULL, G_OBJECT (chat));
}
}
static void
-tp_chat_received_cb (TpChannel *channel,
- guint message_id,
- guint timestamp,
- guint from_handle,
- guint message_type,
- guint message_flags,
- const gchar *message_body,
- gpointer user_data,
- GObject *chat_)
-{
- EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
- EmpathyTpChatPriv *priv = GET_PRIV (chat);
+handle_delivery_report (EmpathyTpChat *self,
+ TpMessage *message)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+ TpDeliveryStatus delivery_status;
+ const GHashTable *header;
+ TpChannelTextSendError delivery_error;
+ gboolean valid;
+ GPtrArray *echo;
+ const gchar *message_body = NULL;
+ const gchar *delivery_dbus_error;
+ const gchar *delivery_token = NULL;
+
+ header = tp_message_peek (message, 0);
+ if (header == NULL)
+ goto out;
- if (priv->channel == NULL)
- return;
+ delivery_token = tp_asv_get_string (header, "delivery-token");
+ delivery_status = tp_asv_get_uint32 (header, "delivery-status", &valid);
+
+ if (!valid) {
+ goto out;
+ } else if (delivery_status == TP_DELIVERY_STATUS_ACCEPTED) {
+ DEBUG ("Accepted %s", delivery_token);
+ tp_chat_set_delivery_status (self, delivery_token,
+ EMPATHY_DELIVERY_STATUS_ACCEPTED);
+ goto out;
+ } else if (delivery_status == TP_DELIVERY_STATUS_DELIVERED) {
+ DEBUG ("Delivered %s", delivery_token);
+ tp_chat_set_delivery_status (self, delivery_token,
+ EMPATHY_DELIVERY_STATUS_NONE);
+ goto out;
+ } else if (delivery_status != TP_DELIVERY_STATUS_PERMANENTLY_FAILED) {
+ goto out;
+ }
+
+ delivery_error = tp_asv_get_uint32 (header, "delivery-error", &valid);
+ if (!valid)
+ delivery_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
- if (priv->listing_pending_messages) {
+ delivery_dbus_error = tp_asv_get_string (header, "delivery-dbus-error");
+
+ /* TODO: ideally we should use tp-glib API giving us the echoed message as a
+ * TpMessage. (fdo #35884) */
+ echo = tp_asv_get_boxed (header, "delivery-echo",
+ TP_ARRAY_TYPE_MESSAGE_PART_LIST);
+ if (echo != NULL && echo->len >= 1) {
+ const GHashTable *echo_body;
+
+ echo_body = g_ptr_array_index (echo, 1);
+ if (echo_body != NULL)
+ message_body = tp_asv_get_string (echo_body, "content");
+ }
+
+ tp_chat_set_delivery_status (self, delivery_token,
+ EMPATHY_DELIVERY_STATUS_NONE);
+ g_signal_emit (self, signals[SEND_ERROR], 0, message_body,
+ delivery_error, delivery_dbus_error);
+
+out:
+ tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (priv->channel),
+ message, NULL, NULL);
+}
+
+static void
+handle_incoming_message (EmpathyTpChat *self,
+ TpMessage *message,
+ gboolean pending)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+ gchar *message_body;
+
+ if (tp_message_is_delivery_report (message)) {
+ handle_delivery_report (self, message);
return;
}
- DEBUG ("Message received from channel %s: %s",
- tp_proxy_get_object_path (channel), message_body);
+ message_body = tp_message_to_text (message, NULL);
- if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
- !tp_strdiff (message_body, "")) {
- GArray *ids;
+ DEBUG ("Message %s (channel %s): %s",
+ pending ? "pending" : "received",
+ tp_proxy_get_object_path (priv->channel), message_body);
+ if (message_body == NULL) {
DEBUG ("Empty message with NonTextContent, ignoring and acking.");
- ids = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
- g_array_append_val (ids, message_id);
- acknowledge_messages (chat, ids);
- g_array_free (ids, TRUE);
-
+ tp_text_channel_ack_message_async (TP_TEXT_CHANNEL (priv->channel),
+ message, NULL, NULL);
return;
}
- tp_chat_build_message (chat,
- TRUE,
- message_id,
- message_type,
- timestamp,
- from_handle,
- message_body,
- message_flags);
+ tp_chat_build_message (self, message, TRUE);
+
+ g_free (message_body);
}
static void
-tp_chat_sent_cb (TpChannel *channel,
- guint timestamp,
- guint message_type,
- const gchar *message_body,
- gpointer user_data,
- GObject *chat_)
-{
- EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
- EmpathyTpChatPriv *priv = GET_PRIV (chat);
+message_received_cb (TpTextChannel *channel,
+ TpMessage *message,
+ EmpathyTpChat *chat)
+{
+ handle_incoming_message (chat, message, FALSE);
+}
- if (priv->channel == NULL)
- return;
+static void
+message_sent_cb (TpTextChannel *channel,
+ TpMessage *message,
+ TpMessageSendingFlags flags,
+ gchar *token,
+ EmpathyTpChat *chat)
+{
+ gchar *message_body;
+
+ message_body = tp_message_to_text (message, NULL);
DEBUG ("Message sent: %s", message_body);
- tp_chat_build_message (chat,
- FALSE,
- 0,
- message_type,
- timestamp,
- 0,
- message_body,
- 0);
+ tp_chat_build_message (chat, message, FALSE);
+
+ g_free (message_body);
}
-static void
-tp_chat_send_error_cb (TpChannel *channel,
- guint error_code,
- guint timestamp,
- guint message_type,
- const gchar *message_body,
- gpointer user_data,
- GObject *chat)
+static TpChannelTextSendError
+error_to_text_send_error (GError *error)
{
- EmpathyTpChatPriv *priv = GET_PRIV (chat);
-
- if (priv->channel == NULL)
- return;
+ if (error->domain != TP_ERRORS)
+ return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
- DEBUG ("Error sending '%s' (%d)", message_body, error_code);
+ switch (error->code) {
+ case TP_ERROR_OFFLINE:
+ return TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE;
+ case TP_ERROR_INVALID_HANDLE:
+ return TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT;
+ case TP_ERROR_PERMISSION_DENIED:
+ return TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED;
+ case TP_ERROR_NOT_IMPLEMENTED:
+ return TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED;
+ }
- g_signal_emit (chat, signals[SEND_ERROR], 0, message_body, error_code);
+ return TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
}
static void
-tp_chat_send_cb (TpChannel *proxy,
- const GError *error,
- gpointer user_data,
- GObject *chat)
+message_send_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- EmpathyMessage *message = EMPATHY_MESSAGE (user_data);
+ EmpathyTpChat *chat = user_data;
+ TpTextChannel *channel = (TpTextChannel *) source;
+ gchar *token = NULL;
+ GError *error = NULL;
- if (error) {
+ if (!tp_text_channel_send_message_finish (channel, result, &token, &error)) {
DEBUG ("Error: %s", error->message);
+
+ /* FIXME: we should use the body of the message as first argument of the
+ * signal but can't easily get it as we just get a user_data pointer. Once
+ * we'll have rebased EmpathyTpChat on top of TpTextChannel we'll be able
+ * to use the user_data pointer to pass the message and fix this. */
g_signal_emit (chat, signals[SEND_ERROR], 0,
- empathy_message_get_body (message),
- TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN);
+ NULL, error_to_text_send_error (error), NULL);
+
+ g_error_free (error);
}
+
+ tp_chat_set_delivery_status (chat, token,
+ EMPATHY_DELIVERY_STATUS_SENDING);
+ g_free (token);
}
typedef struct {
@@ -457,86 +550,33 @@ static void
tp_chat_state_changed_cb (TpChannel *channel,
TpHandle handle,
TpChannelChatState state,
- gpointer user_data,
- GObject *chat)
+ EmpathyTpChat *chat)
{
EmpathyTpChatPriv *priv = GET_PRIV (chat);
empathy_tp_contact_factory_get_from_handle (priv->connection, handle,
tp_chat_state_changed_got_contact_cb, GUINT_TO_POINTER (state),
- NULL, chat);
+ NULL, G_OBJECT (chat));
}
static void
-tp_chat_list_pending_messages_cb (TpChannel *channel,
- const GPtrArray *messages_list,
- const GError *error,
- gpointer user_data,
- GObject *chat_)
+list_pending_messages (EmpathyTpChat *self)
{
- EmpathyTpChat *chat = EMPATHY_TP_CHAT (chat_);
- EmpathyTpChatPriv *priv = GET_PRIV (chat);
- guint i;
- GArray *empty_non_text_content_ids = NULL;
-
- priv->listing_pending_messages = FALSE;
-
- if (priv->channel == NULL)
- return;
-
- if (error) {
- DEBUG ("Error listing pending messages: %s", error->message);
- return;
- }
-
- for (i = 0; i < messages_list->len; i++) {
- GValueArray *message_struct;
- const gchar *message_body;
- guint message_id;
- guint timestamp;
- guint from_handle;
- guint message_type;
- guint message_flags;
-
- message_struct = g_ptr_array_index (messages_list, i);
-
- message_id = g_value_get_uint (g_value_array_get_nth (message_struct, 0));
- timestamp = g_value_get_uint (g_value_array_get_nth (message_struct, 1));
- from_handle = g_value_get_uint (g_value_array_get_nth (message_struct, 2));
- message_type = g_value_get_uint (g_value_array_get_nth (message_struct, 3));
- message_flags = g_value_get_uint (g_value_array_get_nth (message_struct, 4));
- message_body = g_value_get_string (g_value_array_get_nth (message_struct, 5));
-
- DEBUG ("Message pending: %s", message_body);
-
- if (message_flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_NON_TEXT_CONTENT &&
- !tp_strdiff (message_body, "")) {
- DEBUG ("Empty message with NonTextContent, ignoring and acking.");
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+ GList *messages, *l;
- if (empty_non_text_content_ids == NULL) {
- empty_non_text_content_ids = g_array_new (FALSE, FALSE, sizeof (guint));
- }
+ g_assert (priv->channel != NULL);
- g_array_append_val (empty_non_text_content_ids, message_id);
- continue;
- }
+ messages = tp_text_channel_get_pending_messages (
+ TP_TEXT_CHANNEL (priv->channel));
- tp_chat_build_message (chat,
- TRUE,
- message_id,
- message_type,
- timestamp,
- from_handle,
- message_body,
- message_flags);
- }
+ for (l = messages; l != NULL; l = g_list_next (l)) {
+ TpMessage *message = l->data;
- if (empty_non_text_content_ids != NULL) {
- acknowledge_messages (chat, empty_non_text_content_ids);
- g_array_free (empty_non_text_content_ids, TRUE);
+ handle_incoming_message (self, message, FALSE);
}
- check_ready (chat);
+ g_list_free (messages);
}
static void
@@ -836,6 +876,7 @@ tp_chat_finalize (GObject *object)
g_queue_free (priv->messages_queue);
g_queue_free (priv->pending_messages_queue);
+ g_hash_table_destroy (priv->messages_being_sent);
G_OBJECT_CLASS (empathy_tp_chat_parent_class)->finalize (object);
}
@@ -854,6 +895,9 @@ check_almost_ready (EmpathyTpChat *chat)
if (!priv->got_password_flags)
return;
+ if (!priv->got_sms_channel)
+ return;
+
/* We need either the members (room) or the remote contact (private chat).
* If the chat is protected by a password we can't get these information so
* consider the chat as ready so it can be presented to the user. */
@@ -861,32 +905,22 @@ check_almost_ready (EmpathyTpChat *chat)
priv->remote_contact == NULL)
return;
- tp_cli_channel_type_text_connect_to_received (priv->channel,
- tp_chat_received_cb,
- NULL, NULL,
- G_OBJECT (chat), NULL);
- priv->listing_pending_messages = TRUE;
-
- /* TpChat will be ready once ListPendingMessages returned and all the messages
- * have been added to the pending messages queue. */
- tp_cli_channel_type_text_call_list_pending_messages (priv->channel, -1,
- FALSE,
- tp_chat_list_pending_messages_cb,
- NULL, NULL,
- G_OBJECT (chat));
-
- tp_cli_channel_type_text_connect_to_sent (priv->channel,
- tp_chat_sent_cb,
- NULL, NULL,
- G_OBJECT (chat), NULL);
- tp_cli_channel_type_text_connect_to_send_error (priv->channel,
- tp_chat_send_error_cb,
- NULL, NULL,
- G_OBJECT (chat), NULL);
- tp_cli_channel_interface_chat_state_connect_to_chat_state_changed (priv->channel,
- tp_chat_state_changed_cb,
- NULL, NULL,
- G_OBJECT (chat), NULL);
+ /* We use the default factory so this feature should have been prepared */
+ g_assert (tp_proxy_is_prepared (priv->channel,
+ TP_TEXT_CHANNEL_FEATURE_INCOMING_MESSAGES));
+
+ tp_g_signal_connect_object (priv->channel, "message-received",
+ G_CALLBACK (message_received_cb), chat, 0);
+
+ list_pending_messages (chat);
+
+ tp_g_signal_connect_object (priv->channel, "message-sent",
+ G_CALLBACK (message_sent_cb), chat, 0);
+
+ tp_g_signal_connect_object (priv->channel, "chat-state-changed",
+ G_CALLBACK (tp_chat_state_changed_cb), chat, 0);
+
+ check_ready (chat);
}
static void
@@ -1255,6 +1289,41 @@ got_password_flags_cb (TpChannel *proxy,
check_almost_ready (EMPATHY_TP_CHAT (self));
}
+static void
+sms_channel_changed_cb (TpChannel *channel,
+ gboolean sms_channel,
+ gpointer user_data,
+ GObject *chat)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+ priv->sms_channel = sms_channel;
+
+ g_object_notify (G_OBJECT (chat), "sms-channel");
+}
+
+static void
+get_sms_channel_cb (TpProxy *channel,
+ const GValue *value,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *chat)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (chat);
+
+ if (in_error != NULL) {
+ DEBUG ("Failed to get SMSChannel: %s", in_error->message);
+ return;
+ }
+
+ g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (value));
+
+ priv->sms_channel = g_value_get_boolean (value);
+ priv->got_sms_channel = TRUE;
+
+ check_almost_ready (EMPATHY_TP_CHAT (chat));
+}
+
static GObject *
tp_chat_constructor (GType type,
guint n_props,
@@ -1363,6 +1432,22 @@ tp_chat_constructor (GType type,
priv->got_password_flags = TRUE;
}
+ /* Check if the chat is for SMS */
+ if (tp_proxy_has_interface_by_id (priv->channel,
+ TP_IFACE_QUARK_CHANNEL_INTERFACE_SMS)) {
+ tp_cli_channel_interface_sms_connect_to_sms_channel_changed (
+ priv->channel,
+ sms_channel_changed_cb, chat, NULL, G_OBJECT (chat),
+ NULL);
+
+ tp_cli_dbus_properties_call_get (priv->channel, -1,
+ TP_IFACE_CHANNEL_INTERFACE_SMS, "SMSChannel",
+ get_sms_channel_cb, chat, NULL, G_OBJECT (chat));
+ } else {
+ /* if there's no SMS support, then we're not waiting for it */
+ priv->got_sms_channel = TRUE;
+ }
+
return chat;
}
@@ -1391,6 +1476,13 @@ tp_chat_get_property (GObject *object,
case PROP_PASSWORD_NEEDED:
g_value_set_boolean (value, empathy_tp_chat_password_needed (self));
break;
+ case PROP_SMS_CHANNEL:
+ g_value_set_boolean (value, priv->sms_channel);
+ break;
+ case PROP_N_MESSAGES_SENDING:
+ g_value_set_uint (value,
+ g_hash_table_size (priv->messages_being_sent));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
@@ -1472,6 +1564,22 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
FALSE,
G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_SMS_CHANNEL,
+ g_param_spec_boolean ("sms-channel",
+ "SMS Channel",
+ "TRUE if channel is for sending SMSes",
+ FALSE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class,
+ PROP_N_MESSAGES_SENDING,
+ g_param_spec_uint ("n-messages-sending",
+ "Num Messages Sending",
+ "The number of messages being sent",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE));
+
/* Signals */
signals[MESSAGE_RECEIVED] =
g_signal_new ("message-received",
@@ -1489,9 +1597,9 @@ empathy_tp_chat_class_init (EmpathyTpChatClass *klass)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- _empathy_marshal_VOID__STRING_UINT,
+ _empathy_marshal_VOID__STRING_UINT_STRING,
G_TYPE_NONE,
- 2, G_TYPE_STRING, G_TYPE_UINT);
+ 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
signals[CHAT_STATE_CHANGED] =
g_signal_new ("chat-state-changed",
@@ -1535,6 +1643,8 @@ empathy_tp_chat_init (EmpathyTpChat *chat)
chat->priv = priv;
priv->messages_queue = g_queue_new ();
priv->pending_messages_queue = g_queue_new ();
+ priv->messages_being_sent = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, NULL);
}
static void
@@ -1550,7 +1660,7 @@ empathy_tp_chat_new (TpAccount *account,
TpChannel *channel)
{
g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
- g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
+ g_return_val_if_fail (TP_IS_TEXT_CHANNEL (channel), NULL);
return g_object_new (EMPATHY_TYPE_TP_CHAT,
"account", account,
@@ -1639,27 +1749,24 @@ empathy_tp_chat_is_ready (EmpathyTpChat *chat)
void
empathy_tp_chat_send (EmpathyTpChat *chat,
- EmpathyMessage *message)
+ TpMessage *message)
{
EmpathyTpChatPriv *priv = GET_PRIV (chat);
- const gchar *message_body;
- TpChannelTextMessageType message_type;
+ gchar *message_body;
g_return_if_fail (EMPATHY_IS_TP_CHAT (chat));
- g_return_if_fail (EMPATHY_IS_MESSAGE (message));
+ g_return_if_fail (TP_IS_CLIENT_MESSAGE (message));
g_return_if_fail (priv->ready);
- message_body = empathy_message_get_body (message);
- message_type = empathy_message_get_tptype (message);
+ message_body = tp_message_to_text (message, NULL);
DEBUG ("Sending message: %s", message_body);
- tp_cli_channel_type_text_call_send (priv->channel, -1,
- message_type,
- message_body,
- tp_chat_send_cb,
- g_object_ref (message),
- (GDestroyNotify) g_object_unref,
- G_OBJECT (chat));
+
+ tp_text_channel_send_message_async (TP_TEXT_CHANNEL (priv->channel),
+ message, TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY,
+ message_send_cb, chat);
+
+ g_free (message_body);
}
void
@@ -1960,3 +2067,37 @@ empathy_tp_chat_is_invited (EmpathyTpChat *self,
return tp_channel_group_get_local_pending_info (priv->channel, self_handle,
inviter, NULL, NULL);
}
+
+const char *
+empathy_tp_chat_get_channel_path (EmpathyTpChat *self)
+{
+ EmpathyTpChatPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
+
+ priv = GET_PRIV (self);
+
+ return tp_proxy_get_object_path (priv->channel);
+}
+
+gboolean
+empathy_tp_chat_is_sms_channel (EmpathyTpChat *self)
+{
+ EmpathyTpChatPriv *priv;
+
+ g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), FALSE);
+
+ priv = GET_PRIV (self);
+
+ return priv->sms_channel;
+}
+
+EmpathyContact *
+empathy_tp_chat_get_self_contact (EmpathyTpChat *self)
+{
+ EmpathyTpChatPriv *priv = GET_PRIV (self);
+
+ g_return_val_if_fail (EMPATHY_IS_TP_CHAT (self), NULL);
+
+ return priv->user;
+}
diff --git a/libempathy/empathy-tp-chat.h b/libempathy/empathy-tp-chat.h
index 8858f4971..34651d13c 100644
--- a/libempathy/empathy-tp-chat.h
+++ b/libempathy/empathy-tp-chat.h
@@ -59,17 +59,24 @@ typedef struct {
GValue *value;
} EmpathyTpChatProperty;
+typedef enum {
+ EMPATHY_DELIVERY_STATUS_NONE,
+ EMPATHY_DELIVERY_STATUS_SENDING,
+ EMPATHY_DELIVERY_STATUS_ACCEPTED
+} EmpathyDeliveryStatus;
+
GType empathy_tp_chat_get_type (void) G_GNUC_CONST;
EmpathyTpChat *empathy_tp_chat_new (TpAccount *account,
TpChannel *channel);
const gchar * empathy_tp_chat_get_id (EmpathyTpChat *chat);
EmpathyContact *empathy_tp_chat_get_remote_contact (EmpathyTpChat *chat);
+EmpathyContact *empathy_tp_chat_get_self_contact (EmpathyTpChat *chat);
TpChannel * empathy_tp_chat_get_channel (EmpathyTpChat *chat);
TpAccount * empathy_tp_chat_get_account (EmpathyTpChat *chat);
TpConnection * empathy_tp_chat_get_connection (EmpathyTpChat *chat);
gboolean empathy_tp_chat_is_ready (EmpathyTpChat *chat);
void empathy_tp_chat_send (EmpathyTpChat *chat,
- EmpathyMessage *message);
+ TpMessage *message);
void empathy_tp_chat_set_state (EmpathyTpChat *chat,
TpChannelChatState state);
void empathy_tp_chat_set_property (EmpathyTpChat *chat,
@@ -106,6 +113,9 @@ void empathy_tp_chat_join (EmpathyTpChat *chat);
gboolean empathy_tp_chat_is_invited (EmpathyTpChat *chat,
TpHandle *inviter);
+const char * empathy_tp_chat_get_channel_path (EmpathyTpChat *chat);
+gboolean empathy_tp_chat_is_sms_channel (EmpathyTpChat *chat);
+
G_END_DECLS
#endif /* __EMPATHY_TP_CHAT_H__ */
diff --git a/libempathy/empathy-tp-file.c b/libempathy/empathy-tp-file.c
index d69b2153a..3bb2dd49c 100644
--- a/libempathy/empathy-tp-file.c
+++ b/libempathy/empathy-tp-file.c
@@ -79,7 +79,7 @@ struct _EmpathyTpFilePrivate {
/* transfer properties */
gboolean incoming;
- time_t start_time;
+ gint64 start_time;
GArray *socket_address;
guint port;
guint64 offset;
diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c
index bd150dac9..dfc2595c3 100644
--- a/libempathy/empathy-utils.c
+++ b/libempathy/empathy-utils.c
@@ -29,6 +29,7 @@
#include "config.h"
#include <string.h>
+#include <math.h>
#include <time.h>
#include <sys/types.h>
@@ -57,6 +58,8 @@
#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
#include "empathy-debug.h"
+#define TM "\342\204\242" /* trademark */
+
/* Translation between presence types and string */
static struct {
const gchar *name;
@@ -406,18 +409,33 @@ empathy_account_get_error_message (TpAccount *account,
{
const gchar *dbus_error;
const gchar *message;
- const GHashTable *details = NULL;
+ const GHashTable *details = NULL;
TpConnectionStatusReason reason;
+ guint skype_reason;
+ gboolean skype_reason_valid;
dbus_error = tp_account_get_detailed_error (account, &details);
- if (user_requested != NULL)
- {
- if (tp_asv_get_boolean (details, "user-requested", NULL))
- *user_requested = TRUE;
- else
- *user_requested = FALSE;
- }
+ if (user_requested != NULL)
+ {
+ if (tp_asv_get_boolean (details, "user-requested", NULL))
+ *user_requested = TRUE;
+ else
+ *user_requested = FALSE;
+ }
+
+ /* Skype certification requires us to return the precise error message.
+ * Check to see if skype-reason is defined, if it is, try to use that
+ * over the dbus error message */
+ skype_reason = tp_asv_get_uint32 (details, "skype-reason",
+ &skype_reason_valid);
+
+ if (skype_reason_valid)
+ {
+ message = empathy_skype_reason_to_string (skype_reason);
+ if (message != NULL)
+ return message;
+ }
message = empathy_dbus_error_name_get_default_message (dbus_error);
if (message != NULL)
@@ -551,6 +569,7 @@ empathy_protocol_name_to_display_name (const gchar *proto_name)
{ "facebook", N_("Facebook Chat"), TRUE },
{ "groupwise", "GroupWise", FALSE },
{ "sip", "SIP", FALSE },
+ { "skype", "Skype" TM, FALSE },
{ NULL, NULL }
};
@@ -793,3 +812,137 @@ empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert)
return NULL;
}
+
+gchar *
+empathy_format_currency (gint amount,
+ guint scale,
+ const gchar *currency)
+{
+#define MINUS "\342\210\222"
+#define EURO "\342\202\254"
+#define YEN "\302\245"
+#define POUND "\302\243"
+
+ /* localised representations of currency */
+ /* FIXME: check these, especially negatives and decimals */
+ static const struct {
+ const char *currency;
+ const char *positive;
+ const char *negative;
+ const char *decimal;
+ } currencies[] = {
+ /* sym positive negative decimal */
+ { "EUR", EURO "%s", MINUS EURO "%s", "." },
+ { "USD", "$%s", MINUS "$%s", "." },
+ { "JPY", YEN "%s" MINUS YEN "%s", "." },
+ { "GBP", POUND "%s", MINUS POUND "%s", "." },
+ { "PLN", "%s zl", MINUS "%s zl", "." },
+ { "BRL", "R$%s", MINUS "R$%s", "." },
+ { "SEK", "%s kr", MINUS "%s kr", "." },
+ { "DKK", "kr %s", "kr " MINUS "%s", "." },
+ { "HKD", "$%s", MINUS "$%s", "." },
+ { "CHF", "%s Fr.", MINUS "%s Fr.", "." },
+ { "NOK", "kr %s", "kr" MINUS "%s", "," },
+ { "CAD", "$%s", MINUS "$%s", "." },
+ { "TWD", "$%s", MINUS "$%s", "." },
+ { "AUD", "$%s", MINUS "$%s", "." },
+ };
+
+ const char *positive = "%s";
+ const char *negative = MINUS "%s";
+ const char *decimal = ".";
+ char *fmt_amount, *money;
+ guint i;
+
+ /* get the localised currency format */
+ for (i = 0; i < G_N_ELEMENTS (currencies); i++) {
+ if (!tp_strdiff (currency, currencies[i].currency)) {
+ positive = currencies[i].positive;
+ negative = currencies[i].negative;
+ decimal = currencies[i].decimal;
+ break;
+ }
+ }
+
+ /* format the amount using the scale */
+ if (scale == 0) {
+ /* no decimal point required */
+ fmt_amount = g_strdup_printf ("%d", amount);
+ } else {
+ /* don't use floating point arithmatic, it's noisy;
+ * we take the absolute values, because we want the minus
+ * sign to appear before the $ */
+ int divisor = pow (10, scale);
+ int dollars = abs (amount / divisor);
+ int cents = abs (amount % divisor);
+
+ fmt_amount = g_strdup_printf ("%d%s%0*d",
+ dollars, decimal, scale, cents);
+ }
+
+ money = g_strdup_printf (amount < 0 ? negative : positive, fmt_amount);
+ g_free (fmt_amount);
+
+ return money;
+}
+
+const gchar *
+empathy_skype_reason_to_string (guint skype_reason)
+{
+ switch (skype_reason)
+ {
+ case EMP_SKYPE_LOGOUTREASON_NONE:
+ return _("No Reason");
+ case EMP_SKYPE_LOGOUTREASON_LOGOUT_CALLED:
+ return _("Requested");
+ case EMP_SKYPE_LOGOUTREASON_HTTPS_PROXY_AUTH_FAILED:
+ return _("HTTPS proxy authentication failed");
+ case EMP_SKYPE_LOGOUTREASON_SOCKS_PROXY_AUTH_FAILED:
+ return _("SOCKS proxy authentication failed");
+ case EMP_SKYPE_LOGOUTREASON_P2P_CONNECT_FAILED:
+ return _("Peer-to-peer connection failed");
+ case EMP_SKYPE_LOGOUTREASON_SERVER_CONNECT_FAILED:
+ return _("Server connection failed");
+ case EMP_SKYPE_LOGOUTREASON_SERVER_OVERLOADED:
+ return _("Server overloaded");
+ case EMP_SKYPE_LOGOUTREASON_DB_IN_USE:
+ return _("Local database in use");
+ case EMP_SKYPE_LOGOUTREASON_INVALID_SKYPENAME:
+ return _("Invalid username");
+ case EMP_SKYPE_LOGOUTREASON_INVALID_EMAIL:
+ return _("Invalid email address");
+ case EMP_SKYPE_LOGOUTREASON_UNACCEPTABLE_PASSWORD:
+ return _("Unacceptable password");
+ case EMP_SKYPE_LOGOUTREASON_SKYPENAME_TAKEN:
+ return _("Username already taken");
+ case EMP_SKYPE_LOGOUTREASON_REJECTED_AS_UNDERAGE:
+ return _("Rejected as underage");
+ case EMP_SKYPE_LOGOUTREASON_NO_SUCH_IDENTITY:
+ return _("No such identity");
+ case EMP_SKYPE_LOGOUTREASON_INCORRECT_PASSWORD:
+ return _("Incorrect password");
+ case EMP_SKYPE_LOGOUTREASON_TOO_MANY_LOGIN_ATTEMPTS:
+ return _("Too many login attempts");
+ case EMP_SKYPE_LOGOUTREASON_PASSWORD_HAS_CHANGED:
+ return _("Password has changed");
+ case EMP_SKYPE_LOGOUTREASON_PERIODIC_UIC_UPDATE_FAILED:
+ /* FIXME: what now? */
+ return _("Periodic UIC update failed");
+ case EMP_SKYPE_LOGOUTREASON_DB_DISK_FULL:
+ return _("Cannot write to local database: disk full");
+ case EMP_SKYPE_LOGOUTREASON_DB_IO_ERROR:
+ return _("Cannot write to local database: input/output error");
+ case EMP_SKYPE_LOGOUTREASON_DB_CORRUPT:
+ return _("Local database corrupt");
+ case EMP_SKYPE_LOGOUTREASON_DB_FAILURE:
+ return _("Local database failure");
+ case EMP_SKYPE_LOGOUTREASON_INVALID_APP_ID:
+ return _("Invalid application ID");
+ case EMP_SKYPE_LOGOUTREASON_APP_ID_BLACKLISTED:
+ return _("Application ID blacklisted");
+ case EMP_SKYPE_LOGOUTREASON_UNSUPPORTED_VERSION:
+ return _("Unsupported Skype version (software upgrade required)");
+ }
+
+ g_return_val_if_reached (NULL);
+}
diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h
index 2ddfb4c53..f78007aeb 100644
--- a/libempathy/empathy-utils.h
+++ b/libempathy/empathy-utils.h
@@ -102,6 +102,39 @@ gboolean empathy_folks_persona_is_interesting (FolksPersona *persona);
gchar * empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert);
+gchar *empathy_format_currency (gint amount, guint scale, const gchar *currency);
+
+/* this enum is taken from SkypeKit */
+enum {
+ EMP_SKYPE_LOGOUTREASON_NONE = 0, /* No reason given */
+ EMP_SKYPE_LOGOUTREASON_LOGOUT_CALLED = 1, /* manual logout (or unknown reason from previous session) */
+ EMP_SKYPE_LOGOUTREASON_HTTPS_PROXY_AUTH_FAILED = 2, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_SOCKS_PROXY_AUTH_FAILED = 3, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_P2P_CONNECT_FAILED = 4, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_SERVER_CONNECT_FAILED = 5, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_SERVER_OVERLOADED = 6, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_DB_IN_USE = 7, /* sync errors at login/registration */
+ EMP_SKYPE_LOGOUTREASON_INVALID_SKYPENAME = 8, /* sync errors at registration */
+ EMP_SKYPE_LOGOUTREASON_INVALID_EMAIL = 9, /* sync errors at registration */
+ EMP_SKYPE_LOGOUTREASON_UNACCEPTABLE_PASSWORD = 10, /* sync errors at registration */
+ EMP_SKYPE_LOGOUTREASON_SKYPENAME_TAKEN = 11, /* sync errors at registration */
+ EMP_SKYPE_LOGOUTREASON_REJECTED_AS_UNDERAGE = 12, /* sync errors at registration */
+ EMP_SKYPE_LOGOUTREASON_NO_SUCH_IDENTITY = 13, /* sync errors at login */
+ EMP_SKYPE_LOGOUTREASON_INCORRECT_PASSWORD = 14, /* sync errors at login */
+ EMP_SKYPE_LOGOUTREASON_TOO_MANY_LOGIN_ATTEMPTS = 15, /* sync errors at login */
+ EMP_SKYPE_LOGOUTREASON_PASSWORD_HAS_CHANGED = 16, /* async errors (can happen anytime while logged in) */
+ EMP_SKYPE_LOGOUTREASON_PERIODIC_UIC_UPDATE_FAILED = 17, /* async errors (can happen anytime while logged in) */
+ EMP_SKYPE_LOGOUTREASON_DB_DISK_FULL = 18, /* async errors (can happen anytime while logged in) */
+ EMP_SKYPE_LOGOUTREASON_DB_IO_ERROR = 19, /* async errors (can happen anytime while logged in) */
+ EMP_SKYPE_LOGOUTREASON_DB_CORRUPT = 20, /* async errors (can happen anytime while logged in) */
+ EMP_SKYPE_LOGOUTREASON_DB_FAILURE = 21, /* deprecated (superceded by more detailed DB_* errors) */
+ EMP_SKYPE_LOGOUTREASON_INVALID_APP_ID = 22, /* platform sdk */
+ EMP_SKYPE_LOGOUTREASON_APP_ID_BLACKLISTED = 23, /* platform sdk */
+ EMP_SKYPE_LOGOUTREASON_UNSUPPORTED_VERSION = 24, /* forced upgrade/discontinuation */
+};
+
+const gchar *empathy_skype_reason_to_string (guint skype_reason);
+
G_END_DECLS
#endif /* __EMPATHY_UTILS_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4b5af18b9..3145f1965 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,6 +6,7 @@ data/org.gnome.Empathy.gschema.xml.in
data/empathy-accounts.desktop.in.in
libempathy/empathy-ft-handler.c
+libempathy/empathy-message.c
libempathy/empathy-tp-contact-list.c
libempathy/empathy-tp-file.c
libempathy/empathy-utils.c
@@ -19,11 +20,13 @@ libempathy-gtk/empathy-account-widget.c
[type: gettext/glade]libempathy-gtk/empathy-account-widget-icq.ui
libempathy-gtk/empathy-account-widget-irc.c
libempathy-gtk/empathy-account-widget-sip.c
+libempathy-gtk/empathy-account-widget-skype.c
[type: gettext/glade]libempathy-gtk/empathy-account-widget-irc.ui
[type: gettext/glade]libempathy-gtk/empathy-account-widget-jabber.ui
[type: gettext/glade]libempathy-gtk/empathy-account-widget-msn.ui
[type: gettext/glade]libempathy-gtk/empathy-account-widget-local-xmpp.ui
[type: gettext/glade]libempathy-gtk/empathy-account-widget-sip.ui
+[type: gettext/glade]libempathy-gtk/empathy-account-widget-skype.ui
[type: gettext/glade]libempathy-gtk/empathy-account-widget-yahoo.ui
libempathy-gtk/empathy-avatar-chooser.c
libempathy-gtk/empathy-avatar-image.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 246296172..3915f0404 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -259,7 +259,7 @@ dist_man_MANS = \
src-marshal.list: $(empathy_SOURCES) Makefile.am
$(AM_V_GEN)( cd $(srcdir) && \
sed -n -e 's/.*src_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \
- $(empathy_SOURCES) $(empathy_av_SOURCES) ) \
+ $(empathy_SOURCES) $(empathy_av_SOURCES) $(empathy_call_SOURCES) ) \
| sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp
@if cmp -s $@.tmp $@; then \
rm $@.tmp; \
diff --git a/src/empathy-account-assistant.c b/src/empathy-account-assistant.c
index e84fa650c..e3fffc2ed 100644
--- a/src/empathy-account-assistant.c
+++ b/src/empathy-account-assistant.c
@@ -34,6 +34,7 @@
#include <libempathy/empathy-utils.h>
#include <libempathy-gtk/empathy-account-widget.h>
+#include <libempathy-gtk/empathy-account-widget-skype.h>
#include <libempathy-gtk/empathy-protocol-chooser.h>
#include <libempathy-gtk/empathy-ui-utils.h>
@@ -400,6 +401,15 @@ account_assistant_protocol_changed_cb (GtkComboBox *chooser,
/* we are not ready yet */
return;
+ if (!tp_strdiff (proto->name, "skype"))
+ {
+ if (!empathy_account_widget_skype_show_eula (GTK_WINDOW (self)))
+ {
+ gtk_combo_box_set_active (chooser, 0);
+ return;
+ }
+ }
+
/* Create account */
if (is_gtalk)
name = "gtalk";
@@ -555,6 +565,11 @@ account_assistant_finish_enter_or_create_page (EmpathyAccountAssistant *self,
_("Enter the details for the new account"));
}
+ /* if someone clicked 'Back' this signal handler can be connected twice:
+ * disconnect any existing handlers */
+ g_signal_handlers_disconnect_by_func (priv->chooser,
+ account_assistant_protocol_changed_cb, self);
+
g_signal_connect (priv->chooser, "changed",
G_CALLBACK (account_assistant_protocol_changed_cb), self);
diff --git a/src/empathy-accounts-dialog.c b/src/empathy-accounts-dialog.c
index 3c690c071..6619442a8 100644
--- a/src/empathy-accounts-dialog.c
+++ b/src/empathy-accounts-dialog.c
@@ -46,6 +46,7 @@
#include <libempathy-gtk/empathy-account-widget.h>
#include <libempathy-gtk/empathy-account-widget-irc.h>
#include <libempathy-gtk/empathy-account-widget-sip.h>
+#include <libempathy-gtk/empathy-account-widget-skype.h>
#include <libempathy-gtk/empathy-cell-renderer-activatable.h>
#include <libempathy-gtk/empathy-images.h>
#include <libempathy-gtk/mx-gtk-light-switch.h>
@@ -257,6 +258,20 @@ accounts_dialog_enable_switch_flipped (MxGtkLightSwitch *sw,
if (account == NULL)
return;
+ if (state == TRUE &&
+ !tp_strdiff (tp_account_get_connection_manager (account), "psyke"))
+ {
+ /* psyke typically doesn't support more than one simultaneous connection
+ * unless you've compiled it to do so */
+ if (!empathy_accounts_dialog_skype_disable_other_accounts (account,
+ GTK_WINDOW (dialog)))
+ {
+ /* user chose not to proceed */
+ mx_gtk_light_switch_set_active (sw, FALSE);
+ return;
+ }
+ }
+
tp_account_set_enabled_async (account, state,
accounts_dialog_enable_account_cb, NULL);
}
@@ -740,6 +755,8 @@ accounts_dialog_protocol_changed_cb (GtkWidget *widget,
GtkTreeIter iter;
gboolean creating;
EmpathyAccountSettings *settings;
+ TpConnectionManager *cm;
+ TpConnectionManagerProtocol *proto;
gchar *account = NULL, *password = NULL;
/* The "changed" signal is fired during the initiation of the
@@ -758,6 +775,25 @@ accounts_dialog_protocol_changed_cb (GtkWidget *widget,
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
return;
+ cm = empathy_protocol_chooser_dup_selected (
+ EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol), &proto, NULL, NULL);
+
+ if (cm == NULL)
+ return;
+
+ g_object_unref (cm);
+
+ if (!tp_strdiff (proto->name, "skype"))
+ {
+ if (!empathy_account_widget_skype_show_eula (GTK_WINDOW (dialog)))
+ {
+ gtk_combo_box_set_active (
+ GTK_COMBO_BOX (priv->combobox_protocol), 0);
+ empathy_account_dialog_cancel (dialog);
+ return;
+ }
+ }
+
/* Save "account" and "password" parameters */
g_object_get (priv->setting_widget_object, "settings", &settings, NULL);
diff --git a/src/empathy-call-handler.c b/src/empathy-call-handler.c
index aff96bd6e..56d109838 100644
--- a/src/empathy-call-handler.c
+++ b/src/empathy-call-handler.c
@@ -189,13 +189,18 @@ static void
on_call_state_changed_cb (TpyCallChannel *call,
TpyCallState state,
TpyCallFlags flags,
- const GValueArray *call_state_reason,
+ const GValueArray *call_state_reason,
GHashTable *call_state_details,
EmpathyCallHandler *handler)
{
EmpathyCallHandlerPriv *priv = handler->priv;
+ gchar *dbus_reason;
+ guint actor, reason;
- g_signal_emit (handler, signals[STATE_CHANGED], 0, state);
+ tp_value_array_unpack ((GValueArray *) call_state_reason, 3,
+ &actor, &reason, &dbus_reason);
+
+ g_signal_emit (handler, signals[STATE_CHANGED], 0, state, dbus_reason);
if (state == TPY_CALL_STATE_ENDED)
{
@@ -503,8 +508,8 @@ empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
signals[STATE_CHANGED] =
g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
+ _src_marshal_VOID__UINT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
}
EmpathyCallHandler *
diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c
index 67a587013..0e1b55fdb 100644
--- a/src/empathy-call-window.c
+++ b/src/empathy-call-window.c
@@ -2105,14 +2105,37 @@ empathy_call_window_update_timer (gpointer user_data)
return TRUE;
}
-#if 0
+enum
+{
+ EMP_RESPONSE_BALANCE
+};
+
+static void
+on_error_infobar_response_cb (GtkInfoBar *info_bar,
+ gint response_id,
+ gpointer user_data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_CLOSE:
+ gtk_widget_destroy (GTK_WIDGET (info_bar));
+ break;
+ case EMP_RESPONSE_BALANCE:
+ empathy_url_show (GTK_WIDGET (info_bar),
+ g_object_get_data (G_OBJECT (info_bar), "uri"));
+ break;
+ }
+}
+
static void
display_error (EmpathyCallWindow *self,
- TpyCallChannel *call,
const gchar *img,
const gchar *title,
const gchar *desc,
- const gchar *details)
+ const gchar *details,
+ const gchar *button_text,
+ const gchar *uri,
+ gint button_response)
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
GtkWidget *info_bar;
@@ -2127,6 +2150,14 @@ display_error (EmpathyCallWindow *self,
info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
NULL);
+ if (button_text != NULL)
+ {
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ button_text, button_response);
+ g_object_set_data_full (G_OBJECT (info_bar),
+ "uri", g_strdup (uri), g_free);
+ }
+
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
@@ -2174,13 +2205,14 @@ display_error (EmpathyCallWindow *self,
}
g_signal_connect (info_bar, "response",
- G_CALLBACK (gtk_widget_destroy), NULL);
+ G_CALLBACK (on_error_infobar_response_cb), NULL);
gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
gtk_widget_show_all (info_bar);
}
+#if 0
static gchar *
media_stream_error_to_txt (EmpathyCallWindow *self,
TpyCallChannel *call,
@@ -2300,14 +2332,87 @@ empathy_call_window_video_stream_error (TpyCallChannel *call,
#endif
static void
+got_balance_props (TpProxy *proxy,
+ GHashTable *props,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (weak_object);
+ GValueArray *balance;
+ gchar *balance_str, *currency, *tmp;
+ const gchar *uri;
+ gint amount, scale;
+
+ if (error != NULL)
+ {
+ DEBUG ("Failed to get Balance: %s", error->message);
+ return;
+ }
+
+ balance = tp_asv_get_boxed (props, "AccountBalance",
+ TP_STRUCT_TYPE_CURRENCY_AMOUNT);
+ uri = tp_asv_get_string (props, "ManageCreditURI");
+ tp_value_array_unpack (balance, 3,
+ &amount,
+ &scale,
+ &currency);
+
+ if (amount == 0 &&
+ scale == G_MAXINT32 &&
+ tp_str_empty (currency))
+ {
+ /* unknown balance */
+ balance_str = g_strdup ("(--)");
+ }
+ else
+ {
+ char *money = empathy_format_currency (amount, scale, currency);
+
+ balance_str = g_strdup_printf ("%s %s",
+ currency, money);
+ g_free (money);
+ }
+
+ /* FIXME: don't hardcode Skype here */
+ display_error (self,
+ NULL,
+ _("Sorry, you don’t have enough Skype Credit for that call."),
+ tmp = g_strdup_printf (_("Your current balance is %s."),
+ balance_str),
+ NULL,
+ _("Buy Skype credit..."),
+ uri,
+ EMP_RESPONSE_BALANCE);
+ g_free (tmp);
+}
+
+static void
empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
TpyCallState state,
+ gchar *reason,
EmpathyCallWindow *self)
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
TpyCallChannel *call;
gboolean can_send_video;
+ if (state == TPY_CALL_STATE_ENDED &&
+ !tp_strdiff (reason, TP_ERROR_STR_INSUFFICIENT_BALANCE))
+ {
+ g_object_get (handler, "call-channel", &call, NULL);
+
+ tp_cli_dbus_properties_call_get_all (
+ tp_channel_borrow_connection (TP_CHANNEL (call)),
+ -1,
+ TP_IFACE_CONNECTION_INTERFACE_BALANCE,
+ got_balance_props,
+ NULL, NULL, G_OBJECT (self));
+
+ g_object_unref (call);
+ return;
+ }
+
if (state != TPY_CALL_STATE_ACCEPTED)
return;
diff --git a/src/empathy-chat-manager.c b/src/empathy-chat-manager.c
index cfa38ec84..8455001f5 100644
--- a/src/empathy-chat-manager.c
+++ b/src/empathy-chat-manager.c
@@ -119,18 +119,16 @@ process_tp_chat (EmpathyTpChat *tp_chat,
gint64 user_action_time)
{
EmpathyChat *chat = NULL;
- const gchar *id;
+ const char *channel_path;
tell_chatroom_manager_if_needed (account, tp_chat);
- id = empathy_tp_chat_get_id (tp_chat);
- if (!tp_str_empty (id))
- {
- chat = empathy_chat_window_find_chat (account, id);
- }
+ channel_path = empathy_tp_chat_get_channel_path (tp_chat);
+ chat = empathy_chat_window_find_chat_by_channel (channel_path);
if (chat != NULL)
{
+ DEBUG ("found chat %p for path %s", chat, channel_path);
empathy_chat_set_tp_chat (chat, tp_chat);
}
else
@@ -218,6 +216,16 @@ handle_channels (TpSimpleHandler *handler,
TpChannel *channel = l->data;
EmpathyTpChat *tp_chat;
+ if (tp_proxy_get_invalidated (channel) != NULL)
+ continue;
+
+ if (!TP_IS_TEXT_CHANNEL (channel))
+ {
+ DEBUG ("Channel %s doesn't implement Messages; can't handle it",
+ tp_proxy_get_object_path (channel));
+ continue;
+ }
+
tp_chat = empathy_tp_chat_new (account, channel);
if (empathy_tp_chat_is_ready (tp_chat))
diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c
index 4995dcdc6..d498d8fec 100644
--- a/src/empathy-chat-window.c
+++ b/src/empathy-chat-window.c
@@ -146,6 +146,8 @@ static const GtkTargetEntry drag_types_dest_file[] = {
static void chat_window_update (EmpathyChatWindow *window,
gboolean update_contact_menu);
+static EmpathyChat *empathy_chat_window_find_chat (TpAccount *account,
+ const gchar *id);
G_DEFINE_TYPE (EmpathyChatWindow, empathy_chat_window, G_TYPE_OBJECT);
@@ -292,6 +294,16 @@ chat_window_create_label (EmpathyChatWindow *window,
gtk_box_pack_start (GTK_BOX (hbox), event_box, TRUE, TRUE, 0);
if (is_tab_label) {
+ GtkWidget *sending_spinner;
+
+ sending_spinner = gtk_spinner_new ();
+
+ gtk_box_pack_start (GTK_BOX (hbox), sending_spinner,
+ FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT (chat),
+ "chat-window-tab-sending-spinner",
+ sending_spinner);
+
close_button = gtk_button_new ();
gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
g_object_set_data (G_OBJECT (chat), "chat-window-tab-close-button", close_button);
@@ -442,14 +454,14 @@ get_all_unread_messages (EmpathyChatWindowPriv *priv)
static gchar *
get_window_title_name (EmpathyChatWindowPriv *priv)
{
- const gchar *active_name;
+ gchar *active_name, *ret;
guint nb_chats;
guint current_unread_msgs;
nb_chats = g_list_length (priv->chats);
g_assert (nb_chats > 0);
- active_name = empathy_chat_get_name (priv->current_chat);
+ active_name = empathy_chat_dup_name (priv->current_chat);
current_unread_msgs = empathy_chat_get_nb_unread_messages (
priv->current_chat);
@@ -457,9 +469,9 @@ get_window_title_name (EmpathyChatWindowPriv *priv)
if (nb_chats == 1) {
/* only one tab */
if (current_unread_msgs == 0)
- return g_strdup (active_name);
+ ret = g_strdup (active_name);
else
- return g_strdup_printf (ngettext (
+ ret = g_strdup_printf (ngettext (
"%s (%d unread)",
"%s (%d unread)", current_unread_msgs),
active_name, current_unread_msgs);
@@ -471,7 +483,7 @@ get_window_title_name (EmpathyChatWindowPriv *priv)
if (all_unread_msgs == 0) {
/* no unread message */
- return g_strdup_printf (ngettext (
+ ret = g_strdup_printf (ngettext (
"%s (and %u other)",
"%s (and %u others)", nb_others),
active_name, nb_others);
@@ -479,7 +491,7 @@ get_window_title_name (EmpathyChatWindowPriv *priv)
else if (all_unread_msgs == current_unread_msgs) {
/* unread messages are in the current tab */
- return g_strdup_printf (ngettext (
+ ret = g_strdup_printf (ngettext (
"%s (%d unread)",
"%s (%d unread)", current_unread_msgs),
active_name, current_unread_msgs);
@@ -487,7 +499,7 @@ get_window_title_name (EmpathyChatWindowPriv *priv)
else if (current_unread_msgs == 0) {
/* unread messages are in other tabs */
- return g_strdup_printf (ngettext (
+ ret = g_strdup_printf (ngettext (
"%s (%d unread from others)",
"%s (%d unread from others)",
all_unread_msgs),
@@ -496,13 +508,17 @@ get_window_title_name (EmpathyChatWindowPriv *priv)
else {
/* unread messages are in all the tabs */
- return g_strdup_printf (ngettext (
+ ret = g_strdup_printf (ngettext (
"%s (%d unread from all)",
"%s (%d unread from all)",
all_unread_msgs),
active_name, all_unread_msgs);
}
}
+
+ g_free (active_name);
+
+ return ret;
}
static void
@@ -630,7 +646,7 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
EmpathyChatWindow *window;
EmpathyChatWindowPriv *priv;
EmpathyContact *remote_contact;
- const gchar *name;
+ gchar *name;
const gchar *id;
TpAccount *account;
const gchar *subject;
@@ -641,6 +657,8 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
const gchar *icon_name;
GtkWidget *tab_image;
GtkWidget *menu_image;
+ GtkWidget *sending_spinner;
+ guint nb_sending;
window = chat_window_find_chat (chat);
if (!window) {
@@ -649,7 +667,7 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
priv = GET_PRIV (window);
/* Get information */
- name = empathy_chat_get_name (chat);
+ name = empathy_chat_dup_name (chat);
account = empathy_chat_get_account (chat);
subject = empathy_chat_get_subject (chat);
remote_contact = empathy_chat_get_remote_contact (chat);
@@ -668,6 +686,9 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
else if (g_list_find (priv->chats_composing, chat)) {
icon_name = EMPATHY_IMAGE_TYPING;
}
+ else if (empathy_chat_is_sms_channel (chat)) {
+ icon_name = EMPATHY_IMAGE_SMS;
+ }
else if (remote_contact) {
icon_name = empathy_icon_name_for_contact (remote_contact);
} else {
@@ -686,6 +707,16 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
gtk_widget_hide (menu_image);
}
+ /* Update the sending spinner */
+ nb_sending = empathy_chat_get_n_messages_sending (chat);
+ sending_spinner = g_object_get_data (G_OBJECT (chat),
+ "chat-window-tab-sending-spinner");
+
+ g_object_set (sending_spinner,
+ "active", nb_sending > 0,
+ "visible", nb_sending > 0,
+ NULL);
+
/* Update tab tooltip */
tooltip = g_string_new (NULL);
@@ -696,11 +727,24 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
id = name;
}
+ if (empathy_chat_is_sms_channel (chat)) {
+ append_markup_printf (tooltip, "%s ", _("SMS:"));
+ }
+
append_markup_printf (tooltip,
"<b>%s</b><small> (%s)</small>",
id,
tp_account_get_display_name (account));
+ if (nb_sending > 0) {
+ append_markup_printf (tooltip, "\n");
+ append_markup_printf (tooltip,
+ ngettext ("Sending %d message",
+ "Sending %d messages",
+ nb_sending),
+ nb_sending);
+ }
+
if (!EMP_STR_EMPTY (status)) {
append_markup_printf (tooltip, "\n<i>%s</i>", status);
}
@@ -731,6 +775,8 @@ chat_window_update_chat_tab_full (EmpathyChat *chat,
if (priv->current_chat == chat) {
chat_window_update (window, update_contact_menu);
}
+
+ g_free (name);
}
static void
@@ -858,21 +904,24 @@ chat_window_favorite_toggled_cb (GtkToggleAction *toggle_action,
EmpathyChatWindowPriv *priv = GET_PRIV (window);
gboolean active;
TpAccount *account;
+ gchar *name;
const gchar *room;
EmpathyChatroom *chatroom;
active = gtk_toggle_action_get_active (toggle_action);
account = empathy_chat_get_account (priv->current_chat);
room = empathy_chat_get_id (priv->current_chat);
+ name = empathy_chat_dup_name (priv->current_chat);
chatroom = empathy_chatroom_manager_ensure_chatroom (
priv->chatroom_manager,
account,
room,
- empathy_chat_get_name (priv->current_chat));
+ name);
empathy_chatroom_set_favorite (chatroom, active);
g_object_unref (chatroom);
+ g_free (name);
}
static void
@@ -882,21 +931,24 @@ chat_window_always_urgent_toggled_cb (GtkToggleAction *toggle_action,
EmpathyChatWindowPriv *priv = GET_PRIV (window);
gboolean active;
TpAccount *account;
+ gchar *name;
const gchar *room;
EmpathyChatroom *chatroom;
active = gtk_toggle_action_get_active (toggle_action);
account = empathy_chat_get_account (priv->current_chat);
room = empathy_chat_get_id (priv->current_chat);
+ name = empathy_chat_dup_name (priv->current_chat);
chatroom = empathy_chatroom_manager_ensure_chatroom (
priv->chatroom_manager,
account,
room,
- empathy_chat_get_name (priv->current_chat));
+ name);
empathy_chatroom_set_always_urgent (chatroom, active);
g_object_unref (chatroom);
+ g_free (name);
}
static void
@@ -1359,18 +1411,20 @@ chat_window_show_or_update_notification (EmpathyChatWindow *window,
static void
chat_window_set_highlight_room_tab_label (EmpathyChat *chat)
{
- gchar *markup;
+ gchar *markup, *name;
GtkWidget *widget;
if (!empathy_chat_is_room (chat))
return;
+ name = empathy_chat_dup_name (chat);
markup = g_markup_printf_escaped (
"<span color=\"red\" weight=\"bold\">%s</span>",
- empathy_chat_get_name (chat));
+ name);
widget = g_object_get_data (G_OBJECT (chat), "chat-window-tab-label");
gtk_label_set_markup (GTK_LABEL (widget), markup);
+ g_free (name);
g_free (markup);
}
@@ -2214,6 +2268,12 @@ empathy_chat_window_add_chat (EmpathyChatWindow *window,
g_signal_connect (chat, "notify::remote-contact",
G_CALLBACK (chat_window_chat_notify_cb),
NULL);
+ g_signal_connect (chat, "notify::sms-channel",
+ G_CALLBACK (chat_window_chat_notify_cb),
+ NULL);
+ g_signal_connect (chat, "notify::n-messages-sending",
+ G_CALLBACK (chat_window_chat_notify_cb),
+ NULL);
chat_window_chat_notify_cb (chat);
gtk_notebook_append_page_menu (GTK_NOTEBOOK (priv->notebook), child, label, popup_label);
@@ -2330,7 +2390,7 @@ empathy_chat_window_has_focus (EmpathyChatWindow *window)
return has_focus;
}
-EmpathyChat *
+static EmpathyChat *
empathy_chat_window_find_chat (TpAccount *account,
const gchar *id)
{
@@ -2361,6 +2421,39 @@ empathy_chat_window_find_chat (TpAccount *account,
return NULL;
}
+EmpathyChat *
+empathy_chat_window_find_chat_by_channel (const char *channel_path)
+{
+ GList *l;
+
+ g_return_val_if_fail (!EMP_STR_EMPTY (channel_path), NULL);
+
+ for (l = chat_windows; l; l = l->next) {
+ EmpathyChatWindowPriv *priv;
+ EmpathyChatWindow *window;
+ GList *ll;
+
+ window = l->data;
+ priv = GET_PRIV (window);
+
+ for (ll = priv->chats; ll; ll = ll->next) {
+ EmpathyChat *chat;
+ EmpathyTpChat *tp_chat;
+ const char *path;
+
+ chat = ll->data;
+ tp_chat = empathy_chat_get_tp_chat (chat);
+ path = empathy_tp_chat_get_channel_path (tp_chat);
+
+ if (!tp_strdiff (channel_path, path)) {
+ return chat;
+ }
+ }
+ }
+
+ return NULL;
+}
+
void
empathy_chat_window_present_chat (EmpathyChat *chat,
gint64 timestamp)
diff --git a/src/empathy-chat-window.h b/src/empathy-chat-window.h
index 4cbd2094a..33caec70c 100644
--- a/src/empathy-chat-window.h
+++ b/src/empathy-chat-window.h
@@ -70,8 +70,7 @@ void empathy_chat_window_move_chat (EmpathyChatWindow *old_wi
void empathy_chat_window_switch_to_chat (EmpathyChatWindow *window,
EmpathyChat *chat);
gboolean empathy_chat_window_has_focus (EmpathyChatWindow *window);
-EmpathyChat * empathy_chat_window_find_chat (TpAccount *account,
- const gchar *id);
+EmpathyChat * empathy_chat_window_find_chat_by_channel (const char *path);
void empathy_chat_window_present_chat (EmpathyChat *chat,
gint64 timestamp);
diff --git a/src/empathy-event-manager.c b/src/empathy-event-manager.c
index 041d22a18..e2ce0725f 100644
--- a/src/empathy-event-manager.c
+++ b/src/empathy-event-manager.c
@@ -1037,7 +1037,7 @@ approve_channels (TpSimpleApprover *approver,
channel_type = tp_channel_get_channel_type_id (channel);
- if (channel_type == TP_IFACE_QUARK_CHANNEL_TYPE_TEXT)
+ if (TP_IS_TEXT_CHANNEL (channel))
{
EmpathyTpChat *tp_chat;
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index c93b25b9d..e81218f2c 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -144,6 +144,10 @@ struct _EmpathyMainWindowPriv {
GtkWidget *edit_context;
GtkWidget *edit_context_separator;
+ GtkActionGroup *balance_action_group;
+ GtkAction *view_balance_show_in_roster;
+ GtkWidget *balance_vbox;
+
guint size_timeout_id;
/* reffed TpAccount* => visible GtkInfoBar* */
@@ -907,6 +911,295 @@ main_window_update_status (EmpathyMainWindow *window)
}
}
+static char *
+main_window_account_to_action_name (TpAccount *account)
+{
+ char *r;
+
+ /* action names can't have '/' in them, replace it with '.' */
+ r = g_strdup (tp_account_get_path_suffix (account));
+ r = g_strdelimit (r, "/", '.');
+
+ return r;
+}
+
+static void
+main_window_balance_activate_cb (GtkAction *action,
+ EmpathyMainWindow *window)
+{
+ const char *uri;
+
+ uri = g_object_get_data (G_OBJECT (action), "manage-credit-uri");
+
+ if (!tp_str_empty (uri)) {
+ DEBUG ("Top-up credit URI: %s", uri);
+ empathy_url_show (GTK_WIDGET (window), uri);
+ } else {
+ DEBUG ("unknown protocol for top-up");
+ }
+}
+
+static void
+main_window_balance_update_balance (GtkAction *action,
+ GValueArray *balance)
+{
+ TpAccount *account = g_object_get_data (G_OBJECT (action), "account");
+ GtkWidget *label;
+ int amount = 0;
+ guint scale = G_MAXINT32;
+ const char *currency = "";
+ char *money, *str;
+
+ if (balance != NULL)
+ tp_value_array_unpack (balance, 3,
+ &amount,
+ &scale,
+ &currency);
+
+ if (amount == 0 &&
+ scale == G_MAXINT32 &&
+ tp_str_empty (currency)) {
+ /* unknown balance */
+ money = g_strdup ("--");
+ } else {
+ char *tmp = empathy_format_currency (amount, scale, currency);
+
+ money = g_strdup_printf ("%s %s", currency, tmp);
+ g_free (tmp);
+ }
+
+ /* Translators: this string will be something like:
+ * Top up Skype ($1.23)..." */
+ str = g_strdup_printf (_("Top up %s (%s)..."),
+ tp_account_get_display_name (account),
+ money);
+
+ gtk_action_set_label (action, str);
+ g_free (str);
+
+ /* update the money label in the roster */
+ label = g_object_get_data (G_OBJECT (action), "money-label");
+
+ gtk_label_set_text (GTK_LABEL (label), money);
+ g_free (money);
+}
+
+static void
+main_window_setup_balance_got_balance_props (TpProxy *conn,
+ GHashTable *props,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *action)
+{
+ GValueArray *balance = NULL;
+ const char *uri;
+
+ if (in_error != NULL) {
+ DEBUG ("Failed to get account balance properties: %s",
+ in_error->message);
+ goto finally;
+ }
+
+ balance = tp_asv_get_boxed (props, "AccountBalance",
+ TP_STRUCT_TYPE_CURRENCY_AMOUNT);
+ uri = tp_asv_get_string (props, "ManageCreditURI");
+
+ g_object_set_data_full (action, "manage-credit-uri",
+ g_strdup (uri), g_free);
+ gtk_action_set_sensitive (GTK_ACTION (action), !tp_str_empty (uri));
+
+finally:
+ main_window_balance_update_balance (GTK_ACTION (action), balance);
+}
+
+static void
+main_window_balance_changed_cb (TpConnection *conn,
+ const GValueArray *balance,
+ gpointer user_data,
+ GObject *action)
+{
+ main_window_balance_update_balance (GTK_ACTION (action),
+ (GValueArray *) balance);
+}
+
+static GtkAction *
+main_window_setup_balance_create_action (EmpathyMainWindow *window,
+ TpAccount *account)
+{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ GtkAction *action;
+ char *name, *ui;
+ guint merge_id;
+ GError *error = NULL;
+
+ /* create the action group if required */
+ if (priv->balance_action_group == NULL) {
+ priv->balance_action_group =
+ gtk_action_group_new ("balance-action-group");
+
+ gtk_ui_manager_insert_action_group (priv->ui_manager,
+ priv->balance_action_group, -1);
+ }
+
+ /* create the action */
+ name = main_window_account_to_action_name (account);
+ action = gtk_action_new (name,
+ tp_account_get_display_name (account),
+ _("Top up account credit"),
+ NULL);
+ g_object_bind_property (account, "icon-name", action, "icon-name",
+ G_BINDING_SYNC_CREATE);
+
+ g_object_set_data (G_OBJECT (action), "account", account);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (main_window_balance_activate_cb), window);
+
+ gtk_action_group_add_action (priv->balance_action_group, action);
+ g_object_unref (action);
+
+ ui = g_strdup_printf (
+ "<ui>"
+ " <menubar name='menubar'>"
+ " <menu action='view'>"
+ " <placeholder name='view_balance_placeholder'>"
+ " <menuitem action='%s'/>"
+ " </placeholder>"
+ " </menu>"
+ " </menubar>"
+ "</ui>",
+ name);
+
+ merge_id = gtk_ui_manager_add_ui_from_string (priv->ui_manager,
+ ui, -1, &error);
+ if (error != NULL) {
+ DEBUG ("Failed to add balance UI for %s: %s",
+ tp_account_get_display_name (account),
+ error->message);
+ g_error_free (error);
+ }
+
+ g_object_set_data (G_OBJECT (action),
+ "merge-id", GUINT_TO_POINTER (merge_id));
+
+ g_free (name);
+ g_free (ui);
+
+ return action;
+}
+
+static GtkWidget *
+main_window_setup_balance_create_widget (EmpathyMainWindow *window,
+ GtkAction *action)
+{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ TpAccount *account;
+ GtkWidget *hbox, *image, *label, *button;
+
+ account = g_object_get_data (G_OBJECT (action), "account");
+ g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+
+ /* protocol icon */
+ image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
+ g_object_bind_property (action, "icon-name", image, "icon-name",
+ G_BINDING_SYNC_CREATE);
+
+ /* account name label */
+ label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ g_object_bind_property (account, "display-name", label, "label",
+ G_BINDING_SYNC_CREATE);
+
+ /* balance label */
+ label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ g_object_set_data (G_OBJECT (action), "money-label", label);
+
+ /* top up button */
+ button = gtk_button_new_with_label (_("Top Up..."));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_action_activate), action);
+
+ gtk_box_pack_start (GTK_BOX (priv->balance_vbox), hbox, FALSE, TRUE, 0);
+ gtk_widget_show_all (hbox);
+
+ /* tie the lifetime of the widget to the lifetime of the action */
+ g_object_weak_ref (G_OBJECT (action),
+ (GWeakNotify) gtk_widget_destroy, hbox);
+
+ return hbox;
+}
+
+static void
+main_window_setup_balance_conn_ready (GObject *conn,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyMainWindow *window = user_data;
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ TpAccount *account = g_object_get_data (conn, "account");
+ GtkAction *action;
+ GtkWidget *widget;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (conn, result, &error)) {
+ DEBUG ("Failed to prepare connection: %s", error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ if (!tp_proxy_has_interface_by_id (conn,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_BALANCE)) {
+ return;
+ }
+
+ DEBUG ("Setting up balance for acct: %s",
+ tp_account_get_display_name (account));
+
+ /* create the action */
+ action = main_window_setup_balance_create_action (window, account);
+
+ if (action == NULL)
+ return;
+
+ gtk_action_set_visible (priv->view_balance_show_in_roster, TRUE);
+
+ /* create the display widget */
+ widget = main_window_setup_balance_create_widget (window, action);
+
+ /* request the current balance and monitor for any changes */
+ tp_cli_dbus_properties_call_get_all (conn, -1,
+ TP_IFACE_CONNECTION_INTERFACE_BALANCE,
+ main_window_setup_balance_got_balance_props,
+ window, NULL, G_OBJECT (action));
+
+ tp_cli_connection_interface_balance_connect_to_balance_changed (
+ TP_CONNECTION (conn), main_window_balance_changed_cb,
+ window, NULL, G_OBJECT (action), NULL);
+}
+
+static void
+main_window_setup_balance (EmpathyMainWindow *window,
+ TpAccount *account)
+{
+ TpConnection *conn = tp_account_get_connection (account);
+
+ if (conn == NULL)
+ return;
+
+ /* need to prepare the connection:
+ * store the account on the connection */
+ g_object_set_data (G_OBJECT (conn), "account", account);
+ tp_proxy_prepare_async (conn, NULL,
+ main_window_setup_balance_conn_ready, window);
+}
+
static void
main_window_connection_changed_cb (TpAccount *account,
guint old_status,
@@ -916,6 +1209,8 @@ main_window_connection_changed_cb (TpAccount *account,
GHashTable *details,
EmpathyMainWindow *window)
{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+
main_window_update_status (window);
if (current == TP_CONNECTION_STATUS_DISCONNECTED &&
@@ -926,6 +1221,44 @@ main_window_connection_changed_cb (TpAccount *account,
if (current == TP_CONNECTION_STATUS_DISCONNECTED) {
empathy_sound_play (GTK_WIDGET (window),
EMPATHY_SOUND_ACCOUNT_DISCONNECTED);
+
+ /* remove balance action if required */
+ if (priv->balance_action_group != NULL) {
+ GtkAction *action;
+ char *name;
+ GList *a;
+
+ name = main_window_account_to_action_name (account);
+
+ action = gtk_action_group_get_action (
+ priv->balance_action_group, name);
+
+ if (action != NULL) {
+ guint merge_id;
+
+ DEBUG ("Removing action");
+
+ merge_id = GPOINTER_TO_UINT (g_object_get_data (
+ G_OBJECT (action),
+ "merge-id"));
+
+ gtk_ui_manager_remove_ui (priv->ui_manager,
+ merge_id);
+ gtk_action_group_remove_action (
+ priv->balance_action_group, action);
+ }
+
+ g_free (name);
+
+ a = gtk_action_group_list_actions (
+ priv->balance_action_group);
+
+ gtk_action_set_visible (
+ priv->view_balance_show_in_roster,
+ g_list_length (a) > 0);
+
+ g_list_free (a);
+ }
}
if (current == TP_CONNECTION_STATUS_CONNECTED) {
@@ -934,6 +1267,7 @@ main_window_connection_changed_cb (TpAccount *account,
/* Account connected without error, remove error message if any */
main_window_remove_error (window, account);
+ main_window_setup_balance (window, account);
}
}
@@ -1778,6 +2112,8 @@ account_manager_prepared_cb (GObject *source_object,
window);
g_hash_table_insert (priv->status_changed_handlers,
account, GUINT_TO_POINTER (handler_id));
+
+ main_window_setup_balance (window, account);
}
g_signal_connect (manager, "account-validity-changed",
@@ -1873,6 +2209,7 @@ empathy_main_window_init (EmpathyMainWindow *window)
filename = empathy_file_lookup ("empathy-main-window.ui", "src");
gui = empathy_builder_get_file (filename,
"main_vbox", &priv->main_vbox,
+ "balance_vbox", &priv->balance_vbox,
"errors_vbox", &priv->errors_vbox,
"auth_vbox", &priv->auth_vbox,
"ui_manager", &priv->ui_manager,
@@ -1890,6 +2227,7 @@ empathy_main_window_init (EmpathyMainWindow *window)
"notebook", &priv->notebook,
"no_entry_label", &priv->no_entry_label,
"roster_scrolledwindow", &sw,
+ "view_balance_show_in_roster", &priv->view_balance_show_in_roster,
NULL);
g_free (filename);
@@ -2047,6 +2385,14 @@ empathy_main_window_init (EmpathyMainWindow *window)
/* Set window size. */
empathy_geometry_bind (GTK_WINDOW (window), GEOMETRY_NAME);
+ /* bind view_balance_show_in_roster */
+ g_settings_bind (priv->gsettings_ui, "show-balance-in-roster",
+ priv->view_balance_show_in_roster, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_object_bind_property (priv->view_balance_show_in_roster, "active",
+ priv->balance_vbox, "visible",
+ G_BINDING_SYNC_CREATE);
+
/* Enable event handling */
priv->call_observer = empathy_call_observer_dup_singleton ();
priv->event_manager = empathy_event_manager_dup_singleton ();
diff --git a/src/empathy-main-window.ui b/src/empathy-main-window.ui
index a538e0120..9d6474ba5 100644
--- a/src/empathy-main-window.ui
+++ b/src/empathy-main-window.ui
@@ -69,6 +69,13 @@
</object>
</child>
<child>
+ <object class="GtkToggleAction" id="view_balance_show_in_roster">
+ <property name="name">view_balance_show_in_roster</property>
+ <property name="label" translatable="yes">Credit Balance</property>
+ <property name="visible">False</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="view_show_map">
<property name="name">view_show_map</property>
<property name="label" translatable="yes">Contacts on a _Map</property>
@@ -256,6 +263,9 @@
<menuitem action="view_show_offline"/>
<menuitem action="view_show_protocols"/>
<separator/>
+ <menuitem action="view_balance_show_in_roster"/>
+ <placeholder name="view_balance_placeholder"/>
+ <separator/>
<menuitem action="view_sort_by_name"/>
<menuitem action="view_sort_by_status"/>
<separator/>
@@ -305,7 +315,8 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="errors_vbox">
+ <object class="GtkVBox" id="balance_vbox">
+ <property name="spacing">3</property>
<child>
<placeholder/>
</child>
@@ -317,7 +328,7 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="auth_vbox">
+ <object class="GtkVBox" id="errors_vbox">
<child>
<placeholder/>
</child>
@@ -329,6 +340,18 @@
</packing>
</child>
<child>
+ <object class="GtkVBox" id="auth_vbox">
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -360,7 +383,7 @@
</child>
</object>
<packing>
- <property name="position">4</property>
+ <property name="position">5</property>
</packing>
</child>
</object>
diff --git a/src/empathy-map-view.c b/src/empathy-map-view.c
index 31fa997c2..321fbce53 100644
--- a/src/empathy-map-view.c
+++ b/src/empathy-map-view.c
@@ -231,7 +231,7 @@ map_view_contacts_update_label (ChamplainMarker *marker)
gchar *date;
gchar *label;
GValue *gtime;
- time_t loctime;
+ gint64 loctime;
GHashTable *location;
EmpathyContact *contact;
@@ -242,18 +242,24 @@ map_view_contacts_update_label (ChamplainMarker *marker)
if (gtime != NULL)
{
- time_t now;
+ GDateTime *now, *d;
+ GTimeSpan delta;
loctime = g_value_get_int64 (gtime);
date = empathy_time_to_string_relative (loctime);
label = g_strconcat ("<b>", name, "</b>\n<small>", date, "</small>", NULL);
g_free (date);
- now = time (NULL);
+ now = g_date_time_new_now_utc ();
+ d = g_date_time_new_from_unix_utc (loctime);
+ delta = g_date_time_difference (now, d);
/* if location is older than a week */
- if (now - loctime > (60 * 60 * 24 * 7))
- clutter_actor_set_opacity (CLUTTER_ACTOR (marker), 0.75 * 255);
+ if (delta > G_TIME_SPAN_DAY * 7)
+ clutter_actor_set_opacity (marker, 0.75 * 255);
+
+ g_date_time_unref (now);
+ g_date_time_unref (d);
}
else
{
diff --git a/telepathy-yell b/telepathy-yell
-Subproject 47367965cd8f9ed9b72e8660f3f6887546e5992
+Subproject 179a352f0f8a248d54afe0fca93d1a78ccd8d6a