sbPlayQueueService.cpp
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 
30 #include "sbPlayQueueService.h"
31 
32 #include <nsIAppStartupNotifier.h>
33 #include <nsICategoryManager.h>
34 #include <nsIObserverService.h>
35 #include <nsIPrefBranch.h>
36 #include <nsIPrefService.h>
37 #include <nsIStringBundle.h>
38 #include <nsIVariant.h>
39 #include <nsServiceManagerUtils.h>
40 #include <nsStringGlue.h>
41 #include <nsThreadUtils.h>
42 
43 #include <sbIDataRemote.h>
44 #include <sbILibraryManager.h>
45 #include <sbILocalDatabaseLibrary.h>
46 #include <sbIMediacoreEventTarget.h>
47 #include <sbIMediacoreSequencer.h>
48 #include <sbIMediacoreStatus.h>
49 #include <sbIMediaListView.h>
50 #include <sbIOrderableMediaList.h>
51 #include <sbLibraryManager.h>
52 #include <sbPropertiesCID.h>
53 #include <sbStandardProperties.h>
54 #include <sbStringUtils.h>
55 
56 #include <prlog.h>
57 
58 /*
59  * The name of the play queue mediaList. Use the same string that is displayed
60  * as a label at the top of our play queue displaypane UI.
61  */
62 #define SB_NAMEKEY_PLAYQUEUE_LIST \
63  "&chrome://songbird/locale/songbird.properties#playqueue.pane.title"
64 
65 /*
66  * By default, only show the track name
67  */
68 #define SB_PLAYQUEUE_DEFAULTCOLUMNSPEC \
69  NS_LL("http://songbirdnest.com/data/1.0#ordinal 30 ") \
70  NS_LL("http://songbirdnest.com/data/1.0#trackName 130")
71 
72 #define SB_DATAREMOTE_FACEPLATE_STATUS \
73  NS_LITERAL_STRING("faceplate.status.override.text")
74 
75 #define SB_BUNDLE_URL "chrome://songbird/locale/songbird.properties"
76 
77 #define SB_PLAYQUEUE_PANE_TITLE NS_LITERAL_STRING("playqueue.pane.title")
78 #define SB_LIBRARY_TRACKSADDED NS_LITERAL_STRING("library.tracksadded")
79 
84 #ifdef PR_LOGGING
85  static PRLogModuleInfo* gPlayQueueServiceLog = nsnull;
86 #define TRACE(args) PR_LOG(gPlayQueueServiceLog, PR_LOG_DEBUG, args)
87 #define LOG(args) PR_LOG(gPlayQueueServiceLog, PR_LOG_WARN, args)
88 #ifdef __GNUC__
89 #define __FUNCTION__ __PRETTY_FUNCTION__
90 #endif /* __GNUC__ */
91 #else
92 #define TRACE(args) /* nothing */
93 #define LOG(args) /* nothing */
94 #endif /* PR_LOGGING */
95 
99 
101 : mService(aService)
102 {
103 }
104 
106 {
107  Finalize();
108 }
109 
110 nsresult
112 {
113  nsresult rv;
114 
115  mDataRemote =
116  do_CreateInstance("@songbirdnest.com/Songbird/DataRemote;1", &rv);
117  NS_ENSURE_SUCCESS(rv, rv);
118 
119  rv = mDataRemote->Init(SB_DATAREMOTE_FACEPLATE_STATUS, EmptyString());
120  NS_ENSURE_SUCCESS(rv, rv);
121 
122  nsCOMPtr<nsIStringBundleService> stringBundleService =
123  do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
124  NS_ENSURE_SUCCESS(rv, rv);
125 
126  rv = stringBundleService->CreateBundle(SB_BUNDLE_URL,
127  getter_AddRefs(mBundle));
128  NS_ENSURE_SUCCESS(rv, rv);
129 
130  rv = mBundle->GetStringFromName(SB_PLAYQUEUE_PANE_TITLE.get(),
131  getter_Copies(mQueueName));
132  NS_ENSURE_SUCCESS(rv, rv);
133 
134  return NS_OK;
135 }
136 
137 void
138 sbPlayQueueAsyncListener::Finalize()
139 {
140  nsresult rv;
141 
142  if (mDataRemote) {
143  rv = mDataRemote->Unbind();
144  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to unbind face plate data remote");
145  }
146 }
147 
148 //----------------------------------------------------------------------------
149 //
150 // Implementation of sbIMediaListAsyncListener
151 //
152 //-----------------------------------------------------------------------------
153 
154 NS_IMETHODIMP
155 sbPlayQueueAsyncListener::OnProgress(PRUint32 aItemsProcessed, PRBool aComplete)
156 {
157  nsresult rv;
158 
159  nsString processed = sbAutoString(aItemsProcessed);
160 
161  nsString message;
162  const PRUnichar* strings[2] = { processed.get(), mQueueName.get() };
163  rv = mBundle->FormatStringFromName(SB_LIBRARY_TRACKSADDED.get(),
164  strings,
165  2,
166  getter_Copies(message));
167  if (NS_FAILED(rv)) {
168  message = SB_LIBRARY_TRACKSADDED;
169  }
170 
171  rv = mDataRemote->SetStringValue(message);
172  NS_ENSURE_SUCCESS(rv, rv);
173 
174  if (aComplete) {
175  rv = mService->NotifyQueueOperationCompleted();
176  NS_ENSURE_SUCCESS(rv, rv);
177 
178  mService->SetIgnoreListListener(PR_FALSE);
179  }
180 
181  return NS_OK;
182 }
183 
184 NS_IMETHODIMP
185 sbPlayQueueAsyncListener::OnItemAdded(sbIMediaItem * aMediaItem)
186 {
187  return NS_OK;
188 }
189 
190 NS_IMETHODIMP
191 sbPlayQueueAsyncListener::OnComplete()
192 {
193  return NS_OK;
194 }
195 
201  nsIObserver)
202 
204  : mMediaList(nsnull),
205  mLibrary(nsnull),
206  mIndex(0),
207  mInitialized(PR_FALSE),
208  mExplicitStop(PR_FALSE),
209  mBatchRemovalIndex(0),
210  mBatchBeginAllHistory(PR_FALSE),
211  mIgnoreListListener(PR_FALSE),
212  mSequencerOnQueue(PR_FALSE),
213  mSequencerPlayingOrPaused(PR_FALSE),
214  mOperationInProgress(PR_FALSE),
215  mLibraryListener(nsnull),
216  mWeakMediacoreManager(nsnull),
217  mAsyncListener(nsnull)
218 {
219  #if PR_LOGGING
220  if (!gPlayQueueServiceLog) {
221  gPlayQueueServiceLog = PR_NewLogModule("sbPlayQueueService");
222  }
223  #endif /* PR_LOGGING */
224  TRACE(("%s[%p]", __FUNCTION__, this));
225 }
226 
228 {
229  TRACE(("%s[%p]", __FUNCTION__, this));
230  Finalize();
231 }
232 
233 nsresult
235 {
236  TRACE(("%s[%p]", __FUNCTION__, this));
237  nsresult rv;
238 
239  nsCOMPtr<nsIObserverService> observerService =
240  do_GetService("@mozilla.org/observer-service;1", &rv);
241  NS_ENSURE_SUCCESS(rv, rv);
242 
243  // Add observers for library manager topics
244  rv = observerService->AddObserver(this,
246  PR_FALSE);
247  NS_ENSURE_SUCCESS(rv, rv);
248 
249  rv = observerService->AddObserver(this,
251  PR_FALSE);
252  NS_ENSURE_SUCCESS(rv, rv);
253 
254  PRBool success = mListeners.Init();
255  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
256 
257  mAsyncListener = new sbPlayQueueAsyncListener(this);
258  NS_ENSURE_TRUE(mAsyncListener, NS_ERROR_OUT_OF_MEMORY);
259 
260  rv = mAsyncListener->Init();
261  NS_ENSURE_SUCCESS(rv, rv);
262 
263  return NS_OK;
264 }
265 
266 void
267 sbPlayQueueService::Finalize()
268 {
269  TRACE(("%s[%p]", __FUNCTION__, this));
270  nsresult rv;
271 
272  if (mMediaList) {
273  mMediaList->RemoveListener(this);
274  mMediaList = nsnull;
275  }
276 
277  if (mLibraryListener && mLibrary) {
278  mLibrary->RemoveListener(mLibraryListener);
279  mLibraryListener = nsnull;
280  }
281 
282  nsCOMPtr<sbILocalDatabaseLibrary> localDBLibrary = do_QueryInterface(mLibrary, &rv);
283  if (NS_SUCCEEDED(rv)) {
284  rv = localDBLibrary->RemoveCopyListener(this);
285  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove copy listener");
286  }
287  mLibrary = nsnull;
288 
289  if (mExternalListener) {
290  mExternalListener->RemoveListeners();
291  mExternalListener = nsnull;
292  }
293 
294  if (mWeakMediacoreManager) {
295  nsCOMPtr<sbIMediacoreEventTarget> target =
296  do_QueryReferent(mWeakMediacoreManager, &rv);
297  if (NS_SUCCEEDED(rv)) {
298  rv = target->RemoveListener(this);
299  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove mediacore listener");
300  }
301  mWeakMediacoreManager = nsnull;
302  }
303 
304  mRemovedItemGUIDs.Clear();
305 
306  if (mInitialized) {
307  nsCOMPtr<nsIObserverService> observerService =
308  do_GetService("@mozilla.org/observer-service;1", &rv);
309 
310  if (NS_SUCCEEDED(rv)) {
311  observerService->RemoveObserver(this,
313  }
314  }
315 
316  mListeners.Clear();
317 
318  mInitialized = PR_FALSE;
319 }
320 
321 //------------------------------------------------------------------------------
322 //
323 // Implementation of sbIPlayQueueService and associated helper methods
324 //
325 //------------------------------------------------------------------------------
326 
327 NS_IMETHODIMP
328 sbPlayQueueService::GetMediaList(sbIMediaList** aMediaList)
329 {
330  TRACE(("%s[%p]", __FUNCTION__, this));
331 
332  NS_ASSERTION(NS_IsMainThread(),
333  "GetMediaList() must be called from the main thread");
334  NS_ENSURE_ARG_POINTER(aMediaList);
335  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
336 
337  NS_IF_ADDREF(*aMediaList = mMediaList);
338  return NS_OK;
339 }
340 
341 NS_IMETHODIMP
342 sbPlayQueueService::GetIndex(PRUint32* aIndex)
343 {
344  TRACE(("%s[%p]", __FUNCTION__, this));
345 
346  NS_ASSERTION(NS_IsMainThread(),
347  "GetIndex() must be called from the main thread");
348  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
349  NS_ENSURE_ARG_POINTER(aIndex);
350 
351  *aIndex = mIndex;
352  return NS_OK;
353 }
354 
355 NS_IMETHODIMP
356 sbPlayQueueService::SetIndex(PRUint32 aIndex)
357 {
358  TRACE(("%s[%p]", __FUNCTION__, this));
359 
360  NS_ASSERTION(NS_IsMainThread(),
361  "SetIndex() must be called from the main thread");
362  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
363 
364  PRUint32 length;
365  nsresult rv = mMediaList->GetLength(&length);
366  NS_ENSURE_SUCCESS(rv, rv);
367 
368  // Only notify if the index actually changes
369  PRUint32 oldIndex = mIndex;
370 
371  // The valid range for our index is from 0 to the length of the list.
372  if (aIndex > length) {
373  mIndex = length;
374  } else {
375  mIndex = aIndex;
376  }
377 
378  // Notify listeners.
379  if (mIndex != oldIndex)
380  mListeners.EnumerateEntries(OnIndexUpdatedCallback, &mIndex);
381 
382  return NS_OK;
383 }
384 
385 NS_IMETHODIMP
386 sbPlayQueueService::GetOperationInProgress(PRBool *aOperationInProgress)
387 {
388  TRACE(("%s[%p]", __FUNCTION__, this));
389 
390  NS_ENSURE_ARG_POINTER(aOperationInProgress);
391 
392  *aOperationInProgress = mOperationInProgress;
393 
394  return NS_OK;
395 }
396 
397 NS_IMETHODIMP
398 sbPlayQueueService::QueueNext(sbIMediaItem* aMediaItem)
399 {
400  TRACE(("%s[%p]", __FUNCTION__, this));
401 
402  NS_ASSERTION(NS_IsMainThread(),
403  "QueueNext() must be called from the main thread");
404  NS_ASSERTION(!mOperationInProgress,
405  "QueueNext() should not be called while an async operation is in progress");
406  NS_ENSURE_ARG_POINTER(aMediaItem);
407  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
408  nsresult rv;
409 
410  mIgnoreListListener = PR_TRUE;
411 
412  // If the sequencer is on a view of our list and is playing or paused, insert
413  // the item immediately after the playing/paused item, whose index should
414  // correspond to mIndex. If the sequencer is stopped or on a view of a
415  // different list, insert the item immediately before the item at mIndex. The
416  // value of mIndex should never change on a queueNext operation, although the
417  // mediaItem at mIndex could change such that the new item is at mIndex.
418 
419  // The index into mMediaList that we will insert aMediaItem before.
420  PRUint32 insertBeforeIndex =
421  (mSequencerOnQueue && mSequencerPlayingOrPaused) ? mIndex + 1 : mIndex;
422 
423  // We need to use internal QueueLast methods if insertBeforeIndex is not a
424  // valid index into the list because it is beyond the last index.
425  PRUint32 length;
426  rv = mMediaList->GetLength(&length);
427  NS_ENSURE_SUCCESS(rv, rv);
428 
429  PRBool callQueueLast = (insertBeforeIndex >= length);
430 
431  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
432  if (NS_SUCCEEDED(rv)) {
433  // The item is a mediaList
434  if (callQueueLast) {
435  rv = QueueLastInternal(itemAsList);
436  } else {
437  rv = QueueNextInternal(itemAsList, insertBeforeIndex);
438  }
439  } else {
440  // Just a mediaItem
441  if (callQueueLast) {
442  rv = QueueLastInternal(aMediaItem);
443  } else {
444  rv = QueueNextInternal(aMediaItem, insertBeforeIndex);
445  }
446  }
447  NS_ENSURE_SUCCESS(rv, rv);
448 
449  mIgnoreListListener = PR_FALSE;
450 
451  return NS_OK;
452 }
453 
454 NS_IMETHODIMP
455 sbPlayQueueService::QueueLast(sbIMediaItem* aMediaItem)
456 {
457  TRACE(("%s[%p]", __FUNCTION__, this));
458 
459  NS_ASSERTION(NS_IsMainThread(),
460  "QueueLast() must be called from the main thread");
461  NS_ASSERTION(!mOperationInProgress,
462  "QueueLast() should not be called while an async operation is in progress");
463  NS_ENSURE_ARG_POINTER(aMediaItem);
464  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
465  nsresult rv;
466 
467  mIgnoreListListener = PR_TRUE;
468 
469  // Determine if we have a list or just a single item and add it to the end of
470  // our list. QueueLast should never change mIndex.
471 
472  nsCOMPtr<sbIMediaList> itemAsList = do_QueryInterface(aMediaItem, &rv);
473  if (NS_SUCCEEDED(rv)) {
474  // The item is a medialist
475  rv = QueueLastInternal(itemAsList);
476  } else {
477  rv = QueueLastInternal(aMediaItem);
478  }
479  NS_ENSURE_SUCCESS(rv, rv);
480 
481  mIgnoreListListener = PR_FALSE;
482 
483  return NS_OK;
484 }
485 
486 NS_IMETHODIMP
487 sbPlayQueueService::QueueSomeBefore(PRUint32 aIndex,
488  nsISimpleEnumerator* aMediaItems)
489 {
490  TRACE(("%s[%p]", __FUNCTION__, this));
491 
492  NS_ASSERTION(NS_IsMainThread(),
493  "QueueSomeBefore() must be called from the main thread");
494  NS_ASSERTION(!mOperationInProgress,
495  "QueueSomeBefore() should not be called while an async operation is in progress");
496  NS_ENSURE_ARG_POINTER(aMediaItems);
497  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
498  nsresult rv;
499 
500 #if DEBUG
501  PRUint32 length;
502  rv = mMediaList->GetLength(&length);
503  NS_ENSURE_SUCCESS(rv, rv);
504 
505  NS_ENSURE_ARG_RANGE(aIndex, 0, length - 1);
506 #endif
507 
508  // Still need to increment the mIndex properly in OnItemAdded when
509  // aIndex <= mIndex
510  if (aIndex > mIndex)
511  mIgnoreListListener = PR_TRUE;
512 
513  rv = NotifyQueueOperationStarted();
514  NS_ENSURE_SUCCESS(rv, rv);
515 
516  nsCOMPtr<sbIOrderableMediaList> orderedList =
517  do_QueryInterface(mMediaList, &rv);
518  NS_ENSURE_SUCCESS(rv, rv);
519 
520  rv = orderedList->InsertSomeBeforeAsync(aIndex,
521  aMediaItems,
522  mAsyncListener);
523  NS_ENSURE_SUCCESS(rv, rv);
524 
525  return NS_OK;
526 }
527 
528 NS_IMETHODIMP
529 sbPlayQueueService::QueueSomeNext(nsISimpleEnumerator* aMediaItems)
530 {
531  TRACE(("%s[%p]", __FUNCTION__, this));
532 
533  NS_ASSERTION(NS_IsMainThread(),
534  "QueueSomeNext() must be called from the main thread");
535  NS_ASSERTION(!mOperationInProgress,
536  "QueueSomeNext() should not be called while an async operation is in progress");
537  NS_ENSURE_ARG_POINTER(aMediaItems);
538  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
539  nsresult rv;
540 
541  mIgnoreListListener = PR_TRUE;
542 
543  rv = NotifyQueueOperationStarted();
544  NS_ENSURE_SUCCESS(rv, rv);
545 
546  // The index into mMediaList that we will insert aMediaItem before.
547  PRUint32 insertBeforeIndex =
548  (mSequencerOnQueue && mSequencerPlayingOrPaused) ? mIndex + 1 : mIndex;
549 
550  PRUint32 length;
551  rv = mMediaList->GetLength(&length);
552  NS_ENSURE_SUCCESS(rv, rv);
553 
554  if (insertBeforeIndex >= length) {
555  rv = mMediaList->AddMediaItems(aMediaItems, mAsyncListener, true);
556  NS_ENSURE_SUCCESS(rv, rv);
557  } else {
558  nsCOMPtr<sbIOrderableMediaList> orderedList =
559  do_QueryInterface(mMediaList, &rv);
560  NS_ENSURE_SUCCESS(rv, rv);
561 
562  rv = orderedList->InsertSomeBeforeAsync(insertBeforeIndex,
563  aMediaItems,
564  mAsyncListener);
565  NS_ENSURE_SUCCESS(rv, rv);
566  }
567 
568  return NS_OK;
569 }
570 
571 NS_IMETHODIMP
572 sbPlayQueueService::QueueSomeLast(nsISimpleEnumerator* aMediaItems)
573 {
574  TRACE(("%s[%p]", __FUNCTION__, this));
575 
576  NS_ASSERTION(NS_IsMainThread(),
577  "QueueSomeLast() must be called from the main thread");
578  NS_ASSERTION(!mOperationInProgress,
579  "QueueSomeLast() should not be called while an async operation is in progress");
580  NS_ENSURE_ARG_POINTER(aMediaItems);
581  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
582  nsresult rv;
583 
584  mIgnoreListListener = PR_TRUE;
585 
586  rv = NotifyQueueOperationStarted();
587  NS_ENSURE_SUCCESS(rv, rv);
588 
589  rv = mMediaList->AddMediaItems(aMediaItems, mAsyncListener, true);
590  NS_ENSURE_SUCCESS(rv, rv);
591 
592  return NS_OK;
593 }
594 
595 NS_IMETHODIMP
596 sbPlayQueueService::ClearAll()
597 {
598  TRACE(("%s[%p]", __FUNCTION__, this));
599 
600  NS_ASSERTION(NS_IsMainThread(),
601  "ClearAll() must be called from the main thread");
602  NS_ASSERTION(!mOperationInProgress,
603  "ClearAll() should not be called while an async operation is in progress");
604  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
605 
606  mIgnoreListListener = PR_TRUE;
607 
608  // Remove all non-list items from the play queue library.
609  nsresult rv = mLibrary->ClearItems();
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  SetIndex(0);
613 
614  mIgnoreListListener = PR_FALSE;
615 
616  return NS_OK;
617 }
618 
619 NS_IMETHODIMP
620 sbPlayQueueService::ClearHistory()
621 {
622  TRACE(("%s[%p]", __FUNCTION__, this));
623 
624  NS_ASSERTION(NS_IsMainThread(),
625  "ClearHistory() must be called from the main thread");
626  NS_ASSERTION(!mOperationInProgress,
627  "ClearHistory() should not be called while an async operation is in progress");
628  nsresult rv;
629 
630  if (mIndex == 0) {
631  // No history items, do nothing.
632  return NS_OK;
633  }
634 
635  // Remove all tracks with an index lower than mIndex. Create an emumerator so
636  // that the actual removal of items occurs in a batch. Note that
637  // sbLocalDatabaseSimpleMediaList::RemoveSome uses IndexOf to determine which
638  // item to actually remove. This is fine because we're always clearing from
639  // the beginning of the list in ClearHistory.
640 
641  nsCOMPtr<nsIMutableArray> historyItems =
642  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
643  NS_ENSURE_SUCCESS(rv, rv);
644 
645  for (PRUint32 i = 0; i < mIndex; ++i) {
646  nsCOMPtr<sbIMediaItem> item;
647  rv = mMediaList->GetItemByIndex(i, getter_AddRefs(item));
648  NS_ENSURE_SUCCESS(rv, rv);
649 
650  rv = historyItems->AppendElement(item, PR_FALSE);
651  NS_ENSURE_SUCCESS(rv, rv);
652  }
653 
654  nsCOMPtr<nsISimpleEnumerator> historyEnumerator;
655  rv = historyItems->Enumerate(getter_AddRefs(historyEnumerator));
656  NS_ENSURE_SUCCESS(rv, rv);
657 
658  // Remove the items, and let our list listener take care of mIndex and cleanup
659  // of the play queue library.
660  rv = mMediaList->RemoveSome(historyEnumerator);
661  NS_ENSURE_SUCCESS(rv, rv);
662 
663  return NS_OK;
664 }
665 
666 nsresult
667 sbPlayQueueService::QueueNextInternal(sbIMediaItem* aMediaItem,
668  PRUint32 aInsertBeforeIndex)
669 {
670  TRACE(("%s[%p]", __FUNCTION__, this));
671  NS_ENSURE_ARG_POINTER(aMediaItem);
672  nsresult rv;
673 
674  // Our list should always implement sbIOrderableMediaList
675  nsCOMPtr<sbIOrderableMediaList> orderedList =
676  do_QueryInterface(mMediaList, &rv);
677  NS_ENSURE_SUCCESS(rv, rv);
678 
679  rv = orderedList->InsertBefore(aInsertBeforeIndex, aMediaItem);
680  NS_ENSURE_SUCCESS(rv, rv);
681 
682  return NS_OK;
683 }
684 
685 nsresult
686 sbPlayQueueService::QueueNextInternal(sbIMediaList* aMediaList,
687  PRUint32 aInsertBeforeIndex)
688 {
689  TRACE(("%s[%p]", __FUNCTION__, this));
690  NS_ENSURE_ARG_POINTER(aMediaList);
691  nsresult rv;
692 
693  // Our list should always implement sbIOrderableMediaList
694  nsCOMPtr<sbIOrderableMediaList> orderedList =
695  do_QueryInterface(mMediaList, &rv);
696  NS_ENSURE_SUCCESS(rv, rv);
697 
698  rv = orderedList->InsertAllBefore(aInsertBeforeIndex, aMediaList);
699  NS_ENSURE_SUCCESS(rv, rv);
700 
701  return NS_OK;
702 }
703 
704 nsresult
705 sbPlayQueueService::QueueLastInternal(sbIMediaItem* aMediaItem)
706 {
707  TRACE(("%s[%p]", __FUNCTION__, this));
708  NS_ENSURE_ARG_POINTER(aMediaItem);
709 
710  // Add the item to the end of our list
711  nsresult rv = mMediaList->Add(aMediaItem);
712  NS_ENSURE_SUCCESS(rv, rv);
713 
714  return NS_OK;
715 }
716 
717 nsresult
718 sbPlayQueueService::QueueLastInternal(sbIMediaList* aMediaList)
719 {
720  TRACE(("%s[%p]", __FUNCTION__, this));
721  NS_ENSURE_ARG_POINTER(aMediaList);
722 
723  // Add the contents of aMediaList to the end of our list.
724  nsresult rv = mMediaList->AddAll(aMediaList);
725  NS_ENSURE_SUCCESS(rv, rv);
726 
727  return NS_OK;
728 }
729 
730 nsresult
731 sbPlayQueueService::CreateMediaList()
732 {
733  TRACE(("%s[%p]", __FUNCTION__, this));
734 
735  NS_ENSURE_STATE(mLibrary);
736 
737  nsresult rv;
738  rv = mLibrary->CreateMediaList(NS_LITERAL_STRING("simple"),
739  nsnull,
740  getter_AddRefs(mMediaList));
741  NS_ENSURE_SUCCESS(rv, rv);
742 
743  // Save the GUID as a property on the play queue library
744  nsAutoString listGUID;
745  rv = mMediaList->GetGuid(listGUID);
746  NS_ENSURE_SUCCESS(rv, rv);
747 
748 
749  rv = mLibrary->SetProperty(
750  NS_LITERAL_STRING(SB_PROPERTY_PLAYQUEUE_MEDIALIST_GUID),
751  listGUID);
752  NS_ENSURE_SUCCESS(rv, rv);
753 
754  rv = mMediaList->SetName(NS_LITERAL_STRING(SB_NAMEKEY_PLAYQUEUE_LIST));
755  NS_ENSURE_SUCCESS(rv, rv);
756 
757  rv = mMediaList->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_DEFAULTCOLUMNSPEC),
758  NS_MULTILINE_LITERAL_STRING(SB_PLAYQUEUE_DEFAULTCOLUMNSPEC));
759 
760  rv = mMediaList->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISSORTABLE),
761  NS_LITERAL_STRING("0"));
762  NS_ENSURE_SUCCESS(rv, rv);
763 
764  return NS_OK;
765 }
766 
767 nsresult
768 sbPlayQueueService::InitLibrary()
769 {
770  TRACE(("%s[%p]", __FUNCTION__, this));
771 
772  nsresult rv;
773 
774  // Get the play queue library guid, which is stored as a pref.
775  nsCOMPtr<nsIPrefBranch> prefBranch =
776  do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
777  NS_ENSURE_SUCCESS(rv, rv);
778 
779  nsCOMPtr<nsISupportsString> supportsString;
780  nsAutoString guid;
781  rv = prefBranch->GetComplexValue(SB_PREF_PLAYQUEUE_LIBRARY,
782  NS_GET_IID(nsISupportsString),
783  getter_AddRefs(supportsString));
784  NS_ENSURE_SUCCESS(rv, rv);
785 
786  rv = supportsString->GetData(guid);
787  NS_ENSURE_SUCCESS(rv, rv);
788 
789  // Get the library
790  nsCOMPtr<sbILibraryManager> libraryManager =
791  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
792  NS_ENSURE_SUCCESS(rv, rv);
793 
794  rv = libraryManager->GetLibrary(guid, getter_AddRefs(mLibrary));
795  NS_ENSURE_SUCCESS(rv, rv);
796 
797  // Hook up the external library listener to keep our properties in sync.
798  mExternalListener = new sbPlayQueueExternalLibraryListener();
799  NS_ENSURE_TRUE(mExternalListener, NS_ERROR_OUT_OF_MEMORY);
800  rv = mExternalListener->SetMasterLibrary(mLibrary);
801  NS_ENSURE_SUCCESS(rv, rv);
802 
803  nsCOMPtr<nsISimpleEnumerator> libEnum;
804  rv = libraryManager->GetStartupLibraries(getter_AddRefs(libEnum));
805  NS_ENSURE_SUCCESS(rv, rv);
806 
807  PRBool hasMore;
808  while (NS_SUCCEEDED(libEnum->HasMoreElements(&hasMore)) && hasMore) {
809  nsCOMPtr<nsISupports> next;
810  if (NS_SUCCEEDED(libEnum->GetNext(getter_AddRefs(next))) && next) {
811  nsCOMPtr<sbILibrary> library(do_QueryInterface(next, &rv));
812  if (NS_SUCCEEDED(rv) && library && library != mLibrary) {
813  rv = mExternalListener->AddExternalLibrary(library);
814  NS_ENSURE_SUCCESS(rv, rv);
815  }
816  }
817  }
818 
819  nsCOMPtr<sbILocalDatabaseLibrary> localDBLibrary = do_QueryInterface(mLibrary, &rv);
820  NS_ENSURE_SUCCESS(rv, rv);
821  localDBLibrary->AddCopyListener(this);
822 
823  return NS_OK;
824 }
825 
826 nsresult
827 sbPlayQueueService::InitMediaList()
828 {
829  TRACE(("%s[%p]", __FUNCTION__, this));
830 
831  NS_ENSURE_STATE(mLibrary);
832  nsresult rv;
833 
834  // The list GUID is stored as a property on the playqueue library
835  nsAutoString listGUID;
836  rv = mLibrary->GetProperty(
837  NS_LITERAL_STRING(SB_PROPERTY_PLAYQUEUE_MEDIALIST_GUID),
838  listGUID);
839  NS_ENSURE_SUCCESS(rv, rv);
840 
841  if (!listGUID.IsEmpty()) {
842  // Try to get the list
843  nsCOMPtr<sbIMediaItem> listAsItem;
844  rv = mLibrary->GetMediaItem(listGUID, getter_AddRefs(listAsItem));
845  if (NS_SUCCEEDED(rv)) {
846  mMediaList = do_QueryInterface(listAsItem, &rv);
847  NS_ENSURE_SUCCESS(rv, rv);
848 
849  // We found our list, so we can return.
850  return NS_OK;
851  }
852  }
853 
854  // We either didn't get a GUID or we couldn't find a mediaList with the
855  // expected GUID, so clear the play queue library and create a new list. The
856  // library can be cleared because we don't ever want it to contain more than
857  // the play queue list and items that are contained on that list. Our
858  // listeners aren't hooked up yet, so don't worry about notifications.
859  rv = mLibrary->Clear();
860  NS_ENSURE_SUCCESS(rv, rv);
861 
862  rv = CreateMediaList();
863  NS_ENSURE_SUCCESS(rv, rv);
864 
865  return NS_OK;
866 }
867 
868 nsresult
869 sbPlayQueueService::SetIndexToPlayingTrack()
870 {
871  TRACE(("%s[%p]", __FUNCTION__, this));
872  nsresult rv;
873 
874  if (mSequencerOnQueue) {
875  nsCOMPtr<sbIMediacoreManager> manager =
876  do_QueryReferent(mWeakMediacoreManager, &rv);
877  NS_ENSURE_SUCCESS(rv, rv);
878 
879  nsCOMPtr<sbIMediacoreSequencer> sequencer;
880  rv = manager->GetSequencer(getter_AddRefs(sequencer));
881  NS_ENSURE_SUCCESS(rv, rv);
882 
883  nsCOMPtr<sbIMediaListView> view;
884  rv = sequencer->GetView(getter_AddRefs(view));
885  NS_ENSURE_SUCCESS(rv, rv);
886 
887  PRUint32 filteredIndex;
888  rv = sequencer->GetViewPosition(&filteredIndex);
889  NS_ENSURE_SUCCESS(rv, rv);
890 
891  PRUint32 unfilteredIndex;
892  rv = view->GetUnfilteredIndex(filteredIndex, &unfilteredIndex);
893 
894  if (NS_SUCCEEDED(rv)) {
895  SetIndex(unfilteredIndex);
896  }
897  }
898 
899  return NS_OK;
900 }
901 
902 //------------------------------------------------------------------------------
903 //
904 // Implementation of sbIMediaListListener
905 //
906 //------------------------------------------------------------------------------
907 
908 // We have to allow access to our mediaList as a readonly attribute so that
909 // client code can create UI elements with the list. Thus, we have to be able to
910 // catch changes to the underlying list and update mIndex accordingly. As a
911 // general rule, we'll ignore these notifications if we are modifying the list
912 // from within the service.
913 
914 // During batch additions to our list, OnItemAdded provides indices reflecting
915 // the state of the mediaList after the entire batch has been added.
916 
917 // Batch removal notifications can be unreliable. OnAfterItemRemoved can
918 // actually be sent before the item is removed. OnBeforeItemRemoved provides
919 // indices that reflect the state of the mediaList prior to any removals, so we
920 // don't want to update mIndex incrementally during batch removal. Instead, we
921 // must always compare the index provided by onBeforeItemRemoved to the initial
922 // mIndex prior to the batch. This ensures that all comparisons during batch
923 // removal reflect the state of the list prior to the batch, and we use
924 // mBatchHelperIndex to track index decrementing during the batch.
925 
926 NS_IMETHODIMP
927 sbPlayQueueService::OnBatchBegin(sbIMediaList* aMediaList)
928 {
929  TRACE(("%s[%p]", __FUNCTION__, this));
930 
931  if (!mBatchHelper.IsActive()) {
932  mBatchRemovalIndex = mIndex;
933 
934  // If no batch is active, we shouldn't have any items marked for removal
935  // from the play queue library. If there are any, clear them.
936  mRemovedItemGUIDs.Clear();
937 
938  // Keep track of whether or not all items were 'history' items at the start
939  // of the batch.
940  PRUint32 length;
941  nsresult rv = mMediaList->GetLength(&length);
942  NS_ENSURE_SUCCESS(rv, rv);
943 
944  mBatchBeginAllHistory = length == mIndex;
945  }
946 
947  mBatchHelper.Begin();
948 
949  return NS_OK;
950 }
951 
952 NS_IMETHODIMP
953 sbPlayQueueService::OnBatchEnd(sbIMediaList* aMediaList)
954 {
955  TRACE(("%s[%p]", __FUNCTION__, this));
956  nsresult rv;
957 
958  mBatchHelper.End();
959  if (!mBatchHelper.IsActive()) {
960  // Iterate through the array of items that were removed from the queue list
961  // in the batch. It should not have any repeats. If there are no remaining
962  // instances of a given item in the queue, remove the item from the library.
963  PRUint32 removedCount = mRemovedItemGUIDs.Length();
964  LOG(("removed count %u", removedCount));
965 
966  if (removedCount == 0) {
967  // This must be an add batch, not a removal.
968  return NS_OK;
969  }
970 
971  for (PRUint32 i = 0; i < removedCount; ++i) {
972 
973  nsCOMPtr<sbIMediaItem> item;
974  rv = mLibrary->GetMediaItem(mRemovedItemGUIDs[i], getter_AddRefs(item));
975  if (NS_FAILED(rv) || ! item) {
976  continue;
977  }
978 
979  PRBool contains;
980  rv = mMediaList->Contains(item, &contains);
981  if (NS_FAILED(rv)) {
982  continue;
983  }
984 
985  if (!contains) {
986  rv = mLibrary->Remove(item);
987  NS_ENSURE_SUCCESS(rv, rv);
988  }
989  }
990 
991  mRemovedItemGUIDs.Clear();
992  LOG(("Changing index from %i to %i", mIndex, mBatchRemovalIndex));
993  SetIndex(mBatchRemovalIndex);
994  }
995 
996  return NS_OK;
997 }
998 
999 NS_IMETHODIMP
1000 sbPlayQueueService::OnItemAdded(sbIMediaList* aMediaList,
1001  sbIMediaItem* aMediaItem,
1002  PRUint32 aIndex,
1003  PRBool* aNoMoreForBatch)
1004 {
1005  TRACE(("%s[%p]", __FUNCTION__, this));
1006  nsresult rv;
1007 
1008  // Editing metadata for items in the queue is disabled.
1009  nsCOMPtr<sbIMutablePropertyArray> props =
1010  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1011  rv = props->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ISREADONLY),
1012  NS_LITERAL_STRING("1"));
1013  NS_ENSURE_SUCCESS(rv, rv);
1014  rv = mExternalListener->SetPropertiesNoSync(aMediaItem, props);
1015  NS_ENSURE_SUCCESS(rv, rv);
1016 
1017  if (mIgnoreListListener ||
1018  (mSequencerOnQueue && mSequencerPlayingOrPaused) ||
1019  mLibraryListener->ShouldIgnore())
1020  {
1021  return NS_OK;
1022  }
1023 
1024  // We need to know if the list was all 'history' items prior to the addition.
1025  // This should also be true if the list was empty, as logically they are the
1026  // same case (i.e. mIndex equals the length of the list)
1027  PRBool wasAllHistory;
1028  if (mBatchHelper.IsActive()) {
1029  wasAllHistory = mBatchBeginAllHistory;
1030  } else {
1031  PRUint32 length;
1032  rv = mMediaList->GetLength(&length);
1033  NS_ENSURE_SUCCESS(rv, rv);
1034 
1035  // For non-batch additions, the list was all 'history' items before the
1036  // addition if the length is one greater than the index here.
1037  wasAllHistory = length == mIndex + 1;
1038  }
1039 
1040  // The logic for updating the index on added items is the same for both batch
1041  // and non-batch additions. This ensures that the first item added to an empty
1042  // list becomes the 'current' item. It also ensures that an item added to the
1043  // end of a queue that is all 'history' items becomes the current item.
1044  if (aIndex <= mIndex && !(wasAllHistory && aIndex == mIndex)) {
1045  SetIndex(mIndex + 1);
1046  }
1047 
1048  LOG(("Added item at index %u, current index is now %i", aIndex, mIndex));
1049  return NS_OK;
1050 }
1051 
1052 NS_IMETHODIMP
1053 sbPlayQueueService::OnBeforeItemRemoved(sbIMediaList* aMediaList,
1054  sbIMediaItem* aMediaItem,
1055  PRUint32 aIndex,
1056  PRBool* aNoMoreForBatch)
1057 {
1058  TRACE(("%s[%p]", __FUNCTION__, this));
1059  NS_ENSURE_ARG_POINTER(aMediaItem);
1060  nsresult rv;
1061 
1062  if (mIgnoreListListener || mLibraryListener->ShouldIgnore())
1063  {
1064  return NS_OK;
1065  }
1066 
1067  if (mBatchHelper.IsActive()) {
1068  LOG(("Removing item at index %u", aIndex));
1069 
1070  nsAutoString guid;
1071  rv = aMediaItem->GetGuid(guid);
1072  NS_ENSURE_SUCCESS(rv, rv);
1073 
1074  LOG(("Removed item guid: %s",
1075  NS_ConvertUTF16toUTF8(guid).BeginReading()));
1076 
1077  if (mRemovedItemGUIDs.IndexOf(guid) == mRemovedItemGUIDs.NoIndex) {
1078  nsString* success = mRemovedItemGUIDs.AppendElement(guid);
1079  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1080  }
1081 
1082  // In batch removal, aIndex reflects the index in the list prior to removal
1083  // of any items in the batch. We keep mIndex in the pre-batch state as well,
1084  // so decrement the helper index if necessary.
1085  if (aIndex < mIndex) {
1086  mBatchRemovalIndex--;
1087  }
1088  }
1089 
1090  if (aNoMoreForBatch) {
1091  *aNoMoreForBatch = PR_FALSE;
1092  }
1093 
1094  return NS_OK;
1095 }
1096 
1097 NS_IMETHODIMP
1098 sbPlayQueueService::OnAfterItemRemoved(sbIMediaList* aMediaList,
1099  sbIMediaItem* aMediaItem,
1100  PRUint32 aIndex,
1101  PRBool* aNoMoreForBatch)
1102 {
1103  TRACE(("%s[%p]", __FUNCTION__, this));
1104  NS_ENSURE_ARG_POINTER(aMediaList);
1105  NS_ENSURE_ARG_POINTER(aMediaItem);
1106  nsresult rv;
1107 
1108  if (mIgnoreListListener ||
1109  (mSequencerOnQueue && mSequencerPlayingOrPaused) ||
1110  mLibraryListener->ShouldIgnore())
1111  {
1112  return NS_OK;
1113  }
1114 
1115  // Handle non-batch item removal here, where we should get the mediaList in
1116  // its post-removal state
1117  if (!mBatchHelper.IsActive()) {
1118  LOG(("Non batch item removed from index %u", aIndex));
1119  LOG(("aIndex %u, mIndex %u", aIndex, mIndex));
1120  if (aIndex < mIndex) {
1121  SetIndex(mIndex - 1);
1122  }
1123 
1124  PRBool contains;
1125  rv = mMediaList->Contains(aMediaItem, &contains);
1126  NS_ENSURE_SUCCESS(rv, rv);
1127 
1128  if (!contains) {
1129  LOG(("Removing the item from the library"));
1130  rv = mLibrary->Remove(aMediaItem);
1131  NS_ENSURE_SUCCESS(rv, rv);
1132  }
1133 
1134  }
1135 
1136  return NS_OK;
1137 }
1138 
1139 NS_IMETHODIMP
1140 sbPlayQueueService::OnItemUpdated(sbIMediaList* aMediaList,
1141  sbIMediaItem* aMediaItem,
1142  sbIPropertyArray* aProperties,
1143  PRBool* aNoMoreForBatch)
1144 {
1145  TRACE(("%s[%p]", __FUNCTION__, this));
1146 
1147  // Updates are handled in sbExternalLibraryListener.
1148 
1149  return NS_OK;
1150 }
1151 
1152 NS_IMETHODIMP
1153 sbPlayQueueService::OnItemMoved(sbIMediaList* aMediaList,
1154  PRUint32 aFromIndex,
1155  PRUint32 aToIndex,
1156  PRBool* aNoMoreForBatch)
1157 {
1158  TRACE(("%s[%p]", __FUNCTION__, this));
1159 
1160  if (mIgnoreListListener ||
1161  (mSequencerOnQueue && mSequencerPlayingOrPaused) ||
1162  mLibraryListener->ShouldIgnore())
1163  {
1164  return NS_OK;
1165  }
1166 
1167  // If the item at the current index is moved, keep that item as current.
1168  // Otherwise, update the current index if an item was moved from after it to
1169  // before it (or vise versa).
1170 
1171  if (aFromIndex == mIndex) {
1172  SetIndex(aToIndex);
1173  } else if (aFromIndex < mIndex && aToIndex >= mIndex) {
1174  SetIndex(mIndex - 1);
1175  } else if (aFromIndex > mIndex && aToIndex <= mIndex) {
1176  SetIndex(mIndex + 1);
1177  }
1178 
1179  LOG(("Item was moved from %u to %u, new index: %i", aFromIndex, aToIndex, mIndex));
1180 
1181  return NS_OK;
1182 }
1183 
1184 NS_IMETHODIMP
1185 sbPlayQueueService::OnBeforeListCleared(sbIMediaList* aMediaList,
1186  PRBool aExcludeLists,
1187  PRBool* aNoMoreForBatch)
1188 {
1189  TRACE(("%s[%p]", __FUNCTION__, this));
1190 
1191  if (mIgnoreListListener || mLibraryListener->ShouldIgnore()) {
1192  return NS_OK;
1193  }
1194 
1195  if (aNoMoreForBatch) {
1196  *aNoMoreForBatch = PR_FALSE;
1197  }
1198  return NS_OK;
1199 }
1200 
1201 NS_IMETHODIMP
1202 sbPlayQueueService::OnListCleared(sbIMediaList* aMediaList,
1203  PRBool aExcludeLists,
1204  PRBool* aNoMoreForBatch)
1205 {
1206  TRACE(("%s[%p]", __FUNCTION__, this));
1207 
1208  if (mIgnoreListListener || mLibraryListener->ShouldIgnore()) {
1209  return NS_OK;
1210  }
1211 
1212  LOG(("Clearing all items, but not lists, from queue library"));
1213  nsresult rv = mLibrary->ClearItems();
1214  NS_ENSURE_SUCCESS(rv, rv);
1215 
1216  SetIndex(0);
1217 
1218  return NS_OK;
1219 }
1220 
1221 //------------------------------------------------------------------------------
1222 //
1223 // Implementation of sbIMediacoreEventListener and mediacore event handlers
1224 //
1225 //------------------------------------------------------------------------------
1226 
1227 NS_IMETHODIMP
1228 sbPlayQueueService::OnMediacoreEvent(sbIMediacoreEvent* aEvent)
1229 {
1230  TRACE(("%s[%p]", __FUNCTION__, this));
1231 
1232  NS_ENSURE_ARG_POINTER(aEvent);
1233  nsresult rv;
1234 
1235  PRUint32 eventType;
1236  rv = aEvent->GetType(&eventType);
1237  NS_ENSURE_SUCCESS(rv, rv);
1238 
1239  LOG(("Event type 0x%x", eventType));
1240 
1241  // Determine if we are playing or paused.
1242  nsCOMPtr<sbIMediacoreManager> manager =
1243  do_QueryReferent(mWeakMediacoreManager, &rv);
1244  NS_ENSURE_SUCCESS(rv, rv);
1245 
1246  nsCOMPtr<sbIMediacoreSequencer> sequencer;
1247  rv = manager->GetSequencer(getter_AddRefs(sequencer));
1248  NS_ENSURE_SUCCESS(rv, rv);
1249 
1250  nsCOMPtr<sbIMediacoreStatus> status = do_QueryInterface(sequencer, &rv);
1251  NS_ENSURE_SUCCESS(rv, rv);
1252 
1253  PRUint32 state;
1254  rv = status->GetState(&state);
1255  NS_ENSURE_SUCCESS(rv, rv);
1256 
1257  mSequencerPlayingOrPaused = (state == sbIMediacoreStatus::STATUS_PLAYING ||
1259 
1260  // Detect view change so we can keep track of whether or not the sequencer's
1261  // view is a view of our mediaList.
1262  if (eventType == sbIMediacoreEvent::VIEW_CHANGE) {
1263  rv = OnViewChange(aEvent);
1264  NS_ENSURE_SUCCESS(rv, rv);
1265 
1266  return NS_OK;
1267  }
1268 
1269  // Ignore other event types when the sequencer is not on a view of our list
1270  if (!mSequencerOnQueue) {
1271  return NS_OK;
1272  }
1273 
1274  switch (eventType) {
1275 
1277  mExplicitStop = PR_TRUE;
1278  break;
1279 
1280  // SEQUENCE_END right after EXPLICIT_STOP means user stops the playback.
1281  // Handle SEQUENCE_END by bumping mIndex if EXPLICIT_STOP is not fired
1282  // earlier.
1284  if (!mExplicitStop) {
1285  // Call SetIndex so mIndex is constrained to the length of the list.
1286  rv = SetIndex(mIndex + 1);
1287  NS_ENSURE_SUCCESS(rv, rv);
1288  } else {
1289  mExplicitStop = PR_FALSE;
1290  }
1291  break;
1292 
1294  rv = OnTrackChange(aEvent);
1295  NS_ENSURE_SUCCESS(rv, rv);
1296  break;
1297 
1299  rv = OnTrackIndexChange(aEvent);
1300  NS_ENSURE_SUCCESS(rv, rv);
1301  break;
1302 
1303  default:
1304  break;
1305  }
1306 
1307  return NS_OK;
1308 }
1309 
1310 nsresult
1311 sbPlayQueueService::OnViewChange(sbIMediacoreEvent* aEvent)
1312 {
1313  TRACE(("%s[%p]", __FUNCTION__, this));
1314  NS_ENSURE_ARG_POINTER(aEvent);
1315  nsresult rv;
1316 
1317  // Check to see if the new view is a view of our mediaList.
1318 
1319  nsCOMPtr<nsIVariant> variant;
1320  rv = aEvent->GetData(getter_AddRefs(variant));
1321  NS_ENSURE_SUCCESS(rv, rv);
1322 
1323  nsCOMPtr<nsISupports> supports;
1324  rv = variant->GetAsISupports(getter_AddRefs(supports));
1325  NS_ENSURE_SUCCESS(rv, rv);
1326 
1327  nsCOMPtr<sbIMediaListView> view = do_QueryInterface(supports, &rv);
1328  NS_ENSURE_SUCCESS(rv, rv);
1329 
1330  nsCOMPtr<sbIMediaList> viewList;
1331  rv = view->GetMediaList(getter_AddRefs(viewList));
1332  NS_ENSURE_SUCCESS(rv, rv);
1333 
1334  PRBool onQueue;
1335  rv = viewList->Equals(mMediaList, &onQueue);
1336  NS_ENSURE_SUCCESS(rv, rv);
1337  LOG(("Is the view change a view of our list? %i", onQueue));
1338 
1339  mSequencerOnQueue = onQueue;
1340 
1341  return NS_OK;
1342 }
1343 
1344 nsresult
1345 sbPlayQueueService::OnTrackChange(sbIMediacoreEvent* aEvent)
1346 {
1347  TRACE(("%s[%p]", __FUNCTION__, this));
1348  NS_ENSURE_ARG_POINTER(aEvent);
1349 
1350  nsresult rv = SetIndexToPlayingTrack();
1351  NS_ENSURE_SUCCESS(rv, rv);
1352 
1353  return NS_OK;
1354 }
1355 
1356 nsresult
1357 sbPlayQueueService::OnTrackIndexChange(sbIMediacoreEvent* aEvent)
1358 {
1359  TRACE(("%s[%p]", __FUNCTION__, this));
1360  NS_ENSURE_ARG_POINTER(aEvent);
1361 
1362  if (mSequencerPlayingOrPaused) {
1363  nsresult rv = SetIndexToPlayingTrack();
1364  NS_ENSURE_SUCCESS(rv, rv);
1365  }
1366 
1367  return NS_OK;
1368 }
1369 
1370 //------------------------------------------------------------------------------
1371 //
1372 // Implementation of sbILocalDatabaseLibraryCopyListener
1373 //
1374 //------------------------------------------------------------------------------
1375 
1376 nsresult
1377 sbPlayQueueService::OnItemCopied(sbIMediaItem* aSourceItem,
1378  sbIMediaItem* aDestinationItem)
1379 {
1380  TRACE(("%s[%p]", __FUNCTION__, this));
1381  nsresult rv;
1382 
1383  nsCOMPtr<sbIMutablePropertyArray> props =
1384  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1385  rv = props->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ISREADONLY),
1386  NS_LITERAL_STRING("0"));
1387  NS_ENSURE_SUCCESS(rv, rv);
1388 
1389  NS_ENSURE_STATE(mExternalListener);
1390  rv = mExternalListener->SetPropertiesNoSync(aDestinationItem, props);
1391  NS_ENSURE_SUCCESS(rv, rv);
1392 
1393  return NS_OK;
1394 }
1395 
1396 //------------------------------------------------------------------------------
1397 //
1398 // Implementation of nsIObserver
1399 //
1400 //------------------------------------------------------------------------------
1401 
1402 NS_IMETHODIMP
1403 sbPlayQueueService::Observe(nsISupports* aSubject,
1404  const char* aTopic,
1405  const PRUnichar* aData)
1406 {
1407 
1408  NS_ENSURE_ARG_POINTER(aTopic);
1409  nsresult rv;
1410 
1411  TRACE(("%s[%p]: Observing %s", __FUNCTION__, this, aTopic));
1412 
1413  nsCOMPtr<nsIObserverService> observerService =
1414  do_GetService("@mozilla.org/observer-service;1", &rv);
1415  NS_ENSURE_SUCCESS(rv, rv);
1416 
1417  if (!strcmp(SB_LIBRARY_MANAGER_READY_TOPIC, aTopic)) {
1418  rv = observerService->RemoveObserver(this, aTopic);
1419  NS_ENSURE_SUCCESS(rv, rv);
1420 
1421  // Get a weak reference to the mediacore manager
1422  nsCOMPtr<nsISupportsWeakReference> weak =
1423  do_GetService(SB_MEDIACOREMANAGER_CONTRACTID, &rv);
1424  NS_ENSURE_SUCCESS(rv, rv);
1425 
1426  rv = weak->GetWeakReference(getter_AddRefs(mWeakMediacoreManager));
1427  NS_ENSURE_SUCCESS(rv, rv);
1428 
1429  // Add ourself as a mediacore event listener
1430  nsCOMPtr<sbIMediacoreEventTarget> target =
1431  do_QueryReferent(mWeakMediacoreManager, &rv);
1432  NS_ENSURE_SUCCESS(rv, rv);
1433 
1434  rv = target->AddListener(this);
1435  NS_ENSURE_SUCCESS(rv, rv);
1436 
1437  // Make sure we have a library
1438  nsresult rv = InitLibrary();
1439  NS_ENSURE_SUCCESS(rv, rv);
1440 
1441  // Make sure we have our underlying medialist
1442  rv = InitMediaList();
1443  NS_ENSURE_SUCCESS(rv, rv);
1444 
1445  NS_ENSURE_STATE(mMediaList);
1446 
1447  // Listen to our list (everything except OnItemUpdated, which is handled by
1448  // our external listener).
1449  rv = mMediaList->AddListener(this,
1450  PR_FALSE,
1459  nsnull);
1460  NS_ENSURE_SUCCESS(rv, rv);
1461 
1462  // Listen to the play queue library
1463  mLibraryListener = new sbPlayQueueLibraryListener();
1464  NS_ENSURE_TRUE(mLibraryListener, NS_ERROR_OUT_OF_MEMORY);
1465 
1466  rv = mLibrary->AddListener(mLibraryListener,
1467  PR_FALSE,
1470  nsnull);
1471  NS_ENSURE_SUCCESS(rv, rv);
1472 
1473  mInitialized = PR_TRUE;
1474  }
1475  else if (!strcmp(SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC, aTopic)) {
1476  Finalize();
1477  }
1478 
1479  return NS_OK;
1480 }
1481 
1482 NS_IMETHODIMP
1483 sbPlayQueueService::AddListener(sbIPlayQueueServiceListener* aListener)
1484 {
1485  TRACE(("%s[%p]", __FUNCTION__, this));
1486  nsISupportsHashKey* success = mListeners.PutEntry(aListener);
1487  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1488 
1489  return NS_OK;
1490 }
1491 
1492 NS_IMETHODIMP
1493 sbPlayQueueService::RemoveListener(sbIPlayQueueServiceListener* aListener)
1494 {
1495  TRACE(("%s[%p]", __FUNCTION__, this));
1496  mListeners.RemoveEntry(aListener);
1497 
1498  return NS_OK;
1499 }
1500 
1501 PLDHashOperator PR_CALLBACK
1502 sbPlayQueueService::OnIndexUpdatedCallback(nsISupportsHashKey* aKey,
1503  void* aUserData)
1504 {
1505  TRACE(("%s[static]", __FUNCTION__));
1506  NS_ASSERTION(aKey && aUserData, "Args should not be null!");
1507 
1508  nsresult rv;
1509  nsCOMPtr<sbIPlayQueueServiceListener> listener =
1510  do_QueryInterface(aKey->GetKey(), &rv);
1511 
1512  if (NS_SUCCEEDED(rv)) {
1513  PRUint32* index = static_cast<PRUint32*>(aUserData);
1514  rv = listener->OnIndexUpdated(*index);
1515  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1516  "OnIndexUpdated returned a failure code");
1517  }
1518  return PL_DHASH_NEXT;
1519 }
1520 
1521 nsresult
1522 sbPlayQueueService::NotifyQueueOperationStarted() {
1523  TRACE(("%s[%p]", __FUNCTION__, this));
1524 
1525  NS_ASSERTION(NS_IsMainThread(),
1526  "NotifyQueueOperationStarted() must be called from the main thread");
1527 
1528  mOperationInProgress = PR_TRUE;
1529  return mListeners.EnumerateEntries(OnQueueStartedCallback, nsnull);
1530 };
1531 
1532 nsresult
1533 sbPlayQueueService::NotifyQueueOperationCompleted() {
1534  TRACE(("%s[%p]", __FUNCTION__, this));
1535 
1536  NS_ASSERTION(NS_IsMainThread(),
1537  "NotifyQueueOperationCompleted() must be called from the main thread");
1538 
1539  mListeners.EnumerateEntries(OnQueueCompletedCallback, nsnull);
1540  mOperationInProgress = PR_FALSE;
1541 
1542  return NS_OK;
1543 };
1544 
1545 PLDHashOperator PR_CALLBACK
1546 sbPlayQueueService::OnQueueStartedCallback(nsISupportsHashKey* aKey,
1547  void* aUserData)
1548 {
1549  TRACE(("%s[static]", __FUNCTION__));
1550  NS_ASSERTION(aKey, "aKey should not be null!");
1551 
1552  nsresult rv;
1553  nsCOMPtr<sbIPlayQueueServiceListener> listener =
1554  do_QueryInterface(aKey->GetKey(), &rv);
1555 
1556  if (NS_SUCCEEDED(rv)) {
1557  listener->OnQueueOperationStarted();
1558  }
1559  return PL_DHASH_NEXT;
1560 }
1561 
1562 PLDHashOperator PR_CALLBACK
1563 sbPlayQueueService::OnQueueCompletedCallback(nsISupportsHashKey* aKey,
1564  void* aUserData)
1565 {
1566  TRACE(("%s[static]", __FUNCTION__));
1567  NS_ASSERTION(aKey, "Args should not be null!");
1568 
1569  nsresult rv;
1570  nsCOMPtr<sbIPlayQueueServiceListener> listener =
1571  do_QueryInterface(aKey->GetKey(), &rv);
1572 
1573  if (NS_SUCCEEDED(rv)) {
1574  listener->OnQueueOperationCompleted();
1575  }
1576  return PL_DHASH_NEXT;
1577 }
1578 
1579 // -----------------------------------------------------------------------------
1580 //
1581 // XPCOM Registration
1582 //
1583 // -----------------------------------------------------------------------------
1584 
1585 /* static */ NS_METHOD
1586 sbPlayQueueService::RegisterSelf(nsIComponentManager *aCompMgr,
1587  nsIFile *aPath,
1588  const char *aLoaderStr,
1589  const char *aType,
1590  const nsModuleComponentInfo *aInfo)
1591 {
1592  NS_ENSURE_ARG_POINTER(aCompMgr);
1593  NS_ENSURE_ARG_POINTER(aPath);
1594  NS_ENSURE_ARG_POINTER(aLoaderStr);
1595  NS_ENSURE_ARG_POINTER(aType);
1596  NS_ENSURE_ARG_POINTER(aInfo);
1597 
1598  nsresult rv = NS_ERROR_UNEXPECTED;
1599  nsCOMPtr<nsICategoryManager> catMgr =
1600  do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
1601  NS_ENSURE_SUCCESS(rv, rv);
1602 
1603  rv = catMgr->AddCategoryEntry(APPSTARTUP_CATEGORY,
1605  "service,"
1607  PR_TRUE,
1608  PR_TRUE,
1609  nsnull);
1610  NS_ENSURE_SUCCESS(rv, rv);
1611 
1612  return NS_OK;
1613 }
attribute unsigned long index
Index of the current track in the Play Queue.
return NS_OK
_updateCookies aPath
static NS_METHOD RegisterSelf(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *aLoaderStr, const char *aType, const nsModuleComponentInfo *aInfo)
const SB_LIBRARY_MANAGER_READY_TOPIC
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
const unsigned long STATUS_PLAYING
void SetIgnoreListListener(PRBool aIgnore)
const unsigned long SEQUENCE_END
Sequence end.
const NS_PREFSERVICE_CONTRACTID
const unsigned long TRACK_CHANGE
Track playing has changed.
Songbird Library Manager Definition.
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
Interface used to listen for items being copied from a library.
#define SB_PLAYQUEUESERVICE_CONTRACTID
friend class sbPlayQueueAsyncListener
const unsigned long LISTENER_FLAGS_BEFOREITEMREMOVED
Class to allow items in a "master" internal library to stay in sync with duplicates in other librarie...
A brief description of the contents of this interface.
const unsigned long EXPLICIT_STOP
Explicit call to stop the playback.
#define SB_PROPERTY_ISSORTABLE
#define SB_LIBRARY_TRACKSADDED
const unsigned long LISTENER_FLAGS_BATCHEND
Interface used to listen to changes to a media list.
var strings
Definition: Info.js:46
Definition of the sbIMediacoreEvent interface.
Songbird Play Queue Service Component Definition.
#define SB_PROPERTY_PLAYQUEUE_MEDIALIST_GUID
Listener for the Play Queue service.
#define SB_PLAYQUEUESERVICE_CLASSNAME
#define SB_PLAYQUEUE_DEFAULTCOLUMNSPEC
const unsigned long LISTENER_FLAGS_BEFORELISTCLEARED
const unsigned long LISTENER_FLAGS_BATCHBEGIN
const unsigned long TRACK_INDEX_CHANGE
Index in view of item currently playing has changed.
const unsigned long VIEW_CHANGE
Sequencer view changed.
const unsigned long LISTENER_FLAGS_AFTERITEMREMOVED
#define SB_PREF_PLAYQUEUE_LIBRARY
Interface for use with async methods on sbIMediaList. NOTE: this interface is now deprecated...
var libraryManager
NS_IMPL_THREADSAFE_ISUPPORTS2(sbPlayQueueAsyncListener, sbIAddMediaItemsListener, sbIMediaListAsyncListener)
#define SB_PROPERTY_ISREADONLY
#define SB_PROPERTY_DEFAULTCOLUMNSPEC
#define SB_NAMEKEY_PLAYQUEUE_LIST
#define SB_PLAYQUEUE_PANE_TITLE
GstMessage * message
const unsigned long LISTENER_FLAGS_LISTCLEARED
const unsigned long LISTENER_FLAGS_ITEMMOVED
#define SB_DATAREMOTE_FACEPLATE_STATUS
#define SB_BUNDLE_URL
StringArrayEnumerator prototype hasMore
const unsigned long LISTENER_FLAGS_ITEMADDED
Service for interacting with the Play Queue.
NS_IMPL_ISUPPORTS5(sbPlayQueueService, sbIPlayQueueService, sbIMediaListListener, sbIMediacoreEventListener, sbILocalDatabaseLibraryCopyListener, nsIObserver) sbPlayQueueService
#define LOG(args)
NS_DECL_ISUPPORTS NS_DECL_SBIADDMEDIAITEMSLISTENER sbPlayQueueAsyncListener(sbPlayQueueService *aService)
#define TRACE(args)
#define SB_MEDIACOREMANAGER_CONTRACTID
const unsigned long STATUS_PAUSED
Interface that defines a single item of media in the system.
const nsISupportsString
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
_getSelectedPageStyle s i
const SB_LIBRARY_MANAGER_BEFORE_SHUTDOWN_TOPIC
_updateTextAndScrollDataForFrame aData
function next()