home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-macos9-1.3.1.sea.bin / Mozilla1.3.1 / Chrome / comm.jar / content / communicator / bookmarks / bookmarks.xml < prev    next >
Extensible Markup Language  |  2003-06-08  |  98KB  |  2,180 lines

  1. <?xml version="1.0"?>  
  2.  
  3. <!-- -*- Mode: HTML; indent-tabs-mode: nil; -*- -->
  4. <!--
  5.  
  6.   The contents of this file are subject to the Netscape Public
  7.   License Version 1.1 (the "License"); you may not use this file
  8.   except in compliance with the License. You may obtain a copy of
  9.   the License at http://www.mozilla.org/NPL/
  10.  
  11.   Software distributed under the License is distributed on an "AS
  12.   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  13.   implied. See the License for the specific language governing
  14.   rights and limitations under the License.
  15.  
  16.   The Original Code is mozilla.org code.
  17.  
  18.   The Initial Developer of the Original Code is Netscape
  19.   Communications Corporation.  Portions created by Netscape are
  20.   Copyright (C) 1998 Netscape Communications Corporation. All
  21.   Rights Reserved.
  22.  
  23.   Contributor(s): 
  24.     Ben Goodger <ben@netscape.com> (Original Author)
  25.     Blake Ross <blakeross@telocity.com>
  26.  
  27. -->
  28. <!DOCTYPE window [
  29.   <!ENTITY % bookmarksDTD SYSTEM "chrome://communicator/locale/bookmarks/bookmarks.dtd" >
  30.   %bookmarksDTD;
  31. ]>
  32.  
  33. <bindings id="bookmarksBindings" 
  34.           xmlns="http://www.mozilla.org/xbl" 
  35.           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
  36.           xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  37.           xmlns:xbl="http://www.mozilla.org/xbl">
  38.  
  39.   <binding id="bookmarksBase">
  40.     <implementation>
  41.       <!-- RDF Namespace URIs -->
  42.       <field name="RDF_NS">"http://www.w3.org/1999/02/22-rdf-syntax-ns#"</field>
  43.       <field name="NC_NS">"http://home.netscape.com/NC-rdf#"</field>
  44.       <field name="NC_NS_CMD">this.NC_NS + "command?cmd=";</field>
  45.       <property name="rdfContainer">
  46.         <getter><![CDATA[
  47.           const kRDFCContractID = "@mozilla.org/rdf/container;1";
  48.           const kRDFCIID = Components.interfaces.nsIRDFContainer;
  49.           return Components.classes[kRDFCContractID].getService(kRDFCIID);
  50.         ]]></getter>
  51.       </property>
  52.       <field name="_rdf">null</field>
  53.       <property name="rdf">
  54.         <getter><![CDATA[
  55.           if (!this._rdf) {
  56.             const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
  57.             const kRDFIID = Components.interfaces.nsIRDFService;
  58.             this._rdf = Components.classes[kRDFContractID].getService(kRDFIID);
  59.           }
  60.           return this._rdf;
  61.         ]]></getter>
  62.       </property>
  63.       <field name="_bundle">null</field>
  64.       <field name="_bookmarksDS">null</field>
  65.       <property name="bookmarksDS">
  66.         <getter><![CDATA[
  67.           if (!this._bookmarksDS)
  68.             this._bookmarksDS = this.rdf.GetDataSource("rdf:bookmarks");
  69.           return this._bookmarksDS;
  70.         ]]></getter>
  71.       </property>
  72.       <method name="flushBMDatasource">
  73.         <body><![CDATA[
  74.           var remoteDS = this.bookmarksDS.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
  75.           remoteDS.Flush();
  76.         ]]></body>
  77.       </method>
  78.     </implementation>
  79.   </binding>
  80.  
  81.   <binding id="bookmarks-tree"
  82.            extends="chrome://communicator/content/bookmarks/bookmarks.xml#bookmarksBase">
  83.     <implementation>
  84.       <constructor><![CDATA[
  85.         var bookmarksSvc = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService(Components.interfaces.nsIBookmarksService);
  86.         // This function only reads in the bookmarks from disk if they have not already been read.
  87.         bookmarksSvc.ReadBookmarks();
  88.         
  89.         // We implement nsIController
  90.         this.tree.controllers.appendController(this.controller);
  91.         var olb = document.getAnonymousElementByAttribute(this, "anonid", "bookmarks-tree");
  92.         olb = olb.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
  93.         olb.addObserver(this.builderObserver);
  94.         
  95.         // need to create string bundle manually instead of using <xul:stringbundle/>
  96.         // see bug 63370 for details
  97.         var localeService = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
  98.                                       .getService(Components.interfaces.nsILocaleService);
  99.         var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService);
  100.         var bundleURL = "chrome://communicator/locale/bookmarks/bookmark.properties";
  101.         this._bundle = stringBundleService.createBundle(bundleURL, localeService.GetApplicationLocale());
  102.         
  103.  
  104.         // Load column settings from persisted attribute
  105.         var colinfostr = this.getAttribute("colinfo");
  106.         var colinfo = colinfostr.split(" ");
  107.         for (var i = 0; i < colinfo.length; ++i) {
  108.           if (colinfo[i] == "") continue;
  109.           
  110.           var querymarker = colinfo[i].indexOf("?");
  111.           var anonid = colinfo[i].substring(4, querymarker);
  112.           var col = document.getAnonymousElementByAttribute(this, "id", anonid);
  113.           
  114.           if (!anonid || !col) break;
  115.           
  116.           var attrstring = colinfo[i].substr(querymarker + 1);
  117.  
  118.           var attrpairs = attrstring.split("&");
  119.           for (var j = 0; j < attrpairs.length; ++j) {
  120.             var pair = attrpairs[j].split("=");
  121.             col.setAttribute(pair[0], pair[1]);
  122.           }
  123.         }
  124.         
  125.         // Load sort data from preferences
  126.         this.refreshSort();
  127.         
  128.         // Observe for changes in sort from other concurrent UI
  129.         const kPrefSvcContractID = "@mozilla.org/preferences;1";
  130.         const kPrefSvcIID = Components.interfaces.nsIPrefService;
  131.         var prefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  132.         var prefs = prefSvc.getBranch(null);
  133.         const kPrefBranchInternalIID = Components.interfaces.nsIPrefBranchInternal;
  134.         var bookmarksPrefsInternal = prefs.QueryInterface(kPrefBranchInternalIID);
  135.         bookmarksPrefsInternal.addObserver(this.sortChangedObserver.domain, 
  136.                                            this.sortChangedObserver, false);
  137.       ]]></constructor>
  138.       <destructor><![CDATA[
  139.         
  140.         this.treeBuilder.removeObserver(this.builderObserver);
  141.         this.tree.controllers.removeController(this.controller);
  142.  
  143.         // Save column settings and sort info to persisted attribute
  144.         var persistString = "";
  145.         
  146.         var sortResource = this.NC_NS + "Name";
  147.         var sortDirection = "none";
  148.  
  149.         var treecols = document.getAnonymousElementByAttribute(this, "anonid", "treecols");
  150.         var child = treecols.firstChild;
  151.         while (child) {
  152.           if (child.localName != "splitter") {
  153.             var formatString = " col:%1%?width=%2%&hidden=%3%&ordinal=%6%";
  154.             formatString = formatString.replace(/%1%/, child.getAttribute("id"));
  155.             formatString = formatString.replace(/%2%/, child.getAttribute("width"));
  156.             formatString = formatString.replace(/%3%/, child.getAttribute("hidden"));
  157.  
  158.             // While we're walking the columns, if we discover the column that represents the
  159.             // field sorted by, save the resource associated with that column so that we 
  160.             // can save that in prefs (see below)
  161.             if (child.getAttribute("sortActive") == "true") {
  162.               sortResource = child.getAttribute("sort");
  163.               sortDirection = child.getAttribute("sortDirection");
  164.             }
  165.             formatString = formatString.replace(/%6%/, child.getAttribute("ordinal"));
  166.             persistString += formatString;
  167.           }          
  168.           child = child.nextSibling;
  169.         }
  170.         this.setAttribute("colinfo", persistString);
  171.         
  172.         document.persist(this.id, "colinfo");
  173.         
  174.         // Unhook the sort change observer for this tree
  175.         const kPrefSvcContractID = "@mozilla.org/preferences;1";
  176.         const kPrefSvcIID = Components.interfaces.nsIPrefService;
  177.         var prefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  178.         var prefs = prefSvc.getBranch(null);
  179.         const kPrefBranchInternalIID = Components.interfaces.nsIPrefBranchInternal;
  180.         var bookmarksPrefsInternal = prefs.QueryInterface(kPrefBranchInternalIID);
  181.         bookmarksPrefsInternal.removeObserver(this.sortChangedObserver.domain, 
  182.                                               this.sortChangedObserver);
  183.  
  184.       ]]></destructor>
  185.  
  186.       <field name="sortChangedObserver">
  187.       <![CDATA[
  188.       ({
  189.         outer: this,
  190.         domain: "browser.bookmarks.sort",
  191.         observe: function BMOL_sortChangedObserver(aSubject, aTopic, aPrefName) 
  192.         {
  193.           if (aTopic != "nsPref:changed") return;
  194.           if (aPrefName.substr(0, this.domain.length) != this.domain) return;
  195.                     
  196.           this.outer.refreshSort();
  197.         }      
  198.       })
  199.       ]]>
  200.       </field>
  201.       
  202.       <field name="sorted">false</field>
  203.       <method name="refreshSort">
  204.         <body>
  205.         <![CDATA[
  206.           const kPrefSvcContractID = "@mozilla.org/preferences;1";
  207.           const kPrefSvcIID = Components.interfaces.nsIPrefService;
  208.           var prefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  209.           var bookmarksSortPrefs = prefSvc.getBranch("browser.bookmarks.sort.");
  210.           
  211.           // This ensures that we don't sort twice in the tree that is clicked on 
  212.           // as a result of 1) the click and 2) the pref listener. 
  213.           if (!this.sorted) {
  214.             try {
  215.               var sortResource = bookmarksSortPrefs.getCharPref("resource");
  216.               var sortDirection = bookmarksSortPrefs.getCharPref("direction");
  217.           
  218.               // Walk the columns, when we find a column with a sort resource that matches the supplied
  219.               // data, stop and make sure it's sort active. 
  220.               var treecols = document.getAnonymousElementByAttribute(this, "anonid", "treecols");
  221.               var child = treecols.firstChild;
  222.               while (child) {
  223.                 if (child.localName != "splitter") {
  224.                   if (child.getAttribute("sort") == sortResource) {
  225.                     child.setAttribute("sortActive", "true");
  226.                     child.setAttribute("sortDirection", sortDirection);
  227.                     this.treeBuilder.sort(child, false);
  228.                     break;
  229.                   }
  230.                 }          
  231.                 child = child.nextSibling;
  232.               }
  233.             }
  234.             catch (e) {
  235.             }
  236.           }
  237.  
  238.           this.sorted = false;
  239.         ]]>
  240.         </body>
  241.       </method>
  242.       
  243.       <property name="columns">
  244.         <getter>
  245.         <![CDATA[
  246.           var cols = [];
  247.         
  248.           var treecols = document.getAnonymousElementByAttribute(this, "anonid", "treecols");
  249.           var child = treecols.firstChild;
  250.           while (child) {
  251.             if (child.localName != "splitter") {
  252.               var obj = {
  253.                 label: child.getAttribute("label"),
  254.                 accesskey: child.getAttribute("accesskey"),
  255.                 resource: child.getAttribute("sort"),
  256.                 sortActive: child.getAttribute("sortActive") == "true",
  257.                 hidden: child.getAttribute("hidden")
  258.               }
  259.               cols.push(obj);
  260.             }
  261.             child = child.nextSibling;
  262.           }
  263.  
  264.           return cols;
  265.         ]]>
  266.         </getter>
  267.       </property>
  268.       
  269.       <method name="toggleColumnVisibility">
  270.         <parameter name="aColumnResource"/>
  271.         <body>
  272.         <![CDATA[
  273.           var elt = document.getAnonymousElementByAttribute(this, "sort", aColumnResource);
  274.           if (elt)
  275.             elt.setAttribute("hidden", elt.getAttribute("hidden") != "true");
  276.         ]]>
  277.         </body>
  278.       </method>
  279.       
  280.  
  281.       <property name="treeBoxObject">
  282.         <getter><![CDATA[
  283.           return this.tree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);
  284.         ]]></getter>
  285.       </property>
  286.  
  287.       <property name="treeBuilder">
  288.         <getter><![CDATA[
  289.           return this.tree.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
  290.         ]]></getter>
  291.       </property>
  292.  
  293.       <property name="tree">
  294.         <getter><![CDATA[
  295.           return document.getAnonymousElementByAttribute(this, "anonid", "bookmarks-tree");
  296.         ]]></getter>
  297.       </property>
  298.  
  299.       <property name="currentIndex">
  300.         <getter><![CDATA[
  301.           return this.treeBoxObject.selection.currentIndex;
  302.         ]]></getter>
  303.       </property>
  304.  
  305.       <property name="currentRes">
  306.         <getter><![CDATA[
  307.           return this.treeBuilder.getResourceAtIndex(this.currentIndex);
  308.         ]]></getter>
  309.       </property>
  310.  
  311.       <property name="parentRes">
  312.         <getter><![CDATA[
  313.           const currIndex = this.currentIndex;
  314.  
  315.           if (currIndex == -1)
  316.             return this.rdf.GetResource("NC:BookmarksRoot");
  317.  
  318.           var parentIndex = this.treeBoxObject.view.getParentIndex(currIndex);
  319.           if (parentIndex != -1)
  320.             return this.treeBuilder.getResourceAtIndex(parentIndex)
  321.           return this.rdf.GetResource("NC:BookmarksRoot"); // assume its parent is the root
  322.         ]]></getter>
  323.       </property>
  324.  
  325.       <property name="firstSelectedIndex">
  326.         <getter><![CDATA[
  327.           var first = { };
  328.           this.treeBoxObject.selection.getRangeAt(0, first, { });
  329.           return first.value;
  330.         ]]></getter>
  331.       </property>
  332.  
  333.       <property name="lastSelectedIndex">
  334.         <getter><![CDATA[
  335.           var bo = this.treeBoxObject;
  336.           var last = { };
  337.           var rangeCount = bo.selection.getRangeCount();
  338.           bo.selection.getRangeAt(rangeCount - 1, { }, last);
  339.           return last.value;
  340.         ]]></getter>
  341.       </property>
  342.       
  343.       <property name="_browserURL">
  344.         <getter><![CDATA[
  345.           try {
  346.             var prefs = Components.classes["@mozilla.org/preferences;1"];
  347.             if (prefs) {
  348.               prefs = prefs.getService();
  349.               if (prefs)
  350.                 prefs = prefs.QueryInterface(Components.interfaces.nsIPref);
  351.             }
  352.             if (prefs) {
  353.               var url = prefs.CopyCharPref("browser.chromeURL");
  354.               if (url)
  355.                 return url;
  356.             }
  357.           } catch(e) {
  358.           }
  359.           return "chrome://navigator/content/navigator.xul";
  360.         ]]></getter>
  361.       </property>
  362.       
  363.       <field name="_type">null</field>
  364.  
  365.       <property name="type">
  366.         <getter><![CDATA[
  367.           if (!this._type) {
  368.             var type = this.getAttribute("type");
  369.             if (!type)
  370.               type = "multi-column";
  371.             this._type = type;
  372.           }
  373.           return this._type;
  374.         ]]></getter>
  375.       </property>
  376.       
  377.       <!-- Returns the row index of the best row at which to perform an operation
  378.            relative to the current selection, e.g. creating a new bookmark 
  379.            adjacent to the current selection. -->
  380.       <method name="getNextRowIndex">
  381.         <body><![CDATA[
  382.           var bo = this.treeBoxObject;
  383.           var rangeCount = bo.selection.getRangeCount();
  384.           var currLevel, lastIndex = 0;
  385.           if (!rangeCount) 
  386.             currLevel = 0;
  387.           else
  388.             currLevel = bo.view.getLevel(this.firstSelectedIndex);
  389.  
  390.           // Walk the selection. If the level for the current item is less than
  391.           // our running minimum, assume it is higher in the hierarchy than any
  392.           // previous. In this case, or in the case with a selected item of same
  393.           // hierarchy, set the lastIndex.
  394.           // XXXben - erk this comment sort of sucks. A diagram might help. 
  395.           for (var i = 0; i < rangeCount; ++i) {
  396.             var rangeMin = { };
  397.             var rangeMax = { };
  398.             bo.selection.getRangeAt(i, rangeMin, rangeMax);
  399.             for (var j = rangeMax.value; j >= rangeMin.value; --j) {
  400.               var level = bo.view.getLevel(j);
  401.               if (level <= currLevel) {
  402.                 currLevel = level;
  403.                 lastIndex = j;
  404.               }
  405.             } 
  406.           }
  407.           return lastIndex;
  408.         ]]></body>
  409.       </method>
  410.  
  411.       <method name="getTypeAtIndex">
  412.         <parameter name="aIndex" />
  413.         <body><![CDATA[
  414.           try {
  415.             return BookmarksUtils.resolveType(this.treeBuilder.getResourceAtIndex(aIndex));
  416.           }
  417.           catch(ex) {
  418.             return null;
  419.           }
  420.         ]]></body>
  421.       </method>
  422.  
  423.       <!-- observer -->
  424.       <field name="newFolderRDFObserver" readonly="true"><![CDATA[
  425.       ({
  426.         _newFolderURI: null,
  427.         onAssert: function (aDS, aSource, aProperty, aValue)
  428.         {
  429.           try {
  430.             var value = aValue.QueryInterface(Components.interfaces.nsIRDFResource);
  431.             if (aDS.URI == "rdf:bookmarks" && aProperty.Value == this.RDF_NS + "type" &&
  432.                 value.Value == this.NC_NS + "Folder")
  433.               this._newFolderURI = aSource.Value;
  434.           }
  435.           catch (e) {
  436.             // Failures are OK, the value could be a literal instead of a resource.
  437.           }
  438.         },
  439.         onUnassert: function (aDS, aSource, aProperty, aTarget) { },
  440.         onChange: function (aDS, aSource, aProperty, aOldTarget, aNewTarget) { },
  441.         onMove: function (aDS, aOldSource, aNewSource, aProperty, aTarget) { },
  442.         beginUpdateBatch: function (aDS) { },
  443.         endUpdateBatch: function (aDS) { }
  444.       })
  445.       ]]></field>
  446.  
  447.       <!-- observer -->
  448.       <field name="DNDObserver" readonly="true"><![CDATA[
  449.       ({
  450.         mOuter: this,
  451.         onDragStart: function (aEvent, aXferData, aDragAction)
  452.         {
  453.           if (this.mOuter.tree.getAttribute("sortActive") == "true")
  454.             throw Components.results.NS_OK; 
  455.           aXferData.data = new TransferDataSet();
  456.           var rangeCount = this.mOuter.treeBoxObject.selection.getRangeCount();
  457.           for (var i = rangeCount - 1; i >= 0; --i) {
  458.             var rangeMin = { };
  459.             var rangeMax = { };
  460.             this.mOuter.treeBoxObject.selection.getRangeAt(i, rangeMin, rangeMax);
  461.             for (var j = rangeMax.value; j >= rangeMin.value; --j) {
  462.               var currRes = this.mOuter.treeBuilder.getResourceAtIndex(j); 
  463.               var parentIndex = this.mOuter.treeBoxObject.view.getParentIndex(j);
  464.               var parentRes;
  465.               if (parentIndex == -1)
  466.                 parentRes = this.mOuter.rdf.GetResource("NC:BookmarksRoot");
  467.               else
  468.                 parentRes = this.mOuter.treeBuilder.getResourceAtIndex(parentIndex);
  469.     
  470.               var type = this.mOuter.db.GetTarget(currRes, this.mOuter.rdf.GetResource(this.mOuter.RDF_NS + "type"), true);
  471.               type = type.QueryInterface(Components.interfaces.nsIRDFResource).Value;
  472.               if (!type || (type != (this.mOuter.NC_NS + "BookmarkSeparator") && 
  473.                   type != (this.mOuter.NC_NS + "Bookmark") && 
  474.                   type != (this.mOuter.NC_NS + "Folder"))) 
  475.                 throw Components.results.NS_OK;
  476.               var name = this.mOuter.db.GetTarget(currRes, this.mOuter.rdf.GetResource(this.mOuter.NC_NS + "Name"), true);        
  477.               var data = new TransferData();
  478.               if (name) {
  479.                 name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  480.                 data.addDataForFlavour("text/x-moz-url", currRes.Value + "\n" + name);
  481.               }
  482.               else {
  483.                 data.addDataForFlavour("text/x-moz-url", currRes.Value);
  484.               }
  485.               data.addDataForFlavour("moz/rdfitem", currRes.Value + "\n" + parentRes.Value);
  486.               data.addDataForFlavour("text/unicode", currRes.Value);
  487.               
  488.               aXferData.data.push(data);
  489.             }
  490.  
  491.            if (aEvent.ctrlKey) {
  492.              const kDSIID = Components.interfaces.nsIDragService;
  493.              aDragAction.action = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  494.            }
  495.          }
  496.        }
  497.      })
  498.      ]]></field>      
  499.         
  500.       <!-- nsIController -->
  501.       <field name="controller" readonly="true"><![CDATA[
  502.       ({
  503.         mOuter: this,
  504.         
  505.         supportsCommand: function BMOLController_supportsCommand(aCommand) 
  506.         {
  507.           switch (aCommand) {
  508.           case "cmd_bm_undo":
  509.           case "cmd_bm_redo":
  510.             return false;
  511.           case "cmd_bm_cut":
  512.           case "cmd_bm_copy":
  513.           case "cmd_bm_paste":
  514.           case "cmd_bm_delete":
  515.           case "cmd_bm_selectAll":
  516.           case "cmd_bm_open":
  517.           case "cmd_bm_openinnewwindow":
  518.           case "cmd_bm_openfolder":
  519.           case "cmd_bm_newbookmark":
  520.           case "cmd_bm_newfolder":
  521.           case "cmd_bm_newseparator":
  522.           case "cmd_bm_find":
  523.           case "cmd_bm_properties":
  524.           case "cmd_bm_rename":
  525.           case "cmd_bm_setnewbookmarkfolder":
  526.           case "cmd_bm_setpersonaltoolbarfolder":
  527.           case "cmd_bm_setnewsearchfolder":
  528.           case "cmd_bm_import":
  529.           case "cmd_bm_export":
  530.           case "cmd_bm_fileBookmark":
  531.             return true;
  532.           default:
  533.             return false;
  534.           }
  535.         },
  536.         
  537.         isCommandEnabled: function BMOLController_isCommandEnabled(aCommand)
  538.         {
  539.           var bo = this.mOuter.treeBoxObject;
  540.           var type;   // The bookmark's RDF:type arc
  541.           
  542.           switch (aCommand) {
  543.           case "cmd_bm_undo":
  544.           case "cmd_bm_redo":
  545.             return false;
  546.           case "cmd_bm_openfolder":
  547.             // 'Expand' is only available if one item is selected and that item is a
  548.             // container item.
  549.             return bo.view.rowCount && bo.selection.count == 1 && bo.view.isContainer(this.mOuter.firstSelectedIndex);
  550.           case "cmd_bm_open":
  551.             return this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex) == this.mOuter.NC_NS + "Bookmark";
  552.           case "cmd_bm_openinnewwindow":
  553.             return true;
  554.           case "cmd_bm_rename":
  555.           case "cmd_bm_properties":
  556.             if (bo.selection.count != 1)  
  557.               return false;
  558.             type = this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex);
  559.             return type == this.mOuter.NC_NS + "Bookmark" || type == this.mOuter.NC_NS + "Folder";
  560.           case "cmd_bm_cut":
  561.             type = this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex);
  562.             if (type != this.mOuter.NC_NS + "Bookmark" && type != this.mOuter.NC_NS + "BookmarkSeparator" && type != this.mOuter.NC_NS + "Folder")
  563.               return false;
  564.             return bo.selection.count > 0;
  565.           case "cmd_bm_find":
  566.             return true;
  567.           case "cmd_bm_newbookmark":
  568.           case "cmd_bm_newfolder":
  569.           case "cmd_bm_newseparator":
  570.             // This is not really correct because it gives the false impression
  571.             // that it is possible to create an item as a child of some immutable folders
  572.             // like IE Favorites, but it will do for now. 
  573.             return true;
  574.           case "cmd_bm_delete":
  575.             // Determining whether or not the selection is mutable is handled by
  576.             // the deletion routine.
  577.             type = this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex);
  578.             if (type != this.mOuter.NC_NS + "Bookmark" && type != this.mOuter.NC_NS + "BookmarkSeparator" &&
  579.                 type != this.mOuter.NC_NS + "Folder" && type != this.mOuter.NC_NS + "IEFavoriteFolder")
  580.               return false;
  581.             return bo.selection.count >= 1;
  582.           case "cmd_bm_selectAll":
  583.             // "Select All" is disabled when all visible rows are selected.
  584.             // XXXben this could be cleverer. 
  585.             return bo.view.rowCount != bo.selection.count;
  586.           case "cmd_bm_copy":
  587.             return bo.selection.count >= 1;
  588.           case "cmd_bm_paste":
  589.             return this.mOuter.canPaste();
  590.           case "cmd_bm_setnewbookmarkfolder":
  591.           case "cmd_bm_setpersonaltoolbarfolder":
  592.           case "cmd_bm_setnewsearchfolder":
  593.             if (bo.selection.count != 1) 
  594.               return false;
  595.             // XXXbar if a folder has more than one of these special attributes,
  596.             // this won't work
  597.             var folderURI;
  598.             if (aCommand == "cmd_bm_setnewbookmarkfolder")
  599.               folderURI = "NC:NewBookmarkFolder";
  600.             else if (aCommand == "cmd_bm_setpersonaltoolbarfolder")
  601.               folderURI = "NC:PersonalToolbarFolder";
  602.             else
  603.               folderURI = "NC:NewSearchFolder";
  604.             return (this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex) == this.mOuter.NC_NS + "Folder")
  605.                    && (this.mOuter.treeBuilder.getResourceAtIndex(this.mOuter.firstSelectedIndex).Value != folderURI);
  606.           case "cmd_bm_import":
  607.           case "cmd_bm_export":
  608.             return true;
  609.           case "cmd_bm_fileBookmark":
  610.             if (bo.selection.count < 1) 
  611.               return false;
  612.             type = this.mOuter.getTypeAtIndex(this.mOuter.firstSelectedIndex);
  613.             return type == this.mOuter.NC_NS + "Folder" || type == this.mOuter.NC_NS + "Bookmark";
  614.           default:
  615.             return false;
  616.           }
  617.         },
  618.  
  619.         doCommand: function BMOLController_doCommand(aCommand)
  620.         {
  621.           switch (aCommand) {
  622.           case "cmd_bm_cut":
  623.             this.mOuter.copySelection();
  624.             this.mOuter.deleteSelection();
  625.             break;
  626.           case "cmd_bm_copy":
  627.             this.mOuter.copySelection();
  628.             break;
  629.           case "cmd_bm_paste":
  630.             this.mOuter.paste();
  631.             break;
  632.           case "cmd_bm_delete":
  633.             this.mOuter.deleteSelection();
  634.             break;
  635.           case "cmd_bm_selectAll":
  636.             this.mOuter.selectAll();
  637.             break;
  638.           case "cmd_bm_open":
  639.             this.mOuter.openItem(null, false, false);
  640.             break;
  641.           case "cmd_bm_openfolder":
  642.             this.mOuter.treeBoxObject.view.toggleOpenState(this.mOuter.currentIndex);
  643.             break;
  644.           case "cmd_bm_openinnewwindow":
  645.             var type = this.mOuter.getTypeAtIndex(this.mOuter.currentIndex);
  646.             if (type == this.mOuter.NC_NS + "Folder")
  647.               this.mOuter.openFolderInNewWindow();
  648.             else
  649.               this.mOuter.openItem(null, true, false);
  650.             break;
  651.           case "cmd_bm_newbookmark":
  652.             this.mOuter.addBookmark();
  653.             break;
  654.           case "cmd_bm_newfolder":
  655.             this.mOuter.createNewFolder();
  656.             break;
  657.           case "cmd_bm_newseparator":
  658.             this.mOuter.createNewSeparator();
  659.             break;
  660.           case "cmd_bm_find":
  661.             this.mOuter.openFindDialog();
  662.             break;
  663.           case "cmd_bm_properties":
  664.           case "cmd_bm_rename":
  665.             this.mOuter.openPropertiesForItem();
  666.             break;
  667.           case "cmd_bm_setnewbookmarkfolder":
  668.             this.mOuter.setAsNewBookmarkFolder();
  669.             break;
  670.           case "cmd_bm_setpersonaltoolbarfolder":
  671.             this.mOuter.setAsPersonalToolbarFolder();
  672.             break;         
  673.           case "cmd_bm_setnewsearchfolder":
  674.             this.mOuter.setAsNewSearchFolder();
  675.             break;
  676.           case "cmd_bm_import":
  677.             this.mOuter.importBookmarks();
  678.             break;
  679.           case "cmd_bm_export":
  680.             this.mOuter.exportBookmarks();
  681.             break;
  682.           case "cmd_bm_fileBookmark":
  683.             this.mOuter.fileBookmark();
  684.             break;
  685.           default:
  686.           }
  687.         }
  688.       })
  689.       ]]></field>
  690.  
  691.       <method name="onCommandUpdate">
  692.         <body><![CDATA[
  693.           var commands = ["cmd_bm_properties", "cmd_bm_rename", "cmd_bm_copy",
  694.                           "cmd_bm_paste", "cmd_bm_cut", "cmd_bm_delete",
  695.                           "cmd_bm_setpersonaltoolbarfolder", 
  696.                           "cmd_bm_setnewbookmarkfolder",
  697.                           "cmd_bm_setnewsearchfolder", "cmd_bm_fileBookmark", 
  698.                           "cmd_bm_openfolder"];
  699.           for (var i = 0; i < commands.length; ++i) {
  700.             var enabled = this.controller.isCommandEnabled(commands[i]);
  701.             var commandNode = document.getElementById(commands[i]);
  702.             if (commandNode) { 
  703.               if (enabled) 
  704.                 commandNode.removeAttribute("disabled");
  705.               else 
  706.                 commandNode.setAttribute("disabled", "true");
  707.             }
  708.           }
  709.         ]]></body>
  710.       </method>
  711.  
  712.       <method name="selectionChanged">
  713.         <parameter name="aEvent" />
  714.         <body><![CDATA[
  715.         ]]></body>
  716.       </method>
  717.  
  718.       <!-- nsIXULTreeBuilderObserver -->
  719.       <field name="builderObserver"><![CDATA[
  720.       ({
  721.         mOuter: this,
  722.         canDropOn: function(index)
  723.         {
  724.           return true;
  725.         },
  726.         canDropBeforeAfter: function(index, before)
  727.         {
  728.           return true;
  729.         },
  730.         onDrop: function(row, orientation)
  731.         {  
  732.           var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService().QueryInterface(Components.interfaces.nsIDragService);  
  733.           var dragSession = dragService.getCurrentSession();
  734.           if (!dragSession)
  735.             return;
  736.           const kRDFCContractID = "@mozilla.org/rdf/container;1";
  737.           const kRDFIID = Components.interfaces.nsIRDFContainer;
  738.           var RDFC = Components.classes[kRDFCContractID].getService(kRDFIID);
  739.           var rTarget = this.mOuter.treeBuilder.getResourceAtIndex(row);
  740.           var parentIndex = this.mOuter.treeBoxObject.view.getParentIndex(row);
  741.           var rContainer;
  742.           if (parentIndex == -1)
  743.             rContainer = this.mOuter.rdf.GetResource("NC:BookmarksRoot");
  744.           else
  745.             rContainer = this.mOuter.treeBuilder.getResourceAtIndex(parentIndex);
  746.           var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  747.           var rBookmark = this.mOuter.rdf.GetResource(this.mOuter.NC_NS + "Bookmark");
  748.           trans.addDataFlavor("moz/rdfitem");
  749.           trans.addDataFlavor("text/x-moz-url");
  750.           trans.addDataFlavor("text/unicode");
  751.    
  752.           var list = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
  753.    
  754.           var sourceUri;
  755.           var sourceResource;
  756.           var dirty = false;
  757.           var additiveFlag = false;
  758.           for (var i = 0; i < dragSession.numDropItems; ++i) {
  759.             dragSession.getData(trans, i);
  760.             var dataObj = {};
  761.             var bestFlavor = {};
  762.             var len = {};
  763.             trans.getAnyTransferData(bestFlavor, dataObj, len);
  764.             if (dataObj)
  765.               dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
  766.             if (!dataObj)
  767.               continue;
  768.             sourceUri = dataObj.data.substring(0, len.value);
  769.             if (!sourceUri)
  770.               continue;
  771.  
  772.             var sourceID = [], parentID = [], nameRequired = [], name = [];      
  773.             nameRequired[i] = false;
  774.             name[i] = null;
  775.             switch (bestFlavor.value) {
  776.             case "moz/rdfitem":
  777.               var ix = sourceUri.indexOf("\n");
  778.               sourceID[i] = ix >= 0 ? (parentID[i] = sourceUri.substr(ix+1), sourceUri.substr(0, ix)) : sourceUri;
  779.               break;
  780.             case "text/x-moz-url":
  781.               ix = sourceUri.indexOf("\n");
  782.               sourceID[i] = ix >= 0 ? (name[i] = sourceUri.substr(ix+1), sourceUri.substr(0, ix)) : sourceUri;
  783.               break;
  784.             case "text/unicode":
  785.               sourceID[i] = sourceUri;
  786.               nameRequired[i] = true;
  787.               break;
  788.             default: 
  789.               continue;
  790.             }
  791.             var rSource = this.mOuter.rdf.GetResource(sourceID[i]);
  792.             var rParent = parentID[i] ? this.mOuter.rdf.GetResource(parentID[i]) : null;
  793.  
  794.             const kBMDS = this.mOuter.bookmarksDS;
  795.             var bmType = BookmarksUtils.resolveType(rSource);
  796.             var rType = this.mOuter.rdf.GetResource(this.mOuter.RDF_NS + "type");
  797.             if (!bmType) 
  798.               kBMDS.Assert(rSource, rType, rBookmark, true);
  799.             
  800.             // prevent dropping folder within itself or one of its subfolders
  801.             if (bmType == this.mOuter.NC_NS + "Folder") {
  802.               var currRow = row;
  803.               do {
  804.                 var currURI = this.mOuter.treeBuilder.getResourceAtIndex(currRow).Value;
  805.                 currRow = this.mOuter.treeBoxObject.view.getParentIndex(currRow);
  806.               }
  807.               while (currRow != -1 && currURI != "NC:BookmarksRoot" && currURI != rSource.Value);
  808.               if (currURI == rSource.Value)
  809.                 return;
  810.             }
  811.  
  812.             var dropIndex;
  813.             if (orientation == Components.interfaces.nsITreeView.inDropAfter &&
  814.                 this.mOuter.treeBoxObject.view.isContainer(row)     &&
  815.                 this.mOuter.treeBoxObject.view.isContainerOpen(row) &&
  816.                !this.mOuter.treeBoxObject.view.isContainerEmpty(row)) {
  817.               rContainer = rTarget;
  818.               dropIndex = 1;        
  819.             } 
  820.             else {
  821.               RDFC.Init(kBMDS, rContainer);
  822.               dropIndex = RDFC.IndexOf(rTarget);
  823.               if (dropIndex == -1)
  824.                 break;
  825.               if (orientation == Components.interfaces.nsITreeView.inDropAfter) {
  826.                 ++dropIndex;
  827.               }
  828.             }
  829.  
  830.             const kDSIID = Components.interfaces.nsIDragService;
  831.             const kCopyAction = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  832.             if (rParent) {
  833.               if (!(dragSession.dragAction & kCopyAction)) {
  834.                 RDFC.Init(kBMDS, rParent);
  835.                 RDFC.RemoveElement(rSource, false);
  836.               }
  837.             }
  838.     
  839.             if (bmType == this.mOuter.NC_NS + "Folder") {
  840.               // If we're going to copy a folder type, we need to clone the folder 
  841.               // rather than just asserting the new node as a child of the drop folder.
  842.               if (dragSession.dragAction & kCopyAction)
  843.                 rSource = BookmarksUtils.cloneFolder(rSource, rContainer, rTarget);
  844.             }
  845.             if (orientation != Components.interfaces.nsITreeView.inDropOn) {
  846.               RDFC.Init(kBMDS, rContainer);
  847.               RDFC.InsertElementAt(rSource, dropIndex, true);
  848.             }
  849.             else {
  850.               RDFC.Init(kBMDS, rTarget);
  851.               RDFC.AppendElement(rSource);
  852.             }
  853.  
  854.             dirty = true;
  855.  
  856.             //if (rParent) {
  857.             //  gBookmarksShell.selectFolderItem(rContainer.Value, sourceID[i], additiveFlag);
  858.             //  if (!additiveFlag) additiveFlag = true;
  859.             //} 
  860.             // If a name is supplied, we want to assert this information into the 
  861.             // graph. E.g. user drags an internet shortcut to the app, we want to 
  862.             // preserve not only the URL but the name of the shortcut. The other case
  863.             // where we need to assert a name is when the node does not already exist
  864.             // in the graph, in this case we'll just use the URL as the name.
  865.             if (name[i] || nameRequired[i]) {
  866.               var currentName = kBMDS.GetTarget(this.mOuter.rdf.GetResource(sourceID[i]), this.mOuter.rdf.GetResource(this.mOuter.NC_NS + "Name"), true);
  867.               if (!currentName) {
  868.                 var rDefaultName = this.mOuter.rdf.GetLiteral(name[i] || sourceID[i]);
  869.                 if (rDefaultName) {
  870.                   var rName = this.mOuter.rdf.GetResource(this.mOuter.NC_NS + "Name");
  871.                   kBMDS.Assert(rSource, rName, rDefaultName, true);
  872.                 }
  873.               }
  874.             } 
  875.             if (dirty)
  876.               this.mOuter.flushBMDatasource();
  877.           }
  878.         },
  879.         onToggleOpenState: function BMOLBuilderObserver_onToggleOpenState(aItemIndex)
  880.         {
  881.         },
  882.         
  883.         onCycleHeader: function BMOLBuilderObserver_onCycleHeader(aColumnID, aHeaderElement)
  884.         {
  885.           const kPrefSvcContractID = "@mozilla.org/preferences;1";
  886.           const kPrefSvcIID = Components.interfaces.nsIPrefService;
  887.           var prefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  888.           var bookmarksSortPrefs = prefSvc.getBranch("browser.bookmarks.sort.");
  889.           
  890.           // Sorted! http://www.sorted.org.nz/
  891.           this.mOuter.sorted = true;
  892.  
  893.           bookmarksSortPrefs.setCharPref("resource", aHeaderElement.getAttribute("sort"));
  894.           bookmarksSortPrefs.setCharPref("direction", aHeaderElement.getAttribute("sortDirection"));
  895.         },
  896.     
  897.         onCycleCell: function BMOLBuilderObserver_onCycleCell(aItemIndex, aColumnID)
  898.         {
  899.         },
  900.         
  901.         onSelectionChanged: function BMOLBuilderObserver_onSelectionChanged()
  902.         {
  903.           this.mOuter.onCommandUpdate();
  904.           const kStatusBar = document.getAnonymousElementByAttribute(this.mOuter, "anonid", "statusbar-text");
  905.           if (!kStatusBar)
  906.             return;
  907.           const currentIndex = this.mOuter.currentIndex;
  908.           var displayValue = "";
  909.           if (this.mOuter.treeBoxObject.selection.count == 1) {
  910.             if (this.mOuter.treeBoxObject.view.isContainer(currentIndex)) {
  911.               const kRDFCContractID = "@mozilla.org/rdf/container;1";
  912.               const kRDFCIID = Components.interfaces.nsIRDFContainer;
  913.               const kRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
  914.               const krSrc = this.mOuter.treeBuilder.getResourceAtIndex(currentIndex);
  915.               try {
  916.                 kRDFC.Init(this.mOuter.db, krSrc);
  917.                 var count = kRDFC.GetCount();
  918.                 displayValue = this.mOuter._bundle.GetStringFromName("status_foldercount");
  919.                 displayValue = displayValue.replace(/%num_items%/, count);
  920.               }
  921.               catch (e) {
  922.               }
  923.             }
  924.             else {
  925.               try {
  926.                 displayValue = this.mOuter.db.GetTarget(this.mOuter.treeBuilder.getResourceAtIndex(currentIndex), this.mOuter.rdf.GetResource(this.mOuter.NC_NS + "URL"), true);
  927.                 displayValue = displayValue.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  928.               }
  929.               catch (e) {
  930.                 displayValue = "";
  931.               }
  932.             }
  933.             if (displayValue.substring(0, 3) == "NC:")
  934.               displayValue = "";
  935.           }
  936.           kStatusBar.label = displayValue;
  937.         },
  938.         
  939.         isEditable: function BMOLBuilderObserver_isEditable(aItemIndex, aColumnID)
  940.         {
  941.         },
  942.  
  943.         onSetCellText: function BMOLBuilderObserver_onSetCellText(aItemIndex, aColumnID, aValue)
  944.         {
  945.         },
  946.         
  947.         onPerformAction: function BMOLBuilderObserver_onPerformAction(aAction)
  948.         {
  949.         },
  950.         
  951.         onPerformActionOnRow: function BMOLBuilderObserver_onPerformActionOnRow(aAction, aItemIndex)
  952.         {
  953.         },
  954.         
  955.         onPerformActionOnCell: function BMOLBuilderObserver_onPerformActionOnCell(aAction, aItemIndex, aColumnID)
  956.         {
  957.         }
  958.       })
  959.       ]]></field>
  960.  
  961.       <!-- RDF utility functions required by base binding -->
  962.       <property name="db">
  963.         <getter><![CDATA[
  964.           return this.tree.database;
  965.         ]]></getter>
  966.       </property>
  967.       
  968.       <method name="doBookmarksCommand">
  969.         <parameter name="aSourceURI"/>
  970.         <parameter name="aCommand"/>
  971.         <parameter name="aArgumentsArray"/>
  972.         <body><![CDATA[
  973.           var rCommand = this.rdf.GetResource(aCommand);
  974.         
  975.           var kSuppArrayContractID = "@mozilla.org/supports-array;1";
  976.           var kSuppArrayIID = Components.interfaces.nsISupportsArray;
  977.           var sourcesArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  978.           if (aSourceURI) {
  979.             var rSource = this.rdf.GetResource(aSourceURI);
  980.             sourcesArray.AppendElement (rSource);
  981.           }
  982.         
  983.           var argsArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  984.           for (var i = 0; i < aArgumentsArray.length; ++i) {
  985.             var rArc = this.rdf.GetResource(aArgumentsArray[i].property);
  986.             argsArray.AppendElement(rArc);
  987.             var rValue = null;
  988.             if ("resource" in aArgumentsArray[i]) 
  989.               rValue = this.rdf.GetResource(aArgumentsArray[i].resource);
  990.             else
  991.               rValue = this.rdf.GetLiteral(aArgumentsArray[i].literal);
  992.             argsArray.AppendElement(rValue);
  993.           }
  994.       
  995.           // Exec the command in the Composite (not Bookmarks) datasource
  996.           // so that command aggregation works
  997.           this.db.DoCommand(sourcesArray, rCommand, argsArray);
  998.         ]]></body>
  999.       </method>
  1000.       
  1001.       <method name="selectAll">
  1002.         <body><![CDATA[
  1003.           this.treeBoxObject.selection.selectAll();
  1004.         ]]></body>
  1005.       </method>
  1006.  
  1007.       <method name="paste">
  1008.         <body><![CDATA[
  1009.           const kXferableContractID = "@mozilla.org/widget/transferable;1";
  1010.           const kXferableIID = Components.interfaces.nsITransferable;
  1011.           var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
  1012.           xferable.addDataFlavor("moz/bookmarkclipboarditem");
  1013.           xferable.addDataFlavor("text/x-moz-url");
  1014.           xferable.addDataFlavor("text/unicode");
  1015.       
  1016.           const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
  1017.           const kClipboardIID = Components.interfaces.nsIClipboard;
  1018.           var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
  1019.           clipboard.getData(xferable, kClipboardIID.kGlobalClipboard);
  1020.           
  1021.           var flavour = { };
  1022.           var data = { };
  1023.           var length = { };
  1024.           xferable.getAnyTransferData(flavour, data, length);
  1025.           var nodes = []; var names = [];
  1026.           data = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
  1027.           switch (flavour.value) {
  1028.           case "moz/bookmarkclipboarditem":
  1029.             nodes = data.split("\n");
  1030.             break;
  1031.           case "text/x-moz-url":
  1032.             var ix = data.indexOf("\n");
  1033.             nodes.push(data.substring(0, ix != -1 ? ix : data.length));
  1034.             names.push(data.substring(ix));
  1035.             break;
  1036.           default: 
  1037.             return;
  1038.           }
  1039.           const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
  1040.           const kRDFIID = Components.interfaces.nsIRDFService;
  1041.           const ksRDF = Components.classes[kRDFContractID].getService(kRDFIID);
  1042.           var parentRes;
  1043.           const currentIndex = this.currentIndex;
  1044.           if (!this.treeBoxObject.view.isContainer(currentIndex))
  1045.             parentRes = this.parentRes;
  1046.           else
  1047.             parentRes = this.treeBuilder.getResourceAtIndex(currentIndex);
  1048.  
  1049.           const currentRes = this.currentRes;
  1050.           const kRDFCContractID = "@mozilla.org/rdf/container;1";
  1051.           const kRDFCIID = Components.interfaces.nsIRDFContainer;
  1052.           const ksRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
  1053.           var additiveFlag = false;
  1054.           for (var i = 0; i < nodes.length; ++i) {
  1055.             if (!nodes[i]) continue;
  1056.             var aNode = nodes[i];
  1057.             var rType = null;
  1058.  
  1059.             var tabIndex = aNode.indexOf("\t");
  1060.             if (tabIndex > 0)
  1061.             {
  1062.               rType = ksRDF.GetResource(aNode.substring(tabIndex + 1));
  1063.               aNode = aNode.substring(0, tabIndex);
  1064.             }
  1065.             var rCurrent = ksRDF.GetResource(aNode);
  1066.             if (!rType && names[i])
  1067.             {
  1068.                 const krName = ksRDF.GetResource(names[i]);
  1069.                 const krNameProperty = ksRDF.GetResource("http://home.netscape.com/NC-rdf#Name");
  1070.                 const krBookmark = ksRDF.GetResource("http://home.netscape.com/NC-rdf#Bookmark");
  1071.                 kBMDS.Assert(rCurrent, krNameProperty, krName, true);
  1072.                 // note: never need to assert type (it is derived)
  1073.             }
  1074.  
  1075.             // If the node is a folder, then we need to create a new anonymous 
  1076.             // resource and copy all the arcs over.
  1077.             if (rType && rType.Value == "http://home.netscape.com/NC-rdf#Folder") {
  1078.              // but wait, copying a folder into its child or itself may cause recursive
  1079.              // folder, so need a check before the cloning.
  1080.               var checkIndex = this.currentIndex;
  1081.               var checkRes;
  1082.               while (checkIndex != -1) {
  1083.                 checkRes = this.treeBuilder.getResourceAtIndex(checkIndex);
  1084.                 if (checkRes.EqualsNode(rCurrent))
  1085.                   break;
  1086.                 checkIndex = this.treeBoxObject.view.getParentIndex(checkIndex);
  1087.               }
  1088.               if (checkIndex == -1)
  1089.                 rCurrent = BookmarksUtils.cloneFolder(rCurrent, parentRes, currentRes);
  1090.               else
  1091.                 continue;
  1092.             }
  1093.             // If this item already exists in this container, don't paste, as 
  1094.             // this will result in the creation of multiple copies in the datasource
  1095.             // but will not result in an update of the UI. (In Short: we don't
  1096.             // handle multiple bookmarks well)
  1097.             ksRDFC.Init(this.bookmarksDS, parentRes);
  1098.             ix = ksRDFC.IndexOf(rCurrent);
  1099.             if (ix != -1)
  1100.               continue;
  1101.  
  1102.             ix = ksRDFC.IndexOf(currentRes);
  1103.             if (ix != -1)
  1104.               ksRDFC.InsertElementAt(rCurrent, ix+1, true);
  1105.             else
  1106.               ksRDFC.AppendElement(rCurrent);
  1107.       
  1108.             this.flushBMDatasource();
  1109.           }
  1110.         ]]></body>
  1111.       </method>
  1112.  
  1113.       <method name="copySelection">
  1114.         <body><![CDATA[
  1115.           const kSuppArrayContractID = "@mozilla.org/supports-array;1";
  1116.           const kSuppArrayIID = Components.interfaces.nsISupportsArray;
  1117.           var itemArray = Components.classes[kSuppArrayContractID].createInstance(kSuppArrayIID);
  1118.           const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
  1119.           const kRDFIID = Components.interfaces.nsIRDFService;
  1120.           const ksRDF = Components.classes[kRDFContractID].getService(kRDFIID);
  1121.           const kSuppWStringContractID = "@mozilla.org/supports-string;1";
  1122.           const kSuppWStringIID = Components.interfaces.nsISupportsString;
  1123.           var bmstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  1124.           var unicodestring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  1125.           var htmlstring = Components.classes[kSuppWStringContractID].createInstance(kSuppWStringIID);
  1126.         
  1127.           var sBookmarkItem = ""; var sTextUnicode = ""; var sTextHTML = "";
  1128.           var rangeCount = this.treeBoxObject.selection.getRangeCount();
  1129.           for (var i = rangeCount - 1; i >= 0; --i) {
  1130.             var rangeMin = {};
  1131.             var rangeMax = {};
  1132.             this.treeBoxObject.selection.getRangeAt(i, rangeMin, rangeMax);
  1133.             for (var j = rangeMax.value; j >= rangeMin.value; --j) {
  1134.               var res = this.treeBuilder.getResourceAtIndex(j);
  1135.               var urlRes = ksRDF.GetResource("http://home.netscape.com/NC-rdf#URL");
  1136.               var nameRes = ksRDF.GetResource("http://home.netscape.com/NC-rdf#Name");
  1137.               var typeRes = ksRDF.GetResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
  1138.  
  1139.               var url = this.db.GetTarget(res, urlRes, true);
  1140.               if (url)
  1141.                 url = url.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1142.               var name = this.db.GetTarget(res, nameRes, true);
  1143.               name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1144.  
  1145.               var bmkType = this.db.GetTarget(res, typeRes, true);
  1146.               if (bmkType)
  1147.                 bmkType = bmkType.Value;
  1148.  
  1149.               // XXXben is "\n" the best delimiter here? I think not. 
  1150.               sBookmarkItem += res.Value;
  1151.               if (bmkType)
  1152.               {
  1153.                 sBookmarkItem += "\t" + bmkType;
  1154.               }
  1155.               sBookmarkItem += "\n";
  1156.               sTextUnicode += url + "\n";
  1157.               sTextHTML += "<A HREF=\"" + url + "\">" + name + "</A>";
  1158.             }
  1159.           }
  1160.           
  1161.           const kXferableContractID = "@mozilla.org/widget/transferable;1";
  1162.           const kXferableIID = Components.interfaces.nsITransferable;
  1163.           var xferable = Components.classes[kXferableContractID].createInstance(kXferableIID);
  1164.       
  1165.           xferable.addDataFlavor("moz/bookmarkclipboarditem");
  1166.           bmstring.data = sBookmarkItem;
  1167.           xferable.setTransferData("moz/bookmarkclipboarditem", bmstring, sBookmarkItem.length*2)
  1168.           
  1169.           xferable.addDataFlavor("text/html");
  1170.           htmlstring.data = sTextHTML;
  1171.           xferable.setTransferData("text/html", htmlstring, sTextHTML.length*2)
  1172.           
  1173.           xferable.addDataFlavor("text/unicode");
  1174.           unicodestring.data = sTextUnicode;
  1175.           xferable.setTransferData("text/unicode", unicodestring, sTextUnicode.length*2)
  1176.           
  1177.           const kClipboardContractID = "@mozilla.org/widget/clipboard;1";
  1178.           const kClipboardIID = Components.interfaces.nsIClipboard;
  1179.           var clipboard = Components.classes[kClipboardContractID].getService(kClipboardIID);
  1180.           clipboard.setData(xferable, null, kClipboardIID.kGlobalClipboard);
  1181.         ]]></body>
  1182.       </method>
  1183.  
  1184.       <method name="deleteSelection">
  1185.         <body><![CDATA[
  1186.           const kRDFCContractID = "@mozilla.org/rdf/container;1";
  1187.           const kRDFContractID = "@mozilla.org/rdf/rdf-service;1";
  1188.           const kRDFIID = Components.interfaces.nsIRDFService;
  1189.           const kRDFCIID = Components.interfaces.nsIRDFContainer;
  1190.           const ksRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
  1191.           const ksRDF = Components.classes[kRDFContractID].getService(kRDFIID);
  1192.  
  1193.           var nextIndex = 0; // Used to determine which item to select after the deletion
  1194.                              // is performed.
  1195.  
  1196.           // Whether or not the selection contains items which cannot be deleted.                             
  1197.           // var selectionContainsImmutableItems = false;
  1198.  
  1199.           var boxObject = this.treeBoxObject;
  1200.           var treeBuilder = this.treeBuilder;
  1201.           var rangeCount = this.treeBoxObject.selection.getRangeCount();
  1202.           
  1203.           var rangeMax = { };
  1204.           var rangeMin = { };
  1205.           
  1206.           // First, walk the selection and see if there's anything we /can't/ 
  1207.           // delete. If so, present a warning dialog listing the items that 
  1208.           // we can't remove. 
  1209.           const kIMDSContractID = "@mozilla.org/rdf/datasource;1?name=in-memory-datasource";
  1210.           const kIMDSIID = Components.interfaces.nsIRDFDataSource;
  1211.           var imDS = Components.classes[kIMDSContractID].getService(kIMDSIID);
  1212.  
  1213.           const kRDFCUContractID = "@mozilla.org/rdf/container-utils;1";
  1214.           const kRDFCUIID = Components.interfaces.nsIRDFContainerUtils;
  1215.           const kRDFCU = Components.classes[kRDFCUContractID].getService(kRDFCUIID);
  1216.  
  1217.           /*
  1218.           var immutableList = this.rdf.GetResource("NC:ImmutableBookmarkList");
  1219.           var container = kRDFCU.MakeSeq(imDS, immutableList);
  1220.           
  1221.           for (var i = rangeCount - 1; i >= 0; --i) {
  1222.             boxObject.selection.getRangeAt(i, rangeMin, rangeMax);
  1223.             for (var j = rangeMax.value; j >= rangeMin.value; --j) {
  1224.               var resource = treeBuilder.getResourceAtIndex(j);
  1225.               var type = this.getTypeAtIndex(j);
  1226.               if (resource.Value == "NC:BookmarksRoot" || type == this.NC_NS + "IEFavoriteFolder")
  1227.                 continue;
  1228.               if (type != this.NC_NS + "Bookmark" && type != this.NC_NS + "BookmarkSeparator" && type != this.NC_NS + "Folder") {
  1229.                 // This delete operation contains items which cannot be deleted
  1230.                 // for one reason or another. Make a list of the items which
  1231.                 // we can't delete, and show a dialog. 
  1232.                 container.AppendElement(resource);
  1233.                 selectionContainsImmutableItems = true;
  1234.                 
  1235.                 // Now we need to ensure that this item's parent chain is not
  1236.                 // part of the current selection so that we don't delete that. We
  1237.                 // preserve selection on any deletable items in the folder chain
  1238.                 // however, and these are removed. 
  1239.                 for (var parentIndex = boxObject.view.getParentIndex(j);
  1240.                      parentIndex != -1;
  1241.                      parentIndex = boxObject.view.getParentIndex(parentIndex)) {
  1242.                   // Note that we don't combine this with the check in the 
  1243.                   // |while| above, as this would mean that if you had a folder 
  1244.                   // hierarchy consisting of:
  1245.                   // 
  1246.                   //   Folder ->    [ selected ]
  1247.                   //        Folder ->    [ not selected ]
  1248.                   //             Immutable item [ selected ]
  1249.                   //
  1250.                   // This code would not work and the containing folder would be
  1251.                   // deleted. 
  1252.                   if (boxObject.selection.isSelected(parentIndex))
  1253.                     boxObject.selection.toggleSelect(parentIndex);
  1254.                 }
  1255.               }
  1256.             }
  1257.           }
  1258.           
  1259.            // Present a dialog showing any items that we can't delete. 
  1260.            // if (selectionContainsImmutableItems) {
  1261.            //   openDialog("chrome://communicator/content/bookmarks/deleteBookmark.xul", "", "modal=yes,resizable=no", imDS);
  1262.             
  1263.             // Now clear the list of immutable items. 
  1264.             ksRDFC.Init(imDS, immutableList);
  1265.             var count = ksRDFC.GetCount();
  1266.             for (var i = 0; i < count; ++i)
  1267.               ksRDFC.RemoveElementAt(i, false);
  1268.           }
  1269.           */
  1270.           const kPrefSvcContractID = "@mozilla.org/preferences;1";
  1271.           const kPrefSvcIID = Components.interfaces.nsIPrefService;
  1272.           var prefSvc = Components.classes[kPrefSvcContractID].getService(kPrefSvcIID);
  1273.           var bookmarksPrefs = prefSvc.getBranch("browser.bookmarks.");
  1274.           for (i = rangeCount - 1; i >= 0; --i) {
  1275.             rangeMax = { };
  1276.             rangeMin = { };
  1277.             boxObject.selection.getRangeAt(i, rangeMin, rangeMax);
  1278.  
  1279.             for (var j = rangeMax.value; j >= rangeMin.value; --j) {
  1280.               var resource = treeBuilder.getResourceAtIndex(j);
  1281.               if (resource.Value == "NC:BookmarksRoot")
  1282.                 continue;
  1283.               // If we're removing the static root, make sure we set the pref that 
  1284.               // tells the bookmark service that it's been added so that after
  1285.               // we remove it, the bookmark service doesn't try to add it back
  1286.               // on subsequent launches. 
  1287.               if (resource.Value == "NC:SystemBookmarksStaticRoot")
  1288.                 bookmarksPrefs.setBoolPref("added_static_root", true);
  1289.  
  1290.               var currType = this.getTypeAtIndex(j);
  1291.               if (currType == this.NC_NS + "IEFavoriteFolder")
  1292.                 bookmarksPrefs.setBoolPref("import_system_favorites", false);
  1293.               else if (currType != this.NC_NS + "Bookmark" && 
  1294.                        currType != this.NC_NS + "BookmarkSeparator" &&
  1295.                        currType != this.NC_NS + "Folder")
  1296.                 continue;
  1297.               var parentIndex = boxObject.view.getParentIndex(j);
  1298.               var parent;
  1299.               if (parentIndex != -1)
  1300.                 parent = treeBuilder.getResourceAtIndex(parentIndex);
  1301.               else {
  1302.                 // assume its parent is the root
  1303.                 parent = ksRDF.GetResource("NC:BookmarksRoot");
  1304.               }
  1305.  
  1306.               /*
  1307.                 Use RDF datasource command APIs instead of trying to
  1308.                 directly manipulate the bookmarks datasource graph via:
  1309.  
  1310.                 ksRDFC.Init(this.bookmarksDS, parent); 
  1311.                 ksRDFC.RemoveElement(resource, true);
  1312.               */
  1313.  
  1314.               try {
  1315.                 var args = [{ property: this.NC_NS + "parent",
  1316.                               resource: parent.Value }];
  1317.                 this.doBookmarksCommand(resource.Value, this.NC_NS_CMD + "deletebookmark", args);
  1318.               }
  1319.               catch (e) {
  1320.               }
  1321.  
  1322.  
  1323.               nextIndex = j;
  1324.             }
  1325.           }
  1326.  
  1327.           // Select the next row          
  1328.           boxObject.selection.select(nextIndex);
  1329.         ]]></body>
  1330.       </method>
  1331.       <method name="openFindDialog">
  1332.         <body><![CDATA[
  1333.           openDialog("chrome://communicator/content/bookmarks/findBookmark.xul",
  1334.                      "FindBookmarksWindow",
  1335.                      "dialog=no,centerscreen,resizable=no,chrome,dependent");
  1336.         ]]></body>
  1337.       </method>
  1338.       <method name="canSendLink">
  1339.         <body><![CDATA[
  1340.           var selectedIndex = this.treeBoxObject.selection.currentIndex;
  1341.           return (this.treeBoxObject.selection.count == 1 &&
  1342.                   !this.treeBoxObject.view.isContainer(selectedIndex) &&
  1343.                   this.getTypeAtIndex(selectedIndex) != this.NC_NS + "BookmarkSeparator");
  1344.         ]]></body>
  1345.       </method>
  1346.       <method name="sendLink">
  1347.         <body><![CDATA[
  1348.           const currentRes = this.currentRes;
  1349.           var urlLiteral = this.db.GetTarget(currentRes, this.rdf.GetResource(this.NC_NS + "URL"), true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1350.           var nameLiteral = this.db.GetTarget(currentRes, this.rdf.GetResource(this.NC_NS + "Name"), true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1351.           sendLink(urlLiteral, nameLiteral);          
  1352.         ]]></body>
  1353.       </method>
  1354.       <method name="getAllCmds">
  1355.         <parameter name="aNodeID"/>
  1356.         <body><![CDATA[
  1357.           var type = BookmarksUtils.resolveType(aNodeID);
  1358.           if (!type) {
  1359.             if (aNodeID == "NC:PersonalToolbarFolder" || aNodeID == "NC:BookmarksRoot")
  1360.               type = "http://home.netscape.com/NC-rdf#Folder";
  1361.             else
  1362.               return null;
  1363.           }
  1364.           var commands = [];
  1365.           // menu order:
  1366.           // 
  1367.           // bm_open
  1368.           // bm_openfolder
  1369.           // bm_openinnewwindow
  1370.           // /* bm_openinnewtab not yet supported */
  1371.           // ---------------------
  1372.           // /* bm_find removed */
  1373.           // bm_newfolder
  1374.           // ---------------------
  1375.           // bm_cut
  1376.           // bm_copy
  1377.           // bm_paste
  1378.           // bm_fileBookmark
  1379.           // ---------------------
  1380.           // bm_delete
  1381.           // bm_rename
  1382.           // ---------------------
  1383.           // bm_properties
  1384.           switch (type) {
  1385.           case "http://home.netscape.com/NC-rdf#BookmarkSeparator":
  1386.             commands = ["bm_newfolder", "bm_separator", 
  1387.                         "bm_cut", "bm_copy", "bm_paste", "bm_separator",
  1388.                         "bm_delete"];
  1389.             break;
  1390.           case "http://home.netscape.com/NC-rdf#Bookmark":
  1391.             commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
  1392.                         "bm_newfolder", "bm_separator",
  1393.                         "bm_cut", "bm_copy", "bm_paste", "bm_fileBookmark", "bm_separator",
  1394.                         "bm_delete", "bm_rename", "bm_separator",
  1395.                         "bm_properties"];
  1396.             break;
  1397.           case "http://home.netscape.com/NC-rdf#Folder":
  1398.             commands = ["bm_openfolder", "bm_openinnewwindow", "bm_separator", 
  1399.                         "bm_newfolder", "bm_separator",
  1400.                         "bm_cut", "bm_copy", "bm_paste", "bm_fileBookmark", "bm_separator",
  1401.                         "bm_delete", "bm_rename", "bm_separator",
  1402.                         "bm_properties"];
  1403.             break;
  1404.           case "http://home.netscape.com/NC-rdf#IEFavoriteFolder":
  1405.             commands = ["bm_openfolder", "bm_separator",
  1406.                         "bm_delete"];
  1407.             break;
  1408.           case "http://home.netscape.com/NC-rdf#IEFavorite":
  1409.             commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
  1410.                         "bm_copy"];
  1411.             break;
  1412.           case "http://home.netscape.com/NC-rdf#FileSystemObject":
  1413.             commands = ["bm_open", "bm_openinnewwindow", /* "bm_openinnewtab", */ "bm_separator",
  1414.                         "bm_copy"];
  1415.             break;
  1416.           default: 
  1417.             var source = this.rdf.GetResource(aNodeID);
  1418.             return this.db.GetAllCmds(source);
  1419.           }
  1420.           return new CommandArrayEnumerator(commands);
  1421.         ]]></body>
  1422.       </method>
  1423.  
  1424.       <method name="flattenEnumerator">
  1425.         <parameter name="aEnumerator"/>
  1426.         <body><![CDATA[
  1427.           if ("_index" in aEnumerator)
  1428.             return aEnumerator._inner;
  1429.           
  1430.           var temp = [];
  1431.           while (aEnumerator.hasMoreElements()) 
  1432.             temp.push(aEnumerator.getNext());
  1433.           return temp;
  1434.         ]]></body>
  1435.       </method>
  1436.       <method name="findCommonNodes">
  1437.         <parameter name="aNewArray"/>
  1438.         <parameter name="aOldArray"/>
  1439.         <body><![CDATA[
  1440.           var common = [];
  1441.           for (var i = 0; i < aNewArray.length; ++i) {
  1442.             for (var j = 0; j < aOldArray.length; ++j) {
  1443.               if (common.length > 0 && common[common.length-1] == aNewArray[i])
  1444.                 continue;
  1445.               if (aNewArray[i] == aOldArray[j])
  1446.                 common.push(aNewArray[i]);
  1447.             }
  1448.           }
  1449.           return common;
  1450.         ]]></body>
  1451.       </method>
  1452.       <method name="createMenuItem">
  1453.         <parameter name="aDisplayName"/>
  1454.         <parameter name="aCommandName"/>
  1455.         <parameter name="aSelectedIndex"/>
  1456.         <body><![CDATA[
  1457.           const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  1458.           var xulElement = document.createElementNS(kXULNS, "menuitem");
  1459.           xulElement.setAttribute("cmd", aCommandName);
  1460.           xulElement.setAttribute("command", "cmd_" + aCommandName.substring(this.NC_NS_CMD.length));
  1461.           switch (aCommandName) {
  1462.           case this.NC_NS_CMD + "bm_open":
  1463.             xulElement.setAttribute("label", aDisplayName);
  1464.             xulElement.setAttribute("default", "true");
  1465.             break;
  1466.           case this.NC_NS_CMD + "bm_openfolder":
  1467.             aDisplayName = this.treeBoxObject.view.isContainerOpen(aSelectedIndex) ? this._bundle.GetStringFromName("cmd_bm_openfolder2") : aDisplayName;
  1468.             xulElement.setAttribute("label", aDisplayName);
  1469.             xulElement.setAttribute("default", "true");
  1470.             break;
  1471.           case this.NC_NS_CMD + "bm_renamebookmark":
  1472.             if (!document.popupNode.hasAttribute("type")) {
  1473.               xulElement.setAttribute("label", this._bundle.GetStringFromName("cmd_bm_renamebookmark2"));
  1474.               xulElement.setAttribute("cmd", (this.NC_NS_CMD + "bm_editurl"));
  1475.             }
  1476.             else
  1477.               xulElement.setAttribute("label", aDisplayName);
  1478.             break;
  1479.           default:
  1480.             xulElement.setAttribute("label", aDisplayName);
  1481.             break;
  1482.           }
  1483.           return xulElement;
  1484.         ]]></body>
  1485.       </method>
  1486.       <method name="getCommandName">
  1487.         <parameter name="aCommand"/>
  1488.         <body><![CDATA[
  1489.           var cmdName = aCommand.substring(this.NC_NS_CMD.length);
  1490.           try {
  1491.             // Note: this will succeed only if there's a string in the bookmarks
  1492.             //       string bundle for this command name. Otherwise, <xul:stringbundle/>
  1493.             //       will throw, we'll catch & stifle the error, and look up the command
  1494.             //       name in the datasource. 
  1495.             return BookmarksUtils.getLocaleString("cmd_" + cmdName);
  1496.           }
  1497.           catch (e) {
  1498.             // XXX - WORK TO DO HERE! (rjc will cry if we don't fix this) 
  1499.             // need to ask the ds for the commands for this node, however we don't
  1500.             // have the right params. This is kind of a problem. 
  1501.             dump("*** BAD! EVIL! WICKED! NO! ACK! ARGH! ORGH!\n");
  1502.             const rName = this.rdf.GetResource(this.NC_NS + "Name");
  1503.             const rSource = this.rdf.GetResource(aCommand);
  1504.             return this.db.GetTarget(rSource, rName, true).Value;
  1505.           }
  1506.         ]]></body>
  1507.       </method>
  1508.       <method name="createContextMenu">
  1509.         <parameter name="aEvent"/>
  1510.         <body><![CDATA[
  1511.           var popup = aEvent.target;
  1512.           // clear out the old context menu contents (if any)
  1513.           while (popup.hasChildNodes()) 
  1514.             popup.removeChild(popup.firstChild);
  1515.           
  1516.           var popupNode = document.popupNode;
  1517.           var commonCommands = [];
  1518.           var rangeCount = this.treeBoxObject.selection.getRangeCount();
  1519.           for (var i = rangeCount - 1; i >= 0; --i) {
  1520.             var minRange = {};
  1521.             var maxRange = {};
  1522.             this.treeBoxObject.selection.getRangeAt(i, minRange, maxRange);          
  1523.             for (var j = maxRange.value; j >= minRange.value; --j) {
  1524.               var nodeURI = this.treeBuilder.getResourceAtIndex(j).Value;
  1525.               var commands = this.getAllCmds(nodeURI);
  1526.               if (!commands) {
  1527.                 aEvent.preventDefault();
  1528.                 return;
  1529.               }
  1530.               commands = this.flattenEnumerator(commands);
  1531.               if (!commonCommands.length) commonCommands = commands;
  1532.               commonCommands = this.findCommonNodes(commands, commonCommands);
  1533.             }
  1534.           }
  1535.           if (!commonCommands.length) {
  1536.             aEvent.preventDefault();
  1537.             return;
  1538.           }
  1539.           
  1540.           // Now that we should have generated a list of commands that is valid
  1541.           // for the entire selection, build a context menu.
  1542.           for (i = 0; i < commonCommands.length; ++i) {
  1543.             const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  1544.             var currCommand = commonCommands[i].QueryInterface(Components.interfaces.nsIRDFResource).Value;
  1545.             var element = null;
  1546.             if (currCommand != this.NC_NS_CMD + "bm_separator") {
  1547.               var commandName = this.getCommandName(currCommand);
  1548.               var selectedIndex = this.treeBoxObject.selection.currentIndex;
  1549.               element = this.createMenuItem(commandName, currCommand, selectedIndex);
  1550.             }
  1551.             else if (i != 0 && i < commonCommands.length-1) {
  1552.               // Never append a separator as the first or last element in a context
  1553.               // menu.
  1554.               element = document.createElementNS(kXULNS, "menuseparator");
  1555.             }
  1556.             
  1557.             if (element) 
  1558.               popup.appendChild(element);
  1559.           }
  1560.         ]]></body>
  1561.       </method>
  1562.       <method name="openPropertiesForItem">
  1563.         <body><![CDATA[
  1564.           // XXX if not bookmarks separator
  1565.           var itemURI = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1566.           openDialog("chrome://communicator/content/bookmarks/bm-props.xul",
  1567.                      "", "centerscreen,chrome,dialog=no,resizable=no,dependent", 
  1568.                      itemURI);
  1569.         ]]></body>
  1570.       </method>
  1571.       <method name="setAsPersonalToolbarFolder">
  1572.         <body><![CDATA[
  1573.           var selectedURI = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1574.           var args = [];
  1575.           this.doBookmarksCommand(selectedURI, this.NC_NS_CMD + "setpersonaltoolbarfolder", args);
  1576.  
  1577.         ]]></body>
  1578.       </method>
  1579.       <method name="setAsNewSearchFolder">
  1580.         <body><![CDATA[
  1581.           var selectedURI = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1582.           var args = [];
  1583.           this.doBookmarksCommand(selectedURI, this.NC_NS_CMD + "setnewsearchfolder", args);
  1584.         ]]></body>
  1585.       </method>
  1586.       <method name="setAsNewBookmarkFolder">
  1587.         <body><![CDATA[
  1588.           var selectedURI = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1589.           var args = [];
  1590.           this.doBookmarksCommand(selectedURI, this.NC_NS_CMD + "setnewbookmarkfolder", args);
  1591.         ]]></body>
  1592.       </method>
  1593.       <method name="addBookmark">
  1594.         <body><![CDATA[
  1595.           const currentIndex = this.currentIndex;
  1596.  
  1597.           var parentIndex;
  1598.           if (this.treeBoxObject.view.rowCount) {
  1599.             if (this.treeBoxObject.view.isContainerOpen(currentIndex))
  1600.               parentIndex = currentIndex;
  1601.             else
  1602.               parentIndex = this.treeBoxObject.view.getParentIndex(currentIndex);
  1603.           }
  1604.           else
  1605.             parentIndex = -1;
  1606.           var parentURI;
  1607.           if (parentIndex == -1)
  1608.             parentURI = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1609.           else
  1610.             parentURI = this.treeBuilder.getResourceAtIndex(parentIndex).Value;
  1611.             
  1612.           var rv = { newBookmark: null };
  1613.           openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "", 
  1614.                      "centerscreen,chrome,modal=yes,dialog=yes,resizable=no", null, null, parentURI, null, "newBookmark", rv);
  1615.           if (rv.newBookmark) {
  1616.             var ind = this.treeBuilder.getIndexOfResource(rv.newBookmark);
  1617.             this.treeBoxObject.selection.select(ind);
  1618.           }
  1619.         ]]></body>
  1620.       </method>
  1621.       <method name="fileBookmark">
  1622.         <body><![CDATA[
  1623.           // XXX folder arg
  1624.           var rv = { selectedFolder: null };          
  1625.           openDialog("chrome://communicator/content/bookmarks/addBookmark.xul", "", 
  1626.                      "centerscreen,chrome,modal=yes,dialog=yes,resizable=yes", null, null, null, null, "selectFolder", rv);
  1627.           if (rv.selectedFolder) {
  1628.             var rangeCount = this.treeBoxObject.selection.getRangeCount();
  1629.             for (var k = rangeCount - 1; k >= 0; --k) {
  1630.               var rangeMin = {};
  1631.               var rangeMax = {};
  1632.               this.treeBoxObject.selection.getRangeAt(k, rangeMin, rangeMax);
  1633.               for (var i = rangeMax.value; i >= rangeMin.value; --i) {
  1634.                 var selected = this.treeBuilder.getResourceAtIndex(i);
  1635.                 if (selected.Value == rv.selectedFolder) 
  1636.                   return; // Selection contains the target folder. Just fail silently.
  1637.                 var additiveFlag = false;
  1638.                 var parentIndex = this.treeBoxObject.view.getParentIndex(i);
  1639.                 var parentURI;
  1640.                 if (parentIndex == -1)
  1641.                   parentURI = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1642.                 else
  1643.                   parentURI = this.treeBuilder.getResourceAtIndex(parentIndex).Value;
  1644.                 this.moveBookmark(selected.Value, parentURI, rv.selectedFolder);
  1645.                // gBookmarksShell.selectFolderItem(rv.selectedFolder, selected.Value, additiveFlag);
  1646.                 if (!additiveFlag) additiveFlag = true;
  1647.               }
  1648.             }
  1649.             this.flushBMDatasource();
  1650.           }
  1651.         ]]></body>
  1652.       </method>
  1653.       <method name="moveBookmark">
  1654.         <parameter name="aBookmarkURI"/>
  1655.         <parameter name="aFromFolderURI"/>
  1656.         <parameter name="aToFolderURI"/>
  1657.         <body><![CDATA[
  1658.           const kRDFCContractID = "@mozilla.org/rdf/container;1";
  1659.           const kRDFCIID = Components.interfaces.nsIRDFContainer;
  1660.           const kRDFC = Components.classes[kRDFCContractID].getService(kRDFCIID);
  1661.           const krSrc = this.rdf.GetResource(aBookmarkURI);
  1662.           const krOldParent = this.rdf.GetResource(aFromFolderURI);
  1663.           const krNewParent = this.rdf.GetResource(aToFolderURI);
  1664.           kRDFC.Init(this.bookmarksDS, krNewParent);
  1665.           kRDFC.AppendElement(krSrc);
  1666.           kRDFC.Init(this.bookmarksDS, krOldParent);
  1667.           kRDFC.RemoveElement(krSrc, true);
  1668.         ]]></body>
  1669.       </method>
  1670.       <method name="importBookmarks">
  1671.         <body><![CDATA[
  1672.           try {
  1673.             const kFilePickerContractID = "@mozilla.org/filepicker;1";
  1674.             const kFilePickerIID = Components.interfaces.nsIFilePicker;
  1675.             const kFilePicker = Components.classes[kFilePickerContractID].createInstance(kFilePickerIID);
  1676.          
  1677.             const kTitle = this._bundle.GetStringFromName("SelectImport");
  1678.             kFilePicker.init(window, kTitle, kFilePickerIID["modeOpen"]);
  1679.             kFilePicker.appendFilters(kFilePickerIID.filterHTML | kFilePickerIID.filterAll);
  1680.             var fileName;
  1681.             if (kFilePicker.show() != kFilePickerIID.returnCancel) {
  1682.               fileName = kFilePicker.fileURL.spec;
  1683.               if (!fileName) return;
  1684.             }
  1685.             else return;
  1686.           }
  1687.           catch (e) {
  1688.             return;
  1689.           }
  1690.           var seln = null;
  1691.           try {
  1692.             seln = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1693.           }
  1694.           catch(ex) {
  1695.             seln = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1696.           }
  1697.           var args = [{ property: this.NC_NS + "URL", literal: fileName}];
  1698.           this.doBookmarksCommand(seln, this.NC_NS_CMD + "import", args);
  1699.         ]]></body>
  1700.       </method>
  1701.       <method name="exportBookmarks">
  1702.         <body><![CDATA[
  1703.           try {
  1704.             const kFilePickerContractID = "@mozilla.org/filepicker;1";
  1705.             const kFilePickerIID = Components.interfaces.nsIFilePicker;
  1706.             const kFilePicker = Components.classes[kFilePickerContractID].createInstance(kFilePickerIID);
  1707.             
  1708.             const kTitle = this._bundle.GetStringFromName("EnterExport");
  1709.             const kRDFExportIndex = 2;
  1710.             kFilePicker.init(window, kTitle, kFilePickerIID["modeSave"]);
  1711.             kFilePicker.appendFilters(kFilePickerIID.filterHTML | kFilePickerIID.filterAll);
  1712.             kFilePicker.appendFilter("RDF", "*.rdf");   // index 2: kRDFExportIndex
  1713.             kFilePicker.defaultString = "bookmarks.html";
  1714.             var fileName;
  1715.             if (kFilePicker.show() != kFilePickerIID.returnCancel) {
  1716.               fileName = kFilePicker.fileURL.spec;
  1717.               if (!fileName) return;
  1718.             }
  1719.             else return;
  1720.           }
  1721.           catch (e) {
  1722.             return;
  1723.           }
  1724.           var seln = null;
  1725.           try {
  1726.             seln = this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex).Value;
  1727.           }
  1728.           catch(ex) {
  1729.             seln = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1730.           }
  1731.           var args = [{ property: this.NC_NS + "URL", literal: fileName},
  1732.             { property: this.RDF_NS + "type",
  1733.               literal: (kFilePicker.filterIndex == kRDFExportIndex) ? "RDF" : "HTML"}];
  1734.           this.doBookmarksCommand(seln, this.NC_NS_CMD + "export", args);
  1735.         ]]></body>
  1736.       </method>
  1737.       <method name="openFolderInNewWindow">
  1738.         <body><![CDATA[
  1739.           var selectedIndex = this.treeBoxObject.selection.currentIndex;
  1740.           var selectedURI = this.treeBuilder.getResourceAtIndex(selectedIndex).Value;
  1741.           openDialog("chrome://communicator/content/bookmarks/bookmarks.xul", 
  1742.                      "", "chrome,all,dialog=no", selectedURI);
  1743.         ]]>
  1744.         </body>
  1745.       </method>
  1746.       <method name="createNewSeparator">
  1747.         <body><![CDATA[
  1748.           var currIndex = this.treeBoxObject.selection.currentIndex;
  1749.           if (currIndex == -1)
  1750.             currIndex = this.treeBoxObject.view.rowCount - 1;
  1751.           var parentIndex = this.treeBoxObject.view.getParentIndex(currIndex);
  1752.           var parentValue;
  1753.           if (parentIndex == -1)
  1754.             parentValue = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1755.           else
  1756.             parentValue = this.treeBuilder.getResourceAtIndex(parentIndex).Value;
  1757.           args = [{ property: this.NC_NS + "parent", 
  1758.                     resource: parentValue }];
  1759.           this.doBookmarksCommand(this.treeBuilder.getResourceAtIndex(currIndex).Value, 
  1760.                                   this.NC_NS_CMD + "newseparator", args);
  1761.         ]]></body>
  1762.       </method>
  1763.       <method name="createNewFolder">
  1764.         <body><![CDATA[
  1765.           const kPromptSvcContractID = "@mozilla.org/embedcomp/prompt-service;1";
  1766.           const kPromptSvcIID = Components.interfaces.nsIPromptService;
  1767.           const kPromptSvc = Components.classes[kPromptSvcContractID].getService(kPromptSvcIID);
  1768.           var defaultValue  = this._bundle.GetStringFromName("ile_newfolder");
  1769.           var dialogTitle   = this._bundle.GetStringFromName("newfolder_dialog_title");
  1770.           var dialogMsg     = this._bundle.GetStringFromName("newfolder_dialog_msg");
  1771.           var stringValue   = { value: defaultValue };
  1772.           var relativeIndex = this.currentIndex;
  1773.           const currentIndex  = this.currentIndex;
  1774.           var parentRes;
  1775.           var isParent = false;
  1776.           if (kPromptSvc.prompt(window, dialogTitle, dialogMsg, stringValue, null, { value: 0 })) {
  1777.             if (currentIndex != -1 && currentIndex < this.treeBoxObject.view.rowCount) {
  1778.               // If it's an open container, the relative index should be that of the last child. 
  1779.               if (this.treeBoxObject.view.isContainerOpen(currentIndex)) {
  1780.                 isParent = true;
  1781.                 var index = currentIndex + 1;
  1782.                 while (index < this.treeBoxObject.view.rowCount
  1783.                        && this.treeBoxObject.view.getParentIndex(index) == currentIndex)
  1784.                     ++index;
  1785.                   relativeIndex = index - 1;
  1786.               }
  1787.               if (isParent) {
  1788.                 parentRes = this.treeBuilder.getResourceAtIndex(currentIndex);
  1789.               } else {
  1790.                 parentRes = this.parentRes;
  1791.               }
  1792.             }
  1793.             else {
  1794.               parentRes = this.rdf.GetResource("NC:BookmarksRoot");
  1795.               relativeIndex = -1;
  1796.             }
  1797.             var args = [{ property: this.NC_NS + "parent",
  1798.                           resource: parentRes.Value },
  1799.                         { property: this.NC_NS + "Name",
  1800.                           literal:  stringValue.value }];
  1801.             this.bookmarksDS.AddObserver(this.newFolderRDFObserver);
  1802.             var relativeRes;
  1803.             if (relativeIndex != -1)
  1804.               relativeRes = this.treeBuilder.getResourceAtIndex(relativeIndex).Value;
  1805.             else
  1806.               relativeRes = this.rdf.GetResource("NC:BookmarksRoot").Value;
  1807.    
  1808.             this.doBookmarksCommand(relativeRes, this.NC_NS_CMD + "newfolder", args);
  1809.             this.bookmarksDS.RemoveObserver(this.newFolderRDFObserver);
  1810.           }
  1811.       ]]></body>
  1812.       </method>
  1813.      
  1814.       <method name="validOpenClickConditions">
  1815.       <parameter name="aEvent"/>
  1816.         <body><![CDATA[
  1817.           if (aEvent.button != 0 || aEvent.originalTarget.localName != "treechildren")
  1818.             return false;
  1819.           var row = {};
  1820.           var col = {};
  1821.           var obj = {};
  1822.           this.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY, row, col, obj);
  1823.           if (row.value == -1 || obj.value == "twisty")
  1824.             return false;
  1825.           return true;
  1826.         ]]></body>
  1827.       </method>
  1828.  
  1829.       // requires utilityOverlay.js if opening in new window for opentopwin()
  1830.       <method name="openItem">
  1831.       <parameter name="aEvent"/>
  1832.       <parameter name="aInNewWindow"/>
  1833.       <parameter name="aOpenGroups"/>
  1834.         <body><![CDATA[
  1835.           var groupRes = this.rdf.GetResource(this.NC_NS + "FolderGroup");  
  1836.           var groupTarget = this.db.GetTarget(this.treeBuilder.getResourceAtIndex(this.treeBoxObject.selection.currentIndex), 
  1837.                                               groupRes, true);
  1838.  
  1839.           if (groupTarget && (!aOpenGroups || aEvent.detail > 1))
  1840.             return;
  1841.           
  1842.           if (!groupTarget && this.treeBoxObject.view.isContainer(this.treeBoxObject.selection.currentIndex)) {
  1843.             if (this.clickCount == 1) {
  1844.               var row = { };
  1845.               var col = { };
  1846.               var elt = { };
  1847.               this.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY, row, col, elt);
  1848.               if (row.value >= 0) 
  1849.                 this.treeBoxObject.view.toggleOpenState(row.value);
  1850.             }
  1851.             return;
  1852.           }
  1853.           
  1854.           var urlRes, urlValue, w;
  1855.           if (aEvent && aEvent.altKey)
  1856.             this.openPropertiesForItem();
  1857.           else if (aInNewWindow) {
  1858.             var seln = this.treeBoxObject.selection;
  1859.             var rangeCount = seln.getRangeCount();
  1860.             for (var i = rangeCount - 1; i >= 0; --i) {
  1861.               var rangeMin = { };
  1862.               var rangeMax = { };
  1863.               seln.getRangeAt(i, rangeMin, rangeMax);
  1864.               for (var k = rangeMax.value; k >= rangeMin.value; --k) {
  1865.                 groupTarget = this.db.GetTarget(this.treeBuilder.getResourceAtIndex(k), 
  1866.                                                 groupRes, true);
  1867.           
  1868.                 if (!groupTarget) {
  1869.                   urlRes = this.rdf.GetResource(this.NC_NS + "URL");
  1870.                   urlValue = this.db.GetTarget(this.treeBuilder.getResourceAtIndex(k), urlRes, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1871.                   // Ignore "NC:" and empty urls
  1872.                   if (!urlValue || urlValue.substring(0,3) == "NC:") return;
  1873.                 }
  1874.                 else 
  1875.                   urlValue = "about:blank";
  1876.                 
  1877.                 w = openDialog(this._browserURL, "_blank", "chrome,all,dialog=no", urlValue);
  1878.                 if (groupTarget)
  1879.                   w.OpenBookmarkGroupFromResource(this.treeBuilder.getResourceAtIndex(k),
  1880.                                                   this.db, this.rdf);
  1881.               }
  1882.             }
  1883.           }
  1884.           else {
  1885.             groupTarget = this.db.GetTarget(this.currentRes, groupRes, true);
  1886.             if (!groupTarget) {
  1887.               urlRes = this.rdf.GetResource(this.NC_NS + "URL");
  1888.               urlValue = this.db.GetTarget(this.currentRes, urlRes, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  1889.               // Ignore "NC:" and empty urls
  1890.               if (!urlValue || urlValue.substring(0,3) == "NC:") return;
  1891.             }
  1892.             else 
  1893.               urlValue = "about:blank";
  1894.               
  1895.             if (groupTarget) {
  1896.               w = getTopWin();
  1897.               w.focus();
  1898.               w.OpenBookmarkGroupFromResource(this.currentRes, this.db, this.rdf);
  1899.             }
  1900.             else {
  1901.               openTopWin(urlValue);
  1902.             }
  1903.           }
  1904.           if (aEvent) 
  1905.             aEvent.preventBubble();
  1906.         ]]></body>
  1907.       </method>
  1908.       <method name="canPaste">
  1909.         <body><![CDATA[
  1910.           // XXXben
  1911.           return true;
  1912.         ]]></body>
  1913.       </method>
  1914.       <method name="searchBookmarks">
  1915.       <parameter name="aInput"/>
  1916.         <body><![CDATA[
  1917.           if (!aInput) 
  1918.             this.tree.setAttribute("ref", "NC:BookmarksRoot");
  1919.           else {
  1920.             const ref = "find:datasource=rdf:bookmarks&match=http://home.netscape.com/NC-rdf#Name&method=contains&text=";
  1921.             this.tree.setAttribute("ref", ref + escape(aInput));
  1922.           }
  1923.         ]]></body>
  1924.       </method>
  1925.     </implementation>
  1926.   </binding>
  1927.   <!-- Full Bookmarks Tree, multi-columned -->
  1928.   <!-- Localize column labels! -->
  1929.   <binding id="bookmarks-tree-full" extends="chrome://communicator/content/bookmarks/bookmarks.xml#bookmarks-tree">
  1930.     <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:xbl="http://www.mozilla.org/xbl"
  1931.                  contextmenu="_child">
  1932.       <!-- XXXben need focus event handler for cmd update -->
  1933.         <menupopup id="bmContext" 
  1934.                    onpopupshowing="this.parentNode.createContextMenu(event);"/>
  1935.       <vbox flex="1">
  1936.         <tree anonid="bookmarks-tree" flex="1" class="plain" enableColumnDrag="true"
  1937.                   datasources="rdf:bookmarks rdf:internetsearch rdf:files rdf:localsearch" ref="NC:BookmarksRoot" flags="dont-build-content"
  1938.                   onkeypress="if (event.keyCode == 13) this.parentNode.parentNode.openItem(event, false, false);"
  1939.                   ondblclick="if (this.parentNode.parentNode.validOpenClickConditions(event)) this.parentNode.parentNode.openItem(event, false, false);"
  1940.                   ondraggesture="if (event.originalTarget.localName == 'treechildren') nsDragAndDrop.startDrag(event, this.parentNode.parentNode.DNDObserver);"
  1941.                   onselect="this.treeBoxObject.view.selectionChanged();">
  1942.           <template xmlns:nc="http://home.netscape.com/NC-rdf#">
  1943.             <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
  1944.               <treechildren>
  1945.                 <treeitem uri="rdf:*">
  1946.                   <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type separator"/>
  1947.                 </treeitem>
  1948.               </treechildren>
  1949.             </rule>
  1950.             <rule nc:FolderGroup="true">
  1951.               <treechildren>
  1952.                 <treeitem uri="rdf:*">
  1953.                   <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type rdf:http://home.netscape.com/NC-rdf#loading rdf:http://home.netscape.com/WEB-rdf#status">
  1954.                     <treecell properties="group" label="rdf:http://home.netscape.com/NC-rdf#Name" />
  1955.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#URL" />
  1956.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#ShortcutURL" />
  1957.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#Description" />
  1958.                     <treecell label="rdf:http://home.netscape.com/WEB-rdf#LastVisitDate"/>
  1959.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" />
  1960.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate" />
  1961.                   </treerow>
  1962.                 </treeitem>
  1963.               </treechildren>
  1964.             </rule>
  1965.             <rule>
  1966.               <treechildren>
  1967.                 <treeitem uri="rdf:*">
  1968.                   <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type rdf:http://home.netscape.com/NC-rdf#loading rdf:http://home.netscape.com/WEB-rdf#status">
  1969.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#Name" />
  1970.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#URL" />
  1971.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#ShortcutURL" />
  1972.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#Description" />
  1973.                     <treecell label="rdf:http://home.netscape.com/WEB-rdf#LastVisitDate"/>
  1974.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" />
  1975.                     <treecell label="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate" />
  1976.                   </treerow>
  1977.                 </treeitem>
  1978.               </treechildren>
  1979.             </rule>
  1980.           </template>
  1981.           <treecols anonid="treecols">
  1982.             <treecol id="Name" label="&treecol.name.label;" flex="1" primary="true" 
  1983.                          class="sortDirectionIndicator" 
  1984.                          persist="width hidden sortActive sortDirection ordinal" 
  1985.                          sort="rdf:http://home.netscape.com/NC-rdf#Name"
  1986.                          sortActive="true" sortDirection="none"/>
  1987.             <splitter class="tree-splitter" />
  1988.             <treecol id="URL" label="&treecol.url.label;" 
  1989.                          flex="1" class="sortDirectionIndicator" 
  1990.                          sort="rdf:http://home.netscape.com/NC-rdf#URL" 
  1991.                          persist="width hidden sortActive sortDirection ordinal" />
  1992.             <splitter class="tree-splitter" />
  1993.             <treecol id="ShortcutURL" label="&treecol.shortcut.label;" 
  1994.                          hidden="true" flex="1" class="sortDirectionIndicator" 
  1995.                          persist="hidden width sortActive sortDirection ordinal" 
  1996.                          sort="rdf:http://home.netscape.com/NC-rdf#ShortcutURL"/>
  1997.             <splitter class="tree-splitter"/>
  1998.             <treecol id="Description" label="&treecol.description.label;" 
  1999.                          hidden="true" flex="1" class="sortDirectionIndicator" 
  2000.                          persist="hidden width sortActive sortDirection ordinal" 
  2001.                          sort="rdf:http://home.netscape.com/NC-rdf#Description"/>
  2002.             <splitter class="tree-splitter"/>
  2003.             <treecol id="AddDate" label="&treecol.addedon.label;" 
  2004.                          hidden="true" flex="1" class="sortDirectionIndicator" 
  2005.                          sort="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" 
  2006.                          persist="width hidden sortActive sortDirection ordinal" />
  2007.             <splitter class="tree-splitter" />
  2008.             <treecol id="LastModDate" label="&treecol.lastmod.label;" 
  2009.                          hidden="true" flex="1" class="sortDirectionIndicator" 
  2010.                          sort="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate" 
  2011.                          persist="width hidden sortActive sortDirection ordinal" />
  2012.             <splitter class="tree-splitter" />
  2013.             <treecol id="LastVisitDate" label="&treecol.lastvisit.label;" 
  2014.                          hidden="true" flex="1" class="sortDirectionIndicator" 
  2015.                          sort="rdf:http://home.netscape.com/NC-rdf#LastVisitDate" 
  2016.                          persist="width hidden sortActive sortDirection ordinal" />
  2017.           </treecols>
  2018.         </tree>
  2019.         <statusbar class="chromeclass-status" xbl:inherits="hidden=hidestatusbar" hidden="false">
  2020.           <statusbarpanel anonid="statusbar-text" flex="1"/>
  2021.         </statusbar>
  2022.       </vbox>
  2023.     </xbl:content>
  2024.     <implementation>
  2025.       <field name="clickCount">2</field>
  2026.     </implementation>
  2027.   </binding>
  2028.   <!-- Single column tree -->
  2029.   <binding id="bookmarks-tree-name" extends="chrome://communicator/content/bookmarks/bookmarks.xml#bookmarks-tree">
  2030.     <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
  2031.                  xmlns:xbl="http://www.mozilla.org/xbl" contextmenu="_child">
  2032.       <menupopup id="bmContext" 
  2033.                  onpopupshowing="this.parentNode.createContextMenu(event);"/>
  2034.       <tree anonid="bookmarks-tree" flex="1" hidecolumnpicker="true" class="plain"
  2035.                 datasources="rdf:bookmarks rdf:internetsearch rdf:files rdf:localsearch" ref="NC:BookmarksRoot" flags="dont-build-content"
  2036.                 onkeypress="if (event.keyCode == 13) this.parentNode.openItem(event, false, true);"
  2037.                 ondraggesture="if (event.originalTarget.localName == 'treechildren') nsDragAndDrop.startDrag(event, this.parentNode.DNDObserver);"
  2038.                 onclick="if (this.parentNode.validOpenClickConditions(event)) this.parentNode.openItem(event, false, true);"
  2039.                 onselect="this.parentNode.treeBoxObject.view.selectionChanged();">
  2040.         <template xmlns:nc="http://home.netscape.com/NC-rdf#">
  2041.           <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
  2042.             <treechildren>
  2043.               <treeitem uri="rdf:*">
  2044.                 <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type separator" />
  2045.               </treeitem>
  2046.             </treechildren>
  2047.           </rule>
  2048.           <rule nc:FolderGroup="true">
  2049.             <treechildren>
  2050.               <treeitem uri="rdf:*">
  2051.                 <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type rdf:http://home.netscape.com/NC-rdf#loading rdf:http://home.netscape.com/WEB-rdf#status">
  2052.                   <treecell properties="group hidetwisty" label="rdf:http://home.netscape.com/NC-rdf#Name" />
  2053.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#URL" />
  2054.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#ShortcutURL" />
  2055.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#Description" />
  2056.                   <treecell label="rdf:http://home.netscape.com/WEB-rdf#LastVisitDate"/>
  2057.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" />
  2058.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate" />
  2059.                 </treerow>
  2060.               </treeitem>
  2061.             </treechildren>
  2062.           </rule>
  2063.           <rule>
  2064.             <treechildren>
  2065.               <treeitem uri="rdf:*">
  2066.                 <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type rdf:http://home.netscape.com/NC-rdf#loading rdf:http://home.netscape.com/WEB-rdf#status">
  2067.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#Name" />
  2068.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#URL" />
  2069.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#ShortcutURL" />
  2070.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#Description" />
  2071.                   <treecell label="rdf:http://home.netscape.com/WEB-rdf#LastVisitDate"/>
  2072.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" />
  2073.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate" />
  2074.                 </treerow>
  2075.               </treeitem>
  2076.             </treechildren>
  2077.           </rule>
  2078.         </template>
  2079.         <treecols anonid="treecols">
  2080.           <treecol id="Name" label="&treecol.name.label;" flex="1" primary="true" 
  2081.                         class="sortDirectionIndicator" 
  2082.                         persist="width hidden sortActive sortDirection ordinal" 
  2083.                         sort="rdf:http://home.netscape.com/NC-rdf#Name"
  2084.                         sortActive="true" sortDirection="none"/>
  2085.           <splitter class="tree-splitter" />
  2086.           <treecol id="URL" label="&treecol.url.label;" 
  2087.                         flex="1" class="sortDirectionIndicator" 
  2088.                         sort="rdf:http://home.netscape.com/NC-rdf#URL" 
  2089.                         hidden="true"/>
  2090.           <splitter class="tree-splitter" />
  2091.           <treecol id="ShortcutURL" label="&treecol.shortcut.label;" 
  2092.                         hidden="true" flex="1" class="sortDirectionIndicator" 
  2093.                         sort="rdf:http://home.netscape.com/NC-rdf#ShortcutURL"/>
  2094.           <splitter class="tree-splitter" />
  2095.           <treecol id="Description" label="&treecol.description.label;" 
  2096.                         hidden="true" flex="1" class="sortDirectionIndicator" 
  2097.                         sort="rdf:http://home.netscape.com/NC-rdf#Description"/>
  2098.           <splitter class="tree-splitter" />
  2099.           <treecol id="AddDate" label="&treecol.addedon.label;" 
  2100.                         hidden="true" flex="1" class="sortDirectionIndicator" 
  2101.                         sort="rdf:http://home.netscape.com/NC-rdf#BookmarkAddDate" />
  2102.           <splitter class="tree-splitter" />
  2103.           <treecol id="LastModDate" label="&treecol.lastmod.label;" 
  2104.                         hidden="true" flex="1" class="sortDirectionIndicator" 
  2105.                         sort="rdf:http://home.netscape.com/NC-rdf#LastModifiedDate"  />
  2106.           <splitter class="tree-splitter" />
  2107.           <treecol id="LastVisitDate" label="&treecol.lastvisit.label;" 
  2108.                         hidden="true" flex="1" class="sortDirectionIndicator" 
  2109.                         sort="rdf:http://home.netscape.com/NC-rdf#LastVisitDate" />
  2110.         </treecols>
  2111.       </tree>
  2112.     </xbl:content>
  2113.     <implementation>
  2114.       <field name="clickCount">1</field>
  2115.     </implementation>
  2116.   </binding>
  2117.   <!-- Tree with folders only -->
  2118.   <binding id="bookmarks-tree-folders" extends="chrome://communicator/content/bookmarks/bookmarks.xml#bookmarks-tree">
  2119.     <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:xbl="http://www.mozilla.org/xbl">
  2120.       <tree anonid="bookmarks-tree" flex="1" hidecolumnpicker="true"
  2121.                 datasources="rdf:bookmarks rdf:internetsearch rdf:files rdf:localsearch" ref="NC:BookmarksRoot" flags="dont-build-content"
  2122.                 onselect="this.parentNode.treeBoxObject.view.selectionChanged();">
  2123.         <template>
  2124.           <rule iscontainer="true">
  2125.             <treechildren>
  2126.               <treeitem uri="rdf:*">
  2127.                 <treerow properties="rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#type rdf:http://home.netscape.com/NC-rdf#loading rdf:http://home.netscape.com/WEB-rdf#status">
  2128.                   <treecell label="rdf:http://home.netscape.com/NC-rdf#Name" />
  2129.                 </treerow>
  2130.               </treeitem>
  2131.             </treechildren>
  2132.           </rule>
  2133.         </template>
  2134.         <treecols anonid="treecols">
  2135.           <treecol id="Name" label="&treecol.name.label;" flex="1" primary="true"
  2136.                        class="sortDirectionIndicator" persist="width hidden sortActive sortDirection"
  2137.                        sort="rdf:http://home.netscape.com/NC-rdf#Name"
  2138.                        sortActive="true" sortDirection="none"/>
  2139.         </treecols>
  2140.       </tree>
  2141.     </xbl:content>
  2142.     <implementation>
  2143.       <field name="clickCount">1</field>
  2144.     </implementation>
  2145.   </binding>
  2146. </bindings>
  2147. <!--
  2148.  
  2149. API:
  2150.   - root ref
  2151.   - datasources
  2152.   - columns
  2153.   - click count
  2154.   - sort
  2155.   - selection
  2156.   - default action oncommand
  2157.   - context menu
  2158.   - insert, remove bookmarks
  2159.   - 
  2160.  
  2161. //-->
  2162. <!-- 
  2163.   ILE: 
  2164.     - impl ILE on tree.xml
  2165. //-->
  2166. <!--
  2167.   Command handling:
  2168.     - impl |nsIController| 
  2169.     - actual controller implemented by client which intercepts commands and provides
  2170.       special handling for things like open in new browser etc. 
  2171.     - client can alternatively use the built in controller for default functionality.
  2172.     - decisions:
  2173.         Implementation pattern:
  2174.         client: 
  2175.         (A) bookmarksUIElement.prototype.doCommand = function () { .. }
  2176.           OR
  2177.         (B) var ctrlr = { doCommand: function () { bookmarksUIElement.doCommand(); }
  2178.           Tend to prefer (B) as it supports delegation
  2179. //-->
  2180.