diff --git a/im/content/blist.js b/im/content/blist.js index e4e5d7f93f..df11d500b5 100644 --- a/im/content/blist.js +++ b/im/content/blist.js @@ -459,6 +459,25 @@ var buddyList = { return; } + let status = { + back: "AVAILABLE", + away: "AWAY", + busy: "UNAVAILABLE", + dnd: "UNAVAILABLE", + offline: "OFFLINE" + }; + for (let cmd in status) { + let statusValue = Ci.purpleICoreService["STATUS_" + status[cmd]]; + Services.cmd.registerCommand({ + name: cmd, + priority: Ci.imICommand.PRIORITY_HIGH, + run: function(aMsg) { + Services.core.setStatus(statusValue, aMsg); + return true; + } + }); + } + // TODO remove this once we cleanup the way the menus are inserted let menubar = document.getElementById("blistMenubar"); let statusArea = document.getElementById("statusArea"); diff --git a/im/content/conversation.xml b/im/content/conversation.xml index ba99713e45..78efbb7f81 100644 --- a/im/content/conversation.xml +++ b/im/content/conversation.xml @@ -258,14 +258,12 @@ // the /say command is used to bypass command processing var msg = aMsg.match(/^\/say (.*)/); if (!msg) { - try { - if (this._conv.doCommand(aMsg)) { - if (!this._conv.isChat) - this._conv.sendTyping(0); - this.resetInput(); - return; - } - } catch(e) { } + if (Services.cmd.executeCommand(aMsg, this._conv)) { + if (!this._conv.isChat) + this._conv.sendTyping(0); + this.resetInput(); + return; + } } else aMsg = msg[1]; diff --git a/im/modules/imServices.jsm b/im/modules/imServices.jsm index 73789f4544..811adc612e 100644 --- a/im/modules/imServices.jsm +++ b/im/modules/imServices.jsm @@ -43,6 +43,9 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyServiceGetter(Services, "core", "@instantbird.org/purple/core;1", "purpleICoreService"); +XPCOMUtils.defineLazyServiceGetter(Services, "cmd", + "@instantbird.org/purple/commands-service;1", + "imICommandsService"); XPCOMUtils.defineLazyServiceGetter(Services, "tags", "@instantbird.org/purple/tags-service;1", "imITagsService"); diff --git a/purple/purplexpcom/public/imICommandsService.idl b/purple/purplexpcom/public/imICommandsService.idl new file mode 100644 index 0000000000..16d078833c --- /dev/null +++ b/purple/purplexpcom/public/imICommandsService.idl @@ -0,0 +1,94 @@ +/* ***** 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 + * 2011. + * + * The Initial Developer of the Original Code is + * Florian QUEZE . + * Portions created by the Initial Developer are Copyright (C) 2011 + * 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" +interface purpleIConversation; + +[scriptable, uuid(dae17fae-0d04-4d89-a817-637ef433383f)] +interface imICommand: nsISupports { + readonly attribute AUTF8String name; + readonly attribute AUTF8String helpString; + + const short PRIORITY_LOW = -1000; + const short PRIORITY_DEFAULT = 0; + const short PRIORITY_PRPL = 1000; + const short PRIORITY_HIGH = 4000; + // Any integer value is usable as a priority. + // 0 is the default priority. + // < 0 is lower priority. + // > 0 is higher priority. + // Commands registered by protocol plugins will usually use PRIORITY_PRPL. + readonly attribute PRInt32 priority; + + // Will return true if the command handled the message (it should not be sent). + // The leading slash, the command name and the following space are not included + // in the aMessage parameter. + boolean run(in AUTF8String aMessage, + [optional] in purpleIConversation aConversation); +}; + +[scriptable, uuid(467709a0-0bed-4f44-9bdc-13f78b9eaeba)] +interface imICommandsService: nsISupports { + void initCommands(); + void unInitCommands(); + + // Commands registered without a protocol id will work for all protocols. + // Registering several commands of the same name with the same + // protocol id or no protocol id will replace the former command + // with the latter. + void registerCommand(in imICommand aCommand, + [optional] in AUTF8String aPrplId); + + // aPrplId should be the same as what was used for the command registration. + void unregisterCommand(in AUTF8String aCommandName, + [optional] in AUTF8String aPrplId); + + void listCommands([optional] in purpleIConversation aConversation, + [optional] out unsigned long commandCount, + [retval, array, size_is(commandCount)] out imICommand commands); + + // Will return true if a command handled the message (it should not be sent). + // The aConversation parameters is required to execute protocol specific + // commands. Application global commands will work without it. + boolean executeCommand(in AUTF8String aMessage, + [optional] in purpleIConversation aConversation); +}; + +%{ C++ +#define IM_COMMANDS_SERVICE_CONTRACTID \ + "@instantbird.org/purple/commands-service;1" +%} diff --git a/purple/purplexpcom/public/purpleIConversation.idl b/purple/purplexpcom/public/purpleIConversation.idl index 95dcdcf8e6..43fc75e1c4 100644 --- a/purple/purplexpcom/public/purpleIConversation.idl +++ b/purple/purplexpcom/public/purpleIConversation.idl @@ -74,20 +74,6 @@ interface purpleIConversation: nsISupports { /* Send a message in the conversation */ void sendMsg(in AUTF8String aMsg); - /* Handle commands from a potential message. - - Will return true if the message as been handled as a command and - should not be sent as a message, false if the message should be sent. - - Will fail (NS_ERROR_FAILURE) if the message starts with / which - is the native command character of the protocol but is not a - valid command. - - Will also fail (NS_ERROR_FAILURE) if a valid command had wrong - arguments or failed to execute for any other reason. - */ - boolean doCommand(in AUTF8String aMsg); - /* Un-initialize the conversation. Will be called by purpleCoreService::RemoveConversation when the conversation is closed or by purpleCoreService::Quit while exiting. */ diff --git a/purple/purplexpcom/src/imCommands.js b/purple/purplexpcom/src/imCommands.js new file mode 100644 index 0000000000..71532fb18e --- /dev/null +++ b/purple/purplexpcom/src/imCommands.js @@ -0,0 +1,133 @@ +/* ***** 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 + * 2011. + * + * The Initial Developer of the Original Code is + * Florian QUEZE . + * Portions created by the Initial Developer are Copyright (C) 2011 + * 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; +const Cr = Components.results; + +XPCOMUtils.defineLazyServiceGetter(this, "obs", + "@mozilla.org/observer-service;1", + "nsIObserverService"); + +function CommandsService() { } +CommandsService.prototype = { + initCommands: function() { + this._commands = {}; + this.registerCommand({ + name: "raw", + priority: Ci.imICommand.PRIORITY_DEFAULT, + run: function(aMsg, aConv) { + aConv.sendMsg(aMsg); + return true; + } + }); + }, + unInitCommands: function() { + delete this._commands; + }, + + registerCommand: function(aCommand, aPrplId) { + let name = aCommand.name; + if (!name) + throw Cr.NS_ERROR_INVALID_ARG; + + if (!(this._commands.hasOwnProperty(name))) + this._commands[name] = {}; + this._commands[name][aPrplId || ""] = aCommand; + }, + unregisterCommand: function(aCommandName, aPrplId) { + if (this._commands.hasOwnProperty(aCommandName)) { + let prplId = aPrplId || ""; + let commands = this._commands[aCommandName]; + if (commands.hasOwnProperty(aPrplId)) + delete commands[aPrplId]; + if (!Object.keys(commands).length) + delete this._commands[aCommandName]; + } + }, + listCommands: function(aConversation, commandCount) { + let result = []; + let prplId = aConversation && aConversation.account.protocol.id; + for (let name in this._commands) { + let commands = this._commands[name]; + if (commands.hasOwnProperty("")) + result.push(commands[""]); + if (prplId && commands.hasOwnProperty(prplId)) + result.push(commands[prplId]); + } + commandCount.value = result.length; + return result; + }, + executeCommand: function (aMessage, aConversation) { + if (!aMessage) + throw Cr.NS_ERROR_INVALID_ARG; + + let matchResult; + if (aMessage[0] != "/" || + !(matchResult = /^\/([a-z]+)(?: |$)([\s\S]*)/(aMessage))) + return false; + + let [, name, args] = matchResult; + if (!(this._commands.hasOwnProperty(name))) + return false; + + // Get the 2 possible commands (the global and the proto specific) + let cmdArray = []; + let commands = this._commands[name]; + if (commands.hasOwnProperty("")) + cmdArray.push(commands[""]); + + if (aConversation) { + let prplId = aConversation.account.protocol.id; + if (commands.hasOwnProperty(prplId)) + cmdArray.push(commands[prplId]); + } + + // Attempt to apply them in order until one succeeds. + return cmdArray.sort(function(a, b) b.priority - a.priority) + .some(function (aCmd) aCmd.run(args, aConversation)); + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.imICommandsService]), + classDescription: "Commands", + classID: Components.ID("{7cb20c68-ccc8-4a79-b6f1-0b4771ed6c23}"), + contractID: "@instantbird.org/purple/commands-service;1" +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandsService]); diff --git a/purple/purplexpcom/src/imCommands.manifest b/purple/purplexpcom/src/imCommands.manifest new file mode 100644 index 0000000000..726eaafcf6 --- /dev/null +++ b/purple/purplexpcom/src/imCommands.manifest @@ -0,0 +1,2 @@ +component {7cb20c68-ccc8-4a79-b6f1-0b4771ed6c23} imCommands.js +contract @instantbird.org/purple/commands-service;1 {7cb20c68-ccc8-4a79-b6f1-0b4771ed6c23} diff --git a/purple/purplexpcom/src/jsProtoHelper.jsm b/purple/purplexpcom/src/jsProtoHelper.jsm index 12d6cb74c8..409bae338c 100644 --- a/purple/purplexpcom/src/jsProtoHelper.jsm +++ b/purple/purplexpcom/src/jsProtoHelper.jsm @@ -469,7 +469,6 @@ const GenericConversationPrototype = { observer.observe(aSubject, aTopic, aData); }, - doCommand: function(aMsg) false, sendMsg: function (aMsg) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },