User Tools

Site Tools


developer_center:creating_media_views

Creating Media Views

Introduction

As of this writing, Nightingale ships with two default views: a simple traditional playlist view, and a second playlist view incorporating three filter panes across the top for choosing a subset of your music. Views can be extended via extensions that implement Media Pages. Developers can easily create new views as HTML pages or XUL pages which provide custom visual interfaces to Nightingale libraries, and playlists.

How It Works

When a media page is loaded, it is passed the current MediaListView. This object is controlled by the page. Media pages are registered in an install.rdf file to work with media lists that have certain properties. For example, a media page can be set to support all media lists (the default), to target only media lists with a certain customtype (for example, “download”), or even to target a media list with a given GUID.

Certain other operations will call methods on the sbIMediaPage interface, such as dropping a selection onto a media page, attempting to highlight the current track, or initiating playback. Most media pages will include a <sb-playlist/> object which should be passed any calls you do not handle specially. The default media page created by the extension wizard will do this by default.

Creating Media Pages

As usual, the Nightingale Developer Tools Add-on greatly simplifies the process for developers. Using the Extension Wizard (accessed via the Tools menu under Create Extension…), you can create the skeleton/framework for your media page quickly and easily. The Extension Wizard takes care of setting up your install.rdf media page registration, and creating the basic “Hello World” media page.

Registering A Media Page

When creating a new media page, you must add a <mediaPage> block to the install.rdf for that extension. It must include a <contentTitle/> and a <contentUrl/> which define the title shown in the menu and the document to load. It may optionally also include any number of <match/> elements.

Rules for Matching

Because some media pages may be specific to certain kinds of playlists, you may provide any number of <match/> elements to target particular kinds of lists. You can target “type”, “customtype” or any SBProperty of the MediaList within the view.

Examples

Match any “simple” media list:

<match>type:simple</match>  

Match only the downloads list:

<match>customtype:downloads</match>  

Match simple media lists which have a custom property added by your extension:

<match>type:simple myExtensionProperty:someValue

Opting Out

Because media pages can come from many sources, we have provided a mechanism to allow extension developers to specify that a particular MediaList would like to “opt out” of the generic media lists and only support custom media lists specially targetting them.

We have specified that any <mediaPage> rule which has no match rules, or any match rule which only targets the “type” property of a media list can be excluded by setting the “onlyCustomMediaPages” property of the list to true.

Examples

The example uses a mediaList with these properties: mediaList.type == “simple” && mediaList.customtype == “downloads”

<match/> rule SBProperties.onlyCustomMediaPages result reason
(none provided) false matches matches all
(none provided) true does not match (opt-outable rule) opted out
type:simple false matches same type
type:simple true does not match (opt-outable rule) opted out
customtype:download false matches same customtype
customtype:download true matches (not opt-outable) same customtype
type:simple ; customtype:other true/false no match does not match customtype

Multiple Match Rules

If there are multiple <match/> rules, any rule which completely matches the list will cause the page to appear in the menu for that item.

Implementing Media Pages

Media pages can be implemented in either XUL or HTML. The easier method, and the one covered here, is implementing them via XUL. At its core, a media page is as simple as the following (as excerpted from the standard media-page.xul generated by the Extension Wizard)

    <page  
      id="mymediapage-media-page"  
      title="My Media Page"  
      xmlns:html="http://www.w3.org/1999/xhtml"  
      xmlns:rdf="http://www.w3.org/TR/WD-rdf-syntax#"  
      xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"  
      onload="window.mediaPage.onLoad();"  
      onunload="window.mediaPage.onUnload();"  
      windowtype="Songbird:MediaPage"  
    >  
      <sb-playlist id="playlist"   
          flex="1"  
          enableColumnDrag="true"  
          persist="column-list column-widths"  
          editable="true"  
          hidefilters="true"  
      />  
 
 
      <!-- Page Controller.  
           Registers a window.mediaPage object implementing the   
           sbIMediaPage interface.  
        -->  
      <script type="application/x-javascript" src="media-page.js" />  
 
    </page>  

The above media page is a simple recreation of the standard playlist view. You can see it defines a <page/> element of windowtype Songbird:MediaPage, and adds onload & onunload handlers to call the media page registration.

The media page should also define a window.mediaPage object which implements the sbIMediaPage interface. The above XUL implements this in the media-page.js file. The three methods the media page should implement are highlightItem(), canDrop(), and onDrop() which implement the behaviour the media page should take when items are highlighted or dropped.

