XUL/Thunderbird: startEditing return

0

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!

javascript
xul
thunderbird
asked on Stack Overflow Oct 27, 2011 by Clément

2 Answers

2

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.

answered on Stack Overflow Mar 22, 2012 by David Jirovec
1

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.

answered on Stack Overflow Oct 27, 2011 by Wladimir Palant

User contributions licensed under CC BY-SA 3.0