home *** CD-ROM | disk | FTP | other *** search
/ Freelog 100 / FreelogNo100-NovembreDecembre2010.iso / Multimedia / Songbird / Songbird_1.8.0-1800_windows-i686-msvc8.exe / components / sbAddToPlaylist.jsm < prev    next >
Text File  |  2010-08-30  |  23KB  |  750 lines

  1. /*
  2. //
  3. // BEGIN SONGBIRD GPL
  4. //
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. //
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. //
  13. // Software distributed under the License is distributed
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
  15. // express or implied. See the GPL for the specific language
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc.,
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. //
  23. // END SONGBIRD GPL
  24. //
  25.  */
  26.  
  27. Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
  28. Components.utils.import("resource://app/jsmodules/DropHelper.jsm");
  29. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  30.  
  31. const Ci = Components.interfaces;
  32. const Cc = Components.classes;
  33. const Cr = Components.results;
  34.  
  35. const ADDTOPLAYLIST_MENU_TYPE      = "submenu";
  36. const ADDTOPLAYLIST_MENU_ID        = "library_cmd_addtoplaylist";
  37. const ADDTOPLAYLIST_MENU_NAME      = "&command.addtoplaylist";
  38. const ADDTOPLAYLIST_MENU_TOOLTIP   = "&command.tooltip.addtoplaylist";
  39. const ADDTOPLAYLIST_MENU_KEY       = "&command.shortcut.key.addtoplaylist";
  40. const ADDTOPLAYLIST_MENU_KEYCODE   = "&command.shortcut.keycode.addtoplaylist";
  41. const ADDTOPLAYLIST_MENU_MODIFIERS = "&command.shortcut.modifiers.addtoplaylist";
  42. const DOWNLOADTOPLAYLIST_MENU_NAME      = "&command.downloadtoplaylist";
  43. const DOWNLOADTOPLAYLIST_MENU_TOOLTIP   = "&command.tooltip.downloadtoplaylist";
  44. const DOWNLOADTOPLAYLIST_MENU_KEY       = "&command.shortcut.key.downloadtoplaylist";
  45. const DOWNLOADTOPLAYLIST_MENU_KEYCODE   = "&command.shortcut.keycode.downloadtoplaylist";
  46. const DOWNLOADTOPLAYLIST_MENU_MODIFIERS = "&command.shortcut.modifiers.downloadtoplaylist";
  47.  
  48.  
  49. const ADDTOPLAYLIST_COMMAND_ID = "library_cmd_addtoplaylist:";
  50. const ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID = "library_cmd_addtoplaylist_createnew";
  51.  
  52. EXPORTED_SYMBOLS = [ "addToPlaylistHelper",
  53.                      "SBPlaylistCommand_AddToPlaylist",
  54.                      "SBPlaylistCommand_DownloadToPlaylist" ];
  55.  
  56. /**
  57.  * \brief Creates a new unwrapper helper object which ensures
  58.  *        downloadStatusTarget is always set when adding items
  59.  *        to another library or playlist on a device.
  60.  * \param aSelection An sbIMediaListViewSelection.selectedMediaItems enumerator
  61.  * \return A new unwrapper helper object.
  62.  */
  63. function createUnwrapper(aSelection) {
  64.   var unwrapper = Cc["@songbirdnest.com/Songbird/Library/EnumeratorWrapper;1"]
  65.                     .createInstance(Ci.sbIMediaListEnumeratorWrapper);
  66.   unwrapper.initialize(aSelection);
  67.  
  68.   return unwrapper;
  69. }
  70.  
  71. // ----------------------------------------------------------------------------
  72. // The "Add to playlist" dynamic command object
  73. // ----------------------------------------------------------------------------
  74. var SBPlaylistCommand_AddToPlaylist =
  75. {
  76.   m_Context: {
  77.     m_Playlist: null,
  78.     m_Window: null
  79.   },
  80.  
  81.   m_addToPlaylist: null,
  82.  
  83.   m_root_commands :
  84.   {
  85.     m_Types: new Array
  86.     (
  87.       ADDTOPLAYLIST_MENU_TYPE
  88.     ),
  89.  
  90.     m_Ids: new Array
  91.     (
  92.       ADDTOPLAYLIST_MENU_ID
  93.     ),
  94.  
  95.     m_Names: new Array
  96.     (
  97.       ADDTOPLAYLIST_MENU_NAME
  98.     ),
  99.  
  100.     m_Tooltips: new Array
  101.     (
  102.       ADDTOPLAYLIST_MENU_TOOLTIP
  103.     ),
  104.  
  105.     m_Keys: new Array
  106.     (
  107.       ADDTOPLAYLIST_MENU_KEY
  108.     ),
  109.  
  110.     m_Keycodes: new Array
  111.     (
  112.       ADDTOPLAYLIST_MENU_KEYCODE
  113.     ),
  114.  
  115.     m_Enableds: new Array
  116.     (
  117.       true
  118.     ),
  119.  
  120.     m_Modifiers: new Array
  121.     (
  122.       ADDTOPLAYLIST_MENU_MODIFIERS
  123.     ),
  124.  
  125.     m_PlaylistCommands: new Array
  126.     (
  127.       null
  128.     )
  129.   },
  130.  
  131.   _getMenu: function(aSubMenu)
  132.   {
  133.     var cmds;
  134.  
  135.     cmds = this.m_addToPlaylist.handleGetMenu(aSubMenu);
  136.     if (cmds) return cmds;
  137.  
  138.     switch (aSubMenu) {
  139.       default:
  140.         cmds = this.m_root_commands;
  141.         break;
  142.     }
  143.     return cmds;
  144.   },
  145.  
  146.   getVisible: function( aHost )
  147.   {
  148.     return true;
  149.   },
  150.  
  151.   getNumCommands: function( aSubMenu, aHost )
  152.   {
  153.     var cmds = this._getMenu(aSubMenu);
  154.     return cmds.m_Ids.length;
  155.   },
  156.  
  157.   getCommandId: function( aSubMenu, aIndex, aHost )
  158.   {
  159.     var cmds = this._getMenu(aSubMenu);
  160.     if ( aIndex >= cmds.m_Ids.length ) return "";
  161.     return cmds.m_Ids[ aIndex ];
  162.   },
  163.  
  164.   getCommandType: function( aSubMenu, aIndex, aHost )
  165.   {
  166.     var cmds = this._getMenu(aSubMenu);
  167.     if ( aIndex >= cmds.m_Ids.length ) return "";
  168.     return cmds.m_Types[ aIndex ];
  169.   },
  170.  
  171.   getCommandText: function( aSubMenu, aIndex, aHost )
  172.   {
  173.     var cmds = this._getMenu(aSubMenu);
  174.     if ( aIndex >= cmds.m_Names.length ) return "";
  175.     return cmds.m_Names[ aIndex ];
  176.   },
  177.  
  178.   getCommandFlex: function( aSubMenu, aIndex, aHost )
  179.   {
  180.     var cmds = this._getMenu(aSubMenu);
  181.     if ( cmds.m_Types[ aIndex ] == "separator" ) return 1;
  182.     return 0;
  183.   },
  184.  
  185.   getCommandToolTipText: function( aSubMenu, aIndex, aHost )
  186.   {
  187.     var cmds = this._getMenu(aSubMenu);
  188.     if ( aIndex >= cmds.m_Tooltips.length ) return "";
  189.     return cmds.m_Tooltips[ aIndex ];
  190.   },
  191.  
  192.   getCommandValue: function( aSubMenu, aIndex, aHost )
  193.   {
  194.   },
  195.  
  196.   instantiateCustomCommand: function( aDocument, aId, aHost )
  197.   {
  198.     return null;
  199.   },
  200.  
  201.   refreshCustomCommand: function( aElement, aId, aHost )
  202.   {
  203.   },
  204.  
  205.   getCommandVisible: function( aSubMenu, aIndex, aHost )
  206.   {
  207.     return true;
  208.   },
  209.  
  210.   getCommandFlag: function( aSubmenu, aIndex, aHost )
  211.   {
  212.     return false;
  213.   },
  214.  
  215.   getCommandChoiceItem: function( aChoiceMenu, aHost )
  216.   {
  217.     return "";
  218.   },
  219.  
  220.   getCommandEnabled: function( aSubMenu, aIndex, aHost )
  221.   {
  222.     if (this.m_Context.m_Playlist.mediaListView.selection.count == 0)
  223.       return false;
  224.     var cmds = this._getMenu(aSubMenu);
  225.     return (aIndex in cmds.m_Enableds) && cmds.m_Enableds[ aIndex ];
  226.   },
  227.  
  228.   getCommandShortcutModifiers: function ( aSubMenu, aIndex, aHost )
  229.   {
  230.     var cmds = this._getMenu(aSubMenu);
  231.     if ( aIndex >= cmds.m_Modifiers.length ) return "";
  232.     return cmds.m_Modifiers[ aIndex ];
  233.   },
  234.  
  235.   getCommandShortcutKey: function ( aSubMenu, aIndex, aHost )
  236.   {
  237.     var cmds = this._getMenu(aSubMenu);
  238.     if ( aIndex >= cmds.m_Keys.length ) return "";
  239.     return cmds.m_Keys[ aIndex ];
  240.   },
  241.  
  242.   getCommandShortcutKeycode: function ( aSubMenu, aIndex, aHost )
  243.   {
  244.     var cmds = this._getMenu(aSubMenu);
  245.     if ( aIndex >= cmds.m_Keycodes.length ) return "";
  246.     return cmds.m_Keycodes[ aIndex ];
  247.   },
  248.  
  249.   getCommandShortcutLocal: function ( aSubMenu, aIndex, aHost )
  250.   {
  251.     return true;
  252.   },
  253.  
  254.   getCommandSubObject: function ( aSubMenu, aIndex, aHost )
  255.   {
  256.     var cmds = this._getMenu(aSubMenu);
  257.     if ( aIndex >= cmds.m_PlaylistCommands.length ) return null;
  258.     return cmds.m_PlaylistCommands[ aIndex ];
  259.   },
  260.  
  261.   onCommand: function( aSubMenu, aIndex, aHost, id, value )
  262.   {
  263.     if ( id )
  264.     {
  265.       // ADDTOPLAYLIST
  266.       if (this.m_addToPlaylist.handleCommand(id)) return;
  267.  
  268.       // ...
  269.     }
  270.   },
  271.  
  272.   // The object registered with the sbIPlaylistCommandsManager interface acts
  273.   // as a template for instances bound to specific playlist elements
  274.  
  275.   dupObject: function (obj) {
  276.     var r = {};
  277.     for ( var i in obj )
  278.     {
  279.       r[ i ] = obj[ i ];
  280.     }
  281.     return r;
  282.   },
  283.  
  284.   duplicate: function()
  285.   {
  286.     var obj = this.dupObject(this);
  287.     obj.m_Context = this.dupObject(this.m_Context);
  288.     return obj;
  289.   },
  290.  
  291.   initCommands: function(aHost) {
  292.     this.m_addToPlaylist = new addToPlaylistHelper();
  293.     this.m_addToPlaylist.init(this);
  294.   },
  295.  
  296.   shutdownCommands: function() {
  297.     if (!this.m_addToPlaylist) {
  298.       dump("this.m_addToPlaylist is null in SBPlaylistCommand_AddToPlaylist ?!!\n");
  299.       return;
  300.     }
  301.     this.m_addToPlaylist.shutdown();
  302.     this.m_addToPlaylist = null;
  303.     this.m_Context = null;
  304.   },
  305.  
  306.   setContext: function( context )
  307.   {
  308.     var playlist = context.playlist;
  309.     var window = context.window;
  310.  
  311.     // Ah.  Sometimes, things are being secure.
  312.  
  313.     if ( playlist && playlist.wrappedJSObject )
  314.       playlist = playlist.wrappedJSObject;
  315.  
  316.     if ( window && window.wrappedJSObject )
  317.       window = window.wrappedJSObject;
  318.  
  319.     this.m_Context.m_Playlist = playlist;
  320.     this.m_Context.m_Window = window;
  321.   },
  322.  
  323.   QueryInterface : function(aIID)
  324.   {
  325.     if (!aIID.equals(Components.interfaces.sbIPlaylistCommands) &&
  326.         !aIID.equals(Components.interfaces.nsISupportsWeakReference) &&
  327.         !aIID.equals(Components.interfaces.nsISupports))
  328.     {
  329.       throw Components.results.NS_ERROR_NO_INTERFACE;
  330.     }
  331.  
  332.     return this;
  333.   }
  334. }; // SBPlaylistCommand_AddToPlaylist declaration
  335.  
  336. // Same object, different display text.
  337. var SBPlaylistCommand_DownloadToPlaylist = SBPlaylistCommand_AddToPlaylist.duplicate();
  338. SBPlaylistCommand_DownloadToPlaylist.m_root_commands = {
  339.   m_Types: new Array
  340.   (
  341.     ADDTOPLAYLIST_MENU_TYPE
  342.   ),
  343.  
  344.   m_Ids: new Array
  345.   (
  346.     ADDTOPLAYLIST_MENU_ID
  347.   ),
  348.  
  349.   m_Names: new Array
  350.   (
  351.     DOWNLOADTOPLAYLIST_MENU_NAME
  352.   ),
  353.  
  354.   m_Tooltips: new Array
  355.   (
  356.     DOWNLOADTOPLAYLIST_MENU_TOOLTIP
  357.   ),
  358.  
  359.   m_Keys: new Array
  360.   (
  361.     DOWNLOADTOPLAYLIST_MENU_KEY
  362.   ),
  363.  
  364.   m_Keycodes: new Array
  365.   (
  366.     DOWNLOADTOPLAYLIST_MENU_KEYCODE
  367.   ),
  368.  
  369.   m_Enableds: new Array
  370.   (
  371.     true
  372.   ),
  373.  
  374.   m_Modifiers: new Array
  375.   (
  376.     DOWNLOADTOPLAYLIST_MENU_MODIFIERS
  377.   ),
  378.  
  379.   m_PlaylistCommands: new Array
  380.   (
  381.     null
  382.   )
  383. };
  384.  
  385.  
  386. function addToPlaylistHelper() {
  387. }
  388.  
  389. addToPlaylistHelper.prototype.constructor = addToPlaylistHelper;
  390.  
  391. addToPlaylistHelper.prototype = {
  392.   m_listofplaylists: null,
  393.   m_commands: null,
  394.   m_reglist: null,
  395.   m_libraryManager: null,
  396.  
  397.   LOG: function(str) {
  398.     var consoleService = Components.classes['@mozilla.org/consoleservice;1']
  399.                             .getService(Components.interfaces.nsIConsoleService);
  400.     consoleService.logStringMessage(str);
  401.   },
  402.   init: function(aCommands) {
  403.     this.m_libraryManager = Components.classes["@songbirdnest.com/Songbird/library/Manager;1"]
  404.                             .getService(Components.interfaces.sbILibraryManager);
  405.     this.m_libraryManager.addListener(this);
  406.     this.m_commands = aCommands;
  407.     this.makeListOfPlaylists();
  408.   },
  409.  
  410.   shutdown: function() {
  411.     this.m_libraryManager.removeListener(this);
  412.     this.removeLibraryListeners();
  413.     this.m_libraryManager = null;
  414.   },
  415.  
  416.   removeLibraryListeners: function() {
  417.     if (this.m_reglist) {
  418.       for (var i in this.m_reglist) {
  419.         this.m_reglist[i].removeListener(this);
  420.       }
  421.     }
  422.     this.m_reglist = new Array();
  423.   },
  424.  
  425.   makeListOfPlaylists: function( ) {
  426.     // remove previous listeners
  427.     this.removeLibraryListeners();
  428.  
  429.     // todo: make this smarter :(
  430.     var typearray = new Array('simple');
  431.  
  432.     this.m_listofplaylists = {};
  433.     this.m_listofplaylists.m_Types = new Array();
  434.     this.m_listofplaylists.m_Ids = new Array();
  435.     this.m_listofplaylists.m_Names = new Array();
  436.     this.m_listofplaylists.m_Tooltips = new Array();
  437.     this.m_listofplaylists.m_Enableds = new Array();
  438.     this.m_listofplaylists.m_Modifiers = new Array();
  439.     this.m_listofplaylists.m_Keys = new Array();
  440.     this.m_listofplaylists.m_Keycodes = new Array();
  441.     this.m_listofplaylists.m_PlaylistCommands = new Array();
  442.  
  443.     var libs = this.m_libraryManager.getLibraries();
  444.     while (libs.hasMoreElements()) {
  445.       var library = libs.getNext().QueryInterface(
  446.         Components.interfaces.sbILibrary);
  447.       library.addListener(this, false);
  448.       this.m_reglist.push(library);
  449.       this.makePlaylistsForLibrary(library, typearray);
  450.     }
  451.  
  452.     if (this.m_listofplaylists.m_Types.length == 0) {
  453.       this.m_listofplaylists.m_Types.push("action");
  454.       this.m_listofplaylists.m_Ids.push("noplaylist");
  455.       this.m_listofplaylists.m_Names.push("&command.addtoplaylist.noexistingplaylist");
  456.       this.m_listofplaylists.m_Tooltips.push("&command.tooltip.addtoplaylist.noexistingplaylist");
  457.       this.m_listofplaylists.m_Enableds.push(false);
  458.       this.m_listofplaylists.m_Modifiers.push("");
  459.       this.m_listofplaylists.m_Keys.push("");
  460.       this.m_listofplaylists.m_Keycodes.push("");
  461.       this.m_listofplaylists.m_PlaylistCommands.push(null);
  462.     }
  463.  
  464.     this.m_listofplaylists.m_Types.push("separator");
  465.     this.m_listofplaylists.m_Ids.push("separator");
  466.     this.m_listofplaylists.m_Names.push("separator");
  467.     this.m_listofplaylists.m_Tooltips.push("separator");
  468.     this.m_listofplaylists.m_Enableds.push(true);
  469.     this.m_listofplaylists.m_Modifiers.push("");
  470.     this.m_listofplaylists.m_Keys.push("");
  471.     this.m_listofplaylists.m_Keycodes.push("");
  472.     this.m_listofplaylists.m_PlaylistCommands.push(null);
  473.  
  474.     this.m_listofplaylists.m_Types.push("action");
  475.     this.m_listofplaylists.m_Ids.push(ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID);
  476.     this.m_listofplaylists.m_Names.push("&command.addtoplaylist.createnew");
  477.     this.m_listofplaylists.m_Tooltips.push("&command.addtoplaylist.createnew");
  478.     this.m_listofplaylists.m_Enableds.push(true);
  479.     this.m_listofplaylists.m_Modifiers.push("");
  480.     this.m_listofplaylists.m_Keys.push("");
  481.     this.m_listofplaylists.m_Keycodes.push("");
  482.     this.m_listofplaylists.m_PlaylistCommands.push(null);
  483.   },
  484.  
  485.   makePlaylistsForLibrary: function(aLibrary, typearray) {
  486.     this._makingList = true;
  487.     var listener = {
  488.       obj: this,
  489.       items: [],
  490.       _downloadListGUID: null,
  491.       onEnumerationBegin: function() {
  492.         var ddh =
  493.           Components.classes["@songbirdnest.com/Songbird/DownloadDeviceHelper;1"]
  494.                     .getService(Components.interfaces.sbIDownloadDeviceHelper);
  495.         var downloadMediaList = ddh.getDownloadMediaList();
  496.         if (downloadMediaList)
  497.           this._downloadListGUID = downloadMediaList.guid;
  498.  
  499.         this._libraryServicePane = 
  500.           Components.classes['@songbirdnest.com/servicepane/library;1']
  501.           .getService(Components.interfaces.sbILibraryServicePaneService);
  502.       },
  503.       onEnumerationEnd: function() { },
  504.       onEnumeratedItem: function(list, item) {
  505.         var hidden = item.getProperty("http://songbirdnest.com/data/1.0#hidden");
  506.         if (hidden == "1") {
  507.           return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
  508.         }
  509.         var goodtype = false;
  510.         for (var i in typearray) {
  511.           if (typearray[i] == item.type) {
  512.             goodtype = true;
  513.             break;
  514.           }
  515.         }
  516.         if (!goodtype) {
  517.           return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
  518.         }
  519.  
  520.         // XXXsteve Prevent the download playlist from appearing in the list.
  521.         // this should be fixable once we close bug 4017 and have a way to
  522.         // interrogate the policy on each playlist to see if it should be
  523.         // put in this menu
  524.         if (item.guid == this._downloadListGUID) {
  525.           return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
  526.         }
  527.         
  528.         // XXXlone also prevent playlists that do not have a corresponding node in
  529.         // the service pane from appearing (or those whose node, or parent nodes are
  530.         // hidden). This filters out remote playlists, as well as 'utility' extension 
  531.         // playlists. This should be a fairly good test for discriminating which 
  532.         // playlists are useful as sento targets, since it mirror the user's ability 
  533.         // to drag and drop to them. Eventually this should be fixed by testing the 
  534.         // policy on the playlist once we close bug 4017.
  535.         function isHidden(node) {
  536.           while (node) {
  537.             if (node.hidden) return true;
  538.             node = node.parentNode;
  539.           }
  540.           return false;
  541.         }
  542.         var node = this._libraryServicePane.getNodeForLibraryResource(item);
  543.         if (!node || isHidden(node)) {
  544.           return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
  545.         }
  546.  
  547.         this.obj.m_listofplaylists.m_Types.push("action");
  548.         this.obj.m_listofplaylists.m_Ids.push(ADDTOPLAYLIST_COMMAND_ID + item.library.guid + ";" + item.guid);
  549.         this.obj.m_listofplaylists.m_Names.push(item.name ? item.name : "Unnamed Playlist");
  550.         this.obj.m_listofplaylists.m_Tooltips.push(item.name ? item.name : "Unnamed Playlist");
  551.         this.obj.m_listofplaylists.m_Enableds.push(item.userEditable);
  552.         this.obj.m_listofplaylists.m_Modifiers.push("");
  553.         this.obj.m_listofplaylists.m_Keys.push("");
  554.         this.obj.m_listofplaylists.m_Keycodes.push("");
  555.         this.obj.m_listofplaylists.m_PlaylistCommands.push(null);
  556.  
  557.         return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
  558.       }
  559.     };
  560.  
  561.     try {
  562.  
  563.       // Enumerate all lists in this library
  564.       aLibrary.enumerateItemsByProperty("http://songbirdnest.com/data/1.0#isList", "1",
  565.                                         listener );
  566.     } catch (e) {
  567.       // this may happen if a playlist was leaked, and is still there
  568.       // in the aether refreshing its commands, ignore failure, this list
  569.       // will never show up anymore anyway
  570.     }
  571.     this._makingList = false;
  572.   },
  573.  
  574.   handleGetMenu: function(aSubMenu) {
  575.     if (this.m_listofplaylists == null) {
  576.       // handleGetMenu called before makeListOfPlaylists, this would cause infinite recursion :
  577.       // the command object would not find the menu either, would return null to getMenu which
  578.       // corresponds to the root menu, and it'd recurse infinitly.
  579.       throw Components.results.NS_ERROR_FAILURE;
  580.     }
  581.     if (aSubMenu == ADDTOPLAYLIST_MENU_ID) return this.m_listofplaylists;
  582.     return null;
  583.   },
  584.  
  585.   handleCommand: function(id) {
  586.     try {
  587.       var context = this.m_commands.m_Context;
  588.       if (id == ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID) {
  589.         var newMediaList = context.m_Window.makeNewPlaylist("simple");
  590.         this.addToPlaylist(newMediaList.library.guid, newMediaList.guid, context.m_Playlist);
  591.         return true;
  592.       }
  593.       var addtoplstr = ADDTOPLAYLIST_COMMAND_ID;
  594.       if ( id.slice(0, addtoplstr.length) == addtoplstr) {
  595.         var r = id.slice(addtoplstr.length);
  596.         var guids = r.split(';');
  597.         if (guids.length >= 2) {
  598.           var libraryguid = guids[0];
  599.           var playlistguid = guids[1];
  600.  
  601.           this.addToPlaylist(libraryguid, playlistguid, context.m_Playlist);
  602.  
  603.           return true;
  604.         }
  605.       }
  606.     } catch (e) {
  607.       Components.utils.reportError(e);
  608.     }
  609.     return false;
  610.   },
  611.  
  612.   addToPlaylist: function(libraryguid, playlistguid, sourceplaylist) {
  613.     var library = this.m_libraryManager.getLibrary(libraryguid);
  614.     var medialist;
  615.     if (libraryguid == playlistguid)
  616.       medialist = library;
  617.     else
  618.       medialist = library.getMediaItem(playlistguid);
  619.  
  620.     if (medialist) {
  621.  
  622.       var oldLength = medialist.length;
  623.       var selection = sourceplaylist.mediaListView.selection.selectedIndexedMediaItems;
  624.  
  625.       // Create an enumerator that wraps the enumerator we were handed since
  626.       // the enumerator we get hands back sbIIndexedMediaItem, not just plain
  627.       // 'ol sbIMediaItems
  628.       var unwrapper = createUnwrapper(selection);
  629.             
  630.       var asyncListener = {
  631.         onProgress: function(aItemsProcessed, aComplete) {
  632.           DNDUtils.reportAddedTracks(aItemsProcessed, 
  633.                                      0, /* no duplicate reporting */
  634.                                      0, /* no unsupported reporting */
  635.                                      medialist.name);
  636.         },
  637.         QueryInterface: XPCOMUtils.generateQI([Ci.sbIMediaListAsyncListener])
  638.       }
  639.       
  640.       medialist.addSomeAsync(unwrapper, asyncListener);
  641.     }
  642.   },
  643.  
  644.   //-----------------------------------------------------------------------------
  645.   _inbatch       : false,
  646.   _deferredevent : false,
  647.   _makingList    : false,
  648.  
  649.   refreshCommands: function() {
  650.     if (this.m_commands) {
  651.       if (this.m_commands.m_Context && this.m_commands.m_Context.m_Playlist) {
  652.         this.makeListOfPlaylists();
  653.         this.m_commands.m_Context.m_Playlist.refreshCommands();
  654.       }
  655.     }
  656.   },
  657.  
  658.   onUpdateEvent: function(item) {
  659.     if (this._makingList) return;
  660.     if (item instanceof Components.interfaces.sbIMediaList) {
  661.       if (this._inbatch) {
  662.         // if we are in a batch, remember that we saw a playlist event in it
  663.         this._deferredevent = true;
  664.       } else {
  665.         // if we're not in a batch, proceed with refreshing the commands
  666.         this.refreshCommands();
  667.       }
  668.     }
  669.   },
  670.  
  671.   QueryInterface: function QueryInterface(iid) {
  672.     if (!iid.equals(Components.interfaces.sbIMediaListListener) &&
  673.         !iid.equals(Components.interfaces.sbILibraryManagerListener) &&
  674.         !iid.equals(Components.interfaces.nsISupports))
  675.       throw Components.results.NS_ERROR_NO_INTERFACE;
  676.     return this;
  677.   },
  678.  
  679.   onItemAdded: function onItemAdded(list, item, index) {
  680.     // If we are in a batch, ignore future notifications
  681.     if (!this._inbatch) {
  682.       this.onUpdateEvent(item);
  683.     }
  684.     return true;
  685.   },
  686.  
  687.   onBeforeItemRemoved: function onBeforeItemRemoved(list, item, index) {
  688.     return true;
  689.   },
  690.  
  691.   onAfterItemRemoved: function onAfterItemRemoved(list, item, index) {
  692.     // If we are in a batch, ignore future notifications
  693.     if (!this._inbatch) {
  694.       this.onUpdateEvent(item);
  695.     }
  696.     return true;
  697.   },
  698.  
  699.   onItemUpdated: function onItemUpdated(list, item, properties) {
  700.     // If we are in a batch, ignore future notifications
  701.     if (!this._inbatch) {
  702.       this.onUpdateEvent(item);
  703.     }
  704.     return true;
  705.   },
  706.  
  707.   onItemMoved: function onItemMoved(list, fromIndex, toIndex) {
  708.     // XXXsteve Do we need to do anything here?
  709.     return true;
  710.   },
  711.  
  712.   onBeforeListCleared: function onBeforeListCleared(list, excludeLists) {
  713.     return true;
  714.   },
  715.  
  716.   onListCleared: function onListCleared(list, excludeLists) {
  717.     // If we are in a batch, ignore future notifications
  718.     if (!this._inbatch) {
  719.       this.onUpdateEvent(list);
  720.     }
  721.     return true;
  722.   },
  723.  
  724.   onBatchBegin: function onBatchBegin(list) {
  725.     // start deferring the events
  726.     this._inbatch = true;
  727.     this._deferredevent = false;
  728.   },
  729.  
  730.   onBatchEnd: function onBatchEnd(list) {
  731.     // stop deferring the events
  732.     this._inbatch = false;
  733.     // if an event was deferred, handle it
  734.     if (this._deferredevent) {
  735.       // since we're no longer in a batch, this does call refreshCommands
  736.       this.onUpdateEvent(list);
  737.     }
  738.     this._deferredevent = false;
  739.   },
  740.  
  741.   onLibraryRegistered: function onLibraryRegistered(library) {
  742.     this.onUpdateEvent(library);
  743.   },
  744.  
  745.   onLibraryUnregistered: function onLibraryUnregistered(library) {
  746.     this.onUpdateEvent(library);
  747.   }
  748.  
  749. };
  750.