diff --git a/instantbird/components/logger.js b/instantbird/components/logger.js
--- a/instantbird/components/logger.js
+++ b/instantbird/components/logger.js
@@ -349,18 +349,17 @@ Logger.prototype = {
case "profile-after-change":
obs.addObserver(this, "final-ui-startup", false);
break;
case "final-ui-startup":
obs.removeObserver(this, "final-ui-startup");
["new-conversation", "new-text",
"conversation-closed", "conversation-left-chat",
"account-connected", "account-disconnected",
- "buddy-signed-on", "buddy-signed-off",
- "buddy-away", "buddy-idle"].forEach(function(aEvent) {
+ "account-buddy-status-changed"].forEach(function(aEvent) {
obs.addObserver(this, aEvent, false);
}, this);
break;
case "new-text":
if (!aSubject.noLog) {
let log = getLogForConversation(aSubject.conversation);
log.logMessage(aSubject);
}
@@ -376,36 +375,38 @@ Logger.prototype = {
getLogForAccount(aSubject, true).logEvent("+++ " + aSubject.name +
" signed on");
break;
case "account-disconnected":
getLogForAccount(aSubject).logEvent("+++ " + aSubject.name +
" signed off");
closeLogForAccount(aSubject);
break;
- default:
+ case "account-buddy-status-changed":
let status;
if (!aSubject.online)
status = "Offline";
else if (aSubject.mobile)
status = "Mobile";
else if (aSubject.idle)
status = "Idle";
else if (aSubject.available)
status = "Available";
else
status = "Unavailable";
- let statusText = aSubject.status;
+ let statusText = aSubject.statusText;
if (statusText)
status += " (\"" + statusText + "\")";
- let name = aSubject.buddyName;
- let nameText = (aSubject.buddyAlias || name) + " (" + name + ")";
+ let nameText = aSubject.displayName + " (" + aSubject.userName + ")";
getLogForAccount(aSubject.account).logEvent(nameText + " is now " + status);
+ break;
+ default:
+ throw "Unexpected notification " + aTopic;
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.ibILogger]),
classDescription: "Logger",
classID: Components.ID("{fb0dc220-2c7a-4216-9f19-6b8f3480eae9}"),
contractID: "@instantbird.org/logger;1"
};
diff --git a/instantbird/content/blist.js b/instantbird/content/blist.js
--- a/instantbird/content/blist.js
+++ b/instantbird/content/blist.js
@@ -30,18 +30,18 @@
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
-const events = ["buddy-signed-on",
- "buddy-added",
+const events = ["contact-availability-changed",
+ "contact-added",
"account-disconnected",
"status-changed",
"purple-quit"];
const showOfflineBuddiesPref = "messenger.buddies.showOffline";
var gBuddyListContextMenu = null;
@@ -112,18 +112,18 @@ buddyListContextMenu.prototype = {
return;
let popup = document.getElementById("context-moveto-popup");
let item;
while ((item = popup.firstChild) && item.localName != "menuseparator")
popup.removeChild(item);
let groupId = this.target.group.groupId;
- let pts = Components.classes["@instantbird.org/purple/tags;1"]
- .getService(Ci.purpleITagsService);
+ let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
+ .getService(Ci.imITagsService);
let sortFunction = function (a, b) {
let [a, b] = [a.name.toLowerCase(), b.name.toLowerCase()];
return a < b ? 1 : a > b ? -1 : 0;
};
pts.getTags()
.sort(sortFunction)
.forEach(function (aTag) {
@@ -152,18 +152,18 @@ buddyListContextMenu.prototype = {
.createBundle("chrome://instantbird/locale/instantbird.properties");
let title = bundle.GetStringFromName("newGroupPromptTitle");
let message = bundle.GetStringFromName("newGroupPromptMessage");
let name = {};
if (!prompts.prompt(window, title, message, name, null,
{value: false}) || !name.value)
return; // the user canceled
- let pts = Components.classes["@instantbird.org/purple/tags;1"]
- .getService(Ci.purpleITagsService);
+ let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
+ .getService(Ci.imITagsService);
let tag = pts.getTagByName(name.value) || pts.createTag(name.value);
this.target.moveTo(tag.id);
},
showLogs: function blcm_showLogs() {
if (!this.onBuddy)
return;
var logger = Components.classes["@instantbird.org/logger;1"]
@@ -202,57 +202,60 @@ var buddyList = {
let showOffline = prefBranch.getBoolPref(showOfflineBuddiesPref);
this._showOffline = showOffline;
let item = document.getElementById("context-show-offline-buddies");
if (showOffline)
item.setAttribute("checked", "true");
else
item.removeAttribute("checked");
- let pts = Components.classes["@instantbird.org/purple/tags;1"]
- .getService(Ci.purpleITagsService);
+ let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
+ .getService(Ci.imITagsService);
let blistBox = document.getElementById("buddylistbox");
pts.getTags().forEach(function (aTag) {
let elt = document.getElementById("group" + aTag.id);
- if (!elt && showOffline) {
+ if (elt)
+ elt.showOffline = showOffline;
+ else if (showOffline) {
elt = document.createElement("group");
blistBox.appendChild(elt);
elt._showOffline = true;
if (!elt.build(aTag))
blistBox.removeChild(elt);
}
- if (elt)
- elt.showOffline = showOffline;
});
}
if (aTopic == "status-changed") {
this.displayCurrentStatus();
this.showAccountManagerIfNeeded(false);
return;
}
if (aTopic == "account-disconnected") {
let account = aSubject.QueryInterface(Ci.purpleIAccount);
if (account.reconnectAttempt <= 1)
this.showAccountManagerIfNeeded(false);
return;
}
- var pab = aSubject.QueryInterface(Ci.purpleIAccountBuddy);
- var group = pab.tag;
- var groupId = "group" + group.id;
- if ((aTopic == "buddy-signed-on" ||
- (aTopic == "buddy-added" && (this._showOffline || pab.online))) &&
- !document.getElementById(groupId)) {
- let groupElt = document.createElement("group");
- document.getElementById("buddylistbox").appendChild(groupElt);
- if (this._showOffline)
- groupElt._showOffline = true;
- groupElt.build(group);
+ // aSubject is an imIContact
+ if (aSubject.online || this._showOffline) {
+ aSubject.getTags().forEach(function (aTag) {
+ if (!document.getElementById("group" + aTag.id)) {
+dump("document.getElementById(\"group" + aTag.id + "\") not found!\n");
+ let groupElt = document.createElement("group");
+ let blistBox = document.getElementById("buddylistbox");
+ blistBox.appendChild(groupElt);
+ if (this._showOffline)
+ groupElt._showOffline = true;
+ if (!groupElt.build(aTag))
+ blistBox.removeChild(groupElt);
+ }
+ }, this);
}
},
displayStatusType: function bl_displayStatusType(aStatusType) {
document.getElementById("statusMessage")
.setAttribute("statusType", aStatusType);
let bundle =
@@ -502,18 +505,18 @@ var buddyList = {
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
buddyList._showOffline = prefBranch.getBoolPref(showOfflineBuddiesPref);
if (buddyList._showOffline) {
document.getElementById("context-show-offline-buddies")
.setAttribute("checked", "true");
}
- let pts = Components.classes["@instantbird.org/purple/tags;1"]
- .getService(Ci.purpleITagsService);
+ let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
+ .getService(Ci.imITagsService);
let blistBox = document.getElementById("buddylistbox");
pts.getTags().forEach(function (aTag) {
let groupElt = document.createElement("group");
blistBox.appendChild(groupElt);
if (buddyList._showOffline)
groupElt._showOffline = true;
if (!groupElt.build(aTag))
blistBox.removeChild(groupElt);
diff --git a/instantbird/content/buddy.xml b/instantbird/content/buddy.xml
--- a/instantbird/content/buddy.xml
+++ b/instantbird/content/buddy.xml
@@ -49,123 +49,104 @@
-
+
+
+
+
+
1000
20
-
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+ if (aTopic == "contact-signed-on")
+ this.cancelRemoveNode();
+ else if (aTopic == "contact-signed-off" && !this.group.showOffline)
+ this.removeNode();
+ ]]>
-
-
+
-
-
+
+
+
- delete this.accounts[id];
- --this.accountsCount;
-
- if (this.accountsCount) {
- this.update();
- return;
- }
-
- // No account left, this node is now useless, remove it
- this.removing = true;
+
+
+
@@ -263,37 +235,35 @@
@@ -358,39 +328,25 @@
}
]]>
@@ -238,19 +230,19 @@
this.buddy = null;
this.elt = null;
diff --git a/instantbird/content/group.xml b/instantbird/content/group.xml
--- a/instantbird/content/group.xml
+++ b/instantbird/content/group.xml
@@ -62,178 +62,138 @@
20
// restore the potential persisted value
var source = Components.classes["@mozilla.org/rdf/datasource;1?name=local-store"]
.getService(Components.interfaces.nsIRDFDataSource);
var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
var elt = RDF.GetResource(document.location + "#" + this.id);
if (source.HasAssertion(elt, RDF.GetResource("closed"),
- RDF.GetLiteral("true"), true)) {
+ RDF.GetLiteral("true"), true))
this.setAttribute("closed", "true");
- this._updateGroupLabel();
- this._updateClosedState(true);
- }
- let empty = true;
- this.tag.getBuddies().forEach(function (aBuddy) {
- aBuddy.getAccountBuddies()
- .filter(function (b) (this.showOffline || b.online) && b.tag.id == this.groupId, this)
- .forEach(function(b) { this.addBuddy(b); empty = false; }, this);
- }, this);
-
- if (!empty)
- this.tag.addObserver(this);
- return !empty;
+ contacts.forEach(this.addContact, this);
+ this._updateGroupLabel();
+ this.tag.addObserver(this);
+ return true;
]]>
-
-
+
+
-
-
+
+
-
-
+ // remove the contact from the array
+ this.contacts.splice(i, 1);
+ delete this.contactsById[aContact.contact.id];
-
-
-
-
@@ -253,38 +213,22 @@
delete aThis.animInterval;
aThis.tag.removeObserver(aThis);
aThis.parentNode.removeChild(aThis);
}
]]>
-
-
-
-
-
-
-
diff --git a/purple/purplexpcom/public/Makefile.in b/purple/purplexpcom/public/Makefile.in
--- a/purple/purplexpcom/public/Makefile.in
+++ b/purple/purplexpcom/public/Makefile.in
@@ -40,16 +40,17 @@ srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = purplexpcom
XPIDL_MODULE = purplexpcom
XPIDLSRCS = \
+ imIContactsService.idl \
purpleIAccount.idl \
purpleIAccountBuddy.idl \
purpleIAccountsService.idl \
purpleIBuddy.idl \
purpleIConversation.idl \
purpleICoreService.idl \
purpleIMessage.idl \
purpleIPlugin.idl \
diff --git a/purple/purplexpcom/public/imIContactsService.idl b/purple/purplexpcom/public/imIContactsService.idl
new file mode 100644
--- /dev/null
+++ b/purple/purplexpcom/public/imIContactsService.idl
@@ -0,0 +1,347 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Instantbird messenging client, released
+ * 2010.
+ *
+ * The Initial Developer of the Original Code is
+ * Florian QUEZE .
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+#include "nsIObserver.idl"
+#include "nsISimpleEnumerator.idl"
+#include "purpleIConversation.idl"
+
+
+interface imITag;
+interface imIContact;
+interface imIBuddy;
+interface imIAccountBuddy;
+interface purpleIProtocol;
+
+[scriptable, uuid(f1619b49-310b-47aa-ab1c-238aba084c62)]
+interface imIContactsService: nsISupports {
+ void initContacts();
+ void unInitContacts();
+
+ imIContact getContactById(in long aId);
+ imIBuddy getBuddyById(in long aId);
+ imIBuddy getBuddyByNameAndProtocol(in AUTF8String aNormalizedName,
+ in purpleIProtocol aPrpl);
+
+ // These 2 functions are called by the protocol plugins when
+ // synchronizing the buddy list with the server stored list.
+ void accountBuddyAdded(in imIAccountBuddy aAccountBuddy);
+ void accountBuddyRemoved(in imIAccountBuddy aAccountBuddy);
+};
+
+[scriptable, uuid(f799a9c2-23f2-4fd1-96fb-515bad238f8c)]
+interface imITagsService: nsISupports {
+ // Create a new tags or return the existing one if it already exists
+ imITag createTag(in AUTF8String aName);
+ // Get an existing tag by (numeric) id. Returns null if not found.
+ imITag getTagById(in long aId);
+ // Get an existing tag by name (will do an SQL query). Returns null
+ // if not found.
+ imITag getTagByName(in AUTF8String aName);
+ // Get an array of all existing tags.
+ void getTags([optional] out unsigned long tagCount,
+ [retval, array, size_is(tagCount)] out imITag tags);
+};
+
+[scriptable, uuid(c211e5e2-f0a4-4a86-9e4c-3f6b905628a5)]
+interface imITag: nsISupports {
+ readonly attribute long id;
+ attribute AUTF8String name;
+
+ // Get an array of all the contacts associated with this tag.
+ // Contacts can either "have the tag" (added by user action) or
+ // have inherited the tag because it was the server side group for
+ // one of the AccountBuddy of the contact.
+ void getContacts([optional] out unsigned long contactCount,
+ [retval, array, size_is(contactCount)] out imIContact contacts);
+
+ void addObserver(in nsIObserver aObserver);
+ void removeObserver(in nsIObserver aObserver);
+ /* Observers will be notified of changes related to the contacts
+ * that have the tag: contact-*, buddy-*, account-buddy-*
+ * notifications forwarded respectively from the imIContact,
+ * imIBuddy and imIAccountBuddy instances.
+ */
+
+ // Exposed for add-on authors. All usage by Instantbird will come from
+ // the imITag implementation so it wasn't required to expose this.
+ // This can be used to dispatch custom notifications to the
+ // observers of the tag.
+ void notifyObservers(in nsISupports aObj, in string aEvent,
+ [optional] in wstring aData);
+};
+
+[scriptable, uuid(f13dc4fc-5334-45cb-aa58-a92851955e55)]
+interface imIStatusInfo: nsISupports {
+ // Name suitable for display in the UI. Can either by the username,
+ // the server side alias, or the user set local alias of the
+ // contact.
+ readonly attribute AUTF8String displayName;
+ readonly attribute AUTF8String buddyIconFilename;
+
+ const short STATUS_UNKNOWN = 0;
+ const short STATUS_OFFLINE = 1;
+ const short STATUS_INVISIBLE = 2;
+ const short STATUS_MOBILE = 3;
+ const short STATUS_IDLE = 4;
+ const short STATUS_AWAY = 5;
+ const short STATUS_UNAVAILABLE = 6;
+ const short STATUS_AVAILABLE = 7;
+
+ // numerical value used to compare the availability of two buddies
+ // based on their current status.
+ // Use it only for immediate comparisons, do not store the value,
+ // it can change between versions for a same status of the buddy.
+ readonly attribute long statusType;
+
+ readonly attribute boolean online; // (statusType > STATUS_OFFLINE)
+ readonly attribute boolean available; // (statusType == STATUS_AVAILABLE)
+ readonly attribute boolean idle; // (statusType == STATUS_IDLE)
+ readonly attribute boolean mobile; // (statusType == STATUS_MOBILE)
+
+ readonly attribute AUTF8String statusText;
+
+ // Gives more detail to compare the availability of two buddies in
+ // the same status.
+ // This is useful when 2 buddies have been idle for various times.
+ readonly attribute long availabilityDetails;
+
+ // True if the buddy is online or if the account supports sending
+ // offline messages to the buddy.
+ readonly attribute boolean canSendMessage;
+
+ // enumerator of purpleTooltipInfo components
+ nsISimpleEnumerator getTooltipInfo();
+
+ // Will select the buddy automatically based on availability, and
+ // the account (if needed) based on the account order in the account
+ // manager.
+ purpleIConversation createConversation();
+};
+
+
+[scriptable, uuid(f585b0df-f6ad-40d5-9de4-c58b14af13e4)]
+interface imIContact: imIStatusInfo {
+ readonly attribute long id;
+ attribute AUTF8String alias;
+ attribute AUTF8String firstName;
+ attribute AUTF8String lastName;
+
+ void getTags([optional] out unsigned long tagCount,
+ [retval, array, size_is(tagCount)] out imITag tags);
+
+ // In some cases, contacts that have a single buddy can be special cased.
+ // - single object that implements both imIContact and imIBuddy (?)
+ // - different display in the UI: maybe display the protocol icon
+ // if there's only one buddy?
+ readonly attribute short buddyCount;
+
+ readonly attribute imIBuddy preferredBuddy;
+ void getBuddies([optional] out unsigned long buddyCount,
+ [retval, array, size_is(buddyCount)] out imIBuddy buddies);
+
+ void mergeContact(in imIContact aContact);
+ void adoptBuddy(in imIBuddy aBuddy);
+
+ // Returns a new contact that contains only aBuddy, and has the same
+ // list of tags.
+ // Will throw if aBuddy is not a buddy of the contact.
+ imIContact detachBuddy(in imIBuddy aBuddy);
+
+ // remove the contact from the buddy list. Will also remove the
+ // associated buddies.
+ void remove();
+
+ void addObserver(in nsIObserver aObserver);
+ void removeObserver(in nsIObserver aObserver);
+ /* Observers will be notified of changes related to the contact.
+ * aSubject will point to the imIContact object (except for contact-sent-message).
+ * Fired notifications:
+ * contact-availability-changed
+ * when either statusType or availabilityDetails has changed.
+ * contact-signed-on
+ * contact-signed-off
+ * contact-status-changed
+ * when either statusType or statusText has changed.
+ * contact-display-name-changed
+ * when the alias (or serverAlias of the most available buddy if
+ * no alias is set) has changed.
+ * The old display name is provided in aData.
+ * contact-icon-changed
+ * contact-sent-message
+ * aSubject points to the imIMessage object rather than the imIContact.
+ *
+ * Observers will also receive all the (forwarded) notifications
+ * from the linked buddies (imIBuddy instances) and their account
+ * buddies (imIAccountBuddy instances).
+ */
+
+ // Exposed for add-on authors. All usage by Instantbird will come from
+ // the imIContact implementation so it wasn't required to expose this.
+ // This can be used to dispatch custom notifications to the
+ // observers of the contact and its tags.
+ void notifyObservers(in nsISupports aObj, in string aEvent,
+ [optional] in wstring aData);
+};
+
+
+[scriptable, uuid(fea582a0-3839-4d80-bcab-0ff82ae8f97f)]
+interface imIBuddy: imIStatusInfo {
+ readonly attribute long id;
+ readonly attribute purpleIProtocol protocol;
+ readonly attribute AUTF8String userName; // may be formatted
+ readonly attribute AUTF8String normalizedName; // normalized userName
+ // The optional server alias is in displayName (inherited from imIStatusInfo)
+ // displayName = serverAlias || userName.
+
+ readonly attribute imIContact contact;
+ readonly attribute imIAccountBuddy preferredAccountBuddy;
+ void getAccountBuddies([optional] out unsigned long accountBuddyCount,
+ [retval, array, size_is(accountBuddyCount)] out imIAccountBuddy accountBuddies);
+
+ // remove the buddy from the buddy list. If the contact becomes empty, it will be removed too.
+ void remove();
+
+ void addObserver(in nsIObserver aObserver);
+ void removeObserver(in nsIObserver aObserver);
+ /* Observers will be notified of changes related to the buddy.
+ * aSubject will point to the imIBuddy object (except for buddy-sent-message).
+ * Fired notifications:
+ * buddy-availability-changed
+ * when either statusType or availabilityDetails has changed.
+ * buddy-signed-on
+ * buddy-signed-off
+ * buddy-status-changed
+ * when either statusType or statusText has changed.
+ * buddy-display-name-changed
+ * when the serverAlias has changed.
+ * The old display name is provided in aData.
+ * buddy-icon-changed
+ * buddy-sent-message
+ * aSubject points to the imIMessage object rather than the imIBuddy.
+ *
+ * Observers will also receive all the (forwarded) notifications
+ * from the linked account buddies (imIAccountBuddy instances).
+ */
+
+ // Exposed for add-on authors. All usage by Instantbird will come from
+ // the imIBuddy implementation so it wasn't required to expose this.
+ // This can be used to dispatch custom notifications to the
+ // observers of the buddy, its contact and its tags.
+ void notifyObservers(in nsISupports aObj, in string aEvent,
+ [optional] in wstring aData);
+
+ // observe should only be called by the imIAccountBuddy
+ // implementations to report changes.
+ void observe(in nsISupports aObj, in string aEvent,
+ [optional] in wstring aData);
+};
+
+/* imIAccountBuddy implementations can send notifications to their buddy:
+ *
+ * For all of them (except account-buddy-sent-message), aSubject
+ * points to the imIAccountBuddy object.
+ *
+ * Supported notifications:
+ * account-buddy-availability-changed
+ * when either statusType or availabilityDetails has changed.
+ * account-buddy-signed-on
+ * account-buddy-signed-off
+ * account-buddy-status-changed
+ * when either statusType or statusText has changed.
+ * account-buddy-display-name-changed
+ * when the serverAlias has changed.
+ * The old display name is provided in aData.
+ * account-buddy-icon-changed
+ * account-buddy-sent-message
+ * aSubject points to the imIMessage object rather than the imIAccountBuddy.
+ *
+ * All notifications (even unsupported ones) will be forwarded to the contact,
+ * its tags and nsObserverService.
+ */
+[scriptable, uuid(114d24ff-56a1-4fd6-9822-4992efb6e036)]
+interface imIAccountBuddy: imIStatusInfo {
+ readonly attribute imIBuddy buddy;
+ readonly attribute purpleIAccount account;
+ // Setting the tag will move the buddy to a different group on the
+ // server-stored buddy list.
+ attribute imITag tag;
+ readonly attribute AUTF8String userName;
+ readonly attribute AUTF8String normalizedName;
+ readonly attribute AUTF8String serverAlias;
+
+ // remove the buddy from the buddy list of this account.
+ void remove();
+};
+
+/* FIXME
+ * Add Buddy
+ * Add tag on a contact
+ */
+
+/* FIXME missing notifications
+
+ account-buddy
+ added
+ removed
+ idle-changed (useless?)
+ mobile-changed (Mic suggested it. Not sure it's needed)
+
+ buddy
+ added
+ removed
+ deleted (?)
+ away
+ idle
+ alias (deprecated -> display-name-changed)
+
+ document : buddy-preferred-account-changed
+
+ contact
+ added
+ removed (no longer visible on the buddy list)
+ deleted (permanently removed from the list)
+ away
+ idle
+ alias (deprecated -> display-name-changed)
+
+ merge
+ adopt
+
+ tag
+ tag attached/detached from a contact ?
+*/
\ No newline at end of file
diff --git a/purple/purplexpcom/public/purpleIAccount.idl b/purple/purplexpcom/public/purpleIAccount.idl
--- a/purple/purplexpcom/public/purpleIAccount.idl
+++ b/purple/purplexpcom/public/purpleIAccount.idl
@@ -35,18 +35,20 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "purpleIProtocol.idl"
#include "purpleIConversation.idl"
#include "purpleIProxy.idl"
-interface purpleITag;
-interface purpleIBuddy;
+interface purpleITag; // FIXME remove
+interface imITag;
+interface imIBuddy;
+interface imIAccountBuddy;
%{C++
#define PREF_ACCOUNT_PREFIX "messenger.account."
#define PREF_OPTIONS "options."
#define PREF_PRPL "prpl"
#define PREF_NAME "name"
#define PREF_ALIAS "alias"
#define PREF_PROXY "proxy"
@@ -110,19 +112,17 @@ interface purpleIAccount: nsISupports {
void cancelReconnection();
purpleIConversation createConversation(in AUTF8String aName);
// Used when the user wants to add a buddy to the buddy list
void addBuddy(in purpleITag aTag, in AUTF8String aName);
// Used while loading the buddy list at startup.
- void loadBuddy(in purpleIBuddy aBuddy, in AUTF8String aName,
- in AUTF8String aAlias, in AUTF8String aServerAlias,
- in purpleITag aTag);
+ imIAccountBuddy loadBuddy(in imIBuddy aBuddy, in imITag aTag);
readonly attribute boolean canJoinChat;
nsISimpleEnumerator getChatRoomFields();
purpleIChatRoomFieldValues getChatRoomDefaultFieldValues([optional] in AUTF8String aDefaultChatName);
/*
* Create a new chat conversation if it doesn't already exist.
* Returns the conversation if it already existed, null otherwise
*/
diff --git a/purple/purplexpcom/public/purpleIConversation.idl b/purple/purplexpcom/public/purpleIConversation.idl
--- a/purple/purplexpcom/public/purpleIConversation.idl
+++ b/purple/purplexpcom/public/purpleIConversation.idl
@@ -35,17 +35,17 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
#include "nsIObserver.idl"
-interface purpleIAccountBuddy;
+interface imIAccountBuddy;
interface purpleIAccount;
interface nsIURI;
interface nsIDOMDocument;
/*
* This is the XPCOM purple conversation component, a proxy for PurpleConversation.
*/
@@ -97,17 +97,17 @@ interface purpleIConversation: nsISuppor
/* Observers will be all receive new-text notifications.
aSubject will contain the message (purpleIMessage) */
};
[scriptable, uuid(0c072a80-103a-4992-b249-8e442b5f0d46)]
interface purpleIConvIM: purpleIConversation {
/* The buddy at the remote end of the conversation */
- readonly attribute purpleIAccountBuddy buddy;
+ readonly attribute imIAccountBuddy buddy;
/* The remote buddy is not currently typing */
const short NOT_TYPING = 0;
/* The remote buddy is currently typing */
const short TYPING = 1;
/* The remote buddy started typing, but has stopped typing */
diff --git a/purple/purplexpcom/src/Makefile.in b/purple/purplexpcom/src/Makefile.in
--- a/purple/purplexpcom/src/Makefile.in
+++ b/purple/purplexpcom/src/Makefile.in
@@ -60,17 +60,16 @@ ifeq (,$(filter-out WINNT WINCE,$(OS_ARC
EXTRA_LIBS = $(DIST)/lib/purple.lib
endif
CPPSRCS = \
purpleAccount.cpp \
purpleAccountBase.cpp \
purpleAccountBuddy.cpp \
purpleAccountsService.cpp \
- purpleBuddy.cpp \
purpleConversation.cpp \
purpleConvIM.cpp \
purpleConvChat.cpp \
purpleConvChatBuddy.cpp \
purpleCoreService.cpp \
purpleDebug.cpp \
purpleDNS.cpp \
purpleInit.cpp \
@@ -87,27 +86,31 @@ CPPSRCS = \
purpleSockets.cpp \
purpleStorage.cpp \
purpleTag.cpp \
purpleTagsService.cpp \
purpleTimer.cpp \
purpleTooltipInfo.cpp \
purpleUnknownProtocol.cpp \
$(NULL)
+# purpleBuddy.cpp \
ifeq ($(OS_ARCH),Darwin)
CPPSRCS += nsDockTile.cpp
OS_LIBS += -framework Carbon
endif
ifdef PURPLE_PLUGINS
DEFINES += -DPURPLE_PLUGINS
endif
EXTRA_COMPONENTS = gtalkOverrideProtocol.js facebookOverrideProtocol.js overrideProtocols.manifest
+
+EXTRA_COMPONENTS += imContacts.js imContacts.manifest
+
ifdef MOZ_DEBUG
EXTRA_COMPONENTS += jsTestProtocol.js jsTestProtocol.manifest
endif
EXTRA_JS_MODULES = \
jsProtoHelper.jsm \
$(NULL)
diff --git a/purple/purplexpcom/src/imContacts.js b/purple/purplexpcom/src/imContacts.js
new file mode 100644
--- /dev/null
+++ b/purple/purplexpcom/src/imContacts.js
@@ -0,0 +1,677 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Instantbird messenging client, released
+ * 2010.
+ *
+ * The Initial Developer of the Original Code is
+ * Florian QUEZE .
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+XPCOMUtils.defineLazyServiceGetter(this, "obs",
+ "@mozilla.org/observer-service;1",
+ "nsIObserverService");
+XPCOMUtils.defineLazyServiceGetter(this, "prefs",
+ "@mozilla.org/preferences-service;1",
+ "nsIPrefBranch");
+XPCOMUtils.defineLazyServiceGetter(this, "pcs",
+ "@instantbird.org/purple/core;1",
+ "purpleICoreService");
+XPCOMUtils.defineLazyGetter(this, "DBConn", function() pcs.storageConnection);
+
+
+var AccountsById = { };
+function getAccountById(aId) {
+ if (AccountsById.hasOwnProperty(aId))
+ return AccountsById[aId];
+
+ let account = null;
+ try {
+ account = pcs.getAccountByNumericId(aId);
+ } catch (x) { /* Not found */ }
+
+ AccountsById[aId] = account;
+ return account;
+}
+
+function TagsService() { }
+TagsService.prototype = {
+ get wrappedJSObject() this,
+ createTag: function(aName) {
+ // If the tag already exists, we don't want to create a duplicate.
+ let tag = this.getTagByName(aName);
+ if (tag)
+ return tag;
+
+ let statement = DBConn.createStatement("INSERT INTO tags (name, position) VALUES(:name, 0)");
+ statement.params.name = aName;
+ statement.executeStep();
+
+ tag = new Tag(DBConn.lastInsertRowID, aName);
+ Tags.push(tag);
+ return tag;
+ },
+ // Get an existing tag by (numeric) id. Returns null if not found.
+ getTagById: function(aId) TagsById[aId],
+ // Get an existing tag by name (will do an SQL query). Returns null
+ // if not found.
+ getTagByName: function(aName) {
+ let statement = DBConn.createStatement("SELECT id FROM tags where name = :name");
+ statement.params.name = aName;
+ if (!statement.executeStep())
+ return null;
+ return this.getTagById(statement.row.id);
+ },
+ // Get an array of all existing tags.
+ getTags: function(aTagCount) {
+ if (aTagCount)
+ aTagCount.value = Tags.length;
+ return Tags;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.imITagsService]),
+ classDescription: "Tags",
+ classID: Components.ID("{1fa92237-4303-4384-b8ac-4e65b50810a5}"),
+ contractID: "@instantbird.org/purple/tags-service;1"
+};
+
+var Tags = [];
+var TagsById = { };
+function Tag(aId, aName) {
+ this._id = aId;
+ this._name = aName;
+ this._contacts = [];
+ this._observers = [];
+
+ TagsById[this.id] = this;
+}
+Tag.prototype = {
+ get id() this._id,
+ get name() this._name,
+ set name(aNewName) {
+ var statement = DBConn.createStatement("UPDATE tags SET name = :name WHERE id = :id");
+ statement.params.name = aNewName;
+ statement.params.id = this._id;
+ statement.execute();
+
+ //FIXME move the account buddies if some use this tag as their group
+ return aNewName;
+ },
+ getContacts: function(aContactCount) {
+ let contacts = this._contacts.filter(function(c) !c._empty);
+ if (aContactCount)
+ aContactCount.value = contacts.length;
+ return contacts;
+ },
+
+ addObserver: function(aObserver) {
+//dump("tag::addObserver\n");
+ if (this._observers.indexOf(aObserver) == -1)
+ this._observers.push(aObserver);
+ },
+ removeObserver: function(aObserver) {
+//dump("tag::removeObserver\n");
+ let index = this._observers.indexOf(aObserver);
+ if (index != -1)
+ this._observers.splice(index, 1);
+ },
+ notifyObservers: function(aSubject, aTopic, aData) {
+ for each (let observer in this._observers)
+ observer.observe(aSubject, aTopic, aData);
+// dump("obs.notifyObservers: " + aSubject.displayName + " --- " + aTopic + "\n");
+ obs.notifyObservers(aSubject, aTopic, aData);
+ },
+
+ getInterfaces: function(countRef) {
+ var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imITag];
+ countRef.value = interfaces.length;
+ return interfaces;
+ },
+ getHelperForLanguage: function(language) null,
+ implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
+ flags: 0,
+ QueryInterface: XPCOMUtils.generateQI([Ci.imITag, Ci.nsIClassInfo])
+};
+
+var ContactsById = { };
+var LastDummyContactId = 0;
+function Contact(aId, aAlias) {
+ // Assign a negative id to dummy contacts that have a single buddy
+ this._id = aId || --LastDummyContactId;
+ this._alias = aAlias;
+ this._tags = [];
+ this._buddies = [];
+ this._observers = [];
+
+ ContactsById[this._id] = this;
+}
+Contact.prototype = {
+ _id: 0,
+ get id() this._id,
+ get alias() this._alias,
+ set alias(aNewAlias) {
+ var statement = DBConn.createStatement("UPDATE contacts SET alias = :alias WHERE id = :id");
+ statement.params.alias = aNewAlias;
+ statement.params.id = this._id;
+ statement.execute();
+ //FIXME fire a notification for the change
+ return aNewAlias;
+ },
+ firstName: "", // FIXME
+ lastName: "", // FIXME
+
+ getTags: function(aTagCount) {
+ if (aTagCount)
+ aTagCount.value = this._tags.length;
+ return this._tags;
+ },
+ getBuddies: function(aBuddyCount) {
+ if (aBuddyCount)
+ aBuddyCount.value = this._buddies.length;
+ return this._buddies;
+ },
+ get _empty() this._buddies.length == 0 ||
+ this._buddies.every(function(b) b._empty),
+
+ mergeContact: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ adoptBuddy: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ detachBuddy: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ remove: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ // imIStatusInfo implementation
+ _preferredBuddy: null,
+ get preferredBuddy() {
+ if (!this._preferredBuddy)
+ this._updatePreferredBuddy();
+ return this._preferredBuddy;
+ },
+ set preferredBuddy(aBuddy) {
+ let shouldNotify = this._preferredBuddy != null;
+ this._preferredBuddy = aBuddy;
+ if (shouldNotify)
+ this._notifyObservers("preferred-buddy-changed");
+ this._updateStatus();
+ },
+ // aBuddy indicate which buddy's availability has changed.
+ _updatePreferredBuddy: function(aBuddy) {
+ if (aBuddy) {
+ if (!this._preferredBuddy) {
+ this.preferredBuddy = aBuddy;
+ return;
+ }
+
+ if (aBuddy.id == this._preferredBuddy.id) {
+ // The suggested buddy is already preferred, check if its
+ // availability has changed.
+ if (aBuddy.statusType > this._statusType ||
+ (aBuddy.statusType == this._statusType && aBuddy.availabilityDetails >= this._availabilityDetails)) {
+ // keep the currently preferred buddy, only update the status.
+ this._updateStatus();
+ return;
+ }
+ // We aren't sure that the currently preferred buddy should
+ // still be preferred. Let's go through the list!
+ }
+ else {
+ // The suggested buddy is not currently preferred. If it is
+ // more available, prefer it!
+ if (aBuddy.statusType > this._statusType ||
+ (aBuddy.statusType == this._statusType && aBuddy.availabilityDetails > this._availabilityDetails))
+ this._preferredBuddy = aBuddy;
+ return;
+ }
+ }
+
+ let preferred;
+ //TODO take into account the order of the buddies in the contact.
+ for each (let buddy in this._buddies) {
+ if (!preferred || preferred.statusType < buddy.statusType ||
+ (preferred.statusType == buddy.statusType &&
+ preferred.availabilityDetails < buddy.availabilityDetails))
+ preferred = buddy;
+ }
+ if (preferred && (!this._preferredBuddy ||
+ preferred.id != this._preferredBuddy.id))
+ this._preferredBuddy = preferred;
+ },
+ _updateStatus: function() {
+ let buddy = this._preferredBuddy; // for convenience
+
+ // Decide which notifications should be fired.
+ let notifications = [];
+ if (this._statusType != buddy.statusType ||
+ this._availabilityDetails != buddy.availabilityDetails)
+ notifications.push("availability-changed");
+ if (this._statusType != buddy.statusType ||
+ this._statusText != buddy.statusText) {
+ notifications.push("status-changed");
+ if (this.online && buddy.statusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-off");
+ if (!this.online && buddy.statusType > Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-on");
+ }
+
+ // Actually change the stored status.
+ [this._statusType, this._statusText, this._availabilityDetails] =
+ [buddy.statusType, buddy.statusText, buddy.availabilityDetails];
+
+ // Fire the notifications.
+ notifications.forEach(function(aTopic) {
+ this._notifyObservers(aTopic);
+ }, this);
+ },
+ get displayName() this._alias || this.preferredBuddy.displayName,
+ get buddyIconFilename() this.preferredBuddy.buddyIconFileName,
+ _statusType: 0,
+ get statusType() this._statusType,
+ get online() this.statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
+ get available() this.statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
+ get idle() this.statusType == Ci.imIStatusInfo.STATUS_IDLE,
+ get mobile() this.statusType == Ci.imIStatusInfo.STATUS_MOBILE,
+ _statusText: "",
+ get statusText() this._statusText,
+ _availabilityDetails: 0,
+ get availabilityDetails() this._availabilityDetails,
+ get canSendMessage() this.preferredBuddy.canSendMessage,
+ //XXX should we list the buddies in the tooltip?
+ getTooltipInfo: function() this.preferredBuddy.getTooltipInfo(),
+ createConversation: function() this.preferredBuddy.createConversation(),
+
+ addObserver: function(aObserver) {
+//dump("contact::addObserver\n");
+ if (this._observers.indexOf(aObserver) == -1)
+ this._observers.push(aObserver);
+ },
+ removeObserver: function(aObserver) {
+//dump("contact::removeObserver\n");
+ let index = this._observers.indexOf(aObserver);
+ if (index != -1)
+ this._observers.splice(index, 1);
+ },
+ // internal calls + calls from add-ons
+ notifyObservers: function(aSubject, aTopic, aData) {
+ for each (let observer in this._observers)
+ observer.observe(aSubject, aTopic, aData);
+ for each (let tag in this._tags)
+ tag.notifyObservers(aSubject, aTopic, aData);
+//dump("contact::notifyObservers: " + aTopic + " -> done.\n");
+ },
+ _notifyObservers: function(aTopic, aData) {
+ this.notifyObservers(this, "contact-" + aTopic, aData);
+ },
+
+ // This is called by the imIBuddy implementations.
+ _observe: function(aSubject, aTopic, aData) {
+
+//dump("contact::_observe: " + aTopic + "\n");
+ // Forward the notification.
+ this.notifyObservers(aSubject, aTopic, aData);
+
+ let isPreferredBuddy =
+ aSubject instanceof Buddy && aSubject.id == this.preferredBuddy.id;
+ switch (aTopic) {
+ case "buddy-availability-changed":
+ this._updatePreferredBuddy(aSubject);
+ break;
+ case "buddy-status-changed":
+ if (isPreferredBuddy)
+ this._updateStatus();
+ break;
+ case "buddy-display-name-changed":
+ if (isPreferredBuddy)
+ this._notifyObservers("display-name-changed");
+ // FIXME The old display name should be provided in aData.
+ break;
+ case "buddy-icon-changed":
+ if (isPreferredBuddy)
+ this._notifyObservers("icon-changed");
+ break;
+ case "buddy-sent-message":
+ this._notifyObservers("sent-message");
+ }
+ },
+
+ getInterfaces: function(countRef) {
+ var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIContact];
+ countRef.value = interfaces.length;
+ return interfaces;
+ },
+ getHelperForLanguage: function(language) null,
+ implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
+ flags: 0,
+ QueryInterface: XPCOMUtils.generateQI([Ci.imIContact, Ci.nsIClassInfo])
+};
+
+var BuddiesById = { };
+function Buddy(aId, aKey, aName, aSrvAlias, aContactId) {
+ this._id = aId;
+ this._key = aKey;
+ this._name = aName;
+ if (aSrvAlias)
+ this._srvAlias = aSrvAlias;
+ this._accounts = [];
+ this._observers = [];
+
+ if (aContactId)
+ this._contact = ContactsById[aContactId];
+ else
+ this._contact = new Contact(null, null);
+
+ this._contact._buddies.push(this);
+
+ BuddiesById[this._id] = this;
+}
+Buddy.prototype = {
+ get id() this._id,
+ destroy: function() {
+ delete this._accounts;
+ delete this._observers;
+ delete this._preferredAccount;
+ },
+ get protocol() this._accounts[0].account.protocol,
+ get userName() this._name,
+ get normalizedName() this._key,
+ _srvAlias: "",
+ get displayName() this._srvAlias || this._name,
+ _contact: null,
+ get contact() this._contact,
+ getAccountBuddies: function(aAccountBuddyCount) {
+ if (aAccountBuddyCount)
+ aAccountBuddyCount.value = this._accounts.length;
+ return this._accounts;
+ },
+
+ _addAccount: function(aAccountBuddy, aTag) {
+ this._accounts.push(aAccountBuddy);
+ let contact = this._contact;
+ if (this._contact._tags.indexOf(aTag) == -1) {
+ this._contact._tags.push(aTag);
+ aTag._contacts.push(contact);
+ // FIXME decide if this relation should be added in mozStorage?
+ }
+
+ this._preferredAccount = aAccountBuddy;
+ },
+ get _empty() this._accounts.length == 0,
+
+ remove: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ // imIStatusInfo implementation
+ _preferredAccount: null,
+ get preferredAccountBuddy() this._preferredAccount,
+ set preferredAccount(aAccount) {
+ this._preferredAccount = aAccount;
+ this._notifyObservers("preferred-account-changed");
+ this._updateStatus();
+ },
+ // aAccount indicate which account's availability has changed.
+ _updatePreferredAccount: function(aAccount) {
+ if (aAccount) {
+//dump("aAccount = " + aAccount.account.numericId + ", prefered = " + this._preferredAccount.account.numericId + "\n");
+ if (aAccount.account.numericId == this._preferredAccount.account.numericId) {
+ // The suggested account is already preferred, check if its
+ // availability has changed.
+ if (aAccount.statusType > this._statusType ||
+ (aAccount.statusType == this._statusType && aAccount.availabilityDetails >= this._availabilityDetails)) {
+ // keep the currently preferred account, only update the status.
+ this._updateStatus();
+ return;
+ }
+ // We aren't sure that the currently preferred account should
+ // still be preferred. Let's go through the list!
+ }
+ else {
+ // The suggested account is not currently preferred. If it is
+ // more available, prefer it!
+ if (aAccount.statusType > this._statusType ||
+ (aAccount.statusType == this._statusType && aAccount.availabilityDetails > this._availabilityDetails))
+ this.preferredAccount = aAccount;
+ return;
+ }
+ }
+
+ let preferred;
+ //TODO take into account the order of the account-manager list.
+ for each (let account in this._accounts) {
+ if (!preferred || preferred.statusType < account.statusType ||
+ (preferred.statusType == account.statusType &&
+ preferred.availabilityDetails < account.availabilityDetails))
+ preferred = account;
+ }
+ if (!this._preferredAccount) {
+ if (preferred)
+ this.preferredAccount = preferred;
+ return;
+ }
+ if (preferred.account.numericId != this._preferredAccount.account.numericId)
+ this.preferredAccount = preferred;
+ else
+ this._updateStatus();
+ },
+ _updateStatus: function() {
+ let account = this._preferredAccount; // for convenience
+
+ // Decide which notifications should be fired.
+ let notifications = [];
+// dump("old status type = " + this._statusType + " new status = " + account.statusType+"\n");
+ if (this._statusType != account.statusType ||
+ this._availabilityDetails != account.availabilityDetails)
+ notifications.push("availability-changed");
+ if (this._statusType != account.statusType ||
+ this._statusText != account.statusText) {
+ notifications.push("status-changed");
+ if (this.online && account.statusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-off");
+ if (!this.online && account.statusType > Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-on");
+ }
+
+ // Actually change the stored status.
+ [this._statusType, this._statusText, this._availabilityDetails] =
+ [account.statusType, account.statusText, account.availabilityDetails];
+
+ // Fire the notifications.
+ notifications.forEach(function(aTopic) {
+ this._notifyObservers(aTopic);
+ }, this);
+ },
+// get displayName() this._preferredAccount.displayName,
+// Fix the displayName. Decide if it should use srvAlias
+ get buddyIconFilename() this._preferredAccount.buddyIconFileName,
+ _statusType: 0,
+ get statusType() this._statusType,
+ get online() this.statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
+ get available() this.statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
+ get idle() this.statusType == Ci.imIStatusInfo.STATUS_IDLE,
+ get mobile() this.statusType == Ci.imIStatusInfo.STATUS_MOBILE,
+ _statusText: "",
+ get statusText() this._statusText,
+ _availabilityDetails: 0,
+ get availabilityDetails() this._availabilityDetails,
+ get canSendMessage() this._preferredAccount.canSendMessage,
+ //XXX should we list the accounts in the tooltip?
+ getTooltipInfo: function() this._preferredAccount.getTooltipInfo(),
+ createConversation: function() this._preferredAccount.createConversation(),
+
+ addObserver: function(aObserver) {
+//dump("buddy::addObserver\n");
+ if (this._observers.indexOf(aObserver) == -1)
+ this._observers.push(aObserver);
+ },
+ removeObserver: function(aObserver) {
+//dump("buddy::removeObserver\n");
+ let index = this._observers.indexOf(aObserver);
+ if (index != -1)
+ this._observers.splice(index, 1);
+ },
+ // internal calls + calls from add-ons
+ notifyObservers: function(aSubject, aTopic, aData) {
+ for each (let observer in this._observers)
+ observer.observe(aSubject, aTopic, aData);
+ this._contact._observe(aSubject, aTopic, aData);
+ },
+ _notifyObservers: function(aTopic, aData) {
+ this.notifyObservers(this, "buddy-" + aTopic, aData);
+ },
+
+ // This is called by the imIAccountBuddy implementations.
+ observe: function(aSubject, aTopic, aData) {
+try{
+
+//dump("buddy::_observe: " + aTopic + "\n");
+ // Forward the notification.
+ this.notifyObservers(aSubject, aTopic, aData);
+
+ let isPreferredAccount =
+ aSubject.account.numericId == this._preferredAccount.account.numericId;
+ switch (aTopic) {
+ case "account-buddy-availability-changed":
+ this._updatePreferredAccount(aSubject);
+ break;
+ case "account-buddy-status-changed":
+ if (isPreferredAccount)
+ this._updateStatus();
+ break;
+ case "account-buddy-display-name-changed":
+ if (isPreferredAccount)
+ this._notifyObservers("display-name-changed");
+ // FIXME The old display name should be provided in aData.
+ break;
+ case "account-buddy-icon-changed":
+ if (isPreferredAccount)
+ this._notifyObservers("icon-changed");
+ break;
+ case "account-buddy-sent-message":
+ this._notifyObservers("sent-message");
+ }
+} catch (x) {
+dump(x + "\n\n\n");
+Components.utils.reportError(x);
+}
+
+ },
+
+ getInterfaces: function(countRef) {
+ var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIBuddy];
+ countRef.value = interfaces.length;
+ return interfaces;
+ },
+ getHelperForLanguage: function(language) null,
+ implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
+ flags: 0,
+ QueryInterface: XPCOMUtils.generateQI([Ci.imIBuddy, Ci.nsIClassInfo])
+};
+
+
+function ContactsService() { }
+ContactsService.prototype = {
+ initContacts: function() {
+try{
+ var statement = DBConn.createStatement("SELECT id, name FROM tags");
+ while (statement.executeStep())
+ Tags.push(new Tag(statement.getInt32(0), statement.getUTF8String(1)));
+
+ statement = DBConn.createStatement("SELECT id, alias FROM contacts");
+ while (statement.executeStep())
+ new Contact(statement.getInt32(0), statement.getUTF8String(1));
+
+ statement = DBConn.createStatement("SELECT contact_id, tag_id FROM contact_tag");
+ while (statement.executeStep())
+ ContactsById[statement.getInt32(0)]._tags.push(TagsById(statement.getInt32(1)));
+
+ statement = DBConn.createStatement("SELECT id, key, name, srv_alias, contact_id FROM buddies");
+ while (statement.executeStep())
+ new Buddy(statement.getInt32(0), statement.getUTF8String(1),
+ statement.getUTF8String(2), statement.getUTF8String(3),
+ statement.getInt32(4));
+ // FIXME is there a way to enforce that all AccountBuddies of a Buddy have the same protocol?
+
+ statement = DBConn.createStatement("SELECT account_id, buddy_id, tag_id FROM account_buddy");
+ while (statement.executeStep()) {
+ let account = getAccountById(statement.getInt32(0));
+ let buddy = BuddiesById[statement.getInt32(1)];
+ let tag = TagsById[statement.getInt32(2)];
+try{
+ let ab = account.loadBuddy(buddy, tag);
+ if (ab)
+ buddy._addAccount(ab, tag);
+} catch (x) {
+dump(x +"\n");
+}
+ //FIXME ab shouldn't be NULL (once purpleAccount is finished)
+ // We should throw if it is!
+ }
+} catch (x) {
+ dump(x +"\n");
+}
+
+ },
+ unInitContacts: function() {
+ AccountsById = { };
+ Tags = [];
+ TagsById = { };
+ // avoid shutdown leak caused by references to native components
+ // implementing imIAccountBuddy.
+ for (let id in BuddiesById)
+ BuddiesById[id].destroy();
+ BuddiesById = { };
+ ContactsById = { };
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.imIContactsService]),
+ classDescription: "Contacts",
+ classID: Components.ID("{8c3725dd-ee26-489d-8135-736015af8c7f}"),
+ contractID: "@instantbird.org/purple/contacts-service;1"
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContactsService, TagsService]);
+
+
+//Components.classes["@instantbird.org/purple/tags-service;1"].getService(Components.interfaces.imITagsService).getTags().map(function(t)t.getContacts()).join(",")
+//Components.classes["@instantbird.org/purple/tags-service;1"].getService(Components.interfaces.imITagsService).getTags().map(function(t)t.getContacts().map(function(c)c.getBuddies().map(function(b)b.id))).join(",")
+
+// Components.classes["@instantbird.org/purple/tags-service;1"].getService(Components.interfaces.imITagsService).getTags().map(function(t)t.getContacts().map(function(c) c.displayName)).join(",")
+
+// TODO move the Tags to inside the tagsService
\ No newline at end of file
diff --git a/purple/purplexpcom/src/imContacts.manifest b/purple/purplexpcom/src/imContacts.manifest
new file mode 100644
--- /dev/null
+++ b/purple/purplexpcom/src/imContacts.manifest
@@ -0,0 +1,4 @@
+component {8c3725dd-ee26-489d-8135-736015af8c7f} imContacts.js
+contract @instantbird.org/purple/contacts-service;1 {8c3725dd-ee26-489d-8135-736015af8c7f}
+component {1fa92237-4303-4384-b8ac-4e65b50810a5} imContacts.js
+contract @instantbird.org/purple/tags-service;1 {1fa92237-4303-4384-b8ac-4e65b50810a5}
diff --git a/purple/purplexpcom/src/jsProtoHelper.jsm b/purple/purplexpcom/src/jsProtoHelper.jsm
--- a/purple/purplexpcom/src/jsProtoHelper.jsm
+++ b/purple/purplexpcom/src/jsProtoHelper.jsm
@@ -128,18 +128,24 @@ const GenericAccountPrototype = {
checkAutoLogin: function() this._base.checkAutoLogin(),
remove: function() this._base.remove(),
UnInit: function() this._base.UnInit(),
connect: function() this._base.connect(),
disconnect: function() this._base.disconnect(),
cancelReconnection: function() this._base.cancelReconnection(),
createConversation: function(aName) this._base.createConversation(aName),
addBuddy: function(aTag, aName) this._base.addBuddy(aTag, aName),
- loadBuddy: function(aBuddy, aName, aAlias, aServerAlias, aTag)
- this._base.loadBuddy(aBuddy, aName, aAlias, aServerAlias, aTag),
+ loadBuddy: function(aBuddy, aTag) {
+ try {
+ return new AccountBuddy(this, aBuddy, aTag) ;
+ } catch (x) {
+ dump(x + "\n");
+ return null;
+ }
+ },
getChatRoomFields: function() this._base.getChatRoomFields(),
getChatRoomDefaultFieldValues: function(aDefaultChatName)
this._base.getChatRoomDefaultFieldValues(aDefaultChatName),
joinChat: function(aComponents) this._base.joinChat(aComponents),
setBool: function(aName, aVal) this._base.setBool(aName, aVal),
setInt: function(aName, aVal) this._base.setInt(aName, aVal),
setString: function(aName, aVal) this._base.setString(aName, aVal),
save: function() this._base.save(),
@@ -191,16 +197,139 @@ const GenericAccountPrototype = {
return interfaces;
},
getHelperForLanguage: function(language) null,
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
flags: 0,
QueryInterface: XPCOMUtils.generateQI([Ci.purpleIAccount, Ci.nsIClassInfo])
};
+
+var GenericAccountBuddyPrototype = {
+ _init: function(aAccount, aBuddy, aTag) {
+ this._tag = aTag;
+ this._account = aAccount;
+ this._buddy = aBuddy;
+ },
+
+ get account() this._account,
+ get buddy() this._buddy,
+ get tag() this._tag,
+ set tag(aNewTag) {
+ //FIXME!!
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ _notifyObservers: function(aTopic, aData) {
+ this._buddy.observe(this, "account-buddy-" + aTopic, aData);
+ },
+
+ get userName() this._buddy.userName, // FIXME
+ get normalizedName() this._buddy.normalizedName, //FIXME
+ _serverAlias: "",
+ get serverAlias() this._serverAlias,
+ set serverAlias(aNewAlias) {
+ let old = this.displayName;
+ this._serverAlias = aNewAlias;
+ this._notifyObservers("display-name-changed", old);
+ },
+
+ remove: function() {
+ // FIXME: Add a removeAccount in imIBuddy? Or create an imPIBuddy iface?
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ // imIStatusInfo implementation
+ get displayName() this.serverAlias || this.userName,
+ _buddyIconFileName: "",
+ get buddyIconFilename() this._buddyIconFileName,
+ set buddyIconFilename(aNewFileName) {
+ this._buddyIconFileName = aNewFileName;
+ this._notifyObservers("icon-changed");
+ },
+ _statusType: 0,
+ get statusType() this._statusType,
+ get online() this._statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
+ get available() this._statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
+ get idle() this._statusType == Ci.imIStatusInfo.STATUS_IDLE,
+ get mobile() this._statusType == Ci.imIStatusInfo.STATUS_MOBILE,
+ _statusText: "",
+ get statusText() this._statusText,
+
+ // This is for use by the protocol plugin, it's not exposed in the
+ // imIStatusInfo interface.
+ // All parameters are optional and will be ignored if they are null
+ // or undefined.
+ setStatus: function(aStatusType, aStatusText, aAvailabilityDetails) {
+ // Ignore omitted parameters.
+ if (aStatusType === undefined || aStatusType === null)
+ aStatusType = this._statusType;
+ if (aStatusText === undefined || aStatusText === null)
+ aStatusText = this._statusText;
+ if (aAvailabilityDetails === undefined || aAvailabilityDetails === null)
+ aAvailabilityDetails = this._availabilityDetails;
+
+ // Decide which notifications should be fired.
+ let notifications = [];
+ if (this._statusType != aStatusType ||
+ this._availabilityDetails != aAvailabilityDetails)
+ notifications.push("availability-changed");
+ if (this._statusType != aStatusType ||
+ this._statusText != aStatusText) {
+ notifications.push("status-changed");
+ if (this.online && aStatusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-off");
+ if (!this.online && aStatusType > Ci.imIStatusInfo.STATUS_OFFLINE)
+ notifications.push("signed-on");
+ }
+
+ // Actually change the stored status.
+ [this._statusType, this._statusText, this._availabilityDetails] =
+ [aStatusType, aStatusText, aAvailabilityDetails];
+
+ // Fire the notifications.
+ notifications.forEach(function(aTopic) {
+ this._notifyObservers(aTopic);
+ }, this);
+ },
+
+ _availabilityDetails: 0,
+ get availabilityDetails() this._availabilityDetails,
+
+ get canSendMessage() this.online /*|| this.account.canSendOfflineMessage(this) */,
+
+ getTooltipInfo: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ createConversation: function() {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ getInterfaces: function(countRef) {
+ var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIAccountBuddy];
+ countRef.value = interfaces.length;
+ return interfaces;
+ },
+ getHelperForLanguage: function(language) null,
+ implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
+ flags: 0,
+ QueryInterface: XPCOMUtils.generateQI([Ci.imIAccountBuddy, Ci.nsIClassInfo])
+};
+
+function AccountBuddy(aAccount, aBuddy, aTag) {
+ this._init(aAccount, aBuddy, aTag);
+
+ // TODO do the proto specific stuff here:
+// dump("Linking Buddy#" + this.buddy.id + "(" + this.buddy.displayName + ") to account#" + this.account.id + "\n");
+}
+AccountBuddy.prototype = GenericAccountBuddyPrototype;
+
+
+
+
function Message(aWho, aMessage, aObject)
{
this.id = ++Message.prototype._lastId;
this.time = Math.round(new Date() / 1000);
this.who = aWho;
this.message = aMessage;
this.originalMessage = aMessage;
diff --git a/purple/purplexpcom/src/purpleAccount.cpp b/purple/purplexpcom/src/purpleAccount.cpp
--- a/purple/purplexpcom/src/purpleAccount.cpp
+++ b/purple/purplexpcom/src/purpleAccount.cpp
@@ -34,16 +34,17 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "purpleAccount.h"
#include "purpleCoreService.h"
#include "purpleGListEnumerator.h"
#include "purpleStorage.h"
+#include
#include
#include
#include
#include
#include
#include
#include
#include
@@ -492,42 +493,72 @@ NS_IMETHODIMP purpleAccount::AddBuddy(pu
purpleTag *tag = static_cast(aTag);
PurpleBuddy *buddy = purple_buddy_new(mAccount, PromiseFlatCString(aName).get(), NULL);
tag->addBuddy(buddy);
purple_account_add_buddy(mAccount, buddy);
return NS_OK;
}
-/* void LoadBuddy (in purpleIBuddy aBuddy, in AUTF8String aName,
- in AUTF8String aAlias, in AUTF8String aServerAlias,
- in purpleITag aTag); */
-NS_IMETHODIMP purpleAccount::LoadBuddy(purpleIBuddy *aBuddy,
- const nsACString & aName,
- const nsACString & aAlias,
- const nsACString & aServerAlias,
- purpleITag *aTag)
+static inline PurpleGroup *GetPurpleGroupForTag(imITag *aTag)
{
+ nsCString name;
+ nsresult rv = aTag->GetName(name);
+ NS_ENSURE_SUCCESS(rv, NULL);
+
+ // creating an already existing group will return the existing group
+ PurpleGroup *group = purple_group_new(name.get());
+ NS_ENSURE_TRUE(group, NULL);
+
+ if (!GET_NODE_UI_DATA(group)) {
+ // the group is new:
+ // Set ui_data to the tag
+ purple_blist_node_set_ui_data(PURPLE_BLIST_NODE(group), aTag);
+ NS_ADDREF(aTag);
+
+ purple_blist_add_group(group, NULL);
+ }
+
+ return group;
+}
+
+/* imIAccountBuddy LoadBuddy (in imIBuddy aBuddy, in imITag aTag); */
+NS_IMETHODIMP purpleAccount::LoadBuddy(imIBuddy *aBuddy,
+ imITag *aTag,
+ imIAccountBuddy **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aBuddy);
+ NS_ENSURE_ARG_POINTER(aTag);
PURPLE_ENSURE_INIT(mProtocol && mAccount);
- purpleTag *tag = static_cast(aTag);
+ nsCString userName;
+ nsresult rv = aBuddy->GetUserName(userName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!userName.IsEmpty(), NS_ERROR_UNEXPECTED);
+
+ PurpleGroup *group = GetPurpleGroupForTag(aTag);
+ NS_ENSURE_TRUE(group, NS_ERROR_UNEXPECTED);
+
PurpleBuddy *buddy =
- purple_buddy_new(mAccount, PromiseFlatCString(aName).get(),
- aAlias.IsEmpty() ? NULL : PromiseFlatCString(aAlias).get());
+ purple_buddy_new(mAccount, userName.get(), NULL);
- // Ignore server aliases that are identical to the name.
- // (We used to mistakenly store the name in the srv_alias column
- // when there was no server alias.)
- if (!aServerAlias.IsEmpty() && !aServerAlias.Equals(aName)) {
- buddy->server_alias =
- purple_utf8_strip_unprintables(PromiseFlatCString(aServerAlias).get());
- }
- tag->addBuddy(buddy);
+ // Set the server alias on the blist node if the display name of the
+ // imIBuddy is different from the userName
+ nsCString displayName;
+ rv = aBuddy->GetDisplayName(displayName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(!displayName.IsEmpty(), NS_ERROR_UNEXPECTED);
+ if (!displayName.Equals(userName))
+ buddy->server_alias = purple_utf8_strip_unprintables(displayName.get());
- static_cast(aBuddy)->AddAccount(this, buddy, tag);
+ nsCOMPtr accountBuddy =
+ new purpleAccountBuddy(buddy, aBuddy);
+ purple_blist_add_buddy(buddy, NULL, group, NULL);
+
+ NS_ADDREF(*aResult = accountBuddy);
return NS_OK;
}
PurplePluginProtocolInfo *purpleAccount::GetPrplInfo()
{
NS_ENSURE_TRUE(mAccount, NULL);
PurpleConnection *gc = purple_account_get_connection(mAccount);
diff --git a/purple/purplexpcom/src/purpleAccount.h b/purple/purplexpcom/src/purpleAccount.h
--- a/purple/purplexpcom/src/purpleAccount.h
+++ b/purple/purplexpcom/src/purpleAccount.h
@@ -46,17 +46,17 @@ class purpleAccount : public purpleAccou
{
public:
// NS_DECL_PURPLEIACCOUNT
NS_SCRIPTABLE NS_IMETHOD UnInit(void);
NS_SCRIPTABLE NS_IMETHOD Connect(void);
NS_SCRIPTABLE NS_IMETHOD Disconnect(void);
NS_SCRIPTABLE NS_IMETHOD CreateConversation(const nsACString & aName, purpleIConversation **_retval NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD AddBuddy(purpleITag *aTag, const nsACString & aName);
- NS_SCRIPTABLE NS_IMETHOD LoadBuddy(purpleIBuddy *aBuddy, const nsACString & aName, const nsACString & aAlias, const nsACString & aServerAlias, purpleITag *aTag);
+ NS_SCRIPTABLE NS_IMETHOD LoadBuddy(imIBuddy *aBuddy, imITag *aTag, imIAccountBuddy **aResult);
NS_SCRIPTABLE NS_IMETHOD GetCanJoinChat(PRBool *aCanJoinChat);
NS_SCRIPTABLE NS_IMETHOD GetChatRoomFields(nsISimpleEnumerator **_retval NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD GetChatRoomDefaultFieldValues(const nsACString & aDefaultChatName, purpleIChatRoomFieldValues **_retval NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD JoinChat(purpleIChatRoomFieldValues *aComponents);
NS_SCRIPTABLE NS_IMETHOD GetNormalizedName(nsACString & aNormalizedName);
NS_SCRIPTABLE NS_IMETHOD GetPassword(nsACString & aPassword);
NS_SCRIPTABLE NS_IMETHOD SetPassword(const nsACString & aPassword);
NS_SCRIPTABLE NS_IMETHOD GetRememberPassword(PRBool *aRememberPassword);
diff --git a/purple/purplexpcom/src/purpleAccountBase.cpp b/purple/purplexpcom/src/purpleAccountBase.cpp
--- a/purple/purplexpcom/src/purpleAccountBase.cpp
+++ b/purple/purplexpcom/src/purpleAccountBase.cpp
@@ -354,24 +354,20 @@ NS_IMETHODIMP purpleAccountBase::CreateC
/* void addBuddy (in purpleITag aTag, in AUTF8String aName); */
NS_IMETHODIMP purpleAccountBase::AddBuddy(purpleITag *aTag,
const nsACString& aName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
-/* void LoadBuddy (in purpleIBuddy aBuddy, in AUTF8String aName,
- in AUTF8String aAlias, in AUTF8String aServerAlias,
- in purpleITag aTag); */
-NS_IMETHODIMP purpleAccountBase::LoadBuddy(purpleIBuddy *aBuddy,
- const nsACString & aName,
- const nsACString & aAlias,
- const nsACString & aServerAlias,
- purpleITag *aTag)
+/* imIAccountBuddy LoadBuddy (in imIBuddy aBuddy, in imITag aTag); */
+NS_IMETHODIMP purpleAccountBase::LoadBuddy(imIBuddy *aBuddy,
+ imITag *aTag,
+ imIAccountBuddy **aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* nsISimpleEnumerator getChatRoomFields (); */
NS_IMETHODIMP purpleAccountBase::GetChatRoomFields(nsISimpleEnumerator **aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
diff --git a/purple/purplexpcom/src/purpleAccountBase.h b/purple/purplexpcom/src/purpleAccountBase.h
--- a/purple/purplexpcom/src/purpleAccountBase.h
+++ b/purple/purplexpcom/src/purpleAccountBase.h
@@ -63,17 +63,17 @@ class purpleAccountBase : public purpleI
public:
NS_DECL_ISUPPORTS
NS_DECL_PURPLEIACCOUNT
NS_DECL_PURPLEIACCOUNTBASE
purpleAccountBase();
protected:
- ~purpleAccountBase();
+ virtual ~purpleAccountBase();
nsresult GetPrefBranch();
nsresult unstoreAccount();
nsresult SetBoolPref(const char *aName, PRBool aValue);
nsresult SetIntPref(const char *aName, PRInt32 aValue);
nsresult SetStringPref(const char *aName, const char *aValue);
inline void GetBoolPref(const char *aName);
inline void GetIntPref(const char *aName);
inline void GetCharPref(const char *aName);
diff --git a/purple/purplexpcom/src/purpleAccountBuddy.cpp b/purple/purplexpcom/src/purpleAccountBuddy.cpp
--- a/purple/purplexpcom/src/purpleAccountBuddy.cpp
+++ b/purple/purplexpcom/src/purpleAccountBuddy.cpp
@@ -47,59 +47,58 @@
#include
#include
#pragma GCC visibility push(default)
#include
#pragma GCC visibility pop
NS_IMPL_CLASSINFO(purpleAccountBuddy, NULL, 0, PURPLE_ACCOUNTBUDDY_CID)
-NS_IMPL_ISUPPORTS1_CI(purpleAccountBuddy, purpleIAccountBuddy)
+NS_IMPL_ISUPPORTS1_CI(purpleAccountBuddy, imIAccountBuddy)
#ifdef PR_LOGGING
//
// NSPR_LOG_MODULES=purpleAccountBuddy:5
//
static PRLogModuleInfo *gPurpleAccountBuddyLog = nsnull;
#endif
#define LOG(args) PR_LOG(gPurpleAccountBuddyLog, PR_LOG_DEBUG, args)
-purpleAccountBuddy::purpleAccountBuddy()
+purpleAccountBuddy::purpleAccountBuddy(PurpleBuddy *aPurpleBuddy,
+ imIBuddy *aBuddy)
{
/* member initializers and constructor code */
#ifdef PR_LOGGING
if (!gPurpleAccountBuddyLog)
gPurpleAccountBuddyLog = PR_NewLogModule("purpleAccountBuddy");
#endif
+
+ mBuddy = aBuddy;
+ mPurpleBuddy = aPurpleBuddy;
+ purple_blist_node_set_ui_data(PURPLE_BLIST_NODE(aPurpleBuddy), this);
+ PurpleAccount *account = purple_buddy_get_account(aPurpleBuddy);
+ mAccount = purpleAccount::fromPurpleAccount(account);
+ NS_ASSERTION(mAccount, "Failed to get the account from a PurpleBuddy");
}
purpleAccountBuddy::~purpleAccountBuddy()
{
/* destructor code */
-}
-
-void purpleAccountBuddy::Init(purpleIAccount *aAccount,
- PurpleBuddy *aBuddy,
- purpleTag *aTag)
-{
- mAccount = aAccount;
- mBuddy = aBuddy;
- purple_blist_node_set_ui_data(PURPLE_BLIST_NODE(aBuddy), this);
- // aTag is unused because we don't cache the tag here any more, we
- // use the ui data of the libpurple buddy list node instead.
- // ToDo for 0.3: see if we should remove the 'tag' parameters in most
- // 'AddAccount' methods.
+ if (mPurpleBuddy)
+ Uninit();
}
void purpleAccountBuddy::Uninit()
{
- purple_blist_node_set_ui_data(PURPLE_BLIST_NODE(mBuddy), nsnull);
+ purple_blist_node_set_ui_data(PURPLE_BLIST_NODE(mPurpleBuddy), nsnull);
mBuddy = NULL;
+ mPurpleBuddy = NULL;
}
+#if 0
nsresult purpleAccountBuddy::Store(PRInt32 aBuddyId)
{
NS_ENSURE_TRUE(aBuddyId, NS_ERROR_INVALID_ARG);
PRUint32 accountId;
nsresult rv = mAccount->GetNumericId(&accountId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(accountId, NS_ERROR_UNEXPECTED);
@@ -158,54 +157,60 @@ nsresult purpleAccountBuddy::UnStore(PRI
rv = statement->BindInt32Parameter(2, groupId);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("purpleAccountBuddy::UnStore(buddyId = %i, groupId = %i)",
aBuddyId, groupId));
return statement->Execute();
}
+#endif
/* readonly attribute purpleIBuddy buddy; */
-NS_IMETHODIMP purpleAccountBuddy::GetBuddy(purpleIBuddy * *aBuddy)
+NS_IMETHODIMP purpleAccountBuddy::GetBuddy(imIBuddy * *aBuddy)
{
NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
- purpleIBuddy *buddy = purpleBuddy::fromPurpleBuddy(mBuddy);
- NS_ENSURE_TRUE(buddy, NS_ERROR_NOT_INITIALIZED);
-
- NS_ADDREF(*aBuddy = buddy);
+ NS_ADDREF(*aBuddy = mBuddy);
return NS_OK;
}
/* readonly attribute purpleIAccount account; */
NS_IMETHODIMP purpleAccountBuddy::GetAccount(purpleIAccount * *aAccount)
{
NS_ENSURE_TRUE(mAccount, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aAccount = mAccount);
return NS_OK;
}
+static inline imITag *GetTagFromPurpleGroup(PurpleGroup *aGroup)
+{
+ return static_cast(GET_NODE_UI_DATA(aGroup));
+}
+
/* attribute purpleITag tag; */
-NS_IMETHODIMP purpleAccountBuddy::GetTag(purpleITag * *aTag)
+NS_IMETHODIMP purpleAccountBuddy::GetTag(imITag * *aTag)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- purpleITag *tag = purpleTag::fromPurpleGroup(purple_buddy_get_group(mBuddy));
+ PurpleGroup *group = purple_buddy_get_group(mPurpleBuddy);
+ imITag *tag = GetTagFromPurpleGroup(group);
NS_ENSURE_TRUE(tag, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aTag = tag);
return NS_OK;
}
-NS_IMETHODIMP purpleAccountBuddy::SetTag(purpleITag * aTag)
+NS_IMETHODIMP purpleAccountBuddy::SetTag(imITag * aTag)
{
NS_ENSURE_ARG_POINTER(aTag);
NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ return NS_ERROR_NOT_IMPLEMENTED;
+#if 0
static_cast(aTag)->addBuddy(mBuddy);
// Now collect the data we need to make the SQL UPDATE query
// First, the tag id
nsCOMPtr tag;
nsresult rv = GetTag(getter_AddRefs(tag));
NS_ENSURE_SUCCESS(rv, rv);
@@ -247,71 +252,44 @@ NS_IMETHODIMP purpleAccountBuddy::SetTag
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt32Parameter(0, tagId);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(1, accountId);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindInt64Parameter(2, buddyId);
NS_ENSURE_SUCCESS(rv, rv);
return statement->Execute();
+#endif
}
-/* readonly attribute AUTF8String buddyName; */
-NS_IMETHODIMP purpleAccountBuddy::GetBuddyName(nsACString& aBuddyName)
+/* readonly attribute AUTF8String userName; */
+NS_IMETHODIMP purpleAccountBuddy::GetUserName(nsACString& aUserName)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- aBuddyName = purple_buddy_get_name(mBuddy);
+ aUserName = purple_buddy_get_name(mPurpleBuddy);
return NS_OK;
}
/* readonly attribute AUTF8String normalizedName; */
NS_IMETHODIMP purpleAccountBuddy::GetNormalizedName(nsACString& aNormalizedName)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- aNormalizedName = purple_normalize(purple_buddy_get_account(mBuddy),
- purple_buddy_get_name(mBuddy));
- return NS_OK;
-}
-
-/* readonly attribute AUTF8String loggedIn */
-NS_IMETHODIMP purpleAccountBuddy::GetLoggedIn(nsACString& aLoggedIn)
-{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
-
- PurplePresence *presence = purple_buddy_get_presence(mBuddy);
- time_t loginTime = purple_presence_get_login_time(presence);
-
- if ((PURPLE_BUDDY_IS_ONLINE(mBuddy)) && (loginTime > 0)) {
- time_t now = time(NULL);
-
- if (loginTime > now)
- aLoggedIn = purple_date_format_long(localtime(&loginTime));
- else {
- // returns a copy - we need to free it
- char *tmp = purple_str_seconds_to_string(now - loginTime);
- aLoggedIn = tmp;
- g_free(tmp);
- }
- }
- else {
- // didn't find a valid time - set it to NULL
- aLoggedIn.SetIsVoid(true);
- }
-
+ aNormalizedName = purple_normalize(purple_buddy_get_account(mPurpleBuddy),
+ purple_buddy_get_name(mPurpleBuddy));
return NS_OK;
}
/* readonly attribute AUTF8String buddyIconFilename; */
NS_IMETHODIMP purpleAccountBuddy::GetBuddyIconFilename(nsACString& aBuddyIconFilename)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- PurpleBuddyIcon *icon = purple_buddy_get_icon(mBuddy);
+ PurpleBuddyIcon *icon = purple_buddy_get_icon(mPurpleBuddy);
char *fname;
if (icon && (fname = purple_buddy_icon_get_full_path(icon))) {
aBuddyIconFilename = NS_LITERAL_CSTRING("file://");
aBuddyIconFilename.Append(fname);
g_free(fname);
} else {
// set it to NULL
@@ -322,159 +300,209 @@ NS_IMETHODIMP purpleAccountBuddy::GetBud
}
void purpleAccountBuddy::CleanUserInfo(void *aData)
{
if (aData)
purple_notify_user_info_destroy((PurpleNotifyUserInfo *)aData);
}
-PurplePluginProtocolInfo *purpleAccountBuddy::GetPrplInfo(PurpleBuddy *aBuddy)
-{
- PurpleAccount *pAccount = purple_buddy_get_account(aBuddy);
- PurpleConnection *gc = purple_account_get_connection(pAccount);
- if (!gc)
- return NULL;
-
- purpleAccount *account = purpleAccount::fromPurpleAccount(pAccount);
- NS_ENSURE_TRUE(account, NULL);
-
- return account->GetPrplInfo();
-}
-
/* nsISimpleEnumerator getTooltipInfo(); */
NS_IMETHODIMP purpleAccountBuddy::GetTooltipInfo(nsISimpleEnumerator** aTooltipInfo)
{
NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mAccount, NS_ERROR_NOT_INITIALIZED);
#ifdef PR_LOGGING
nsCString name;
mAccount->GetName(name);
LOG(("purpleAccountBuddy::GetTooltipInfo buddy = %s, account = %s",
- mBuddy->name, name.get()));
+ mPurpleBuddy->name, name.get()));
#endif
*aTooltipInfo = nsnull;
- PurplePluginProtocolInfo *prpl_info = GetPrplInfo(mBuddy);
+
+ purpleAccount *account =
+ purpleAccount::fromPurpleAccount(purple_buddy_get_account(mPurpleBuddy));
+ NS_ENSURE_TRUE(account, NS_ERROR_NOT_INITIALIZED);
+
+ PurplePluginProtocolInfo *prpl_info = account->GetPrplInfo();
if (prpl_info && prpl_info->tooltip_text) {
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
/* Idle */
- PurplePresence *presence = purple_buddy_get_presence(mBuddy);
+ PurplePresence *presence = purple_buddy_get_presence(mPurpleBuddy);
if (purple_presence_is_idle(presence)) {
time_t idle_secs = purple_presence_get_idle_time(presence);
if (idle_secs > 0) {
char *tmp = purple_str_seconds_to_string(time(NULL) - idle_secs);
purple_notify_user_info_add_pair(user_info,
purpleGetText::GetText("purple", "Idle"),
tmp);
g_free(tmp);
}
}
- prpl_info->tooltip_text(mBuddy, user_info, true);
+ prpl_info->tooltip_text(mPurpleBuddy, user_info, true);
purpleGListEnumerator *enumerator = new purpleGListEnumerator();
enumerator->Init(purple_notify_user_info_get_entries(user_info),
purpleTypeToInterface,
CleanUserInfo, user_info);
NS_ADDREF(*aTooltipInfo = enumerator);
}
return NS_OK;
}
-/* readonly attribute AUTF8String buddyAlias; */
-NS_IMETHODIMP purpleAccountBuddy::GetBuddyAlias(nsACString& aBuddyAlias)
+/* readonly attribute AUTF8String displayName; */
+NS_IMETHODIMP purpleAccountBuddy::GetDisplayName(nsACString& aDisplayName)
{
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
+
+ aDisplayName = purple_buddy_get_alias(mPurpleBuddy);
+ return NS_OK;
+}
+
+/* readonly attribute AUTF8String serverAlias; */
+NS_IMETHODIMP purpleAccountBuddy::GetServerAlias(nsACString& aServerAlias)
+{
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
+
+ aServerAlias = purple_buddy_get_alias(mPurpleBuddy);
+ return NS_OK;
+}
+
+void purpleAccountBuddy::SetServerAlias(const nsCString& aServerAlias)
+{
+ purple_blist_alias_buddy(mPurpleBuddy, aServerAlias.get());
+ serv_alias_buddy(mPurpleBuddy);
+ NotifyObservers("account-buddy-display-name-changed"); // FIXME send the old alias
+}
+
+nsresult purpleAccountBuddy::NotifyObservers(const char* aSignal)
+{
+ NS_ENSURE_ARG(aSignal);
NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
- aBuddyAlias = purple_buddy_get_alias(mBuddy);
- return NS_OK;
-}
-void purpleAccountBuddy::SetBuddyAlias(const nsCString& aBuddyAlias)
-{
- purple_blist_alias_buddy(mBuddy, aBuddyAlias.get());
- serv_alias_buddy(mBuddy);
+ return mBuddy->Observe(this, aSignal, nsnull);
}
/* purpleIConversation createConversation (); */
NS_IMETHODIMP purpleAccountBuddy::CreateConversation(purpleIConversation **aResult)
{
- NS_ENSURE_TRUE(mAccount && mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mAccount && mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- nsCString buddyName(purple_buddy_get_name(mBuddy));
+ nsCString buddyName(purple_buddy_get_name(mPurpleBuddy));
return mAccount->CreateConversation(buddyName, aResult);
}
/* readonly attribute boolean canSendMessage; */
NS_IMETHODIMP purpleAccountBuddy::GetCanSendMessage(PRBool *aCanSendMessage)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
*aCanSendMessage =
- purple_presence_is_online(purple_buddy_get_presence(mBuddy)) ||
- purple_account_supports_offline_message(purple_buddy_get_account(mBuddy),
- mBuddy);
+ purple_presence_is_online(purple_buddy_get_presence(mPurpleBuddy)) ||
+ purple_account_supports_offline_message(purple_buddy_get_account(mPurpleBuddy),
+ mPurpleBuddy);
return NS_OK;
}
-#define PURPLE_PRESENCE_GET_BOOL_IMPL(aName, aFctName) \
- NS_IMETHODIMP purpleAccountBuddy::Get##aName(PRBool *a##aName) \
- { \
- NS_ENSURE_TRUE(mAccount && mBuddy, NS_ERROR_NOT_INITIALIZED); \
- \
- *a##aName = \
- purple_account_is_connected(purple_buddy_get_account(mBuddy)) && \
- purple_presence_is_##aFctName(purple_buddy_get_presence(mBuddy)); \
- return NS_OK; \
+/* readonly attribute long statusType; */
+NS_IMETHODIMP purpleAccountBuddy::GetStatusType(PRInt32 *aStatusType)
+{
+ NS_ENSURE_TRUE(mAccount && mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
+
+ if (!purple_account_is_connected(purple_buddy_get_account(mPurpleBuddy))) {
+ *aStatusType = STATUS_UNKNOWN;
+ return NS_OK;
+ }
+
+ PurplePresence *presence = purple_buddy_get_presence(mPurpleBuddy);
+ if (!purple_presence_is_online(presence))
+ *aStatusType = STATUS_OFFLINE;
+ else if (purple_presence_is_idle(presence))
+ *aStatusType = STATUS_IDLE;
+ else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
+ *aStatusType = STATUS_MOBILE;
+ else if (purple_presence_is_available(presence))
+ *aStatusType = STATUS_AVAILABLE;
+ else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) ||
+ purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY))
+ *aStatusType = STATUS_AWAY;
+ else
+ *aStatusType = STATUS_UNAVAILABLE;
+ return NS_OK;
+}
+
+#define PURPLE_PRESENCE_GET_BOOL_IMPL(aName, aFctName) \
+ NS_IMETHODIMP purpleAccountBuddy::Get##aName(PRBool *a##aName) \
+ { \
+ NS_ENSURE_TRUE(mAccount && mPurpleBuddy, NS_ERROR_NOT_INITIALIZED); \
+ \
+ *a##aName = \
+ purple_account_is_connected(purple_buddy_get_account(mPurpleBuddy)) && \
+ purple_presence_is_##aFctName(purple_buddy_get_presence(mPurpleBuddy)); \
+ return NS_OK; \
}
/* readonly attribute boolean online; */
PURPLE_PRESENCE_GET_BOOL_IMPL(Online, online)
/* readonly attribute boolean available; */
PURPLE_PRESENCE_GET_BOOL_IMPL(Available, available)
/* readonly attribute boolean idle; */
PURPLE_PRESENCE_GET_BOOL_IMPL(Idle, idle)
/* readonly attribute boolean mobile; */
NS_IMETHODIMP purpleAccountBuddy::GetMobile(PRBool *aMobile)
{
NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
- *aMobile =
- purple_presence_is_status_primitive_active(purple_buddy_get_presence(mBuddy),
- PURPLE_STATUS_MOBILE);
+ PurplePresence *presence = purple_buddy_get_presence(mPurpleBuddy);
+ *aMobile = purple_presence_is_status_primitive_active(presence,
+ PURPLE_STATUS_MOBILE);
return NS_OK;
}
/* readonly attribute string status; */
-NS_IMETHODIMP purpleAccountBuddy::GetStatus(nsACString &aStatus)
+NS_IMETHODIMP purpleAccountBuddy::GetStatusText(nsACString &aStatusText)
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- aStatus.Truncate();
- PurplePluginProtocolInfo *prpl_info = GetPrplInfo(mBuddy);
+ aStatusText.Truncate();
+
+ purpleAccount *account =
+ purpleAccount::fromPurpleAccount(purple_buddy_get_account(mPurpleBuddy));
+ NS_ENSURE_TRUE(account, NS_ERROR_NOT_INITIALIZED);
+
+ PurplePluginProtocolInfo *prpl_info = account->GetPrplInfo();
if (prpl_info && prpl_info->status_text) {
- char *tmp1 = prpl_info->status_text(mBuddy);
+ char *tmp1 = prpl_info->status_text(mPurpleBuddy);
char *tmp2 = purple_unescape_html(tmp1);
- aStatus = tmp2;
+ aStatusText = tmp2;
g_free(tmp1);
g_free(tmp2);
}
return NS_OK;
}
+/* readonly attribute long availabilityDetails; */
+NS_IMETHODIMP purpleAccountBuddy::GetAvailabilityDetails(PRInt32 *aAvailabilityDetails)
+{
+ *aAvailabilityDetails = 0;
+ return NS_OK;
+}
+
/* void remove (); */
NS_IMETHODIMP purpleAccountBuddy::Remove()
{
- NS_ENSURE_TRUE(mBuddy, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_TRUE(mPurpleBuddy, NS_ERROR_NOT_INITIALIZED);
- purple_account_remove_buddy(mBuddy->account, mBuddy,
- purple_buddy_get_group(mBuddy));
- purple_blist_remove_buddy(mBuddy);
+ purple_account_remove_buddy(mPurpleBuddy->account, mPurpleBuddy,
+ purple_buddy_get_group(mPurpleBuddy));
+ purple_blist_remove_buddy(mPurpleBuddy);
return NS_OK;
}
diff --git a/purple/purplexpcom/src/purpleAccountBuddy.h b/purple/purplexpcom/src/purpleAccountBuddy.h
--- a/purple/purplexpcom/src/purpleAccountBuddy.h
+++ b/purple/purplexpcom/src/purpleAccountBuddy.h
@@ -33,54 +33,50 @@
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef PURPLE_ACCOUNT_BUDDY_H_
# define PURPLE_ACCOUNT_BUDDY_H_
-#include "purpleIAccountBuddy.h"
+#include "imIContactsService.h"
#include "purpleTag.h"
#include
-// af152d5b-fa54-45d4-b6a7-5b4cabef9b7c
#define PURPLE_ACCOUNTBUDDY_CID \
{ 0xaf152d5b, 0xfa54, 0x45d4, \
{ 0xb6, 0xa7, 0x5b, 0x4c, 0xab, 0xef, 0x9b, 0x7c } \
}
-#define PURPLE_ACCOUNTBUDDY_CONTRACTID \
- "@instantbird.org/purple/accountbuddy;1"
-
#define GET_NODE_UI_DATA(aNode) \
purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(aNode))
-class purpleAccountBuddy : public purpleIAccountBuddy
+class purpleAccountBuddy : public imIAccountBuddy
{
public:
NS_DECL_ISUPPORTS
- NS_DECL_PURPLEIACCOUNTBUDDY
+ NS_DECL_IMIACCOUNTBUDDY
+ NS_DECL_IMISTATUSINFO
- purpleAccountBuddy();
- void Init(purpleIAccount *aAccount, PurpleBuddy *aBuddy, purpleTag *aTag);
+ purpleAccountBuddy(PurpleBuddy *aPurpleBuddy, imIBuddy *aBuddy);
+ // void Init(purpleIAccount *aAccount, PurpleBuddy *aBuddy, purpleTag *aTag);
void Uninit();
- nsresult Store(PRInt32 aBuddyId);
- nsresult UnStore(PRInt32 aBuddyId);
- PurpleBuddy *GetBuddy() { return mBuddy; }
- void SetBuddyAlias(const nsCString& aBuddyAlias);
+ // nsresult Store(PRInt32 aBuddyId);
+ // nsresult UnStore(PRInt32 aBuddyId);
+ void SetServerAlias(const nsCString& aBuddyAlias);
static inline purpleAccountBuddy *fromPurpleBuddy(PurpleBuddy *aBuddy) {
return static_cast(GET_NODE_UI_DATA(aBuddy));
}
+ nsresult NotifyObservers(const char *aSignal);
private:
~purpleAccountBuddy();
static void CleanUserInfo(void *aData);
- PurplePluginProtocolInfo *GetPrplInfo(PurpleBuddy *aBuddy);
protected:
/* additional members */
nsCOMPtr mAccount;
- PurpleBuddy *mBuddy;
- nsCOMPtr mTag;
+ PurpleBuddy *mPurpleBuddy;
+ nsCOMPtr mBuddy;
};
#endif /* !PURPLE_ACCOUNT_BUDDY_H_ */
diff --git a/purple/purplexpcom/src/purpleConvIM.cpp b/purple/purplexpcom/src/purpleConvIM.cpp
--- a/purple/purplexpcom/src/purpleConvIM.cpp
+++ b/purple/purplexpcom/src/purpleConvIM.cpp
@@ -31,17 +31,17 @@
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "purpleConvIM.h"
-#include "purpleIAccountBuddy.h"
+#include "imIContactsService.h"
#include "purpleCoreService.h"
#include
#include
#include
#include
#include
#include
@@ -115,30 +115,30 @@ NS_IMETHODIMP purpleConvIM::SendMsg(cons
rv = os->NotifyObservers(static_cast(this), "im-sent",
NS_ConvertUTF8toUTF16(aMsg).get());
NS_ENSURE_SUCCESS(rv, rv);
mSentTyping = PR_FALSE;
return NS_OK;
}
-/* readonly attribute purpleIAccountBuddy buddy; */
-NS_IMETHODIMP purpleConvIM::GetBuddy(purpleIAccountBuddy * *aBuddy)
+/* readonly attribute imIAccountBuddy buddy; */
+NS_IMETHODIMP purpleConvIM::GetBuddy(imIAccountBuddy * *aBuddy)
{
NS_ENSURE_TRUE(mConv, NS_ERROR_NOT_INITIALIZED);
PurpleBuddy *buddy =
purple_find_buddy(purple_conversation_get_account(mConv),
purple_conversation_get_name(mConv));
if (!buddy) {
*aBuddy = nsnull;
return NS_OK;
}
- purpleIAccountBuddy *pab = purpleAccountBuddy::fromPurpleBuddy(buddy);
+ imIAccountBuddy *pab = purpleAccountBuddy::fromPurpleBuddy(buddy);
NS_ENSURE_TRUE(pab, NS_ERROR_NOT_INITIALIZED);
NS_ADDREF(*aBuddy = pab);
return NS_OK;
}
/* readonly attribute short typingState; */
NS_IMETHODIMP purpleConvIM::GetTypingState(PRInt16 *aTypingState)
diff --git a/purple/purplexpcom/src/purpleCoreService.cpp b/purple/purplexpcom/src/purpleCoreService.cpp
--- a/purple/purplexpcom/src/purpleCoreService.cpp
+++ b/purple/purplexpcom/src/purpleCoreService.cpp
@@ -39,16 +39,17 @@
#include "purpleAccount.h"
#include "purpleDNS.h"
#include "purpleGetText.h"
#include "purpleNetwork.h"
#include "purpleStorage.h"
#include "purpleSockets.h"
#include "purpleTagsService.h"
#include "purpleTimer.h"
+#include "imIContactsService.h"
#pragma GCC visibility push(default)
#include
#include
#pragma GCC visibility pop
#include
#include
@@ -87,16 +88,17 @@ static PRLogModuleInfo *gPurpleCoreServi
NS_IMPL_CLASSINFO(purpleCoreService, NULL, nsIClassInfo::SINGLETON,
PURPLE_CORE_SERVICE_CID)
NS_IMPL_ISUPPORTS3_CI(purpleCoreService, purpleICoreService,
nsIObserver, purpleIAccountsService)
extern nsresult init_libpurple();
extern void connect_to_blist_signals();
+extern void disconnect_blist_signals();
purpleCoreService::purpleCoreService()
:mInitialized(PR_FALSE),
mQuitting(PR_FALSE),
mAutoLoginStatus(AUTOLOGIN_ENABLED),
mProtocols(nsnull),
mProxies(nsnull),
mConversations(nsnull)
@@ -211,22 +213,24 @@ NS_IMETHODIMP purpleCoreService::Init()
/* Load saved accounts */
mAccountsService = new purpleAccountsService();
NS_ENSURE_TRUE(mAccountsService, NS_ERROR_OUT_OF_MEMORY);
rv = mAccountsService->InitAccounts();
NS_ENSURE_SUCCESS(rv, rv);
+#if 0
/* Load the tags */
mTagsService = do_GetService(PURPLE_TAGS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(mTagsService, NS_ERROR_OUT_OF_MEMORY);
rv = mTagsService->InitTags();
NS_ENSURE_SUCCESS(rv, rv);
+#endif
/* Load the buddy list from mozStorage */
InitBuddyList();
/* Check if we should disable auto-login */
InitAutoLoginStatus();
/* If we are not offline, set the initial status to available */
@@ -406,19 +410,27 @@ nsresult purpleCoreService::GetLastCrash
PR_Close(fd);
*aLastCrashTime = strtol(data.get(), NULL, 10);
return NS_OK;
}
#endif
nsresult purpleCoreService::InitBuddyList()
{
+ nsCOMPtr contacts =
+ do_GetService("@instantbird.org/purple/contacts-service;1");
+ NS_ENSURE_TRUE(contacts, NS_ERROR_UNEXPECTED);
+
+ nsresult rv = contacts->InitContacts();
+ NS_ENSURE_SUCCESS(rv, rv);
+
/* The only effect is to set blist_loaded to TRUE */
purple_blist_load();
+#if 0
/* Now, let's read the real data from mozStorage */
purpleStorage *storageInstance = purpleStorage::GetInstance();
NS_ENSURE_TRUE(storageInstance, NS_ERROR_OUT_OF_MEMORY);
mozIStorageConnection *DBConn = storageInstance->GetConnection();
nsCOMPtr statement;
nsresult rv = DBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT buddies.id, name, srv_alias, alias, tag_id, account_id FROM buddies"
@@ -480,16 +492,17 @@ nsresult purpleCoreService::InitBuddyLis
LOG(("Found purpleBuddy with id %i in HashTable", id));
}
rv = account->LoadBuddy(purpleBuddy, name, alias, serverAlias, tag);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to Load Buddy");
}
}
+#endif
connect_to_blist_signals();
return NS_OK;
}
void purpleCoreService::InitProtocols()
{
LOG(("Start init protocols"));
@@ -583,20 +596,39 @@ NS_IMETHODIMP purpleCoreService::Quit()
os->NotifyObservers(static_cast(this),
"purple-quit", nsnull);
for (PRInt32 i = mConversations.Count() - 1; i >= 0; --i) {
mConversations[i]->UnInit();
}
mConversations.Clear();
+ disconnect_blist_signals();
mAccountsService->UnInitAccounts();
mAccountsService = nsnull;
+#if 0
mTagsService->UnInitTags();
mTagsService = nsnull;
+#endif
+ nsCOMPtr contacts =
+ do_GetService("@instantbird.org/purple/contacts-service;1");
+ if (contacts)
+ contacts->UnInitContacts();
+
+ PurpleBlistNode *gnode;
+ for (gnode = purple_blist_get_root(); gnode; gnode = gnode->next) {
+ if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ continue;
+ void *ui_data = purple_blist_node_get_ui_data(gnode);
+ if (ui_data) {
+ imITag *tag = static_cast(ui_data);
+ NS_RELEASE(tag);
+ }
+ }
+
mProtocols.Clear();
mProxies.Clear();
mBuddiesById.Clear();
mInitialized = PR_FALSE;
purple_core_quit();
LOG(("purple_core_quit"));
purpleStorage::unInit();
purpleSocketWatcher::unInit();
@@ -629,16 +661,18 @@ NS_IMETHODIMP purpleCoreService::GetBudd
*aResult = buddyPtr;
LOG(("purpleBuddy found in HashTable (by Id)"));
return NS_OK;
}
/* [noscript] void purpleBuddyAdded (in PurpleNativeBuddy aBuddy); */
NS_IMETHODIMP purpleCoreService::PurpleBuddyAdded(PurpleBuddy *aBuddy)
{
+ return NS_ERROR_NOT_IMPLEMENTED;
+#if 0
/* Try to get the Id from mozStorage. If we find it, append the new
* PurpleBuddy to it, and add the new PurpleBuddy pointer to the hash.
*/
purpleStorage *storageInstance = purpleStorage::GetInstance();
mozIStorageStatement *statement = storageInstance->mGetBuddyIdFromNameAndPrpl;
mozStorageStatementScoper scoper(statement);
nsCString name(purple_normalize(aBuddy->account, aBuddy->name));
nsresult rv = statement->BindUTF8StringParameter(0, name);
@@ -675,24 +709,27 @@ NS_IMETHODIMP purpleCoreService::PurpleB
* hash.
*/
nsCOMPtr buddy = do_CreateInstance(PURPLE_BUDDY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
buddy->Init(aBuddy);
LOG(("purpleBuddy added in HashTable with Id %i", buddy->getId()));
mBuddiesById.Put(buddy->getId(), buddy);
return NS_OK;
+#endif
}
/* [noscript] void purpleBuddyRemoved (in PurpleNativeBuddy aBuddy); */
NS_IMETHODIMP purpleCoreService::PurpleBuddyRemoved(PurpleBuddy *aBuddy)
{
if (mQuitting)
return NS_OK;
+ return NS_ERROR_NOT_IMPLEMENTED;
+#if 0
purpleBuddy *buddy = purpleBuddy::fromPurpleBuddy(aBuddy);
NS_ENSURE_TRUE(buddy, NS_ERROR_FAILURE);
nsresult rv = buddy->RemoveAccount(aBuddy);
NS_ENSURE_SUCCESS(rv, rv);
if (!buddy->GetAccountCount()) {
// If we removed the last account, we need to remove the whole purpleBuddy
@@ -700,16 +737,17 @@ NS_IMETHODIMP purpleCoreService::PurpleB
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 id = buddy->getId();
buddy->UnInit();
mBuddiesById.Remove(id);
}
return NS_OK;
+#endif
}
/* nsISimpleEnumerator getConversations (); */
NS_IMETHODIMP purpleCoreService::GetConversations(nsISimpleEnumerator **aResult)
{
PURPLE_ENSURE_INIT(mInitialized);
return NS_NewArrayEnumerator(aResult, mConversations);
diff --git a/purple/purplexpcom/src/purpleInit.cpp b/purple/purplexpcom/src/purpleInit.cpp
--- a/purple/purplexpcom/src/purpleInit.cpp
+++ b/purple/purplexpcom/src/purpleInit.cpp
@@ -511,40 +511,46 @@ static PurpleConversationUiOps conversat
static void buddy_signals(PurpleBuddy *aBuddy, const char *aSignal)
{
// FIXME when this was handled by purpleCoreService, signals sent
// while mQuitting was true were ignored.
LOG(("Attempting to send %s signal, group = %s, buddy = %s", aSignal,
purple_group_get_name(purple_buddy_get_group(aBuddy)), aBuddy->name));
- nsCOMPtr pab =
+ nsCOMPtr pab =
purpleAccountBuddy::fromPurpleBuddy(aBuddy);
NS_ENSURE_TRUE(pab, );
- // FIXME: all this should be moved inside purpleAccountBuddy / purpleTag
- nsCOMPtr tag;
- nsresult rv = pab->GetTag(getter_AddRefs(tag));
- if (NS_SUCCEEDED(rv))
- tag->NotifyObservers(pab, aSignal, nsnull);
-
- nsCOMPtr os =
- do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ nsresult rv = pab->NotifyObservers(aSignal);
NS_ENSURE_SUCCESS(rv, );
- os->NotifyObservers(pab, aSignal, nsnull);
}
#define PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER(aSignal, aHandler) \
purple_signal_connect(instance, aSignal, &handle, \
PURPLE_CALLBACK(aHandler), \
(void *)aSignal)
#define PURPLE_CONNECT_BUDDY_SIGNAL(aSignal) \
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER(aSignal, buddy_signals)
+static void buddy_signed_on(PurpleBuddy *aBuddy, const char *aSignal)
+{
+ buddy_signals(aBuddy, "account-buddy-availability-changed");
+ buddy_signals(aBuddy, "account-buddy-status-changed");
+ buddy_signals(aBuddy, "account-buddy-signed-on");
+}
+
+static void buddy_signed_off(PurpleBuddy *aBuddy, const char *aSignal)
+{
+ buddy_signals(aBuddy, "account-buddy-availability-changed");
+ buddy_signals(aBuddy, "account-buddy-status-changed");
+ buddy_signals(aBuddy, "account-buddy-signed-off");
+}
+
static void buddy_added(PurpleBuddy *buddy, void *null)
{
// This is the buddy-added purple signal. It is fired when a buddy
// is added to the list or to a group.
// FIXME what should we do if the buddy is moved to a new group. Is
// ui_data already set in this case?
@@ -568,73 +574,96 @@ static void buddy_removed(PurpleBuddy *b
nsCOMPtr pcs =
do_GetService(PURPLE_CORE_SERVICE_CONTRACTID);
pcs->PurpleBuddyRemoved(buddy);
}
static void buddy_away(PurpleBuddy *aBuddy,
PurpleStatus *old_status, PurpleStatus *status)
{
- buddy_signals(aBuddy, "buddy-away");
+ buddy_signals(aBuddy, "account-buddy-availability-changed");
+ buddy_signals(aBuddy, "account-buddy-status-changed");
}
static void buddy_idle(PurpleBuddy *aBuddy, gboolean old_idle, gboolean idle)
{
- buddy_signals(aBuddy, "buddy-idle");
+ buddy_signals(aBuddy, "account-buddy-availability-changed");
+ buddy_signals(aBuddy, "account-buddy-status-changed");
}
static void buddy_alias(PurpleBlistNode *aNode, const char *old_alias)
{
// We are only interested by buddy aliases
if (!PURPLE_BLIST_NODE_IS_BUDDY(aNode))
return;
- PurpleBuddy *buddy = (PurpleBuddy *)aNode;
- buddy_signals(buddy, "buddy-alias");
+ purpleAccountBuddy *pab =
+ purpleAccountBuddy::fromPurpleBuddy((PurpleBuddy *)aNode);
+ if (!pab)
+ return; // it's fine to ignore these notifications for unknown buddies
+ //XXX is this comment still valid?
- purpleBuddy *buddyPtr = purpleBuddy::fromPurpleBuddy(buddy);
- if (!buddyPtr)
- return; // it's fine to ignore these notifications for unknown buddies
- buddyPtr->UpdateServerAlias(purple_buddy_get_server_alias(buddy));
+ pab->NotifyObservers("account-buddy-display-name-changed");
}
+/*
+ * Used as handle to connect and then mass-disconnect the buddy list signals.
+ * The value is 1 when the signals are connected, 0 when they are not.
+ */
+static int blist_signals_handle = 0;
+
void
connect_to_blist_signals()
{
- int handle;
+#define handle blist_signals_handle
void *instance = purple_blist_get_handle();
- PURPLE_CONNECT_BUDDY_SIGNAL("buddy-signed-on");
- PURPLE_CONNECT_BUDDY_SIGNAL("buddy-signed-off");
+ PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-signed-on", buddy_signed_on);
+ PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-signed-off", buddy_signed_off);
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-added", buddy_added);
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-removed", buddy_removed);
// This buddy-removed-from-group purple signal was added for
// Instantbird. It is fired when a buddy is removed permanenty from
// a group but not from the buddy list. (ie when the buddy has been
// moved)
PURPLE_CONNECT_BUDDY_SIGNAL("buddy-removed-from-group");
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-status-changed", buddy_away);
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("buddy-idle-changed", buddy_idle);
PURPLE_CONNECT_BUDDY_SIGNAL_HANDLER("blist-node-aliased", buddy_alias);
- LOG(("Connecting to blist signals"));
+#undef handle
+ blist_signals_handle = 1;
+ LOG(("Connected to blist signals"));
+}
+
+void
+disconnect_blist_signals()
+{
+ purple_signals_disconnect_by_handle(&blist_signals_handle);
+ blist_signals_handle = 0;
}
static void remove_blist_node(PurpleBuddyList *aList, PurpleBlistNode *aNode)
{
+ // Ignore this ui ops if buddy list signals are not connected:
+ if (!blist_signals_handle)
+ return;
+
// This is the removed blist uiop. It is fired when a possibly
// visible element of the buddy list is removed (because the account
// got disconnected)
// For now, we are only interested by buddy removal
if (!PURPLE_BLIST_NODE_IS_BUDDY(aNode))
return;
PurpleBuddy *buddy = (PurpleBuddy *) aNode;
LOG(("purple uiop : remove_blist_node, name = %s", buddy->name));
- buddy_signals(buddy, "buddy-removed");
+ // buddy_signals(buddy, "buddy-removed");
+ buddy_signals(buddy, "account-buddy-availability-changed");
+ buddy_signals(buddy, "account-buddy-status-changed");
}
static PurpleBlistUiOps blist_uiops = {
NULL, /* new_list */
NULL, /* new_node FIXME: should use it */
NULL, /* show */
NULL, /* update */
remove_blist_node, /* remove */
diff --git a/purple/purplexpcom/src/purpleModule.cpp b/purple/purplexpcom/src/purpleModule.cpp
--- a/purple/purplexpcom/src/purpleModule.cpp
+++ b/purple/purplexpcom/src/purpleModule.cpp
@@ -40,65 +40,57 @@
#include "nsIClassInfoImpl.h"
#include "nsServiceManagerUtils.h"
#include "purpleCoreService.h"
#include "purpleConvIM.h"
#include "purpleConvChat.h"
#include "purpleProtocol.h"
#include "purpleUnknownProtocol.h"
#include "purpleAccountBase.h"
-#include "purpleBuddy.h"
#include "purpleTag.h"
#include "purpleTagsService.h"
-#include "purpleAccountBuddy.h"
#include "purpleMessage.h"
#include "purpleProxy.h"
#include "purpleProxyInfo.h"
#ifdef XP_MACOSX
# include "nsDockTile.h"
#endif
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleAccountBase)
-NS_GENERIC_FACTORY_CONSTRUCTOR(purpleAccountBuddy)
-NS_GENERIC_FACTORY_CONSTRUCTOR(purpleBuddy)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleConvChat)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleConvIM)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleCoreService)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleMessage)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleProtocol)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleProxy)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleProxyInfo)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleTag)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleTagsService)
NS_GENERIC_FACTORY_CONSTRUCTOR(purpleUnknownProtocol)
#ifdef XP_MACOSX
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDockTile)
#endif
NS_DEFINE_NAMED_CID(PURPLE_ACCOUNT_CID);
-NS_DEFINE_NAMED_CID(PURPLE_ACCOUNTBUDDY_CID);
-NS_DEFINE_NAMED_CID(PURPLE_BUDDY_CID);
NS_DEFINE_NAMED_CID(PURPLE_CONV_CHAT_CID);
NS_DEFINE_NAMED_CID(PURPLE_CONV_IM_CID);
NS_DEFINE_NAMED_CID(PURPLE_CORE_SERVICE_CID);
NS_DEFINE_NAMED_CID(PURPLE_MESSAGE_CID);
NS_DEFINE_NAMED_CID(PURPLE_PROTOCOL_CID);
NS_DEFINE_NAMED_CID(PURPLE_PROXY_CID);
NS_DEFINE_NAMED_CID(PURPLE_PROXY_INFO_CID);
NS_DEFINE_NAMED_CID(PURPLE_TAG_CID);
NS_DEFINE_NAMED_CID(PURPLE_TAGS_SERVICE_CID);
NS_DEFINE_NAMED_CID(PURPLE_UNKNOWN_PROTOCOL_CID);
#ifdef XP_MACOSX
NS_DEFINE_NAMED_CID(NSDOCKBADGESERVICE_CID);
#endif
static const mozilla::Module::CIDEntry kPurpleCIDs[] = {
{ &kPURPLE_ACCOUNT_CID, false, NULL, purpleAccountBaseConstructor },
- { &kPURPLE_ACCOUNTBUDDY_CID, false, NULL, purpleAccountBuddyConstructor },
- { &kPURPLE_BUDDY_CID, false, NULL, purpleBuddyConstructor },
{ &kPURPLE_CONV_CHAT_CID, false, NULL, purpleConvChatConstructor },
{ &kPURPLE_CONV_IM_CID, false, NULL, purpleConvIMConstructor },
{ &kPURPLE_CORE_SERVICE_CID, true, NULL, purpleCoreServiceConstructor },
{ &kPURPLE_MESSAGE_CID, false, NULL, purpleMessageConstructor },
{ &kPURPLE_PROTOCOL_CID, false, NULL, purpleProtocolConstructor },
{ &kPURPLE_PROXY_CID, false, NULL, purpleProxyConstructor },
{ &kPURPLE_PROXY_INFO_CID, false, NULL, purpleProxyInfoConstructor },
{ &kPURPLE_TAG_CID, false, NULL, purpleTagConstructor },
@@ -108,18 +100,16 @@ static const mozilla::Module::CIDEntry k
#ifdef XP_MACOSX
{ &kNSDOCKBADGESERVICE_CID, true, NULL, nsDockTileConstructor },
#endif
{ NULL }
};
static const mozilla::Module::ContractIDEntry kPurpleContracts[] = {
{ PURPLE_ACCOUNT_CONTRACTID, &kPURPLE_ACCOUNT_CID },
- { PURPLE_ACCOUNTBUDDY_CONTRACTID, &kPURPLE_ACCOUNTBUDDY_CID },
- { PURPLE_BUDDY_CONTRACTID, &kPURPLE_BUDDY_CID },
{ PURPLE_CONV_CHAT_CONTRACTID, &kPURPLE_CONV_CHAT_CID },
{ PURPLE_CONV_IM_CONTRACTID, &kPURPLE_CONV_IM_CID },
{ PURPLE_CORE_SERVICE_CONTRACTID, &kPURPLE_CORE_SERVICE_CID },
{ PURPLE_MESSAGE_CONTRACTID, &kPURPLE_MESSAGE_CID },
{ PURPLE_PROTOCOL_CONTRACTID, &kPURPLE_PROTOCOL_CID },
{ PURPLE_PROXY_CONTRACTID, &kPURPLE_PROXY_CID },
{ PURPLE_PROXY_INFO_CONTRACTID, &kPURPLE_PROXY_INFO_CID },
{ PURPLE_TAG_CONTRACTID, &kPURPLE_TAG_CID },