[gnome-shell-extensions] new gajim extension. Requires gajim-nightly >= 20110326
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell-extensions] new gajim extension. Requires gajim-nightly >= 20110326
- Date: Fri, 1 Apr 2011 16:40:02 +0000 (UTC)
commit 6edb1c7c6039e0fb3ea06676823031cb489cbe45
Author: Philippe Normand <pnormand igalia com>
Date: Sat Jan 29 16:53:24 2011 +0100
new gajim extension. Requires gajim-nightly >= 20110326
https://bugzilla.gnome.org/show_bug.cgi?id=645760
configure.ac | 6 +-
extensions/Makefile.am | 2 +-
extensions/gajim/Makefile.am | 3 +
extensions/gajim/extension.js | 313 +++++++++++++++++++++++++++++++++++++
extensions/gajim/metadata.json.in | 8 +
po/POTFILES.in | 9 +-
6 files changed, 333 insertions(+), 8 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 301c269..3327581 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,8 +21,7 @@ GLIB_GSETTINGS
ADDITIONAL_PACKAGES=
dnl keep this in sync with extensions/Makefile.am
-ALL_EXTENSIONS="example alternate-tab xrandr-indicator windowsNavigator auto-move-windows
-dock user-theme alternative-status-menu"
+ALL_EXTENSIONS="example alternate-tab xrandr-indicator windowsNavigator auto-move-windows dock user-theme alternative-status-menu gajim"
DEFAULT_EXTENSIONS="alternate-tab windowsNavigator dock alternative-status-menu"
AC_ARG_ENABLE([extensions],
[AS_HELP_STRING([--enable-extensions],[Space separated list of extensions to enable.
@@ -43,7 +42,7 @@ for e in $enable_extensions; do
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
ADDITIONAL_PACKAGES="$ADDITIONAL_PAGKAGES gnome-desktop-3.0 >= 2.91.6"
;;
- alternate-tab|example|windowsNavigator|auto-move-windows|dock|user-theme|alternative-status-menu)
+ alternate-tab|example|windowsNavigator|auto-move-windows|dock|user-theme|alternative-status-menu|gajim)
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
;;
*)
@@ -68,6 +67,7 @@ AC_CONFIG_FILES([
extensions/dock/Makefile
extensions/example/Makefile
extensions/windowsNavigator/Makefile
+ extensions/gajim/Makefile
extensions/xrandr-indicator/Makefile
extensions/user-theme/Makefile
extensions/Makefile
diff --git a/extensions/Makefile.am b/extensions/Makefile.am
index 8ef71ba..f910a10 100644
--- a/extensions/Makefile.am
+++ b/extensions/Makefile.am
@@ -1,3 +1,3 @@
-DIST_SUBDIRS = example alternate-tab xrandr-indicator windowsNavigator auto-move-windows dock user-theme alternative-status-menu
+DIST_SUBDIRS = example alternate-tab xrandr-indicator windowsNavigator auto-move-windows dock user-theme alternative-status-menu gajim
SUBDIRS = $(ENABLED_EXTENSIONS)
diff --git a/extensions/gajim/Makefile.am b/extensions/gajim/Makefile.am
new file mode 100644
index 0000000..f43e339
--- /dev/null
+++ b/extensions/gajim/Makefile.am
@@ -0,0 +1,3 @@
+EXTENSION_ID = gajim
+
+include ../../extension.mk
diff --git a/extensions/gajim/extension.js b/extensions/gajim/extension.js
new file mode 100644
index 0000000..9f97e9a
--- /dev/null
+++ b/extensions/gajim/extension.js
@@ -0,0 +1,313 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const DBus = imports.dbus;
+const Gettext = imports.gettext.domain('gnome-shell');
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Signals = imports.signals;
+const St = imports.gi.St;
+const Tp = imports.gi.TelepathyGLib;
+
+const Main = imports.ui.main;
+const Mainloop = imports.mainloop;
+const MessageTray = imports.ui.messageTray;
+const Shell = imports.gi.Shell;
+const TelepathyClient = imports.ui.telepathyClient;
+
+const _ = Gettext.gettext;
+
+// http://ntt.cc/ext/base64-Encoding-Decoding.html
+const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+function decode64(input) {
+ let output = "";
+ let chr1, chr2, chr3;
+ let enc1, enc2, enc3, enc4;
+ let i = 0;
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ do {
+ enc1 = keyStr.indexOf(input.charAt(i++));
+ enc2 = keyStr.indexOf(input.charAt(i++));
+ enc3 = keyStr.indexOf(input.charAt(i++));
+ enc4 = keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output = output + String.fromCharCode(chr1);
+
+ if (enc3 != 64) {
+ output = output + String.fromCharCode(chr2);
+ }
+ if (enc4 != 64) {
+ output = output + String.fromCharCode(chr3);
+ }
+
+ chr1 = chr2 = chr3 = "";
+ enc1 = enc2 = enc3 = enc4 = "";
+
+ } while (i < input.length);
+
+ return unescape(output);
+}
+
+function wrappedText(text, sender, timestamp, direction) {
+ return {
+ messageType: Tp.ChannelTextMessageType.NORMAL,
+ text: text,
+ sender: sender,
+ timestamp: timestamp,
+ direction: direction
+ };
+}
+
+function Source(gajimClient, accountName, author, initialMessage) {
+ this._init(gajimClient, accountName, author, initialMessage);
+}
+
+Source.prototype = {
+ __proto__: MessageTray.Source.prototype,
+
+ _init: function(gajimClient, accountName, author, initialMessage) {
+ MessageTray.Source.prototype._init.call(this, author);
+ this.isChat = true;
+ this._author = author;
+ this._gajimClient = gajimClient;
+ this._accountName = accountName;
+ this._initialMessage = initialMessage;
+ this._iconUri = null;
+ this._presence = "online";
+
+ this._notification = new TelepathyClient.Notification(this);
+ this._notification.setUrgency(MessageTray.Urgency.HIGH);
+
+ let jid = author.split('/')[0];
+ let proxy = this._gajimClient.proxy();
+ proxy.contact_infoRemote(jid, Lang.bind(this, this._gotContactInfos));
+ this._statusChangeId = proxy.connect('ContactStatus',
+ Lang.bind(this, this._onStatusChange));
+ this._contactAbsenceId = proxy.connect('ContactAbsence',
+ Lang.bind(this, this._onStatusChange));
+ this._chatStateId = proxy.connect('ChatState',
+ Lang.bind(this, this._onChatState));
+ this._messageSentId = proxy.connect('MessageSent',
+ Lang.bind(this, this._messageSent));
+ this._newMessageId = proxy.connect('NewMessage',
+ Lang.bind(this, this._messageReceived));
+ },
+
+ destroy: function() {
+ let proxy = this._gajimClient.proxy();
+ proxy.disconnect(this._statusChangeId);
+ proxy.disconnect(this._contactAbsenceId);
+ proxy.disconnect(this._chatStateId);
+ proxy.disconnect(this._messageSentId);
+ proxy.disconnect(this._newMessageId);
+ MessageTray.Source.prototype.destroy.call(this);
+ },
+
+ _gotContactInfos: function(result, excp) {
+ this.title = result['FN'];
+
+ let avatarUri = null;
+ if (result['PHOTO']) {
+ let mimeType = result['PHOTO']['TYPE'];
+ let avatarData = decode64(result['PHOTO']['BINVAL']);
+ let sha = result['PHOTO']['SHA'];
+ avatarUri = this._gajimClient.cacheAvatar(mimeType, sha, avatarData);
+ }
+
+ this._iconUri = avatarUri;
+ this._setSummaryIcon(this.createNotificationIcon());
+
+ let message = wrappedText(this._initialMessage, this._author, null, TelepathyClient.NotificationDirection.RECEIVED);
+ this._notification.appendMessage(message, false);
+
+ if (!Main.messageTray.contains(this))
+ Main.messageTray.add(this);
+
+ this.notify(this._notification);
+ },
+
+ createNotificationIcon: function() {
+ let iconBox = new St.Bin({ style_class: 'avatar-box' });
+ iconBox._size = this.ICON_SIZE;
+
+ if (!this._iconUri) {
+ iconBox.child = new St.Icon({ icon_name: 'avatar-default',
+ icon_type: St.IconType.FULLCOLOR,
+ icon_size: iconBox._size });
+ } else {
+ let textureCache = St.TextureCache.get_default();
+ iconBox.child = textureCache.load_uri_async(this._iconUri, iconBox._size, iconBox._size);
+ }
+ return iconBox;
+ },
+
+ open: function(notification) {
+ // Lookup for the messages window and display it. In the case where it's not o
+ // opened yet fallback to the roster window.
+ let windows = global.get_window_actors();
+ for (let i = 0; i < windows.length; i++) {
+ let metaWindow = windows[i].metaWindow;
+ if (metaWindow.get_wm_class_instance() == "gajim" &&
+ metaWindow.get_role() == "messages") {
+ Main.activateWindow(metaWindow);
+ return;
+ }
+ }
+
+ let app = Shell.AppSystem.get_default().get_app('gajim.desktop');
+ app.activate_window(null, global.get_current_time());
+ },
+
+ _onChatState: function(emitter, data) {
+ let chatstate = data[1][5];
+ if (chatstate == 'gone')
+ this.destroy();
+ },
+
+ _messageReceived: function(emitter, data) {
+ let author = data[1][0];
+ let text = data[1][1];
+ if (text && (author == this._author)) {
+ let message = wrappedText(text, this._author, null, TelepathyClient.NotificationDirection.RECEIVED);
+ this._notification.appendMessage(message, false);
+ this.notify(this._notification);
+ }
+ },
+
+ _messageSent: function(emitter, data) {
+ let text = data[1][1];
+ let chatstate = data[1][3];
+
+ if (text) {
+ let message = wrappedText(text, this._author, null, TelepathyClient.NotificationDirection.SENT);
+ this._notification.appendMessage(message, false);
+ } else if (chatstate == 'gone')
+ this.destroy();
+ },
+
+ notify: function() {
+
+ MessageTray.Source.prototype.notify.call(this, this._notification);
+ },
+
+ respond: function(text) {
+ let jid = this._author;
+ let keyID = ""; // unencrypted.
+ this._gajimClient.proxy().send_chat_messageRemote(jid, text, keyID, this._accountName);
+ },
+
+ _onStatusChange: function(emitter, data) {
+ if (!this.title)
+ return;
+
+ let jid = data[1][0];
+ let presence = data[1][1];
+ let message = data[1][2];
+
+ if (jid != this._author.split('/')[0])
+ return;
+
+ let presenceMessage, shouldNotify, title;
+ title = GLib.markup_escape_text(this.title, -1);
+ if (presence == "away") {
+ presenceMessage = _("%s is away.").format(title);
+ shouldNotify = false;
+ } else if (presence == "offline") {
+ presenceMessage = _("%s is offline.").format(title);
+ shouldNotify = (this._presence != "offline");
+ } else if (presence == "online") {
+ presenceMessage = _("%s is online.").format(title);
+ shouldNotify = (this._presence == "offline");
+ } else if (presence == "dnd") {
+ presenceMessage = _("%s is busy.").format(title);
+ shouldNotify = false;
+ } else
+ return;
+
+ this._presence = presence;
+
+ if (message)
+ presenceMessage += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
+
+ this._notification.appendPresence(presenceMessage, shouldNotify);
+ if (shouldNotify)
+ this.notify(this._notification);
+ }
+};
+
+
+const GajimIface = {
+ name: 'org.gajim.dbus.RemoteInterface',
+ properties: [],
+ methods: [{ name: 'send_chat_message', inSignature: 'ssss', outSignature: 'b'},
+ { name: 'contact_info', inSignature: 's', outSignature: 'a{sv}'}],
+ signals: [{ name: 'NewMessage', inSignature: 'av' },
+ { name: 'ChatState', inSignature: 'av' },
+ { name: 'ContactStatus', inSignature: 'av' },
+ { name: 'ContactAbsence', inSignature: 'av' },
+ { name: 'MessageSent', inSignature: 'av' }]
+};
+
+let Gajim = DBus.makeProxyClass(GajimIface);
+
+function GajimClient() {
+ this._init();
+}
+
+GajimClient.prototype = {
+ _init: function() {
+ this._sources = {};
+ this._cacheDir = GLib.get_user_cache_dir() + '/gnome-shell/gajim-avatars';
+ GLib.mkdir_with_parents(this._cacheDir, 0x1c0); // 0x1c0 = octal 0700
+
+ this._proxy = new Gajim(DBus.session, 'org.gajim.dbus', '/org/gajim/dbus/RemoteObject');
+ this._proxy.connect('NewMessage', Lang.bind(this, this._messageReceived));
+ },
+
+ proxy : function() {
+ return this._proxy;
+ },
+
+ _messageReceived : function(emitter, data) {
+ let author = data[1][0];
+ let message = data[1][1];
+ let account = data[0];
+ let source = this._sources[author];
+ if (!source) {
+ source = new Source(this, account, author, message);
+ source.connect('destroy', Lang.bind(this,
+ function() {
+ delete this._sources[author];
+ }));
+ this._sources[author] = source;
+ }
+ },
+
+ cacheAvatar : function(mimeType, sha, avatarData) {
+ let ext = mimeType.split('/')[1];
+ let file = this._cacheDir + '/' + sha + '.' + ext;
+ let uri = GLib.filename_to_uri(file, null);
+
+ if (GLib.file_test(file, GLib.FileTest.EXISTS))
+ return uri;
+
+ let success = false;
+ try {
+ success = GLib.file_set_contents(file, avatarData, avatarData.length);
+ } catch (e) {
+ logError(e, 'Error caching avatar data');
+ }
+ return uri;
+ }
+
+};
+
+function main() {
+ let client = new GajimClient();
+}
diff --git a/extensions/gajim/metadata.json.in b/extensions/gajim/metadata.json.in
new file mode 100644
index 0000000..62e499c
--- /dev/null
+++ b/extensions/gajim/metadata.json.in
@@ -0,0 +1,8 @@
+{
+ "uuid": "@uuid@",
+ "name": "Gajim IM integration",
+ "description": "Display Gajim incoming chats as notifications in the Shell message tray.",
+ "shell-version": [ "@shell_current@" ],
+ "localedir": "@LOCALEDIR@",
+ "url": "http://base-art.net"
+}
diff --git a/extensions/gajim/stylesheet.css b/extensions/gajim/stylesheet.css
new file mode 100644
index 0000000..e69de29
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 65a1487..e32a9a5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,8 +1,9 @@
-extensions/example/extension.js
-extensions/windowsNavigator/extension.js
-extensions/xrandr-indicator/extension.js
extensions/alternate-tab/extension.js
extensions/auto-move-windows/extension.js
extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml.in
-extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in
extensions/dock/extension.js
+extensions/example/extension.js
+extensions/gajim/extension.js
+extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml.in
+extensions/windowsNavigator/extension.js
+extensions/xrandr-indicator/extension.js
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]