I'm playing with the thunderbird codebase, the aim being to implement inline contact editing. The current code catches the Click
event on a XUL
tree, and if it's a double click (events.detail == 2
), it open the profile editor. I modified it so as to start editing the current treeCell
, and I did add editable=true
to the corresponding XUL
document. The updated code reads
var orow = {}, ocolumn = {}, opart = {};
gAbResultsTree.treeBoxObject.getCellAt(event.clientX, event.clientY,
orow, ocolumn, opart);
var row = orow.value, column = ocolumn.value.index;
if (row == -1)
return;
if (event.detail == 2)
gAbResultsTree.startEditing(row, column);
Unfortunately, when the code reaches the startEditing
part, it returns
Error: uncaught exception: [Exception... "Component returned failure code: 0x80004001 (NS_ERROR_NOT_IMPLEMENTED) [nsITreeView.isEditable]" nsresult: "0x80004001 (NS_ERROR_NOT_IMPLEMENTED)" location: "JS frame :: chrome://global/content/bindings/tree.xml :: startEditing :: line 337" data: no]
I'm pretty much lost here. Could someone with more XUL experience help? Thanks!
I was trying to do something similar and I have same problem.
Wrapper with original abview set as __proto__
with functions overriden works fine until it is set as abResultsTree's view.
I've finally found (I hope) an elegant solution.
function MyAbView() {
this.originalAbViewInstance = this.originalAbViewFactory.createInstance(null, Ci.nsIAbView);
if (!this.proxiesGenerated) {
// find out which interfaces are implemented by original instance, their property proxies will be generated later
for (var ifName in Ci) {
if (Ci[ifName] instanceof Ci.nsIJSID && this.originalAbViewInstance instanceof Ci[ifName]) {
MyAbView.prototype.supportedInterfaces.push(Ci[ifName]);
}
}
function generatePropertyProxy(name) {
Object.defineProperty(MyAbView.prototype, name, {
get: function() {
return this.originalAbViewInstance[name];
},
set: function(val) {
this.originalAbViewInstance[name] = val;
},
enumerable: true
});
}
for (var prop in this.originalAbViewInstance) {
if (this[prop] == undefined) {
generatePropertyProxy(prop);
}
}
MyAbView.prototype.proxiesGenerated = true;
} else {
for each (var interface in this.supportedInterfaces) {
this.originalAbViewInstance.QueryInterface(interface);
}
}
}
MyAbView.prototype = {
classID: null,
_xpcom_factory: {
createInstance: function(outer, iid) {
return new MyAbView().QueryInterface(iid);
}
},
QueryInterface: function(aIID) {
for each (var interface in this.supportedInterfaces) {
if (interface.equals(aIID)) {
return this;
}
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
originalAbViewFactory: null,
originalAbViewInstance: null,
proxiesGenerated: false,
supportedInterfaces: [],
// any overriden functions come here
};
It's implemented as a component to replace the original abview, but it might be modified to just create a wrapper.
The <tree>
widget uses an nsITreeView object to retrieve or manipulate data that needs to be displayed. There are predefined nsITreeView
implementations reading data from the DOM or RDF datasources but one can choose to use his own tree view. Thunderbird's address book chooses the latter:
gAbView = Components.classes["@mozilla.org/addressbook/abview;1"]
.createInstance(Components.interfaces.nsIAbView);
...
gAbResultsTree.treeBoxObject.view =
gAbView.QueryInterface(Components.interfaces.nsITreeView);
Unfortunately for you, the component in question is implemented in C++, in the file nsAbView.cpp. This means that changing it without recompiling Thunderbird isn't possible. And the existing component doesn't implement isEditable()
and setCellText()
methods that would be required to edit tree cells.
If you don't want to mess with C++ yet, you could wrap that component in your own object. Something like this:
gAbView = Components.classes["@mozilla.org/addressbook/abview;1"]
.createInstance(Components.interfaces.nsIAbView);
gAbViewWrapper = {
__proto__: gAbView,
QueryInterface: function(iid)
{
gAbView.QueryInterface(iid);
return this;
},
isEditable: function(row, col)
{
// Do something here
},
setCellText: function(row, col, value)
{
// Do something here
}
};
...
gAbResultsTree.treeBoxObject.view =
gAbViewWrapper.QueryInterface(Components.interfaces.nsITreeView);
Method isEditable()
should check again whether this particular cell is editable - even if the column is editable, individual cells don't have to be. And setCellText()
should store the new value for the cell.
User contributions licensed under CC BY-SA 3.0