highlightItem() is called when the item should be highlighted, e.g. when the user clicks on the playing track in the faceplate to jump the playlist to the currently playing track.

canDrop() is called when something is dragged on top of the tabbrowser tab (note this is the tab selector underneath the web navigation bar)

onDrop() is called when something is actually dropped onto the tabbrowser tab.

The following code is the media-page.js file referenced above:

    // Shorthand  
    if (typeof(Cc) == "undefined")  
      var Cc = Components.classes;  
    if (typeof(Ci) == "undefined")  
      var Ci = Components.interfaces;  
    if (typeof(Cu) == "undefined")  
      var Cu = Components.utils;  
    if (typeof(Cr) == "undefined")  
      var Cr = Components.results;  
 
 
    /** 
     * Media Page Controller 
     * 
     * In order to display the contents of a library or list, pages 
     * must provide a "window.mediaPage" object implementing 
     * the Nightingale sbIMediaPage interface. This interface allows 
     * the rest of Nightingale to talk to the page without knowledge  
     * of what the page looks like. 
     * 
     * In this particular page most functionality is simply  
     * delegated to the sb-playlist widget. 
     */  
    window.mediaPage = {  
 
        // The sbIMediaListView that this page is to display  
      _mediaListView: null,  
 
        // The sb-playlist XBL binding  
      _playlist: null,   
 
 
      /**  
       * Gets the sbIMediaListView that this page is displaying 
       */  
      get mediaListView()  {  
        return this._mediaListView;  
      },  
 
 
      /**  
       * Set the sbIMediaListView that this page is to display. 
       * Called in the capturing phase of window load by the Nightingale browser. 
       * Note that to simplify page creation mediaListView may only be set once. 
       */  
      set mediaListView(value)  {  
 
        if (!this._mediaListView) {  
          this._mediaListView = value;  
        } else {  
          throw new Error("mediaListView may only be set once.  Please reload the page");  
        }  
      },  
 
 
      /**  
       * Called when the page finishes loading.   
       * By this time window.mediaPage.mediaListView should have  
       * been externally set.   
       */  
      onLoad:  function(e) {  
 
        // Make sure we have the javascript modules we&apos;re going to use  
        if (!window.SBProperties)   
          Cu.import("resource://app/jsmodules/sbProperties.jsm");  
        if (!window.LibraryUtils)   
          Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");  
        if (!window.kPlaylistCommands)   
          Cu.import("resource://app/jsmodules/kPlaylistCommands.jsm");  
 
        if (!this._mediaListView) {  
          Components.utils.reportError("Media Page did not receive  " +   
                                       "a mediaListView before the onload event!");  
          return;  
        }   
 
        this._playlist = document.getElementById("playlist");  
 
        //  
        // TODO: Do something interesting here!  
        //  
 
        // Get playlist commands (context menu, keyboard shortcuts, toolbar)  
        // Note: playlist commands currently depend on the playlist widget.  
        var mgr =  
          Components.classes["@songbirdnest.com/Songbird/PlaylistCommandsManager;1"]  
                    .createInstance(Components.interfaces.sbIPlaylistCommandsManager);  
        var cmds = mgr.request(kPlaylistCommands.MEDIAITEM_DEFAULT);  
 
        // Set up the playlist widget  
        this._playlist.bind(this._mediaListView, cmds);  
      },  
 
 
      /**  
       * Called as the window is about to unload 
       */  
      onUnload:  function(e) {  
        if (this._playlist) {  
          this._playlist.destroy();  
          this._playlist = null;  
        }  
      },  
 
 
      /**  
       * Show/highlight the MediaItem at the given MediaListView index. 
       * Called by the Find Current Track button. 
       */  
      highlightItem: function(aIndex) {  
        this._playlist.highlightItem(aIndex);  
      },  
 
 
      /**  
       * Called when something is dragged over the tabbrowser tab for this window 
       */  
      canDrop: function(aEvent, aSession) {  
        return this._playlist.canDrop(aEvent, aSession);  
      },  
 
 
      /**  
       * Called when something is dropped on the tabbrowser tab for this window 
       */  
      onDrop: function(aEvent, aSession) {  
        return this._playlist.  
            _dropOnTree(this._playlist.mediaListView.length,  
                    Ci.sbIMediaListViewTreeViewObserver.DROP_AFTER);  
      }  
 
 
    } // End window.mediaPage 
developer_center/creating_media_views.txt · Last modified: 2015/05/11 16:00 by zjays