mainPlayerWindow.js
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 
27 var PREF_PLAYER_CONTROL_LOCATION = "songbird.playercontrol.location";
28 
29 if ("undefined" == typeof(Ci)) {
30  this.Ci = Components.interfaces;
31 }
32 if ("undefined" == typeof(Cc)) {
33  this.Cc = Components.classes;
34 }
35 if ("undefined" == typeof(Cr)) {
36  this.Cr = Components.results;
37 }
38 
39 Components.utils.import("resource://app/jsmodules/sbLibraryUtils.jsm");
40 
41 // Assist with moving player controls by setting an attribute on the layout.
42 function movePlayerControls(aIsOnTop)
43 {
44  var locationVal = aIsOnTop ? "top" : "bottom";
45  var contentPlayerWrapper = document.getElementById("content_player_wrapper");
46  if (contentPlayerWrapper) {
47 
48 #ifdef METRICS_ENABLED
49  if (Application.prefs.getValue(PREF_PLAYER_CONTROL_LOCATION, "") != locationVal) {
50  // the location changed, send a metrics ping
51  // see http://bugzilla.songbirdnest.com/show_bug.cgi?id=11509
52  Components.classes["@songbirdnest.com/Songbird/Metrics;1"]
53  .createInstance(Components.interfaces.sbIMetrics)
54  .metricsInc("mainplayer.playercontrols", "location", locationVal);
55  }
56 #endif
57 
58  contentPlayerWrapper.setAttribute("playercontrols", locationVal);
59  Application.prefs.setValue(PREF_PLAYER_CONTROL_LOCATION, locationVal);
60 
61  // Invoke the broadcasters
62  var broadcasterTop = document.getElementById("playercontrols_top");
63  var broadcasterBottom = document.getElementById("playercontrols_bottom");
64  if (aIsOnTop) {
65  broadcasterTop.setAttribute("checked", "true");
66  broadcasterBottom.removeAttribute("checked");
67  }
68  else {
69  broadcasterBottom.setAttribute("checked", "true");
70  broadcasterTop.removeAttribute("checked");
71  }
72  }
73 }
74 
75 
77 {
78  doCommand: function(aCommand)
79  {
80  function dispatchEvent(aType, aCanBubble, aCanCancel, aTarget) {
81  var newEvent = document.createEvent("Events");
82  newEvent.initEvent(aType, aCanBubble, aCanCancel);
83  return (aTarget || window).dispatchEvent(newEvent);
84  }
85 
86  var mm = gMM ||
87  Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
88  .getService(Components.interfaces.sbIMediacoreManager);
89  var status = mm.status;
90  if (aCommand == "cmd_find") {
91  if (gTabBrowser.mediaTab &&
92  gTabBrowser.selectedTab == gTabBrowser.mediaTab &&
93  gTabBrowser.selectedTab.mediaPage != null) {
94  if (window && window.gSearchHandler) {
95  var searchBar = window.gSearchHandler.getSearchBar();
96  if (searchBar) {
97  searchBar.select();
98  searchBar.focus();
99  }
100  }
101  }
102  else {
103  gTabBrowser.onFindCommand();
104  }
105  } else if (aCommand == "cmd_findAgain") {
106  gTabBrowser.onFindAgainCommand();
107  } else if (aCommand == "cmd_print") {
108  PrintUtils.print(gTabBrowser.content);
109  } else if (aCommand == "cmd_metadata") {
110  SBTrackEditorOpen(); // open to the last selected tab
111  } else if (aCommand == "cmd_editmetadata") {
112  SBTrackEditorOpen("edit"); // open to the 'edit' tab
113  } else if (aCommand == "cmd_viewmetadata") {
114  SBTrackEditorOpen("summary"); // open to the 'summary' tab
115  } else if (aCommand == "cmd_getartwork") {
116  SBGetArtworkOpen(); // Show the get artwork dialog
117  } else if (aCommand == "cmd_exportmedia") {
118  var exportService = Cc["@songbirdnest.com/media-export-service;1"]
119  .getService(Ci.sbIMediaExportService);
120  exportService.exportSongbirdData();
121  } else if (aCommand == "cmd_reveal") {
122  SBRevealFile(); // reveal the selected file
123  } else if (aCommand == "cmd_find_current_track") {
124  if (!mm.sequencer.view) {
125  // nothing to see here, move along
126  return;
127  }
128 
129  // steal the focus before going to the track, because of a bug in XR that
130  // will crash the app if the tree is currently in edit mode and we try to
131  // execute the command (crash in nsFrame::HasView during repaint when the
132  // selection moves). Fixes bug 13493.
133  document.documentElement.focus();
134 
135  // Before showing the current track we trigger
136  // a custom event, so that if this object is used wihout
137  // a gBrowser object, the current window may still perform
138  // its own custom action.
139  var handled = !dispatchEvent("ShowCurrentTrack", false, true);
140  if (handled) {
141  return;
142  }
143  gTabBrowser.showIndexInView(mm.sequencer.view, mm.sequencer.viewPosition);
144  } else if (aCommand == "cmd_control_playpause") {
145  // If we are already playing something just pause/unpause playback
146  if (status.state == status.STATUS_PLAYING ||
147  status.state == status.STATUS_BUFFERING) {
148  mm.playbackControl.pause();
149  }
150  else if(status.state == status.STATUS_PAUSED) {
151  mm.playbackControl.play();
152  // Otherwise dispatch a play event. Someone should catch this
153  // and intelligently initiate playback. If not, just have
154  // the playback service play the default.
155  }
156  else {
157  var event = document.createEvent("Events");
158  event.initEvent("Play", true, true);
159  window.dispatchEvent(event);
160  }
161  } else if (aCommand == "cmd_control_next") {
162  mm.sequencer.next();
163  } else if (aCommand == "cmd_control_previous") {
164  mm.sequencer.previous();
165  } else if (aCommand == "cmd_volume_down") {
166  mm.volumeControl.volume = Math.max(0, mm.volumeControl.volume - 0.05);
167  } else if (aCommand == "cmd_volume_up") {
168  mm.volumeControl.volume = Math.min(1, mm.volumeControl.volume + 0.05);
169  } else if (aCommand == "cmd_volume_mute") {
170  mm.volumeControl.mute = !mm.volumeControl.mute;
171  } else if (aCommand == "cmd_delete") {
172  SBDeleteMediaList(this._getTargetPlaylist());
173  } else if (aCommand == "cmd_mediapage_next") {
174  gSongbirdPlayerWindow.nextMediaPage();
175  }
176  },
177 
178  supportsCommand: function(aCommand)
179  {
180  switch(aCommand) {
181  case "cmd_find":
182  case "cmd_findAgain":
183  case "cmd_print":
184  return true;
185  case "cmd_metadata":
186  case "cmd_editmetadata":
187  case "cmd_viewmetadata":
188  case "cmd_reveal":
189  case "cmd_find_current_track":
190  case "cmd_exportmedia":
191  return true;
192  case "cmd_control_playpause":
193  case "cmd_control_next":
194  case "cmd_control_previous":
195  return true;
196  case "cmd_getartwork":
197  case "cmd_delete":
198  return (this._getTargetPlaylist() != null);
199  case "cmd_volume_down":
200  case "cmd_volume_up":
201  case "cmd_volume_mute":
202  return true;
203  case "cmd_mediapage_next":
204  return true;
205  }
206  return false;
207  },
208 
209  isCommandEnabled: function(aCommand)
210  {
211  var browser = null;
212  if (typeof SBGetBrowser == 'function') {
213  browser = SBGetBrowser();
214  }
215  var view = null;
216  if (browser && browser.currentMediaPage) {
217  view = browser.currentMediaPage.mediaListView;
218  }
219 
220  var mm = gMM||
221  Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
222  .getService(Components.interfaces.sbIMediacoreManager);
223  var status = mm.status;
224 
225  var playing = ( status.state == status.STATUS_BUFFERING ||
226  status.state == status.STATUS_PLAYING ||
227  status.state == status.STATUS_PAUSED );
228  switch(aCommand) {
229  case "cmd_find":
230  return (!browser.shouldDisableFindForSelectedTab());
231  case "cmd_findAgain":
232  return (!browser.shouldDisableFindAgainForSelectedTab());
233  case "cmd_print":
234  // printing XUL is not supported, see NS_ERROR_GFX_PRINTER_NO_XUL
235  return !(browser.contentDocument instanceof XULDocument);
236  case "cmd_metadata":
237  case "cmd_editmetadata":
238  case "cmd_viewmetadata": {
239  if (view) {
240  return view.selection.count > 0;
241  }
242  return false;
243  }
244  case "cmd_getartwork":
245  return (view != null);
246  case "cmd_exportmedia":
247  var exportService = Cc["@songbirdnest.com/media-export-service;1"]
248  .getService(Ci.sbIMediaExportService);
249  return exportService.hasPendingChanges;
250  case "cmd_reveal": {
251  if (view && view.selection.count == 1) {
252  var selection = view.selection.selectedIndexedMediaItems;
253  var item = selection.getNext()
254  .QueryInterface(Ci.sbIIndexedMediaItem).mediaItem;
255  var uri = item.contentSrc;
256  return (uri && uri.scheme == "file");
257  }
258  return false;
259  }
260  case "cmd_find_current_track":
261  return mm.sequencer.view ? true : false;
262  case "cmd_control_playpause":
263  return true;
264  case "cmd_control_next":
265  case "cmd_control_previous":
266  var status = mm.status;
267  var playing = ( status.state == status.STATUS_PLAYING ||
268  status.state == status.STATUS_BUFFERING ||
269  status.state == status.STATUS_PAUSED );
270  return playing && document.commandDispatcher.focusedWindow == window;
271  case "cmd_delete": {
272  var node = gServicePane.getKeyboardFocusNode();
273  if(node && node.editable == false) {
274  return false;
275  }
276  }
277  case "cmd_volume_down":
278  return mm.volumeControl.volume > 0;
279  case "cmd_volume_up":
280  return mm.volumeControl.volume < 1;
281  case "cmd_volume_mute":
282  return true;
283  case "cmd_mediapage_next":
284  return (view != null);
285  }
286  return false;
287  },
288 
289  _getTargetPlaylist: function()
290  {
291  var list;
292  var knode = gServicePane.getKeyboardFocusNode(true);
293  if (knode) {
294  var libraryServicePane =
295  Components.classes['@songbirdnest.com/servicepane/library;1']
296  .getService(Components.interfaces.sbILibraryServicePaneService);
297  list = libraryServicePane.getLibraryResourceForNode(knode);
298 
299  } else {
300  var browser;
301  if (typeof SBGetBrowser == 'function')
302  browser = SBGetBrowser();
303  if (browser) {
304  if (browser.currentMediaPage) {
305  var view = browser.currentMediaPage.mediaListView;
306  if (view) {
307  list = view.mediaList;
308  }
309  }
310  }
311  }
312  if (list) {
313  var outerListGuid =
314  list.getProperty(SBProperties.outerGUID);
315  if (outerListGuid) {
316  return list.library.getMediaItem(outerListGuid);
317  }
318  return list;
319  }
320  return null;
321  }
322 };
323 
324 
330 
331 
333  // Window Event Handling //
335 
340  onLoad: function onLoad()
341  {
342  window.removeEventListener("load", this._onLoadCallback, false);
343  this._onLoadCallback = null;
344 
345  this._onUnloadCallback = function(e) { gSongbirdPlayerWindow.onUnload(e); };
346  window.addEventListener("unload",
347  this._onUnloadCallback, false);
348 
349  this._onPlayCallback = function(e) { gSongbirdPlayerWindow.onPlay(e); };
350  window.addEventListener("Play", this._onPlayCallback, false);
351 
352  window.addEventListener("keypress", this.onMainWindowKeyPress, false);
353 
354  window.focus();
356 
357  gTabBrowser = document.getElementById("content");
358  top.controllers.insertControllerAt(0, gSongbirdWindowController);
359 
360  // Set the player controls location
361  var playerControlsLocation =
362  Application.prefs.getValue(PREF_PLAYER_CONTROL_LOCATION, false);
363  movePlayerControls((playerControlsLocation == "top"));
364  try
365  {
366  var timingService = Cc["@songbirdnest.com/Songbird/TimingService;1"]
367  .getService(Ci.sbITimingService);
368  // NOTE: Must be in this order, CSPerfEndEULA doesn't always exist and
369  // will throw an error. CSPerfLibrary is just a timestamp for non-first
370  // runs.
371  timingService.startPerfTimer("CSPerfLibrary");
372  timingService.stopPerfTimer("CSPerfLibrary");
373  timingService.stopPerfTimer("CSPerfEndEULA");
374  }
375  catch (e)
376  {
377  // Ignore errors
378  }
379  },
380 
381 
385  onUnload: function onUnload()
386  {
387  window.removeEventListener("unload", this._onUnloadCallback, false);
388  this._onUnloadCallback = null;
389 
390  window.removeEventListener("Play", this._onPlayCallback, false);
391  this._onPlayCallback = null;
392 
393  window.removeEventListener("keypress", this.onMainWindowKeyPress, false);
394  },
395 
396 
402  onPlay: function onPlay( event )
403  {
404  try {
405  // Try to find a view from the event. If one exists that's probably
406  // what we should play from.
407  var view = this._getMediaListViewForEvent(event);
408 
409  // If no view could be found, try getting one from the current tab
410  if (!(view && view.length > 0)) {
411  view = gBrowser.currentMediaListView;
412  }
413 
414  // If the current tab has failed, try the media tab (if it exists)
415  if (!(view && view.length > 0) && gBrowser.mediaTab) {
416  view = gBrowser.mediaTab.mediaListView;
417  }
418 
419  // If we've got a view, try playing it.
420  if (view && view.length > 0) {
421  var mm =
422  Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
423  .getService(Ci.sbIMediacoreManager);
424 
425  mm.sequencer.playView(view,
426  Math.max(view.selection.currentIndex,
427  Ci.sbIMediacoreSequencer.AUTO_PICK_INDEX));
428 
429  // Since we've handled this play event, prevent any fallback action from
430  // occurring.
431  event.preventDefault();
432  }
433  } catch (e) {
434  Components.utils.reportError(e);
435  }
436  },
437 
438 
439  onMainWindowKeyPress: function onMainWindowKeyPress( event )
440  {
441  // the key press handler for pressing spacebar to play, at the top level
442  if (document.commandDispatcher.focusedWindow != window ||
443  document.commandDispatcher.focusedElement)
444  {
445  // somebody else has focus
446  return true;
447  }
448 
449  // note that we accept shift key being pressed.
450  if (event.charCode != KeyboardEvent.DOM_VK_SPACE ||
451  event.ctrlKey || event.altKey || event.metaKey || event.altGraphKey)
452  {
453  return true;
454  }
455 
456  doMenu("menuitem_control_play");
457  event.preventDefault();
458  event.stopPropagation();
459  return false;
460  },
461 
462  nextMediaPage: function gSongbirdPlayerWindow_nextMediaPage() {
463  // no need to do this if we can't get the browser
464  if (typeof SBGetBrowser != 'function')
465  return;
466 
467  var browser = SBGetBrowser();
468 
469  var mediaListView = browser.currentMediaListView;
470  if (!mediaListView) {
471  // no media list view, can't switch anything
472  return;
473  }
474 
475  var mediaPageMgr = Cc["@songbirdnest.com/Songbird/MediaPageManager;1"]
476  .getService(Ci.sbIMediaPageManager);
477  var pages = mediaPageMgr.getAvailablePages(mediaListView.mediaList);
478  var LSP = Cc["@songbirdnest.com/servicepane/library;1"]
479  .getService(Ci.sbILibraryServicePaneService);
480  var type = LSP.getNodeContentTypeFromMediaListView(mediaListView);
481  var current = mediaPageMgr.getPage(mediaListView.mediaList, null, type);
482  var page = null;
483 
484  while (pages.hasMoreElements()) {
485  page = pages.getNext().QueryInterface(Ci.sbIMediaPageInfo);
486  if (page.contentUrl == current.contentUrl) {
487  if (!pages.hasMoreElements()) {
488  // we're at the last page, restart enumeration and go to first
489  pages = mediaPageMgr.getAvailablePages(mediaListView.mediaList);
490  }
491  page = pages.getNext().QueryInterface(Ci.sbIMediaPageInfo);
492  break;
493  }
494  }
495 
496  if (!page) {
497  Components.reportError(new Components.Exception(
498  "Failed to find any pages from media list view",
499  Components.results.NS_ERROR_NOT_AVAILABLE));
500  return;
501  }
502 
503  mediaPageMgr.setPage(mediaListView.mediaList, page);
504  browser.loadMediaList(mediaListView.mediaList,
505  null,
506  null,
507  mediaListView,
508  null);
509  },
510 
511 
513  // Helper Functions //
515 
516 
521  _getMediaListViewForEvent: function _getMediaListViewForEvent( event )
522  {
523  var target = event.target;
524  if (!target) {
525  return null;
526  }
527 
528  // If the target has a media list view, use that
529  if (target.mediaListView) {
530  return target.mediaListView;
531  }
532  if (target.currentMediaListView) {
533  return target.currentMediaListView;
534  }
535 
536  // If the event came from within a binding, perhaps
537  // the view is on the inner anon element.
538  target = event.originalTarget;
539  if (target.mediaListView) {
540  return target.mediaListView;
541  }
542  if (target.currentMediaListView) {
543  return target.currentMediaListView;
544  }
545 
546  // Maybe this event is from an inner document (browser or iframe)
547  if (event.target.ownerDocument != document && window.gBrowser) {
548  target = gBrowser.getTabForDocument(event.target.ownerDocument);
549  if (target && target.mediaListView) {
550  return target.mediaListView;
551  }
552  }
553  return null;
554  },
555 
556 } // End of gSongbirdPlayerWindow
557 
558 // Set up bubbling load listener
559 gSongbirdPlayerWindow._onLoadCallback =
560  function(e) { gSongbirdPlayerWindow.onLoad(e) };
561 window.addEventListener("load", gSongbirdPlayerWindow._onLoadCallback, false);
562 
var gSongbirdPlayerWindow
const Cc
var gMM
Definition: windowUtils.js:62
tab dispatchEvent(event)
var gTabBrowser
var Application
Definition: sbAboutDRM.js:37
var libraryServicePane
var event
var gSongbirdWindowController
let window
Lastfm onLoad
Definition: mini.js:36
return null
Definition: FeedWriter.js:1143
let node
var uri
Definition: FeedWriter.js:1135
const Cr
const Ci
var gServicePane
Definition: mainWinInit.js:39
function movePlayerControls(aIsOnTop)
var PREF_PLAYER_CONTROL_LOCATION
function windowPlacementSanityChecks()
See if a window needs to be somehow "fixed" after it is opened.
Definition: windowUtils.js:457
var browser
Definition: openLocation.js:42
function onUnload()
onUnload - called when the cover preview window unloads.
Definition: coverPreview.js:36